Share feedback
Answers are generated based on the documentation.

Kit spec reference

Availability: Early Access
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.

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/

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:

PreviousCurrent
memoryagentContext
kind: agentkind: sandbox
agent: blocksandbox: 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

schemaVersion: "1"
kind: <mixin | sandbox>
name: <name>
displayName: <name>
description: <text>
FieldRequiredDescription
schemaVersionYesSpec schema version. Set to "1".
kindYesmixin for kits that extend an agent; sandbox for kits that define one.
nameYesUnique identifier. Lowercase, alphanumeric, hyphens.
displayNameNoHuman-readable name.
descriptionNoShort description.

The sections below apply to both kinds. Sandbox kits also declare a sandbox: block.

Credentials

credentials:
  sources:
    <service-id>:
      env: [<env-var>, ...]
      file:
        path: <path>
        parser: <parser>
      priority: <env-first | file-first>
FieldDescription
sourcesMap of service identifier to credential source.
sources.<id>.envEnvironment variables to read on the host, in priority order.
sources.<id>.file.pathPath on host. ~ expands to home directory.
sources.<id>.file.parserHow to extract the credential value from the file.
sources.<id>.priorityenv-first (default) or file-first.

Service identifiers link credentials to network rules.

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.

ValueBehavior
omitted or emptyReads 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 valueRejected — 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:

credentials:
  sources:
    openai:
      file:
        path: "~/.openai/token"

Nested JSON field, with an environment variable as fallback:

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:

{
  "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 messageCause
field 'X' not found in JSONThe path doesn't exist in the file.
cannot navigate to field 'X': not an objectA path segment hit a string, array, or scalar instead of an object.
field 'X' is not a string valueThe resolved value is an object, array, or null.
failed to parse JSON: ...The file is not valid JSON.

Network

network:
  allowedDomains: [<domain>, ...]
  deniedDomains: [<domain>, ...]
  serviceDomains:
    <domain>: <service-id>
  serviceAuth:
    <service-id>:
      headerName: <header>
      valueFormat: <format>
FieldDescription
allowedDomainsDomains the sandbox can reach. Wildcards supported.
deniedDomainsDomains the sandbox is blocked from reaching. Deny rules take precedence over allow rules, including those from other composed kits.
serviceDomainsMap of domain to service identifier from credentials.sources.
serviceAuth.headerNameHTTP header the proxy sets (for example, Authorization).
serviceAuth.valueFormatFormat string for the header value (for example, "Bearer %s").

Environment

environment:
  variables:
    <NAME>: <value>
  proxyManaged: [<NAME>, ...]
FieldDescription
variablesKey-value pairs set directly in the container.
proxyManagedEnvironment variable names populated by the proxy at request time. Pair with credentials.sources.

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

Commands

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.

FieldDefaultDescription
commandShell command string.
user"0"User to run as. "0" = root.
descriptionHuman-readable description.

startup

Runs at every sandbox start. String array, not interpreted by a shell.

FieldDefaultDescription
commandCommand and args as a string array.
user"1000"User to run as. "1000" = agent.
backgroundfalseRun in background.
descriptionHuman-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.

FieldDefaultDescription
pathAbsolute container path.
contentFile content. ${WORKDIR} expands to the workspace path.
mode"0644"File permissions in octal.
onlyIfMissingfalseSkip if the file already exists.

Static files

my-kit/files/
├── home/       → /home/agent/
└── workspace/  → primary workspace path
Kit pathContainer 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

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.

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.

sandbox:
  image: <image-ref>
  aiFilename: <filename>
  entrypoint:
    run: [<argv>, ...]
    args: [<arg>, ...]
FieldRequiredDescription
sandbox.imageYesDocker image reference. See Base image requirements.
sandbox.aiFilenameNoMemory filename (for example, AGENTS.md). Appends top-level agentContext at creation.
sandbox.entrypoint.runNoCommand and args as a string array. Replaces the image's entrypoint.
sandbox.entrypoint.argsNoArgs 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).

Build on top of docker/sandbox-templates:shell-docker to get these for free.