mcpbeat Sign in

Docker Sandboxes Kits Skill for Claude

>-

13k tokens
context cost
the whole folder, loaded on every use
9
files
instructions only
0
copies elsewhere
how many repositories repackaged it
209
stars on the repo
on the repository, not the skill itself

Install

one command, takes just this skill from the repository
npx skills add https://github.com/docker/skills --skill docker-sandboxes-kits

What comes with it

35 150 bytes besides the instruction
agents/openai.yaml
assets/spec-mixin.yaml
assets/spec-sandbox.yaml
checks/verification.md
references/kit-distribution-commands.md
references/sources.md
references/spec-v2-fields.md
skill.yaml

The instruction itself

18 sections, as written by the author

Docker Sandboxes: Kits (spec.yaml)

Overview

A kit is a directory (or ZIP/OCI/git artifact) containing a spec.yaml

plus an optional files/ tree. sbx composes a kit into a running or

about-to-be-created sandbox at sbx create/sbx run --kit/sbx env time or

at sbx kit add time. This skill owns kit-spec v2 authoring, validation, and

distribution — everything under spec.yaml's own grammar — and defers what a

kit's declarations *mean at runtime* (credential injection, network

enforcement) to docker-sandboxes-network-credentials, and the sandboxes a

kit is composed into to docker-sandboxes-lifecycle.

When to use this skill

Activate this skill when:

  • The user wants to write, validate, or pack a spec.yaml for a `kind:

sandbox (complete agent) or kind: mixin` (extension) kit.

  • The user wants a mixin to add a tool, credential, network allowance, or

files to an existing built-in agent.

  • The user wants to publish a kit to (or pull one from) an OCI registry,

sign it, or verify a signature/provenance attestation.

  • The user is debugging a kit-validation error, an argument-substitution

error, or sbx kit add's recreate-aware requirement.

Do not use this skill when

Do not use this skill when:

  • The task is creating/running/removing the sandbox a kit is composed into,

independent of the kit's own content — use docker-sandboxes-lifecycle.

  • The task is what a credential or network rule a kit declares actually

does at runtime (proxy injection, allow/deny precedence, or what the

CURRENT network/global policy already permits), or is about secrets/

policy that have nothing to do with a kit — use

docker-sandboxes-network-credentials.

  • The task is the sbxenv.yaml file format that references kits via its

own kits: block — use docker-sandboxes-env for that file's schema

(this skill still owns what goes inside the referenced kit itself).

Core guidance

kind: sandbox vs kind: mixin — pick the right one

  • Exactly one kind: sandbox kit composes into any sandbox (a complete

agent: base image + launch config). Any number of kind: mixin kits

layer onto it (tools, credentials, network, files). A mixin must not

declare a sandbox: block, extends:, or mixins:.

  • Every kit needs schemaVersion: "2" (the current clean grammar — no

legacy shims), kind, and name matching

^a-z0-9?$. Decoding is strict: any

unrecognized field anywhere is a hard error (e.g. a typo like

permissions.netwrok:), so a kit that validates has no silent typos.

  schemaVersion: "2"
  kind: mixin
  name: extra-egress
  • Do not redefine a base agent's credential in a mixin: declaring a new

apiKey.name or proxyManaged for the same service fails composition.

shell, docker-agent, and opencode already own github. An additive

routing-only entry (apiKey.inject, no name/proxyManaged/oauth, and

required: false) can extend the base credential instead. OAuth belongs

on sandbox kits, never mixins. See references/spec-v2-fields.md.

Inspect built-in definitions at sandboxlib/agentkits/agents/<agent>/spec.yaml

in the pinned source; sbx kit inspect takes artifact references, not

built-in names. Standalone mixin validation does not test composition.

The sandbox: block (sandbox kits only)

  • Required for kind: sandbox (unless the kit extends: a parent that

already supplies it); forbidden for kind: mixin.

  • image: is the pre-built base image. entrypoint: is the fixed process

prefix (entrypoint[0] is the binary); command: is the mode-specific

argument tail — either a bare list (sets default, interactive falls

back to it) or {default: [...], interactive: [...]}.

For a complete minimal kit, use assets/spec-sandbox.yaml, which inherits

the embedded shell definition rather than inventing an image or command.

  • sandbox.build: (Dockerfile build) is **accepted but not built by the

runtime this release** — a kit that sets build: must still set image:,

or it is rejected at load with an actionable error.

  • **extends: (below) is the simplest way to get a real, working image

without inventing one.** A sandbox kit that extends a built-in agent

(e.g. extends: shell) inherits that agent's real sandbox.image and

may omit sandbox: entirely — see the minimal example asset, which does

exactly this rather than naming a made-up image reference.

Egress: permissions.network — and the all-egress-declared rule

  • permissions.network.allow/deny are the v2 home for what v1 spelled as

top-level network:. Enforced shapes include exact host, exact host+port,

