Zygo — a warm sandbox per request
Zygo forks a warm, sandboxed interpreter for every request: 1.4 ms through its API, and every request starts from a process that has never served one. Rootless, OCI images, and no daemon to install: the one long-lived process is a supervisor under your own user, not a system service.
This page is the first page of the Zygo book, also on the web at mhmtskrc2.github.io/zygo.
Try it now, once Zygo is installed — one program in a fresh sandbox, thrown away when it exits:
And what Zygo is for — a warm function, forked for every request:
Why
A warm worker that serves many requests is fast and dirty: request n sees
whatever request n-1 left behind. A container per request is clean and
slow. Zygo is the third thing. zygo serve starts an interpreter, lets it do
its imports, and parks it inside a sandbox. zygo exec forks it. Each child
has its own cgroup, deadline and secrets, and is thrown away afterwards.
The same import-heavy Python script, run fresh for each call on one host
(a Linux 6.8 VM, 2 vCPU, aarch64;
chapter 25 has the
method, and make bench-embed repeats it):
| usually | what it pays for | |
|---|---|---|
docker run --rm |
542 ms | daemon, containerd, shim, runc, a container object to remove |
zygo run |
70.8 ms | a fresh sandbox — namespaces, cgroup, mounts — in one process |
zygo exec |
2.8 ms | a fork() of the warm interpreter, plus starting the CLI |
Most of the 70.8 ms is Python importing those sixteen modules: the sandbox
itself is about 3.6 ms, and python3 -c pass inside one is 12 ms. Through
the HTTP API rather than the CLI, a warm request is 1.44 ms usually and
10.5 ms for 1 in 100, and one function sustains 1,108 requests a second.
zygo bench all reproduces every number on your own machine.
The zygote idea is older than Zygo: Android starts apps that way, and serverless research forked handlers from a pre-imported process in SOCK (Oakes et al., USENIX ATC 2018) and restored them from a snapshot in Catalyzer (Du et al., ASPLOS 2020). Zygo's part is the packaging — one static binary, rootless, every limit on, an agent protocol any language can speak — not the idea.
Why not bubblewrap, nsjail, nono, sandbox-runtime or kern?
They are good at what they do, and none of them keeps a sandboxed process warm and forks it per request.
| What it is | What Zygo adds | |
|---|---|---|
| bubblewrap, nsjail | building blocks for one confined process | images, mandatory limits, an egress allowlist — and the warm fork |
| nono, sandbox-runtime | confinement for a command you were running anyway (Landlock and seccomp, or bubblewrap and Seatbelt) | a sandbox with its own root filesystem and limits, for code you did not write |
| kern | a daemonless, rootless container per call, in a few ms | a warm interpreter: no interpreter start and no imports on the request path |
| E2B, Modal, Daytona | a microVM or gVisor per session, in their cloud | runs on your hardware, and costs a fork rather than a VM per call |
| Sandlock, Zeroboot | a copy-on-write fork of a Landlock-confined process, or of a Firecracker snapshot | a fork that lands in a sandbox with its own root, pid namespace, cgroup and network — and the tenants, secrets and API around it |
Similar projects compares all of them but sandbox-runtime, with measurements against nsjail and kern.
The boundary
The default backend, ns, is the host kernel: namespaces, cgroup v2, a
seccomp allowlist, Landlock, no capabilities and a read-only root. That is
the right wall for code that is semi-trusted — your customers' scripts,
an agent's tools. A kernel bug is a way through it, as it is for every
container. The same spec also runs on gvisor (a kernel in user space) or
vm (libkrun), one-shot only. Neither is a full wall for hostile code yet.
Both lack a network. A rootless gvisor cannot enforce its limits, and vm
has none inside the guest and a kernel you build yourself. For anonymous
code, the honest answer today is a separate machine.
make escape-linux attempts 21 of the vectors in
the threat model, with 0 escapes, and every
syscall number is swept against the seccomp profiles. The same chapter lists
the tenant-against-tenant vectors not attempted yet, and says where the
boundary is weaker than it looks. No external audit has been done.
Fork safety, question by question covers what
a fork shares with its parent and what it does not.
Install
# Linux, x86_64 or aarch64: one static binary, checked against the release's checksums
url=https://github.com/mhmtskrc2/zygo/releases/latest/download
|
&&
Linux needs kernel 5.3 or newer (6.1 recommended), unprivileged user namespaces and delegated cgroup v2. On a Mac every command runs in a Linux VM, about 22 ms away. There is also a signed container image. Getting started covers all of it, signatures included.
&&
What else is in the box
- One file per project.
sandbox.tomldeclares functions, their images, limits, network and secrets;zygo updeploys it blue/green and pins image digests inzygo.lock. Chapter 20 - Every limit is on by default — memory, CPU, pids, wall clock, scratch, open files — and the deadline kills the request's whole process tree.
- The network is off by default.
egressis an allowlist of names; private ranges and the cloud metadata address stay closed. Chapter 14 - Secrets are files, written from outside the sandbox for one request: never environment variables, never in the warm process's memory.
- Any language. Python and Node agents ship; anything else is a fresh process per request in a held sandbox (about 1.4 ms), or an agent of your own against the protocol.
- For programs: an HTTP API, dependency-free Python and Node clients
(
zygo-sdk), an Elixir client (zygo_sdk), and an MCP server. Chapter 17 - For a multi-tenant product: tenants with their own tokens and budgets, a script or a whole workspace (a tar) sent with each request, streamed output, and cancellation — all over the API.
Building on it? Read 13 (warm functions), then 17 (the API, the SDKs, MCP), then 23 (the threat model), in that order.
Status
v0.1.x: one machine; Linux in production, macOS for development. The warm
path is ns-only by decision (ADR 0002).
ROADMAP.md says what comes next.
Not for: an interactive session or a REPL (a request is one call, not a shell you keep); more than one machine (no scheduler, no cluster); GPUs; Windows without WSL2.
More
Contributing · Security policy · Changelog · Roadmap · Licence: Apache-2.0
Zygo is not affiliated with Zygo Corporation, the metrology company.
Next: The Zygo book → · in the repository