Documentación offline Docker main
por @raupulus

Kit spec reference

main Documentación oficial Licencia Apache-2.0Descargado el 2026-09-15 Bundle .md (6.1 MB) ↓

En esta página

{{< 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 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.

For the normative v2 grammar used by the parser and tests, see the schemaVersion: "2" specification in the docker/sbx-kits-contrib repository.

A kit directory has a required spec.yaml and an optional files/ tree:

my-kit/
├── spec.yaml       # required
└── files/          # optional — static files to inject
    ├── home/
    └── workspace/

Schema versions#

Starting with Docker Sandboxes version 0.36, two schema versions are supported. Use schemaVersion: "2" for new kits. Version "1" remains accepted through the legacy path.

The loader forks on schemaVersion. A v2 spec uses the v2 grammar only. Legacy v1 fields in a schemaVersion: "2" spec are rejected during decode instead of being folded into the v2 model. Keep each spec.yaml on one grammar.

What changed in v2:

v1 v2
credentials.sources.<id> credentials: list entry with service
network.allowedDomains / deniedDomains permissions.network.allow / deny
network.serviceDomains / serviceAuth credentials[].apiKey.inject
network.publishedPorts / publishedPorts top-level ports
standalone oauth: block credentials[].oauth
oauth.skipIfEnv Accepted but ignored
environment.proxyManaged credentials[].apiKey.proxyManaged
memory / agentContext agentInstructions.content
kind: agent / agent: block kind: sandbox / sandbox: block
sandbox.aiFilename agentInstructions.filename
sandbox.entrypoint.run sandbox.entrypoint
sandbox.entrypoint.args sandbox.command.default
sandbox.entrypoint.ttyArgs sandbox.command.interactive
tmpfs: volumes: entries with type: tmpfs
volumes: (mapping form) volumes: sequence (- path: <path>)
commands: / commands.initFiles setup: / setup.files
settings: / kitDir / persistence Removed

Credential discovery also moved out of the kit in v2: a kit declares which credentials it needs and how to inject them, but where each value comes from is controlled by the user through credential bindings.

[!NOTE] mixins and sandbox.build are accepted by the parser, but runtime support is pending. A kit that sets sandbox.build must also set sandbox.image.

Top-level fields#

schemaVersion: "2"
kind: <mixin | sandbox>
name: <name>
version: <version>
displayName: <name>
description: <text>
sourceURL: <url>
licenses:
  - MIT
locked:
  - sandbox.image
security:
  privileged: false
args:
  channel:
    default: stable
    enum: [stable, beta]
Field Required Description
schemaVersion Yes Spec schema version. Use "2" for this grammar.
kind Yes mixin for kits that extend an agent; sandbox for kits that define one.
name Yes Unique identifier. Lowercase alphanumeric with hyphens, 1 to 64 characters.
version No Kit version.
displayName No Human-readable name.
description No Short description.
sourceURL No Source repository or documentation URL.
licenses No SPDX license identifiers.
locked No Dotted paths child kits may not override.
security No Container security settings. security.privileged: true runs the container in privileged mode.
args No Arguments supplied when the kit is loaded. Schema v2 only.

A kit also declares behavior blocks such as agentInstructions, permissions, ports, credentials, environment, setup, and volumes.

Arguments#

A schema v2 kit can declare arguments and reference them anywhere in spec.yaml or under files/ as ${{ kit.args.<name> }}. Substitution happens before the spec is decoded.

args:
  version:
    default: latest
    description: Tool version to install
    pattern: '^(latest|[0-9]+\.[0-9]+\.[0-9]+)$'
  channel:
    default: stable
    enum: [stable, beta, nightly]
  target:
    required: true
    description: Build target

environment:
  variables:
    TOOL_VERSION: "${{ kit.args.version }}"

Don't use kit arguments for API tokens, passwords, or other secrets. Use Credentials to provide sensitive values to a sandbox.

Field Description
Argument name Starts with a letter or underscore and contains only letters, digits, underscores, and hyphens.
default String to use when the caller supplies no value. Mutually exclusive with required: true.
required Set to true when the caller must supply a value. Mutually exclusive with default.
description Optional help text shown when a required value is missing.
enum Optional list of accepted values. Mutually exclusive with pattern.
pattern Optional Go RE2 regular expression matched against the complete value. Mutually exclusive with enum.

Each argument must declare either default, including an empty-string default, or required: true. A declared default must satisfy its own enum or pattern. Every ${{ kit.args.<name> }} reference must have a matching declaration.

Argument values are strings, but substitution happens before YAML decoding. Quote a placeholder in a string-valued field so a value such as 1.20 isn't decoded as a number.

Supply values with --kit-arg or --kit-args-file when loading the kit. See Pass arguments to kits for scoping, precedence, and validation behavior.

Kit kinds#

kind: mixin#

A mixin layers capabilities onto an existing sandbox. It must not declare a sandbox: block, extends:, or mixins:. A mixin can declare requires: to pin the base agent it is designed for:

schemaVersion: "2"
kind: mixin
name: github-tools
requires:
  agent: claude

requires.agent takes one base-agent name. It is validated as a kit name and enforced during composition.

kind: sandbox#

A sandbox kit defines a full agent. A root sandbox must declare a sandbox: block. A sandbox that uses extends: can inherit the parent image and omit its own sandbox: block:

schemaVersion: "2"
kind: sandbox
name: claude-safe
extends: claude

extends: is sandbox-only. The parent must resolve to a sandbox kit. mixins: is also sandbox-only and accepted by the parser, but runtime composition support is pending.

Sandbox block#

sandbox:
  image: <image-ref>
  build:
    context: .
    dockerfile: Dockerfile
    args:
      AGENT_VERSION: "1.0.0"
    target: runtime
    platforms:
      - linux/amd64
  entrypoint: [my-agent, "--flag"]
  command:
    default: ["--task-mode"]
    interactive: []
  resources:
    cpu: 2
    memory: 4g
    gpu: "1"
Field Required Description
sandbox.image When extends: is omitted Docker image reference.
sandbox.build No Build configuration. Runtime support is pending, so a kit with build: must also set image:.
sandbox.entrypoint No Fixed process prefix as a string array. The first element is the agent binary.
sandbox.command No Mode-specific argument tail. Use a list shorthand for default, or a mapping with default and interactive.
sandbox.resources No Optional CPU, memory, and GPU constraints. Memory uses byte-size strings such as 4096m or 4g.

The effective command is entrypoint plus command.default for non-interactive launches, and entrypoint plus command.interactive for TTY sessions. If interactive is omitted, it falls back to default.

For a kit that uses extends:, sandbox.command replaces the full inherited argument tail, including flags after the binary in the parent's sandbox.entrypoint. It doesn't append to that tail. Define every argument the child needs. For example, a child of claude that adds --settings must also include --dangerously-skip-permissions to preserve that behavior.

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, either baked in or installed with setup.install.

Build on top of docker/sandbox-templates:shell-docker to get these base requirements.

Agent instructions#

agentInstructions:
  filename: CLAUDE.md
  content: |
    Ruff is installed. Run `ruff check` before committing.
Field Description
filename AI profile filename. Meaningful for kind: sandbox; ignored with a warning for kind: mixin.
content Markdown instructions. For a sandbox, inlined into the profile. For a mixin, written to kit memory.

For mixins, the engine writes content to <dir-of-AI-file>/kits-memory/<kit-name>.md and adds a ## Kits pointer section to the base AI file. This keeps each mixin's instructions in a separate file.

Credentials#

A kit declares the credentials it needs and how the proxy injects them into outbound requests. It does not declare a host discovery source. The user provides the value through the secret store or the first-run prompt, and a credential binding authorizes its use. A kit can't read arbitrary host environment variables or files.

credentials:
  - service: <service-id>
    description: <text> # optional
    required: <true | false> # optional, default false
    provider: <provider> # optional, reserved
    apiKey:
      name: <ENV_VAR>
      proxyManaged: true
      inject:
        - domain: <domain>
          header: <header>
          format: <format>
        - domain: <domain>
          scheme: bearer
        - domain: <domain>
          scheme: basic
          username: <user> # required with scheme: basic
    oauth:
      tokenEndpoint:
        host: <host>
        path: <path>
      sentinels:
        accessToken: <sentinel>
        refreshToken: <sentinel>
      credentialFile:
        path: <path>
        structure:
          <key>:
            accessToken: "{{.AccessToken}}"
            refreshToken: "{{.RefreshToken}}"
            expiresAt: "{{.ExpiresAt}}"
            scopes: "{{.Scopes}}"

credentials is a list; each entry names a service and configures one or more auth mechanisms.

Field Description
service Credential identifier, matched against the value stored with sbx secret set. Lowercase kebab-case.
description Optional. Shown to the user when approving a binding.
required Marks the credential as essential to the agent. If it has no binding, sbx warns and starts with the credential withheld. Default false.
provider Reserved for a provider registry. Accepted with a warning and no runtime effect.
apiKey API-key injection (see apiKey).
oauth OAuth interception (see oauth).

Each service must declare apiKey, oauth, or both. When both resolve at runtime, the API key takes precedence and OAuth acts as the fallback.

apiKey#

Field Description
name Environment variable name for the credential (for example, ANTHROPIC_API_KEY).
proxyManaged If true, sbx sets name inside the container to the proxy-managed sentinel. Default false.
inject[].domain Domain to inject the credential into. Must also be allowed in permissions.network.
inject[].header HTTP header the proxy sets (for example, x-api-key, Authorization).
inject[].format Header value format, with one %s placeholder (for example, "%s" or "Bearer %s"). Mutually exclusive with scheme.
inject[].scheme Shorthand for common auth schemes. bearer expands to Authorization: Bearer %s; basic requires username. Mutually exclusive with format.
inject[].username Username for HTTP Basic auth, for example x-access-token for Git over HTTPS.

oauth#

For agents that authenticate with OAuth (for example, Claude Code), the proxy intercepts token responses and replaces real tokens with sentinels, then swaps the real token back in on outbound requests. By default, the token never enters the sandbox. Setting passthrough: true opts out of sentinel masking and sends the real token response into the sandbox.

Field Description
tokenEndpoint.host / path The OAuth token endpoint the proxy intercepts.
sentinels.accessToken / refreshToken Sentinel values written into the container in place of the real tokens.
credentialFile.path Where to write the credential file inside the container (~ expands).
credentialFile.structure Declarative JSON shape. Supports {{.AccessToken}}, {{.RefreshToken}}, {{.ExpiresAt}}, and {{.Scopes}}.
credentialFile.template Go template. Supports {{.AccessToken}}, {{.RefreshToken}}, {{.ExpiresAt}}, {{.Scopes}}, and {{.ScopesJSON}}.
resourceHosts API hosts where the proxy attaches the token on outbound requests, distinct from the token endpoint host.
skipIfEnv Accepted for compatibility, but ignored for schema v2. A v2 binding is authoritative instead of host environment variables.
responseFields Overrides the default field names the proxy reads from the token response.
passthrough If true, the proxy passes the token response through unchanged instead of replacing the tokens with sentinels.

credentialFile.structure provides a declarative alternative to credentialFile.template. The engine renders it as well-formed JSON. If both fields are set, structure takes precedence.

Network#

Network egress is declared under permissions.network. Credentials no longer carry their own domain mapping — the proxy injects a credential only into the domains its apiKey.inject lists, and every domain the sandbox reaches must be allowed here.

permissions:
  network:
    allow: [<domain>, ...]
    deny: [<domain>, ...]
Field Description
permissions.network.allow Domains the sandbox can reach.
permissions.network.deny Domains the sandbox is blocked from reaching. Deny takes precedence over allow, including across composed kits.

Allow and deny patterns:

Pattern Example Status
Exact host api.example.com Enforced
Exact host and port api.example.com:8080 Enforced
Single-label wildcard *.example.com Enforced
Multi-label wildcard **.example.com Parsed; enforcement pending
Port range api.example.com:80-443 Parsed; enforcement pending
Port wildcard api.example.com:* Parsed; enforcement pending
CIDR 10.0.0.0/8 Parsed; enforcement pending

In v1 this was the network: block (allowedDomains / deniedDomains, plus serviceDomains / serviceAuth). In v2, those fields are decode errors.

Ports#

Use ports to expose sandbox services to the host:

ports:
  - container: 8080
    name: web
Field Description
container Container port, 1 to 65535.
protocol tcp or udp. Empty publishes one family; see below.
name Optional label surfaced by tools that list published port bindings.

Host ports are allocated ephemerally. Leave protocol empty unless the service listens on IPv6: an empty value publishes IPv4 only (127.0.0.1), which is what a service bound to 0.0.0.0 needs, while tcp publishes both 127.0.0.1 and ::1 — and a client arriving over ::1 is accepted and then reset if nothing in the sandbox is listening there. Users can pin host ports with sbx ports --publish <host>:<container>.

Environment#

environment:
  variables:
    <NAME>: <value>
Field Description
variables Key-value pairs set directly in the container.

Variable names must be valid shell identifiers ([A-Za-z_][A-Za-z0-9_]*).

Do not set DASH_, SBX_, or DOCKER_ variables, and avoid overriding HOME, USER, SHELL, PATH, LD_PRELOAD, and LD_LIBRARY_PATH. The runtime reserves these names and may override them.

Setup#

setup:
  install:
    - command: <shell-string>
      user: <uid>
      description: <text>
  startup:
    - command: [<argv>, ...]
      user: <uid>
      background: <true | false>
      description: <text>
  files:
    - path: <path>
      content: <text>
      mode: <octal>
      onlyIfMissing: <true | false>
      description: <text>

Execution order#

When a sandbox is created, kit content is applied in this order:

  1. Network permissions and environment variables.
  2. Static files under files/home/.
  3. setup.install commands, in declaration order.
  4. setup.files entries.
  5. setup.startup commands are registered for each sandbox start.
  6. Static files under files/workspace/, after the workspace is ready. With --clone, this means after the repository has been cloned.

For stacked kits, entries in each stage are applied in --kit order. An install command can consume a bundled file from files/home/, but not one from files/workspace/ or setup.files, because those files land later.

sbx kit add recreates the sandbox rather than modifying it in place. It supports mixin kits limited to environment.variables, setup.install, and permissions.network.allow, which follow the same order as sandbox creation. It rejects a kit that declares static files, setup.startup, or setup.files. To use those fields, recreate the sandbox with the kit.

install#

Runs synchronously when a kit is applied, either during sandbox creation or through sbx kit add. Shell strings are passed to sh -c.

Kit install commands start in the template image's configured WORKDIR. Docker-provided templates use /home/agent/workspace, which isn't necessarily the primary workspace in a direct-mounted or clone-mode sandbox. Don't rely on the current directory to locate workspace files. Use absolute paths for bundled assets from files/home/.

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 Block later startup commands until this command finishes. Set to true to let later commands run without waiting.
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. A value of false waits within the startup dispatcher before it runs the next command; it doesn't delay the agent entrypoint. Use startup commands for work that can run alongside the agent. Use setup.files 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.

files#

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.

The runtime writes these files as the agent user with UID 1000. The target path must be writable by that user. To write to a root-owned path such as /etc, use an install command, which runs as root by default. Set ownership in the install command if the agent needs to modify the file later.

Static files#

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.

Volumes#

volumes:
  - path: /workspace
    size: 10g
    mode: "0755"
  - path: /tmp/scratch
    type: tmpfs
    size: 512m
    mode: "1777"
Field Description
path Required absolute container path.
type Empty for a block-backed volume, or tmpfs for RAM-backed storage.
size Optional byte-size string.
mode Optional octal permissions.

Volumes are applied only when a sandbox is created. sbx kit add cannot attach volumes to a running container.