From cdde205ba837f5c3deff203900d35153882631f0 Mon Sep 17 00:00:00 2001 From: David Karlsson <35727626+dvdksn@users.noreply.github.com> Date: Mon, 9 Mar 2026 09:41:02 +0100 Subject: [PATCH] sandboxes: add sbx documentation Add full documentation for the sbx CLI sandbox experience including get-started, usage guide, architecture, agents (claude-code, codex, copilot, gemini, kiro, opencode, cagent, custom-environments), security (isolation, credentials, policy, workspace trust), troubleshooting, and FAQ. Update _index.md as the section landing page. Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com> --- _vale/config/vocabularies/Docker/accept.txt | 4 + content/manuals/ai/sandboxes/_index.md | 65 +++- content/manuals/ai/sandboxes/agents/_index.md | 21 ++ .../ai/sandboxes/agents/claude-code.md | 72 ++++ content/manuals/ai/sandboxes/agents/codex.md | 66 ++++ .../manuals/ai/sandboxes/agents/copilot.md | 70 ++++ .../sandboxes/agents/custom-environments.md | 176 ++++++++++ .../ai/sandboxes/agents/docker-agent.md | 72 ++++ content/manuals/ai/sandboxes/agents/gemini.md | 75 +++++ content/manuals/ai/sandboxes/agents/kiro.md | 98 ++++++ .../manuals/ai/sandboxes/agents/opencode.md | 86 +++++ content/manuals/ai/sandboxes/architecture.md | 69 ++++ .../manuals/ai/sandboxes/docker-desktop.md | 31 +- content/manuals/ai/sandboxes/faq.md | 104 ++++++ content/manuals/ai/sandboxes/get-started.md | 110 +++++++ .../ai/sandboxes/images/sbx-security.png | Bin 0 -> 121269 bytes .../manuals/ai/sandboxes/security/_index.md | 91 ++++++ .../ai/sandboxes/security/credentials.md | 147 +++++++++ .../manuals/ai/sandboxes/security/defaults.md | 68 ++++ .../ai/sandboxes/security/isolation.md | 86 +++++ .../manuals/ai/sandboxes/security/policy.md | 293 +++++++++++++++++ .../ai/sandboxes/security/workspace.md | 73 +++++ .../manuals/ai/sandboxes/troubleshooting.md | 137 ++++++++ content/manuals/ai/sandboxes/usage.md | 307 ++++++++++++++++++ content/reference/cli/sbx/_content.gotmpl | 48 +++ data/sbx_cli/sbx.yaml | 32 ++ data/sbx_cli/sbx_completion.yaml | 21 ++ data/sbx_cli/sbx_completion_bash.yaml | 39 +++ data/sbx_cli/sbx_completion_fish.yaml | 30 ++ data/sbx_cli/sbx_completion_powershell.yaml | 27 ++ data/sbx_cli/sbx_completion_zsh.yaml | 41 +++ data/sbx_cli/sbx_create.yaml | 56 ++++ data/sbx_cli/sbx_create_claude.yaml | 49 +++ data/sbx_cli/sbx_create_codex.yaml | 49 +++ data/sbx_cli/sbx_create_copilot.yaml | 49 +++ data/sbx_cli/sbx_create_docker-agent.yaml | 49 +++ data/sbx_cli/sbx_create_gemini.yaml | 49 +++ data/sbx_cli/sbx_create_kiro.yaml | 49 +++ data/sbx_cli/sbx_create_opencode.yaml | 49 +++ data/sbx_cli/sbx_create_shell.yaml | 49 +++ data/sbx_cli/sbx_exec.yaml | 58 ++++ data/sbx_cli/sbx_login.yaml | 15 + data/sbx_cli/sbx_logout.yaml | 15 + data/sbx_cli/sbx_ls.yaml | 24 ++ data/sbx_cli/sbx_policy.yaml | 28 ++ data/sbx_cli/sbx_policy_allow.yaml | 21 ++ data/sbx_cli/sbx_policy_allow_network.yaml | 33 ++ data/sbx_cli/sbx_policy_deny.yaml | 21 ++ data/sbx_cli/sbx_policy_deny_network.yaml | 26 ++ data/sbx_cli/sbx_policy_log.yaml | 46 +++ data/sbx_cli/sbx_policy_ls.yaml | 29 ++ data/sbx_cli/sbx_policy_reset.yaml | 33 ++ data/sbx_cli/sbx_policy_rm.yaml | 17 + data/sbx_cli/sbx_policy_rm_network.yaml | 32 ++ data/sbx_cli/sbx_policy_set-default.yaml | 38 +++ data/sbx_cli/sbx_ports.yaml | 48 +++ data/sbx_cli/sbx_reset.yaml | 44 +++ data/sbx_cli/sbx_rm.yaml | 30 ++ data/sbx_cli/sbx_run.yaml | 49 +++ data/sbx_cli/sbx_save.yaml | 30 ++ data/sbx_cli/sbx_secret.yaml | 27 ++ data/sbx_cli/sbx_secret_ls.yaml | 33 ++ data/sbx_cli/sbx_secret_rm.yaml | 32 ++ data/sbx_cli/sbx_secret_set.yaml | 42 +++ data/sbx_cli/sbx_stop.yaml | 19 ++ data/sbx_cli/sbx_version.yaml | 15 + data/summary.yaml | 2 + hugo.yaml | 7 + layouts/sbx-cli.html | 179 ++++++++++ 69 files changed, 3981 insertions(+), 19 deletions(-) create mode 100644 content/manuals/ai/sandboxes/agents/_index.md create mode 100644 content/manuals/ai/sandboxes/agents/claude-code.md create mode 100644 content/manuals/ai/sandboxes/agents/codex.md create mode 100644 content/manuals/ai/sandboxes/agents/copilot.md create mode 100644 content/manuals/ai/sandboxes/agents/custom-environments.md create mode 100644 content/manuals/ai/sandboxes/agents/docker-agent.md create mode 100644 content/manuals/ai/sandboxes/agents/gemini.md create mode 100644 content/manuals/ai/sandboxes/agents/kiro.md create mode 100644 content/manuals/ai/sandboxes/agents/opencode.md create mode 100644 content/manuals/ai/sandboxes/architecture.md create mode 100644 content/manuals/ai/sandboxes/faq.md create mode 100644 content/manuals/ai/sandboxes/get-started.md create mode 100644 content/manuals/ai/sandboxes/images/sbx-security.png create mode 100644 content/manuals/ai/sandboxes/security/_index.md create mode 100644 content/manuals/ai/sandboxes/security/credentials.md create mode 100644 content/manuals/ai/sandboxes/security/defaults.md create mode 100644 content/manuals/ai/sandboxes/security/isolation.md create mode 100644 content/manuals/ai/sandboxes/security/policy.md create mode 100644 content/manuals/ai/sandboxes/security/workspace.md create mode 100644 content/manuals/ai/sandboxes/troubleshooting.md create mode 100644 content/manuals/ai/sandboxes/usage.md create mode 100644 content/reference/cli/sbx/_content.gotmpl create mode 100644 data/sbx_cli/sbx.yaml create mode 100644 data/sbx_cli/sbx_completion.yaml create mode 100644 data/sbx_cli/sbx_completion_bash.yaml create mode 100644 data/sbx_cli/sbx_completion_fish.yaml create mode 100644 data/sbx_cli/sbx_completion_powershell.yaml create mode 100644 data/sbx_cli/sbx_completion_zsh.yaml create mode 100644 data/sbx_cli/sbx_create.yaml create mode 100644 data/sbx_cli/sbx_create_claude.yaml create mode 100644 data/sbx_cli/sbx_create_codex.yaml create mode 100644 data/sbx_cli/sbx_create_copilot.yaml create mode 100644 data/sbx_cli/sbx_create_docker-agent.yaml create mode 100644 data/sbx_cli/sbx_create_gemini.yaml create mode 100644 data/sbx_cli/sbx_create_kiro.yaml create mode 100644 data/sbx_cli/sbx_create_opencode.yaml create mode 100644 data/sbx_cli/sbx_create_shell.yaml create mode 100644 data/sbx_cli/sbx_exec.yaml create mode 100644 data/sbx_cli/sbx_login.yaml create mode 100644 data/sbx_cli/sbx_logout.yaml create mode 100644 data/sbx_cli/sbx_ls.yaml create mode 100644 data/sbx_cli/sbx_policy.yaml create mode 100644 data/sbx_cli/sbx_policy_allow.yaml create mode 100644 data/sbx_cli/sbx_policy_allow_network.yaml create mode 100644 data/sbx_cli/sbx_policy_deny.yaml create mode 100644 data/sbx_cli/sbx_policy_deny_network.yaml create mode 100644 data/sbx_cli/sbx_policy_log.yaml create mode 100644 data/sbx_cli/sbx_policy_ls.yaml create mode 100644 data/sbx_cli/sbx_policy_reset.yaml create mode 100644 data/sbx_cli/sbx_policy_rm.yaml create mode 100644 data/sbx_cli/sbx_policy_rm_network.yaml create mode 100644 data/sbx_cli/sbx_policy_set-default.yaml create mode 100644 data/sbx_cli/sbx_ports.yaml create mode 100644 data/sbx_cli/sbx_reset.yaml create mode 100644 data/sbx_cli/sbx_rm.yaml create mode 100644 data/sbx_cli/sbx_run.yaml create mode 100644 data/sbx_cli/sbx_save.yaml create mode 100644 data/sbx_cli/sbx_secret.yaml create mode 100644 data/sbx_cli/sbx_secret_ls.yaml create mode 100644 data/sbx_cli/sbx_secret_rm.yaml create mode 100644 data/sbx_cli/sbx_secret_set.yaml create mode 100644 data/sbx_cli/sbx_stop.yaml create mode 100644 data/sbx_cli/sbx_version.yaml create mode 100644 layouts/sbx-cli.html diff --git a/_vale/config/vocabularies/Docker/accept.txt b/_vale/config/vocabularies/Docker/accept.txt index a1e71e343d..35196ee802 100644 --- a/_vale/config/vocabularies/Docker/accept.txt +++ b/_vale/config/vocabularies/Docker/accept.txt @@ -237,6 +237,7 @@ Turtlesim typesafe Ubuntu ufw +uv umask uncaptured Uncaptured @@ -259,6 +260,9 @@ windowsfilter WireMock workdir WORKDIR +[Ww]orktrees? +[Pp]assthrough +[Pp]reconfigured Xdebug xUnit XQuartz diff --git a/content/manuals/ai/sandboxes/_index.md b/content/manuals/ai/sandboxes/_index.md index bf7b77dd73..8b7876b0da 100644 --- a/content/manuals/ai/sandboxes/_index.md +++ b/content/manuals/ai/sandboxes/_index.md @@ -5,11 +5,66 @@ weight: 20 params: sidebar: group: AI + badge: + color: violet + text: Experimental --- -Docker Sandboxes let you run AI coding agents in isolated environments using -the `docker sandbox` command. Sandboxes require Docker Desktop and run agents -in microVMs with private Docker daemons. +{{< summary-bar feature_name="Docker Sandboxes sbx" >}} -For setup instructions and usage details, see the -[Docker Desktop sandboxes](docker-desktop.md) page. +Docker Sandboxes run AI coding agents in isolated microVM sandboxes. Each +sandbox gets its own Docker daemon, filesystem, and network — the agent can +build containers, install packages, and modify files without touching your host +system. + +## Get started + +Install the `sbx` CLI and sign in: + +{{< tabs >}} +{{< tab name="macOS" >}} + +```console +$ brew install docker/tap/sbx +$ sbx login +``` + +{{< /tab >}} +{{< tab name="Windows" >}} + +```powershell +> winget install -h Docker.sbx +> sbx login +``` + +{{< /tab >}} +{{< /tabs >}} + +Then launch an agent in a sandbox: + +```console +$ cd ~/my-project +$ sbx run claude +``` + +See the [get started guide](get-started.md) for a full walkthrough, or jump to +the [usage guide](usage.md) for common patterns. + +## Learn more + +- [Agents](agents/) — supported agents and per-agent configuration +- [Custom environments](agents/custom-environments.md) — build reusable sandbox + images with pre-installed tools +- [Architecture](architecture.md) — microVM isolation, workspace mounting, + networking +- [Security](security/) — isolation model, credential handling, network + policies, workspace trust +- [CLI reference](/reference/cli/sbx/) — full list of `sbx` commands and options +- [Troubleshooting](troubleshooting.md) — common issues and fixes +- [FAQ](faq.md) — login requirements, telemetry, etc + +## Docker Desktop integration + +Docker Desktop also includes a [built-in sandbox command](docker-desktop.md) +(`docker sandbox`) with a subset of features. The `sbx` CLI is recommended for +most use cases. diff --git a/content/manuals/ai/sandboxes/agents/_index.md b/content/manuals/ai/sandboxes/agents/_index.md new file mode 100644 index 0000000000..de3428d47c --- /dev/null +++ b/content/manuals/ai/sandboxes/agents/_index.md @@ -0,0 +1,21 @@ +--- +title: Supported agents +linkTitle: Agents +weight: 30 +description: AI coding agents supported by Docker Sandboxes. +--- + +{{< summary-bar feature_name="Docker Sandboxes sbx" >}} + +Docker Sandboxes runs the following agents out of the box: + +- [Claude Code](claude-code/) +- [Codex](codex/) +- [Copilot](copilot/) +- [Gemini](gemini/) +- [Kiro](kiro/) +- [OpenCode](opencode/) +- [Docker Agent](docker-agent/) + +Want to pre-install tools or customize an agent's environment? +See [Custom environments](custom-environments/). diff --git a/content/manuals/ai/sandboxes/agents/claude-code.md b/content/manuals/ai/sandboxes/agents/claude-code.md new file mode 100644 index 0000000000..f606a2ee7e --- /dev/null +++ b/content/manuals/ai/sandboxes/agents/claude-code.md @@ -0,0 +1,72 @@ +--- +title: Claude Code +weight: 10 +description: | + Use Claude Code in Docker Sandboxes with authentication, configuration, and + YOLO mode for AI-assisted development. +--- + +{{< summary-bar feature_name="Docker Sandboxes sbx" >}} + +Official documentation: [Claude Code](https://code.claude.com/docs) + +## Quick start + +Launch Claude Code in a sandbox by pointing it at a project directory: + +```console +$ sbx run claude ~/my-project +``` + +The workspace parameter defaults to the current directory, so `sbx run claude` +from inside your project works too. To start Claude with a specific prompt: + +```console +$ sbx run claude --name my-sandbox -- "Add error handling to the login function" +``` + +Everything after `--` is passed directly to Claude Code. You can also pipe in a +prompt from a file with `-- "$(cat prompt.txt)"`. + +## Authentication + +Claude Code requires either an Anthropic API key or a Claude subscription. + +**API key**: Store your key using +[stored secrets](../security/credentials.md#stored-secrets): + +```console +$ sbx secret set -g anthropic +``` + +Alternatively, export the `ANTHROPIC_API_KEY` environment variable in your +shell before running the sandbox. See +[Credentials](../security/credentials.md) for details on both methods. + +**Claude subscription**: If no API key is set, Claude Code prompts you to +authenticate interactively using OAuth. The proxy handles the OAuth flow, so +credentials aren't stored inside the sandbox. + +## Configuration + +Sandboxes don't pick up user-level configuration from your host, such as +`~/.claude`. Only project-level configuration in the working directory is +available inside the sandbox. See +[Why doesn't the sandbox use my user-level agent configuration?](../faq.md#why-doesnt-the-sandbox-use-my-user-level-agent-configuration) +for workarounds. + +Any Claude Code CLI options can be passed after the `--` separator: + +```console +$ sbx run claude --name my-sandbox -- --continue +``` + +See the [Claude Code CLI reference](https://code.claude.com/docs/en/cli-reference) +for available options. + +## Base image + +The sandbox uses `docker/sandbox-templates:claude-code` and launches Claude Code +with `--dangerously-skip-permissions` by default. See +[Custom environments](custom-environments.md) to build your own image on +top of this base. diff --git a/content/manuals/ai/sandboxes/agents/codex.md b/content/manuals/ai/sandboxes/agents/codex.md new file mode 100644 index 0000000000..d949ba0e15 --- /dev/null +++ b/content/manuals/ai/sandboxes/agents/codex.md @@ -0,0 +1,66 @@ +--- +title: Codex +weight: 20 +description: | + Use OpenAI Codex in Docker Sandboxes with API key authentication and YOLO + mode configuration. +--- + +{{< summary-bar feature_name="Docker Sandboxes sbx" >}} + +This guide covers authentication, configuration, and usage of Codex in a +sandboxed environment. + +Official documentation: [Codex CLI](https://developers.openai.com/codex/cli) + +## Quick start + +Create a sandbox and run Codex for a project directory: + +```console +$ sbx run codex ~/my-project +``` + +The workspace parameter is optional and defaults to the current directory: + +```console +$ cd ~/my-project +$ sbx run codex +``` + +## Authentication + +Codex requires an OpenAI API key. Store your key using +[stored secrets](../security/credentials.md#stored-secrets): + +```console +$ sbx secret set -g openai +``` + +Alternatively, export the `OPENAI_API_KEY` environment variable in your shell +before running the sandbox. See +[Credentials](../security/credentials.md) for details on both methods. + +## Configuration + +Sandboxes don't pick up user-level configuration from your host, such as +`~/.codex`. Only project-level configuration in the working directory is +available inside the sandbox. See +[Why doesn't the sandbox use my user-level agent configuration?](../faq.md#why-doesnt-the-sandbox-use-my-user-level-agent-configuration) +for workarounds. + +The sandbox runs Codex without approval prompts by default. Pass additional +Codex CLI options after `--`: + +```console +$ sbx run codex --name -- +``` + +## Base image + +Template: `docker/sandbox-templates:codex` + +Preconfigured to run without approval prompts. + +See [Custom environments](custom-environments.md) to pre-install tools or +customize this environment. diff --git a/content/manuals/ai/sandboxes/agents/copilot.md b/content/manuals/ai/sandboxes/agents/copilot.md new file mode 100644 index 0000000000..6ffcc93b7f --- /dev/null +++ b/content/manuals/ai/sandboxes/agents/copilot.md @@ -0,0 +1,70 @@ +--- +title: Copilot +weight: 30 +description: | + Use GitHub Copilot in Docker Sandboxes with GitHub token authentication and + trusted folder configuration. +--- + +{{< summary-bar feature_name="Docker Sandboxes sbx" >}} + +This guide covers authentication, configuration, and usage of GitHub Copilot +in a sandboxed environment. + +Official documentation: [GitHub Copilot CLI](https://docs.github.com/en/copilot/how-tos/copilot-cli) + +## Quick start + +Create a sandbox and run Copilot for a project directory: + +```console +$ sbx run copilot ~/my-project +``` + +The workspace parameter is optional and defaults to the current directory: + +```console +$ cd ~/my-project +$ sbx run copilot +``` + +## Authentication + +Copilot requires a GitHub token with Copilot access. Store your token using +[stored secrets](../security/credentials.md#stored-secrets): + +```console +$ echo "$(gh auth token)" | sbx secret set -g github +``` + +Alternatively, export the `GH_TOKEN` or `GITHUB_TOKEN` environment variable in +your shell before running the sandbox. See +[Credentials](../security/credentials.md) for details on both methods. + +## Configuration + +Sandboxes don't pick up user-level configuration from your host. Only +project-level configuration in the working directory is available inside the +sandbox. See +[Why doesn't the sandbox use my user-level agent configuration?](../faq.md#why-doesnt-the-sandbox-use-my-user-level-agent-configuration) +for workarounds. + +Copilot is configured to trust the workspace directory by default, so it +operates without repeated confirmations for workspace files. + +### Pass options at runtime + +Pass Copilot CLI options after `--`: + +```console +$ sbx run copilot --name -- +``` + +## Base image + +Template: `docker/sandbox-templates:copilot` + +Preconfigured to trust the workspace directory and run without approval prompts. + +See [Custom environments](custom-environments.md) to pre-install tools or +customize this environment. diff --git a/content/manuals/ai/sandboxes/agents/custom-environments.md b/content/manuals/ai/sandboxes/agents/custom-environments.md new file mode 100644 index 0000000000..4bfac553da --- /dev/null +++ b/content/manuals/ai/sandboxes/agents/custom-environments.md @@ -0,0 +1,176 @@ +--- +title: Custom environments +weight: 80 +description: Customize agent sandbox environments or use the shell sandbox for manual setup. +--- + +{{< summary-bar feature_name="Docker Sandboxes sbx" >}} + +Every sandbox is customizable — agents install packages, pull images, and +configure tools as they work, and those changes persist for the sandbox's +lifetime. This page covers two things beyond that: a general-purpose shell +sandbox for manual setup, and custom templates that capture a configured +environment into a reusable image so you don't have to set it up again every +time. + +## Shell sandbox + +`sbx run shell` drops you into a Bash login shell inside a sandbox +with no pre-installed agent binary. It's useful for installing and +configuring agents manually, testing custom implementations, or inspecting a +running environment. + +```console +$ sbx run shell ~/my-project +``` + +The workspace path defaults to the current directory. To run a one-off command +instead of an interactive shell, pass it after `--`: + +```console +$ sbx run shell -- -c "echo 'Hello from sandbox'" +``` + +Set your API keys as environment variables so the sandbox proxy can inject them +into API requests automatically. Credentials are never stored inside the VM: + +```console +$ export ANTHROPIC_API_KEY=sk-ant-xxxxx +$ export OPENAI_API_KEY=sk-xxxxx +``` + +Once inside the shell, you can install agents using their standard methods, +for example `npm install -g @continuedev/cli`. For complex setups, build a +[custom template](#custom-templates) instead of installing interactively each +time. + +The shell sandbox uses the `shell` base image — the common base environment +without a pre-installed agent. + +## Custom templates + +Custom templates are reusable sandbox images that extend one of the built-in +agent environments with additional tools and configuration baked in. Instead of +asking the agent to install packages every time, build a template once and +reuse it across sandboxes and team members. + +Templates make sense when multiple people need the same environment, when setup +involves steps that are tedious to repeat, or when you need pinned versions of +specific tools. For one-off work, the default image is fine — ask the agent to +install what's needed. + +> [!NOTE] +> Custom templates customize an existing agent's environment — they don't +> create new agent runtimes. The agent that launches inside the sandbox is +> determined by the base image variant you extend and the agent you specify +> in the `sbx run` command, not by binaries installed in the template. + +### Base images + +All sandbox templates are published as +`docker/sandbox-templates:`. They are based on Ubuntu and run as a +non-root `agent` user with sudo access. Most variants include Git, Docker +CLI, and common development tools like Node.js, Python, Go, and Java. + +| Variant | Agent | +| --------------------- | -------------------------------------------------------------------- | +| `claude-code` | [Claude Code](https://claude.ai/download) | +| `claude-code-minimal` | Claude Code with a minimal toolset (no Node.js, Python, Go, or Java) | +| `codex` | [OpenAI Codex](https://github.com/openai/codex) | +| `copilot` | [GitHub Copilot](https://github.com/github/copilot-cli) | +| `docker-agent` | [Docker Agent](https://github.com/docker/docker-agent) | +| `gemini` | [Gemini CLI](https://github.com/google-gemini/gemini-cli) | +| `kiro` | [Kiro](https://kiro.dev) | +| `opencode` | [OpenCode](https://opencode.ai) | +| `shell` | No agent pre-installed. Use for manual agent setup. | + +Each variant also has a `-docker` version (for example, +`claude-code-docker`) that includes a full Docker Engine running inside the +sandbox. The `-docker` variants are the defaults used by `sbx run` on macOS +and Linux. They run in privileged mode with a dedicated block volume at +`/var/lib/docker`, and `dockerd` starts automatically. + +The block volume defaults to 50 GB and uses a sparse file, so it only +consumes disk space as Docker writes to it. On Windows, the volume is not +sparse and the full 50 GB is allocated at creation time, which increases +startup time. For this reason, the non-docker variants are the default on +Windows. + +To override the volume size, set the `DOCKER_SANDBOXES_DOCKER_SIZE` +environment variable to a size string before starting the sandbox: + +```console +$ DOCKER_SANDBOXES_DOCKER_SIZE=10g sbx run claude +``` + +Use the non-docker variant if you don't need to build or run containers +inside the sandbox and want a lighter, non-privileged environment. Specify +it explicitly with `--template`: + +```console +$ sbx run claude --template docker.io/docker/sandbox-templates:claude-code +``` + +### Build a custom template + +Building a custom template requires [Docker Desktop](https://docs.docker.com/desktop/). + +Write a Dockerfile that extends one of the base images. Pick the variant that +matches the agent you plan to run. For example, extend `claude-code` to +customize a Claude Code environment, or `codex` to customize an OpenAI Codex +environment. + +The following example creates a Claude Code template with Rust and +protocol buffer tools pre-installed: + +```dockerfile +FROM docker/sandbox-templates:claude-code +USER root +RUN apt-get update && apt-get install -y protobuf-compiler +USER agent +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y +``` + +Use `root` for system-level package installations (`apt-get`), and switch back +to `agent` before installing user-level tools. Tools that install into the +home directory, such as `rustup`, `nvm`, or `pyenv`, must run as `agent` — +otherwise they install under `/root/` and aren't available in the sandbox. + +Build the image and push it to an OCI registry, such as Docker Hub: + +```console +$ docker build -t my-org/my-template:v1 --push . +``` + +> [!NOTE] +> The Docker daemon used by Docker Sandboxes pulls templates from a registry +> directly; it doesn't share the image store of your local Docker daemon on +> the host. + +Unless you use the permissive `allow-all` network policy, you may also need to +allow-list any domains that your custom tools depend on: + +```console +$ sbx policy allow network "*.example.com:443,example.com:443" +``` + +Then run a sandbox with your template. The agent you specify must match the +base image variant your template extends: + +```console +$ sbx run --template docker.io/my-org/my-template:v1 claude +``` + +Because this template extends the `claude-code` base image, you run it with +`claude`. If you extend `codex`, use `codex`; if you extend `shell`, use +`shell` (which drops you into a Bash shell with no agent). + +> [!NOTE] +> Unlike Docker commands, `sbx` does not automatically resolve the Docker Hub +> domain (`docker.io`) in image references. + +### Template caching + +Template images are cached locally. The first use pulls from the registry; +subsequent sandboxes reuse the cache. Cached images persist across sandbox +creation and deletion, and are cleared when you run `sbx reset`. diff --git a/content/manuals/ai/sandboxes/agents/docker-agent.md b/content/manuals/ai/sandboxes/agents/docker-agent.md new file mode 100644 index 0000000000..e6cb9df2eb --- /dev/null +++ b/content/manuals/ai/sandboxes/agents/docker-agent.md @@ -0,0 +1,72 @@ +--- +title: Docker Agent +weight: 70 +description: | + Use Docker Agent in Docker Sandboxes with multi-provider authentication + supporting OpenAI, Anthropic, and more. +--- + +{{< summary-bar feature_name="Docker Sandboxes sbx" >}} + +Official documentation: [Docker Agent](https://docs.docker.com/ai/docker-agent/) + +## Quick start + +Create a sandbox and run Docker Agent for a project directory: + +```console +$ sbx run docker-agent ~/my-project +``` + +The workspace parameter defaults to the current directory, so +`sbx run docker-agent` from inside your project works too. + +## Authentication + +Docker Agent supports multiple providers. Store keys for the providers you want +to use with [stored secrets](../security/credentials.md#stored-secrets): + +```console +$ sbx secret set -g openai +$ sbx secret set -g anthropic +$ sbx secret set -g google +$ sbx secret set -g xai +$ sbx secret set -g nebius +$ sbx secret set -g mistral +``` + +You only need to configure the providers you want to use. Docker Agent detects +available credentials and routes requests to the appropriate provider. + +Alternatively, export the environment variables (`OPENAI_API_KEY`, +`ANTHROPIC_API_KEY`, `GOOGLE_API_KEY`, `XAI_API_KEY`, `NEBIUS_API_KEY`, +`MISTRAL_API_KEY`) in your shell before running the sandbox. See +[Credentials](../security/credentials.md) for details on both methods. + +## Configuration + +Sandboxes don't pick up user-level configuration from your host. Only +project-level configuration in the working directory is available inside the +sandbox. See +[Why doesn't the sandbox use my user-level agent configuration?](../faq.md#why-doesnt-the-sandbox-use-my-user-level-agent-configuration) +for workarounds. + +The sandbox runs Docker Agent without approval prompts by default. Pass +additional CLI options after `--`: + +```console +$ sbx run docker-agent --name my-sandbox -- +``` + +For example, to specify a custom `agent.yml` configuration file: + +```console +$ sbx run docker-agent -- agent.yml +``` + +## Base image + +The sandbox uses `docker/sandbox-templates:docker-agent` and launches Docker +Agent without approval prompts by default. See +[Custom environments](custom-environments.md) to build your own image on top of +this base. diff --git a/content/manuals/ai/sandboxes/agents/gemini.md b/content/manuals/ai/sandboxes/agents/gemini.md new file mode 100644 index 0000000000..6e40b52e09 --- /dev/null +++ b/content/manuals/ai/sandboxes/agents/gemini.md @@ -0,0 +1,75 @@ +--- +title: Gemini +weight: 40 +description: | + Use Google Gemini in Docker Sandboxes with proxy-managed authentication and + API key configuration. +--- + +{{< summary-bar feature_name="Docker Sandboxes sbx" >}} + +This guide covers authentication, configuration, and usage of Google Gemini in +a sandboxed environment. + +Official documentation: [Gemini CLI](https://geminicli.com/docs/) + +## Quick start + +Create a sandbox and run Gemini for a project directory: + +```console +$ sbx run gemini ~/my-project +``` + +The workspace parameter is optional and defaults to the current directory: + +```console +$ cd ~/my-project +$ sbx run gemini +``` + +## Authentication + +Gemini requires either a Google API key or a Google account with Gemini access. + +**API key**: Store your key using +[stored secrets](../security/credentials.md#stored-secrets): + +```console +$ sbx secret set -g google +``` + +Alternatively, export the `GEMINI_API_KEY` or `GOOGLE_API_KEY` environment +variable in your shell before running the sandbox. See +[Credentials](../security/credentials.md) for details on both methods. + +**Google account**: If no API key is set, Gemini prompts you to sign in +interactively when it starts. Interactive authentication is scoped to the +sandbox and doesn't persist if you remove and recreate it. + +## Configuration + +Sandboxes don't pick up user-level configuration from your host, such as +`~/.gemini`. Only project-level configuration in the working directory is +available inside the sandbox. See +[Why doesn't the sandbox use my user-level agent configuration?](../faq.md#why-doesnt-the-sandbox-use-my-user-level-agent-configuration) +for workarounds. + +The sandbox runs Gemini without approval prompts by default and disables +Gemini's built-in sandbox tool (since the sandbox itself provides isolation). +Pass additional Gemini CLI options after `--`: + +```console +$ sbx run gemini --name -- +``` + +## Base image + +Template: `docker/sandbox-templates:gemini` + +Gemini is configured to disable its built-in OAuth flow. Authentication is +managed through the proxy with API keys. Preconfigured to run without +approval prompts. + +See [Custom environments](custom-environments.md) to pre-install tools or +customize this environment. diff --git a/content/manuals/ai/sandboxes/agents/kiro.md b/content/manuals/ai/sandboxes/agents/kiro.md new file mode 100644 index 0000000000..66ffcefc18 --- /dev/null +++ b/content/manuals/ai/sandboxes/agents/kiro.md @@ -0,0 +1,98 @@ +--- +title: Kiro +weight: 50 +description: | + Use Kiro in Docker Sandboxes with device flow authentication for interactive + AI-assisted development. +--- + +{{< summary-bar feature_name="Docker Sandboxes sbx" >}} + +This guide covers authentication, configuration, and usage of Kiro in a +sandboxed environment. + +Official documentation: [Kiro CLI](https://kiro.dev/docs/cli/) + +## Quick start + +Create a sandbox and run Kiro for a project directory: + +```console +$ sbx run kiro ~/my-project +``` + +The workspace parameter is optional and defaults to the current directory: + +```console +$ cd ~/my-project +$ sbx run kiro +``` + +On first run, Kiro prompts you to authenticate using device flow. + +## Authentication + +Kiro uses device flow authentication, which requires interactive login through +a web browser. This method provides secure authentication without storing API +keys directly. + +### Device flow login + +When you first run Kiro, it prompts you to authenticate: + +1. Kiro displays a URL and a verification code +2. Open the URL in your web browser +3. Enter the verification code +4. Complete the authentication flow in your browser +5. Return to the terminal - Kiro proceeds automatically + +The authentication session is persisted in the sandbox and doesn't require +repeated login unless you destroy and recreate the sandbox. + +### Manual login + +You can trigger the login flow manually: + +```console +$ sbx run kiro --name -- login --use-device-flow +``` + +This command initiates device flow authentication without starting a coding +session. + +### Authentication persistence + +Kiro stores authentication state in `~/.local/share/kiro-cli/data.sqlite3` +inside the sandbox. This database persists as long as the sandbox exists. If +you destroy the sandbox, you'll need to authenticate again when you recreate +it. + +## Configuration + +Sandboxes don't pick up user-level configuration from your host. Only +project-level configuration in the working directory is available inside the +sandbox. See +[Why doesn't the sandbox use my user-level agent configuration?](../faq.md#why-doesnt-the-sandbox-use-my-user-level-agent-configuration) +for workarounds. + +Kiro requires minimal configuration. The agent runs with trust-all-tools mode +by default, which lets it execute commands without repeated approval +prompts. + +### Pass options at runtime + +Pass Kiro CLI options after `--`: + +```console +$ sbx run kiro --name -- +``` + +## Base image + +Template: `docker/sandbox-templates:kiro` + +Preconfigured to run without approval prompts. Authentication state is +persisted across sandbox restarts. + +See [Custom environments](custom-environments.md) to pre-install tools or +customize this environment. diff --git a/content/manuals/ai/sandboxes/agents/opencode.md b/content/manuals/ai/sandboxes/agents/opencode.md new file mode 100644 index 0000000000..2fe81f227d --- /dev/null +++ b/content/manuals/ai/sandboxes/agents/opencode.md @@ -0,0 +1,86 @@ +--- +title: OpenCode +weight: 60 +description: | + Use OpenCode in Docker Sandboxes with multi-provider authentication and TUI + interface for AI development. +--- + +{{< summary-bar feature_name="Docker Sandboxes sbx" >}} + +This guide covers authentication, configuration, and usage of OpenCode in a +sandboxed environment. + +Official documentation: [OpenCode](https://opencode.ai/docs) + +## Quick start + +Create a sandbox and run OpenCode for a project directory: + +```console +$ sbx run opencode ~/my-project +``` + +The workspace parameter is optional and defaults to the current directory: + +```console +$ cd ~/my-project +$ sbx run opencode +``` + +OpenCode launches a TUI (text user interface) where you can select your +preferred LLM provider and interact with the agent. + +## Authentication + +OpenCode supports multiple providers. Store keys for the providers you want to +use with [stored secrets](../security/credentials.md#stored-secrets): + +```console +$ sbx secret set -g openai +$ sbx secret set -g anthropic +$ sbx secret set -g google +$ sbx secret set -g xai +$ sbx secret set -g groq +$ sbx secret set -g aws +``` + +You only need to configure the providers you want to use. OpenCode detects +available credentials and offers those providers in the TUI. + +You can also use environment variables (`OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, +`GOOGLE_API_KEY`, `XAI_API_KEY`, `GROQ_API_KEY`, `AWS_ACCESS_KEY_ID`). See +[Credentials](../security/credentials.md) for details on both methods. + +## Configuration + +Sandboxes don't pick up user-level configuration from your host. Only +project-level configuration in the working directory is available inside the +sandbox. See +[Why doesn't the sandbox use my user-level agent configuration?](../faq.md#why-doesnt-the-sandbox-use-my-user-level-agent-configuration) +for workarounds. + +OpenCode uses a TUI interface and doesn't require extensive configuration +files. The agent prompts you to select a provider when it starts, and you can +switch providers during a session. + +### TUI mode + +OpenCode launches in TUI mode by default. The interface shows: + +- Available LLM providers (based on configured credentials) +- Current conversation history +- File operations and tool usage +- Real-time agent responses + +Use keyboard shortcuts to navigate the interface and interact with the agent. + +## Base image + +Template: `docker/sandbox-templates:opencode` + +OpenCode supports multiple LLM providers with automatic credential injection +through the sandbox proxy. + +See [Custom environments](custom-environments.md) to pre-install tools or +customize this environment. diff --git a/content/manuals/ai/sandboxes/architecture.md b/content/manuals/ai/sandboxes/architecture.md new file mode 100644 index 0000000000..5499a652b2 --- /dev/null +++ b/content/manuals/ai/sandboxes/architecture.md @@ -0,0 +1,69 @@ +--- +title: Architecture +weight: 40 +description: Technical architecture of Docker Sandboxes; workspace mounting, storage, networking, and sandbox lifecycle. +--- + +{{< summary-bar feature_name="Docker Sandboxes sbx" >}} + +This page explains how Docker Sandboxes work under the hood. For the security +properties of the architecture, see [Sandbox isolation](security/isolation.md). + +## Workspace mounting + +Your workspace is mounted directly into the sandbox through a filesystem +passthrough. The sandbox sees your actual host files, so changes in either +direction are instant with no sync process involved. + +Your workspace is mounted at the same absolute path as on your host. Preserving +absolute paths means error messages, configuration files, and build outputs all +reference paths you can find on your host. The agent sees exactly the directory +structure you see, which reduces confusion when debugging or reviewing changes. + +## Storage and persistence + +When you create a sandbox, everything inside it persists until you remove it: +Docker images and containers built or pulled by the agent, installed packages, +agent state and history, and workspace changes. + +Sandboxes are isolated from each other. Each one maintains its own Docker +daemon state, image cache, and package installations. Multiple sandboxes don't +share images or layers. + +Each sandbox consumes disk space for its VM image, Docker images, container +layers, and volumes, and this grows as you build images and install packages. + +## Networking + +All outbound traffic from the sandbox routes through an HTTP/HTTPS proxy on +your host. Agents are configured to use the proxy automatically. The proxy +enforces [network access policies](security/policy.md) and handles +[credential injection](security/credentials.md). See +[Network isolation](security/isolation.md#network-isolation) for how this +works and [Default security posture](security/defaults.md) for what is +allowed out of the box. + +## Lifecycle + +`sbx run` initializes a VM with a workspace for a specified agent and starts +the agent. You can stop and restart without recreating the VM, preserving +installed packages and Docker images. + +Sandboxes persist until explicitly removed. Stopping an agent doesn't delete +the VM; environment setup carries over between runs. Use `sbx rm` to delete +the sandbox, its VM, and all of its contents. If the sandbox used +`--branch`, the worktree directories and their branches are also removed. + +## Comparison to alternatives + +| Approach | Isolation | Docker access | Use case | +| --------------------------------------------------- | -------------------- | ------------------ | ------------------ | +| Sandboxes (microVMs) | Full (hypervisor) | Isolated daemon | Autonomous agents | +| Container with socket mount | Partial (namespaces) | Shared host daemon | Trusted tools | +| [Docker-in-Docker](https://hub.docker.com/_/docker) | Partial (privileged) | Nested daemon | CI/CD pipelines | +| Host execution | None | Host daemon | Manual development | + +Sandboxes trade higher resource overhead (a VM plus its own daemon) for +complete isolation. Use containers when you need lightweight packaging without +Docker access. Use sandboxes when you need to give something autonomous full +Docker capabilities without trusting it with your host environment. diff --git a/content/manuals/ai/sandboxes/docker-desktop.md b/content/manuals/ai/sandboxes/docker-desktop.md index 16c27267b7..4aa0e61823 100644 --- a/content/manuals/ai/sandboxes/docker-desktop.md +++ b/content/manuals/ai/sandboxes/docker-desktop.md @@ -13,7 +13,7 @@ convenience integration with limited features compared to the standalone sandbox CLI. > [!NOTE] -> The standalone `ds` CLI provides more features, more flexibility, and doesn't +> The standalone `sbx` CLI provides more features, more flexibility, and doesn't > require Docker Desktop. If you're setting up sandboxed agents for the first > time, consider using the standalone CLI instead. @@ -47,16 +47,16 @@ Replace `claude` with a different [agent](#supported-agents) if needed. ## Supported agents -| Agent | Command | Notes | -| --------------------------------- | ---------- | ------------------------------------ | -| Claude Code | `claude` | Most tested implementation | -| Codex | `codex` | | -| Copilot | `copilot` | | -| Gemini | `gemini` | | -| [Docker Agent](/ai/docker-agent/) | `cagent` | Also available as a standalone tool | -| Kiro | `kiro` | | -| OpenCode | `opencode` | | -| Custom shell | `shell` | Minimal environment for manual setup | +| Agent | Command | Notes | +| --------------------------------- | -------------- | ------------------------------------ | +| Claude Code | `claude` | Most tested implementation | +| Codex | `codex` | | +| Copilot | `copilot` | | +| Gemini | `gemini` | | +| [Docker Agent](/ai/docker-agent/) | `docker-agent` | Also available as a standalone tool | +| Kiro | `kiro` | | +| OpenCode | `opencode` | | +| Custom shell | `shell` | Minimal environment for manual setup | All agents are experimental. The agent type is specified when creating a sandbox and can't be changed later. @@ -110,9 +110,12 @@ $ docker sandbox run -- --continue ## Architecture Each sandbox is a lightweight microVM with its own kernel, using your system's -native virtualization (macOS virtualization.framework, Windows Hyper-V). Every -sandbox includes a private Docker daemon, so `docker build` and -`docker compose up` run inside the sandbox without affecting your host. +native virtualization (macOS virtualization.framework, Windows Hyper-V). The +default agent templates include a private Docker daemon, so `docker build` and +`docker compose up` run inside the sandbox without affecting your host. On +Windows, the Docker daemon is not included by default. See +[Troubleshooting](troubleshooting.md#docker-not-available-inside-the-sandbox-on-windows) +for a workaround. ```plaintext Host system diff --git a/content/manuals/ai/sandboxes/faq.md b/content/manuals/ai/sandboxes/faq.md new file mode 100644 index 0000000000..5c500c2095 --- /dev/null +++ b/content/manuals/ai/sandboxes/faq.md @@ -0,0 +1,104 @@ +--- +title: FAQ +weight: 70 +description: Frequently asked questions about Docker Sandboxes. +--- + +{{< summary-bar feature_name="Docker Sandboxes sbx" >}} + +## Why do I need to sign in? + +Docker Sandboxes is built around the idea that you and your agents are a team. +Signing in gives each sandbox a verified identity, which lets Docker: + +- **Tie sandboxes to a real person.** Governance matters when agents can build + containers, install packages, and push code. Your Docker identity is the + anchor. +- **Enable team features down the road.** Shared environments, org-level + policies, audit logs. These all need a concept of "who," and building that in + later would be worse for everyone. +- **Authenticate against Docker infrastructure.** Sandboxes pull images, run + daemons, and talk to Docker services. A Docker account makes that seamless. + +Your Docker account email is only used for authentication, not marketing. + +## Does the CLI collect telemetry? + +The `sbx` CLI collects basic usage data about CLI invocations: + +- Which subcommand you ran +- Whether it succeeded or failed +- How long it took +- If you're signed in, your Docker username is included + +Docker Sandboxes doesn't monitor sessions, read your prompts, or access your +code. Your code stays in the sandbox and on your host. + +To opt out of all analytics, set the `SBX_NO_TELEMETRY` environment variable: + +```console +$ export SBX_NO_TELEMETRY=1 +``` + +## How do I set custom environment variables inside a sandbox? + +The [`sbx secret`](/reference/cli/sbx/secret/) command only supports a fixed set +of [services](security/credentials.md#supported-services) (Anthropic, OpenAI, +GitHub, and others). If your agent needs an environment variable that isn't +tied to a supported service, such as `BRAVE_API_KEY` or a custom internal +token, write it to `/etc/sandbox-persistent.sh` inside the sandbox. This +file is sourced on every shell login, so the variable persists across agent +sessions for the sandbox's lifetime. + +Use `sbx exec` to append the export: + +```console +$ sbx exec -d bash -c "echo 'export BRAVE_API_KEY=your_key' >> /etc/sandbox-persistent.sh" +``` + +The `bash -c` wrapper is required so the `>>` redirect runs inside the +sandbox instead of on your host. + +> [!NOTE] +> Unlike `sbx secret`, which injects credentials through a host-side proxy +> without exposing them to the agent, this approach stores the value inside +> the sandbox. The agent process can read it directly. Only use this for +> credentials where proxy-based injection isn't available. + +To verify the variable is set, open a shell in the sandbox: + +```console +$ sbx exec -it bash +$ echo $BRAVE_API_KEY +``` + +## How do I know if my agent is running in a sandbox? + +Ask the agent. The agent can see whether or not it's running inside a sandbox. +In Claude Code, use the `/btw` slash command to ask without interrupting an +in-progress task: + +```text +/btw are you running in a sandbox? +``` + +## Why doesn't the sandbox use my user-level agent configuration? + +Sandboxes don't pick up user-level agent configuration from your host. This +includes directories like `~/.claude` for Claude Code or `~/.codex` for Codex, +where hooks, skills, and other settings are stored. Only project-level +configuration in the working directory is available inside the sandbox. + +To make configuration available in a sandbox, copy or move what you need into +your project directory before starting a session: + +```console +$ cp -r ~/.claude/skills .claude/skills +``` + +Don't use symlinks — a sandboxed agent can't follow symlinks to paths outside +the sandbox. + +Collocating skills and other agent configuration with the project itself is a +good practice regardless of sandboxes. It's versioned alongside the code and +evolves with the project as it changes. diff --git a/content/manuals/ai/sandboxes/get-started.md b/content/manuals/ai/sandboxes/get-started.md new file mode 100644 index 0000000000..4cbc1523fd --- /dev/null +++ b/content/manuals/ai/sandboxes/get-started.md @@ -0,0 +1,110 @@ +--- +title: Get started with Docker Sandboxes +linkTitle: Get started +weight: 10 +description: Install the sbx CLI and run an AI coding agent in an isolated sandbox. +--- + +{{< summary-bar feature_name="Docker Sandboxes sbx" >}} + +Docker Sandboxes run AI coding agents in isolated microVM sandboxes. Each +sandbox gets its own Docker daemon, filesystem, and network — the agent can +build containers, install packages, and modify files without touching your host +system. + +## Prerequisites + +- macOS (Apple silicon) or Windows (x86_64, Windows 11 required) +- If you're on Windows, enable Windows Hypervisor Platform (requires + elevated permissions): + ```powershell + Enable-WindowsOptionalFeature -Online -FeatureName HypervisorPlatform -All + ``` +- An API key or authentication method for the agent you want to use. Most + agents require an API key for their model provider (Anthropic, OpenAI, + Google, and others). See the [agent pages](agents/) for provider-specific + instructions, and [Credentials](security/credentials.md) for how to store + and manage keys. + +Docker Desktop is not required to use `sbx`. + +## Install and sign in + +{{< tabs >}} +{{< tab name="macOS" >}} + +```console +$ brew install docker/tap/sbx +$ sbx login +``` + +{{< /tab >}} +{{< tab name="Windows" >}} + +```powershell +> winget install -h Docker.sbx +> sbx login +``` + +{{< /tab >}} +{{< /tabs >}} + +`sbx login` opens a browser for Docker OAuth. On first login (and after `sbx +policy reset`), the CLI prompts you to choose a default network policy for your +sandboxes: + +```plaintext +Choose a default network policy: + + 1. Open — All network traffic allowed, no restrictions. + 2. Balanced — Default deny, with common dev sites allowed. + 3. Locked Down — All network traffic blocked unless you allow it. + +Use ↑/↓ to navigate, Enter to select, or press 1–3. +``` + +See [Policies](security/policy.md) for a full description of each option. + +> [!NOTE] +> See the [FAQ](faq.md) for details on why sign-in is required and what +> happens with your data. + +## Run your first sandbox + +Pick a project directory and launch an agent with [`sbx run`](/reference/cli/sbx/run/): + +```console +$ cd ~/my-project +$ sbx run claude +``` + +Replace `claude` with the agent you want to use — see [Agents](agents/) for the +full list. + +The first run takes a little longer while the agent image is pulled. +Subsequent runs reuse the cached image and start in seconds. + +You can check what's running at any time: + +```console +$ sbx ls +NAME STATUS UPTIME +claude-my-project running 12s +``` + +The agent can modify files in your project directory, so review changes before +merging. See [Workspace trust](security/workspace.md) for details. + +> [!CAUTION] +> Your network policy controls what the sandbox can reach. With **Locked +> Down**, even your model provider API is blocked. With **Balanced**, a broad +> set of common development services is allowed by default — add other hosts +> with `sbx policy allow`. See [Policies](security/policy.md) for details. + +## Next steps + +- [Usage guide](usage.md) — common patterns and workflows +- [Agents](agents/) — supported agents and configuration +- [Custom environments](agents/custom-environments.md) — build your own sandbox + images +- [Policies](security/policy.md) — control outbound access diff --git a/content/manuals/ai/sandboxes/images/sbx-security.png b/content/manuals/ai/sandboxes/images/sbx-security.png new file mode 100644 index 0000000000000000000000000000000000000000..0f86c436c507b31068307a68caa5f03d1b40ab0c GIT binary patch literal 121269 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�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 eVP4Tnq#Lc_3$yl*sID%z2 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 literal 0 HcmV?d00001 diff --git a/content/manuals/ai/sandboxes/security/_index.md b/content/manuals/ai/sandboxes/security/_index.md new file mode 100644 index 0000000000..7cdcbb3924 --- /dev/null +++ b/content/manuals/ai/sandboxes/security/_index.md @@ -0,0 +1,91 @@ +--- +title: Security model +linkTitle: Security model +weight: 50 +description: Trust boundaries, isolation layers, and security properties of Docker Sandboxes. +--- + +{{< summary-bar feature_name="Docker Sandboxes sbx" >}} + +Docker Sandboxes run AI agents in microVMs so they can execute code, install +packages, and use tools without accessing your host system. Multiple isolation +layers protect your host system. + +## Trust boundaries + +The primary trust boundary is the microVM. The agent has full control inside +the VM, including sudo access. The VM boundary prevents the agent from reaching +anything on your host except what is explicitly shared. + +What crosses the boundary into the VM: + +- **Workspace directory:** mounted into the VM with read-write access. With + the default direct mount, changes the agent makes appear on your host + immediately. +- **Credentials:** the host-side proxy injects authentication headers into + outbound HTTP requests. The raw credential values never enter the VM. +- **Network access:** HTTP and HTTPS requests to + [allowed domains](defaults/) are proxied through the host. + +What crosses the boundary back to the host: + +- **Workspace file changes:** visible on your host in real time with the + default direct mount. +- **HTTP/HTTPS requests:** sent to allowed domains through the host proxy. + +Everything else is blocked. The agent cannot access your host filesystem +(outside the workspace), your host Docker daemon, your host network or +localhost, other sandboxes, or any domain not in the allow list. Raw TCP, UDP, +and ICMP are blocked at the network layer. + +![Sandbox security model showing the hypervisor boundary between the sandbox VM and the host system. The workspace directory is shared read-write. The agent process, Docker engine, packages, and VM filesystem are inside the VM. Host filesystem, processes, Docker engine, and network are outside the VM and not accessible. A proxy enforces allow/deny policies and injects credentials into outbound requests.](../images/sbx-security.png) + +## Isolation layers + +The sandbox security model has four layers. See +[Isolation layers](isolation/) for technical details on each. + +- **Hypervisor isolation:** separate kernel per sandbox. No shared memory or + processes with the host. +- **Network isolation:** all HTTP/HTTPS traffic proxied through the host. + [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. +- **Credential isolation:** API keys are injected into HTTP headers by the + host-side proxy. Credential values never enter the VM. + +## What the agent can do inside the sandbox + +Inside the VM, the agent has full privileges: sudo access, package installation, +a private Docker Engine, and read-write access to the workspace. Installed +packages, Docker images, and other VM state persist across restarts. See +[Default security posture](defaults/) for the full breakdown of what is +permitted and what is blocked. + +## What is not isolated by default + +The sandbox isolates the agent from your host system, but the agent's actions +can still affect you through the shared workspace and allowed network channels. + +**Workspace changes are live on your host.** The agent edits the same files you +see on your host. This includes files that execute implicitly during normal +development: Git hooks, CI configuration, IDE task configs, `Makefile`, +`package.json` scripts, and similar build files. Review changes before running +any modified code. Note that Git hooks live inside `.git/` and do not appear in +`git diff` output. Check them separately. +See [Workspace trust](workspace/). + +**Default allowed domains include broad wildcards.** Some defaults like +`*.googleapis.com` cover many services beyond AI APIs. Run `sbx policy ls` to +see the full list of active rules, and remove entries you don't need. See +[Default security posture](defaults/). + +## Learn more + +- [Isolation layers](isolation/): how hypervisor, network, Docker, and + credential isolation work +- [Default security posture](defaults/): what a fresh sandbox permits and + blocks +- [Credentials](credentials/): how to provide and manage API keys +- [Policies](policy/): how to customize network access rules +- [Workspace trust](workspace/): what to review after an agent session diff --git a/content/manuals/ai/sandboxes/security/credentials.md b/content/manuals/ai/sandboxes/security/credentials.md new file mode 100644 index 0000000000..2279ad7dd3 --- /dev/null +++ b/content/manuals/ai/sandboxes/security/credentials.md @@ -0,0 +1,147 @@ +--- +title: Credentials +weight: 20 +description: How Docker Sandboxes handle API keys and authentication credentials for sandboxed agents. +--- + +{{< summary-bar feature_name="Docker Sandboxes sbx" >}} + +Most agents need an API key for their model provider. An HTTP/HTTPS proxy on +your host intercepts outbound API requests from the sandbox and injects the +appropriate authentication headers before forwarding each request. Your +credentials stay on the host and are never stored inside the sandbox VM. For +how this works as a security layer, see +[Credential isolation](isolation.md#credential-isolation). + +There are two ways to provide credentials: + +- **Stored secrets** (recommended): saved in your OS keychain, encrypted and + persistent across sessions. +- **Environment variables:** read from your current shell session. This works + but is less secure on the host side, since environment variables are visible + to other processes running as your user. + +If both are set for the same service, the stored secret takes precedence. For +multi-provider agents (OpenCode, Docker Agent), the proxy automatically selects the +correct credentials based on the API endpoint being called. See individual +[agent pages](../agents/) for provider-specific details. + +## Stored secrets + +The `sbx secret` command stores credentials in your OS keychain so you don't +need to export environment variables in every terminal session. When a sandbox +starts, the proxy looks up stored secrets and uses them to authenticate API +requests on behalf of the agent. The secret is never exposed directly to the +agent. + +### Store a secret + +```console +$ sbx secret set -g anthropic +``` + +This prompts you for the secret value interactively. The `-g` flag stores the +secret globally so it's available to all sandboxes. To scope a secret to a +specific sandbox instead: + +```console +$ sbx secret set my-sandbox openai +``` + +> [!NOTE] +> A sandbox-scoped secret takes effect immediately, even if the sandbox is +> running. A global secret (`-g`) only applies when a sandbox is created. If +> you set or change a global secret while a sandbox is running, recreate the +> sandbox for the new value to take effect. + +You can also pipe in a value for non-interactive use: + +```console +$ echo "$ANTHROPIC_API_KEY" | sbx secret set -g anthropic +``` + +### Supported services + +Each service name maps to a set of environment variables the proxy checks and +the API domains it authenticates requests to: + +| Service | Environment variables | API domains | +| ----------- | -------------------------------------------- | ----------------------------------- | +| `anthropic` | `ANTHROPIC_API_KEY` | `api.anthropic.com` | +| `aws` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY` | AWS Bedrock endpoints | +| `github` | `GH_TOKEN`, `GITHUB_TOKEN` | `api.github.com`, `github.com` | +| `google` | `GEMINI_API_KEY`, `GOOGLE_API_KEY` | `generativelanguage.googleapis.com` | +| `groq` | `GROQ_API_KEY` | `api.groq.com` | +| `mistral` | `MISTRAL_API_KEY` | `api.mistral.ai` | +| `nebius` | `NEBIUS_API_KEY` | `api.studio.nebius.ai` | +| `openai` | `OPENAI_API_KEY` | `api.openai.com` | +| `xai` | `XAI_API_KEY` | `api.x.ai` | + +When you store a secret with `sbx secret set -g `, the proxy uses it +the same way it would use the corresponding environment variable. You don't +need to set both. + +### List and remove secrets + +List all stored secrets: + +```console +$ sbx secret ls +SCOPE SERVICE SECRET +(global) github gho_GCaw4o****...****43qy +``` + +Remove a secret: + +```console +$ sbx secret rm -g github +``` + +> [!NOTE] +> Running `sbx reset` deletes all stored secrets along with all sandbox state. +> You'll need to re-add your secrets after a reset. + +### GitHub token + +The `github` service gives the agent access to the `gh` CLI inside the +sandbox. Pass your existing GitHub CLI token: + +```console +$ echo "$(gh auth token)" | sbx secret set -g github +``` + +This is useful for agents that create pull requests, open issues, or interact +with GitHub APIs on your behalf. + +## Environment variables + +As an alternative to stored secrets, export the relevant environment variable +in your shell before running a sandbox: + +```console +$ export ANTHROPIC_API_KEY=sk-ant-api03-xxxxx +$ sbx run claude +``` + +The proxy reads the variable from your terminal session. See individual +[agent pages](../agents/) for the variable names each agent expects. + +## Best practices + +- Use [stored secrets](#stored-secrets) over environment variables. The OS + keychain encrypts credentials at rest and controls access, while environment + variables are plaintext in your shell. +- Don't set API keys manually inside the sandbox. Credentials stored in + environment variables or configuration files inside the VM are readable by + the agent process directly. +- For Claude Code, the interactive OAuth flow is another secure option: the + proxy handles authentication without exposing the token inside the sandbox. + Leave `ANTHROPIC_API_KEY` unset to use OAuth. + +## Custom templates and placeholder values + +When building custom templates or installing agents manually in a shell +sandbox, some agents require environment variables like `OPENAI_API_KEY` to be +set before they start. Set these to placeholder values (e.g. `proxy-managed`) +if needed. The proxy injects actual credentials regardless of the environment +variable value. diff --git a/content/manuals/ai/sandboxes/security/defaults.md b/content/manuals/ai/sandboxes/security/defaults.md new file mode 100644 index 0000000000..fd9a1286f3 --- /dev/null +++ b/content/manuals/ai/sandboxes/security/defaults.md @@ -0,0 +1,68 @@ +--- +title: Default security posture +linkTitle: Defaults +weight: 15 +description: What a sandbox permits and blocks before you change any settings. +--- + +{{< summary-bar feature_name="Docker Sandboxes sbx" >}} + +A sandbox created with `sbx run` and no additional flags or blueprints has +the following security posture. + +## Network defaults + +All outbound HTTP and HTTPS traffic is blocked unless an explicit rule allows +it (deny-by-default). All non-HTTP protocols (raw TCP, UDP including DNS, and +ICMP) are blocked at the network layer. Traffic to private IP ranges, loopback +addresses, and link-local addresses is also blocked. + +Run `sbx policy ls` to see the active allow rules for your installation. To +customize network access, see [Policies](policy.md). + +## Workspace defaults + +Sandboxes use a direct mount by default. The agent sees and modifies your +working tree directly, and changes appear on your host immediately. + +The agent can read, write, and delete any file within the workspace directory, +including hidden files, configuration files, build scripts, and Git hooks. +See [Workspace trust](workspace.md) for what to review after an agent session. + +## Credential defaults + +No credentials are available to the sandbox unless you provide them using +`sbx secret` or environment variables. When credentials are provided, the +host-side proxy injects them into outbound HTTP headers. The agent cannot +read the raw credential values. + +See [Credentials](credentials.md) for setup instructions. + +## Agent capabilities inside the sandbox + +The agent runs with full control inside the sandbox VM: + +- `sudo` access (the agent runs as a non-root user with sudo privileges) +- A private Docker Engine for building images and running containers +- Package installation through `apt`, `pip`, `npm`, and other package managers +- Full read and write access to the VM filesystem + +Everything the agent installs or creates inside the VM, including packages, +Docker images, and configuration changes, persists across stop and restart +cycles. When you remove the sandbox with `sbx rm`, the VM and its contents +are deleted. Only workspace files remain on the host. + +## What is blocked by default + +The following are blocked for all sandboxes and cannot be changed through +policy configuration: + +- Host filesystem access outside the workspace directory +- Host Docker daemon +- Host network and localhost +- Communication between sandboxes +- Raw TCP, UDP, and ICMP connections +- Traffic to private IP ranges and link-local addresses + +Outbound HTTP/HTTPS to domains not in the allow list is also blocked by +default, but you can add allow rules with `sbx policy allow`. diff --git a/content/manuals/ai/sandboxes/security/isolation.md b/content/manuals/ai/sandboxes/security/isolation.md new file mode 100644 index 0000000000..91dfaefba3 --- /dev/null +++ b/content/manuals/ai/sandboxes/security/isolation.md @@ -0,0 +1,86 @@ +--- +title: Isolation layers +weight: 10 +description: How Docker Sandboxes isolate AI agents using hypervisor, network, Docker Engine, and credential boundaries. +--- + +{{< 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. + +## Hypervisor isolation + +Every sandbox runs inside a lightweight microVM with its own Linux kernel. +Unlike containers, which share the host kernel, a sandbox VM cannot access host +processes, files, or resources outside its defined boundaries. + +- **Process isolation:** separate kernel per sandbox; processes inside the VM + are invisible to your host and to other sandboxes +- **Filesystem isolation:** only your workspace directory is shared with the + host. The rest of the VM filesystem persists across restarts but is removed + when you delete the sandbox. Symlinks pointing outside the workspace scope + are not followed. +- **Full cleanup:** when you remove a sandbox with `sbx rm`, the VM and + everything inside it is deleted + +The agent runs as a non-root user with sudo privileges inside the VM. The +hypervisor boundary is the isolation control, not in-VM privilege separation. + +## Network isolation + +Each sandbox has its own isolated network. Sandboxes cannot communicate with +each other and cannot reach your host's localhost. There is no shared network +between sandboxes or between a sandbox and your host. + +All HTTP and HTTPS traffic leaving a sandbox passes through a proxy on your +host that enforces the [network policy](policy.md). The sandbox routes +traffic through either a forward proxy or a transparent proxy depending on the +client's configuration. Both enforce the network policy; only the forward proxy +[injects credentials](credentials.md) for AI services. + +Raw TCP connections, UDP, and ICMP are blocked at the network layer. DNS +resolution is handled by the proxy; the sandbox cannot make raw DNS queries. +Traffic to private IP ranges, loopback, and link-local addresses is also +blocked. Only domains explicitly listed in the policy are reachable. + +For the default set of allowed domains, see +[Default security posture](defaults.md). + +## Docker Engine isolation + +Agents often need to build images, run containers, and use Docker Compose. +Mounting your host Docker socket into a container would give the agent full +access to your environment. + +Docker Sandboxes avoid this by running a separate [Docker +Engine](https://docs.docker.com/engine/) inside the sandbox environment, isolated from +your host. When the agent runs `docker build` or `docker compose up`, those +commands execute against that engine. The agent has no path to your host Docker +daemon. + +```plaintext +Host system + ├── Host Docker daemon + │ └── Your containers and images + │ + └── Sandbox Docker engine (isolated from host) + ├── [VM] Agent container — sandbox 1 + │ └── [VM] Containers created by agent + └── [VM] Agent container — sandbox 2 + └── [VM] Containers created by agent +``` + +## Credential isolation + +Most agents need API keys for their model provider. Rather than passing keys +into the sandbox, the host-side proxy intercepts outbound API requests and +injects authentication headers before forwarding each request. + +Credential values are never stored inside the VM. They are not available as +environment variables or files inside the sandbox unless you explicitly set +them. This means a compromised sandbox cannot read API keys from the local +environment. + +For how to store and manage credentials, see [Credentials](credentials.md). diff --git a/content/manuals/ai/sandboxes/security/policy.md b/content/manuals/ai/sandboxes/security/policy.md new file mode 100644 index 0000000000..6001a0dd2f --- /dev/null +++ b/content/manuals/ai/sandboxes/security/policy.md @@ -0,0 +1,293 @@ +--- +title: Policies +weight: 30 +description: Configure network access and filesystem rules for sandboxes. +--- + +{{< summary-bar feature_name="Docker Sandboxes sbx" >}} + +Sandboxes are [network-isolated](isolation.md) from your host and from each +other. A policy system controls what a sandbox can access — which external +hosts it can reach over the network, and which host paths it can mount as +workspaces. + +Policies can be set at two levels: + +- **Organization policies** {{< badge color=blue text="Limited Access" >}} — configured by admins in the + [Docker Admin Console](https://app.docker.com/admin) under AI governance + settings. These apply to all sandboxes across the organization. +- **Local policies** — configured by individual users with the `sbx policy` + command. These apply to all sandboxes on the local machine. + +If your organization has enabled governance, organization policies take +precedence over local rules and can't be overridden locally. See +[Precedence](#precedence) for the full evaluation model. + +## Organization policies {tier="Limited Access"} + +> [!NOTE] +> Organization governance is a Limited Access feature. Contact your Docker +> account team to request access. + +Organization admins can centrally manage policies through the +[Docker Admin Console](https://app.docker.com/admin). Navigate to your +organization settings and enable **Manage AI governance**. + +Once enabled, the policies defined in the Admin Console apply to all +sandboxes across the organization, regardless of any local policies +configured with `sbx policy`. + +### Local extensions to organization policy + +Organization policy is the baseline for all sandboxes in your organization. +Admins can optionally permit users to extend it locally by enabling the +**User defined** setting in AI governance settings. When enabled, users can +add hosts to the allowlist from their own machine using `sbx policy allow network`. + +Local extensions can only expand access within what the organization permits. +They can't override organization-level deny rules. + +## Network policies + +The only way traffic can leave a sandbox is through an HTTP/HTTPS proxy on +your host, which enforces access rules on every outbound request. + +### Initial policy selection + +On first start, and after running `sbx policy reset`, the daemon prompts you to +choose a network policy: + +```plaintext +Choose a default network policy: + + 1. Open — All network traffic allowed, no restrictions. + 2. Balanced — Default deny, with common dev sites allowed. + 3. Locked Down — All network traffic blocked unless you allow it. + + Use ↑/↓ to navigate, Enter to select, or press 1–3. +``` + +| Policy | Description | +| ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Open | All outbound traffic is allowed. No restrictions. Equivalent to adding a wildcard allow rule with `sbx policy allow network "**"`. | +| Balanced | Default deny, with a baseline allowlist covering AI provider APIs, package managers, code hosts, container registries, and common cloud services. You can extend this with `sbx policy allow`. | +| Locked Down | All outbound traffic is blocked, including model provider APIs (for example, `api.anthropic.com`). You must explicitly allow everything you need. | + +You can change your effective policy at any time using `sbx policy allow` and +`sbx policy deny`, or start over by running `sbx policy reset`. + +### Non-interactive environments + +In non-interactive environments such as CI pipelines or headless servers, the +interactive prompt can't be displayed. Use `sbx policy set-default` to set the +default network policy before running any other `sbx` commands: + +```console +$ sbx policy set-default balanced +``` + +Available values are `allow-all`, `balanced`, and `deny-all`. After setting the +default, you can customize further with `sbx policy allow` and +`sbx policy deny` as usual. + +### Default policy + +All outbound HTTP/HTTPS traffic is blocked by default unless an explicit rule +allows it. The **Balanced** policy ships with a baseline allowlist covering AI provider +APIs, package managers, code hosts, container registries, and common cloud +services. Run `sbx policy ls` to see the active rules for your installation. + +Organization admins can modify or remove these defaults when configuring +[organization policies](#organization-policies). + +### Managing local rules + +Use [`sbx policy allow`](/reference/cli/sbx/policy/allow/) and +[`sbx policy deny`](/reference/cli/sbx/policy/deny/) to add network access +rules. Changes take effect immediately and apply to all sandboxes: + +```console +$ sbx policy allow network api.anthropic.com +$ sbx policy deny network ads.example.com +``` + +Specify multiple hosts in one command with a comma-separated list: + +```console +$ sbx policy allow network "api.anthropic.com,*.npmjs.org,*.pypi.org" +``` + +List all active policy rules with `sbx policy ls`: + +```console +$ sbx policy ls +ID TYPE DECISION RESOURCES +a1b2c3d4-e5f6-7890-abcd-ef1234567890 network allow api.anthropic.com, *.npmjs.org +f9e8d7c6-b5a4-3210-fedc-ba0987654321 network deny ads.example.com +``` + +Use `--type network` to show only network policies. + +Remove a policy by resource or by rule ID: + +```console +$ sbx policy rm network --resource ads.example.com +$ sbx policy rm network --id 2d3c1f0e-4a73-4e05-bc9d-f2f9a4b50d67 +``` + +### Resetting to defaults + +To remove all custom policies and restore the default policy, use +`sbx policy reset`: + +```console +$ sbx policy reset +``` + +This deletes the local policy store and stops the daemon. When the daemon +restarts on the next command, you are prompted to choose a new network policy. +If sandboxes are running, they stop when the daemon shuts down. You are prompted for +confirmation unless you pass `--force`: + +```console +$ sbx policy reset --force +``` + +### Switching to allow-by-default + +If you prefer a permissive policy where all outbound traffic is allowed, add +a wildcard allow rule: + +```console +$ sbx policy allow network "**" +``` + +This lets agents install packages and call any external API without additional +configuration. You can still deny specific hosts with `sbx policy deny`. + +### Org-level rules {tier="Limited Access"} + +Define network allow and deny rules in the Admin Console under +**AI governance > Network access**. Each rule takes a network target (domain, +wildcard, or CIDR range) and an action (allow or deny). You can add multiple +entries at once, one per line. + +Organization-level rules use the same [wildcard syntax](#wildcard-syntax) as +local rules. + +### Wildcard syntax + +Rules support exact domains (`example.com`), wildcard subdomains +(`*.example.com`), and optional port suffixes (`example.com:443`). + +Note that `example.com` doesn't match subdomains, and `*.example.com` doesn't +match the root domain. Specify both to cover both. + +### Common patterns + +Allow access to package managers so agents can install dependencies: + +```console +$ sbx policy allow network "*.npmjs.org,*.pypi.org,files.pythonhosted.org,github.com" +``` + +The **Balanced** policy already includes AI provider APIs, package managers, +code hosts, and container registries. You only need to add allow rules for +additional domains your workflow requires. If you chose **Locked Down**, you +must explicitly allow everything. + +> [!WARNING] +> Allowing broad domains like `github.com` permits access to any content on +> that domain, including user-generated content. Only allow domains you trust +> with your data. + +### Monitoring + +Use `sbx policy log` to see which hosts your sandboxes have contacted: + +```console +$ sbx policy log +Blocked requests: +SANDBOX TYPE HOST PROXY RULE LAST SEEN COUNT +my-sandbox network blocked.example.com transparent policykit 10:15:25 29-Jan 1 + +Allowed requests: +SANDBOX TYPE HOST PROXY RULE LAST SEEN COUNT +my-sandbox network api.anthropic.com forward policykit 10:15:23 29-Jan 42 +my-sandbox network registry.npmjs.org transparent policykit 10:15:20 29-Jan 18 +``` + +The **PROXY** column shows how the request left the sandbox: + +| Value | Description | +| ------------- | --------------------------------------------------------------------------------------------------- | +| `forward` | Routed through the forward proxy. Supports [credential injection](credentials.md). | +| `transparent` | Intercepted by the transparent proxy. Policy is enforced but credential injection is not available. | +| `network` | Non-HTTP traffic (raw TCP, UDP, ICMP). Always blocked. | + +Filter by sandbox name by passing it as an argument: + +```console +$ sbx policy log my-sandbox +``` + +Use `--limit N` to show only the last `N` entries, `--json` for +machine-readable output, or `--type network` to filter by policy type. + +## Filesystem policies + +Filesystem policies control which host paths a sandbox can mount as +workspaces. By default, sandboxes can mount any directory the user has +access to. + +### Org-level restrictions {tier="Limited Access"} + +Admins can restrict which paths are mountable by defining filesystem allow +and deny rules in the Admin Console under **AI governance > Filesystem access**. +Each rule takes a path pattern and an action (allow or deny). + +> [!CAUTION] +> Use `**` (double wildcard) rather than `*` (single wildcard) when writing +> path patterns to match path segments recursively. A single `*` only matches +> within a single path segment. For example, `~/**` matches all paths under the +> user's home directory, whereas `~/*` matches only paths directly under `~`. + +## Precedence + +Within any layer, deny rules beat allow rules — if a domain matches both, +it's blocked regardless of specificity. + +Docker Sandboxes ships with a baseline allowlist (the default policies). Local +`sbx policy` rules add to this baseline. The full evaluation order when +organization policies are enabled: + +1. **Organization policies** (Docker Admin Console) — highest precedence. + Organization admins can modify or replace the default allowlist and define + their own rules. Organization-level denials can't be overridden locally. +2. **Local extensions** — if the admin has enabled the **User defined** + setting, users can add allow rules with `sbx policy allow network`. These + can only expand access within what the organization permits. +3. **Local rules** (`sbx policy`) — lowest precedence. Can't override + organization-level denials. + +The same model applies to filesystem policies: organization-level rules take +precedence over local behavior. + +To unblock a domain, identify where the deny rule comes from. For local rules, +remove it with `sbx policy rm`. For organization-level rules, contact your +organization admin. + +## Troubleshooting + +### Policy changes not taking effect + +If policy changes aren't taking effect, run `sbx policy reset` to wipe the +local policy store and restart the daemon. On next start, you are prompted to +choose a new network policy, and the latest organization policies are pulled if +governance is enabled. + +### Sandbox cannot mount workspace + +If a sandbox fails to mount with a `mount policy denied` error, verify that +the filesystem allow rule uses `**` rather than `*`. A single `*` doesn't +match across directory separators. diff --git a/content/manuals/ai/sandboxes/security/workspace.md b/content/manuals/ai/sandboxes/security/workspace.md new file mode 100644 index 0000000000..cc1db7d200 --- /dev/null +++ b/content/manuals/ai/sandboxes/security/workspace.md @@ -0,0 +1,73 @@ +--- +title: Workspace trust +weight: 40 +description: | + How sandboxed agents interact with your workspace files and what to review + after an agent session. +--- + +{{< summary-bar feature_name="Docker Sandboxes sbx" >}} + +Agents running in sandboxes have full access to the workspace directory without +prompting. With the default direct mount, changes the agent makes appear on +your host immediately. Treat sandbox-modified workspace files the same way +you would treat a pull request from an untrusted contributor: review before +you trust them on your host. + +## What the agent can modify + +The agent can create, modify, and delete any file in the workspace. This +includes: + +- Source code files +- Configuration files (`.eslintrc`, `pyproject.toml`, `.env`, etc.) +- Build files (`Makefile`, `package.json`, `Cargo.toml`) +- Git hooks (`.git/hooks/`) +- CI configuration (`.github/workflows/`, `.gitlab-ci.yml`) +- IDE configuration (`.vscode/tasks.json`, `.idea/` run configurations) +- Hidden files and directories +- Shell scripts and executables + +> [!CAUTION] +> Files like Git hooks, CI configuration, IDE task configs, and build scripts +> execute code when triggered by normal development actions such as committing, +> building, or opening the project in an IDE. Review these files after any agent +> session before performing those actions. + +## 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. + +## Reviewing changes + +After an agent session, review changes before executing any code the agent +touched. + +With the default direct mount, changes are in your working tree: + +```console +$ git diff +``` + +If you used `--branch`, the agent's changes are on a separate branch: + +```console +$ git diff main..my-feature +``` + +Pay particular attention to: + +- **Git hooks** (`.git/hooks/`): run on commit, push, and other Git actions. + These are inside `.git/` and **do not appear in `git diff` output**. Check + them separately with `ls -la .git/hooks/`. +- **CI configuration** (`.github/workflows/`, `.gitlab-ci.yml`): runs on push +- **Build files** (`Makefile`, `package.json` scripts, `Cargo.toml`): run + during build or install steps +- **IDE configuration** (`.vscode/tasks.json`, `.idea/`): can run tasks when + you open the project +- **Executable files and shell scripts**: can run directly + +These files execute code without you explicitly running them. Review them before +committing, building, or opening the project in an IDE. diff --git a/content/manuals/ai/sandboxes/troubleshooting.md b/content/manuals/ai/sandboxes/troubleshooting.md new file mode 100644 index 0000000000..16eef2a8b4 --- /dev/null +++ b/content/manuals/ai/sandboxes/troubleshooting.md @@ -0,0 +1,137 @@ +--- +title: Troubleshooting +weight: 60 +description: Resolve common issues when using Docker Sandboxes. +--- + +{{< summary-bar feature_name="Docker Sandboxes sbx" >}} + +## Resetting sandboxes + +If you hit persistent issues or corrupted state, run +[`sbx reset`](/reference/cli/sbx/reset/) to stop all VMs and delete all sandbox +data. Create fresh sandboxes afterwards. + +## Agent can't install packages or reach an API + +Sandboxes use a [deny-by-default network policy](security/policy.md). +If the agent fails to install packages or call an external API, the target +domain is likely not in the allow list. Check which requests are being blocked: + +```console +$ sbx policy log +``` + +Then allow the domains your workflow needs: + +```console +$ sbx policy allow network "*.npmjs.org,*.pypi.org,files.pythonhosted.org" +``` + +To allow all outbound traffic instead: + +```console +$ sbx policy allow network "**" +``` + +## Docker authentication failure + +If you see a message like `You are not authenticated to Docker`, your login +session has expired. In an interactive terminal, the CLI prompts you to sign in +again. In non-interactive environments such as scripts or CI, run `sbx login` +to re-authenticate. + +## Agent authentication failure + +If the agent can't reach its model provider or you see API key errors, the key +is likely invalid, expired, or not configured. Verify it's set in your shell +configuration file and that you sourced it or opened a new terminal. + +For agents that use the [credential proxy](security/credentials.md), make sure +you haven't set the API key to an invalid value inside the sandbox — the proxy +injects credentials automatically on outbound requests. + +If credentials are configured correctly but API calls still fail, check +`sbx policy log` and look at the **PROXY** column. Requests routed through +the `transparent` proxy don't get credential injection. This can happen when a +client inside the sandbox (such as a process in a Docker container) isn't +configured to use the forward proxy. See +[Monitoring network activity](security/policy.md#monitoring-network-activity) +for details. + +## Docker not available inside the sandbox on Windows + +On Windows, sandboxes use non-docker template variants by default, so `docker` +commands aren't available inside the sandbox. To use Docker inside a sandbox on +Windows, specify a `-docker` template: + +```console +$ sbx run --template docker.io/docker/sandbox-templates:claude-code-docker claude +``` + +The `-docker` variants work on Windows but have slower startup times. See +[Base images](agents/custom-environments.md#base-images) for details and the +full list of templates. + +## Docker build export fails with "lchown: operation not permitted" + +Running `docker build` with the local exporter (`--output=type=local` or `-o +`) inside a sandbox fails because the exporter tries to `lchown` output +files to preserve ownership from the build. Processes inside the sandbox run as +an unprivileged user without `CAP_CHOWN`, so the operation is denied. + +Use the tar exporter and extract the archive instead: + +```console +$ docker build --output type=tar,dest=- . | tar xf - -C ./result +``` + +Extracting the tar archive as the current user avoids the `chown` call. + +## Stale Git worktree after removing a sandbox + +If you used `--branch`, worktree cleanup during `sbx rm` is best-effort. If +it fails, the sandbox is removed but the branch and worktree are left behind. +If `git worktree list` shows a stale worktree in `.sbx/` after removing a +sandbox, clean it up manually: + +```console +$ git worktree remove .sbx/-worktrees/ +$ git branch -D +``` + +## Clock drift after sleep/wake + +If your laptop sleeps and wakes while a sandbox is running, the VM clock can +fall behind the host clock. This causes problems such as: + +- External API calls failing because of timestamp validation. +- Git commits with incorrect timestamps. +- TLS certificate errors due to time mismatches. + +To fix the issue, stop and restart the sandbox: + +```console +$ sbx stop +$ sbx run +``` + +Restarting the sandbox re-syncs the VM clock with the host. + +## Removing all state + +As a last resort, if `sbx reset` doesn't resolve your issue, you can remove the +`sbx` state directory entirely. This deletes all sandbox data, configuration, and +cached images. Stop all running sandboxes first with `sbx reset`. + +macOS: + +```console +$ rm -rf ~/Library/Application\ Support/com.docker.sandboxes/ +``` + +Windows: + +```powershell +> Remove-Item -Recurse -Force "$env:LOCALAPPDATA\DockerSandboxes" +``` diff --git a/content/manuals/ai/sandboxes/usage.md b/content/manuals/ai/sandboxes/usage.md new file mode 100644 index 0000000000..6d7fb15655 --- /dev/null +++ b/content/manuals/ai/sandboxes/usage.md @@ -0,0 +1,307 @@ +--- +title: Usage +weight: 20 +description: Common patterns for working with sandboxes. +--- + +{{< summary-bar feature_name="Docker Sandboxes sbx" >}} + +## Working with sandboxes + +The basic workflow is [`run`](/reference/cli/sbx/run/) to start, +[`ls`](/reference/cli/sbx/ls/) to check status, +[`stop`](/reference/cli/sbx/stop/) to pause, and +[`rm`](/reference/cli/sbx/rm/) to clean up: + +```console +$ sbx run claude # start an agent +$ sbx ls # see what's running +$ sbx stop my-sandbox # pause it +$ sbx rm my-sandbox # delete it entirely +``` + +To get a shell inside a running sandbox — useful for inspecting the environment, +checking Docker containers, or manually installing something: + +```console +$ sbx exec -it bash +``` + +If you need a clean slate, remove the sandbox and re-run: + +```console +$ sbx rm my-sandbox +$ sbx run claude +``` + +## Interactive mode + +Running `sbx` with no subcommands opens an interactive terminal dashboard: + +```console +$ sbx +``` + +The dashboard shows all your sandboxes as cards with live status, CPU, and +memory usage. From here you can: + +- **Create** a sandbox (`c`). +- **Start or stop** a sandbox (`s`). +- **Attach** to an agent session (`Enter`), same as `sbx run`. +- **Open a shell** inside the sandbox (`x`), same as `sbx exec`. +- **Remove** a sandbox (`r`). + +The dashboard also includes a network governance panel where you can monitor +outbound connections made by your sandboxes and manage network rules. Use `tab` +to switch between the sandboxes panel and the network panel. + +From the network panel you can browse connection logs, allow or block specific +hosts, and add custom network rules. Press `?` to see all keyboard shortcuts. + +## Git workflow + +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. + +### 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 + +```console +$ sbx run claude --branch my-feature # agent works on the my-feature branch +``` + +Use `--branch auto` to let the CLI generate a branch name for you: + +```console +$ 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: + +```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 + +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 +``` + +#### 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: + +```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 +``` + +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 + +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`: + +```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)" +``` + +> [!TIP] +> If `git config --global core.excludesFile` is empty, set one first: +> `git config --global core.excludesFile ~/.gitignore`. + +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. + +## Reconnecting and naming + +Sandboxes persist after the agent exits. Running the same workspace path again +reconnects to the existing sandbox rather than creating a new one: + +```console +$ sbx run claude ~/my-project # creates sandbox +$ sbx run claude ~/my-project # reconnects to same sandbox +``` + +Use `--name` to make this explicit and avoid ambiguity: + +```console +$ sbx run claude --name my-project +``` + +## Creating without attaching + +[`sbx run`](/reference/cli/sbx/run/) creates the sandbox and attaches you to +the agent. To create a sandbox in the background without attaching: + +```console +$ 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 +later with `sbx run`: + +```console +$ sbx run claude-my-project +``` + +## Multiple workspaces + +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. + +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 +shared libraries the agent shouldn't modify: + +```console +$ sbx run claude ~/project-a ~/shared-libs:ro ~/docs:ro +``` + +Each sandbox is completely isolated, so you can also run separate projects +side-by-side. Remove unused sandboxes when you're done to reclaim disk space: + +```console +$ sbx run claude ~/project-a +$ sbx run claude ~/project-b +$ sbx rm # when finished +``` + +## Installing dependencies and using Docker + +Ask the agent to install what's needed — it has sudo access, and installed +packages persist for the sandbox's lifetime. For teams or repeated setups, +[custom templates](agents/custom-environments.md) let you pre-install tools +into a reusable image. + +Agents can also build Docker images, run containers, and use +[Compose](https://docs.docker.com/compose/). Everything runs inside the sandbox's private Docker +daemon, so containers started by the agent never appear in your host's +`docker ps`. When you remove the sandbox, all images, containers, and volumes +inside it are deleted with it. + +## Accessing services in the sandbox + +Sandboxes are [network-isolated](security/isolation.md) — your browser or local +tools can't reach a server running inside one by default. Use +[`sbx ports`](/reference/cli/sbx/ports/) to forward traffic from your host into +a running sandbox. + +The common case: an agent has started a dev server or API, and you want to open +it in your browser or run tests against it. + +```console +$ sbx ports my-sandbox --publish 8080:3000 # host 8080 → sandbox port 3000 +$ open http://localhost:8080 +``` + +To let the OS pick a free host port instead of choosing one yourself: + +```console +$ sbx ports my-sandbox --publish 3000 # ephemeral host port +$ sbx ports my-sandbox # check which port was assigned +``` + +`sbx ls` shows active port mappings alongside each sandbox, and `sbx ports` +lists them in detail: + +```console +$ sbx ls +SANDBOX AGENT STATUS PORTS WORKSPACE +my-sandbox claude running 127.0.0.1:8080->3000/tcp /home/user/proj +``` + +To stop forwarding a port: + +```console +$ sbx ports my-sandbox --unpublish 8080:3000 +``` + +A few things to keep in mind: + +- **Services must bind to `0.0.0.0`** — a service listening on `127.0.0.1` + inside the sandbox won't be reachable through a published port. Most dev + servers default to `127.0.0.1`, so you'll usually need to pass a flag like + `--host 0.0.0.0` when starting them. +- **Not persistent** — published ports are lost when the sandbox stops or the + daemon restarts. Re-publish after restarting. +- **No create-time flag** — unlike `docker run -p`, there's no `--publish` + option on `sbx run` or `sbx create`. Ports can only be published after the + sandbox is running. +- **Unpublish requires the host port** — `--unpublish 3000` is rejected; you + must use `--unpublish 8080:3000`. Run `sbx ports my-sandbox` first if you + used an ephemeral port and need to find the assigned host port. + +## What persists + +While a sandbox exists, installed packages, Docker images, configuration +changes, and command history all persist across stops and restarts. When you +remove a sandbox, everything inside is deleted — only your workspace files +remain on your host. To preserve a configured environment, create a +[custom template](agents/custom-environments.md). diff --git a/content/reference/cli/sbx/_content.gotmpl b/content/reference/cli/sbx/_content.gotmpl new file mode 100644 index 0000000000..b1475f2a05 --- /dev/null +++ b/content/reference/cli/sbx/_content.gotmpl @@ -0,0 +1,48 @@ +{{- /* + Content adapter for sbx CLI reference pages. + + Generates pages from YAML data files in site.Data.sbx_cli (cobra/doc format). + For each YAML file it creates a page at the command path (spaces → slashes). + Commands with child entries in see_also become Hugo sections; others become + regular pages. +*/ -}} + +{{- range $name, $data := site.Data.sbx_cli -}} + {{- if not $data.name -}} + {{- continue -}} + {{- end -}} + + {{- /* Determine if this command has child commands (from see_also) */ -}} + {{- $hasChildren := false -}} + {{- $prefix := printf "%s " $data.name -}} + {{- with $data.see_also -}} + {{- range . -}} + {{- $cmd := index (split . " - ") 0 -}} + {{- if strings.HasPrefix $cmd $prefix -}} + {{- $hasChildren = true -}} + {{- end -}} + {{- end -}} + {{- end -}} + + {{- /* Compute path relative to this adapter (strip "sbx/" prefix) */ -}} + {{- $fullPath := replace $data.name " " "/" -}} + {{- $kind := cond $hasChildren "section" "page" -}} + + {{- if eq $data.name "sbx" -}} + {{- /* Root command is the section index */ -}} + {{- $.AddPage (dict + "path" "." + "title" $data.name + "kind" "section" + "params" (dict "datafile" $name) + ) -}} + {{- else -}} + {{- $relPath := strings.TrimPrefix "sbx/" $fullPath -}} + {{- $.AddPage (dict + "path" $relPath + "title" $data.name + "kind" $kind + "params" (dict "datafile" $name) + ) -}} + {{- end -}} +{{- end -}} diff --git a/data/sbx_cli/sbx.yaml b/data/sbx_cli/sbx.yaml new file mode 100644 index 0000000000..0cdde69210 --- /dev/null +++ b/data/sbx_cli/sbx.yaml @@ -0,0 +1,32 @@ +name: sbx +synopsis: Manage AI coding agent sandboxes. +description: |- + Docker Sandboxes creates isolated sandbox environments for AI agents, powered by Docker. + + Run without a command to launch interactive mode, or pass a command for CLI usage. +usage: sbx +options: + - name: debug + shorthand: D + default_value: "false" + usage: Enable debug logging + - name: help + shorthand: h + default_value: "false" + usage: help for sbx +see_also: + - sbx completion - Generate the autocompletion script for the specified shell + - sbx create - Create a sandbox for an agent + - sbx exec - Execute a command inside a sandbox + - sbx login - Sign in to Docker + - sbx logout - Sign out of Docker + - sbx ls - List sandboxes + - sbx policy - Manage sandbox policies + - sbx ports - Manage sandbox port publishing + - sbx reset - Reset all sandboxes and clean up state + - sbx rm - Remove one or more sandboxes + - sbx run - Run an agent in a sandbox + - sbx save - Save a snapshot of the sandbox as a template + - sbx secret - Manage stored secrets + - sbx stop - Stop one or more sandboxes without removing them + - sbx version - Show Docker Sandboxes version information diff --git a/data/sbx_cli/sbx_completion.yaml b/data/sbx_cli/sbx_completion.yaml new file mode 100644 index 0000000000..1416516a62 --- /dev/null +++ b/data/sbx_cli/sbx_completion.yaml @@ -0,0 +1,21 @@ +name: sbx completion +synopsis: Generate the autocompletion script for the specified shell +description: | + Generate the autocompletion script for sbx for the specified shell. + See each sub-command's help for details on how to use the generated script. +options: + - name: help + shorthand: h + default_value: "false" + usage: help for completion +inherited_options: + - name: debug + shorthand: D + default_value: "false" + usage: Enable debug logging +see_also: + - sbx - Manage AI coding agent sandboxes. + - sbx completion bash - Generate the autocompletion script for bash + - sbx completion fish - Generate the autocompletion script for fish + - sbx completion powershell - Generate the autocompletion script for powershell + - sbx completion zsh - Generate the autocompletion script for zsh diff --git a/data/sbx_cli/sbx_completion_bash.yaml b/data/sbx_cli/sbx_completion_bash.yaml new file mode 100644 index 0000000000..4e06d587fb --- /dev/null +++ b/data/sbx_cli/sbx_completion_bash.yaml @@ -0,0 +1,39 @@ +name: sbx completion bash +synopsis: Generate the autocompletion script for bash +description: | + Generate the autocompletion script for the bash shell. + + This script depends on the 'bash-completion' package. + If it is not installed already, you can install it via your OS's package manager. + + To load completions in your current shell session: + + source <(sbx completion bash) + + To load completions for every new session, execute once: + + #### Linux: + + sbx completion bash > /etc/bash_completion.d/sbx + + #### macOS: + + sbx completion bash > $(brew --prefix)/etc/bash_completion.d/sbx + + You will need to start a new shell for this setup to take effect. +usage: sbx completion bash +options: + - name: help + shorthand: h + default_value: "false" + usage: help for bash + - name: no-descriptions + default_value: "false" + usage: disable completion descriptions +inherited_options: + - name: debug + shorthand: D + default_value: "false" + usage: Enable debug logging +see_also: + - sbx completion - Generate the autocompletion script for the specified shell diff --git a/data/sbx_cli/sbx_completion_fish.yaml b/data/sbx_cli/sbx_completion_fish.yaml new file mode 100644 index 0000000000..2d38fe4b2d --- /dev/null +++ b/data/sbx_cli/sbx_completion_fish.yaml @@ -0,0 +1,30 @@ +name: sbx completion fish +synopsis: Generate the autocompletion script for fish +description: | + Generate the autocompletion script for the fish shell. + + To load completions in your current shell session: + + sbx completion fish | source + + To load completions for every new session, execute once: + + sbx completion fish > ~/.config/fish/completions/sbx.fish + + You will need to start a new shell for this setup to take effect. +usage: sbx completion fish [flags] +options: + - name: help + shorthand: h + default_value: "false" + usage: help for fish + - name: no-descriptions + default_value: "false" + usage: disable completion descriptions +inherited_options: + - name: debug + shorthand: D + default_value: "false" + usage: Enable debug logging +see_also: + - sbx completion - Generate the autocompletion script for the specified shell diff --git a/data/sbx_cli/sbx_completion_powershell.yaml b/data/sbx_cli/sbx_completion_powershell.yaml new file mode 100644 index 0000000000..6a442b5d99 --- /dev/null +++ b/data/sbx_cli/sbx_completion_powershell.yaml @@ -0,0 +1,27 @@ +name: sbx completion powershell +synopsis: Generate the autocompletion script for powershell +description: | + Generate the autocompletion script for powershell. + + To load completions in your current shell session: + + sbx completion powershell | Out-String | Invoke-Expression + + To load completions for every new session, add the output of the above command + to your powershell profile. +usage: sbx completion powershell [flags] +options: + - name: help + shorthand: h + default_value: "false" + usage: help for powershell + - name: no-descriptions + default_value: "false" + usage: disable completion descriptions +inherited_options: + - name: debug + shorthand: D + default_value: "false" + usage: Enable debug logging +see_also: + - sbx completion - Generate the autocompletion script for the specified shell diff --git a/data/sbx_cli/sbx_completion_zsh.yaml b/data/sbx_cli/sbx_completion_zsh.yaml new file mode 100644 index 0000000000..2ccf93f0dd --- /dev/null +++ b/data/sbx_cli/sbx_completion_zsh.yaml @@ -0,0 +1,41 @@ +name: sbx completion zsh +synopsis: Generate the autocompletion script for zsh +description: | + Generate the autocompletion script for the zsh shell. + + If shell completion is not already enabled in your environment you will need + to enable it. You can execute the following once: + + echo "autoload -U compinit; compinit" >> ~/.zshrc + + To load completions in your current shell session: + + source <(sbx completion zsh) + + To load completions for every new session, execute once: + + #### Linux: + + sbx completion zsh > "${fpath[1]}/_sbx" + + #### macOS: + + sbx completion zsh > $(brew --prefix)/share/zsh/site-functions/_sbx + + You will need to start a new shell for this setup to take effect. +usage: sbx completion zsh [flags] +options: + - name: help + shorthand: h + default_value: "false" + usage: help for zsh + - name: no-descriptions + default_value: "false" + usage: disable completion descriptions +inherited_options: + - name: debug + shorthand: D + default_value: "false" + usage: Enable debug logging +see_also: + - sbx completion - Generate the autocompletion script for the specified shell diff --git a/data/sbx_cli/sbx_create.yaml b/data/sbx_cli/sbx_create.yaml new file mode 100644 index 0000000000..99ce7bd634 --- /dev/null +++ b/data/sbx_cli/sbx_create.yaml @@ -0,0 +1,56 @@ +name: sbx create +synopsis: Create a sandbox for an agent +description: |- + Create a sandbox with access to a host workspace for an agent. + + Use "sbx run SANDBOX" to attach to the agent after creation. +usage: sbx create [flags] AGENT PATH [PATH...] +options: + - name: branch + usage: Create a Git worktree on the given branch + - name: help + shorthand: h + default_value: "false" + usage: help for create + - name: memory + shorthand: m + usage: | + Memory limit in binary units (e.g., 1024m, 8g). Default: 50% of host memory, max 32 GiB + - name: name + usage: | + Name for the sandbox (default: -, letters, numbers, hyphens, periods, plus signs and minus signs only) + - name: quiet + shorthand: q + default_value: "false" + usage: Suppress verbose output + - name: template + shorthand: t + usage: | + Container image to use for the sandbox (default: agent-specific image) +inherited_options: + - name: debug + shorthand: D + default_value: "false" + usage: Enable debug logging +example: |4- + # Create a sandbox for Claude in the current directory + sbx create claude . + + # Create a sandbox with a custom name + sbx create --name my-project claude /path/to/project + + # Create with additional read-only workspaces + sbx create claude . /path/to/docs:ro + + # Create with a Git worktree for isolated changes + sbx create --branch=feature/login claude . +see_also: + - sbx - Manage AI coding agent sandboxes. + - sbx create claude - Create a sandbox for claude + - sbx create codex - Create a sandbox for codex + - sbx create copilot - Create a sandbox for copilot + - sbx create docker-agent - Create a sandbox for docker-agent + - sbx create gemini - Create a sandbox for gemini + - sbx create kiro - Create a sandbox for kiro + - sbx create opencode - Create a sandbox for opencode + - sbx create shell - Create a sandbox for shell diff --git a/data/sbx_cli/sbx_create_claude.yaml b/data/sbx_cli/sbx_create_claude.yaml new file mode 100644 index 0000000000..455e1ffda3 --- /dev/null +++ b/data/sbx_cli/sbx_create_claude.yaml @@ -0,0 +1,49 @@ +name: sbx create claude +synopsis: Create a sandbox for claude +description: |- + Create a sandbox with access to a host workspace for claude. + + The workspace path is required and will be mounted inside the sandbox at the + same path as on the host. Additional workspaces can be provided as extra + arguments. Append ":ro" to mount them read-only. + + Use "sbx run SANDBOX" to attach to the agent after creation. +usage: sbx create claude PATH [PATH...] [flags] +options: + - name: help + shorthand: h + default_value: "false" + usage: help for claude +inherited_options: + - name: branch + usage: Create a Git worktree on the given branch + - name: debug + shorthand: D + default_value: "false" + usage: Enable debug logging + - name: memory + shorthand: m + usage: | + Memory limit in binary units (e.g., 1024m, 8g). Default: 50% of host memory, max 32 GiB + - name: name + usage: | + Name for the sandbox (default: -, letters, numbers, hyphens, periods, plus signs and minus signs only) + - name: quiet + shorthand: q + default_value: "false" + usage: Suppress verbose output + - name: template + shorthand: t + usage: | + Container image to use for the sandbox (default: agent-specific image) +example: |4- + # Create in the current directory + sbx create claude . + + # Create with a specific path + sbx create claude /path/to/project + + # Create with additional read-only workspaces + sbx create claude . /path/to/docs:ro +see_also: + - sbx create - Create a sandbox for an agent diff --git a/data/sbx_cli/sbx_create_codex.yaml b/data/sbx_cli/sbx_create_codex.yaml new file mode 100644 index 0000000000..e99f6af82b --- /dev/null +++ b/data/sbx_cli/sbx_create_codex.yaml @@ -0,0 +1,49 @@ +name: sbx create codex +synopsis: Create a sandbox for codex +description: |- + Create a sandbox with access to a host workspace for codex. + + The workspace path is required and will be mounted inside the sandbox at the + same path as on the host. Additional workspaces can be provided as extra + arguments. Append ":ro" to mount them read-only. + + Use "sbx run SANDBOX" to attach to the agent after creation. +usage: sbx create codex PATH [PATH...] [flags] +options: + - name: help + shorthand: h + default_value: "false" + usage: help for codex +inherited_options: + - name: branch + usage: Create a Git worktree on the given branch + - name: debug + shorthand: D + default_value: "false" + usage: Enable debug logging + - name: memory + shorthand: m + usage: | + Memory limit in binary units (e.g., 1024m, 8g). Default: 50% of host memory, max 32 GiB + - name: name + usage: | + Name for the sandbox (default: -, letters, numbers, hyphens, periods, plus signs and minus signs only) + - name: quiet + shorthand: q + default_value: "false" + usage: Suppress verbose output + - name: template + shorthand: t + usage: | + Container image to use for the sandbox (default: agent-specific image) +example: |4- + # Create in the current directory + sbx create codex . + + # Create with a specific path + sbx create codex /path/to/project + + # Create with additional read-only workspaces + sbx create codex . /path/to/docs:ro +see_also: + - sbx create - Create a sandbox for an agent diff --git a/data/sbx_cli/sbx_create_copilot.yaml b/data/sbx_cli/sbx_create_copilot.yaml new file mode 100644 index 0000000000..06a2c48032 --- /dev/null +++ b/data/sbx_cli/sbx_create_copilot.yaml @@ -0,0 +1,49 @@ +name: sbx create copilot +synopsis: Create a sandbox for copilot +description: |- + Create a sandbox with access to a host workspace for copilot. + + The workspace path is required and will be mounted inside the sandbox at the + same path as on the host. Additional workspaces can be provided as extra + arguments. Append ":ro" to mount them read-only. + + Use "sbx run SANDBOX" to attach to the agent after creation. +usage: sbx create copilot PATH [PATH...] [flags] +options: + - name: help + shorthand: h + default_value: "false" + usage: help for copilot +inherited_options: + - name: branch + usage: Create a Git worktree on the given branch + - name: debug + shorthand: D + default_value: "false" + usage: Enable debug logging + - name: memory + shorthand: m + usage: | + Memory limit in binary units (e.g., 1024m, 8g). Default: 50% of host memory, max 32 GiB + - name: name + usage: | + Name for the sandbox (default: -, letters, numbers, hyphens, periods, plus signs and minus signs only) + - name: quiet + shorthand: q + default_value: "false" + usage: Suppress verbose output + - name: template + shorthand: t + usage: | + Container image to use for the sandbox (default: agent-specific image) +example: |4- + # Create in the current directory + sbx create copilot . + + # Create with a specific path + sbx create copilot /path/to/project + + # Create with additional read-only workspaces + sbx create copilot . /path/to/docs:ro +see_also: + - sbx create - Create a sandbox for an agent diff --git a/data/sbx_cli/sbx_create_docker-agent.yaml b/data/sbx_cli/sbx_create_docker-agent.yaml new file mode 100644 index 0000000000..d7824dc954 --- /dev/null +++ b/data/sbx_cli/sbx_create_docker-agent.yaml @@ -0,0 +1,49 @@ +name: sbx create docker-agent +synopsis: Create a sandbox for docker-agent +description: |- + Create a sandbox with access to a host workspace for docker-agent. + + The workspace path is required and will be mounted inside the sandbox at the + same path as on the host. Additional workspaces can be provided as extra + arguments. Append ":ro" to mount them read-only. + + Use "sbx run SANDBOX" to attach to the agent after creation. +usage: sbx create docker-agent PATH [PATH...] [flags] +options: + - name: help + shorthand: h + default_value: "false" + usage: help for docker-agent +inherited_options: + - name: branch + usage: Create a Git worktree on the given branch + - name: debug + shorthand: D + default_value: "false" + usage: Enable debug logging + - name: memory + shorthand: m + usage: | + Memory limit in binary units (e.g., 1024m, 8g). Default: 50% of host memory, max 32 GiB + - name: name + usage: | + Name for the sandbox (default: -, letters, numbers, hyphens, periods, plus signs and minus signs only) + - name: quiet + shorthand: q + default_value: "false" + usage: Suppress verbose output + - name: template + shorthand: t + usage: | + Container image to use for the sandbox (default: agent-specific image) +example: |4- + # Create in the current directory + sbx create docker-agent . + + # Create with a specific path + sbx create docker-agent /path/to/project + + # Create with additional read-only workspaces + sbx create docker-agent . /path/to/docs:ro +see_also: + - sbx create - Create a sandbox for an agent diff --git a/data/sbx_cli/sbx_create_gemini.yaml b/data/sbx_cli/sbx_create_gemini.yaml new file mode 100644 index 0000000000..f74247b3f8 --- /dev/null +++ b/data/sbx_cli/sbx_create_gemini.yaml @@ -0,0 +1,49 @@ +name: sbx create gemini +synopsis: Create a sandbox for gemini +description: |- + Create a sandbox with access to a host workspace for gemini. + + The workspace path is required and will be mounted inside the sandbox at the + same path as on the host. Additional workspaces can be provided as extra + arguments. Append ":ro" to mount them read-only. + + Use "sbx run SANDBOX" to attach to the agent after creation. +usage: sbx create gemini PATH [PATH...] [flags] +options: + - name: help + shorthand: h + default_value: "false" + usage: help for gemini +inherited_options: + - name: branch + usage: Create a Git worktree on the given branch + - name: debug + shorthand: D + default_value: "false" + usage: Enable debug logging + - name: memory + shorthand: m + usage: | + Memory limit in binary units (e.g., 1024m, 8g). Default: 50% of host memory, max 32 GiB + - name: name + usage: | + Name for the sandbox (default: -, letters, numbers, hyphens, periods, plus signs and minus signs only) + - name: quiet + shorthand: q + default_value: "false" + usage: Suppress verbose output + - name: template + shorthand: t + usage: | + Container image to use for the sandbox (default: agent-specific image) +example: |4- + # Create in the current directory + sbx create gemini . + + # Create with a specific path + sbx create gemini /path/to/project + + # Create with additional read-only workspaces + sbx create gemini . /path/to/docs:ro +see_also: + - sbx create - Create a sandbox for an agent diff --git a/data/sbx_cli/sbx_create_kiro.yaml b/data/sbx_cli/sbx_create_kiro.yaml new file mode 100644 index 0000000000..766a5cca8f --- /dev/null +++ b/data/sbx_cli/sbx_create_kiro.yaml @@ -0,0 +1,49 @@ +name: sbx create kiro +synopsis: Create a sandbox for kiro +description: |- + Create a sandbox with access to a host workspace for kiro. + + The workspace path is required and will be mounted inside the sandbox at the + same path as on the host. Additional workspaces can be provided as extra + arguments. Append ":ro" to mount them read-only. + + Use "sbx run SANDBOX" to attach to the agent after creation. +usage: sbx create kiro PATH [PATH...] [flags] +options: + - name: help + shorthand: h + default_value: "false" + usage: help for kiro +inherited_options: + - name: branch + usage: Create a Git worktree on the given branch + - name: debug + shorthand: D + default_value: "false" + usage: Enable debug logging + - name: memory + shorthand: m + usage: | + Memory limit in binary units (e.g., 1024m, 8g). Default: 50% of host memory, max 32 GiB + - name: name + usage: | + Name for the sandbox (default: -, letters, numbers, hyphens, periods, plus signs and minus signs only) + - name: quiet + shorthand: q + default_value: "false" + usage: Suppress verbose output + - name: template + shorthand: t + usage: | + Container image to use for the sandbox (default: agent-specific image) +example: |4- + # Create in the current directory + sbx create kiro . + + # Create with a specific path + sbx create kiro /path/to/project + + # Create with additional read-only workspaces + sbx create kiro . /path/to/docs:ro +see_also: + - sbx create - Create a sandbox for an agent diff --git a/data/sbx_cli/sbx_create_opencode.yaml b/data/sbx_cli/sbx_create_opencode.yaml new file mode 100644 index 0000000000..ea45254945 --- /dev/null +++ b/data/sbx_cli/sbx_create_opencode.yaml @@ -0,0 +1,49 @@ +name: sbx create opencode +synopsis: Create a sandbox for opencode +description: |- + Create a sandbox with access to a host workspace for opencode. + + The workspace path is required and will be mounted inside the sandbox at the + same path as on the host. Additional workspaces can be provided as extra + arguments. Append ":ro" to mount them read-only. + + Use "sbx run SANDBOX" to attach to the agent after creation. +usage: sbx create opencode PATH [PATH...] [flags] +options: + - name: help + shorthand: h + default_value: "false" + usage: help for opencode +inherited_options: + - name: branch + usage: Create a Git worktree on the given branch + - name: debug + shorthand: D + default_value: "false" + usage: Enable debug logging + - name: memory + shorthand: m + usage: | + Memory limit in binary units (e.g., 1024m, 8g). Default: 50% of host memory, max 32 GiB + - name: name + usage: | + Name for the sandbox (default: -, letters, numbers, hyphens, periods, plus signs and minus signs only) + - name: quiet + shorthand: q + default_value: "false" + usage: Suppress verbose output + - name: template + shorthand: t + usage: | + Container image to use for the sandbox (default: agent-specific image) +example: |4- + # Create in the current directory + sbx create opencode . + + # Create with a specific path + sbx create opencode /path/to/project + + # Create with additional read-only workspaces + sbx create opencode . /path/to/docs:ro +see_also: + - sbx create - Create a sandbox for an agent diff --git a/data/sbx_cli/sbx_create_shell.yaml b/data/sbx_cli/sbx_create_shell.yaml new file mode 100644 index 0000000000..c591992fdd --- /dev/null +++ b/data/sbx_cli/sbx_create_shell.yaml @@ -0,0 +1,49 @@ +name: sbx create shell +synopsis: Create a sandbox for shell +description: |- + Create a sandbox with access to a host workspace for shell. + + The workspace path is required and will be mounted inside the sandbox at the + same path as on the host. Additional workspaces can be provided as extra + arguments. Append ":ro" to mount them read-only. + + Use "sbx run SANDBOX" to attach to the agent after creation. +usage: sbx create shell PATH [PATH...] [flags] +options: + - name: help + shorthand: h + default_value: "false" + usage: help for shell +inherited_options: + - name: branch + usage: Create a Git worktree on the given branch + - name: debug + shorthand: D + default_value: "false" + usage: Enable debug logging + - name: memory + shorthand: m + usage: | + Memory limit in binary units (e.g., 1024m, 8g). Default: 50% of host memory, max 32 GiB + - name: name + usage: | + Name for the sandbox (default: -, letters, numbers, hyphens, periods, plus signs and minus signs only) + - name: quiet + shorthand: q + default_value: "false" + usage: Suppress verbose output + - name: template + shorthand: t + usage: | + Container image to use for the sandbox (default: agent-specific image) +example: |4- + # Create in the current directory + sbx create shell . + + # Create with a specific path + sbx create shell /path/to/project + + # Create with additional read-only workspaces + sbx create shell . /path/to/docs:ro +see_also: + - sbx create - Create a sandbox for an agent diff --git a/data/sbx_cli/sbx_exec.yaml b/data/sbx_cli/sbx_exec.yaml new file mode 100644 index 0000000000..d060aad82f --- /dev/null +++ b/data/sbx_cli/sbx_exec.yaml @@ -0,0 +1,58 @@ +name: sbx exec +synopsis: Execute a command inside a sandbox +description: |- + Execute a command in a sandbox. If the sandbox is stopped, it is started first. + + Flags match the behavior of "docker exec". +usage: sbx exec [flags] SANDBOX COMMAND [ARG...] +options: + - name: detach + shorthand: d + default_value: "false" + usage: 'Detached mode: run command in the background' + - name: detach-keys + usage: Override the key sequence for detaching a container + - name: env + shorthand: e + default_value: '[]' + usage: Set environment variables + - name: env-file + default_value: '[]' + usage: Read in a file of environment variables + - name: help + shorthand: h + default_value: "false" + usage: help for exec + - name: interactive + shorthand: i + default_value: "false" + usage: Keep STDIN open even if not attached + - name: privileged + default_value: "false" + usage: Give extended privileges to the command + - name: tty + shorthand: t + default_value: "false" + usage: Allocate a pseudo-TTY + - name: user + shorthand: u + usage: 'Username or UID (format: [:])' + - name: workdir + shorthand: w + usage: Working directory inside the container +inherited_options: + - name: debug + shorthand: D + default_value: "false" + usage: Enable debug logging +example: |4- + # Open a shell inside a sandbox + sbx exec -it my-sandbox bash + + # Run a command in the background + sbx exec -d my-sandbox npm start + + # Run as root + sbx exec -u root my-sandbox apt-get update +see_also: + - sbx - Manage AI coding agent sandboxes. diff --git a/data/sbx_cli/sbx_login.yaml b/data/sbx_cli/sbx_login.yaml new file mode 100644 index 0000000000..6635623a85 --- /dev/null +++ b/data/sbx_cli/sbx_login.yaml @@ -0,0 +1,15 @@ +name: sbx login +synopsis: Sign in to Docker +usage: sbx login [flags] +options: + - name: help + shorthand: h + default_value: "false" + usage: help for login +inherited_options: + - name: debug + shorthand: D + default_value: "false" + usage: Enable debug logging +see_also: + - sbx - Manage AI coding agent sandboxes. diff --git a/data/sbx_cli/sbx_logout.yaml b/data/sbx_cli/sbx_logout.yaml new file mode 100644 index 0000000000..7d030e1b1c --- /dev/null +++ b/data/sbx_cli/sbx_logout.yaml @@ -0,0 +1,15 @@ +name: sbx logout +synopsis: Sign out of Docker +usage: sbx logout [flags] +options: + - name: help + shorthand: h + default_value: "false" + usage: help for logout +inherited_options: + - name: debug + shorthand: D + default_value: "false" + usage: Enable debug logging +see_also: + - sbx - Manage AI coding agent sandboxes. diff --git a/data/sbx_cli/sbx_ls.yaml b/data/sbx_cli/sbx_ls.yaml new file mode 100644 index 0000000000..51f664b274 --- /dev/null +++ b/data/sbx_cli/sbx_ls.yaml @@ -0,0 +1,24 @@ +name: sbx ls +synopsis: List sandboxes +description: | + List all sandboxes with their agent, status, published ports, and workspace. +usage: sbx ls [flags] +options: + - name: help + shorthand: h + default_value: "false" + usage: help for ls + - name: json + default_value: "false" + usage: Output in JSON format + - name: quiet + shorthand: q + default_value: "false" + usage: Only display sandbox names +inherited_options: + - name: debug + shorthand: D + default_value: "false" + usage: Enable debug logging +see_also: + - sbx - Manage AI coding agent sandboxes. diff --git a/data/sbx_cli/sbx_policy.yaml b/data/sbx_cli/sbx_policy.yaml new file mode 100644 index 0000000000..693817fc2e --- /dev/null +++ b/data/sbx_cli/sbx_policy.yaml @@ -0,0 +1,28 @@ +name: sbx policy +synopsis: Manage sandbox policies +description: |- + Manage persistent access policies for sandboxes. + + Policies are rules stored locally that control what sandboxes can access. + They apply globally across all sandboxes and persist across restarts. + Use subcommands to allow, deny, list, or remove policies. +usage: sbx policy COMMAND +options: + - name: help + shorthand: h + default_value: "false" + usage: help for policy +inherited_options: + - name: debug + shorthand: D + default_value: "false" + usage: Enable debug logging +see_also: + - sbx - Manage AI coding agent sandboxes. + - sbx policy allow - Add an allow policy for sandboxes + - sbx policy deny - Add a deny policy for sandboxes + - sbx policy log - Show sandbox policy logs + - sbx policy ls - List sandbox policies + - sbx policy reset - Reset policies to defaults + - sbx policy rm - Remove a policy + - sbx policy set-default - Set the default network policy diff --git a/data/sbx_cli/sbx_policy_allow.yaml b/data/sbx_cli/sbx_policy_allow.yaml new file mode 100644 index 0000000000..c9f2dfa017 --- /dev/null +++ b/data/sbx_cli/sbx_policy_allow.yaml @@ -0,0 +1,21 @@ +name: sbx policy allow +synopsis: Add an allow policy for sandboxes +description: |- + Add a policy that permits sandboxes to access specified resources. + + Allowed resources are accessible to all sandboxes. If a resource matches both + an allow and a deny rule, the deny rule takes precedence. +usage: sbx policy allow COMMAND +options: + - name: help + shorthand: h + default_value: "false" + usage: help for allow +inherited_options: + - name: debug + shorthand: D + default_value: "false" + usage: Enable debug logging +see_also: + - sbx policy - Manage sandbox policies + - sbx policy allow network - Allow network access to specified hosts diff --git a/data/sbx_cli/sbx_policy_allow_network.yaml b/data/sbx_cli/sbx_policy_allow_network.yaml new file mode 100644 index 0000000000..6ec52fe692 --- /dev/null +++ b/data/sbx_cli/sbx_policy_allow_network.yaml @@ -0,0 +1,33 @@ +name: sbx policy allow network +synopsis: Allow network access to specified hosts +description: |- + Allow sandbox network access to the specified hosts. + + RESOURCES is a comma-separated list of hostnames, domains, or IP addresses. + Supports exact domains (example.com), wildcard subdomains (*.example.com), + and optional port suffixes (example.com:443). Use "**" to allow all hosts. +usage: sbx policy allow network RESOURCES [flags] +options: + - name: help + shorthand: h + default_value: "false" + usage: help for network +inherited_options: + - name: debug + shorthand: D + default_value: "false" + usage: Enable debug logging +example: |4- + # Allow access to a single host + sbx policy allow network api.example.com + + # Allow access to multiple hosts + sbx policy allow network "api.example.com,cdn.example.com" + + # Allow all subdomains of a host + sbx policy allow network "*.npmjs.org" + + # Allow all outbound traffic + sbx policy allow network "**" +see_also: + - sbx policy allow - Add an allow policy for sandboxes diff --git a/data/sbx_cli/sbx_policy_deny.yaml b/data/sbx_cli/sbx_policy_deny.yaml new file mode 100644 index 0000000000..885ec0e68c --- /dev/null +++ b/data/sbx_cli/sbx_policy_deny.yaml @@ -0,0 +1,21 @@ +name: sbx policy deny +synopsis: Add a deny policy for sandboxes +description: |- + Add a policy that blocks sandboxes from accessing specified resources. + + Deny rules always take precedence over allow rules. If a resource matches + both an allow and a deny rule, the request is blocked. +usage: sbx policy deny COMMAND +options: + - name: help + shorthand: h + default_value: "false" + usage: help for deny +inherited_options: + - name: debug + shorthand: D + default_value: "false" + usage: Enable debug logging +see_also: + - sbx policy - Manage sandbox policies + - sbx policy deny network - Deny network access to specified hosts diff --git a/data/sbx_cli/sbx_policy_deny_network.yaml b/data/sbx_cli/sbx_policy_deny_network.yaml new file mode 100644 index 0000000000..b26ceffba3 --- /dev/null +++ b/data/sbx_cli/sbx_policy_deny_network.yaml @@ -0,0 +1,26 @@ +name: sbx policy deny network +synopsis: Deny network access to specified hosts +description: |- + Block sandbox network access to the specified hosts. + + RESOURCES is a comma-separated list of hostnames, domains, or IP addresses. + Deny rules always take precedence over allow rules. +usage: sbx policy deny network RESOURCES [flags] +options: + - name: help + shorthand: h + default_value: "false" + usage: help for network +inherited_options: + - name: debug + shorthand: D + default_value: "false" + usage: Enable debug logging +example: |4- + # Block access to a host + sbx policy deny network ads.example.com + + # Block all outbound traffic + sbx policy deny network "**" +see_also: + - sbx policy deny - Add a deny policy for sandboxes diff --git a/data/sbx_cli/sbx_policy_log.yaml b/data/sbx_cli/sbx_policy_log.yaml new file mode 100644 index 0000000000..34c68a60e9 --- /dev/null +++ b/data/sbx_cli/sbx_policy_log.yaml @@ -0,0 +1,46 @@ +name: sbx policy log +synopsis: Show sandbox policy logs +description: |- + Show policy logs for all sandboxes, or filter by a specific sandbox name. + + Displays which hosts were allowed or blocked by the proxy, along with the + matching rule, proxy type, and request count. Useful for debugging connectivity + issues or auditing network activity. +usage: sbx policy log [SANDBOX] [flags] +options: + - name: help + shorthand: h + default_value: "false" + usage: help for log + - name: json + default_value: "false" + usage: Output in JSON format + - name: limit + default_value: "0" + usage: Maximum number of log entries to show + - name: quiet + shorthand: q + default_value: "false" + usage: Only display log entries + - name: type + default_value: all + usage: 'Filter logs by type: "all" or "network" (default "all")' +inherited_options: + - name: debug + shorthand: D + default_value: "false" + usage: Enable debug logging +example: |4- + # Show all policy logs + sbx policy log + + # Show logs for a specific sandbox + sbx policy log my-sandbox + + # Output in JSON format + sbx policy log --json + + # Show the last 20 entries + sbx policy log --limit 20 +see_also: + - sbx policy - Manage sandbox policies diff --git a/data/sbx_cli/sbx_policy_ls.yaml b/data/sbx_cli/sbx_policy_ls.yaml new file mode 100644 index 0000000000..87493267f7 --- /dev/null +++ b/data/sbx_cli/sbx_policy_ls.yaml @@ -0,0 +1,29 @@ +name: sbx policy ls +synopsis: List sandbox policies +description: |- + List all active policies. + + Displays the policy name (or ID if no name is set), type, decision + (allow/deny), and the associated resources for each rule. +usage: sbx policy ls [flags] +options: + - name: help + shorthand: h + default_value: "false" + usage: help for ls + - name: type + default_value: all + usage: 'Filter policies by type: "all" or "network" (default "all")' +inherited_options: + - name: debug + shorthand: D + default_value: "false" + usage: Enable debug logging +example: |4- + # List all policies + sbx policy ls + + # List only network policies + sbx policy ls --type network +see_also: + - sbx policy - Manage sandbox policies diff --git a/data/sbx_cli/sbx_policy_reset.yaml b/data/sbx_cli/sbx_policy_reset.yaml new file mode 100644 index 0000000000..7c4c1ec80b --- /dev/null +++ b/data/sbx_cli/sbx_policy_reset.yaml @@ -0,0 +1,33 @@ +name: sbx policy reset +synopsis: Reset policies to defaults +description: |- + Remove all custom policies and restart the daemon to restore defaults. + + This deletes the local policy store and stops the daemon. When the daemon + restarts (automatically on next command), the default policy is installed. + + If sandboxes are currently running, they will be stopped when the daemon + shuts down. You will be prompted for confirmation unless --force is used. +usage: sbx policy reset [flags] +options: + - name: force + shorthand: f + default_value: "false" + usage: Skip confirmation prompt + - name: help + shorthand: h + default_value: "false" + usage: help for reset +inherited_options: + - name: debug + shorthand: D + default_value: "false" + usage: Enable debug logging +example: |4- + # Reset policies (prompts if sandboxes are running) + sbx policy reset + + # Reset policies without confirmation + sbx policy reset --force +see_also: + - sbx policy - Manage sandbox policies diff --git a/data/sbx_cli/sbx_policy_rm.yaml b/data/sbx_cli/sbx_policy_rm.yaml new file mode 100644 index 0000000000..0d71eaf6ee --- /dev/null +++ b/data/sbx_cli/sbx_policy_rm.yaml @@ -0,0 +1,17 @@ +name: sbx policy rm +synopsis: Remove a policy +description: Remove a previously added allow or deny policy. +usage: sbx policy rm COMMAND +options: + - name: help + shorthand: h + default_value: "false" + usage: help for rm +inherited_options: + - name: debug + shorthand: D + default_value: "false" + usage: Enable debug logging +see_also: + - sbx policy - Manage sandbox policies + - sbx policy rm network - Remove a network policy diff --git a/data/sbx_cli/sbx_policy_rm_network.yaml b/data/sbx_cli/sbx_policy_rm_network.yaml new file mode 100644 index 0000000000..7c154bf058 --- /dev/null +++ b/data/sbx_cli/sbx_policy_rm_network.yaml @@ -0,0 +1,32 @@ +name: sbx policy rm network +synopsis: Remove a network policy +description: |- + Remove a network policy by rule ID, resource, or both. + + Use "sbx policy ls" to see active policies and their IDs/resources. +usage: sbx policy rm network [flags] +options: + - name: help + shorthand: h + default_value: "false" + usage: help for network + - name: id + usage: Remove by rule ID + - name: resource + usage: Remove by resource value(s), comma-separated +inherited_options: + - name: debug + shorthand: D + default_value: "false" + usage: Enable debug logging +example: |4- + # Remove a rule by resource + sbx policy rm network --resource api.example.com + + # Remove a rule by ID + sbx policy rm network --id 2d3c1f0e-4a73-4e05-bc9d-f2f9a4b50d67 + + # Remove by ID and resource using one filter + sbx policy rm network --id 2d3c1f0e-4a73-4e05-bc9d-f2f9a4b50d67 --resource "api.example.com,cdn.example.com" +see_also: + - sbx policy rm - Remove a policy diff --git a/data/sbx_cli/sbx_policy_set-default.yaml b/data/sbx_cli/sbx_policy_set-default.yaml new file mode 100644 index 0000000000..1fce7a945e --- /dev/null +++ b/data/sbx_cli/sbx_policy_set-default.yaml @@ -0,0 +1,38 @@ +name: sbx policy set-default +synopsis: Set the default network policy +description: |- + Set the default network policy for all sandboxes. + + This must be run before adding custom allow/deny rules or starting a sandbox + for the first time. The default policy determines the baseline network access. + + Available policies: + allow-all All outbound network traffic is allowed + balanced Common dev traffic allowed (AI services, package registries, etc.) + deny-all All outbound network traffic is blocked + + After setting defaults, use "sbx policy allow/deny" to add custom rules. + Use "sbx policy reset" to clear all policies and start over. +usage: sbx policy set-default [flags] +options: + - name: help + shorthand: h + default_value: "false" + usage: help for set-default +inherited_options: + - name: debug + shorthand: D + default_value: "false" + usage: Enable debug logging +example: |4- + # Set balanced defaults (recommended) + sbx policy set-default balanced + + # Allow all traffic + sbx policy set-default allow-all + + # Block everything, then allow specific sites + sbx policy set-default deny-all + sbx policy allow network api.example.com:443 +see_also: + - sbx policy - Manage sandbox policies diff --git a/data/sbx_cli/sbx_ports.yaml b/data/sbx_cli/sbx_ports.yaml new file mode 100644 index 0000000000..14d586c116 --- /dev/null +++ b/data/sbx_cli/sbx_ports.yaml @@ -0,0 +1,48 @@ +name: sbx ports +synopsis: Manage sandbox port publishing +description: |- + Manage sandbox port publishing. + + List, publish, or unpublish ports for a running sandbox. Without --publish or + --unpublish flags, lists all published ports. + + Port spec format: [[HOST_IP:]HOST_PORT:]SANDBOX_PORT[/PROTOCOL] + If HOST_PORT is omitted, an ephemeral port is allocated automatically. + HOST_IP defaults to 127.0.0.1, PROTOCOL defaults to tcp. + Supported protocols: tcp, tcp4, tcp6, udp, udp4, udp6. +usage: sbx ports SANDBOX [flags] +options: + - name: help + shorthand: h + default_value: "false" + usage: help for ports + - name: json + default_value: "false" + usage: Output in JSON format (for port listing) + - name: publish + default_value: '[]' + usage: | + Publish a port (can be repeated): [[HOST_IP:]HOST_PORT:]SANDBOX_PORT[/PROTOCOL] + - name: unpublish + default_value: '[]' + usage: | + Unpublish a port (can be repeated): [HOST_IP:]HOST_PORT:SANDBOX_PORT[/PROTOCOL] +inherited_options: + - name: debug + shorthand: D + default_value: "false" + usage: Enable debug logging +example: |4- + # List published ports + sbx ports my-sandbox + + # Publish sandbox port 8080 to an ephemeral host port + sbx ports my-sandbox --publish 8080 + + # Publish with a specific host port + sbx ports my-sandbox --publish 3000:8080 + + # Unpublish a port + sbx ports my-sandbox --unpublish 3000:8080 +see_also: + - sbx - Manage AI coding agent sandboxes. diff --git a/data/sbx_cli/sbx_reset.yaml b/data/sbx_cli/sbx_reset.yaml new file mode 100644 index 0000000000..90f3cf4938 --- /dev/null +++ b/data/sbx_cli/sbx_reset.yaml @@ -0,0 +1,44 @@ +name: sbx reset +synopsis: Reset all sandboxes and clean up state +description: |- + Reset Docker Sandboxes to a freshly-installed state. + + This command will: + - Stop all running sandboxes gracefully (30s timeout) + - Clear image cache + - Clear all internal registries + - Delete all sandbox state + - Remove all policies + - Delete all stored secrets + - Sign out of Docker Sandboxes + - Stop the daemon + - Remove all state, cache, and config directories + + WARNING: This is destructive and cannot be undone. + Running agents will be terminated and their work lost. + Cached images will be deleted and recreated on next use. + Stored secrets will need to be re-entered. + + Use --preserve-secrets to keep stored secrets. + By default, you will be prompted to confirm (y/N). + Use --force to skip the confirmation prompt. +usage: sbx reset [flags] +options: + - name: force + shorthand: f + default_value: "false" + usage: Skip confirmation prompt + - name: help + shorthand: h + default_value: "false" + usage: help for reset + - name: preserve-secrets + default_value: "false" + usage: Keep stored secrets +inherited_options: + - name: debug + shorthand: D + default_value: "false" + usage: Enable debug logging +see_also: + - sbx - Manage AI coding agent sandboxes. diff --git a/data/sbx_cli/sbx_rm.yaml b/data/sbx_cli/sbx_rm.yaml new file mode 100644 index 0000000000..4e391e73ba --- /dev/null +++ b/data/sbx_cli/sbx_rm.yaml @@ -0,0 +1,30 @@ +name: sbx rm +synopsis: Remove one or more sandboxes +description: |- + Remove one or more sandboxes and all associated resources. + + Stops running sandboxes, removes their containers, cleans up any Git + worktrees, and deletes sandbox state. This action cannot be undone. + + Use --all to remove every sandbox (requires confirmation). + Use --force to skip confirmation prompts (for non-interactive scripts). +usage: sbx rm [SANDBOX...] [flags] +options: + - name: all + default_value: "false" + usage: Remove all sandboxes + - name: force + shorthand: f + default_value: "false" + usage: Skip confirmation prompts + - name: help + shorthand: h + default_value: "false" + usage: help for rm +inherited_options: + - name: debug + shorthand: D + default_value: "false" + usage: Enable debug logging +see_also: + - sbx - Manage AI coding agent sandboxes. diff --git a/data/sbx_cli/sbx_run.yaml b/data/sbx_cli/sbx_run.yaml new file mode 100644 index 0000000000..e9dfb8a6f8 --- /dev/null +++ b/data/sbx_cli/sbx_run.yaml @@ -0,0 +1,49 @@ +name: sbx run +synopsis: Run an agent in a sandbox +description: |- + Run an agent in a sandbox, creating the sandbox if it does not already exist. + + Pass agent arguments after the "--" separator. Additional workspaces can be + provided as extra arguments. Append ":ro" to mount them read-only. + + To create a sandbox without attaching, use "sbx create" instead. + + Available agents: claude, codex, copilot, docker-agent, gemini, kiro, opencode, shell +usage: sbx run [flags] SANDBOX | AGENT [PATH...] [-- AGENT_ARGS...] +options: + - name: branch + usage: | + Create a Git worktree on the given branch (use --branch auto to auto-generate) + - name: help + shorthand: h + default_value: "false" + usage: help for run + - name: memory + shorthand: m + usage: | + Memory limit in binary units (e.g., 1024m, 8g). Default: 50% of host memory, max 32 GiB + - name: name + usage: 'Name for the sandbox (default: -)' + - name: template + shorthand: t + usage: | + Container image to use for the sandbox (default: agent-specific image) +inherited_options: + - name: debug + shorthand: D + default_value: "false" + usage: Enable debug logging +example: |4- + # Create and run a sandbox with claude in current directory + sbx run claude + + # Create and run with additional workspaces (read-only) + sbx run claude . /path/to/docs:ro + + # Run an existing sandbox + sbx run existing-sandbox + + # Run a sandbox with agent arguments + sbx run claude -- --continue +see_also: + - sbx - Manage AI coding agent sandboxes. diff --git a/data/sbx_cli/sbx_save.yaml b/data/sbx_cli/sbx_save.yaml new file mode 100644 index 0000000000..3239e15ef8 --- /dev/null +++ b/data/sbx_cli/sbx_save.yaml @@ -0,0 +1,30 @@ +name: sbx save +synopsis: Save a snapshot of the sandbox as a template +description: |- + Save a snapshot of the sandbox as a template. + + By default, the image is loaded into the host's Docker daemon (requires Docker to be running). + Use --output to save the image to a tar file instead. +usage: sbx save SANDBOX TAG [flags] +options: + - name: help + shorthand: h + default_value: "false" + usage: help for save + - name: output + shorthand: o + usage: | + Save image to specified tar file instead of loading into host Docker +inherited_options: + - name: debug + shorthand: D + default_value: "false" + usage: Enable debug logging +example: |4- + # Load into host Docker (requires host Docker running) + sbx save my-sandbox myimage:v1.0 + + # Save to file (works without host Docker) + sbx save my-sandbox myimage:v1.0 --output /tmp/myimage.tar +see_also: + - sbx - Manage AI coding agent sandboxes. diff --git a/data/sbx_cli/sbx_secret.yaml b/data/sbx_cli/sbx_secret.yaml new file mode 100644 index 0000000000..55b80ff61d --- /dev/null +++ b/data/sbx_cli/sbx_secret.yaml @@ -0,0 +1,27 @@ +name: sbx secret +synopsis: Manage stored secrets +description: |- + Manage stored secrets for sandbox environments. + + Secrets are stored per service name (e.g., "github", "anthropic", "openai"). + When a sandbox starts, the proxy uses stored secrets to authenticate API + requests on behalf of the agent. The secret is never exposed directly to the + agent. + + Secrets can be scoped globally (shared across all sandboxes) or to a + specific sandbox. +options: + - name: help + shorthand: h + default_value: "false" + usage: help for secret +inherited_options: + - name: debug + shorthand: D + default_value: "false" + usage: Enable debug logging +see_also: + - sbx - Manage AI coding agent sandboxes. + - sbx secret ls - List stored secrets + - sbx secret rm - Remove a secret + - sbx secret set - Create or update a secret diff --git a/data/sbx_cli/sbx_secret_ls.yaml b/data/sbx_cli/sbx_secret_ls.yaml new file mode 100644 index 0000000000..4105985920 --- /dev/null +++ b/data/sbx_cli/sbx_secret_ls.yaml @@ -0,0 +1,33 @@ +name: sbx secret ls +synopsis: List stored secrets +usage: sbx secret ls [sandbox] [OPTIONS] [flags] +options: + - name: global + shorthand: g + default_value: "false" + usage: Only list global secrets + - name: help + shorthand: h + default_value: "false" + usage: help for ls + - name: service + usage: Filter by secret service name +inherited_options: + - name: debug + shorthand: D + default_value: "false" + usage: Enable debug logging +example: |4- + # List all secrets + sbx secret ls + + # List only global secrets + sbx secret ls -g + + # List secrets for a specific sandbox + sbx secret ls my-sandbox + + # Filter by service + sbx secret ls --service github +see_also: + - sbx secret - Manage stored secrets diff --git a/data/sbx_cli/sbx_secret_rm.yaml b/data/sbx_cli/sbx_secret_rm.yaml new file mode 100644 index 0000000000..06ab46132e --- /dev/null +++ b/data/sbx_cli/sbx_secret_rm.yaml @@ -0,0 +1,32 @@ +name: sbx secret rm +synopsis: Remove a secret +usage: sbx secret rm [-g | sandbox] [service] [flags] +options: + - name: force + shorthand: f + default_value: "false" + usage: Delete without confirmation prompt + - name: global + shorthand: g + default_value: "false" + usage: Use global secret scope + - name: help + shorthand: h + default_value: "false" + usage: help for rm +inherited_options: + - name: debug + shorthand: D + default_value: "false" + usage: Enable debug logging +example: |4- + # Remove a global secret + sbx secret rm -g github + + # Remove a sandbox-scoped secret + sbx secret rm my-sandbox openai + + # Remove without confirmation prompt + sbx secret rm -g github -f +see_also: + - sbx secret - Manage stored secrets diff --git a/data/sbx_cli/sbx_secret_set.yaml b/data/sbx_cli/sbx_secret_set.yaml new file mode 100644 index 0000000000..250e6a31b8 --- /dev/null +++ b/data/sbx_cli/sbx_secret_set.yaml @@ -0,0 +1,42 @@ +name: sbx secret set +synopsis: Create or update a secret +description: |- + Create or update a secret for a service. + + Available services: anthropic, aws, github, google, groq, mistral, nebius, openai, xai + + When no arguments are provided, an interactive prompt guides you through + scope and service selection. +usage: sbx secret set [-g | sandbox] [service] [flags] +options: + - name: force + shorthand: f + default_value: "false" + usage: Overwrite an existing secret when --token is used + - name: global + shorthand: g + default_value: "false" + usage: Use global secret scope + - name: help + shorthand: h + default_value: "false" + usage: help for set + - name: token + shorthand: t + usage: 'Secret value (less secure: visible in shell history)' +inherited_options: + - name: debug + shorthand: D + default_value: "false" + usage: Enable debug logging +example: |4- + # Store a GitHub token globally (available to all sandboxes) + sbx secret set -g github + + # Store an OpenAI key for a specific sandbox + sbx secret set my-sandbox openai + + # Non-interactive via stdin (e.g., from a secret manager or env var) + echo "$ANTHROPIC_API_KEY" | sbx secret set -g anthropic +see_also: + - sbx secret - Manage stored secrets diff --git a/data/sbx_cli/sbx_stop.yaml b/data/sbx_cli/sbx_stop.yaml new file mode 100644 index 0000000000..97b32c1b61 --- /dev/null +++ b/data/sbx_cli/sbx_stop.yaml @@ -0,0 +1,19 @@ +name: sbx stop +synopsis: Stop one or more sandboxes without removing them +description: |- + Stop one or more running sandboxes without removing them. + + Stopped sandboxes retain their state and can be restarted with "sbx run". +usage: sbx stop SANDBOX [SANDBOX...] +options: + - name: help + shorthand: h + default_value: "false" + usage: help for stop +inherited_options: + - name: debug + shorthand: D + default_value: "false" + usage: Enable debug logging +see_also: + - sbx - Manage AI coding agent sandboxes. diff --git a/data/sbx_cli/sbx_version.yaml b/data/sbx_cli/sbx_version.yaml new file mode 100644 index 0000000000..cdab8a1f16 --- /dev/null +++ b/data/sbx_cli/sbx_version.yaml @@ -0,0 +1,15 @@ +name: sbx version +synopsis: Show Docker Sandboxes version information +usage: sbx version +options: + - name: help + shorthand: h + default_value: "false" + usage: help for version +inherited_options: + - name: debug + shorthand: D + default_value: "false" + usage: Enable debug logging +see_also: + - sbx - Manage AI coding agent sandboxes. diff --git a/data/summary.yaml b/data/summary.yaml index d3953bf540..b3c0639efb 100644 --- a/data/summary.yaml +++ b/data/summary.yaml @@ -195,6 +195,8 @@ Docker Pass: availability: Beta Docker Projects: availability: Beta +Docker Sandboxes sbx: + availability: Experimental Docker Sandboxes: availability: Experimental requires: Docker Desktop [4.58](/manuals/desktop/release-notes.md#4580) or later diff --git a/hugo.yaml b/hugo.yaml index f7f95da3fd..70878452bf 100644 --- a/hugo.yaml +++ b/hugo.yaml @@ -30,6 +30,13 @@ cascade: - target: path: /reference/cli/docker/** layout: cli + # sbx CLI reference pages use the sbx-cli layout (cobra/doc YAML) + - target: + path: /reference/cli/sbx + layout: sbx-cli + - target: + path: /reference/cli/sbx/** + layout: sbx-cli # Remove the /manuals prefix for content in the manuals section permalinks: diff --git a/layouts/sbx-cli.html b/layouts/sbx-cli.html new file mode 100644 index 0000000000..654b61d31b --- /dev/null +++ b/layouts/sbx-cli.html @@ -0,0 +1,179 @@ +{{ define "article" }} + {{ $data := index site.Data.sbx_cli .Params.datafile }} + + {{ .Store.Set "headings" slice }} +
+ {{ partial "breadcrumbs.html" . }} +
+

