AI Agent Sandboxing: Blast Radius Is an Architecture Choice

Form3 runs payments infrastructure across thousands of repositories, and like everyone else at that scale they had a dependency-patching backlog that never emptied. So they built a coding agent to clear it, gave it what it needed to be useful, and shipped it to production. Then their infosec team came round the corner with a very reasonable question.
Is this automation, or a supply chain incident waiting to happen?
That question — and the honest answer, which is "both, until you change the architecture" — is the spine of Moritz Johner's AI Engineer talk We Gave an Agent Production Code Access and Then Tried to Sleep at Night. It is one of the few talks about agent security that is a production case study rather than a threat model, and its conclusion is the sentence every team shipping agents should have on a wall.
TL;DR — A useful coding agent is a supply chain actor whether you planned for that or not. The moment you hand it production credentials it deserves the same guardrails as a human engineer. The fix is not a better prompt: it is a two-layer split where a boring deterministic controller holds the dangerous capabilities — git push, CI triggers, registry credentials — and the agent only ever edits files inside an isolated microVM. Prompt injection is unsolved and will stay unsolved, so the blast radius of an agent is an architecture decision, and the choice of what is deterministic versus what is agentic is your security model. The context that steers the agent — repo-specific instructions, policy, its own retrospectives — belongs in durable markdown, which is what MDflow is for. Start free.
What "blast radius" means for an AI agent
Blast radius is everything an agent can damage if it goes wrong — and it is defined by what the agent actually holds at runtime, not by what you asked it to do. Credentials, tools, network egress, filesystem reach. A prompt saying "only fix the CVE" does nothing to change it.
This matters more for patching agents than the boring subject matter suggests. Johner's starting point is that Dependabot and Renovate were built for a world where fixing a CVE means bumping a version in a manifest. That world is gone:
- The vulnerable thing often isn't visible in the manifest at all — it lives in an OS package inside your base image, or in a binary the Dockerfile downloads during the build.
- Patches never happen in isolation. Bump the Go runtime and you may have to bump the linter; bump the linter and new rules invalidate the codebase. The old tools open the PR and walk away, leaving you the mess.
So you don't only have a patching problem, you have a reasoning problem — which is exactly what an agent is good at, and exactly why the agent ends up holding real credentials. Usefulness and blast radius grow together. That's the trap.
The two-layer split that actually shrinks it
Patch Pilot has two layers, and the split between them is the security model. A deterministic Go application does orchestration — deliberately boring. Inside it, agents are spawned for the reasoning.
The end-to-end flow:
- Deterministic: discover vulnerable artifacts (OCI images), scan them, resolve which image is built by which repository.
- Deterministic: clone the repo, create a directory inside it, and fill it with context for the agent — a roughly 2,000-word prompt, the assessment manifest, and the plumbing for talking back.
- Agentic: the CVE remediation agent makes the smallest effective change set. Only versions that fix the finding — not "bump everything to latest", which is free extra risk. Then it verifies its own work: does the Dockerfile build, does a rescan come back clean.
- Deterministic: vet the diff for nonsense (empty files from a mangled shell pipeline, a compiled binary the agent left behind), then commit, push, open the PR, watch CI.
- Agentic, on red CI: a second agent gets the CI logs and workflow metadata and makes minimum-effort fixes — with an explicit instruction not to revert its own earlier changes, which is what LLMs reach for first.
- Loop until CI is green or a retry limit is hit, then hand it to a human.
Now look at the capability list, because this is where the whole talk lands. Patch Pilot as a whole needs GitHub read and write, CI trigger rights, OCI registry credentials, Go and Python runtimes, linters, a bash shell and network access. But those capabilities are not all held by the same layer:
| Capability | Held by | Why |
|---|---|---|
| GitHub write (commit, push, open PR) | Deterministic controller | Irreversible and outward-facing — must be code you can reason about |
| Trigger CI | Deterministic controller | Same: a lever an injected agent must not reach |
| Clone repo, read CI logs | Deterministic controller | Fetched for the agent, as data |
| Registry credentials (list images) | Deterministic controller | Discovery is a fixed, enumerable job |
| Edit files on a local filesystem | Agent | The actual reasoning task |
| Run builds, linters, rescans | Agent | Verifying its own work |
The agent never gets the dangerous credentials. It writes files and hands control back. Those capabilities still exist in the system — they just live behind a layer that does exactly one thing, every time, and cannot be talked into doing something else.
That boundary is what makes prompt injection survivable, and it needs to be, because the attack surface here is enormous: Johner points out that the "small" PR in his screenshot changed around 70,000 lines. Nobody is reading that for hidden instructions.
Prompt injection: you cannot solve it, only contain it
Prompt injection is the top entry in the OWASP Top 10 for LLM Applications and has no complete mitigation, because instructions and data travel down the same channel. A coding agent reading vendored dependencies, changelogs, migration guides and CI logs is swimming in attacker-controllable text.
Form3's mitigations are modest on purpose, and worth copying:
- Prompt steering by provenance. They know which directories hold untrusted content — the vendor directory, the CI log files — and say so explicitly in the prompt. Not a control, but it costs nothing.
- An injection eval. They built a deliberately hostile repository — a deprecated function whose migration guide tries to recruit the agent into doing something malicious — and run Patch Pilot against it end to end. Regression testing for injection, in other words.
- A boundary that assumes both fail. Which is the point. Steering and evals cover the vectors you know about; the layer split is what covers the ones you don't.
The sandbox on the slide is not a sandbox
"Draw a box, put the agent in it" stops working the moment the agent needs to verify its own work. To check a Dockerfile builds, or to see which package versions actually exist, the agent wants to build and run containers. So you give it the Docker socket.
At that point it is over. Access to the Docker daemon is equivalent to root on the host: spawn a privileged container, mount the host filesystem, read other processes' environment variables and memory, plant SSH keys. Form3 ran it that way in production for a while. It did not feel good.
The Linux toolbox doesn't rescue you either. Johner's team worked through Landlock, bubblewrap, seccomp, fanotify/unotify, and the unprivileged-build options like Kaniko and BuildKit. His verdict: they don't compose well with containers, and none of them can contain a Docker socket or a daemon running on the host.
What worked was changing the shape of the box: a microVM with its own kernel. Put the agent and the Docker socket inside, and an escape from the daemon lands you in a disposable VM rather than on the host. Form3 used Firecracker — the Apache-2.0 VMM behind AWS Lambda and Fargate, which boots a microVM in around 125 ms with under 5 MiB of memory overhead per VM.
Networking gets the same two-layer treatment. The deterministic controller's egress is knowable in advance — GitHub and a short list of hosts. The agent's is not: a Java repo pulls from a completely different ecosystem than Python or Go. So cut the microVM off from the host entirely, run a DNS and TCP forwarder inside it, and push every packet out over a vsock to a host process that applies policy by hostname, port and CIDR. (If you want to go further, plant a custom CA inside and terminate TLS — hard, especially for the Docker socket, but possible.)
Where the tooling actually is, in 2026
Johner is blunt that this is not a solved product category:
| Option | Status |
|---|---|
| Built-in sandboxes in Claude Code, Codex | Present, but not a boundary once a Docker socket is in play |
| OpenCode, Pi and similar | No sandbox by design |
| Roll your own Firecracker VM + plumbing | Entirely doable — "a VM with a little bit of plumbing" |
| microsandbox | Open source, libkrun microVMs, sub-second boot, network controls included — Johner's pick if he rebuilt Patch Pilot today |
| Sandbox-as-a-service vendors | Vary widely; many lack Docker-socket containment and real egress control |
| kubernetes-sigs/agent-sandbox | A Sandbox CRD backed by gVisor or Kata, with Firecracker/QEMU on the roadmap; active development, not yet production-ready for every workload |
The gap isn't that the tools don't exist. It's that most of them are still in beta, and there is real distance between "works on my laptop" and the feature set an enterprise deployment needs.
The retrospective loop nobody talks about
One detail from the talk deserves more attention than it usually gets. At the end of every agent invocation, Patch Pilot asks the agent for a short retrospective: what went well, what went wrong, what tools were missing, and what context would have helped this time.
Aggregate those across hundreds of PRs and you get something you cannot otherwise buy — a map of where your agent harness is thin. Form3 saw two clusters:
- Infrastructure gaps — network failures, or the agent lacking permission to clone a repository.
- Complexity gaps — repositories that are simply hard to reason about without context the agent didn't have.
The fix for cluster 2 is not a smarter model. It is either a system-prompt change or repository-specific instructions, written down and fed to the agent on the next run. Agent observability tooling is still being built by the community; a structured retrospective in the agent's own words is a cheap stand-in that works today.
Which teams need this most
- Anyone running automated dependency or CVE remediation across more than a handful of repositories — the exact case above.
- Coding agents with write access to real repositories, where the agent's output is a PR that a human is nominally reviewing but realistically skimming.
- Regulated environments — finance, health, public sector — where "the agent did it" is not an answer an auditor accepts.
- Anything where the agent reads third-party content: vendored code, upstream changelogs, issue trackers, CI logs, scraped pages.
- Platform teams offering agents as a shared service, who inherit every consumer's blast radius at once.
- Long-running unattended agents. The longer the loop runs without a human, the more chances there are to reach something you forgot to fence off. (Related: why AI agents route around your guardrails.)
How MDflow fits
MDflow is not a sandbox and will never be one. It does not sit in your agent loop vetoing tool calls, and any document tool that claims to be a guardrail is selling you a prompt. What it is is the durable context layer on both sides of that loop — the instructions going in, and the record coming out.
Look again at the two places Form3's system depends on written prose rather than code: the ~2,000-word prompt plus repository-specific instructions on the way in, and the per-invocation retrospectives on the way out. Both are markdown. Both need a home that is versioned, attributable and retrievable by the agent itself.
What already lines up today
Context an agent can actually find. Documents are plain markdown in folders, and each folder carries a description that states what the folder is for. Over MCP, mdflow_get_context ranks those descriptions above folder names and document titles before returning matching bodies — so "the patching policy for the Go services" retrieves the right document rather than the one with the closest-matching title. That is the mechanism for repo-specific instructions at scale: one folder per concern, described once. (More on the pattern: folder descriptions as agent context.)
A place for retrospectives that isn't a log file. Agents can create and update documents over MCP, the REST API, the VS Code extension or the n8n node — so the "what context would have helped" note the agent writes at the end of a run lands next to the instructions it will read at the start of the next one, in the same searchable workspace.
Credentials that match the two-layer thinking. Every API and MCP call is scoped to the authenticated token's owner in the query itself, server-side. An agent that decides it needs another account's document doesn't get a refusal it can argue with — the row is not in the result set. Credentials are hashed Personal Access Tokens or OAuth 2.1 access tokens issued after a browser consent screen, calls are rate-limited per token and per user, and unauthenticated calls get a machine-readable 401 challenge.
Destructive writes need explicit intent. Replacing a document body with an empty one is refused with a 400 unless the call sets confirmEmpty: true — in the editor, the HTTP API and both MCP servers. Be precise about what that is: the agent can still set the flag. It is friction plus a deliberateness signal, aimed at the realistic failure — a client with an unloaded buffer silently wiping a document — not at a determined attacker.
Every write names an actor. The Document Log records every create, edit, share and delete with who did it: you for browser actions, or automated · token name for anything arriving over the API, MCP, the clipper or the mobile app. Click an edited row for a diff. It is kept 30 days (free accounts see the last 24 hours; Pro sees the full window), and Pro also keeps version history with markdown diffs and restore. Attributable and reversible, without the agent's cooperation — the boring, load-bearing half of any agent security story.
Content a model cannot read at all. For material that should never enter a context window, documents can be encrypted in the browser with AES-256-GCM; the server stores ciphertext only.
Where we are headed
Direction, not a dated commitment: finer-grained token scopes so a read-only agent can be given a credential that is structurally read-only rather than politely asked to behave, folder-scoped access for agents that should only ever see one corner of a workspace, and a machine-readable export of the Document Log for teams that need agent activity in their own SIEM. The principle is the same one this whole post is about — put the limit somewhere the agent cannot argue with it.
The bottom line
A useful coding agent is a supply chain actor. Not because agents are dangerous, and not because they're fine — because the moment you give one production credentials to make it useful, it has the same reach as an engineer in your team, and deserves the same guardrails.
You cannot prompt your way out of that. You can only decide, deliberately, which capabilities live in code you can reason about and which live behind a model you cannot. Move git push and CI triggers to the deterministic layer. Put the agent and its Docker socket inside a microVM with its own kernel. Filter egress by policy. Seed a repository with injection attempts and run your agent at it. Keep a ledger good enough that the escalation you eventually get is one a human can judge — and keep the prose that steers the agent somewhere durable rather than in a prompt string nobody owns.
The blast radius of an agent is an architecture decision. Make it on purpose.
Start free · Connect an AI agent · Read the API docs
Frequently asked questions
What does blast radius mean for an AI agent?
Blast radius is the total set of things an agent can damage if it goes wrong — whether through a bug, a hallucination or a prompt injection. It is defined by the credentials, tools, network access and filesystem the agent actually holds at runtime, not by what its prompt tells it to do. Because those are all decisions you make when you build the system, blast radius is an architecture choice rather than a property of the model.
Should an AI coding agent have push access to your repository?
Usually not. Form3's Patch Pilot deliberately withheld GitHub write access and CI trigger rights from the agent and moved them into a deterministic Go controller that commits, pushes, opens the pull request and watches CI. The agent only modifies files on a local filesystem. The dangerous capabilities still exist in the system, but they sit behind code you can reason about, so a prompt-injected agent cannot reach them.
Is a container a good sandbox for an AI coding agent?
Not once the agent needs to build or run containers itself. Verifying its own work usually means giving it the Docker socket, and access to the Docker daemon is equivalent to root on the host — the agent can start a privileged container, mount the host filesystem and escape. Linux primitives like Landlock, seccomp and bubblewrap do not compose well around a Docker daemon either. A microVM with its own kernel, such as Firecracker or libkrun, is the boundary that actually holds.
Can you stop prompt injection in a coding agent?
No. Prompt injection is the top entry in the OWASP Top 10 for LLM Applications and has no complete mitigation, because instructions and data share one channel. What you can do is shrink the consequences: withhold the dangerous credentials from the agent, isolate it in a microVM, filter its egress by hostname and port, mark untrusted directories such as vendored code and CI logs in the prompt, and run an end-to-end eval against a repository deliberately seeded with injection attempts.
How does MDflow help teams running agents with production access?
MDflow is the durable context layer around the agent, not the sandbox. Repository-specific instructions, patching policy and the retrospectives an agent writes after each run live as plain markdown in folders whose descriptions define what the folder is for, and mdflow_get_context retrieves the right ones over MCP. On the deterministic side, every API and MCP call is scoped to the token owner server-side, tokens are hashed Personal Access Tokens or OAuth 2.1 access tokens, and every write is logged with the actor named — automated calls appear as the token that made them.
Further reading
- Moritz Johner (Form3), We Gave an Agent Production Code Access and Then Tried to Sleep at Night — AI Engineer, July 2026
- OWASP GenAI Security Project, LLM01:2025 Prompt Injection
- AWS, Announcing the Firecracker open source technology
- kubernetes-sigs/agent-sandbox — a
SandboxCRD for isolated agent workloads - microsandbox — self-hosted microVM sandboxes for AI agents
- SecureFlag, Privilege escalation in Docker
- MDflow: Why AI agents route around your guardrails · Agent authorization: why an API key is the wrong credential · LLM security is an infrastructure problem · MCP docs · API docs · FAQ