Spawn isolated, disposable Linux sandboxes ("boxes") for each Claude agent instance against a host workspace. Fast cold start, full host-side isolation, instant switching between agents in VS Code.
The default backend is a local Docker container (everything in this doc is the docker path unless noted). A second backend, Daytona Cloud (agentbox create --provider daytona), composes the same in-box supervisor + host relay on top of a remote Daytona sandbox; the shape diverges in workspace seeding (git bundle vs. bind-mounted .git/), comms (signed preview URL + bridge poller vs. localhost relay), and snapshots (Daytona snapshots vs. docker commit). See cloud-providers.md for the cloud-specific design + the daytona-backlog.md for what's done vs. pending on that path.
/workspace is a plain directory in the container's writable layer — created by the image (mkdir /workspace && chown vscode:vscode /workspace in Dockerfile.box), populated at create time, and wiped on agentbox destroy. No FUSE overlay, no upper volume, no virtiofs bridge for working-tree reads.
How /workspace gets populated depends on the source:
- Git repo (the common case):
agentbox createrunsgit worktree add -b agentbox/<box-name> /workspace HEADinside the container against the host main repo's.git/, which is bind-mounted at its identical absolute host path (RW). Branch + uncommitted-state replay (hostgit stash createSHA +tar-piped untracked files) are applied in-container as uservscode. - No git repo: a
tar-pipe from the host workspace (or its--host-snapshotAPFS clone) into/workspace. - Checkpoint restore (
--snapshot <ref>): the box is started with the checkpoint Docker image as its base —/workspaceis already populated; nogit worktree add, no tar pipe.
The retired FUSE overlay had lowerdir=/host-src,upperdir=/upper/upper and reasonable boot speed, but every advantage it had against macOS hosts evaporated once we depended on per-box git worktrees + a bind-mounted .git — the worktree path was the isolation, the overlay added nothing.
Container needs /dev/fuse + SYS_ADMIN + apparmor:unconfined/seccomp=unconfined not for the outer FS but for the in-box dockerd's storage driver (the kernel-native overlay2, picked by a runtime probe in agentbox-dockerd-start, with a fuse-overlayfs fallback) and cgroup remounts.
- Build base image with
node,pnpm,fuse3,fuse-overlayfs(inner dockerd's fallback storage driver),rsync,git,tmux, plus Claude Code and agent-browser. - Run the in-box
/agentbox-setupwizard once: it inspects the workspace, writesagentbox.yamlwith install + service definitions, and (optionally) takes the first checkpoint. --host-snapshotis the optional "freeze the source bytes" knob:cp -cAPFS clone of the workspace into~/.agentbox/snapshots/<id>/before the tar pipe so host edits during create don't leak in. No-op when a git repo is detected (worktree content comes from.git).
volumes:
# Per-box (wiped on destroy)
- <hostMainRepo>/.git:<hostMainRepo>/.git # identical-path RW so worktree pointers resolve symmetrically
- <boxDir>/run:/run/agentbox # agentbox-ctl unix socket
- <boxDir>/workspace:/host-export # rsync target for `agentbox open`
- agentbox-docker-<id>:/var/lib/docker # in-box dockerd data root
- agentbox-vscode-server-<id>:/home/vscode/.vscode-server
- agentbox-cursor-server-<id>:/home/vscode/.cursor-server
# Shared across boxes (allowlisted in `prune --all`, never auto-removed)
- agentbox-claude-config:/home/vscode/.claude # user identity (auth, skills, plugins)
- agentbox-vscode-extensions:/home/vscode/.vscode-server/extensions
- agentbox-cursor-extensions:/home/vscode/.cursor-server/extensions/workspace is not a volume — it lives in the container's writable layer. node_modules, .next, target, .venv land there alongside the rest of the working tree; the wizard-generated agentbox.yaml install task rebuilds them on first start (marker-guarded by node_modules/.agentbox-installed).
- Each box exposes container
:80(web service) +:6080(noVNC) via random ephemeral host ports, and is reachable via the Dev Containers extension (agentbox code <box>). - Per-box
agentbox-{vscode,cursor}-server-<id>volumes hold the downloaded server binary; sharedagentbox-{vscode,cursor}-extensionsvolumes hold installed extensions. - TS server, file watchers, language services live in the container. Cache stays warm across the box's lifetime.
Inactive boxes are docker paused, not stopped.
docker pausefreezes all processes via cgroup freezer: 0 CPU, RAM stays mapped but pageable.docker unpauseresumes instantly.vscode-serverand its TS server pick up mid-instruction — no cache rehydration.- Switching boxes: pause the outgoing, unpause the incoming, focus the VS Code window. Sub-second.
docker stop + docker start works too — /workspace lives in the container writable layer, so it survives a stop/start natively. agentbox start only needs to relaunch ctl/dockerd/Xvnc (they die with the container).
| State | Trigger | Cost |
|---|---|---|
| Running, active | User attached | Full |
| Paused | User switched away | 0 CPU, RAM pageable |
| Stopped | Explicit, or N hours idle | 0 |
| Destroyed | User discards agent | Container + volumes purged |
The container's writable layer is the agent's "diff against base image". Persists across pause/stop. Discarded on destroy. Inspectable with agentbox open (rsync /workspace → host scratch dir → Finder) or exportable via agentbox checkpoint (docker commit).
A checkpoint captures a box's accumulated state — /workspace (incl. node_modules, build caches, in-box .env files) plus everything else the agent wrote into the container's writable layer — so a new box can start warm instead of from bare host code. Primary use: after a setup wizard or a merged PR, agentbox checkpoint create <box> --set-default makes every future box in the project inherit the warm state.
- Cleanup runs first:
docker exec --user root <ctr> /usr/local/bin/agentbox-checkpoint-cleanupstrips apt cache +/tmp+/var/log+ bash history. Caches under~/.npm/~/.cacheand/var/lib/dockerare kept (warm state worth carrying). - Layered checkpoint (default, fast):
docker commit <ctr> agentbox-ckpt-<projectHash>:<name>. New layer on top of the box's current image; lineage is implicit in Docker image history. Theparentschain in the manifest tracks refs for display and the auto-flatten threshold. - Flattened checkpoint (
--merged, or auto whenchainDepth >= checkpoint.maxLayers, default 3):docker commit→docker create→docker export <tmp> > rootfs.tar→ tinyFROM scratchDockerfile thatADDs the rootfs and replays the base image'sEnv/Cmd/Entrypoint/WorkingDir/User/ExposedPorts(lost bydocker export) →docker build. The resulting image is a single ADD layer; lineage resets. - Restore:
agentbox create/claude --snapshot <ref>(or per-project defaultbox.defaultCheckpoint) passes the checkpoint image tag torunBoxas the base image. No mount, no overlay;/workspaceis already there.
Storage:
- Host metadata only:
~/.agentbox/checkpoints/<projectHash>/<name>/manifest.json(schema 2, references the image tag). - Image tags use the deterministic
agentbox-ckpt-<sha1-16(projectRoot)>repo prefix (parallel to the per-project config-dir hash);agentbox prune --allreaps any tag under that prefix not referenced by a survivingBoxRecord.checkpointImage. agentbox checkpoint rm <ref>deletes the manifest +docker image rmthe tag.
In-box agents trigger capture via the existing relay (agentbox-ctl checkpoint → /rpc checkpoint.create → host agentbox checkpoint create CLI). No host creds in the box.
This is distinct from the host snapshot (--host-snapshot, config box.hostSnapshot): a per-box APFS clone of the host workspace used only as a stable source for the create-time tar pipe. Orthogonal to checkpoints, which capture box-side state.
- FUSE overlay with the host workspace as lower — retired once isolation moved into the per-box git worktree + container fs. With
.gitbind-mounted and a fresh branch per box, the overlay added no isolation and lost the speed argument (linux native deps are rebuilt anyway). Seedocs/create-and-checkpoints.mdfor the new create flow. - Per-project checkpoint volume (the previous design) — replaced by
docker commitimages. Layered chain is now native Docker image history; flatten isdocker export+FROM scratchrebuild. - Mount/symlink swapping under a single VS Code window — causes TS server cache invalidation storms and watcher floods on every switch. Per-box server + pause is strictly better.
- Using the host's
node_modulesas-is — platform mismatch (macOS binaries on Linux). The wizard install task force-rebuilds Linux-native deps on first box start. - Git push/pull from inside the container with credential forwarding — leaks creds. Push goes through the relay (
git -C <hostMainRepo> push origin <branch>runs on host with user SSH/gitconfig); pull splits into a relaygit.fetchplus a creds-free in-container merge.
- Idle-timeout policy: auto-pause after N minutes of no VS Code activity? Auto-stop after M hours?
- Memory ceiling: cap concurrent unpaused boxes, or rely on swap pressure?
- Cross-box diff dashboard: single pane showing
git diff --statper box with deep-links into the right attached VS Code window.