Privacy & security
Local-first isn't a marketing word here. It's the architecture.
Three containers on your machine, every port on 127.0.0.1. No account, no tenancy, no server holding your career history. One hard rule follows from that.
Nothing is uploaded
Scoring, rendering, tracking and export run locally. The only outbound traffic goes to the one LLM provider you configure.
Your key stays yours
It lives in your .env or your local database and goes to exactly one place: the provider endpoint you set. The API never echoes a stored key back out; the settings endpoint reports only whether one is configured. A non-http(s) model endpoint is refused outright, because that URL decides where your key is sent.
No telemetry leaves your machine
The extension's one telemetry endpoint posts to your own backend on localhost. There is no collector at the other end, in this repo or anywhere else.
Your data is files you own
Resumes are JSON on your disk. Renders land in company-and-role folders. Your KB exports to one career.md with no model involved.
The trade that buys all this: no authentication
Single-user and local-first means no login, and no authentication on any HTTP or MCP endpoint. That absence is what removes the accounts, the tenancy and the server holding your career history.
So: don't expose it to a network. Not the internet, not your LAN, not a tunnel. Anything that can reach the API has full read and write access to your entire career record and your saved API keys.
Running it locally is safe by construction. Publishing it isn't, and no setting makes it so.
The extension
What the extension keeps, and what it can't
What it keeps
Which fields it met: label, kind, rule, outcome
The page's hostname and a first-seen timestamp
What it structurally cannot keep
Any value you typed. The models have no column for one.
Signatures, attestations, consent boxes, credentials, government IDs
“No values” isn't the same as “nothing personal”. What that table accumulates is a record of which companies you applied to and when. Three things bound it: it never leaves your machine, capture is a toggle in the widget's ⋯ menu, and Clear data wipes all of it whenever you want without turning capture off.
Tracing
Optional tracing, and why compose won't forward it
Every LLM call can be traced to a Langfuse instance you already run. All three variables are required, and the compose stack deliberately does not forward them. Traces contain your prompts, which means your resume text, and a stale key pair passed through silently would ship that to a third party. To trace, run the backend yourself outside compose. Tracing is off whenever any of the three is unset.
Leaving
Nothing about leaving is engineered to be difficult.
Because nothing here was ever monetized by making it so.
career.md
Your whole career record as one deterministic Markdown file. No model involved, readable and diffable, downloadable from the Career KB page or over MCP.
applications/
Every render filed in its own company-and-role-named folder with the typeset source and the exact PDF you sent.
base_resumes/
Your resumes are JSON files on your disk, not rows in someone else's database.
The database
Postgres in a container you own. docker compose down -v is yours to run — and it deletes everything, which is the point.
Career tooling should be infrastructure, not a rental.
Clone it, run it, keep everything it produces. Nothing here was built to make leaving hard.
Apache-2.0 · no account · no subscription · your data stays on your disk