sandboxing

Is it safe to run AI agents with permissions off? Sandboxing, explained

Almost everyone runs coding agents with permission prompts off. Here's the real threat model, why scale makes it worse, and what a write sandbox actually protects.

Approving every permission prompt is slow. You start out reading each one, then you approve a dozen in a row that were all fine, and eventually you reach for the flag that turns them off. In Claude Code that flag is --dangerously-skip-permissions, nicknamed yolo mode. Codex and Cursor have their own versions. The prompts go away, the agent stops stalling, and the work gets faster.

Very few people add any boundary back after they do that. That is the part worth talking about. The prompts were the only thing standing between the agent and your machine, and turning them off without replacing them with anything is the default state most people end up in. This post is about what that actually costs and what to put back.

What an unsandboxed agent can do

An AI coding agent is a process that runs shell commands. It edits files, installs packages, runs your tests, and executes whatever its plan calls for. With permissions off, it does all of that without asking. That is the point. It is also the problem.

The failures here are mostly not attacks. They are accidents and confused-agent behavior, which is exactly why they are easy to underrate. A few concrete ones:

  • rm -rf aimed at the wrong path because a variable expanded to empty, or because the agent guessed at a directory that did not exist.
  • git reset --hard or git clean -fd to “get to a clean state,” wiping uncommitted work you had not staged yet.
  • An edit to ~/.zshrc or ~/.gitconfig because the agent decided your environment was the thing standing in its way.
  • A curl ... | sh from a README, run to install a dependency, executing whoever’s script that is.
  • Reading files it has no business reading. Not to steal them, just because they were nearby and looked relevant.

None of this requires a bad instruction, though a bad instruction (a poisoned line in a file the agent reads, a misleading issue comment) makes all of it more likely. The point is that “the agent is usually careful” is not a security model. It is a description of the average case, and the average case is not what hurts you. The tail is. A tool that is right ninety-nine percent of the time and can delete uncommitted work the other one percent is a tool you cannot leave alone.

Scale is what changes the math

One agent, you can babysit. You watch the transcript, you catch the rm -rf before it lands, you undo the config edit. Supervision works because there is one thing to supervise.

The reason people run these agents at all is to stop doing that. The payoff is parallelism: split a job across several agents, race the same task on two models, keep one working while you review another. Ten agents running at once is the goal, not the edge case. And ten agents is ten times the blast radius with no human watching any single one of them. You cannot read ten transcripts in real time. You are not going to catch the one bad command in agent number seven while you are reading agent number three.

So the two properties you want most, autonomy and volume, are exactly the properties that make no-boundary dangerous. The tool gets useful precisely at the point where supervision stops being possible. That is the whole tension, and it does not resolve itself. You have to add a boundary that does not depend on a human being present.

What a boundary actually does (and does not do)

There are two honest layers to reach for, and they protect different things.

The first is OS-level write protection. On macOS that means a sandbox profile (sandbox-exec, also called Seatbelt) that denies file writes by default and re-allows only a few specific places: the agent’s working directory and a handful of spots it genuinely needs. The agent runs as you, on your machine, with your toolchains right there, so there is effectively no overhead and nothing to set up. What it stops is the whole category above: the agent cannot write outside its lane, so it cannot trash your repo, your dotfiles, or the rest of your disk.

Be precise about what it does not do. A write sandbox does not stop the agent from reading files your user can read, and it does not stop it from making network calls. It is a write-protection boundary, not a VM. “Can’t wreck your machine” is a real and useful guarantee. “Air-gapped” it is not.

The second layer is a container or a VM. This isolates the whole process, not just its writes. The agent sees only what you mount or install into it, so reads and network can be constrained too, and untrusted code runs somewhere that is not your actual filesystem. The cost is convenience: containers need their own credentials (your Keychain and your logged-in CLIs are not in there), builds run on Linux rather than macOS, and there is setup and resource overhead. Stronger boundary, more friction. That trade is real and worth making deliberately rather than by default.

Neither layer is a reason to skip the other basics. Keep credentials out of the agent’s reach so a read cannot become an exfiltration. And whatever you do, do not point a permissionless agent at your home directory. Give it a project, not your life.

If you take one thing from this and never touch Fletch: run agents inside something with a write boundary. A sandbox profile, a container, or a throwaway VM. Any of the three beats a permissionless agent running loose on your account.

How Fletch draws the line

Fletch runs every agent inside a per-agent Seatbelt profile from the moment it spawns. There is no opt-in step; you would have to work to get out of isolation. The profile denies writes by default and re-allows only what the agent legitimately needs: its own workspace, its RPC mailbox, temp directories, the device files a terminal needs, and a curated list of the agent CLI’s own config and cache directories so it keeps its login and state.

The exclusions are the interesting part, because they are where a lazy allowlist would leak. Fletch deliberately does not grant ~/.local/bin, since a write there could plant a binary you would later run. It does not grant all of ~/.claude, because that settings file can define hooks that execute on your host. And Fletch’s own app data, the database holding your other agents’ transcripts, is denied both read and write. Everything else on disk stays readable but not writable, which is the honest shape of a write boundary.

For a harder line, Docker is an optional stronger engine, selectable per agent under Settings. Each agent runs in its own container that sees only its mounts, which narrows the reading-and-network caveat to what you can enumerate. In this version those containers run as root and are a live-process containment measure, not a hardened trust boundary.

Which is the right framing for all of this. The sandbox is a write-protection boundary, not a VM, and it is not pretending to be one. The real gate on what reaches your repository is structural: every agent works in its own disposable clone with its own .git, and nothing lands until you review the diff and merge the pull request. The sandbox keeps a confused agent from wrecking your machine on the way there. The clone and the review are what keep bad code out of your project. Both matter, and it helps to know which one is doing which job.