Tutorial
Share one container with your agents
A team of agents working in your checkout is a team working on your real machine, with your real credentials, usually with their permission prompts turned off. The sandbox puts the whole project — your panes and every agent on the team — inside a single container instead. One filesystem, one toolchain, one blast radius. This walkthrough turns it on, opens a shell in it, sends a team into it, and shows what it does and does not protect.
The sandbox needs Docker or Podman — nothing else. No VS Code,
no devcontainer CLI, no image to pull from a registry Sinclair
publishes. If your project happens to have a devcontainer.json, it is
read; if it does not, nothing about this is different. On macOS, ⌘ is the
modifier — on Linux, read it as ⌃ (Ctrl).
1. Turn it on
Open File ▸ Sandbox (it is also under AI ▸ Sandbox once AI is enabled) and pick Use Sandbox for This Project. The first row of that menu is a live status line, so it is always the place to look when you want to know what the sandbox is doing.
The toggle writes a real setting, so it survives a restart:
// ~/.config/sinclair/settings.json
{
"sandbox-enabled": true
}
"This project" means the repository the focused pane is in —
found by walking up to the nearest .git, so every pane in a checkout
lands in the same container instead of a pane three directories down getting one of
its own. Each project gets its own container, named after the path, so two checkouts
called api in different places never share one, and reopening either
finds the container it already had rather than starting a second.
Paths too broad to mount are refused: /, anything one level down
(/Users, /home), and your home directory. A sandbox mounts
what it is given, and mounting $HOME would hand an agent with its
prompts turned off your whole machine. Open a pane inside a repository first.
2. Open a shell in it
Sandbox ▸ Open Shell in Sandbox gives you a normal tab whose shell is inside the container. Scrollback, search, copy mode, recording, themes: all of it works, because the pane is an ordinary pty like every other.
The first time, this takes a few minutes. Sinclair generates a small Dockerfile, layers your agent CLI onto a Debian base, and builds it locally — there is no image to download from us. The menu status line names the step it is on (Building the sandbox image…) the whole time. Afterwards the image is cached and opening a shell is instant.
The Containers panel in the sidebar (⌘ the activity bar's ❖ icon) has the same controls plus anything Sinclair wants to warn you about, and it is the one place that shows how many panes are currently inside.
3. Your paths still mean what they meant
The project is mounted at its own path. If your repo is
/Users/you/code/api on the host, it is
/Users/you/code/api inside the container too — not
/workspaces/api.
That is not a cosmetic choice. Git records absolute paths when it
creates a worktree: the main repo's .git/worktrees/<name>/gitdir
and the worktree's own .git file point at each other. Under any other
mapping, a worktree made inside the container is broken on the host and vice versa.
With the paths equal, the worktree workflow keeps
working unchanged from both sides — which matters, because giving each agent its own
worktree is exactly how you keep a team from editing over each other inside
the shared container.
Create worktrees under the project, not beside it. The tutorial's
../wt/thing convention puts them outside the mount, where no agent can
see them. .worktrees/thing is inside, and works.
4. Send a team in
With the sandbox on, AI ▸ Teams ▸ any team brings the container up first, then opens the roster. Every member is exec'd into that same container, so the whole team shares a filesystem, a build cache, and one toolchain. A quick-launched agent (AI ▸ Agents ▸ Launch …) and a saved agent definition go to the same place.
Relay itself stays on the host. Only the agent process runs inside, which is why a
crash in the container can never take the mesh — or your terminal — down with it.
The bus is still reachable: Sinclair points each agent at the engine's gateway host
(host.docker.internal, or host.containers.internal under
Podman) instead of a loopback address that would mean the container itself.
Standalone, the same thing from the CLI:
relay launch alice --role frontend \
--sandbox sinclair-sbx-api-9f3c1a20 \
--sandbox-engine docker \
--sandbox-workdir /Users/you/code/api
5. Signing in, once
An agent inside a Linux container cannot read a credential your macOS keychain is holding, so the first agent you launch there will ask you to sign in. It only happens once: the agent's home directory is a named volume, so the login — along with folder-trust answers and shell history — survives rebuilding the image and recreating the container.
6. What it actually protects
Sinclair launches unattended team members with their permission prompts bypassed, because a member sitting on a dialog in a split nobody is watching is a member doing nothing. That trade is very different depending on where it happens:
- Only the project is mounted. The rest of your home directory is not there to delete.
- Only the credentials you mount are present. Nothing reaches
your keychain, your other repos, or your cloud config unless you put it in
sandbox-mountyourself. - Resources are capped. A supervisor can spawn up to eight
workers into one container; a pid ceiling is always set, and
sandbox-memory/sandbox-cpusbound the rest.
Never mount /var/run/docker.sock into the sandbox. It is a common
devcontainer convenience and it is a one-line container escape: it hands an agent
running with prompts bypassed root on your host, which is the whole thing you were
trying to prevent.
7. Make it your environment
The generated image is deliberately thin. Point it somewhere better, or add to it:
{
"sandbox-enabled": true,
// Use an image you already maintain, as-is. Sinclair builds nothing —
// it trusts this image to carry the agent CLI.
// "sandbox-image": "ghcr.io/you/dev:latest",
// Or keep the generated layer and change what it is built on / with.
"sandbox-base": "debian:bookworm-slim",
"sandbox-packages": ["ripgrep", "build-essential"],
"sandbox-setup": ["curl -sSf https://sh.rustup.rs | sh -s -- -y"],
// Extra mounts: source:target[:ro]. A bare path mounts at itself.
"sandbox-mount": ["~/.gitconfig:/sandbox/home/.gitconfig:ro"],
"sandbox-env": ["DATABASE_URL=postgres://host.docker.internal/dev"],
// Ceilings for everything in the container, together.
"sandbox-memory": "8g",
"sandbox-cpus": "4"
}
The generated layer uses apt-get, so it wants a Debian or Ubuntu
base. For anything else — Alpine, a language-specific image, your company's build
image — set sandbox-image and install the agent CLI in that image
yourself.
8. If the project has a devcontainer.json
It is read, and used where it helps. The image it names becomes the base Sinclair
layers the agent CLI onto, so your agents land in the toolchain your team already
agreed on. Its containerEnv and remoteEnv reach the
container, with your own sandbox-env winning on conflicts.
Mounts and remoteUser from the file are applied too. The full list
of what is and is not read is in
Work in your project's dev container.
If an editor already has a container up for this folder, Sinclair enters that one instead of building a parallel one — and will never stop or remove it, because your editor session is very likely attached to it. The Containers panel says so when that happens.
Two lines in your devcontainer.json make it behave much better with
agents: "shutdownAction": "none" so closing your editor does not stop a
container with a team working in it, and the identity mount below so worktrees stay
valid on both sides.
// .devcontainer/devcontainer.json
{
"workspaceMount": "source=${localWorkspaceFolder},target=${localWorkspaceFolder},type=bind",
"workspaceFolder": "${localWorkspaceFolder}",
"shutdownAction": "none"
}
Turn the whole behaviour off with "sandbox-devcontainer": false if you
would rather Sinclair ignored the file.
9. Cleaning up
The container outlives any single pane — several panes and every agent are inside
it, so closing one tab must not take the others down. By default it also outlives
the last one: rebuilding a toolchain every session is slow and an idle container
costs nothing. Set "sandbox-persist": false to have it stop when the
last pane leaves.
- Stop Sandbox — stops the container now. Panes inside it end with it.
- Rebuild Sandbox — removes the container and brings a fresh one up from your current settings. Anything written outside the mounted project is gone, which is the point.
Neither ever touches a container Sinclair did not create.
Known limits
- A worker a supervisor starts with the MCP
spawntool runs on the host, not in the sandbox: the relay daemon is not told which container a session belongs to. Thanks to the identity mount it sees the same files; its toolchain is the host's. - The generated image assumes an
apt-getbase. Usesandbox-imageotherwise. - A
devcontainer.jsonthat builds from a Dockerfile is not built for you — setsandbox-imageto the image it produces.
Where next
- Run an agent team — the roster the sandbox holds.
- Parallel agents in worktrees — how members stay out of each other's way inside one container.
- Work in your project's dev container — using the environment your repo already defines.
- Linux in a tab — throwaway containers, the sandbox's disposable sibling.