{{ .Title }}

+
+ + {{- /* Summary table */ -}} +
+ + + {{ with $data.synopsis }} + + + + + {{ end }} + {{ with $data.usage }} + + + + + {{ end }} + +
Description{{ . }}
Usage{{ . }}
+
+ + {{- /* Description */ -}} + {{ with $data.description }} + {{ $heading := dict "level" 2 "text" "Description" }} + {{ partialCached "heading.html" $heading "sbx-cli-description" }} + {{ $.Store.Add "headings" $heading }} + {{ . | $.RenderString (dict "display" "block") }} + {{ end }} + + {{- /* Subcommands (for section pages) */ -}} + {{ if eq .Kind "section" }} + {{ $heading := dict "level" 2 "text" "Commands" }} + {{ partialCached "heading.html" $heading "sbx-cli-commands" }} + {{ $.Store.Add "headings" $heading }} + + + + + + + + + {{ range .Pages }} + {{ if .Params.datafile }} + {{ $child := index site.Data.sbx_cli .Params.datafile }} + + + + + {{ end }} + {{ end }} + +
CommandDescription
{{ .Title }}{{ $child.synopsis }}
+ {{ end }} + + {{- /* Options */ -}} + {{ with $data.options }} + {{ $opts := where . "name" "ne" "help" }} + {{ with $opts }} + {{ $heading := dict "level" 2 "text" "Options" }} + {{ partialCached "heading.html" $heading "sbx-cli-options" }} + {{ $.Store.Add "headings" $heading }} +
+ + + + + + + + + + {{ range . }} + + + {{ $skipDefault := `[],false,` }} + + + + {{ end }} + +
OptionDefaultDescription
+ {{ with .shorthand }}-{{ . }}, {{ end }}--{{ .name }} + + {{ with .default_value }} + {{ cond (in $skipDefault .) "" (printf "%s" . | safeHTML) }} + {{ end }} + + {{ with .usage }} + {{ strings.TrimSpace . }} + {{ end }} +
+
+ {{ end }} + {{ end }} + + {{- /* Inherited (global) options */ -}} + {{ with $data.inherited_options }} + {{ $opts := where . "name" "ne" "help" }} + {{ with $opts }} + {{ $heading := dict "level" 2 "text" "Global options" }} + {{ partialCached "heading.html" $heading "sbx-cli-global-options" }} + {{ $.Store.Add "headings" $heading }} +
+ + + + + + + + + + {{ range . }} + + + {{ $skipDefault := `[],false,` }} + + + + {{ end }} + +
OptionDefaultDescription
+ {{ with .shorthand }}-{{ . }}, {{ end }}--{{ .name }} + + {{ with .default_value }} + {{ cond (in $skipDefault .) "" (printf "%s" . | safeHTML) }} + {{ end }} + + {{ with .usage }} + {{ strings.TrimSpace . }} + {{ end }} +
+
+ {{ end }} + {{ end }} + + {{- /* Examples */ -}} + {{ with $data.example }} + {{ $heading := dict "level" 2 "text" "Examples" }} + {{ partialCached "heading.html" $heading "sbx-cli-examples" }} + {{ $.Store.Add "headings" $heading }} + {{- /* Dedent: strip up to 6 leading spaces from each line */ -}} + {{ $dedented := replaceRE `(?m)^ {2,6}` "" . }} + {{ $code := printf "```console\n%s\n```" (strings.TrimSpace $dedented) }} + {{ $code | $.RenderString (dict "display" "block") }} + {{ end }} + +
+{{ end }} + +{{ define "right" }} +
+{{ end }}