Privacy
nerfd measures how a session went, never what it was about.
Prompts, code, file paths, repo names and notes stay on your machine. When sharing is on, one small JSON object per session is sent to one address. Before believing any of that, run nerfd privacy: it prints the exact record, built by the same code that sends it.
What is sent
One record per finished session, only while sharing is on. This is the complete list.
| model, effort level | which model answered, and at what reasoning effort |
| tool, tool version | which coding agent, and its version. this is the field that separates a model change from a harness change |
| plan tier, price | the subscription you told nerfd about, or the one it read off that tool’s own config on your machine, so "what did $200 buy" can be answered |
| plan source | one word: declared, detected or unknown. which file and field a detector opened stays on your machine; that word and the plan id are the whole of what detection sends |
| ISO week, end time | when the session finished |
| task category, size | code / debug / refactor / review / ux / strategy / writing / research / ops, inferred from your first prompt on your machine |
| repo language, size bucket, age bucket | ts, py, go … ; small / medium / large by tracked file count; greenfield or established |
| duration | how long it ran |
| counts | prompts, turns, tool calls, edits, files touched, tests run, errors, rate-limit hits, timeouts, model switches, interrupts, tool-call errors, context-limit hits |
| behavioural signals | how much you had to steer: corrections, reprompts, frustration, pushback, clarifications, edits without a prior read, and whether the session was abandoned. counts and rates only, with the detector version next to them. computed on your machine from a transcript already on your disk; the text is dropped before the function returns |
| token totals, latency p50 and p95 | volume and speed |
| subscription window usage | the percentage the tool reports, where it reports one |
| rating, kept | your optional 1-5 rating and whether you kept the work. absent unless you typed it |
| survival ratio | the share of lines the session added that were still there an hour later. a number, not the lines |
| evidence link | empty unless you attached a public gist or PR yourself |
| reporter id | a hash of a random number made when you installed. not an account, not derived from anything about you |
The shape is defined once, in packages/core/src/types.ts. The conversion from a local session to a public record is a single function, toReport in packages/core/src/redact.ts, short enough to read in a minute. The server rejects anything that does not match.
What is never sent
- prompt text, or any part of it
- model output, or any part of it
- source code, diffs, or line contents
- file names or paths, absolute or relative
- repository names, remotes, branches, or commit hashes
- the note you type when you rate a session
- your username, hostname, email, git identity, or environment variables
- location, advertising ids, or anything from a third-party analytics service
Under any setting. There is no flag that turns any of it on. The codebase has zero runtime dependencies, so nothing can add an analytics SDK without it showing up in a diff.
When it is sent
- Never, if sharing is off. No heartbeat, no version check, no crash report.
- After a session ends, if sharing is on. The hook gives up after six seconds and never blocks or breaks your coding tool.
- When you re-rate a session that was already sent, so the public record matches what you actually think.
- When you run
nerfd shareyourself, whether or not automatic sharing is on.
The one-line installer turns automatic sharing on and prints the field list while it does it. nerfd share off stops it for good.
See it before it is sent
nerfd privacy # status, everything stored locally, and the exact next record
nerfd share --dry-run # prints the record, sends nothing
nerfd show last --public # the same record
nerfd export --public # every record you have ever sent
Stop, and delete
nerfd share off # stops all sending, immediately
nerfd init --remove # takes the hooks back out of your tools
nerfd privacy purge --yes # deletes the local database and the hook log
rm -rf ~/.nerfd # removes everything nerfd has ever written
To delete what was already shared: your reporter id is printed by nerfd privacy, and it is the key to every record you sent. A self-serve deletion endpoint is being built; until it ships, send the reporter ids to the contact address and the rows are deleted by hand within seven days. Deletion is real: the rows go and the next aggregate is recomputed without them.
Stored on your machine
Everything lives in ~/.nerfd, created mode 0700. These fields are kept there because the local scorecard needs them, and are never sent:
| first prompt | the first 300 characters, so the local session list is readable |
| touched files | absolute paths of files a session edited, used for the count |
| working directory | needed to re-measure code survival later |
| branch and commit | recorded at session start |
| your note | whatever you typed when you rated the session |
| line hashes | salted hashes of added lines, for the survival check |
| transcript path | where your tool keeps its own transcript |
nerfd privacy shows the size of each file and how many of each of these it is holding. If your home directory is backed up or synced, treat ~/.nerfd/local.db the way you treat your shell history.
Retention
On your machine: until you delete it.
On the server: raw reports for 13 months, then deleted. Weekly aggregates — counts, medians and intervals, carrying no reporter id — are kept indefinitely, because a historical record of how models moved is the entire point. Any access log in front of the application keeps addresses for at most seven days and is never joined to reports.
The server sees the IP address that sent a report, the way every HTTP server does. It is not stored in the reports table, not written to an application log, and not used for anything.
Fingerprinting, honestly
A record with no name in it can still be a fingerprint. A rare language, plus an unusual tool build, plus an unusual model id, plus a timestamp to the millisecond, can single out one person. The mitigations, stated so they can be checked:
- The reporter id rotates weekly. Derived from the random install id and the ISO week, so records can be deduplicated within a week but not chained across years into a profile. Lasting identity exists only if you choose it, by signing in for a verified badge.
- End times are bucketed to the hour, in UTC. Week-over-week analysis needs the week, not the millisecond, and an hour-of-day pattern gives away a timezone and a working day.
- Model ids are matched against a public catalogue. A custom id such as
acme-internal-finetunenames an employer, so unrecognised ids are not sent until you allow them. - Tool versions must look like versions, and repo languages must come from a known list. A wrapped
--versionbanner or a rare in-house file extension is otherwise an identifier. - Plan prices are catalogue prices or nothing. A free-typed dollar figure is small and sharp.
- Small cells are not published. Fewer than three sessions is unscored; fewer than ten never reaches a public tier.
Governance
- No money from model labs. Ever. Not sponsorship, not a data deal, not a grant. Every evaluator that took lab money stopped being worth reading.
- The collector is open source. The code that decides what leaves your machine is about forty lines with no dependencies. Read it instead of trusting this page.
- The scoring formula is published in the board footer, so a number you disagree with can be argued with.
- Open data, CC BY 4.0. The tier board, drift tables, weekly summaries and the JSON behind them are yours to use, chart and quote; just say where they came from. Raw per-session rows are published under the same licence once the fingerprinting mitigations above are all shipped — until then this site serves aggregates only.
Questions people at companies ask
Can my employer see this?
Not through nerfd. There is no admin console, no fleet view, no org enrolment, and no way to look anyone up. Your employer can see your machine and your network the way they always could. Nothing in the record tells them, or anyone, which of their staff sent it.
Does it read my code?
It reads your working tree to count lines, and turns each added line into a salted 20-character hash so it can tell later whether that line survived. The hashes stay in the local database. No line, no file name and no hash is ever sent.
Does it phone home if sharing is off?
No. One network call exists in the whole CLI, on the sharing path. With sharing off nothing reaches it. Block the endpoint at your proxy and watch nothing break.
What about Gemini CLI’s telemetry?
Gemini CLI ships its own OpenTelemetry exporter whose common attributes include the user’s email address. That is Google’s pipeline, under Google’s settings, and unrelated to nerfd. When nerfd adds Gemini CLI support it reads the local chat files and deliberately does not touch the OTel stream. Installing nerfd turns no other tool’s telemetry on or off.
Can I install this without asking legal?
That is the design target. The argument: derived metrics about your own sessions, no content, no identifiers, one endpoint, off with one command, and a collector small enough to read over a coffee. Send your reviewer to the section below.
For your security team
Every network call the CLI makes
| when | call |
|---|---|
a finished session with sharing on, a manual nerfd share, or a re-rating | POST https://nerfd.org/v1/reports — one JSON body under 2 KB, 6-second timeout, no retry, no cookies, no authentication header |
| install only | GET https://nerfd.org/install.sh and GET https://nerfd.org/dist/nerfd.tgz, via curl |
| planned, not yet built | a GET /v1/models or /api/show against a local model runtime you configured, to identify a local model. localhost or your own host only, never a third party |
Nothing else. No version check, no crash reporting, no telemetry, no CDN, no web fonts. This page and the board load no external assets.
Every process it spawns
git (rev-parse, ls-files, log, rev-list, diff) inside your project directory, and claude --version / codex --version. All with a timeout, all with stderr discarded.
Every file it writes
| ~/.nerfd/config.json | settings and the random install id, mode 0600 |
| ~/.nerfd/local.db | one row per session |
| ~/.nerfd/hook.log | hook errors only |
| ~/.nerfd/app/** | the CLI itself, if installed by curl |
| ~/.local/bin/nerfd, ~/.local/bin/ms | launchers, if installed by curl |
| ~/.claude/settings.json | hook entries merged in, with a timestamped backup written first |
| ~/.claude/commands/nerfd.md | the /nerfd rating command |
| ~/.codex/hooks.json | hook entries merged in, backed up the same way |
| ~/.codex/prompts/nerfd.md | the /nerfd rating prompt |
It writes nothing inside your repository, and never writes to your tool’s transcript files.
What the server stores
One append-only table: the JSON described at the top, plus the time it arrived. No IP column, no user-agent column, no account table, no cookies, and no third-party script on any page.