The Sandbox Is Not the Boundary
Something is forming in the agent tooling market, worth naming before the category names itself.
On one side, operators running coding agents on their own machines are asking out loud whether the wall around the agent is the thing they assume it is. On the other, builders are shipping into that doubt: sandbox launchers entering beta, agent registries, and a run of near-identical posts asking the same question from the supply side — sandboxes solve where agent code runs; what controls what it does next? Three of the twelve hottest threads in one framework community last week were versions of that sentence, all with almost no comments. That is what a category looks like a week before anyone argues about it.
The question is right. The answer most teams have bought is not.
Containment is a property of the process. Authority is a property of the credential. Only one of them travels with the request.
What a sandbox actually answers
A sandbox is a good thing and you should have one. It answers a useful set of questions: which filesystem the agent sees, which parts of the machine it can touch, what happens to the rest of your system when the thing inside crashes or spins. A wedged agent that cannot take the host down with it is strictly better than one that can.
But look at what those questions have in common. Every one is about the inside of a process running on your machine. None is about what happens when code inside that process makes an outbound call — perfectly ordinary, correctly-formed, fully-authorized — as you.
The wall bounds the blast of a crash. It does not bound the reach of a call.
This is not a gap in any particular implementation. It is definitional. A token sitting inside the sandbox is not a thing the sandbox governs; it is a claim about permissions that live on somebody else's server, evaluated by an authorization layer that has never heard of your container. Anything the agent can authenticate as is outside the wall by construction. Isolation is local. Authority is remote. The request carries the second and leaves the first at home.
The analogy that stuck for us: a locked room is not a smaller bank account. Put someone in one with a card and a network connection and the room stops being the interesting variable. The card's limit is the boundary.
Three boring limits that actually held
None of this is clever. It is mechanical, which is the point — these held because they are enforced somewhere the agent does not author.
1. Allowlists enforced where tools are registered, not stated in the prompt.
Instructions are suggestions. An agent's role file can say "never touch billing" in bold capitals and that sentence has the force of a comment. The layer that decides whether a capability exists in the session is the only one with teeth.
We know ours is real because of how it fails. We once granted a capability in the scope configuration and never declared it in the layer that registers primitives at all — a grant that was a silent no-op for hours. That failure mode is only available to you if the enforcement is genuine; a prompt-level rule cannot fail that way, because it was never binding. If your permission model has never broken something, check whether it is doing anything.
2. Limits on rate, not only on scope.
Scope answers may it do this. It does not answer how much of this. A correctly-scoped agent doing a permitted thing hundreds of times in an hour is a different incident from a wrongly-scoped one doing it once, and only the second is a permissions problem. The first is a volume problem a permission model will happily wave through.
Caps have to live in the tool layer and count against durable state, not against anything in the session — a session cannot count across sessions, and an agent with a fresh context has a fresh idea of how much it has already done. We learned that on the spend side in Spend Authority for Agents — that post is about buyer-side authority, who may spend and up to what ceiling, and its rule that an agent gets a tool and never a credential is taken as settled here. The general form: any budget worth having is a number on disk a tool decrements, not a number in a prompt.
3. Per-action reversibility.
Design so a wrong step can be thrown away instead of patched over. Classify the operation before running it — read, reversible, irreversible — and refuse the irreversible ones in code the model passes through rather than logic the model produces (Pre-Execution Risk Gating). In practice it looks unglamorous: append instead of overwrite, new rows instead of edits in place, output staged where something else has to promote it. That is the difference between "undo this" and "reconstruct this."
Scope the credential
If the wall is decoration for threat-modeling purposes — and it is — the design work moves to what the agent can authenticate as, per role and per task.
Two questions, in order:
- At its widest, what can this credential reach?
- How much of that does this task actually need?
The gap between those two answers is your real blast radius. Not the sandbox's confinement, not the guardrail's wording. That gap, and nothing else.
Scope reads and writes separately; they are different budgets protecting against different things. Writes are the damage surface. Reads are the exfiltration surface, and that is the one people forget. A role that ingests untrusted third-party text and can read your entire repository can read your secrets, and the sandbox has no opinion about it — every byte is a legitimate local file access by a process allowed to read local files. The wall is not being defeated. The wall is not involved.
This is where the runtime containment people are actually buying fails to substitute for design-time default-deny. Default-deny decides what is reachable before anything runs. A sandbox decides where the running happens. Buying the second and behaving as though you bought the first is the whole mistake.
The part that is annoying
Scoping breaks working agents in ways that look exactly like bugs. We have shipped that bug ourselves.
We narrowed a role's permissions and silently removed a tool its own instructions required it to use every session. Nothing errored in a way anyone was watching. The role kept running, kept completing work, and quietly stopped doing one mandatory thing until a live session hit it.
The tell was in the wording. This capability does not exist in this context and this path is denied are categorically different failures — one means the primitive was never registered, the other means it was registered and correctly refused. Teams that read both as "permissions broke" loosen the wrong one, usually the one that was working.
So: when you scope something, verify both directions against ground truth on disk. The intended path still works, and the paths you meant to close still fail. Never the agent's self-report — an agent asked whether its permissions are correct will tell you what its instructions say they are.
The cost is real. Least privilege is overhead you pay every session, forever, as we argued in The Harness Floor. It is also the only part of that bill buying a boundary that travels with the request.
Two ways this goes wrong
Worth separating from a neighboring failure. In When Agents Remove Their Own Guardrails, the constraint was reachable and the agent rewrote it. Here the constraint is intact, correctly enforced, and in the wrong place — guarding the room instead of the card. The first is a tampering problem. The second is an architecture problem, and it survives every audit that only checks whether controls are present.
And it pairs with its own sibling: your blast radius is detection latency covers how fast you notice. This one covers how far it can reach before you do. Multiply them together and you have the actual number.
What none of us ships yet is the obvious end state: a credential minted per task, scoped to that task's declared needs, expiring when the task completes, revocable mid-run. Everything above is a workaround for its absence. That is the next post.