Cocoon logo

Rust · safe sandbox for AI agents

Watch a container
be born.

Cocoon is a safe code-execution sandbox for AI agents, and a rootless Linux container runtime you can read end to end. Give it a command, get back a structured Outcome: exit code, output, wall time, peak memory. Edit a bundle config and press Launch to watch the real isolation sequence, namespaces, pivot_root, dropped capabilities, seccomp, then the measured result an agent receives. No daemon, no root, one small binary that also speaks MCP.

Launch the simulation View on GitHub
How to use this playground

Edit the cocoon.conf bundle on the left, or pick a preset chip, then press Launch container to watch the isolation sequence play out: new namespaces, pivot_root, dropped capabilities, and the seccomp filter. Drag the timeline slider to step through it tick by tick, and read the measured Outcome an agent would receive at the end.

cocoon.conf

 

live isolation

host
0.0s

identity

filesystem

capabilities

seccomp

what the agent gets back
→ run_in_sandbox(…)

  

runs fully client-side, the same config parser and planner as the CLI, nothing leaves the page

One command in, one JSON Outcome out.

The sandbox runs the bundle and hands back the exact result an agent parses: exit code, captured output, wall time, and peak memory, on one line.

$ cocoon spec mybox # write cocoon.conf + rootfs/ skeleton
$ cocoon exec mybox --json # run it in the sandbox, print the measured Outcome
{"exit_code":0,"timed_out":false,"oom_killed":false,"wall_ms":6,
 "peak_mem_kib":2776,"stdout":"hello from the sandbox\n","stderr":""}

a chatty program never deadlocks: output is drained through pipes as the process runs, and a timeout kills an overrun and reports timed_out: true

Where Cocoon fits

An agent that runs code it just wrote needs isolation, a timeout, metering, and a result it can parse. The usual options sit on either side of that gap. This is an honest positioning, not a benchmark.

Docker

Needs a daemon and a root-ish setup, ships an image format and layers, and is heavy to embed next to an agent.

runc, bubblewrap

Bare isolation CLIs. They give you a namespace but no timeout, no metering, and no structured result, so you script that part yourself.

gVisor, Firecracker, a VM

Stronger isolation, but heavyweight, and a lot to stand up just to run one command and read its output.

Cocoon

The small gap in the middle: give it a command, get back a JSON Outcome. Rootless, no daemon, no image format, one small binary you drop next to your agent, and it speaks MCP. Not a drop-in for runc, and it says so.

what actually isolates the process

The primitives, each a few lines you can read

The whole isolation sequence lives in one file, src/exec_linux.rs. These are the pieces it composes, all without root.

namespace user

Maps your outer uid and gid to 0 inside the new user namespace. This is the move that lets everything below run with no sudo and no real privilege.

namespace mount + pivot_root

Makes the mount tree private, pivot_roots into the bundle rootfs, mounts a fresh /proc, binds a minimal /dev, and drops the old root.

namespace pid

The forked child is pid 1 in a new pid namespace and sees only its own processes, not the host's hundreds.

namespace uts

Its own hostname, set inside the container, independent of the host's.

namespace ipc

Isolates System V IPC and POSIX message queues so the process cannot reach the host's.

hardening dropped capabilities

The ambient set is cleared and the whole capability bounding set is dropped, so the code runs with an empty effective set (CapEff is 0) and cannot regain privilege.

hardening no_new_privs

Set before exec so a setuid binary or file capabilities can never raise privilege for the child or anything it spawns.

hardening seccomp filter

A curated deny-list returns EPERM for dangerous syscalls, including the whole mount family and pivot_root, so a read-only mount cannot be re-opened from inside.

It really isolates, rootless

scripts/prove.sh builds a busybox rootfs, runs a container as an ordinary user, and checks what the process inside sees. This runs in CI on every push.

# host is 'kali' at uid 1000. inside the container:
container said: host=cocoonbox pid=1 uid=0 procs=4
isolation: only 4 process(es) visible in the pid namespace (host has hundreds)
PROOF OK: rootless isolation verified

# and the hardening, checked inside a running container:
NoNewPrivs: 1          # cannot regain privilege
Seccomp:    2          # a seccomp filter is loaded

Different hostname, pid 1, uid 0 mapped from your real uid with no privilege used, its own filesystem and process view, capabilities dropped and a seccomp filter loaded. The whole runtime is one file, src/exec_linux.rs.

Use it

One small binary with a handful of subcommands, a structured result, an MCP server, and a dependency-free core you can call as a library.

CLI

spec writes a bundle skeleton, plan shows what a run will do on any OS, run forwards the exit code, exec runs it and returns the measured result.

exec --json

The full Outcome on one line: exit_code, stdout, stderr, wall_ms, peak_mem_kib, and the timed_out and oom_killed flags, ready for a caller to parse.

MCP server

cocoon mcp speaks JSON-RPC 2.0 over stdio and exposes one tool, run_in_sandbox, so Claude and other agents call the sandbox directly.

Profiles

strict for untrusted code (network off, read-only base, 5s, 128 MiB) and build for artifacts (writable, 5m, 1 GiB); any explicit key still wins.

Library core

The config, plan, and lifecycle core is dependency-free and portable, so it builds and tests anywhere, including macOS, with the Linux isolation pulled in only on Linux.

# the portable core builds and tests anywhere, including macOS
cargo test

# make a bundle skeleton, inspect the plan, run the container (Linux)
cocoon spec mybox
cocoon plan mybox        # works on any OS
cocoon run  mybox        # Linux only, no root