`com.docker.sandbox/*` capabilities
A kit grants itself nothing: each capability is typed and versioned, the resolver unions one workload with its mixins, and any widening on update stops for approval.
The gh mixin a colleague published reaches api.github.com with your token. You want to know exactly which requests it can make with that token, and what changes when version 2 arrives. Both answers are in its capabilities list.
When you finish this section, you can read that list, predict the merged grant set, and say which update will stop and ask.
Typed, versioned requests
Capability: one typed request in the capabilities list of a kit, for something that the kit needs and cannot supply itself. The host answers each one: granted, refused, or prompted.
Each entry has a type of the form <namespace>/<name>@<version>, an optional display name, and an optional flag. Its config is decoded strictly for the types the specification defines, so an unknown key is an error. The version names the config schema, so network-policy@1 and @2 both exist and a descriptor states one of them. A type that describes a policy appears once, while instance-shaped types repeat once per thing requested, such as credential@1 per service and phase.
Unknown types are allowed by design, because new types are how the format grows. A required unknown type fails resolution, and an optional one is skipped and recorded.
The nineteen types at the pin
| Type | Shape | What it asks for |
|---|---|---|
network-policy@1 | singleton | hosts the sandbox can reach, per phase |
network-policy@2 | singleton, exclusive with @1 | hosts plus HTTP methods and paths |
credential@1 | per service and phase | one service the workload authenticates to |
ssh-agent@1 | per phase | what the forwarded SSH agent signs |
volume@1 | per path | persistent or tmpfs storage |
host-mount@1 | per path | a host directory, sharing the storage key with volume@1 |
port@1 | per container port and transport | a published port |
usb-device@1 | instance | a USB device match |
resources@1 | singleton | CPU, memory, and GPU limits, a constraint rather than a grant |
privileged@1 | singleton, no config | a privileged container |
long-running@1 | singleton, no config | keep running with no session attached |
lifecycle@1 | singleton | install hooks, startup hooks, files, the interactive argv |
agent-context@1 | singleton | the instruction file for the agent |
agent-sessions@1 | singleton | headless prompt and resume verbs |
agent-skills@1 | per path | where the agent reads skills |
agent-skill@1 | per effective name | one bundled skill |
git-identity@1 | singleton, no config | the runtime's git name and email |
kit-registry@1 | singleton, no config | reach the runtime's own kit registry, where builds push and pull |
sbx@1 | singleton, no config | launch the workload a particular way, with the identity the image states |
The table is kitspec §7.2 at the v3.0.0-m.8 tag. The main branch adds a twentieth, com.docker.sandbox/agent-interactive-sessions@1, as a singleton, unreleased at the pin. The newest type in sbx itself arrived with the pinned release. A kit can declare com.docker.sandbox/long-running@1 to keep a local sandbox running after all its sessions disconnect.
network-policy@2 and credential@1
A network-policy@2 entry is a plain host string or an object with hosts, methods, and paths. A host string grants the connection for any protocol, as @1 did. An entry with methods or paths is bounded: it grants those HTTP requests and nothing else on that host. Deny entries list what to refuse, and a deny wins. A refused request gets a status, because a runtime must answer it with HTTP status 403. The proxy that enforces this is the one in the policy log section.
A credential@1 entry names a service and a phase, and never the place where the secret is kept. With apiKey.proxyManaged, the real value stays on the host. Every inject domain must appear in the allow list of the same phase. The binding that answers it is the one the secret section stores.
Resolution and the merged set
A runtime must include exactly one workload kit in each composition. It must fail when two kits provide the same normalized name, so claude plus claude-mixin is refused. Across the set, allow entries union per phase, deny entries union per phase, and deny wins on the result. When one kit allows a host outright, another kit's bounded entry for that host is dropped, because the union is the unbounded grant. Figure 4.2 runs that arithmetic on three kits.
What a grant looks like inside sbx
The recording host ran a v2 mixin, so the captured rows come from permissions.network.allow rather than a v3 entry. The daemon stores the grant as a policy rule the sandbox owner cannot edit:
"name": "kit:m101-kit",
…
"scope": "sandbox:m101-kit",
…
"resource_type": "network",
"decision": "allow",
"resources": [
"example.com"
],
…
"editable": false,
…
"created_via": "provisioned",sbx policy check network example.com --sandbox m101-kit --json then answers "allowed": true (the capture). The rule carries created_via: provisioned and answers to --source kit, which is how the policy section tells a kit rule from one you added.
Updates and the lock
Every grant maps onto one normalized set. A consumer that controls updates stores that set in its lock file and compares each candidate against it. A new version whose set stays inside the stored one can apply silently. Any widening must stop for approval.
The list is explicit. It holds a new allow entry, a removed deny entry, and a new credential, path, or port. Write access over a read-only skills path is also on it. A removed deny counts, because the deny was part of what made the grant acceptable. optional does not change the set. Six types contribute nothing to it: resources@1, lifecycle@1, agent-context@1, agent-sessions@1, sbx@1, and long-running@1.
Docker Sandboxes (sbx) and Docker Agent (docker-agent) sbx 0.47.0, docker-agent 1.149.0 · bf4169c · 2026-10-07 · Edition 2026.10