Kit spec reference
NoteKits 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:
| 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
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.
Credentials
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.
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:
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-firstGiven ~/.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 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
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
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. |
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.
| 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
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
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>.mdin a siblingkits-agent-context/directory next to the main memory file. - The main memory file gets a
## Kitssection 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>, ...]| Field | Required | Description |
|---|---|---|
sandbox.image | Yes | Docker image reference. See Base image requirements. |
sandbox.aiFilename | No | Memory filename (for example, AGENTS.md). Appends top-level agentContext 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
agentuser at UID 1000 with passwordless sudo. - A
/home/agent/home directory owned byagent. - 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.