4.1

`# 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:

the v3 descriptor the capture kit wroteyamlFull log
# 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.com

The 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.

Fig. 4.1a v3 descriptor and the image manifest that carries itstructure
A v3 descriptor and the image manifest that carries it Left: the nine top-level entries of the kit.yaml of the hello kit, from the syntax line to the capabilities list, each with a note on what it becomes. Right: what the local registry returned for m101/hello-kit:v1, an OCI image index with no annotations and the linux/arm64 image manifest. The manifest carries four vnd.docker.sandbox.kit annotations, the descriptor as JSON with 402 derived deb provides, schema-version 3, the network-policy@2 capability, and built-by sandbox-kit 3.0.0-m.8, plus four org.opencontainers.image keys, the OCI config, and 16 layers with the sources staged at /usr/share/sandbox/kit/kit. Teal lines connect each descriptor row to the annotation or layer it becomes. KIT.YAML OF HELLO-KIT, AS WRITTEN WHAT THE REGISTRY RETURNED hello-kit/kit.yaml # syntax=docker/sandbox-kit:3 dispatches the frontend, sandbox-kit 3.0.0-m.8 schemaVersion: "3" REQUIRED, exactly the string "3" (kitspec 4) kind: workload workload, mixin, or set (kitspec 4) displayName: hello-kit becomes org.opencontainers.image.title description: A shell workload with one network grant, in the v3 descriptor form. becomes org.opencontainers.image.description version: "1.0.0" becomes org.opencontainers.image.version licenses: [Apache-2.0] becomes org.opencontainers.image.licenses build: | FROM docker/sandbox-templates:shell COPY HELLO.md /home/agent/HELLO.md the recipe of the layers (kitspec 3.2) capabilities: - type: com.docker.sandbox/network-policy@2 config: runtime: allow: - example.com a list of type and config (kitspec 7) OCI image index, tag v1 application/vnd.oci.image.index.v1+json linux/arm64 and an attestation manifest annotations: none platform manifest, linux/arm64 application/vnd.oci.image.manifest.v1+json annotations vnd.docker.sandbox.kit.descriptor kit.yaml as JSON, plus 402 deb/ provides vnd.docker.sandbox.kit.schema-version "3" vnd.docker.sandbox.kit.capabilities com.docker.sandbox/network-policy@2 vnd.docker.sandbox.kit.built-by docker/sandbox-kit 3.0.0-m.8, 129be2ff org.opencontainers.image.* title hello-kit, version 1.0.0, licenses Apache-2.0, description config application/vnd.oci.image.config.v1+json 16 layers shell template, HELLO.md, and sources staged at /usr/share/sandbox/kit/kit docker buildx build -f fixtures/kits/hello-kit/kit.yaml --builder m101-builder -t m101-registry:5000/m101/hello-kit:v1 --push fixtures/kits/hello-kit With the default docker driver, the same push kept no annotation (28-manifest-docker-driver.json). The index carries none of the eight, although kitspec 9.3 promotes them there (28-index.json).
The frontend copies the hello-kit descriptor into one manifest annotation, derives six more annotations from its fields, and records its own release in built-by. Read left to right. The left column is the hello kit's kit.yaml, and each note says what the row becomes. The right column is the index and the arm64 manifest that the local registry returned, from the index and manifest captures. Teal lines are the writes of the frontend.

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:

the annotations of the hello-kit manifestjsonFull log
  "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 config, the layers, and most of the descriptor are cut. The registry stores the descriptor as one JSON string, and the capture kit decodes it and keeps five of its 402 provides entries.

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:

the v2 tooling refuses the v3 directorytextFull log
$ 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]
inspect builds a v3 source with the host Docker daemontextFull log
$ 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 documentation link at the end of the error is cut. 28-kit-inspect-source.txt repeats the same error.

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 refuses a kit from outside docker.iotextFull log
$ 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 advice after the allowlist value and the second suggestion are cut. 28-kit-inspect.txt shows the same refusal for sbx kit inspect.

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 saysThe capture showsFiles
docker buildx build -f kit.yaml --push publishes the kitthe default docker driver of Desktop 4.94.0 pushed a Docker schema 2 manifest with no annotations28-buildx-docker-driver.txt, 28-manifest-docker-driver.json
the frontend copies all four onto the image index whenever the export makes onethe index has an attestation manifest and no annotations, and the arm64 manifest carries all eight28-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-0228-buildx.txt
during development, sbx takes a local source directory insteadsbx kit inspect of the directory failed: the OCI exporter is not supported for the docker driver13-kit-v3-inspect.txt, 28-kit-inspect-source.txt
source-form builds run in a shared builder sandbox named sbx-kit-builderthat 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