Agentic Security / agent / isolation / enforcement point
Prompt-Level Versus Process-Level Isolation
That 'the agent is sandboxed' is one fact. It is four different facts about four enforcement points, and the useful question is which set of actions each point sits astride. A regex over proposed shell commands is bypassed by the shell's own grammar; a container blocks the process and not the Markdown image the chat client fetches; a domain allowlist that includes any host the attacker can read — a paste site, a package registry, your own telemetry endpoint, DNS — is an open channel measured in bits per request, not a wall. Teams pick a layer, feel done, and never enumerate the primitives that go around it.
"The agent is sandboxed" is four different claims wearing one word. A rule in the system prompt, an allowlist in the harness, a kernel sandbox around the process, and a policy on the network namespace are four enforcement points, and each one mediates only the actions that physically pass through it. The useful question is never "is it sandboxed". It is "which set of actions does this thing sit astride, and which does it not".
A layer mediates an action when the action cannot happen without passing through it. A container mediates syscalls made by the processes inside it. It does not mediate a request made by your chat client on your laptop, or a job that runs tomorrow on a continuous-integration runner because the agent committed a file today. Those are not sandbox escapes. Nothing escaped: the action was never inside.
Below is a list of fourteen concrete things a compromised agent could be induced to do — this is a defender's inventory, not a recipe, and every one of them is a documented class rather than a working exploit. Configure the four layers and read which primitives each one is even in the path of. Start from the default, which is what most teams mean when they say the agent is sandboxed: a command allowlist, a container, and an outbound HTTP proxy with a host allowlist.
The five environment toggles are not defences. They are facts about the deployment that decide whether a primitive exists at all, and they are the part nobody writes down. DNS stands for the Domain Name System, the lookup an agent performs before almost any outbound request; CI stands for continuous integration.
this layer is in the path
and stops the action ·
in the path and lets it
through ·
the system prompt names
this action, which is a request rather than an enforcement ·
a dash means the layer is not in the path of this action at all, which is
a different and much worse thing than letting it through ·
no layer means the opposite problem — this layer would be in
the path, but you have not configured one. Select any row to read the
reason for each cell.
What is real and what is a model. The mediation relation is real and is the whole point: each primitive declares which layers are physically in its path, and a layer is asked to rule only on the actions that reach it. The primitive list is a defender's inventory of documented classes — it is deliberately not exhaustive, and a real review of your own system will find primitives that are not here. What is modelled is the single compliance number for the system prompt: real refusal rates depend on the model, the wording and whether the agent is being steered, and nothing here measures yours.
At the default — the configuration almost everyone describes as sandboxed — 11 of 14 primitives still reach their effect. Read the three layer readouts and the reason becomes arithmetic rather than opinion. The harness gate sits astride 13 of the fourteen and blocks 1. The container sits astride 12 and blocks 1. The network policy sits astride 4 and blocks 1. Each layer is doing exactly what it says on the tin, and the tin covers a narrow strip of the surface.
The survivors no layer could ever sit astride readout says 1. That
one is the Markdown image: the model emits
 in its reply, the reply is
