Sandbox Kits & Custom Templates

Table of Contents

  1. What a kit actually is
  2. Anatomy of a kit
  3. Mixin kits vs. sandbox kits
  4. Writing your first kit
  5. Using a kit
  6. Sharing, signing, and trusting kits
  7. Beginner Pitfalls

1. What a kit actually is

Every sandbox you've created so far in this course started from a bare agent (shell, claude, ...) and got its extra setup — apt-get install, npm install, environment variables — typed in by hand, every time. A kit is that setup written down once, as a declarative file, so it can be applied to any sandbox with a single flag instead of re-typed.

A kit can bundle:

  • Tools to install
  • Environment variables to set
  • Credentials to inject (without ever exposing the raw secret to the agent)
  • Network rules to allow or deny
  • Files to drop into the sandbox
  • Startup commands to run
  • Memory instructions to give the agent (project conventions, house style, "run ruff check before committing" — that kind of thing)

Note: kits are a newer, explicitly experimental part of sbx — Docker documents them as subject to change. Everything below matches the documented behavior at the time of writing; if a command errors out, check docs.docker.com/ai/sandboxes/customize/kits for what changed.

2. Anatomy of a kit

A kit is a directory, not a single file:

my-kit/
├── spec.yaml       # the kit's declaration
└── files/          # optional: static files to drop into the sandbox
    ├── home/
    │   └── .config/tool/settings.json
    └── workspace/
        └── .editorconfig

spec.yaml is where everything is declared. A minimal one:

kind: mixin
name: my-kit

setup:
  install:
    - command: "apt-get update && apt-get install -y jq"
  startup:
    - command: ["my-daemon"]
      background: true
  files:
    - path: /home/agent/.config/tool/config.json
      content: '{"workspace": "${WORKDIR}"}'
      onlyIfMissing: true

environment:
  variables:
    MY_TOOL_WORKSPACE: /home/agent/my-tool

permissions:
  network:
    allow:
      - api.example.com
      - "*.cdn.example.com"
    deny:
      - telemetry.example.com

agentInstructions:
  content: |
    A linter is installed. Run it before committing.

setup.install commands run once, when the sandbox is created. setup.startup commands run every time the sandbox starts, so they must be safe to run more than once (idempotent) — the same discipline you'd apply to a docker-entrypoint.sh. agentInstructions.content gets appended to the agent's memory file, the same file claude/CLAUDE.md-style agents already read on startup.

3. Mixin kits vs. sandbox kits

kind: decides what a kit is allowed to do:

  • kind: mixin — extends an existing agent (claude, codex, a plain shell, ...) with extra tools, files, or permissions. This is what you'll write most often: "give this agent linting" or "give this agent read access to our internal package registry."

  • kind: sandbox — defines a complete agent from scratch, including its own container image:

    kind: sandbox
    name: my-agent
    sandbox:
      image: "my-registry/my-agent:latest"
      entrypoint: ["my-agent", "--yolo"]
    

    A sandbox kit can also extends: claude to inherit an existing agent's image, credentials, network permissions, and settings, and layer changes on top rather than starting from zero.

A mixin can't declare a sandbox: block — that's the one thing reserved for kind: sandbox. Multiple mixins can be applied to the same sandbox at once; their install/startup/files entries just run one after another, in order.

4. Writing your first kit

From your host (kits are authored outside a sandbox, then applied to one):

mkdir -p ~/my-kit && cd ~/my-kit
cat > spec.yaml << 'EOF'
kind: mixin
name: ripgrep-and-jq

setup:
  install:
    - command: "apt-get update && apt-get install -y ripgrep jq"

agentInstructions:
  content: |
    ripgrep (rg) and jq are installed. Prefer rg over grep for searching
    the codebase, and jq for inspecting JSON output.
EOF

Check it's well-formed before trying to use it:

sbx kit validate ~/my-kit
sbx kit inspect ~/my-kit

5. Using a kit

Apply a kit at sandbox creation time with --kit:

sbx run claude --kit ~/my-kit

Or add it to an existing, already-running sandbox:

sbx kit add my-agent-sandbox ~/my-kit

This restarts the sandbox but preserves its state — installed packages, pulled images, shell history all survive. Adding a kit to a running sandbox only supports a subset of fields (environment variables, install commands, and network allow rules) — anything that needs a fresh container (like a kind: sandbox image swap) has to be applied at creation time instead.

If your kit takes arguments (declared under args: in spec.yaml), pass them with --kit-arg:

sbx run claude --kit ~/my-kit --kit-arg channel=beta

6. Sharing, signing, and trusting kits

A kit is just files, so it can live anywhere your team already shares code:

sbx run claude --kit ./my-kit                                        # local path
sbx run claude --kit "git+https://github.com/org/kits.git#dir=jq-kit" # Git repo, pinned
sbx run claude --kit ghcr.io/myorg/my-kit:1.0                         # OCI registry

Because a kit can run arbitrary install commands, sbx treats "some kit someone linked you" the same way you should treat "some curl | sh someone linked you" — worth verifying before you run it. Kits support cosign-style signing:

sbx kit sign ~/my-kit
sbx kit verify \
  --certificate-identity you@example.com \
  --certificate-oidc-issuer https://accounts.google.com \
  ~/my-kit

and a team can require signatures from specific, trusted signers before any kit is allowed to run at all:

sbx settings set kit.trustedSigners \
  '[{"identity":"release-bot@example.com","issuer":"https://accounts.google.com"}]'
sbx settings set kit.requireSignature true

7. Beginner Pitfalls

  • A kit's environment.variables should never include HTTP_PROXY, HTTPS_PROXY, or NO_PROXY — the sandbox manages those itself to route traffic through its policy-enforcing proxy. Overriding them routes traffic around that proxy, silently disabling network policy and credential injection for anything that uses them.
  • If governance (see Part 13) is active for your organization, a kit's own permissions.network.allow rules are ignored — only org-level allow rules grant access. A kit can still tighten things with deny rules, just not loosen them.
  • Signing covers spec.yaml and the files/ directory, but not anything referenced by a mutable tag, like a container image tag in a kind: sandbox kit. Pin image digests if you need that covered too.

Buy Me a Coffee

Found this tutorial helpful? Consider buying me a coffee to support more free content!