Skip to content

Security & Sandboxing

A coding agent is a process that runs arbitrary shell commands. It edits files, installs packages, and executes whatever its plan calls for — and one wrong tool call (or one poisoned instruction in a file it read) can write anywhere your user account can write: your repo’s history, another agent’s half-finished work, your dotfiles. Running one agent unsandboxed is a calculated risk. Running ten at once is not a risk you can watch closely enough to take.

Fletch’s answer is structural: every agent runs inside a sandbox, in its own checkout, from the moment it spawns. You never opt in to isolation — you’d have to work to get out of it.

Fletch has two interchangeable sandbox engines, chosen under Settings › General › Sandbox. The engine applies to newly created agents; an existing agent keeps the engine it started with for its whole life, so flipping the setting never changes a running agent.

Each agent process runs as your user under a per-agent macOS sandbox-exec profile that denies file writes by default and re-allows only what that agent legitimately needs:

  • its own workspace at ~/.fletch/workspaces/<id>/ and its RPC mailbox
  • temp directories and the PTY/device files terminal programs need
  • the agent CLI’s own config and cache directories (so it can keep its login and state)

Deliberately not granted: executable directories on your PATH (a write there would let an agent plant a binary you’d run later), and Fletch’s own app database — the agent can’t even read your other agents’ transcripts. Everything else on disk is readable but not writable.

Seatbelt is native to macOS: no install, no daemon, no image to build, and effectively no performance overhead — the agent runs directly on your machine with your toolchains already available.

Its limit is what it is: a write-protection boundary, not a VM. A Seatbelt agent can read anything your user can read and can reach the network. It can’t trash your repo or your machine, but it isn’t air-gapped.

Docker — opt-in, full container isolation

Section titled “Docker — opt-in, full container isolation”

With the Docker engine, each agent runs in its own Linux container (docker run --rm --init, one container per agent). The container sees only what’s mounted into it: the agent’s writable workspace, its RPC mailbox, and a read-only view of the borrowed git object store. Your wider filesystem simply isn’t there — the reading-and-network caveat of Seatbelt narrows to the mounts you can enumerate.

The trade-offs are the ones containers always have:

  • Setup. Docker Desktop must be installed and running; Fletch probes the daemon and disables the option (with a one-click Start Docker Desktop) when it’s down. The first agent launch builds the agent image, with progress shown in a toast.
  • Linux, not macOS. Builds and tests run on Linux inside the container. That’s a feature if you ship to Linux, a surprise if your project needs Xcode.
  • Resources. Containers default to 4 GB memory and 2 CPUs; both, and the image itself, can be changed under Settings › Experimental.
  • Provider coverage. Claude Code, Codex, OpenCode, Pi, and Cursor Agent run in containers. Antigravity stays on Seatbelt (its CLI has no non-interactive login, so a fresh container can’t authenticate). Claude in a container also needs credentials the container can reach — see the Connect Claude for containers flow in Settings › General › Sandbox, which automates claude setup-token.

One honesty note: in this version, containers run as root and are a live-process containment measure, not a hardened trust boundary. The isolated clone and the review-before-merge flow remain the real gate on what lands in your repo.

Start with Seatbelt. It’s zero-setup, fast, and its write-deny profile already makes the fleet safe against the failure that actually happens — an agent writing where it shouldn’t. Switch an agent to Docker when you want a hard boundary around what it can read as well as write, when the task involves running untrusted or unfamiliar code, or when you want builds and tests to run on Linux. Because the engine is stamped per agent, you can mix: everyday agents on Seatbelt, one Docker agent for the sketchy dependency upgrade.

Isolation only holds if the sandbox has nothing sensitive inside it — and a git checkout usually smuggles in the most sensitive thing of all: your repository’s .git directory.

A linked git worktree (the common approach to parallel checkouts) has a .git file that points back inside your real repo’s .git. Give an agent a worktree and you must give it write access there — and a writable .git/hooks means arbitrary code execution on your machine the next time you run git.

Fletch provisions each workspace as a git clone --shared instead. The clone has its own real, writable .git, entirely inside the sandboxed workspace, so your repo’s .git is never writable by an agent — no hook-planting, no config rewriting, no history surgery on the original. The clone borrows your repo’s object store through git’s alternates mechanism rather than copying it, so spawning an agent costs kilobytes and milliseconds regardless of history size. New objects the agent creates land in its own .git; your repo stays byte-for-byte untouched — no worktree entries, no “branch already checked out elsewhere” errors — and discarding an agent is just deleting a directory.

Each checkout starts detached at a pinned fork-point commit taken from the freshest state of your base branch, so an agent’s diff is always measured against exactly where it started. The one constraint of borrowing objects is that the workspace depends on the source repo staying in place, which Fletch manages for you.

Pushing and opening pull requests need your GitHub credentials, and those must not live where an agent can read them. So agents don’t hold credentials at all: when an agent needs a remote action, it asks the host through a broker that accepts only a small, fixed set of operations — push, PR creation, and a credentialed fetch — which run host-side with your token. An agent can therefore publish code and open PRs under your identity (choose tasks and repos accordingly), but it can never exfiltrate the token itself, because it never has it.