DEV Community

Cover image for Claude Code shipped a sandbox. Here's what it protects — and what it doesn't.
zerodrop for Termaxa

Posted on • Originally published at termaxa.com

Claude Code shipped a sandbox. Here's what it protects — and what it doesn't.

Anthropic shipped OS-level sandboxing for Claude Code. If you run an agent
against a repo you care about, it's worth understanding precisely what moved —
because a fair amount of the commentary treats it as "agents are contained
now," and that's not what the documentation says.

I read the docs carefully, partly because I build a tool in adjacent territory
and needed to know whether I'd just been made redundant. Short answer: no. The
longer answer is more interesting, and it starts with a compliment: the docs
are unusually honest about their own limits.
Most of what follows isn't
something I discovered — it's something Anthropic wrote down, and more people
should read it.

What it actually does

The sandbox uses OS primitives — Seatbelt on macOS, bubblewrap on Linux and
WSL2. By default, sandboxed commands can write only to your working directory
and the session temp directory. No network domains are pre-allowed: the first
time a command needs a new host you're prompted, and approving it lasts the
session.

Crucially, this is enforced by the operating system on the running process,
not by the model correctly interpreting a command. The docs put it well: the
boundary holds regardless of what the model chose to run, and even if an
allowed command does more than its name suggests. That's a real improvement
over asking an agent nicely, and it's the right layer for what it solves.

The motivation named in the docs is the same one I keep seeing in the wild:
reducing the permission prompts that people stop reading. Approval fatigue is
the disease; this is a real treatment for part of it.

Five things worth knowing before you rely on it

It's Bash-only. The sandbox constrains Bash commands and their child
processes. Claude Code's own Read, Edit and Write tools don't run through it —
they go through the permission system instead. "The sandbox is on" means shell
commands are contained, not that every file operation is.

Your working directory is inside the boundary by design. The default write
scope is the current working directory, plus session temp. That's what makes
the agent useful — it has to edit your code. It also means the sandbox is not
protecting the thing many developers assume it is. A destructive command
confined to your project is still confined to your project.

There's a sharp exception worth knowing, and it cuts in the reassuring
direction: even in auto-allow mode, rm and rmdir targeting /, your home
directory, or other critical system paths still trigger a prompt or a
classifier check, and explicit deny rules are always respected. Anthropic
special-cased the catastrophic paths. Everything inside your working directory
remains permitted, deliberately.

Reads are much wider than writes. Default read access is the entire
computer
minus a few denied directories — and the docs say plainly that this
still allows reading credential files such as ~/.aws/credentials and
~/.ssh/. If you want those protected you configure sandbox.credentials
(deny, or mask) or add them to denyRead. There is no built-in credential deny
list. This is the item I'd action first.

There's an escape hatch, on by default — but it's permissioned. When a
command fails because of sandbox restrictions, Claude may retry it with
dangerouslyDisableSandbox. Importantly, that retry goes through the normal
permission flow: a confirmation prompt in default mode, or the classifier in
auto mode. So it isn't a silent bypass — it's a sanctioned door with a
doorbell. If you want the door bricked up, allowUnsandboxedCommands: false
enables what the panel calls Strict sandbox mode.

It fails open. If the sandbox can't start — missing bubblewrap, unsupported
platform — Claude Code warns and runs commands unsandboxed, unless you set
failIfUnavailable: true. That's a defensible default (a safety layer that
bricks your agent gets switched off, and then it protects nobody); I make the
same choice in my own tool. But fail-open has a shadow: the thing can be
inactive while everything looks fine. I learned that expensively when a hook
API rename left my own tool silently gating nothing for four minor versions,
with a fully green test suite.

And on native Windows it doesn't run at all. Windows users are pointed at
WSL2. If you run Claude Code natively on Windows — plenty do — the sandbox
isn't in your picture.

Credit where it's due: the docs name their own gaps

Before I make my argument, the Limitations section deserves reading in full,
because it's more candid than most vendor security pages:

  • The built-in proxy makes allow decisions from the client-supplied hostname and, by default, does not terminate or inspect TLS — so allowing broad domains like github.com can create paths for data exfiltration, with domain fronting named explicitly.
  • allowUnixSockets can inadvertently grant host access: allowing /var/run/docker.sock effectively grants access to the host system.
  • allowAppleEvents on macOS removes code-execution isolation.
  • enableWeakerNestedSandbox considerably weakens security.
  • And the summary line: sandboxing reduces risk but is not a complete isolation boundary.