single-label wildcards (*.example.com), multi-label wildcards

(**.example.com), and CIDR prefixes. Port ranges are not supported by

the runtime matcher; use separate exact ports.

Deny wins within domain rules or within CIDR rules. A decisive domain

decision is evaluated before CIDR rules: an allowed hostname is not

checked against a CIDR deny for its resolved IP. Do not rely on a CIDR

deny alone to block an already-allowed hostname.

  permissions:
    network:
      allow:
        - registry.npmjs.org
      deny:
        - telemetry.example.com
  • **permissions.network.allow is additive across a composition, and a

kit's own allow list is not the only thing granting a sandbox egress.**

The sandbox already carries the base agent's own allow list, plus

whatever the *global* or *per-sandbox* network policy (sbx policy,

independently of any kit) permits — see `docker-sandboxes-network-

credentials. **Removing a host from one kit's allow` list does not by

itself prove that host is blocked** — the global policy defaults

(balanced allows common package registries and AI services; allow-all

allows everything) or another composed kit may still permit it. Never

claim a host is blocked without checking the actual effective decision

with sbx policy check network --sandbox <name> <host> on a real

sandbox.

  • Declare the egress a kit requires explicitly for reproducibility. Credential

injection does not itself grant network access. Omitting an allow entry

leaves reachability dependent on the existing global/per-sandbox policy;

it does not necessarily block the host. Check the effective decision.

credentials — what the kit needs, never how the user stores it

  • Each entry declares a service identity and where to inject the

resolved value (apiKey and/or oauth); it never declares *how* the

user obtains or stores the credential — that lives in the user's own

bindings file, wired through sbx secret set (see

docker-sandboxes-network-credentials).

  • apiKey.inject[] needs a domain and either an explicit header+

format (format must contain exactly one %s) or the scheme:

sugar: scheme: bearer expands to Authorization: Bearer %s (no

username), scheme: basic requires username and is mutually

exclusive with format. **Pick a service name no composed base agent

already declares** (see the duplicate-service rule above) — see

references/spec-v2-fields.md for a complete fragment.

  • apiKey.proxyManaged: true sets the in-container env var to the literal

proxy-managed sentinel rather than leaving it unset; the real value is

substituted only by the proxy, on the allow-listed inject domains.

  • oauth needs tokenEndpoint.host/.path and, unless

passthrough: true, non-empty sentinels.accessToken/.refreshToken.

passthrough: true is a security downgrade — the real token reaches

the container instead of a sentinel — use it only when the kit's own

design requires it and say so in description.

setup — install (once) vs. startup (every start) vs. files (startup-time writes)

| Block | Command shape | Runs |

|---|---|---|

| setup.install[].command | string, via sh -c | Once, synchronously, before the agent first launches. Runs for every kit, built-in or not. |

| setup.startup[].command | list<string>, exec-style (no shell) | On every container start (create, stop/start, daemon restart, host reboot) — must be idempotent. |

| setup.files[] | file write via shell exec | At container startup; path absolute; only ${WORKDIR} placeholder allowed in content. |

Optional fragment for the shell kit in assets/spec-sandbox.yaml:

setup:
  startup:
    - command: ["sh", "-c", "mkdir -p ~/.my-kit"]
  files:
    - path: /home/agent/.my-kit/config.json
      content: '{"workdir": "${WORKDIR}"}'
  • **setup.files is not the same mechanism as the files/ directory

tree (below).** setup.files entries are dynamic, ${WORKDIR}-

substituted writes performed at startup time; the files/home/ and

files/workspace/ directory tree is a set of static files packed

alongside spec.yaml and copied in at container-create time, and it is

specifically the files/workspace/ half of that tree — not

setup.files — that is written after the workspace is populated

(e.g. after an in-container git clone under --clone). Do not

conflate the two: setup.files has no "after workspace population"

timing guarantee of its own.

  • All three setup: lists concatenate in --kit order across composed

kits.

  • Default execution users: install as root (user: "0") unless overridden;

startup/entrypoint as the agent user (uid 1000) unless overridden.

Root install steps writing under /home/agent must chown it back to

agent:agent, or later agent-user writes there fail.

volumes — creation-time only, every volume must set a size

  • Each entry needs an absolute path:, optional type: tmpfs (RAM-backed;

omit/"" for the default block-backed volume), optional size:

(byte-size string) and mode: (octal).

  • Volumes apply only at sandbox-create time — sbx kit add (runtime

injection) skips volume changes entirely; a kit that needs one must be

present at creation.

  • Always set size: on a block volume. An unsized volume inherits a

50 GiB default and costs real host disk immediately (ext4 inode-table

zeroing); 512 MiB is the practical floor — below it mke2fs switches

inode density and the space savings mostly disappear.

args — parameterizing a kit

  • Declare under top-level args: (v2 only — the frozen v1 grammar has no

args block), each with exactly one of default/required: true, plus

