From 17a22ac8bc73f5f3dc0a694112858b9597876bc6 Mon Sep 17 00:00:00 2001 From: Nicolas De Loof Date: Fri, 8 May 2026 14:14:41 +0200 Subject: [PATCH] sandboxes: document branch-mode in-container clone and source-repo isolation Update the branch-mode workflow to reflect the move from on-host worktrees to an in-container clone exposed over git-daemon, and add a new source-repository isolation layer to the security model. Highlights the strong guarantee that an unconstrained agent cannot corrupt the host's .git directory or working tree, since the source mount is read-only and the agent operates on a private clone with its own index, refs, and working tree. Tracks docker/sandboxes#2477. --- .../manuals/ai/sandboxes/security/_index.md | 5 +- .../ai/sandboxes/security/isolation.md | 67 ++++++++- .../ai/sandboxes/security/workspace.md | 19 ++- content/manuals/ai/sandboxes/usage.md | 127 +++++++++--------- 4 files changed, 143 insertions(+), 75 deletions(-) diff --git a/content/manuals/ai/sandboxes/security/_index.md b/content/manuals/ai/sandboxes/security/_index.md index c80f550b04a..b4534d71577 100644 --- a/content/manuals/ai/sandboxes/security/_index.md +++ b/content/manuals/ai/sandboxes/security/_index.md @@ -43,7 +43,7 @@ and ICMP are blocked at the network layer. ## Isolation layers -The sandbox security model has four layers. See +The sandbox security model has five layers. See [Isolation layers](isolation/) for technical details on each. - **Hypervisor isolation:** separate kernel per sandbox. No shared memory or @@ -52,6 +52,9 @@ The sandbox security model has four layers. See [Deny-by-default policy](defaults/). Non-HTTP protocols blocked entirely. - **Docker Engine isolation:** each sandbox has its own Docker Engine with no path to the host daemon. +- **Source-repository isolation (branch mode):** the agent works on a private + in-VM clone with your `.git` mounted read-only. Even an unconstrained + agent cannot corrupt your host repository. - **Credential isolation:** API keys are injected into HTTP headers by the host-side proxy. Credential values never enter the VM. diff --git a/content/manuals/ai/sandboxes/security/isolation.md b/content/manuals/ai/sandboxes/security/isolation.md index b533c638813..c83e58df9a6 100644 --- a/content/manuals/ai/sandboxes/security/isolation.md +++ b/content/manuals/ai/sandboxes/security/isolation.md @@ -1,15 +1,16 @@ --- title: Isolation layers weight: 10 -description: How Docker Sandboxes isolate AI agents using hypervisor, network, Docker Engine, and credential boundaries. -keywords: docker sandboxes, isolation, hypervisor, network, credentials +description: How Docker Sandboxes isolate AI agents using hypervisor, network, Docker Engine, source-repository, and credential boundaries. +keywords: docker sandboxes, isolation, hypervisor, network, credentials, git --- {{< summary-bar feature_name="Docker Sandboxes sbx" >}} AI coding agents need to execute code, install packages, and run tools on -your behalf. Docker Sandboxes run each agent in its own microVM with four -isolation layers: hypervisor, network, Docker Engine, and credential proxy. +your behalf. Docker Sandboxes run each agent in its own microVM with five +isolation layers: hypervisor, network, Docker Engine, source-repository +(in branch mode), and credential proxy. ## Hypervisor isolation @@ -73,6 +74,64 @@ Host system └── [VM] Containers created by agent ``` +## Source-repository isolation + +When you start a sandbox with `--branch` (see the +[branch-mode workflow](../usage.md#branch-mode)), the agent never works +directly against your host repository. Even with full root inside the VM, +it cannot corrupt your local Git state. + +The boundary works like this: + +- Your repository's Git root is bind-mounted into the sandbox at + `/run/sandbox/source` as **read-only**. The agent — and anything it + spawns — cannot write to your `.git` directory, your working tree, or + any tracked file via that mount. +- The agent's working copy is a private `git clone --reference` populated + on the sandbox's overlay filesystem. The clone has its own index, its + own refs, and its own working tree. Object storage is shared via + `.git/objects/info/alternates`, so the clone is space-efficient and + full history is walkable, but writes to the clone never reach your + host's object database. +- Your host pulls the agent's commits over a `git-daemon` exposed by the + sandbox on `127.0.0.1:`. The CLI registers it as a + `sandbox-` remote on your host repo. Fetching from that + remote uses the same trust model as fetching from any third-party + remote: nothing is integrated until you explicitly merge or check out + the fetched refs. + +```plaintext +Host repository Sandbox VM + .git/ /run/sandbox/source/ (RO bind mount) + objects/ ◄─────── alternates ───────── clone/.git/objects/ + refs/ clone/.git/refs/ (private) + HEAD clone/.git/HEAD (private) + working tree clone/working tree (overlay FS) + remote sandbox- ──── git:// ────► git-daemon :9418 + (published 127.0.0.1:) +``` + +The practical guarantees: + +- **Index and ref corruption are impossible.** Concurrent `git` commands + on the host and inside the sandbox can't race on a shared `.git/index` + or shared refs because there is no shared writable state. +- **No write-back to your working tree.** A compromised or buggy agent + can't drop a `.git/hooks/pre-commit`, modify `.github/workflows/`, or + edit any other tracked file in a way that affects your host until you + fetch and merge from the `sandbox-` remote. +- **No access to your `.git/config`.** Credentials, signing keys, and + global settings declared in your repository configuration stay on the + host. The agent's clone has its own configuration. +- **Cleanup is automatic.** `sbx rm` deletes the clone, the published + port, and the `sandbox-` remote on your host. Nothing leaks + outside the sandbox lifecycle. + +In direct mode (no `--branch`), the agent edits your working tree directly +and this isolation does not apply. Use branch mode whenever you want a +strong boundary between the agent's Git activity and your host +repository. + ## Credential isolation Most agents need API keys for their model provider. Rather than passing keys diff --git a/content/manuals/ai/sandboxes/security/workspace.md b/content/manuals/ai/sandboxes/security/workspace.md index 900677b50a9..48cbb137ad3 100644 --- a/content/manuals/ai/sandboxes/security/workspace.md +++ b/content/manuals/ai/sandboxes/security/workspace.md @@ -37,9 +37,16 @@ includes: ## Branch mode -The `--branch` flag lets the agent work on a separate branch. This is a -workflow convenience, not a security boundary: the agent still mounts the full -repository. See the [usage guide](../usage.md) for details. +The `--branch` flag isolates the agent from your host repository: it works +on a private clone inside the sandbox, with your `.git` directory +bind-mounted **read-only** as a reference. This means the agent cannot +modify any tracked file or any byte under `.git/` on your host, no matter +how unconstrained the agent runs. You see the agent's commits only after +explicitly running `git fetch sandbox-`. + +See [Source-repository isolation](isolation.md#source-repository-isolation) +for the full boundary, and the [usage guide](../usage.md#branch-mode) for +the workflow. ## Reviewing changes @@ -52,10 +59,12 @@ With the default direct mount, changes are in your working tree: $ git diff ``` -If you used `--branch`, the agent's changes are on a separate branch: +If you used `--branch`, the agent's changes are on the `sandbox-` +remote until you fetch and merge them: ```console -$ git diff main..my-feature +$ git fetch sandbox-my-sandbox +$ git diff main..sandbox-my-sandbox/my-feature ``` Pay particular attention to: diff --git a/content/manuals/ai/sandboxes/usage.md b/content/manuals/ai/sandboxes/usage.md index 62ea8590487..859ca4cf288 100644 --- a/content/manuals/ai/sandboxes/usage.md +++ b/content/manuals/ai/sandboxes/usage.md @@ -77,21 +77,29 @@ time, they may step on each other's changes. See ### Branch mode -Pass `--branch ` to give the agent its own -[Git worktree](https://git-scm.com/docs/git-worktree) and branch. This -prevents conflicts when multiple agents, or you and an agent, write to the -same files at the same time. You can set `--branch` on `create`, `run`, or -both. - -The CLI creates worktrees under `.sbx/` in your repository root. The -worktree is a separate working directory, so the agent doesn't touch your main -working tree. This means: - -- The worktree branches off your latest commit when you create it. - Uncommitted changes in your working tree are not included (`sbx` warns you - if it detects any). -- Files you add or change in your main working tree won't be visible to the - agent, and vice versa. The two directories are independent. +Pass `--branch ` to give the agent its own branch and an isolated clone +of your repository inside the sandbox. This prevents conflicts when multiple +agents, or you and an agent, write to the same files at the same time. You +can set `--branch` on `create` or, equivalently, on `run` at create time. + +When `--branch` is active: + +- The agent works on a private clone living entirely inside the sandbox. + Your repository's `.git` directory is bind-mounted **read-only** as a + reference for the clone's object database, so the agent reuses your local + history without consuming extra disk space. +- The CLI creates the new branch on your host repository and checks it out + if your working tree is clean. If it's dirty, the branch ref is still + created but the checkout is skipped with a warning, so your uncommitted + changes are preserved. +- The sandbox runs a `git-daemon` over a `127.0.0.1`-bound ephemeral port + that exports the in-container clone. The CLI registers it as a Git remote + named `sandbox-` on your host repository, so you can pull + the agent's commits with `git fetch`. +- The agent's clone has its own index, refs, and working tree. Concurrent + Git operations on the host and inside the sandbox can't corrupt each + other. See [Source-repository isolation](security/isolation.md#source-repository-isolation) + for the security implications. #### Starting a branch @@ -105,46 +113,44 @@ Use `--branch auto` to let the CLI generate a branch name for you: $ sbx run claude --branch auto ``` -You can also create the sandbox first and add a branch at run time: - -```console -$ sbx create --name my-sandbox claude . -$ sbx run --branch my-feature my-sandbox -``` - -Or set the branch at create time and reuse it on subsequent runs: +You can also create the sandbox first and attach later: ```console $ sbx create --name my-sandbox --branch my-feature claude . -$ sbx run my-sandbox # resumes in the my-feature worktree -$ sbx run --branch my-feature my-sandbox # same — reuses the existing worktree +$ sbx run my-sandbox # resumes in the my-feature clone ``` -#### Multiple branches per sandbox - -You can run multiple worktrees in the same sandbox by passing different branch -names: - -```console -$ sbx run --branch feature-a my-sandbox -$ sbx run --branch feature-b my-sandbox -``` +> [!NOTE] +> A sandbox is bound to the branch chosen at create time. To work on a +> different branch, create a new sandbox with `sbx create --branch +> other-feature ...`. Running `sbx run --branch ...` on an existing sandbox +> with a different branch is rejected. #### Reviewing and pushing changes -To review the agent's work, find the worktree with `git worktree list`, then -push or open a PR from there: +The CLI wires the agent's in-container clone as a `sandbox-` +Git remote on your host repository. Review the agent's work with the same +commands you'd use for any other remote — no `cd` into a worktree, no extra +tooling: ```console -$ git worktree list # find the worktree path -$ cd .sbx/-worktrees/my-feature -$ git log # see what the agent did +$ git fetch sandbox-my-sandbox # pull the agent's commits +$ git log sandbox-my-sandbox/my-feature # see what the agent did +$ git diff main..sandbox-my-sandbox/my-feature # full diff +$ git checkout my-feature && git merge --ff-only \ + sandbox-my-sandbox/my-feature # fast-forward your local branch $ git push -u origin my-feature $ gh pr create ``` -Some agents don't commit automatically and leave changes uncommitted in the -worktree. If that happens, commit from the worktree directory before pushing. +Some agents don't commit automatically. If `git log sandbox-/` +shows nothing new, open a shell in the sandbox and commit from there before +fetching: + +```console +$ sbx exec -it my-sandbox bash +$ git -C $(pwd) commit -am "save work" +``` See [Workspace trust](security/workspace.md) for security considerations when reviewing agent changes. @@ -187,31 +193,22 @@ For common signing failures, see #### Cleanup -`sbx rm` removes the sandbox and all of its worktrees and branches. - -#### Ignoring the `.sbx/` directory - -Branch mode stores worktrees under `.sbx/` in your repository root. To keep -this directory out of `git status`, add it to your project's `.gitignore`: +`sbx rm` deletes the sandbox, its in-container clone, the published Git +port, and the `sandbox-` remote on your host repository. The +local branch the agent worked on stays on your host so you don't lose any +commits you've already fetched. -```console -$ echo '.sbx/' >> .gitignore -``` - -Or, to ignore it across all repositories, add `.sbx/` to your global gitignore: - -```console -$ echo '.sbx/' >> "$(git config --global core.excludesFile)" -``` +#### Restrictions -> [!TIP] -> If `git config --global core.excludesFile` is empty, set one first: -> `git config --global core.excludesFile ~/.gitignore`. +A few configurations are incompatible with branch mode and are rejected at +create time: -You can also create Git worktrees yourself and run an agent directly in one, -but the sandbox won't have access to the `.git` directory in the parent -repository. This means the agent can't commit, push, or use Git. `--branch` -solves this by setting up the worktree so that Git works inside the sandbox. +- `--branch` together with `--workspace-volume`: the source-repository + isolation relies on bind-mounting your Git root, which is incompatible + with a volume-backed workspace. +- `--branch` from inside a host Git worktree: the bind mount can't resolve + the worktree's `.git` pointer file. Run `sbx create --branch ...` from + the main repository instead. ## Reconnecting and naming @@ -250,8 +247,8 @@ $ sbx run claude-my-project You can mount extra directories into a sandbox alongside the main workspace. The first path is the primary workspace — the agent starts here, and the -sandbox's Git worktree is created from this directory if you use `--branch`. -Extra workspaces are always mounted directly. +sandbox's branch-mode clone is created from this directory if you use +`--branch`. Extra workspaces are always mounted directly. All workspaces appear inside the sandbox at their absolute host paths. Append `:ro` to mount an extra workspace read-only — useful for reference material or