skribbl
productpricingfree!questionswriting
download
concepts · 12 August 2026 · 7 min read

Sandboxing coding agents: what actually contains them

One asks first. The other cannot be talked into anything at all.

skribbl/writing/concepts
a permission prompt is not a sandbox

A permission prompt asks before a risky tool call; a sandbox means the process cannot reach the network or filesystem outside a boundary no matter what it decides to do. The difference between the two, what a real sandbox has to restrict, and a sane default posture for running an agent next to a real repository and real credentials.

three different things people call "safe"

Ask three people what makes it safe to run a coding agent against a real repository and you get three different mechanisms, often used interchangeably even though they stop almost nothing in common. In order of how much they actually restrict the agent:

PERMISSION PROMPTthe agent asks before a risky call. enforced by the agent
SANDBOXthe process cannot reach outside a boundary. enforced by the kernel
CONTAINER OR VMthe whole OS environment is isolated. enforced below the process

Only the last two are security boundaries in the sense a security engineer would use the term: a restriction that holds regardless of what the restricted thing decides to do. The first is not, and the gap between "asks first" and "cannot" is the subject of this post.

1. the permission prompt

Most coding agents, run locally, ship a permission system: before writing outside the working directory, running a shell command, or making an outbound network call, the agent stops and asks. You approve or deny, sometimes with a remembered "always allow" for a given tool or path.

This is genuinely useful. It catches the mundane case well: an agent about to rm -rf a directory you didn't expect it to touch, or push to a branch you didn't ask it to push to. A prompt in front of an obviously wrong action, read by a human paying attention, stops that action.

What it actually is, mechanically

The prompt is a check inside the agent's own process. The code that decides "this call needs approval" runs with the same privileges as the code that would execute the call. Nothing separates the part of the program that asks permission from the part of the program that would act without asking. It is, structurally, the same as a function that calls confirm() before doing something: a courtesy the calling code extends to itself.

why a permission prompt is not a boundary

Three ways that courtesy fails to hold, none of them exotic:

  • The model does something the prompt logic didn't anticipate. Permission systems enumerate risky tool calls. A capability the list-writer didn't think to gate through it. If the agent can reach the network through any code path the permission layer doesn't intercept, that path is unrestricted by definition.
  • Prompt injection changes what the agent decides to do. This is a well-documented category of failure, not a hypothetical: an agent that reads untrusted content (a fetched web page, a file in the repo, an issue comment) can have instructions embedded in that content that the model treats as legitimate. If those instructions steer the agent toward a tool call it would normally ask about, and the injected content also contains text aimed at getting a human to click approve, the prompt is not protecting against a compromised decision-maker. It is being asked by the compromised decision-maker.
  • Approval fatigue makes the human part of the system unreliable. A prompt that fires on every file write gets "always allow"'d within the hour. This is not a flaw in any particular person; it is what happens when a security control has a cost paid on every single action. A control whose cost scales with usage gets worn down by usage.

None of this makes permission prompts worthless. It makes them a UX feature layered on top of trust in the agent process, not a substitute for restricting what that process can do. The question worth asking about any agent setup is not "does it ask before risky actions" but "what happens if it does the risky action without asking, because it was wrong, tricked, or buggy." A sandbox is the answer to that second question.

2. the sandbox

A sandbox restricts what a process is capable of, enforced by something outside the process: the kernel, a filesystem namespace, a network policy. The process does not get a vote. If it is not permitted to open a socket to a given host, attempting to do so fails the same way it fails for any other unprivileged process on the machine, whether the attempt came from a well-reasoned plan, a hallucinated one, or injected instructions makes no difference to the kernel.

This is the mechanical difference that matters: a permission prompt is inside the trust boundary of the agent. A sandbox is the trust boundary. Put differently, a permission prompt answers "should I," enforced by the thing being asked. A sandbox answers "can I," enforced by something that does not care what the process wants.

This is not a new idea specific to AI agents. It is the same reasoning that put unprivileged build steps in a chroot, CI runners in ephemeral containers, and browser tabs in a renderer sandbox years before an LLM ever called a tool. The novelty with agents is only that the thing running inside the boundary now writes its own instructions on the fly, which is exactly the case a capability-based boundary was designed to not care about.

what a sandbox actually has to restrict

A sandbox that only restricts one of these is a sandbox with a hole in it. All three need to be true at once for the boundary to mean anything against a coding agent specifically.

Filesystem scope

