From 2f9b9fe90e3918155cf4b7d3182f5a371ad41b8f Mon Sep 17 00:00:00 2001 From: Nicolas De Loof Date: Thu, 21 May 2026 19:37:28 +0200 Subject: [PATCH] sandboxes: document --clone, in-container clone and source-repository isolation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces the `--branch[=NAME]` documentation with `--clone` (boolean) on both `sbx create` and `sbx run`, matching the CLI rename in docker/sandboxes#3055. Clone mode is now strictly about running the agent on a private in-container `git clone --reference` of the host repository; no host-side branch is created on the user's behalf — the user instructs the agent to `git checkout -b ...` inside the sandbox if a dedicated working branch is needed. Key documentation changes: - usage.md: "Branch mode" section is renamed to "Clone mode" and rewritten end-to-end. Drops the `.sbx/-worktrees/...` directory mention (no longer created), the host-side branch creation/checkout step, the `--branch=auto` flow, and the legacy "Multiple branches per sandbox" section. Adds: * an explicit note that clone mode is fixed at create time; * the forge-remote propagation behaviour (origin/upstream propagated from host into the in-container clone, local-path remotes skipped); * a Cleanup warning about lost in-container commits when running `sbx rm` (mirrors the runtime warning). - security/isolation.md: adds the "Source-repository isolation" section introduced in #25007. Same content, but uses `--clone` and "clone mode" terminology throughout, plus a clarifying note that `sbx rm` warns before dropping unfetched/unpushed commits. - security/workspace.md: the existing "Branch mode" stub (claiming the old `--branch` was a "workflow convenience, not a security boundary") is replaced with a "Clone mode" section that explicitly calls out the isolation guarantee — clone mode IS a security boundary, the host's `.git` cannot be touched. - security/_index.md: source-repository isolation is added to the five-layer model (was four). Wording aligned with isolation.md. Migration: callers that scripted `sbx create --branch=feature/x` now pass `--clone` and tell the agent to create the branch. This PR is an alternative to docker/docs#25007, written against the post-rename CLI. Signed-off-by: Nicolas De loof --- .../manuals/ai/sandboxes/security/_index.md | 5 +- .../ai/sandboxes/security/isolation.md | 68 ++++++- .../ai/sandboxes/security/workspace.md | 21 ++- content/manuals/ai/sandboxes/usage.md | 174 ++++++++++-------- 4 files changed, 177 insertions(+), 91 deletions(-) diff --git a/content/manuals/ai/sandboxes/security/_index.md b/content/manuals/ai/sandboxes/security/_index.md index 12a2cb72214e..dc2375751909 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 (clone 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 b533c6388131..e4d4b8e625d9 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 clone mode), and credential proxy. ## Hypervisor isolation @@ -73,6 +74,65 @@ Host system └── [VM] Containers created by agent ``` +## Source-repository isolation + +When you start a sandbox with `--clone` (see the +[clone-mode workflow](../usage.md#clone-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 a read-only mount. 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 repository. 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 can't happen — concurrent `git` commands on the + host and inside the sandbox don't race on a shared `.git/index` or shared + refs because there is no shared writable state. +- The agent can't 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. +- Credentials, signing keys, and global settings declared in your + repository's `.git/config` stay on the host. The agent's clone has its + own independent configuration. +- Cleanup is automatic: `sbx rm` deletes the clone, the published port, + and the `sandbox-` remote on your host. Any in-container commits + that have not been fetched or pushed to an upstream remote are dropped + with the sandbox — `sbx rm` prints a warning before doing so. + +In direct mode (no `--clone`), the agent edits your working tree directly +and this isolation does not apply. Use clone 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 900677b50a9d..e668fff9b703 100644 --- a/content/manuals/ai/sandboxes/security/workspace.md +++ b/content/manuals/ai/sandboxes/security/workspace.md @@ -35,11 +35,18 @@ includes: > building, or opening the project in an IDE. Review these files after any agent > session before performing those actions. -## Branch mode +## Clone 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 `--clone` flag isolates the agent from your host repository: it works +on a private clone inside the sandbox, with your `.git` directory +bind-mounted as a read-only 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#clone-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 `--clone`, 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/ ``` Pay particular attention to: diff --git a/content/manuals/ai/sandboxes/usage.md b/content/manuals/ai/sandboxes/usage.md index 7d1eda900bc1..9ead55cf95a3 100644 --- a/content/manuals/ai/sandboxes/usage.md +++ b/content/manuals/ai/sandboxes/usage.md @@ -79,117 +79,131 @@ When your workspace is a Git repository, the agent edits your working tree directly by default. Changes appear in your working tree immediately, the same as working in a normal terminal. -If you run multiple agents on the same repository at once, use [branch -mode](#branch-mode) to give each agent its own branch and working directory. +If you want the agent to work in isolation from your host repository — for +example to run multiple agents in parallel, or to prevent any chance of an +agent rewriting your local Git state — use [clone mode](#clone-mode). The +agent runs against a private Git clone inside the sandbox; your host repository +sees the agent's commits only after you explicitly fetch them. ### Direct mode (default) The agent edits your working tree directly. Stage, commit, and push as you normally would. If you run multiple agents on the same repository at the same time, they may step on each other's changes. See -[branch mode](#branch-mode) for an alternative. - -### 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. - -#### Starting a branch +[clone mode](#clone-mode) for an alternative. + +### Clone mode + +Pass `--clone` to run the agent on a private Git clone living entirely +inside the sandbox, instead of bind-mounting your working tree. Your host +repository is mounted read-only as the clone's reference, so the agent — even +with full root inside the VM — cannot modify any byte of your `.git` +directory or working tree. You can set `--clone` on `create` or, equivalently, +on `run` at create time. + +When `--clone` is active: + +- The agent works on a private clone populated by + `git clone --reference` from your repository, on the sandbox's overlay + filesystem. The clone has its own index, refs, and 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 repository's Git root is bind-mounted at `/run/sandbox/source` as a + read-only mount. The agent's `git clone --reference` reads from this + mount; nothing on the host is writable from inside the sandbox. +- The clone follows whatever HEAD your host repository is on at create time. + No branch is created automatically — if you want the agent to work on a + dedicated branch, instruct the agent to `git checkout -b my-feature` + inside the sandbox before it starts editing. +- 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`. +- Forge remotes you have on the host (`origin`, `upstream`, …) are + propagated into the in-container clone with their existing URLs, so the + agent can `git push origin …` to your GitHub fork as you would on the + host. Local-path remotes (`file://`, paths) are skipped because they + aren't reachable from inside the sandbox. + +See [Source-repository isolation](security/isolation.md#source-repository-isolation) +for the security boundary. + +#### Starting a sandbox in clone mode ```console -$ sbx run claude --branch my-feature # agent works on the my-feature branch +$ sbx run --clone claude # private clone of the current repository ``` -Use `--branch auto` to let the CLI generate a branch name for you: +You can also create the sandbox first and attach later: ```console -$ sbx run claude --branch auto +$ sbx create --clone --name my-sandbox claude . +$ sbx run my-sandbox # resumes in the in-container clone ``` -You can also create the sandbox first and add a branch at run time: +> [!NOTE] +> Clone mode is fixed at create time. Recreate the sandbox with +> `sbx create --clone ...` to switch an existing sandbox into clone mode. -```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: - -```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 -``` - -#### Multiple branches per sandbox +#### Reviewing and pushing changes -You can run multiple worktrees in the same sandbox by passing different branch -names: +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 extra tooling, no `cd` into +a separate directory: ```console -$ sbx run --branch feature-a my-sandbox -$ sbx run --branch feature-b my-sandbox +$ git fetch sandbox-my-sandbox # pull the agent's commits +$ git log sandbox-my-sandbox/ # see what the agent did +$ git diff main..sandbox-my-sandbox/ +$ git checkout -b my-feature sandbox-my-sandbox/ +$ git push -u origin my-feature +$ gh pr create ``` -#### Reviewing and pushing changes +If the agent committed on a dedicated branch (because you asked it to +`git checkout -b ...`), that branch name appears on the `sandbox-` +remote. If it stayed on the HEAD it inherited at create time, its commits +extend that branch instead — you'll see them by fetching and diffing. -To review the agent's work, find the worktree with `git worktree list`, then -push or open a PR from there: +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. `sbx exec` drops you into the in-container clone: ```console -$ git worktree list # find the worktree path -$ cd .sbx/-worktrees/my-feature -$ git log # see what the agent did -$ git push -u origin my-feature -$ gh pr create +$ sbx exec -it my-sandbox bash +$ git commit -am "save work" ``` -Some agents don't commit automatically and leave changes uncommitted in the -worktree. If that happens, commit from the worktree directory before pushing. - See [Workspace trust](security/workspace.md) for security considerations when reviewing agent changes. #### Cleanup -`sbx rm` removes the sandbox and all of its worktrees and branches. - -#### Ignoring the `.sbx/` directory +`sbx rm` deletes the sandbox, its in-container clone, the published Git +port, and the `sandbox-` remote on your host repository. -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`: +> [!WARNING] +> Any commits the agent made inside the sandbox that you have not yet +> fetched (via `git fetch sandbox-`) or pushed to an upstream +> remote will be lost — the in-container clone lives on the sandbox's +> overlay filesystem and is dropped with it. `sbx rm` prints a warning +> for clone-mode sandboxes; review it before confirming the removal. -```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 clone 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. +- `--clone` together with `--workspace-volume`: the source-repository + isolation relies on bind-mounting your Git root, which is incompatible + with a volume-backed workspace. +- `--clone` from inside a host Git worktree: the bind mount can't resolve + the worktree's `.git` pointer file. Run `sbx create --clone ...` from + the main repository instead. +- `--clone` on a non-Git workspace: clone mode requires a Git repository. + Run `sbx create` without `--clone` for non-Git workspaces. ### Signed commits @@ -253,7 +267,7 @@ $ sbx create claude . ``` Unlike `run`, `create` requires an explicit workspace path. It uses direct -mode by default, or pass `--branch` for [branch mode](#branch-mode). Attach +mode by default, or pass `--clone` for [clone mode](#clone-mode). Attach later with `sbx run`: ```console @@ -264,8 +278,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 in-container Git clone is populated from this directory if you +use `--clone`. 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