optional description/enum/pattern. Reference with

${{ kit.args.NAME }} anywhere in spec.yaml or files/; substitution

happens before the spec is decoded. Every reference must be

declared, or loading fails — that is what makes the block a trustworthy

list of a kit's inputs. Quote a placeholder used in a string field

(VERSION: "${{ kit.args.version }}"), or an unquoted numeric-looking

value decodes as a number and fails to decode into a string field.

  • Supply values with --kit-arg name=value (every kit) or

--kit-arg kitname.name=value (one kit only), or --kit-args-file.

Never pass a secret this way — --kit-arg values are not masked; see

docker-sandboxes-network-credentials.

extends and mixins — composition, not runtime injection

  • extends: resolves only built-in agent names at this pinned release

(shell, claude, etc.). Remote git/OCI parents fail to resolve, even

if pinned; the broader format specification is not an implementation

guarantee. The minimal asset uses the supported extends: shell.

  • mixins: is accepted with a warning but is not applied by this runtime.

Use --kit or sbx kit add for composition. The format's immutable-ref

requirements do not make unimplemented remote inheritance work.

  • Prefer digest/commit-pinned CLI kit references for reproducibility.

--kit and sbx kit add still accept mutable tags/branches; the CLI

parser does not enforce this recommendation.

  • requires.agent (mixin-only; rejected on kind: sandbox) pins the

single base agent a mixin is designed for (e.g. Claude-specific env

vars). It is well-formedness-checked by the spec library; the actual

agent-affinity mismatch is enforced by the composition consumer, not by

sbx kit validate alone.

Validating, packaging, and distributing

| Command | Purpose |

|---|---|

| sbx kit validate REFERENCE [--kit-arg ...] | Local directory, ZIP, or git reference; OCI is rejected. Schema-only well-formedness check. Never composes against a base agent — cannot catch a duplicate-service credential collision or confirm any domain is reachable at runtime. |

| sbx kit inspect REFERENCE [--kit-arg ...] [--json] | Loads and prints the decoded artifact before composing it, including --kit-arg substitution preview. |

| sbx kit pack DIRECTORY [-o OUTPUT.zip] | Packages a validated directory as a ZIP. |

| sbx kit pull REFERENCE [-o OUTPUT] | Pulls a kit's raw layer payload from an OCI registry without composing it. |

| sbx kit push DIRECTORY REGISTRY/REPO:TAG [--sign] | Packages and pushes; every push attaches an unsigned-by-default SLSA provenance attestation. |

| sbx kit provenance REFERENCE [--certificate-identity ...] | Prints the attestation push attached; marked UNSIGNED unless verified against a matching key/identity. |

| sbx kit sign REFERENCE / sbx kit verify REFERENCE | Sigstore sign/verify (keyless by default); prefer --identity-token-file over --identity-token. |

| sbx kit add SANDBOX REFERENCE [--kit-arg ...] | Injects a mixin only into an existing sandbox at runtime (recreate-aware label required); container-immutable settings (security.privileged, volumes:) cannot take effect this way. |

See references/kit-distribution-commands.md for full flag lists and

worked examples of each command above.

  • For the sandboxes a kit is composed into (sbx create/run --kit,

sbx kit add SANDBOX), use docker-sandboxes-lifecycle.

  • For what a kit's credentials:/permissions.network: declarations mean

at runtime — proxy injection, allow/deny precedence, the effective

policy a sandbox actually has once global/per-sandbox policy is

included, where the user stores the actual secret value — use

docker-sandboxes-network-credentials.

  • For the sbxenv.yaml file whose kits:/agent: fields reference a kit

by this schema, use docker-sandboxes-env.

References

  • references/sources.md — provenance for every rule above (spec package, SPEC-v2.md, help captures, docs URLs).
  • references/spec-v2-fields.md — the complete v2 field table (common fields, sandbox-only fields, mixin-only fields, shared blocks) for lookup without re-reading the full spec.
  • references/kit-distribution-commands.md — full flags and worked examples for sbx kit validate/inspect/pack/pull/push/provenance/sign/verify/add.

Assets

  • assets/spec-sandbox.yaml — a genuine minimal kind: sandbox kit that

extends: shell to inherit a real, working image rather than inventing

one.

  • assets/spec-mixin.yaml — a genuine minimal kind: mixin kit with no

credentials at all (an egress-only extension), which composes cleanly

with every built-in agent.

Checks

  • checks/verification.md — Schema, composition, egress, and kit-add checks (unexecuted integration runbook; isolated --app-name, no registry publishing or signing).

How to use it

Copy the folder

Take docker/docker-sandboxes-kits from the repository into ~/.claude/skills for personal use, or into .claude/skills inside a project.

Check the name does not clash

The agent identifies a skill by the name field in its header. Two skills with the same name cannot sit side by side — one of them will be ignored.