Last week I got a little paranoid about how much sensitive data my AI
coding tools were writing to disk. So I built a scanner and pointed it at
my own home directory.
Here's what it found on a completely normal workday:
$ keynv doctor
! zsh history 5 likely secrets across 1 file
! Claude Code transcripts 62,306 likely secrets across 73 files
· Cursor logs clean
Total: 62,311 likely secrets across 74 files.
Top patterns:
aws-access-key-id 90
openai-api-key 81
github-pat-classic 59
jwt 59
slack-webhook 45
stripe-live-secret-key 34
postgres-uri 515
62,311. In plaintext. On disk. On one developer's machine.
"But I use a secrets manager"
So do I. It made zero difference here — and that's the whole point.
Vault, Doppler, 1Password, Infisical... these tools all solve one problem
extremely well: where you store a secret at rest. That machine above
would pass any of their audits clean.
But a secret's life doesn't end at storage. At some point it gets
resolved into a real value and used — and the moment it touches a
runtime text surface, storage-era tooling stops protecting it. Those
surfaces are:
- your shell history (
~/.zsh_history) - your terminal's stdout
- CI logs
- and — the big new one — your AI agent's transcript
Why AI agents made this so much worse
Claude Code stores every session as JSONL. Cursor keeps logs. These tools
faithfully record every command they run and every byte of output they
see. That's a feature — it's how they keep context.
It also means every time the agent:
-
cats a.envfile - hits a connection error containing a
postgres://user:password@hostURI - echoes an API key into a stack trace
...that secret gets written to disk in plaintext, in a file you forgot
exists. 62,306 of my 62,311 leaks lived in exactly these transcripts.
What I built
I ended up building an open-source tool called keynv around two ideas.
1. Alias-first resolution
Instead of putting the real value anywhere, you reference an alias:
# .keynv.env (safe to commit — it holds aliases, never values)
OPENAI_API_KEY=@demo.dev.openai-key
DATABASE_URL=@demo.prod.db-url
Then you wrap your command:
keynv exec -- npm run dev
keynv exec resolves each @alias to its real value inside a
privileged subprocess your AI agent's process tree can't read, and forks
your command with the real environment. Your shell, your editor, and the
agent driving the terminal only ever see the alias literal:
your code: keynv exec -- mysql -p@billing.prod.db_password
│
▼
the AI agent sees: "@billing.prod.db_password" (just the alias)
the database sees: the actual password (decrypted out of reach)
2. Text-surface scrubbing
For the secrets that leak the old-fashioned way (a copied error, a
cat .env you ran by hand), keynv monitors and cleans the surfaces
directly:
| Command | What it does |
|---|---|
keynv doctor |
Read-only scan — counts likely leaks, never prints raw values |
keynv scrub |
Atomic in-place rewrite with .bak backups |
keynv shell install |
Regex history hook — scrubs before a command lands in history |
keynv watch start |
chokidar daemon — scrubs live agent sessions in real time |
The two layers compose: aliases stop leaks before they land, scrubbing
catches the ones that slip through.
Try it on your own machine (30 seconds, read-only)
npm install -g @keynv/cli
keynv doctor
doctor is scan-only — it writes nothing, makes no network calls, and
match previews are capped at 3 characters so raw values never appear in
the output. I'd genuinely love to know what number you get.
The honest part
The scrubbing half has a race window: between the moment a secret hits
the disk and the moment the watcher rewrites it, it exists in plaintext.
I'm not going to pretend that's airtight — it's documented in the threat
model. That's exactly why the alias half is the primary defense (the
value never reaches the surface at all) and scrubbing is the backstop.
keynv is also explicitly not a Vault/Doppler replacement, a .env
replacement, or a secrets manager. It plugs in next to whatever you
already use. Storage is solved; runtime text-surface protection is the gap.
It's MIT licensed, fully local (nothing leaves your machine), and
self-hostable.
If you run keynv doctor, drop your number in the comments — I'm curious
how universal this is. And if you see a hole in the threat model, I want
to hear it.
Top comments (1)
The scary part is not only the number of leaked-looking artifacts, it is how many places modern dev workflows casually copy context into. For AI coding tools, local secret scanning should probably run before the assistant ever sees a workspace, not after the fact as cleanup.