MicroVM sandboxes for AI agents, built on cocoon: a fast-boot guest stack, an in-guest product daemon, a per-node control plane with warm pools, and SDKs for Go and Python. Warm claims are sub-millisecond; a pool miss clones from a golden snapshot in tens of milliseconds; cold boot is ~0.2–0.4 s on bare metal.
Documentation: cocoonstack.github.io/sandbox
(source in docs/).
SDK (Go/Python) sandboxd (per node) guest microVM
client.New/new ─ HTTP ─► claim: warm pool / golden clone Cloud Hypervisor
exec/files/… ─ HTTP upgrade (kept) ─► byte relay ─ vsock ─► silkd :2048
memberlist mesh: warm-count gossip,
MOVED-style redirect to the owning node
Cloud Hypervisor serves both network lanes. net=none has no NIC and uses
vsock-only I/O (hardened default); net=egress attaches a bridge/CNI NIC.
On a Linux node with KVM, cocoon v0.6.9
or newer, Cloud Hypervisor and jq, as root, from a checkout with Go installed.
Not on a node that already runs sandboxd on the same cocoon root: at start
sandboxd removes every sbx- VM it does not own.
make sandboxd
cocoon image pull ghcr.io/cocoonstack/sandbox/rt:24.04
cat >config.json <<'EOF'
{
"listen": "127.0.0.1:7777",
"api_token": "change-me",
"pools": [
{"template": "ghcr.io/cocoonstack/sandbox/rt:24.04", "net": "none", "size": "small", "warm": 2}
]
}
EOF
dist/sandboxd -config config.json &
# Wait for the golden snapshot and the warm pool.
until curl -sf -H 'Authorization: Bearer change-me' http://127.0.0.1:7777/v1/info |
jq -e '(.pools | length) > 0 and all(.pools[]; .golden and .warm >= .target)' >/dev/null; do sleep 1; done
# Claim, exec and release three times; prints each latency.
(cd e2e && GOWORK=off go run ./cmd/demo -addr 127.0.0.1:7777 -token change-me \
-template ghcr.io/cocoonstack/sandbox/rt:24.04)A stopped sandboxd leaves its VMs for the next start. To remove them:
kill %1
cocoon vm list --format json | jq -r '.[].config.name | select(startswith("sbx-"))' |
while read -r vm; do cocoon vm stop --force "$vm"; cocoon vm rm --force "$vm"; donedemo is a small program over the Go SDK; the
Python SDK has the same calls. Deploying
sandboxd covers release binaries, the full config and systemd.
- cocoon — the microVM engine underneath
- vk-sandbox — the Kubernetes provider that claims from sandboxd
- sandbox-operator — the Kubernetes apiserver and e2b-compatible data plane over warm pools
silkd/— in-guest product daemon (Rust), baked into the base image; newline-JSON frames over vsock 2048 (reference)sandboxd/— per-node control plane (Go): warm pools, the claim API and the relays to silkd (deploy, API)sdk/go/— Go SDK;sdk/go/silkd/is its connection layer andsdk/go/silkd/silkdtest/a test fakesdk/python/— Python SDK, stdlib-only and synchronousmcp/—sandbox-mcp, an MCP stdio serversdk/openai/—cocoonstack-sandbox-openai, a sandbox provider for the OpenAI Agents SDKsdk/langchain/—cocoonstack-sandbox-langchain, a LangChain toolkitprotocol/wire/— the frame vocabulary andfixtures/, the golden corpus the Rust, Go and Python tests all round-trip, so wire drift fails CIe2e/— in-process full-stack tests (real pool/engine/relay/SDK, fake cocoon + guest) and the bare-metal driversboot/kernel/— kernel version pin (VERSION+ matching tarballSHA256, bump both together) and config fragmentsboot/init/—sandbox-init, the entire initramfs userland (static Rust)boot/Dockerfile— multi-stage: kernel → init → cpio → scratch image with/boot/vmlinuz-sandbox+/boot/initrd.img-sandboxos-image/— VM images consuming the boot artifact:base,rt,python,python-rt,node,node-rt,browser,android,e2b-rt,e2b-ciscripts/— benchmarks and bare-metal e2e, see Verifying a nodepackaging/— the systemd unit deploy installs
silkd.yml/sandboxd.yml— Rust and Go test+lint suitesboot-init.yml— the boot/init crate's own fmt+clippy+test gatepython.yml— ruff + pytest for the three Python packages, mypy for the base SDKshell.yml— shellcheck over every tracked shell scriptimages.yml— the single image entry point: on a push touchingboot/**,silkd/**, oros-image/**it builds the changed carriers (viabuild-boot.yml/build-silkd.yml,workflow_call) then os-images, in order, so the chain is deterministicbuild-os-images.yml— bakes base + flavors FROM the sha-pinned carriersrelease.yml— on a version tag, checks the tag against silkd's version, then builds the release binaries and archivespublish-pypi.yml— on ansdk-*-v*tag, builds and publishes the matching package via PyPI Trusted Publishing (OIDC, per-package environment)
On a fresh repo run build-boot first — images build FROM the boot artifact.
cloud-hypervisor
→ guest kernel (amd64: PVH ELF vmlinux, everything =y, no decompress stage; arm64: flat Image)
→ uncompressed ~1.5MB cpio: /init = sandbox-init (static Rust)
→ resolve virtio-blk serials via sysfs (2ms poll, no udev)
→ mount EROFS layers → overlayfs + ext4 COW → switch_root
→ exec /sbin/init (systemd, trimmed; cocoon-agent + silkd start at sysinit)
The cmdline keys sandbox-init consumes are in the
boot contract.
make help # every target; the ones below are the daily set
make lint test # Rust: boot/init + silkd (fmt --check, clippy -D warnings, tests)
make go-lint # Go: protocol/wire + sandboxd + sdk/go + e2e + mcp, GOOS linux AND darwin
make go-test # Go: go test -race across the Go modules
make sh-lint # shellcheck every tracked shell script
make sandboxd # build dist/sandboxd
make bench # claim-tier + data-plane benchmarks; removes every sbx-* VM
make boot # kernel + initramfs artifact image (docker)
make silkd-image # silkd release binary in a scratch carrier image
make images # base + python images against the local boot + silkd imagesThe server stack — sandboxd, silkd, the boot chain, the OS images, and the
MCP server — is a product, and is licensed under AGPL-3.0. The client SDKs
(sdk/go, sdk/python, sdk/openai, sdk/langchain) and the
protocol/wire module the Go SDK requires are licensed under Apache-2.0
(see the LICENSE file in each directory) and carry no copyleft obligation.