That's a vendor telling you where their boundary ends. Take them at their word
rather than at the marketing.

The distinction that actually matters

Containment, consequence and recovery are three different questions, and tools
tend to answer exactly one:

Containment asks can this process touch things outside its box? That's
the sandbox, Docker, seccomp. A boundary in space.

Consequence asks what will this specific command destroy if it runs?
Fifty thousand rows. Three dependent tables. One commit that exists only on the
remote. A containment boundary has no opinion about this, because a permitted
command inside the boundary is just a permitted command.

Recovery asks and if it happens anyway, can I get it back? Neither
containment nor prediction gets you here. This is a backup taken before
execution, and a path back.

The failures I've been collecting all month live in the second and third
categories. An agent following NTFS junctions out of a Windows.old cleanup
into someone's live Documents folder. Prompt-configured rules — "don't touch
.env" — ignored, because instructions are suggestions and the execution path
doesn't read your CLAUDE.md. rm -rf ~/.crit/reviews/$SID where $SID had
already been cleared, so it expanded to nothing and took the parent directory.

Notice what those share. None is an agent doing something obviously stupid.
Each is a correct-looking command whose real scope differs from its apparent
scope.
Containment doesn't address that class, because the command was
allowed to run in the place it ran.

What I'd actually do

Turn the sandbox on. It's built in, OS-enforced, and it closes a real
category. On Windows that means WSL2 or nothing — worth knowing before you
assume you're covered.

Then convert three soft defaults into firm ones, each a one-line change:

{
  "sandbox": {
    "enabled": true,
    "failIfUnavailable": true,
    "allowUnsandboxedCommands": false,
    "credentials": {
      "files": [
        { "path": "~/.aws/credentials", "mode": "deny" },
        { "path": "~/.ssh", "mode": "deny" }
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Know it's broken rather than assume it's working; make the boundary hold rather
than negotiate; and stop sandboxed commands reading your keys, since the
default read policy allows it.

Then be clear-eyed that you've solved containment, not consequence or
recovery.
For those: least-privilege credentials — an agent token that
cannot drop your production tables beats any prompt asking it not to —
backups that happen before execution rather than on a nightly cron, and, if
you want it, something that shows the blast radius while there's still a
decision to make.

That last part is what I build (Termaxa,
open source, Rust). I'm obviously not neutral. But the argument survives
without it: vendors will keep shipping containment, because containment is what
a platform can own. Consequence and recovery sit at a different layer, and
structurally, no agent vendor is going to build that layer for their
competitors' agents. Anthropic won't gate Cursor. Cursor won't gate Claude
Code. If you run more than one, that layer has to come from somewhere else.

The honest summary

Claude Code's sandbox is a good, real improvement that answers one of three
questions well, and its documentation is refreshingly clear about where it
stops. It's Bash-scoped; reads are far wider than writes and include your
credentials by default; the escape hatch is on but permissioned; it fails open;
and it isn't there at all on native Windows.

Read the limitations section. Flip the flags that matter to you from soft to
firm. And keep a backup — because the thing that gets you won't look dangerous.
It'll look like a command you'd have approved.


Verified against Claude Code's sandboxing documentation, August 2026.
Incidents referenced are public posts from r/ClaudeAI, r/ClaudeCode and
r/cursor over the past two weeks. The docs move quickly; where this and the
documentation disagree, the documentation is right and I'm out of date. Termaxa is MIT/Apache, cargo install termaxa — and if you can get an agent past it in a way I haven't documented, that's the most useful thing you could send me: issues or security@termaxa.com.

Top comments (1)

Collapse
 
komo profile image
Reid Marlow

The OS-enforced part is the piece I wish more people separated from the agent UI. A sandbox is useful because the model cannot narrate its way around it, but it still needs boring policy around network, secrets, and what counts as a trusted working directory. I would treat it as a narrower blast radius, not a permission system by itself.