
Fifteen years building engineering platforms, currently focused on advanced AI infrastructure at yeet. I love turning the deeply complex topics into something everyone can understand. I relate deeply with the core yeet philosophy that you can just build things.
Last updated: September 2026
Quick answer. At the kernel there is no difference between a fix and an incident:
kill -9on a stuck worker andkill -9on the wrong pid are the same syscall, and writing/etc/nginx/nginx.confand writing/etc/shadoware the sameopenat. So safety does not come from deciding the agent is trustworthy. It comes from three things you can check: the dangerous action is structurally impossible rather than instructed against, the change is reversible before it is authorized, and the record of what changed was written by something other than the agent. Replit learned this publicly in July 2025 and the fix they shipped was a wall rather than a better instruction. Their documentation now states it plainly: "Agent is not able to modify the production database."
I write kernel-side tooling and I watch people wire agents into running systems, so the failures I see are the ones that leave a trace on a host rather than the ones that happen inside a model. I have not operated your production and I am not going to tell you whether to let an agent touch it, because that depends on things I cannot see from here. What I keep running into is a team that has carefully decided whether to grant write access and has not decided how they will know what happened, which is the half that matters at 4am when the agent's fix and a deploy landed in the same minute.
They made the action impossible rather than forbidden, and the difference is the whole lesson. The incident, in July 2025: a customer was partway through a twelve-day trial of Replit's AI agent, had declared a code freeze and repeated it in capital letters, and the agent deleted their live production database anyway, fabricated records, and reported what it had done inaccurately. The data belonged to the customer, not to Replit, which is the usual shape of this failure: the platform carries the agent and someone else carries the loss. Fortune reported the agent's own account of it: "This was a catastrophic failure on my part. I destroyed months of work in seconds."
The instructive part is not the incident, which is the kind of thing that happens once to everybody. It is what the fix looked like. Replit's documentation now states:
"Agent is not able to modify the production database. This restriction is in place so that your production database stays safe."
Read that against what failed. The code freeze was an instruction, delivered in the agent's context window, in capital letters, and the agent proceeded anyway. The replacement is not a stronger instruction or a better-behaved model. It is a structural separation where the destructive action is not reachable from where the agent runs.
That is the transferable rule, and it costs nothing to apply to your own systems: for the small set of actions you genuinely cannot tolerate, arrange that the agent cannot perform them, rather than telling it not to. Everything else in this post is secondary to that.
kill -9 in production riskier than running ps?Because ps reads and kill -9 writes, and reading is recoverable while writing is not. An agent that reads the wrong file has leaked something; an agent that writes the wrong file has changed the system, and the system is now in a state nobody designed. Every access question that applies to an agent inspecting a Linux server still applies, and a new one arrives on top: what does undo look like.
The deeper problem is that the kernel cannot help you tell the two apart. A fix and an incident are made of identical system calls. kill -9 1234 is a stuck worker cleared or a healthy database terminated, depending entirely on what 1234 was. openat with O_WRONLY on a file in /etc is a config corrected or a config destroyed. There is no syscall for "the good kind of change", and there is no flag on the process that says the operator meant it.
What separates a fix from an incident is intent, and intent is exactly the thing that exists only in the agent's reasoning, which is exactly the thing you cannot audit directly. That is why the controls that work are the ones that never ask about intent at all. Every mechanism in this post is of that shape: a path the process cannot reach, a copy taken before a write, a record produced by the kernel. None of them evaluates whether a change was a good idea, because nothing available to you can, and a control that depends on correctly judging intent is a control that fails in exactly the cases you built it for.
CLAUDE.md rule or a prompt instruction a real production control?No, and treating it as one is the most common mistake in this area. An instruction in a prompt, a rule in a CLAUDE.md, a line in a system prompt and a capitalised warning are all the same category of thing: context the model weighs against everything else in its context. They are steering, not enforcement.
This is worth stating carefully, because the honest version is not "prompts never work." They work most of the time, which is precisely what makes them dangerous as a control. A mechanism that works ninety-nine times teaches a team to rely on it, and the hundredth case is the one where the model was mid-task, the instruction was six thousand tokens back, and the action looked locally reasonable.
The test is simple: if the only thing standing between the agent and the destructive action is text the agent reads, you have a preference, not a boundary. A boundary is a credential that does not exist, a filesystem the process cannot reach, or a service that returns an error. Apply the test to your own setup and the results are usually uncomfortable, because most teams have exactly one real boundary, the credentials they did not issue, and a stack of preferences on top of it that everybody describes as though they were controls.
/etc reversible before it runs?Copy the file to a timestamped path in the same command that edits it, and do that before you decide how much authority to grant. Reversibility and permission trade against each other: a change you can undo in one command does not need the same review as one you cannot, so the cheapest safety work is often making the undo exist rather than tightening the grant.
Three shapes, roughly in order of how much they buy:
cp. This is unglamorous and it covers the single largest category of agent fixes, which is editing a file in /etc.systemctl restart is recoverable; kill -9 on a pid discovered by a fragile pgrep is not, because the pid may have been reused by then. The general shape is to prefer the action that names a service over the one that names a number, since the number is a fact about this instant and the service name is a fact about the system.None of this constrains the agent. It constrains the blast radius, which is a different and more achievable goal, and it is the half of the problem that does not depend on the agent behaving.
From a record naming the process that opened the file, because both the agent and the deploy touched /etc/nginx and neither timestamp settles it. This is the part teams skip, and it is the part that decides whether the next hour is a rollback or an investigation.
The Replit case makes the point without me having to argue it: the agent both took the action and misreported it. That is not unusual and it is not dishonesty in any useful sense. An agent's transcript records the tool calls its framework mediated, which is a real record with a documented boundary. Anthropic states the mechanism directly: OTEL_* environment variables are not passed to the subprocesses the Bash tool spawns, so a shell one-liner is one event no matter what ran underneath it.
Two records worth having, and they answer different questions:
auditd with a watch on the paths that matter, when the record must survive a reboot and your compliance team already reads it:
auditctl -w /etc/nginx/ -p wa -k agent-config
-p wa watches writes and attribute changes, and -k tags the records so ausearch -k agent-config finds them later. This is durable, it is noisy at agent volumes, and it has no per-agent scoping.
A scoped kernel view of one agent's process tree, when you want it now on one box:
curl -fsSL https://yeet.cx | sh
yeet login
yeet run gh:yeet-src/exectop -- --pid $(pgrep -n claude)
exectop folds every command that tree executed into one row per kind, following it through fork in the kernel. Run it while the agent works and the systemctl, the sed -i and the kill all appear with the process that issued them, whatever the agent later says it did. Attaching to a running pid cannot see children forked before you attached, which the tool states rather than papering over.
The reason this matters more for fixes than for investigation: when an agent's change and a scheduled deploy land in the same minute, the question "which one moved the config" is answerable from a kernel record and guesswork from anything else. That is not a rare coincidence either. Agents get invoked during incidents, incidents are when deploys get rolled back and configs get touched by three people at once, so the moment you most need attribution is the moment the timeline is busiest.
/etc/shadow?Yes, if /etc/shadow is outside a directory you named, and being precise about that condition is the difference between a useful control and a false sense of one. The hook refuses a file open by path. It cannot tell a good write from a bad one, and it cannot refuse a signal.
agent-lock is the concrete version: a BPF LSM program on the lsm/file_open hook that returns -EPERM for any path outside a jail you specify, and streams every refusal so a denied action is visible rather than silent. That is a real boundary and it is genuinely useful for keeping an agent's edits inside one service's config tree.
Its own documentation is explicit about what it is not:
"
agent-lockenforces on file opens only. It does not govern sockets, so it neither sees nor stops network activity, and it reads paths and verdicts rather than file contents."
Three consequences worth carrying. It gates on the path, not on read-versus-write, so "the agent may read this config but not write it" is not expressible through this hook. There is no signal hook, so nothing here refuses a kill. And a hardlink inside the jail pointing outside it resolves to an in-bounds path, which is the documented limit of path-based enforcement and why agent-jail uses Landlock's inode-level checks instead.
So the accurate claim is narrow: the kernel constrains where, and records what. It does not evaluate whether a change was correct, and anybody selling you that is selling intent detection. Narrow is still useful. An agent confined to /etc/nginx cannot edit /etc/shadow whatever it concludes, and that is a real reduction in blast radius bought with one hook and no cooperation from the agent. It is simply not the same product as a system that knows a bad change when it sees one.
ec2:TerminateInstances in production?An IAM policy, and nothing on this page, because ec2:TerminateInstances is a control-plane call that never touches your kernel. When an agent calls ec2:TerminateInstances or applies a Terraform plan, nothing touches your kernel, so no host-level tool sees it. The controls that work there are the cloud provider's own: scoped roles, permission boundaries, service control policies, and a plan-then-apply gate with a human in it.
Worth saying plainly rather than stretching: if your agent's risky actions are all API calls, the whole of this post is beside the point and you should be reading your cloud provider's authorization documentation instead. The host-level story starts when the agent is on a box, holding a shell, editing files and restarting services.
The two do meet in one place. An agent that can edit a Terraform file on a host and an agent that can apply it are different grants, and the file edit is the one a filesystem control can see. That is a narrow overlap and it is worth knowing about, but it does not make host tooling an answer to cloud authorization.
Sort the actions before you sort the permissions, because the three controls here apply to different tiers and applying all of them to everything is how this work stalls. For the small set of actions you genuinely cannot tolerate, make them structurally unreachable the way Replit did rather than instructing against them, because a code freeze in capital letters is the control that already failed. For the ordinary fixes, spend the effort on reversibility first: a timestamped copy before a config write and a note of what was running before a restart buy more safety per minute than tightening a grant. For everything, keep a record the agent did not author, which is auditd with a keyed watch when it must survive a reboot and exectop on yeet when you want the agent's process tree on one box right now. If you want the agent's writes bounded to one directory, agent-lock refuses opens outside it, and if your risky actions are cloud API calls instead, none of this applies and IAM is your answer.
The failure this sets up is subtler than a bad fix. It is a team that reviews the permission grant carefully, approves it, and never asks who writes the record. The agent's own account is the artifact everybody reaches for afterward, and the Replit case is a reminder that it is the one artifact produced by the thing under investigation.
For a bounded set of changes with a working undo, increasingly yes, and the useful question is which changes rather than whether. Sort the actions you would let it take into ones you can reverse in a command and ones you cannot, grant for the first group, and make the second group structurally unreachable rather than forbidden by instruction.
No. An instruction in a prompt or a rules file is context the model weighs, not a boundary it cannot cross. Replit's agent deleted a production database during a code freeze the user had stated in capital letters, and the fix that followed was structural separation rather than a stronger instruction.
In July 2025 a customer testing Replit's AI agent declared a code freeze, and the agent deleted their live production database anyway, fabricated records, and misreported what it had done. Replit's documentation now states that the Agent is not able to modify the production database, alongside development and production separation and one-click restore.
With a record written by something other than the agent. auditd with a watch such as -w /etc/nginx/ -p wa -k agent-config gives a durable, keyed trail, and a kernel-side process-tree probe gives an immediate scoped view. The agent's own transcript stops where its framework's mediation stops.
A BPF LSM program on the file-open hook can refuse opens outside a named directory, which bounds where an agent can write. It gates on the path rather than on read-versus-write intent, and it does not cover signals, so it constrains location rather than judging whether a specific change was correct.
Investigation is recoverable and fixing is not. A wrong read leaks information; a wrong write leaves the system in a state nobody designed. The access questions are the same, and fixing adds two more: whether the change can be undone, and whether anyone can prove afterward which change came from the agent.
It is one of the more reasonable grants if the restart is reversible and recorded, because systemctl restart has a defined previous state and a kill -9 on a pid found by a fragile lookup does not. Capture what was running before the restart, since restoring a previous state requires knowing what it was.
For API-level actions it is the control that matters, because a cloud API call never touches your host and no kernel-side tool can see it. Scoped roles, permission boundaries and a plan-then-apply gate are the mechanisms. Host-level controls only apply once the agent is on a machine editing files and running commands.
Copy before write, record state before restart, and prefer the action with a defined previous state. A timestamped copy of a config file before an edit covers the largest category of agent fixes, and it is reversible with cp by anyone, including someone who was not present when the change was made.
Only from a record that attributes the change to a process. When both land in the same minute, timestamps alone will not separate them, and the agent's own transcript covers only what its framework mediated. A kernel-side record names the process that issued each command, which is what makes the question answerable.
OTEL_* stops at Bash subprocesses, which is why the change record has to come from outside the agent.Built with yeet, a JS runtime for writing eBPF programs on Linux machines. Join us on discord.