4.2

`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

TypeShapeWhat it asks for
network-policy@1singletonhosts the sandbox can reach, per phase
network-policy@2singleton, exclusive with @1hosts plus HTTP methods and paths
credential@1per service and phaseone service the workload authenticates to
ssh-agent@1per phasewhat the forwarded SSH agent signs
volume@1per pathpersistent or tmpfs storage
host-mount@1per patha host directory, sharing the storage key with volume@1
port@1per container port and transporta published port
usb-device@1instancea USB device match
resources@1singletonCPU, memory, and GPU limits, a constraint rather than a grant
privileged@1singleton, no configa privileged container
long-running@1singleton, no configkeep running with no session attached
lifecycle@1singletoninstall hooks, startup hooks, files, the interactive argv
agent-context@1singletonthe instruction file for the agent
agent-sessions@1singletonheadless prompt and resume verbs
agent-skills@1per pathwhere the agent reads skills
agent-skill@1per effective nameone bundled skill
git-identity@1singleton, no configthe runtime's git name and email
kit-registry@1singleton, no configreach the runtime's own kit registry, where builds push and pull
sbx@1singleton, no configlaunch 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.

Fig. 4.2three kits resolve into one grant settree
Three kits resolve into one grant set Top: three kits, the captured hello-kit workload with one network-policy@2 allow for example.com, the gh mixin from the specification example with network-policy@1 allows and a GitHub credential, and a mixin that carries the network-policy@2 config example with a bounded allow and two deny entries. Middle: sbx resolves the closed set with exactly one workload. Below: the merged grant set stored in the lock, five rows of allow, deny, install, and credential entries. Bottom: two branches for the next version of a kit. A version whose grants stay inside the stored set applies silently. A version that removes the deny on DELETE widens the set and stops for approval. THE DECLARED KITS hello-kit, workload fixtures/kits/hello-kit network-policy@2, runtime: allow example.com gh, mixin kitspec 2 example network-policy@1 runtime allow github.com, api.github.com, uploads.github.com credential@1: github, runtime, GH_TOKEN proxyManaged bounds, mixin kitcap network-policy@2 config install: registry.npmjs.org runtime: allow api.github.com GET and HEAD on /repos/** deny api.github.com DELETE deny telemetry.example.com sbx resolves the set (kitspec 5.3) closed set, one workload, one provider per name, deny wins the lock records the grant set the merged grant set for the sandbox (kitspec 7.4) runtime allow example.com, github.com, uploads.github.com runtime allow api.github.com, every method and path (bounded entry dropped, kitspec 9.5) runtime deny api.github.com DELETE, telemetry.example.com (deny wins, kitcap) install allow registry.npmjs.org (closed again before the agent starts, kitspec 12) credential github at runtime, GH_TOKEN proxyManaged, inject on api.github.com the next version of a kit stays inside the set a bounded allow on a host already granted applies silently (kitspec 7.4) removes deny api.github.com DELETE a removed deny entry is a widening stops and asks before it applies same grants wider grants A kit grants itself nothing: each row is a request the host answered. The lock judges the next version. From hello-kit/kit.yaml, kitspec 2, 5.3, 7.4 and 9.5, and kitcap network-policy@2 and credential@1.
One workload and two mixins merge into one grant set where allows union, deny wins, and a removed deny on the next version stops for approval. Read top down. The three kits are the captured hello-kit, the gh example of kitspec §2, and the network-policy@2 config example of its capability page. The teal block is the lock.

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:

the kit's allow rule on sandbox m101-kitjsonFull log
      "name": "kit:m101-kit",
…
      "scope": "sandbox:m101-kit",
…
      "resource_type": "network",
      "decision": "allow",
      "resources": [
        "example.com"
      ],
…
      "editable": false,
…
        "created_via": "provisioned",
The rules array has one element, cut to the fields named in the text.

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