API keys in your coding agent transcripts

every key an agent printed is still on disk, in a plaintext file nothing scans

A coding agent writes down everything that happens in a session: what you typed, what it ran, and what came back. When it runs cat .env, or you paste a failing deploy with the connection string still in it, the value goes into the transcript as-is. Claude Code writes those transcripts as JSONL under ~/.claude/projects, Codex under ~/.codex/sessions, and most other agents keep an equivalent file or database under your home directory (where each one lives). Claude Code and Codex store the value exactly as it was printed.

What one machine was carrying

One developer machine, about 2,400 sessions over a year, from 20 of the agents deja reads. Counting only findings that match a named provider or a protocol shape:

KindFound
Credentials in a URL (database and service connection strings)20
Password on a command line13
Bearer tokens8
Cookies8
JWTs6
Private keys5
AWS access keys and secrets8
OpenAI keys4
GitHub tokens3
Google API keys3
Hugging Face tokens2
Other passwords2
Anthropic key, Stripe key1 each

84 in 42 sessions. On top of that, about 3,500 weaker matches — api_key = "…"-shaped assignments and high-entropy strings — that are not listed because most of them are not secrets: the largest group turned out to be a WireGuard dump of public keys. Nothing here was checked against a provider, so some of the 84 are expired or test values. The point is that nobody knew they were there.

Why it matters

These are ordinary files in your home directory, which is where backups, dotfile sync and IDE settings sync go looking. Claude Code sweeps its transcripts after 30 days by default (cleanupPeriodDays, see what the sweep does); the other stores are yours to clean up. A memory tool that uploads transcripts to a hosted service ships those values with them.

Reproduce it

A transcript with AWS's documented example keys, a GitHub token and a database URL, written the way Claude Code writes a session where you pasted an env and the agent ran cat .env:

$ deja index
$ deja secrets
4 credentials in your agent history, in 1 session

  claude · 2026-03-02 · work/app
    aws-access-key, aws-secret, github-token, url-credentials
    ~/.claude/projects/-work-app/s1.jsonl

these values are in the source transcripts, not in deja's copy — deja redacted its own.
rotate the live ones; the files above are yours to edit or delete.

The values are still in s1.jsonl; deja's own index holds [REDACTED] in their place, which is why deja secrets can name the kind and the file but cannot print the value.

Finding yours

# install, then index what is already on disk (local, no network)
brew install deja-vu        # or: curl -fsSL https://raw.githubusercontent.com/vshulcz/deja-vu/main/install.sh | sh
deja index
deja secrets                # sessions and kinds, never values
deja secrets --json --limit 0
deja secrets --scrub --dry-run   # what a rewrite would change, changing nothing

It reads the transcripts of 34 agents, groups findings by session so one pasted URL repeated over ten turns is one line, and never prints a value.

What to do with the list

  1. Rotate anything still live. Deleting the file does not help if the key was also in a backup.
  2. Or let deja rewrite them. deja secrets --scrub --dry-run says which transcripts it would rewrite and what it would replace; deja secrets --scrub does it, putting the same [redacted:kind] marker where the value was and leaving the original at <file>.deja-backup-<stamp>. It only touches the kinds the report names, skips a session an agent is in, and prints how many findings it could not reach — a store with no per-session file (opencode, Zed, Cursor IDE, Copilot Chat) cannot be rewritten, and a later run on the same machine put 36 of its 88 findings out of reach that way. The index still holds the old records until the next build, so run deja index afterwards and the rewritten ones drop out of the report then.
  3. Delete or edit the files it names. Per agent: Claude Code, Codex, Cursor, Gemini CLI.
  4. Keep them out of what syncs. Exclude ~/.claude/projects, ~/.codex/sessions and the others from dotfile repos and cloud-synced folders.
  5. Stop the next one. Load secrets into the environment instead of reading them back through the agent, and ask it not to print env files.

Found this useful? Star deja-vu on GitHub.