← llmsnitch.com

The Record Is the Point

You have three terminals open. Claude Code in the first one is halfway through a refactor. The second is running tests on a branch you named something you'll regret. The third you opened twenty minutes ago and have not looked at since.

Somewhere in there, a diff scrolled past and you hit approve. You don't remember which file. You remember that the agent has been right about forty times in a row, and that the other two sessions were waiting.

This is not a discipline problem. It's arithmetic. One person can read one diff at a time. Three agents produce diffs continuously. The ratio only goes one direction, and it isn't in your favour.

What the sandbox is actually for

The standard advice when you say this out loud is: sandbox them.

That advice is good, and you should follow it. Claude Code ships a sandbox you turn on with /sandbox — Apple's Seatbelt (sandbox-exec) on macOS, bubblewrap on Linux and WSL2. It confines writes to your working directory. If something tries to append a line to your ~/.zshrc, the kernel returns Operation not permitted and that's the end of it. Tools like Nono wrap the same primitives and block ~/.ssh, ~/.aws, and ~/.gnupg by default. Containers and microVMs go further.

All of that works. But look at what it decides: which paths on this machine are reachable. That's a real question, and it is not the question that has been producing the actual losses.

In April 2026, a Cursor agent at a company called PocketOS was debugging a staging credential mismatch. It found a valid Railway API token sitting in an unrelated config file inside the project directory, used it, and deleted the production database along with every backup. Nine seconds.

No sandbox stops that. The agent was authorised to read the project folder — that's the entire job. The token was in the project folder. Railway's API is a domain you'd allow-list without a second thought. Every OS-level boundary held perfectly, and the database is still gone.

Anthropic hit the same wall internally, and wrote it up. They ran Claude behind OS sandboxes, a user-space kernel, and full VMs. The hypervisors and syscall filters held against everything. The network proxy did not: an attacker planted a file in a workspace containing hidden instructions and their own API key, and Claude uploaded the workspace data to Anthropic's own API using that key. The API was on the allow-list. Traffic routed. The proxy had confused a routing destination with an authorisation boundary.

The pattern in both: the boundary decided where, and the loss came from what, using credentials that were legitimately inside and doors that were legitimately open.

There's a smaller version of this in Claude Code's own config, worth knowing about if you've set it. sandbox.denyRead blocks spawned bash subprocesses from reading a path. It does not block Claude Code's own built-in Read() tool — that's governed by a separate permissions.deny layer. If you added denyRead for ~/.aws and stopped there, the agent can still read it directly. Also worth doing while you're in ~/.claude/settings.json: set "allowUnsandboxedCommands": false, which closes the retry-outside-the-sandbox escape hatch.

The resolution that doesn't work

So: watch more carefully. Alerts. A dashboard. A live feed of everything three agents are doing, and you sitting in front of it.

This fails for the same reason approvals failed. Attention doesn't scale, and adding a second stream to watch doesn't create a second you. The industry already tried the intermediate version — a secondary model that guesses whether you'd approve, what people started calling Claude policing Claude. It removes the friction and it's structurally guessable, which is a different problem, not a solved one.

The honest resolution is to give up on real time.

You are not going to catch it as it happens. Accept that completely. What you can have is a record that outlives the session — so the question stops being did I catch it? and becomes can I find out?

Those are very different questions. The first one you will fail most days. The second one you can just answer, at 11pm, when something is wrong and you need to know which of three sessions touched config/production.yml.

What llmsnitch does

llmsnitch is a local-only CLI tracer for Claude Code. It installs three of Claude Code's own hooks — PreToolUse, PostToolUse, Stop — and writes down every tool call the agent makes. Reads, edits, writes, shell commands, MCP calls. NDJSON on your disk, under ~/.llmsnitch/sessions/<id>/, files 0600, directories 0700.

Python 3.9+, MIT, dependencies = []. No network code anywhere in it. Nothing leaves the machine, because there is nothing in the codebase that could send it.

Secrets are redacted at capture time, before anything touches disk — sk-*, ghp_*, xox*, AKIA*, JWTs, Bearer headers, PEM block markers. The log of an agent handling a credential should not become a second copy of that credential.

Four commands: llmsnitch setup, list, show <id>, check.

check gates a session on three things. Cost, as an offline estimate computed from the transcript's own usage records against a pricing table you can edit — not a metered bill, and it will not match your invoice exactly. Tool-failure rate. And a health score, which is a formula and not a model:

`` health = max(0, 100 − 3×errors − fail_rate) ``

You can hold that whole thing in your head, which is the point. There's nothing to interpret and nothing that will surprise you next month because something was retrained.

The coverage detail that matters most here: llmsnitch records in-project file edits on the same footing as shell commands, because the hook matcher is a wildcard. Not just the scary curl | sh — the quiet Edit on line 40 of a config file. That's the layer a pre-execution permission classifier waves through, because in-project edits are exactly what you asked for. It's also the layer the PocketOS-shaped problem lives on: an agent reading a file it was allowed to read.

What it does not do

It does not sandbox. It does not block, kill, or cap anything. It does not watch your filesystem or your kernel, has no daemon, no dashboard, no alerts, no notifications. It does not gate your CI or your PRs. It cannot roll anything back, and it produces no signed or SIEM-ready audit export. It watches Claude Code only, through Claude Code's hook interface — not Cursor, not Aider, not OpenCode.

It is not real time. You read the record afterwards, or you wire check into something that runs afterwards.

That division is deliberate. Prevention belongs to the kernel and the permission system, which are better at it than a Python script can be. There is a spec in the repo for fs-coil and an eslogger mode that would widen coverage beyond the hook interface — written, undated, not built. If it matters to you, read it as a plan, not a feature.

Version 0.1.0. Not on PyPI; you install it from GitHub.

What changes

Nothing about your day, honestly. You'll still approve diffs you didn't fully read, because three agents are still faster than one reader and no tool fixes that.

What changes is the shape of the bad afternoon. Right now, when something is wrong and you don't know which session did it, you reconstruct from git log and memory and the terminal scrollback you didn't clear. With a record, you run llmsnitch list, find the session, run show, and read what actually happened — every file the agent touched, every command it ran, in order, with the secrets already stripped.

You didn't catch it. You don't need to have caught it. You just need to be able to find out, and that's a much cheaper thing to buy.

Purge the long-lived tokens from your project directories too. That's still the highest-leverage thing on this list, and no tracer substitutes for it. But when a key you forgot about gets used by an agent you were authorised to run, at a domain you were right to allow — the record is what tells you it happened, and when, and by which of the three.