`# syntax=docker/sandbox-kit:3` and kit.yaml
A v3 kit is an ordinary OCI image whose manifest annotation carries a strict YAML descriptor, which sbx resolves when it creates the sandbox.
Your team wants every Codex sandbox to carry the same three tools, the same two allowed hosts, and the same instructions. A kit declares the tools, the hosts, and the credentials in one file, and the file travels as an image.
When you finish this section, you can read a v3 descriptor line by line and say what the frontend writes into the image. You can also tell which commands build, inspect, and run it.
One image, one annotation
Kit: one OCI image whose manifest annotation vnd.docker.sandbox.kit.descriptor carries the kit's declarations, while its layers carry the content. Stock tooling can pull a kit, inspect it, and use it in FROM. An engine that does not read the annotation runs it as an ordinary image.
Workload: a kit whose layers are a root filesystem and whose image config carries the launch command. A composition has exactly one.
Mixin: a kit whose layers are an overlay applied on a workload's filesystem, zero or more per composition. A third kind, set, exists only while you write the kit. Publishing turns it into workload or mixin from the kits that it lists.
The descriptor, line by line
The capture kit wrote one v3 descriptor, a shell workload with an inline recipe and a single network grant:
# syntax=docker/sandbox-kit:3
schemaVersion: "3"
kind: workload
displayName: hello-kit
description: A shell workload with one network grant, in the v3 descriptor form.
version: "1.0.0"
licenses: [Apache-2.0]
build: |
FROM docker/sandbox-templates:shell
COPY HELLO.md /home/agent/HELLO.md
capabilities:
- type: com.docker.sandbox/network-policy@2
config:
runtime:
allow:
- example.comThe first line of the descriptor, # syntax=docker/sandbox-kit:3, selects the frontend. schemaVersion must be exactly the string "3", and kind is workload, mixin, or set. displayName, description, version, and licenses are optional display metadata. No name field exists, because identity is the reference that a kit is consumed by.
The build: field holds plain Dockerfile text. capabilities is a list of entries with type and config. Here one network-policy@2 entry allows example.com in the runtime phase, the agent's steady state.
Decoding is strict: a field that the parser does not know is an error anywhere in the document. The reason is policy. A misspelled key that the parser ignored would be a policy that is missing without a warning. Figure 4.1 puts the file beside the manifest that the build pushed.
What the frontend publishes
docker buildx build ./my-kit -f ./my-kit/my-kit.yaml -t docker.io/<NAMESPACE>/my-kit:1.0.0 --push builds and publishes a kit. The capture kit ran it against a local registry with a docker-container builder (28-builder.txt, 28-buildx.txt), and the registry returned this manifest:
"mediaType": "application/vnd.oci.image.manifest.v1+json",
…
"annotations": {
"org.opencontainers.image.description": "A shell workload with one network grant, in the v3 descriptor form.",
"org.opencontainers.image.licenses": "Apache-2.0",
"org.opencontainers.image.title": "hello-kit",
"org.opencontainers.image.version": "1.0.0",
"vnd.docker.sandbox.kit.built-by": "{\"name\":\"docker/sandbox-kit\",\"version\":\"3.0.0-m.8\",\"revision\":\"129be2ff45e8f9463450eb3cf04ddcb52c2b76e5\"}",
"vnd.docker.sandbox.kit.capabilities": "com.docker.sandbox/network-policy@2",
"vnd.docker.sandbox.kit.descriptor": {
"schemaVersion": "3",
…
"provides": [
"deb/adduser@3.153",
…
"... 397 more derived provides entries"
],
…
"vnd.docker.sandbox.kit.schema-version": "3"The descriptor annotation holds the published descriptor as compact JSON, with the build: text kept. For a workload, the frontend also reads the package database and adds one deb/ entry per installed package. schema-version and capabilities repeat two fields, and the org.opencontainers.image.* keys copy the display fields. The build staged the sources at /usr/share/sandbox/kit/kit, after the stem of kit.yaml.
Which commands accept a v3 kit
Conflict C13 asks which generation each command accepts, and the captures settle it:
$ sbx kit validate ./fixtures/kits/hello-kit
error: kit ./fixtures/kits/hello-kit is a v3 source kit and this load path has no kit builder configured; artifact validation failed
[exit 1]$ sbx kit inspect ./fixtures/kits/hello-kit --json
→ build kit ./fixtures/kits/hello-kit (sbx-kit-src:kit-<id>)
error: build kit ./fixtures/kits/hello-kit: exit status 1 ERROR: failed to build: OCI exporter is not supported for the docker driver. Switch to a different driver, or turn on the containerd image store, and try again. …
[exit 1]The ruling: sbx kit validate, pack, push, and pull are v1 and v2 tooling. pack refused the directory too, for lack of a spec.yaml (13-kit-v3-pack.txt). The documentation agrees: use Buildx for v3 kits, because sbx kit pack, push, and pull are for v1 and v2 kits.
A v3 kit is consumed by sbx run and --kit, since sbx run and sbx create accept a kit reference in the place of the agent name. The pushed hello-kit still never reached a sandbox, because kit.allowedSources defaults to ["docker.io/"] (C40, 02-settings.txt):
$ sbx create localhost:15000/m101/hello-kit:v1 fixtures/repo-kit --name m101-v3
error: resolve kits: kit "localhost:15000/m101/hello-kit:v1": kit "localhost:15000/m101/hello-kit:v1" cannot be installed — its source is not in your allowlist; current kit.allowedSources: docker.io/; …
try: sbx settings set kit.allowedSources '["docker.io/","localhost:15000/m101/"]'
…
[exit 1]The capture kit never changes sbx settings, so no capture shows sbx running a v3 kit. Two rules limit the mix. A sandbox cannot combine v3 kits with v1 or v2 kits. The built-in agent names, such as claude and codex, select v2 kits. Docker publishes its v3 workloads as docker/sbx-kit-*, such as docker.io/docker/sbx-kit-codex:0.155.1.
Where the run and the pages disagree
Five results of the run contradict the specification or the documentation, and rows C113 to C117 of the register cover them. The table prints both sides:
| The page says | The capture shows | Files |
|---|---|---|
docker buildx build -f kit.yaml --push publishes the kit | the default docker driver of Desktop 4.94.0 pushed a Docker schema 2 manifest with no annotations | 28-buildx-docker-driver.txt, 28-manifest-docker-driver.json |
| the frontend copies all four onto the image index whenever the export makes one | the index has an attestation manifest and no annotations, and the arm64 manifest carries all eight | 28-index.json, 28-manifest.json |
the floating docker/sandbox-kit:3 never moves for a milestone (the README of the specification) | :3 resolved to the milestone 3.0.0-m.8 of 2026-10-02 | 28-buildx.txt |
during development, sbx takes a local source directory instead | sbx kit inspect of the directory failed: the OCI exporter is not supported for the docker driver | 13-kit-v3-inspect.txt, 28-kit-inspect-source.txt |
source-form builds run in a shared builder sandbox named sbx-kit-builder | that build ran in the host Docker daemon, and the builder status recorded after it reads "not created" | 13-kit-v3-inspect.txt, 13-kit-builder-status.txt |
An image without the descriptor annotation is not a kit to any consumer, so build with a docker-container builder. A consumer that finds no annotation on the index reads the platform manifest. The specification calls itself experimental, with a final version targeted for Q4 2026.
The default size of a kit volume stays in dispute (C12). The v0.39.0 notes say 512 MB, the research notes say 20 GiB, and this manual prints both. kit-tck validate judges a published artifact, kit-tck inspect reads it back, and this manual ran neither. Section 7.4 lists the claims it checked instead.
Docker Sandboxes (sbx) and Docker Agent (docker-agent) sbx 0.47.0, docker-agent 1.149.0 · bf4169c · 2026-10-07 · Edition 2026.10