Devcontainers vs Bernato Sandboxes for Running Untrusted AI Code
Devcontainers isolate a coding agent inside a separate container filesystem and process namespace: strong isolation, but an image has to build or pull before the agent can do anything, and access is usually all-or-nothing per container. Bernato wraps the agent process directly on the host using macOS Seatbelt, gated by capability tokens for network, filesystem, subprocess, and screen access, so it starts in under a second and lets you flip permissions individually. The tradeoff is real: a devcontainer's isolation boundary is a kernel namespace, Bernato's is an OS sandbox profile, and those fail differently if something goes wrong.
Ask an agent to refactor a module and it's going to run npm install, execute a test suite, maybe hit a package registry, all without you reading every command before it runs. Whether that's fine or alarming depends entirely on what sits between the agent process and the rest of your machine. Two answers have become the default in practice: put the agent inside a devcontainer, or wrap the process itself in an OS-level sandbox. I've run both against the same day-to-day Claude Code workflows for long enough to have a real opinion on where each one wins, and it isn't the same answer for every setup.
What a devcontainer actually isolates
A devcontainer, in the VS Code / devcontainer.json sense, gives the agent an entirely separate filesystem and process namespace by building or pulling a container image and running the agent inside it. Everything the agent touches, files, installed packages, environment variables, stays inside that container unless you explicitly mount a volume through. If the agent runs rm -rf on the wrong path, it takes out the container's copy of the repo, not your home directory.
That boundary is enforced by the container runtime and, ultimately, the host kernel's namespace and cgroup machinery. It's a mature, heavily audited isolation model. Escaping a properly configured container (no --privileged, no mounted Docker socket, no host PID namespace) requires either a kernel vulnerability or a misconfiguration, and both are things security teams have spent a decade hardening against. That's exactly why devcontainers are the standard answer when "isolation" is the entire point, not just a nice-to-have.
The cost shows up before the agent does anything useful. An image has to build (or pull, if cached) and the container has to start before you get a shell inside it. And by default, devcontainer network and filesystem access tends to be a single switch: the container can reach the network or it can't, it can see a mounted directory or it can't. Getting finer-grained control, letting an agent read the repo but not write to it, or hit npm's registry but nothing else, usually means hand-rolling network policies or seccomp profiles on top of the base devcontainer spec. Nobody does that by default.
What Bernato's sandbox actually isolates
Bernato takes a different starting point. Instead of giving the agent a separate filesystem, it wraps the agent process directly on the host operating system using macOS Seatbelt, the same native, kernel-level sandboxing mechanism Apple uses to confine its own system processes. There's no container image, no separate filesystem namespace, no virtualized kernel. The agent's process runs on your actual OS, constrained by a sandbox profile.
What makes that profile useful instead of all-or-nothing is that it's gated by capability tokens issued per agent: workspace_rw, network, subprocess, screen_capture, accessibility, and full_disk_access. You decide, per agent, which of those it gets. An agent doing a pure refactor doesn't need network access at all. One running integration tests against a local API needs network but not screen capture. One driving a browser automation task might need accessibility. Each token is a yes/no you set before the agent starts, not a container-wide toggle. If you want the mechanics of how the Seatbelt profile itself is built, I covered that in detail in Sandboxing Claude Code on macOS; this post is specifically about how that model compares to devcontainers, not a rehash of how it works internally.
Startup time changes how you iterate
This is the part that's easy to underrate until you've felt it. A devcontainer that's already built and cached typically takes somewhere around 10 to 30 seconds to start and attach. If the image needs a fresh build, from a Dockerfile with real dependencies, you're looking at something more like one to three minutes before the agent can run its first command. Bernato's sandbox, because there's no image and no separate filesystem to construct, spawns the wrapped process in under a second.
That difference compounds. If you're running an agent through dozens of small edit-test-fix cycles in a session, and the sandbox itself has to be torn down and rebuilt between runs, or you're spinning up a fresh container per task, the container startup tax adds up to real dead time across a session. It's not a dealbreaker for a long-running task where the container spins up once and stays warm. It's a real cost for the kind of fast, iterative loop that trusted-but-imperfect agents like Claude Code are usually run in.
Capability control: all-or-nothing vs per-token
The other practical difference is how much control you get once the sandbox is running, not just how fast it starts. A devcontainer's isolation is mostly binary at the container boundary: the container can reach the network or it's cut off, it can see a mounted volume or it can't. Changing that usually means editing devcontainer.json and rebuilding. Bernato's capability tokens are set per agent before it starts and can differ from one agent to the next without touching any image definition, which matters if you're running several agents with different jobs at once. I wrote more about the token model itself, and why capability-scoped grants beat one big allow/deny switch, in Capability-based permissions.
Blast radius when something goes wrong
Here's the part I want to be honest about instead of glossing over, because it's the actual tradeoff and not just an implementation detail. A devcontainer escape is a well-studied failure mode: it generally requires a kernel exploit, a misconfigured privileged flag, or a mounted Docker socket that shouldn't have been mounted. When it happens, the blast radius is still contained by the kernel's namespace and cgroup boundaries even if the container itself is compromised, because the isolation layer sits below the application entirely.
Bernato's sandbox is a process-level boundary on the host OS, not a separate kernel namespace or filesystem. If a bug in the Seatbelt profile, or in how a capability token is checked, lets an agent process step outside its intended boundary, it's stepping directly onto your real filesystem and your real user session, not a walled-off container copy of it. That's a materially different failure mode, and a worse one in the worst case, even though it's a much lighter and faster sandbox day to day. Fast and fine-grained doesn't mean the isolation boundary is equally strong; it's a different boundary with different failure characteristics, and you should pick based on what you're actually running through it.
When each one actually wins
Given that tradeoff, the decision comes down to what's running inside the sandbox and who else needs to reproduce the environment. Devcontainers win when you need a bit-for-bit reproducible environment across a team, everyone gets the same base image, the same dependency versions, the same OS libraries, regardless of what laptop they're on. They also win when the code genuinely can't be trusted: a third-party PR, an agent operating on an untrusted input, anything where you'd want kernel-level isolation even if it's slower to spin up. That's not a Bernato use case, and I wouldn't pretend it is.
Bernato's native sandbox wins when you're iterating fast on an agent you basically trust but don't want unsupervised, which describes most day-to-day Claude Code sessions on your own machine. You're not distributing this environment to a team, you're not running adversarial code, you just want the agent to install packages and run tests without also being able to read your SSH keys or turn on your webcam unless you explicitly grant it. Near-instant startup and per-agent capability tokens matter a lot more than kernel-level isolation in that scenario, because the actual risk profile is "agent does something dumb," not "agent is actively hostile."
FAQ
Can I run Bernato and a devcontainer together?
Yes, and it's a reasonable belt-and-suspenders setup if you want it. You can run a Bernato-supervised agent inside a devcontainer: the container gives you the separate filesystem and kernel namespace boundary, and Bernato's capability tokens still gate what the agent process is allowed to do inside that container, plus you keep Bernato's fleet supervision and live PTY access on top. It's more setup than either one alone, but nothing about the two models is mutually exclusive.
Does Bernato's native sandbox work on Linux or Windows?
Not today. The sandbox described here is built on macOS Seatbelt, which is a macOS-specific kernel mechanism. It doesn't run on Linux or Windows, and we're not going to claim cross-platform sandboxing support that doesn't exist. If you need this exact isolation model on Linux, a devcontainer or a Linux-native sandboxing tool (seccomp, bubblewrap, and similar) is the honest answer right now, not Bernato's Seatbelt wrapper.
What's the actual escape risk in plain terms?
With a devcontainer, escaping means breaking out of a kernel namespace, which is a narrow, heavily audited attack surface; when it fails, the agent still isn't automatically on your real filesystem. With Bernato's sandbox, the agent process is already on your real host, and the sandbox profile plus capability tokens are what's standing between it and your files, network, and permissions. A bug there has a more direct path to your actual system. Neither is theoretical-only, but they fail in different places and with different severity.
Is a devcontainer always more secure than Bernato's sandbox?
For genuinely untrusted or adversarial code, yes, the kernel-level boundary is stronger and that's the right call. For a trusted agent you're supervising in real time on your own machine, "more secure" is less clear-cut: a slower, all-or-nothing container doesn't help if it pushes you toward granting broader access just to avoid rebuilding the image every time you need a new permission. Fine-grained, per-agent tokens can be the more secure practical choice even with a lighter isolation boundary underneath them.
Do I need Docker running to get devcontainer-level isolation?
Yes, the devcontainer model depends on a container runtime, almost always Docker or a Docker-compatible engine, being installed and running. That's part of the overhead: a background daemon, disk space for images, and the build or pull step before the agent starts. Bernato's sandbox has none of that dependency, which is a big part of why it starts in under a second instead of tens of seconds.