Know what you've already leaked to AI. A scanner for the credentials sitting in your coding agents' local logs.
Install & run
No install step. If you can run an agent CLI, you already have Node.
$ npx spilled
It reads local transcripts, prints a report, and exits. It makes no network calls and needs no API key.
Reading the output
Four blocks, in order of how much they should worry you.
How the data actually left
Tool call counts. If Bash dominates, most of your exposure arrived through command output rather than file reads - which is exactly what other scanners miss.
Exposure by project
Files touched, sessions, date range and finding count per project, ranked worst-first.
Secrets found
Masked value, rule, affected projects, first-seen date, and whether it is STILL LIVE.
Possible attempts to steer your agent
Only appears if something in a tool result looked like an instruction aimed at the model.
Flags
| Flag | Effect |
|---|---|
--disk | Transcript disk usage, biggest sessions, reclaimable space |
--explain | Semantic triage of exposed files (needs a key) |
--local | Use local Ollama for --explain instead of the API |
--model=<id> | Override the classifier model |
--paranoid | Also sweep high-entropy strings. Noisy - see below |
--no-git | Skip the liveness check |
--json | Machine-readable output for CI |
The two ledgers
Each transcript record carries cwd, gitBranch, sessionId and timestamp - forensics-grade attribution. Inside, content blocks are typed. A representative session:
1913 tool_use 1913 tool_result 88 text 87 thinking
Machine-generated content outnumbers what the human typed by more than 20 to 1. You are not leaking what you write - you are leaking what the agent reads on your behalf, and you never see it.
spilled extracts both: tool_use inputs (what was asked for) and tool_result plus toolUseResult bodies (what actually came back). The second is where leaked content physically lives.
Still live vs already rotated
Same finding, completely different urgency. For every affected repository, spilled makes one pass over the working tree and reports which values are still present.
Values are matched in-process rather than by shelling out to git grep -e <value> - command arguments are visible in the process list, and a security tool must not broadcast the secret it just found.
The three verbs
| Bucket | What it means | Action |
|---|---|---|
| rotate | Live credentials | Rotate. The leak is fully undone. |
| change | Protected by obscurity - token schemes, fraud rules, thresholds, validation logic | Change the logic. The leaked knowledge dies with it. |
| cannot-fix | Trade secrets, customer data, plans | No code change undoes it. Record it and harden. |
Sorting change from cannot-fix is a security judgment on real code, not a pattern match - which is why it lives behind --explain.
Precision over recall
The entropy sweep is off by default, and that is a deliberate product decision rather than a tuning gap. Measured on a real 126 MB corpus:
| Pass | Findings | True positives |
|---|---|---|
| Named rules | 4 | 4 |
| Entropy sweep | ~130,000 | ~0 |
Base64-encoded binaries are the main culprit: an embedded image shatters into thousands of high-entropy fragments. Blobs are stripped before any entropy work, but the ratio still doesn't justify the default. A scanner you stop trusting on the first run is worse than no scanner.
Hijack detection
Prompt injection drives most agentic-AI security failures in production, and the worst variant is tool poisoning - an instruction hidden in a tool description, package, config or remote MCP server that fires on every invocation, silently, for every user, until somebody notices.
Functional tests never catch it, because a hijacked run completes successfully. The transcript is the only place the evidence survives. spilled flags override attempts, role reassignment, prompt extraction, hidden HTML comments, pipe-to-shell, exfiltration and concealment instructions.
Disk bloat
$ npx spilled --disk
Agent transcript directories grow without bound. Reported cases include multi-GB single sessions from duplicated progress entries, and runaway task output filling a disk entirely - at which point the cascade can take out settings and auth with it.
--disk reports total size by agent, the biggest sessions, how much is older than 30 days, and whether you have a cap configured:
// ~/.claude/settings.json
{ "cleanupPeriodDays": 14 }
Semantic triage
$ export ANTHROPIC_API_KEY=sk-ant-... $ npx spilled --explain # or fully local $ npx spilled --explain --local
Reads the exposed files still on disk and sorts them into the three buckets. Batched, run at low effort - this is classification, not reasoning. You bring the key; the project never sees it and never bills you.
Supported agents
| Agent | Location | Status |
|---|---|---|
| Claude Code | ~/.claude/projects | Verified against real data |
| Codex CLI | ~/.codex/sessions, ~/.codex/history | Verified against real data |
| Cursor | ~/.cursor/chats | Permissive fallback |
| opencode | ~/.local/share/opencode/storage | Permissive fallback |
Where a record shape isn't pinned down, the parser sweeps the whole record rather than skipping it. A redundant scan is cheap; a missed leak is not.
JSON output
$ npx spilled --json
{
"scanned": { "sessions": 41, "megabytes": 126.4, "calls": 5325, "results": 10616 },
"findings": [
{ "rule": "DB connection string", "verb": "rotate", "masked": "post****st",
"stillLive": true, "projects": ["/Users/me/data-lift-compass"],
"firstSeen": "2026-08-31" }
]
}
Exit code is always 0 - this is a report, not a gate. Filter on stillLive if you want CI to fail.
Privacy
- No network calls unless you explicitly pass
--explainwithout--local. - No telemetry, no account, no server. There is nothing to sign up for.
- Raw secret values live in memory only, purely to run the liveness check, and are masked before any output.
- Nothing is written to disk. spilled only reads.
Limits, stated plainly
It cannot see browser pastes. Code dropped into claude.ai or chatgpt.com leaves no local trace, and that is probably the most common way code leaks. A tool that reported "here's everything that leaked" while blind to that would produce false confidence - worse than no tool.
It also cannot see a teammate's machine, or transcripts already deleted by cleanup.
Read a clean report as "your CLI agents are clean", nothing more.
FAQ
Does it send my code anywhere?
No - not unless you ask for --explain with a key, and even then it sends short snippets of files that were already exposed. --local keeps it entirely on your machine.
Why is my repo listed with zero findings?
Because it's clean. Ranked listings that only show hits make it impossible to tell "no problems" from "not scanned".
Can it delete or redact the secrets it finds?
Not yet. It reports; you act. Rotating a live credential is always the correct first move, and redacting the transcript afterwards does not un-send it.
I found a secret. What now?
Rotate it, then check --explain for anything in the change bucket. Then put blind in front so the next one never leaves.