The agent should be able to read and write the project directory it is meant to be working in, and nothing else by default. Not the home directory, not sibling repos, not ~/.ssh or ~/.aws. The common failure mode here is not malice, it is a shell command the agent runs that happens to resolve a relative path outside the project root, or a cp that globs wider than intended. A filesystem boundary makes that a failed syscall instead of a mistake that silently succeeds.

Network egress

The meaningful control is an allowlist of hosts the sandbox can reach, not a blanket "internet access on/off" toggle and not a prompt that fires per request. A process that has been tricked into exfiltrating a file does not stop to ask permission for the request that exfiltrates it; egress restriction has to be enforced structurally, at the network layer, for it to matter in exactly the scenario it exists for.

# illustrative shape of a scoped egress policy, not a specific product's syntax
allow:
  - registry.npmjs.org
  - github.com
  - api.anthropic.com
deny: "*"

Credential access

This is the one that turns a contained mistake into an incident. An agent that can read your standing SSH private key or your long-lived cloud credentials can act as you anywhere those credentials are valid, which is usually far outside the project directory the sandbox otherwise scoped it to. The fix is not "don't give the agent credentials," it is scoping what it gets: a short-lived, narrowly-permissioned token injected into the sandbox for the one operation that needs it (a git push to one repo, a deploy to one staging environment), rather than the same broad credentials sitting in your home directory that every other process on your machine can also read.

3. the container or VM

Filesystem scope, network egress and credential scoping are policies. Something still has to enforce them, and that enforcement lives at the OS level: namespaces and cgroups for a container, a hypervisor boundary for a VM. This is the layer that makes "the process cannot reach outside its boundary" true rather than merely configured.

CONTAINERshares the host kernel. cheap, fast to start, boundary is namespace-level
VMown kernel. more overhead, boundary holds even against a kernel exploit
NEITHERthe agent runs with your user’s full filesystem and network access

For most local development, a container with an actually-restricted mount and an actually-restricted egress policy is a reasonable default. The step up to a VM buys a boundary that survives a kernel-level compromise of the sandboxed process, which is worth paying for once real production credentials or genuinely untrusted, unreviewed code are in the picture, and not necessary for most everyday repo work.

a default posture, not a certified one

This is a rule of thumb for running an agent against a real repository with real credentials somewhere on the same machine, not a compliance checklist:

  • Run the agent in a container scoped to the project directory. Not the home directory mounted read-write, the project directory. Everything else on the machine should be invisible to it by default.
  • Set an egress allowlist, not a prompt. Package registries, the model API, the specific git remote. Deny the rest at the network layer.
  • Inject scoped, short-lived credentials for the one action that needs them, then let them expire. A push token good for one repo and one hour is a smaller blast radius than a standing personal access token with account-wide scope sitting in an environment variable.
  • Keep the permission prompt. It is still the right first line for the actions a sandbox is not built to catch, like "this diff touches a payments file, are you sure." Keep it for what it is good at and stop relying on it for what it cannot do.
  • Treat anything the agent fetched as untrusted input to itself. Web pages, issue text, README files in a cloned dependency. None of that content should be able to widen what the sandbox permits, whatever it says.

None of this requires exotic infrastructure. It requires treating the agent process the way you would treat any other automated process with write access to your code and reach to your network: scoped by default, not trusted by default and merely asked to behave.

Watching several sandboxed agents at once is a different problem from containing any one of them, and it is the one Skribbl is built for: a menu-bar canvas showing what each agent is doing and what it is spending, live, across a fleet of otherwise-isolated sessions. See the download page for what it runs on.
READ NEXT
Who may command whom: permissions for coding agentsTwo permission questions. Most setups only answer the first one.9 minRunning Claude Code in CI/CDThe one setting that must never be on when the trigger is a stranger’s PR.6 minClaude Code hooks: a practical guide with examplesEvery event, the payload it carries, and the exit code that blocks a tool call.12 min
ON THIS PAGE
three different things called "safe"1. the permission promptwhy a prompt is not a boundary2. the sandboxwhat a sandbox actually has to restrict3. the container or VMa default posture
run them on a canvasSkribbl puts every agent, its terminal and what it is spending on one board. macOS, one day free.

get the next one by email.

One email when there is something worth reading. Unsubscribe is one click and it is in every issue.

download
productpricingdocsquestionswhat it iscomparereleaseswritingnewsletterlaunchesprivacycancel
give them infinity.© skribbl