mirror of
https://github.com/docker/docs.git
synced 2026-06-19 07:35:16 +00:00
Merge pull request #25339 from dvdksn/worktree-split-kits-reference
Split kit spec reference and update kit field renames
This commit is contained in:
@@ -40,12 +40,12 @@ dependencies — anything you'd rather not reinstall on every sandbox start.
|
||||
|
||||
A kit is a YAML artifact applied at sandbox creation. The kit can run
|
||||
install commands, drop files into the sandbox, declare network and
|
||||
credential rules, and (for agent kits) define which template image the
|
||||
credential rules, and (for sandbox kits) define which template image the
|
||||
agent runs in. Use kits for things that vary per agent or per team:
|
||||
shared linter config, project-specific install steps, credential
|
||||
injection for a service the agent talks to.
|
||||
|
||||
Templates and kits work together. An agent kit's `agent.image` field
|
||||
Templates and kits work together. A sandbox kit's `sandbox.image` field
|
||||
points at a template: the template provides the base environment, the
|
||||
kit layers config, secrets, and runtime behavior on top. A team can ship
|
||||
one heavy template and several thin kits without rebuilding the image
|
||||
@@ -58,7 +58,7 @@ each time something changes.
|
||||
| Pre-install tools and packages into a reusable base image | [Template](templates.md) |
|
||||
| Capture a configured running sandbox for reuse | [Saved template](templates.md#saving-a-sandbox-as-a-template) |
|
||||
| Add a tool, credential, or config to agent runs via YAML | [Kit (mixin)](kits.md) |
|
||||
| Define a new agent from scratch | [Kit (agent)](kits.md#defining-an-agent) |
|
||||
| Define a new agent from scratch | [Kit (sandbox)](kits.md#define-an-agent) |
|
||||
|
||||
Templates and kits can be used together. A template bakes heavy tools into
|
||||
the image for fast sandbox startup; a kit layered on top adds per-run
|
||||
@@ -67,4 +67,4 @@ credentials, config, or extra capabilities.
|
||||
## Tutorials
|
||||
|
||||
- [Build your own agent kit](build-an-agent.md) — step-by-step walkthrough
|
||||
for packaging [Amp](https://ampcode.com/) as an agent kit.
|
||||
for packaging [Amp](https://ampcode.com/) as a sandbox kit.
|
||||
|
||||
@@ -18,8 +18,8 @@ This tutorial walks through building an agent kit for the
|
||||
[Amp](https://ampcode.com/) coding agent. Each step explains the decision
|
||||
behind a part of the spec, so you can apply the same reasoning to other agents.
|
||||
|
||||
For reference on every field, see the [Kits](kits.md) page. This tutorial
|
||||
focuses on the journey.
|
||||
For reference on every field, see the [Kit spec reference](kit-reference.md).
|
||||
This tutorial focuses on the journey.
|
||||
|
||||
The finished kit is also published as a runnable sample at
|
||||
[docker/sbx-kits-contrib](https://github.com/docker/sbx-kits-contrib/tree/main/amp) —
|
||||
@@ -28,7 +28,7 @@ useful as a reference while you follow along.
|
||||
## Choose a base image
|
||||
|
||||
An agent kit needs a container image that satisfies the
|
||||
[base image requirements](kits.md#base-image-requirements): non-root
|
||||
[base image requirements](kit-reference.md#base-image-requirements): non-root
|
||||
`agent` user at UID 1000, passwordless sudo, `/home/agent/` home, and HTTP
|
||||
proxy environment variable forwarding.
|
||||
|
||||
@@ -65,32 +65,28 @@ substitutes the real key on outbound requests to the API host, so the
|
||||
secret never enters the sandbox. A later section walks through the
|
||||
specific command for storing the key.
|
||||
|
||||
## Write the agent block
|
||||
## Write the sandbox block
|
||||
|
||||
The `agent:` block tells the sandbox how to launch Amp when the user
|
||||
The `sandbox:` block tells the sandbox how to launch Amp when the user
|
||||
attaches.
|
||||
|
||||
```yaml {title="amp/spec.yaml"}
|
||||
schemaVersion: "1"
|
||||
kind: agent
|
||||
kind: sandbox
|
||||
name: amp
|
||||
displayName: Amp
|
||||
description: The frontier coding agent.
|
||||
|
||||
agent:
|
||||
sandbox:
|
||||
image: "docker/sandbox-templates:shell-docker"
|
||||
aiFilename: AGENTS.md
|
||||
persistence: persistent
|
||||
entrypoint:
|
||||
run: [amp, --dangerously-allow-all]
|
||||
```
|
||||
|
||||
- `aiFilename: AGENTS.md` tells the sandbox to create `AGENTS.md` at launch
|
||||
and append the [`memory`](#prime-amp-with-memory) block to it. Amp reads
|
||||
and append the [`agentContext`](#prime-amp-with-memory) block to it. Amp reads
|
||||
this file for instructions.
|
||||
- `persistence: persistent` keeps Amp's state (auth tokens, history) in a
|
||||
named volume across sandbox restarts. Without it, you re-authenticate
|
||||
every time.
|
||||
- `entrypoint.run` runs `amp` in "YOLO-mode" when the sandbox starts. Adjust if
|
||||
you want to pass different args on startup.
|
||||
|
||||
@@ -157,12 +153,12 @@ pick any name.
|
||||
|
||||
## Prime Amp with memory
|
||||
|
||||
The `memory` field appends markdown to `AGENTS.md` at sandbox creation.
|
||||
The `agentContext` field appends markdown to `AGENTS.md` at sandbox creation.
|
||||
Use it to tell Amp about the sandbox environment so it knows the
|
||||
conventions when it starts.
|
||||
|
||||
```yaml
|
||||
memory: |
|
||||
agentContext: |
|
||||
## Sandbox environment
|
||||
|
||||
You are running inside a Docker sandbox. The workspace is mounted at
|
||||
@@ -180,15 +176,14 @@ Putting it all together:
|
||||
|
||||
```yaml {title="amp/spec.yaml"}
|
||||
schemaVersion: "1"
|
||||
kind: agent
|
||||
kind: sandbox
|
||||
name: amp
|
||||
displayName: Amp
|
||||
description: The frontier coding agent.
|
||||
|
||||
agent:
|
||||
sandbox:
|
||||
image: "docker/sandbox-templates:shell-docker"
|
||||
aiFilename: AGENTS.md
|
||||
persistence: persistent
|
||||
entrypoint:
|
||||
run: [amp, --dangerously-allow-all]
|
||||
|
||||
@@ -209,7 +204,7 @@ commands:
|
||||
user: "1000"
|
||||
description: Install Amp
|
||||
|
||||
memory: |
|
||||
agentContext: |
|
||||
## Sandbox environment
|
||||
|
||||
You are running inside a Docker sandbox. The workspace is mounted at
|
||||
@@ -287,7 +282,7 @@ Two loops help:
|
||||
- Edit the spec and re-run `sbx run --kit ./amp/ amp` to pick up changes.
|
||||
Remove the sandbox first (`sbx rm <name>`) for a clean start.
|
||||
|
||||
Flesh out the `memory` block as you refine how Amp should behave in the
|
||||
Flesh out the `agentContext` block as you refine how Amp should behave in the
|
||||
sandbox.
|
||||
|
||||
## Publish
|
||||
@@ -316,7 +311,7 @@ the same decisions for your agent:
|
||||
placeholder. If it accepts the env var as-is, declare
|
||||
`environment.proxyManaged` in the kit and skip the user-side step.
|
||||
|
||||
The rest — memory block, network-policy iteration, packaging — is the
|
||||
The rest — agent-context block, network-policy iteration, packaging — is the
|
||||
same regardless of agent.
|
||||
|
||||
## Remove the stored secret
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
title: Kit examples
|
||||
linkTitle: Examples
|
||||
description: Copy-and-adapt spec.yaml snippets for common mixin and agent kit patterns — static files, install commands, background services, initFiles, Claude Code skills, and agent forks.
|
||||
description: Copy-and-adapt spec.yaml snippets for common mixin and sandbox kit patterns — static files, install commands, background services, initFiles, Claude Code skills, and agent forks.
|
||||
keywords: sandboxes, sbx, kits, mixins, examples, patterns, skills
|
||||
weight: 25
|
||||
---
|
||||
@@ -17,7 +17,7 @@ weight: 25
|
||||
Each section below shows one `spec.yaml` snippet that demonstrates a
|
||||
single kit pattern. These aren't complete, distributable kits — they're
|
||||
small, focused examples you can lift into your own kit. For the full
|
||||
spec reference, see [Kits](kits.md).
|
||||
spec reference, see [Kit spec reference](kit-reference.md).
|
||||
|
||||
## Drop a shared config file
|
||||
|
||||
@@ -253,7 +253,7 @@ idempotent. The heredoc pattern overwrites cleanly each time.
|
||||
|
||||
## Fork an existing agent
|
||||
|
||||
Agent kits (`kind: agent`) define a full agent from scratch. The most
|
||||
Sandbox kits (`kind: sandbox`) define a full agent from scratch. The most
|
||||
common variant is a fork of a built-in agent — same image and
|
||||
credentials, but a different entrypoint. This example reproduces the
|
||||
built-in `claude` agent but drops `--dangerously-skip-permissions` so
|
||||
@@ -261,15 +261,14 @@ every tool call prompts for approval:
|
||||
|
||||
```yaml {title="claude-safe/spec.yaml"}
|
||||
schemaVersion: "1"
|
||||
kind: agent
|
||||
kind: sandbox
|
||||
name: claude-safe
|
||||
displayName: Claude Code (with approval prompts)
|
||||
description: Claude Code without --dangerously-skip-permissions
|
||||
|
||||
agent:
|
||||
sandbox:
|
||||
image: "docker/sandbox-templates:claude-code-docker"
|
||||
aiFilename: CLAUDE.md
|
||||
persistence: persistent
|
||||
entrypoint:
|
||||
run: [claude]
|
||||
|
||||
@@ -297,7 +296,7 @@ Launch with the kit's `name:` as the agent argument to `sbx run`:
|
||||
$ sbx run claude-safe --kit ./claude-safe
|
||||
```
|
||||
|
||||
For a step-by-step walkthrough of building a new agent kit from
|
||||
For a step-by-step walkthrough of building a new sandbox kit from
|
||||
scratch, see [Build an agent](build-an-agent.md).
|
||||
|
||||
## More examples
|
||||
|
||||
@@ -0,0 +1,348 @@
|
||||
---
|
||||
title: Kit spec reference
|
||||
linkTitle: Spec reference
|
||||
description: Field-by-field reference for a kit's spec.yaml — credentials, network rules, environment, commands, files, agent context, and the sandbox block.
|
||||
keywords: sandboxes, sbx, kits, spec.yaml, reference, schema, fields
|
||||
weight: 22
|
||||
---
|
||||
|
||||
{{< summary-bar feature_name="Docker Sandboxes sbx" >}}
|
||||
|
||||
> [!NOTE]
|
||||
> Kits are experimental. The kit file format, CLI commands, and experience
|
||||
> for creating, loading, and managing kits are subject to change as the
|
||||
> feature evolves. Share feedback and bug reports in the
|
||||
> [docker/sbx-releases](https://github.com/docker/sbx-releases) repository.
|
||||
|
||||
This page documents every field in a kit's `spec.yaml`. For an overview of
|
||||
what kits are and how to use them, see [Kits](kits.md).
|
||||
|
||||
A kit directory has a required `spec.yaml` and an optional `files/` tree:
|
||||
|
||||
```text
|
||||
my-kit/
|
||||
├── spec.yaml # required
|
||||
└── files/ # optional — static files to inject
|
||||
├── home/
|
||||
└── workspace/
|
||||
```
|
||||
|
||||
## Changelog
|
||||
|
||||
Renamed fields are still accepted for backward compatibility, but
|
||||
`sbx kit validate` reports a deprecation warning for each, and a future
|
||||
release may stop accepting them. Update kits to the current names.
|
||||
|
||||
### v0.32.0
|
||||
|
||||
The following `spec.yaml` fields were renamed:
|
||||
|
||||
| Previous | Current |
|
||||
| -------------- | ---------------- |
|
||||
| `memory` | `agentContext` |
|
||||
| `kind: agent` | `kind: sandbox` |
|
||||
| `agent:` block | `sandbox:` block |
|
||||
|
||||
The per-kit directory was also renamed from `kits-memory/` to
|
||||
`kits-agent-context/`. An existing `kits-memory/` directory is migrated
|
||||
automatically the next time the sandbox starts.
|
||||
|
||||
## Top-level fields
|
||||
|
||||
```yaml
|
||||
schemaVersion: "1"
|
||||
kind: <mixin | sandbox>
|
||||
name: <name>
|
||||
displayName: <name>
|
||||
description: <text>
|
||||
```
|
||||
|
||||
| Field | Required | Description |
|
||||
| --------------- | -------- | -------------------------------------------------------------------------- |
|
||||
| `schemaVersion` | Yes | Spec schema version. Set to `"1"`. |
|
||||
| `kind` | Yes | `mixin` for kits that extend an agent; `sandbox` for kits that define one. |
|
||||
| `name` | Yes | Unique identifier. Lowercase, alphanumeric, hyphens. |
|
||||
| `displayName` | No | Human-readable name. |
|
||||
| `description` | No | Short description. |
|
||||
|
||||
The sections below apply to both kinds. Sandbox kits also declare a
|
||||
[`sandbox:` block](#sandbox-block).
|
||||
|
||||
## Credentials
|
||||
|
||||
```yaml
|
||||
credentials:
|
||||
sources:
|
||||
<service-id>:
|
||||
env: [<env-var>, ...]
|
||||
file:
|
||||
path: <path>
|
||||
parser: <parser>
|
||||
priority: <env-first | file-first>
|
||||
```
|
||||
|
||||
| Field | Description |
|
||||
| -------------------------- | ------------------------------------------------------------- |
|
||||
| `sources` | Map of service identifier to credential source. |
|
||||
| `sources.<id>.env` | Environment variables to read on the host, in priority order. |
|
||||
| `sources.<id>.file.path` | Path on host. `~` expands to home directory. |
|
||||
| `sources.<id>.file.parser` | How to extract the credential value from the file. |
|
||||
| `sources.<id>.priority` | `env-first` (default) or `file-first`. |
|
||||
|
||||
Service identifiers link credentials to [network rules](#network).
|
||||
|
||||
### file.parser
|
||||
|
||||
`file.parser` tells the proxy how to extract a credential from the file at `file.path`.
|
||||
Omit it for plain-text files; set it to `json:<dot.path>` to extract a field from a JSON file.
|
||||
|
||||
| Value | Behavior |
|
||||
| ----------------- | ------------------------------------------------------------------------------------ |
|
||||
| omitted or empty | Reads the entire file as the credential. Leading and trailing whitespace is trimmed. |
|
||||
| `json:<dot.path>` | Parses the file as JSON and returns the value at the dot-separated path. |
|
||||
| any other value | Rejected — `unsupported parser: <value>`. |
|
||||
|
||||
For `json:` paths, segments are separated by `.` (for example, `json:credentials.github.token`).
|
||||
Only object keys can be navigated — arrays are not supported and there is no `[0]`-style indexing.
|
||||
Keys that contain a literal `.` cannot be referenced. The resolved value must be a string, number,
|
||||
or boolean; numbers and booleans are converted to strings. Objects, arrays, and null are rejected.
|
||||
|
||||
When a source has both `env` and `file` defined, `priority` controls which is tried first. The
|
||||
preferred source is used when it exists — the environment variable is set, or the file is
|
||||
present on disk. If it doesn't, the other source is used instead. The choice is made once at
|
||||
discovery time, so parser errors (missing JSON field, wrong value type, invalid JSON) surface
|
||||
as errors rather than triggering a fallback.
|
||||
|
||||
Plain-text token file:
|
||||
|
||||
```yaml
|
||||
credentials:
|
||||
sources:
|
||||
openai:
|
||||
file:
|
||||
path: "~/.openai/token"
|
||||
```
|
||||
|
||||
Nested JSON field, with an environment variable as fallback:
|
||||
|
||||
```yaml
|
||||
credentials:
|
||||
sources:
|
||||
github:
|
||||
env:
|
||||
- GH_TOKEN
|
||||
file:
|
||||
path: "~/.config/myapp/creds.json"
|
||||
parser: "json:credentials.github.token"
|
||||
priority: file-first
|
||||
```
|
||||
|
||||
Given `~/.config/myapp/creds.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"credentials": {
|
||||
"github": { "token": "ghp_xyz", "expires": "2026-12-31" }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The proxy resolves the credential to `ghp_xyz`, falling back to `GH_TOKEN` if the file is
|
||||
missing. If the file exists but the JSON path doesn't resolve, the request fails with the
|
||||
parser error below instead of falling back.
|
||||
|
||||
Common errors when using `json:` parsers:
|
||||
|
||||
| Error message | Cause |
|
||||
| --------------------------------------------- | ------------------------------------------------------------------- |
|
||||
| `field 'X' not found in JSON` | The path doesn't exist in the file. |
|
||||
| `cannot navigate to field 'X': not an object` | A path segment hit a string, array, or scalar instead of an object. |
|
||||
| `field 'X' is not a string value` | The resolved value is an object, array, or null. |
|
||||
| `failed to parse JSON: ...` | The file is not valid JSON. |
|
||||
|
||||
## Network
|
||||
|
||||
```yaml
|
||||
network:
|
||||
allowedDomains: [<domain>, ...]
|
||||
deniedDomains: [<domain>, ...]
|
||||
serviceDomains:
|
||||
<domain>: <service-id>
|
||||
serviceAuth:
|
||||
<service-id>:
|
||||
headerName: <header>
|
||||
valueFormat: <format>
|
||||
```
|
||||
|
||||
| Field | Description |
|
||||
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `allowedDomains` | Domains the sandbox can reach. Wildcards supported. |
|
||||
| `deniedDomains` | Domains the sandbox is blocked from reaching. Deny rules take precedence over allow rules, including those from other composed kits. |
|
||||
| `serviceDomains` | Map of domain to service identifier from `credentials.sources`. |
|
||||
| `serviceAuth.headerName` | HTTP header the proxy sets (for example, `Authorization`). |
|
||||
| `serviceAuth.valueFormat` | Format string for the header value (for example, `"Bearer %s"`). |
|
||||
|
||||
## Environment
|
||||
|
||||
```yaml
|
||||
environment:
|
||||
variables:
|
||||
<NAME>: <value>
|
||||
proxyManaged: [<NAME>, ...]
|
||||
```
|
||||
|
||||
| Field | Description |
|
||||
| -------------- | ------------------------------------------------------------------------------------------------------------------- |
|
||||
| `variables` | Key-value pairs set directly in the container. |
|
||||
| `proxyManaged` | Environment variable names populated by the proxy at request time. Pair with [`credentials.sources`](#credentials). |
|
||||
|
||||
Variable names must be valid shell identifiers (`[A-Za-z_][A-Za-z0-9_]*`).
|
||||
|
||||
## Commands
|
||||
|
||||
```yaml
|
||||
commands:
|
||||
install:
|
||||
- command: <shell-string>
|
||||
user: <uid>
|
||||
description: <text>
|
||||
startup:
|
||||
- command: [<argv>, ...]
|
||||
user: <uid>
|
||||
background: <true | false>
|
||||
description: <text>
|
||||
initFiles:
|
||||
- path: <path>
|
||||
content: <text>
|
||||
mode: <octal>
|
||||
onlyIfMissing: <true | false>
|
||||
description: <text>
|
||||
```
|
||||
|
||||
### install
|
||||
|
||||
Runs once during sandbox creation. Shell strings passed to `sh -c`.
|
||||
|
||||
| Field | Default | Description |
|
||||
| ------------- | ------- | ----------------------------- |
|
||||
| `command` | — | Shell command string. |
|
||||
| `user` | `"0"` | User to run as. `"0"` = root. |
|
||||
| `description` | — | Human-readable description. |
|
||||
|
||||
### startup
|
||||
|
||||
Runs at every sandbox start. String array, not interpreted by a shell.
|
||||
|
||||
| Field | Default | Description |
|
||||
| ------------- | -------- | ----------------------------------- |
|
||||
| `command` | — | Command and args as a string array. |
|
||||
| `user` | `"1000"` | User to run as. `"1000"` = agent. |
|
||||
| `background` | `false` | Run in background. |
|
||||
| `description` | — | Human-readable description. |
|
||||
|
||||
Startup commands are non-interactive. They run before the agent
|
||||
attaches, with no terminal connected, so they can't prompt the user
|
||||
(for example, an interactive `aws login` will hang or fail). They also
|
||||
don't gate the agent's entrypoint: the agent launches once startup
|
||||
commands have been dispatched, regardless of `background`. Use them
|
||||
for non-interactive prep — launching daemons, warming caches,
|
||||
refreshing config — and use `commands.initFiles` for any value that
|
||||
needs to land on disk before the agent runs.
|
||||
|
||||
Startup commands must be idempotent. They run on every sandbox start
|
||||
and replay on container restarts, so a command that fails or
|
||||
misbehaves on a second invocation breaks the restart path. Guard
|
||||
work with existence checks, use upserts instead of inserts, and
|
||||
prefer commands that converge to the same end state regardless of
|
||||
how many times they run.
|
||||
|
||||
### initFiles
|
||||
|
||||
Files written at sandbox start, with runtime substitution.
|
||||
|
||||
| Field | Default | Description |
|
||||
| --------------- | -------- | --------------------------------------------------------- |
|
||||
| `path` | — | Absolute container path. |
|
||||
| `content` | — | File content. `${WORKDIR}` expands to the workspace path. |
|
||||
| `mode` | `"0644"` | File permissions in octal. |
|
||||
| `onlyIfMissing` | `false` | Skip if the file already exists. |
|
||||
|
||||
## Static files
|
||||
|
||||
```text
|
||||
my-kit/files/
|
||||
├── home/ → /home/agent/
|
||||
└── workspace/ → primary workspace path
|
||||
```
|
||||
|
||||
| Kit path | Container destination |
|
||||
| ------------------ | --------------------------------------- |
|
||||
| `files/home/` | `/home/agent/` (config files, dotfiles) |
|
||||
| `files/workspace/` | The primary workspace path |
|
||||
|
||||
Parent directories are created automatically. Existing files are
|
||||
overwritten. Absolute paths and path-traversal sequences (`../../`) are
|
||||
rejected.
|
||||
|
||||
## Agent context
|
||||
|
||||
```yaml
|
||||
agentContext: |
|
||||
<markdown>
|
||||
```
|
||||
|
||||
Top-level field. Available in both mixin and sandbox kits. Markdown
|
||||
appended to the agent's memory file at sandbox creation. The agent reads
|
||||
this content at startup. Write it as instructions or notes the agent
|
||||
should follow when working in the sandbox. Applied only when the active
|
||||
sandbox kit sets [`sandbox.aiFilename`](#sandbox-block).
|
||||
|
||||
The file is written to the parent of the workspace path inside the
|
||||
sandbox, not to the workspace itself. For a workspace mounted at
|
||||
`/Users/you/myproject`, the memory file lands at
|
||||
`/Users/you/AGENTS.md` (or whatever `aiFilename` is set to). It exists
|
||||
only inside the sandbox. Nothing is written to the host.
|
||||
|
||||
When several loaded kits declare `agentContext:` blocks, the content is split
|
||||
across files instead of being concatenated into the main one:
|
||||
|
||||
- Each kit's agent context is written to `<kit-name>.md` in a sibling
|
||||
`kits-agent-context/` directory next to the main memory file.
|
||||
- The main memory file gets a `## Kits` section listing every kit with
|
||||
a pointer to its file. The section is delimited by
|
||||
`<!-- sbx:kits-section start -->` and `<!-- sbx:kits-section end -->`
|
||||
markers so it can be regenerated when kits are added or removed.
|
||||
|
||||
## Sandbox block
|
||||
|
||||
Required for `kind: sandbox`.
|
||||
|
||||
```yaml
|
||||
sandbox:
|
||||
image: <image-ref>
|
||||
aiFilename: <filename>
|
||||
entrypoint:
|
||||
run: [<argv>, ...]
|
||||
args: [<arg>, ...]
|
||||
```
|
||||
|
||||
| Field | Required | Description |
|
||||
| ------------------------- | -------- | ----------------------------------------------------------------------------------------------------------- |
|
||||
| `sandbox.image` | Yes | Docker image reference. See [Base image requirements](#base-image-requirements). |
|
||||
| `sandbox.aiFilename` | No | Memory filename (for example, `AGENTS.md`). Appends top-level [`agentContext`](#agent-context) at creation. |
|
||||
| `sandbox.entrypoint.run` | No | Command and args as a string array. Replaces the image's entrypoint. |
|
||||
| `sandbox.entrypoint.args` | No | Args appended to the image's existing entrypoint. |
|
||||
|
||||
### Base image requirements
|
||||
|
||||
The agent's container image must provide:
|
||||
|
||||
- A non-root `agent` user at UID 1000 with passwordless sudo.
|
||||
- A `/home/agent/` home directory owned by `agent`.
|
||||
- HTTP proxy environment variables (`HTTP_PROXY`, `HTTPS_PROXY`,
|
||||
`NO_PROXY`) preserved across sudo.
|
||||
- The agent binary (baked in, or installed via
|
||||
[`commands.install`](#commands)).
|
||||
|
||||
Build on top of `docker/sandbox-templates:shell-docker` to get these for
|
||||
free.
|
||||
@@ -29,11 +29,11 @@ and enforces them at runtime. Credentials stay on the host and go through
|
||||
a proxy instead of entering the VM, and outbound traffic is restricted to
|
||||
the domains permitted by the kit's network rules.
|
||||
|
||||
A kit is either a mixin or an agent:
|
||||
A kit is either a mixin or a sandbox:
|
||||
|
||||
- Mixin kits (`kind: mixin`) extend an existing agent with extra
|
||||
capabilities. Stack several on the same sandbox.
|
||||
- Agent kits (`kind: agent`) define a full agent from scratch: its image,
|
||||
- Sandbox kits (`kind: sandbox`) define a full agent from scratch: its image,
|
||||
entrypoint, network policies, and everything else the agent needs.
|
||||
|
||||
## What kits can do
|
||||
@@ -55,7 +55,7 @@ commands:
|
||||
|
||||
Startup commands cover things like launching background services,
|
||||
warming caches, or refreshing config on each start. They must be
|
||||
idempotent — see the [`startup`](#startup) spec reference:
|
||||
idempotent — see the [`startup`](kit-reference.md#startup) spec reference:
|
||||
|
||||
```yaml
|
||||
commands:
|
||||
@@ -96,7 +96,7 @@ commands:
|
||||
onlyIfMissing: true
|
||||
```
|
||||
|
||||
See [`initFiles`](#initfiles) in the spec reference for all fields.
|
||||
See [`initFiles`](kit-reference.md#initfiles) in the spec reference for all fields.
|
||||
|
||||
Sandboxes seed settings files for some built-in agents during setup.
|
||||
For example, the sandbox writes `/home/agent/.claude/settings.json`
|
||||
@@ -105,7 +105,7 @@ for the `claude` agent. This happens after the kit's static files and
|
||||
Workspace files (such as `<workspace>/.claude/settings.local.json`)
|
||||
aren't affected, and you can ship them under `files/workspace/` as
|
||||
usual. To override a path the sandbox writes to, use a
|
||||
[`commands.startup`](#startup) script instead. See
|
||||
[`commands.startup`](kit-reference.md#startup) script instead. See
|
||||
[Override agent settings](kit-examples.md#override-agent-settings) for
|
||||
an example.
|
||||
|
||||
@@ -203,45 +203,45 @@ the agent project conventions, usage tips for a tool the kit installs,
|
||||
or other guidance that should be in scope when the sandbox runs.
|
||||
|
||||
```yaml
|
||||
memory: |
|
||||
agentContext: |
|
||||
Ruff is installed. Run `ruff check` before committing.
|
||||
Shared config lives at `/workspace/ruff.toml`.
|
||||
```
|
||||
|
||||
Both mixin and agent kits can declare `memory:`. The content is written
|
||||
only when the active agent kit sets [`agent.aiFilename`](#agent-block),
|
||||
Both mixin and sandbox kits can declare `agentContext:`. The content is written
|
||||
only when the active sandbox kit sets [`sandbox.aiFilename`](kit-reference.md#sandbox-block),
|
||||
which determines the memory file's name.
|
||||
|
||||
When more than one loaded kit declares a `memory:` block, each kit's
|
||||
When more than one loaded kit declares an `agentContext:` block, each kit's
|
||||
content is written to its own `<kit-name>.md` file under a sibling
|
||||
`kits-memory/` directory. The main memory file gets a `## Kits` section
|
||||
that points to each kit file:
|
||||
`kits-agent-context/` directory. The main memory file gets a `## Kits`
|
||||
section that points to each kit file:
|
||||
|
||||
```text
|
||||
/Users/you/
|
||||
├── myproject/ # workspace
|
||||
├── AGENTS.md # main memory file with a "## Kits" index
|
||||
└── kits-memory/
|
||||
├── myproject/ # workspace
|
||||
├── AGENTS.md # main memory file with a "## Kits" index
|
||||
└── kits-agent-context/
|
||||
├── ruff-lint.md
|
||||
├── vale.md
|
||||
└── git-ssh-sign.md
|
||||
```
|
||||
|
||||
See [`memory`](#memory) in the spec reference for the full field schema.
|
||||
See [`agentContext`](kit-reference.md#agent-context) in the spec reference for the full field schema.
|
||||
|
||||
### Define an agent
|
||||
|
||||
Agent kits declare an `agent:` block with the image the agent runs in and
|
||||
Sandbox kits declare a `sandbox:` block with the image the agent runs in and
|
||||
the command the user attaches to when they launch the sandbox:
|
||||
|
||||
```yaml
|
||||
agent:
|
||||
sandbox:
|
||||
image: "my-registry/my-agent:latest"
|
||||
entrypoint:
|
||||
run: [my-agent, "--yolo"]
|
||||
```
|
||||
|
||||
See [Agent kits](#agent-kits) for use cases and an example.
|
||||
See [Sandbox kits](#sandbox-kits) for use cases and an example.
|
||||
|
||||
## Mixin kits
|
||||
|
||||
@@ -307,9 +307,9 @@ To apply the mixin to a sandbox that's already running, use
|
||||
[`sbx kit add`](#local) instead. The `--kit` flag only takes effect when a
|
||||
sandbox is created.
|
||||
|
||||
## Agent kits
|
||||
## Sandbox kits
|
||||
|
||||
An agent kit defines a full agent from scratch — image, entrypoint, and
|
||||
A sandbox kit defines a full agent from scratch — image, entrypoint, and
|
||||
everything the agent needs. Common use cases:
|
||||
|
||||
- Package a custom agent you've built so others can run it
|
||||
@@ -317,25 +317,24 @@ everything the agent needs. Common use cases:
|
||||
- Run a fork of an existing agent with your own config
|
||||
- Prototype a new agent integration
|
||||
|
||||
Agent kits declare everything a mixin kit can, plus an
|
||||
[`agent:` block](#agent-block) that tells the sandbox how to launch the
|
||||
Sandbox kits declare everything a mixin kit can, plus an
|
||||
[`sandbox:` block](kit-reference.md#sandbox-block) that tells the sandbox how to launch the
|
||||
agent. For a step-by-step walkthrough, see
|
||||
[Build your own agent kit](build-an-agent.md).
|
||||
|
||||
### Example: the built-in `claude` agent
|
||||
|
||||
The `claude` agent you get from `sbx run claude` is defined as a kit. Here
|
||||
is an abbreviated version of its spec, showing how the agent block combines
|
||||
is an abbreviated version of its spec, showing how the sandbox block combines
|
||||
with network, credentials, environment, and commands:
|
||||
|
||||
```yaml {title="claude/spec.yaml"}
|
||||
schemaVersion: "1"
|
||||
kind: agent
|
||||
kind: sandbox
|
||||
name: claude
|
||||
agent:
|
||||
sandbox:
|
||||
image: "docker/sandbox-templates:claude-code-docker"
|
||||
aiFilename: CLAUDE.md
|
||||
persistence: persistent
|
||||
entrypoint:
|
||||
run: [claude, "--dangerously-skip-permissions"]
|
||||
|
||||
@@ -459,317 +458,9 @@ Docker credential store, so pushing to a private registry requires a prior
|
||||
|
||||
## Spec reference
|
||||
|
||||
A kit directory has a required `spec.yaml` and an optional `files/` tree:
|
||||
|
||||
```text
|
||||
my-kit/
|
||||
├── spec.yaml # required
|
||||
└── files/ # optional — static files to inject
|
||||
├── home/
|
||||
└── workspace/
|
||||
```
|
||||
|
||||
### Top-level fields
|
||||
|
||||
```yaml
|
||||
schemaVersion: "1"
|
||||
kind: <mixin | agent>
|
||||
name: <name>
|
||||
displayName: <name>
|
||||
description: <text>
|
||||
```
|
||||
|
||||
| Field | Required | Description |
|
||||
| --------------- | -------- | ------------------------------------------------------------------------ |
|
||||
| `schemaVersion` | Yes | Spec schema version. Set to `"1"`. |
|
||||
| `kind` | Yes | `mixin` for kits that extend an agent; `agent` for kits that define one. |
|
||||
| `name` | Yes | Unique identifier. Lowercase, alphanumeric, hyphens. |
|
||||
| `displayName` | No | Human-readable name. |
|
||||
| `description` | No | Short description. |
|
||||
|
||||
The sections below apply to both kinds. Agent kits also declare an
|
||||
[`agent:` block](#agent-block).
|
||||
|
||||
### Credentials
|
||||
|
||||
```yaml
|
||||
credentials:
|
||||
sources:
|
||||
<service-id>:
|
||||
env: [<env-var>, ...]
|
||||
file:
|
||||
path: <path>
|
||||
parser: <parser>
|
||||
priority: <env-first | file-first>
|
||||
```
|
||||
|
||||
| Field | Description |
|
||||
| -------------------------- | ------------------------------------------------------------- |
|
||||
| `sources` | Map of service identifier to credential source. |
|
||||
| `sources.<id>.env` | Environment variables to read on the host, in priority order. |
|
||||
| `sources.<id>.file.path` | Path on host. `~` expands to home directory. |
|
||||
| `sources.<id>.file.parser` | How to extract the credential value from the file. |
|
||||
| `sources.<id>.priority` | `env-first` (default) or `file-first`. |
|
||||
|
||||
Service identifiers link credentials to [network rules](#network).
|
||||
|
||||
#### file.parser
|
||||
|
||||
`file.parser` tells the proxy how to extract a credential from the file at `file.path`.
|
||||
Omit it for plain-text files; set it to `json:<dot.path>` to extract a field from a JSON file.
|
||||
|
||||
| Value | Behavior |
|
||||
| ----------------- | ------------------------------------------------------------------------------------ |
|
||||
| omitted or empty | Reads the entire file as the credential. Leading and trailing whitespace is trimmed. |
|
||||
| `json:<dot.path>` | Parses the file as JSON and returns the value at the dot-separated path. |
|
||||
| any other value | Rejected — `unsupported parser: <value>`. |
|
||||
|
||||
For `json:` paths, segments are separated by `.` (for example, `json:credentials.github.token`).
|
||||
Only object keys can be navigated — arrays are not supported and there is no `[0]`-style indexing.
|
||||
Keys that contain a literal `.` cannot be referenced. The resolved value must be a string, number,
|
||||
or boolean; numbers and booleans are converted to strings. Objects, arrays, and null are rejected.
|
||||
|
||||
When a source has both `env` and `file` defined, `priority` controls which is tried first. The
|
||||
preferred source is used when it exists — the environment variable is set, or the file is
|
||||
present on disk. If it doesn't, the other source is used instead. The choice is made once at
|
||||
discovery time, so parser errors (missing JSON field, wrong value type, invalid JSON) surface
|
||||
as errors rather than triggering a fallback.
|
||||
|
||||
Plain-text token file:
|
||||
|
||||
```yaml
|
||||
credentials:
|
||||
sources:
|
||||
openai:
|
||||
file:
|
||||
path: "~/.openai/token"
|
||||
```
|
||||
|
||||
Nested JSON field, with an environment variable as fallback:
|
||||
|
||||
```yaml
|
||||
credentials:
|
||||
sources:
|
||||
github:
|
||||
env:
|
||||
- GH_TOKEN
|
||||
file:
|
||||
path: "~/.config/myapp/creds.json"
|
||||
parser: "json:credentials.github.token"
|
||||
priority: file-first
|
||||
```
|
||||
|
||||
Given `~/.config/myapp/creds.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"credentials": {
|
||||
"github": { "token": "ghp_xyz", "expires": "2026-12-31" }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The proxy resolves the credential to `ghp_xyz`, falling back to `GH_TOKEN` if the file is
|
||||
missing. If the file exists but the JSON path doesn't resolve, the request fails with the
|
||||
parser error below instead of falling back.
|
||||
|
||||
Common errors when using `json:` parsers:
|
||||
|
||||
| Error message | Cause |
|
||||
| --------------------------------------------- | ------------------------------------------------------------------- |
|
||||
| `field 'X' not found in JSON` | The path doesn't exist in the file. |
|
||||
| `cannot navigate to field 'X': not an object` | A path segment hit a string, array, or scalar instead of an object. |
|
||||
| `field 'X' is not a string value` | The resolved value is an object, array, or null. |
|
||||
| `failed to parse JSON: ...` | The file is not valid JSON. |
|
||||
|
||||
### Network
|
||||
|
||||
```yaml
|
||||
network:
|
||||
allowedDomains: [<domain>, ...]
|
||||
deniedDomains: [<domain>, ...]
|
||||
serviceDomains:
|
||||
<domain>: <service-id>
|
||||
serviceAuth:
|
||||
<service-id>:
|
||||
headerName: <header>
|
||||
valueFormat: <format>
|
||||
```
|
||||
|
||||
| Field | Description |
|
||||
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `allowedDomains` | Domains the sandbox can reach. Wildcards supported. |
|
||||
| `deniedDomains` | Domains the sandbox is blocked from reaching. Deny rules take precedence over allow rules, including those from other composed kits. |
|
||||
| `serviceDomains` | Map of domain to service identifier from `credentials.sources`. |
|
||||
| `serviceAuth.headerName` | HTTP header the proxy sets (for example, `Authorization`). |
|
||||
| `serviceAuth.valueFormat` | Format string for the header value (for example, `"Bearer %s"`). |
|
||||
|
||||
### Environment
|
||||
|
||||
```yaml
|
||||
environment:
|
||||
variables:
|
||||
<NAME>: <value>
|
||||
proxyManaged: [<NAME>, ...]
|
||||
```
|
||||
|
||||
| Field | Description |
|
||||
| -------------- | ------------------------------------------------------------------------------------------------------------------- |
|
||||
| `variables` | Key-value pairs set directly in the container. |
|
||||
| `proxyManaged` | Environment variable names populated by the proxy at request time. Pair with [`credentials.sources`](#credentials). |
|
||||
|
||||
Variable names must be valid shell identifiers (`[A-Za-z_][A-Za-z0-9_]*`).
|
||||
|
||||
### Commands
|
||||
|
||||
```yaml
|
||||
commands:
|
||||
install:
|
||||
- command: <shell-string>
|
||||
user: <uid>
|
||||
description: <text>
|
||||
startup:
|
||||
- command: [<argv>, ...]
|
||||
user: <uid>
|
||||
background: <true | false>
|
||||
description: <text>
|
||||
initFiles:
|
||||
- path: <path>
|
||||
content: <text>
|
||||
mode: <octal>
|
||||
onlyIfMissing: <true | false>
|
||||
description: <text>
|
||||
```
|
||||
|
||||
#### `install`
|
||||
|
||||
Runs once during sandbox creation. Shell strings passed to `sh -c`.
|
||||
|
||||
| Field | Default | Description |
|
||||
| ------------- | ------- | ----------------------------- |
|
||||
| `command` | — | Shell command string. |
|
||||
| `user` | `"0"` | User to run as. `"0"` = root. |
|
||||
| `description` | — | Human-readable description. |
|
||||
|
||||
#### `startup`
|
||||
|
||||
Runs at every sandbox start. String array, not interpreted by a shell.
|
||||
|
||||
| Field | Default | Description |
|
||||
| ------------- | -------- | ----------------------------------- |
|
||||
| `command` | — | Command and args as a string array. |
|
||||
| `user` | `"1000"` | User to run as. `"1000"` = agent. |
|
||||
| `background` | `false` | Run in background. |
|
||||
| `description` | — | Human-readable description. |
|
||||
|
||||
Startup commands are non-interactive. They run before the agent
|
||||
attaches, with no terminal connected, so they can't prompt the user
|
||||
(for example, an interactive `aws login` will hang or fail). They also
|
||||
don't gate the agent's entrypoint: the agent launches once startup
|
||||
commands have been dispatched, regardless of `background`. Use them
|
||||
for non-interactive prep — launching daemons, warming caches,
|
||||
refreshing config — and use `commands.initFiles` for any value that
|
||||
needs to land on disk before the agent runs.
|
||||
|
||||
Startup commands must be idempotent. They run on every sandbox start
|
||||
and replay on container restarts, so a command that fails or
|
||||
misbehaves on a second invocation breaks the restart path. Guard
|
||||
work with existence checks, use upserts instead of inserts, and
|
||||
prefer commands that converge to the same end state regardless of
|
||||
how many times they run.
|
||||
|
||||
#### `initFiles`
|
||||
|
||||
Files written at sandbox start, with runtime substitution.
|
||||
|
||||
| Field | Default | Description |
|
||||
| --------------- | -------- | --------------------------------------------------------- |
|
||||
| `path` | — | Absolute container path. |
|
||||
| `content` | — | File content. `${WORKDIR}` expands to the workspace path. |
|
||||
| `mode` | `"0644"` | File permissions in octal. |
|
||||
| `onlyIfMissing` | `false` | Skip if the file already exists. |
|
||||
|
||||
### Static files
|
||||
|
||||
```text
|
||||
my-kit/files/
|
||||
├── home/ → /home/agent/
|
||||
└── workspace/ → primary workspace path
|
||||
```
|
||||
|
||||
| Kit path | Container destination |
|
||||
| ------------------ | --------------------------------------- |
|
||||
| `files/home/` | `/home/agent/` (config files, dotfiles) |
|
||||
| `files/workspace/` | The primary workspace path |
|
||||
|
||||
Parent directories are created automatically. Existing files are
|
||||
overwritten. Absolute paths and path-traversal sequences (`../../`) are
|
||||
rejected.
|
||||
|
||||
### Memory
|
||||
|
||||
```yaml
|
||||
memory: |
|
||||
<markdown>
|
||||
```
|
||||
|
||||
Top-level field. Available in both mixin and agent kits. Markdown
|
||||
appended to the agent's memory file at sandbox creation. The agent reads
|
||||
this content at startup. Write it as instructions or notes the agent
|
||||
should follow when working in the sandbox. Applied only when the active
|
||||
agent kit sets [`agent.aiFilename`](#agent-block).
|
||||
|
||||
The file is written to the parent of the workspace path inside the
|
||||
sandbox, not to the workspace itself. For a workspace mounted at
|
||||
`/Users/you/myproject`, the memory file lands at
|
||||
`/Users/you/AGENTS.md` (or whatever `aiFilename` is set to). It exists
|
||||
only inside the sandbox. Nothing is written to the host.
|
||||
|
||||
When several loaded kits declare `memory:` blocks, the content is split
|
||||
across files instead of being concatenated into the main one:
|
||||
|
||||
- Each kit's memory is written to `<kit-name>.md` in a sibling
|
||||
`kits-memory/` directory next to the main memory file.
|
||||
- The main memory file gets a `## Kits` section listing every kit with
|
||||
a pointer to its file. The section is delimited by
|
||||
`<!-- sbx:kits-section start -->` and `<!-- sbx:kits-section end -->`
|
||||
markers so it can be regenerated when kits are added or removed.
|
||||
|
||||
### Agent block
|
||||
|
||||
Required for `kind: agent`.
|
||||
|
||||
```yaml
|
||||
agent:
|
||||
image: <image-ref>
|
||||
aiFilename: <filename>
|
||||
persistence: <persistent | ephemeral>
|
||||
entrypoint:
|
||||
run: [<argv>, ...]
|
||||
args: [<arg>, ...]
|
||||
```
|
||||
|
||||
| Field | Required | Description |
|
||||
| ----------------------- | -------- | ---------------------------------------------------------------------------------------------- |
|
||||
| `agent.image` | Yes | Docker image reference. See [Base image requirements](#base-image-requirements). |
|
||||
| `agent.aiFilename` | No | Memory filename (for example, `AGENTS.md`). Appends top-level [`memory`](#memory) at creation. |
|
||||
| `agent.persistence` | No | `persistent` (named volume across restarts) or `ephemeral` (default). |
|
||||
| `agent.entrypoint.run` | No | Command and args as a string array. Replaces the image's entrypoint. |
|
||||
| `agent.entrypoint.args` | No | Args appended to the image's existing entrypoint. |
|
||||
|
||||
#### Base image requirements
|
||||
|
||||
The agent's container image must provide:
|
||||
|
||||
- A non-root `agent` user at UID 1000 with passwordless sudo.
|
||||
- A `/home/agent/` home directory owned by `agent`.
|
||||
- HTTP proxy environment variables (`HTTP_PROXY`, `HTTPS_PROXY`,
|
||||
`NO_PROXY`) preserved across sudo.
|
||||
- The agent binary (baked in, or installed via
|
||||
[`commands.install`](#commands)).
|
||||
|
||||
Build on top of `docker/sandbox-templates:shell-docker` to get these for
|
||||
free.
|
||||
For a field-by-field reference of every `spec.yaml` block — top-level
|
||||
fields, credentials, network, environment, commands, static files,
|
||||
agent context, and the sandbox block — see [Kit spec reference](kit-reference.md).
|
||||
|
||||
## Debugging
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ ask the agent to install what's needed.
|
||||
> 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. To
|
||||
> define a new agent from scratch, see [Kits](kits.md#defining-an-agent).
|
||||
> define a new agent from scratch, see [Kits](kits.md#define-an-agent).
|
||||
|
||||
### Base images
|
||||
|
||||
|
||||
@@ -111,22 +111,22 @@ startup. In Claude Code, use the `/permissions` command to change the mode
|
||||
interactively.
|
||||
|
||||
To make approval prompts the default for every session, define a custom
|
||||
agent kit that overrides the agent's entrypoint to drop the
|
||||
sandbox kit that overrides the agent's entrypoint to drop the
|
||||
permission-skipping flag. For example, a kit that launches Claude Code
|
||||
without `--dangerously-skip-permissions`:
|
||||
|
||||
```yaml {title="claude-safe/spec.yaml"}
|
||||
schemaVersion: "1"
|
||||
kind: agent
|
||||
kind: sandbox
|
||||
name: claude-safe
|
||||
agent:
|
||||
sandbox:
|
||||
image: "docker/sandbox-templates:claude-code-docker"
|
||||
entrypoint:
|
||||
run: [claude]
|
||||
```
|
||||
|
||||
Run it with `sbx run claude-safe --kit ./claude-safe/`. See
|
||||
[Agent kits](customize/kits.md#agent-kits) for the full pattern.
|
||||
[Sandbox kits](customize/kits.md#sandbox-kits) for the full pattern.
|
||||
|
||||
## How do I know if my agent is running in a sandbox?
|
||||
|
||||
|
||||
Reference in New Issue
Block a user