delivered to a chat client on somebody's laptop, and the client makes
the request. The harness never sees it because no tool was called. The
container never sees it because it is a different process on a different
machine. The network policy never sees it because it is a different network
namespace. There is no layer to turn up. This is the fifth enforcement point,
it is the renderer, and it usually belongs to a different team.
The harness gate is not the sandbox, and neither is the prompt
Select the row shell out through an allowlisted binary's own config and read the four reasons. The harness gate is in the path and lets it through, because the binary is on your allowlist and the grammar that turns it into a shell belongs to the binary rather than to you. The sandbox is in the path and lets it through, because starting a shell is an ordinary syscall. The network policy is not in the path at all. Move the harness gate to allowlist plus argument inspection and that row flips to stopped; the hero number goes from 11 to 10 of 14.
Now select run arbitrary code through an allowlisted interpreter with argument inspection still on. It survives, and the reason given is the honest one: the argument is a program, so inspecting it for intent is the halting problem wearing a regular expression. There is no argument policy that distinguishes a test script from an exfiltration script, because the difference is in what the program does when it runs. Only no shell at all, typed tools only stops it, and that takes the hero number to 9 of 14 at the cost of an agent that can no longer run a command you did not anticipate.
This is the split people miss when they say sandboxed. The harness gate is policy over proposed tool calls and it is bypassed by grammar: the grammar of a shell, of a binary's own configuration flags, of an interpreter. The kernel sandbox is policy over syscalls and it is bypassed by scope: anything not executed by that process tree. They fail in completely different ways and neither substitutes for the other. Put the harness gate back to command allowlist — so that only the sandbox is moving — and set the sandbox to container plus a seccomp syscall filter. Watch which row changes: only the kernel-syscall one, taking the hero number from 11 to 10 of 14. A seccomp filter is attack-surface reduction against the kernel, not policy about what your agent may do, and reading it as the latter is how a container ends up described as a permission system. The process-level machinery underneath is the ordinary namespaces-and-cgroups stack, with all of its usual leaks.
The system prompt is not a layer at all, and the readouts say so. It names 6 of 14 actions — those are the ones somebody thought to write a rule about — and the compliance slider does nothing to the other eight. Put the sandbox back to container: namespaces and cgroups, so you are standing on the defaults again and the hero number reads 11 of 14. Now push model follows the written rule to 100% and the chance at least one lands readout stays at 100.0%, because a perfectly obedient model still has no rule telling it not to encode a secret in a hostname. Prompt rules are an enumeration exercise, and the enumeration is always shorter than the primitive list. This is the same reason instruction-hierarchy defences are priced in residual rate rather than in guarantees. Put the slider back to 95% — it is load-bearing for a number two sections down, and it is the easiest control on this page to leave somewhere by accident.
Turn everything up and read what is left
Set the harness gate to typed tools only, the sandbox to read-only rootfs, seccomp, no host mounts, the network policy to deny all outbound, and switch on the write gate. This is the most locked-down agent anyone has proposed to you, and it can no longer install a dependency, run a test through an interpreter, write outside its own tree, or open a socket. The hero number is 4 of 14.
Those four are worth naming, because they are what the entire layer stack does not cover. The agent can read a secret and put it in the reply, where whoever reads the transcript is the exfiltration channel. It can emit a Markdown image that the chat client fetches. It can commit a workflow file that CI runs tomorrow with CI's token, which is a privilege escalation into a process your sandbox has no relationship with. And it can edit its own instructions file, so the next session starts compromised — which is memory poisoning with the retrieval step replaced by "always in the context window".
Three of those four are named by a prompt rule; one is not. That is why the chance at least one lands readout still says 100.0%. Now switch off the chat client renders Markdown images in replies — a change made entirely in the renderer, at an enforcement point that does not appear in any of the four selects — and the readout drops to 14.3%. Switch off the CI runner too and it is 9.8%. Those two toggles moved the number further than every layer control on the page, and neither of them is a sandbox.
Then go the other way for the calibration. Put everything back to where this
lesson started — command allowlist, container, HTTP proxy allowlist, no write
gate, image rendering and CI back on — and switch on the container runtime
socket is mounted inside. The hero number goes from 11 of 14 to
12 of 15, with exec as host
joining the reachable effect classes. One line in a compose file, added so
the agent could build images, and the container is now a suggestion. The
read-only rootfs, seccomp, no host mounts level is the only one that
takes it away, and it takes the bind mount with it.
What the specifications actually say, and what they leave to you
The Model Context Protocol — MCP, the standard interface for handing an agent a set of tools — is unusually explicit about the layer distinction in its Security Best Practices document. Under Local MCP Server Compromise it tells clients to "execute MCP server commands in a sandboxed environment with minimal default privileges", to "launch MCP servers with restricted access to the file system, network, and other system resources", and to "use platform-appropriate sandboxing technologies (containers, chroot, application sandboxes, etc.)". Every one of those is a process-level control, and the document is careful that none of them are prompt text. It also states the thing worth putting on a wall: MCP servers run with the same privileges as the client.
What no specification does is tell you which actions each of those technologies sits astride, which is the question this page exists to make answerable. That gap is why "we run it in Docker" and "we run it with a system prompt that forbids destructive commands" get treated as points on the same scale. They are not on the same scale. One of them is an enforcement point and the other is a request, and the matrix above will tell you the difference for any action you can name.
It is also why the loudest signal in this area is people trying to remove the controls. The single most-reacted issue in a sweep of agent tooling was a feature request to add a flag that skips permission prompts, at 93 reactions, and a bug report that a bypass flag stopped working carries 65. Read that as a measurement rather than as recklessness: the harness gate is the layer that generates friction proportional to how much work the agent does, and when a control's cost is felt on every action and its benefit is one blocked primitive in thirteen, people turn it off. Where that pressure ends up is the approval prompt, and what volume does to it.
Checking this on a system you actually run
Write the matrix for your own deployment. Two columns are enough to start: the action, and for each layer you believe you have, whether that layer is physically in the path. The test for "in the path" is mechanical — name the process that performs the action and the machine it runs on. If it is not the agent's process tree, the container is not in the path. If no tool call is issued, the harness is not in the path. If the connection originates somewhere other than the agent's network namespace, the egress policy is not in the path. Most teams discover their inventory has three or four rows where every layer column is a dash, and those rows are the entire risk.
Then verify the layers you think you have, rather than the ones in the
README. Inside the running container, check the process-id namespace with
readlink /proc/self/ns/pid and compare it to the host's; list
mounts with findmnt and look for anything from the host,
including the runtime socket; read /proc/self/status and check
the Seccomp and CapEff fields, because a container
with Seccomp: 0 has no syscall filter no matter what the
orchestration says. For the network, do not test whether a blocked host is
blocked — test whether an allowed host accepts an arbitrary path and
body, and whether a DNS query for a name you control reaches your
authoritative server. That last test takes two minutes and answers the
question the allowlist cannot.
Instrument the layer that has no configuration. Render model output through a
sanitiser that rewrites or drops remote image sources and link targets in
agent transcripts, and log what it dropped. Point the container at a resolver
you control and log every query name with its length. Route agent HTTP
through a proxy that logs the full URL rather than the hostname, because
registry.example.com in a log tells you nothing and
registry.example.com/v1/<base32> tells you everything.
These are the three highest-yield changes available, and none of them is a
sandbox setting.
Finally, get the environment toggles into review. The five on this page — image rendering, a CI runner that executes what the agent commits, a host bind mount, DNS, a mounted runtime socket — are each one line in a config file, each added for an unrelated reason, and each one adds or removes a primitive without changing anything anybody would call a security setting. Put them in the same review as the tool manifest, because they decide the same thing: what set of actions exists, which is the input to every question about what the agent is permitted to do.