spilled / documentation

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.

Nothing is uploaded, and no secret value is ever printed in full or written to disk. Values are masked at the moment of capture.

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

FlagEffect
--diskTranscript disk usage, biggest sessions, reclaimable space
--explainSemantic triage of exposed files (needs a key)
--localUse local Ollama for --explain instead of the API
--model=<id>Override the classifier model
--paranoidAlso sweep high-entropy strings. Noisy - see below
--no-gitSkip the liveness check
--jsonMachine-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

BucketWhat it meansAction
rotateLive credentialsRotate. The leak is fully undone.
changeProtected by obscurity - token schemes, fraud rules, thresholds, validation logicChange the logic. The leaked knowledge dies with it.
cannot-fixTrade secrets, customer data, plansNo 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:

PassFindingsTrue positives
Named rules44
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

AgentLocationStatus
Claude Code~/.claude/projectsVerified against real data
Codex CLI~/.codex/sessions, ~/.codex/historyVerified against real data
Cursor~/.cursor/chatsPermissive fallback
opencode~/.local/share/opencode/storagePermissive 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

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.