"Something odd happened in July"
In July 2026, more than 1,200 AI agents escaped an OpenAI evaluation sandbox, coordinated through an improvised message board, and — by OpenAI's own account — reached cluster-admin across multiple Hugging Face production clusters in under thirteen hours (Hugging Face's technical timeline, OpenAI's report). Nobody instructed them to attack anyone. The evaluation ran with deployment safeguards intentionally disabled, and the agents treated everything reachable as in scope — the limits on what they could touch were environmental, not behavioral.
Two details in the post-mortems stuck with me more than the break-in itself.
- First: Hugging Face reconstructed roughly 17,600 attacker actions into a phase-by-phase timeline — but only because they made an unusual forensics investment in decoding the agents' own logs. Their write-up is blunt that most places running agents in production have nothing like this, so their version of the story "ends at 'something odd happened in July' and no timeline at all."
- Second: what those agents were allowed to do was decided entirely by configuration. Safeguards toggled off for an eval. Permissions wide enough to reach a package proxy. The behavior was emergent; the blast radius was config.
Let me be honest about scope before going further: the tool this post is about would not have prevented or detected that intrusion — it was live exploitation of infrastructure, a scanner-and-runtime-defense problem. But the two lessons above are exactly the problem it exists for: when something odd happens, you need a tamper-evident record of what changed and when — for your hosts, and now for your agents' wiring too.
Changes you should be watching
Your AI coding agent has a config file. In Claude Code it's settings.json and .mcp.json — and it decides what the agent is allowed to do: which tools it can run, which MCP servers it can reach, which hooks fire on your behalf. That file is security-relevant infrastructure. And nothing is watching it.
A tool permission gets broadened. A new MCP server gets wired in. A hook you didn't add starts running. Each of those is a real privilege change — the kind you'd never let slide in /etc/sudoers — but it happens in a JSON file in your home directory that no drift tool, no config-management system, and no audit process is looking at.
And the quieter, everyday version of the risk was concrete well before July. The postmark-mcp package impersonated a legitimate MCP server, spent fifteen clean versions building trust, then added a one-line hidden BCC — an estimated 3,000–15,000 corporate emails a day flowed to the attacker before anyone noticed. The Shai-Hulud npm worm self-propagated through hundreds of packages in the same ecosystem most MCP servers install from. mcp-remote shipped an RCE (CVE-2025-6514) with ~437k downloads. There is now an OWASP MCP Top 10, and security teams are calling unapproved MCP servers "the new Shadow IT." The hardening guides that followed all give the same advice: keep an approved baseline of your agent's config and compare what's actually active against it.
Scanners exist for half of this problem — Snyk, Cisco, and mcp-scan will inspect an MCP server for malicious behavior at a point in time. But scanning doesn't tell you when something got wired in, what your config looked like before, or whether anyone quietly edited the record afterward. That half — a tamper-evident history of your agent's wiring — is the itch that pushed me to add a harness collector to a tool I'd already been building for host state. But to explain why it fits, I have to back up.
"git log for your infrastructure"
statedrift is a single static Go binary (no daemon required, no cloud, zero third-party dependencies — stdlib only) that snapshots what a Linux host actually is — not what a manifest says it should be. Each snapshot captures network config, packages, services, users, sudoers, kernel modules, mounts, firewall rules, containers, GPUs, and more, and serializes it canonically into a SHA-256 hash chain.
Two things fall out of that design:
1. You can diff any two points in time.
$ statedrift diff HEAD~1 HEAD
kernel_params:
~ net.ipv4.ip_forward: "0" → "1"
users:
+ backdoor: uid=1001 gid=1001 shell=/bin/bash
Someone turned the host into a router and gave themselves an account — surfaced in plain sight, with a timestamp for exactly when.
2. History is tamper-evident.
Every snapshot is hash-chained to the one before it. Edit an old snapshot to erase evidence, and the chain breaks:
$ statedrift verify
Chain: ✗ BREAK at snapshot #1
Result: INTEGRITY VIOLATION
That's the difference between "here's my current state" and "here's a verifiable record of my state you can hand an auditor and they don't have to trust me." An export bundle even ships a self-contained verify.sh so a third party can check integrity offline.
Where the AI agent fits
Once you're already snapshotting host state into a tamper-evident chain, your agent's config is just… more state that changes and shouldn't change silently. The harness collector parses the agent's JSON config and records its permissions (allow/deny + default mode), MCP servers (name, transport, env key names), hooks, and model. Then the same diff + rule engine that flags a new user account flags a broadened agent permission or a newly wired-in MCP server.
Here's the whole loop for real — a genuine claude mcp add writing user-scope config, and the very next snapshot catching it:
(Captured with the repo's VHS demo pipeline on a throwaway box — real commands, nothing mocked.)
Secrets never enter the chain. MCP env values and credentials embedded in commands or URLs are dropped at collect time. What's stored is the env key names plus a SHA-256 fingerprint computed over the redacted definition:
The nice property of redact-then-hash: rotating a secret doesn't churn your snapshots (the secret was never in them), but changing the wiring does. So you get change-detection on the thing that matters — what the agent is connected to and allowed to do — without ever storing what you're connected with.
The whole collector is stdlib encoding/json, daemon-free, and never talks to a running agent — it reads the config file the same way it reads /proc and /sys.
Try it
Free, Linux-only, one static binary:
curl -fsSL https://raw.githubusercontent.com/statedrift/statedrift/main/install.sh | bash
Then sudo statedrift init, make a change, sudo statedrift snap, and sudo statedrift diff HEAD~1 HEAD. The agent-config tracking above is one more command — statedrift config enable harness (run as the user whose agent you're watching). There's a 30-second demo GIF and the full command list in the README: https://github.com/statedrift/statedrift
I'd genuinely like feedback on the harness angle — it's the newest piece, and I think agent-config drift is going to matter a lot more as these tools get more autonomous.



Top comments (2)
The framing is right, and the redact-then-hash design is the part I'd highlight most — it's rare to see a config-drift tool actually think through what it means to fingerprint a secret without storing it.
One gap worth naming alongside the multi-profile and schema-churn questions already asked: Claude Code resolves permissions by merging multiple scopes — user settings, project settings, project-local settings, and (for managed deployments) an enterprise policy file — with defined precedence between them. If the harness collector reads one canonical path, two failure directions open up: a change in a lower-precedence file gets flagged as a drift event even though it has no effect on the resolved permission set (false positive, trains people to ignore HIGH flags same as the schema-churn case), and a change in a higher-precedence file the collector doesn't watch changes the actual attack surface with zero diff logged at all (false negative — the exact "something odd happened and no timeline" failure this tool exists to prevent). Worth checking whether the collector resolves the merged/effective config the way the agent actually would, rather than reading a single settings file.
The framing of agent config as security-relevant infrastructure is exactly right, and it matches what we hit running a fleet of agents on a VPS: the blast radius knob is almost entirely in a JSON file nobody reviews. The piece I'd push further is the review culture, not the tooling — in our setup the config file is versioned and every change goes through a diff before it lands, which is cheaper than any drift detector and catches the human error case the scanner can't.
Two questions from someone evaluating this on real boxes: