DEV Community

Vadim Karnopelev
Vadim Karnopelev

Posted on

Your career profile is a database with terrible normalization — so I fixed mine and made it free

The bug report on my own career

Here's a data model I'd fail you for in code review.

The same fact — "shipped a habit-tracking app called Quietleaf" — stored independently in my CV, my LinkedIn, my portfolio site, my Upwork profile, and two freelance profiles I'd forgotten the passwords to. Six copies. No source of truth. Updates applied by hand, one surface at a time, and never to all of them.

If a junior handed me that, I'd write "denormalize this — one row, render per view" and move on. It took me embarrassingly long to point that review at myself.

So I did the obvious thing. One master profile is the source of truth. Everything a recruiter, client, or platform wants is just a view of it — CV, published page, portfolio, freelance proposal, embed, share card. Edit the profile, every view re-renders. It's called Weft.

And it's free to use right now — no payment, no card. I'm in early launch and keeping the free limits wide open on purpose. I'd rather you actually use it and tell me where it breaks. (More on Pro and the extension at the bottom — both honest.)

👉 tryweft.me — sign in with Google and you're in.

The dashboard — one profile, many rendered outputs

What it actually does

Weft is one place that becomes every career surface you need:

  • A published page on your own *.tryweft subdomain — a real, themeable personal site.
  • A CV / résumé you can download as a clean PDF.
  • A portfolio / case-studies section — profession-agnostic. It's "Work," never "repos," so a photographer's page and a backend dev's page run the same code path.
  • Freelance proposals drafted from your real experience (Upwork, Freelancer, Fiverr…).
  • A job-fit score + tailored CV for any job posting you paste in.
  • Embeds and a share card to drop anywhere.

The point: you update one thing, and every one of those stays in sync.

How it works (the simple version)

1. It captures instead of making you fill out yet another form.
A browser extension reads your LinkedIn and whatever tab you're on; the server pulls your GitHub by username, parses an uploaded résumé, and reads public URLs you give it. You're not re-typing your life into a wizard — it's extraction, not data entry.

2. The AI weaves it into one clean profile.
All those messy inputs get normalized and merged into a single structured profile — deduped, tidied, one source of truth. You watch it happen live (it streams as it works). Then you edit anything you don't like in plain language: there's an "ask AI to edit" box right on the profile.

The master profile editor — one source of truth, edit in plain language

3. Everything else renders from that profile.
Publish the page, download the CV, build out your work section, generate a proposal. Edit a bullet on your CV and it writes back to the master, so your page and portfolio get it too. But layout, theme, section order, and what's visible are per-surface — the facts are shared, the presentation isn't. Six themes on the published page to start.

A real published page — same data, themed output

The part freelancers and job-seekers actually want

This is the feature I built for myself, because I freelance.

Paste a job description → get a fit score and a proposal drafted from your real profile. On Upwork the extension grabs the posting in one click. The score comes with reasoning — your strengths for this role, and the gap they'll poke at in the interview — and the generated pitch cites work you actually did, not "I am a passionate developer." It keeps a history of what you applied to.

Target Job Fit — scored against your real experience, not vibes

Same idea for job applications: tailor your CV to a specific posting without rewriting it from scratch, and track what you've sent. And for the service-provider side, there are service packs — your offer written for the platform you're pitching on.

Service packs — your offer, written for the platform

The one rule: it never makes things up

This was the non-negotiable. Every "AI résumé" tool I tried would happily invent a metric to make a bullet sound better — "increased performance by 40%." For a CV that's not a feature, it's a liability: you get caught in the interview.

So Weft's generation is constrained. It drafts anything it can ground in your captured facts, and when a section is thin it flags the gap instead of filling it. The dashboard literally shows you a to-do list: "this role has no outcome — add one." Honest beats impressive.

For the devs: how the AI brain is built

Since this is dev.to — the whole thing is Phoenix LiveView + Elixir, Postgres, Oban, and PubSub. No React, no Python: the entire "AI brain" is Elixir, and it's been a genuinely pleasant place to orchestrate an LLM pipeline. The extension is WXT (TypeScript, MV3), cross-browser. A few pieces I think are worth stealing:

The LLM provider is a swappable behaviour, not a hardcoded SDK.
There's a Weft.LLM.Provider behaviour and adapters behind it, picked by an LLM_PROVIDER env var — the same pattern you'd use for a payment gateway. Swapping the whole model backend (it runs on Anthropic today; Gemini and DeepSeek are drop-in adapters) is a config change, not a rewrite. The model matrix itself lives in config too: a fast tier for cheap mechanical steps (normalize, merge) and a smart tier for the generation that users actually read. Routing by cost/quality is a data change, not a code change.

Every token is metered. Each call writes to an llm_usage ledger — tokens in / out / cached, tagged by operation (weave, tailor, proposal, job_fit…) and by run. A tiny cost function reprices those counts against a rate book in config (Σ tokens × rate / 1e6), so I can answer "what did this user's weave actually cost?" and "which operation is eating the budget?" from a plain Ecto query. That same ledger powers a "here's what Weft did for you this month" view for users — the metering is one source, read two ways.

The pipeline is caching + validation + streaming.
The JSON schema goes into the system prompt and is kept byte-identical so the provider's prompt cache actually hits; per-call context goes in the user message. Output is validated with an Ecto changeset and retried once with the errors fed back in (traverse_errors), so a malformed response self-heals instead of 500-ing. Whole responses are cached in Postgres, the multi-stage work runs as Oban jobs, and progress streams to the browser live over PubSub — you watch your profile get woven token by token.

A couple more bits: published pages are served per-subdomain through a plug and themed at render time; the analytics are first-party and privacy-friendly — a salted daily hash of IP + user-agent, no cookies and no raw IP stored, so I get views/visitors without a tracking script.

Ask me anything about any of that below — the provider abstraction and the "meter everything, price from config" approach are the parts I'd most recommend to anyone building on LLMs.

How to get in — and about Pro & the extension

It's free right now. No payment, no card. I've got the free limits set wide while I'm launching, so you can build a profile, publish a page, download a CV, and draft proposals without hitting a wall. Your captured data stays private until you choose to publish.

Two things to know, because I'm being straight with you:

  • Pro isn't for sale yet. There's a Pro tier planned (custom domain, remove the "Made with Weft" badge, all themes, unlimited AI, analytics) — but I'm not charging anyone today. If you hit the free ceiling or want to try the Pro features, just comment or DM me here and I'll flip your account to Pro. No payment.
  • The extension isn't in the Chrome/Firefox stores yet. It's the LinkedIn + Upwork capture arm, and I'm handing out builds manually during early launch. Just drop a comment below (or DM me) saying which browser you're on — Chrome or Firefox — and I'll send you the build to load. (You can do plenty without it — GitHub, résumé upload, and public URLs all work server-side — but the extension is what makes LinkedIn and one-click job capture nice.)

Come break it

👉 tryweft.me

My profile

Top comments (0)