pyodide and monty runtimes run python3 in-process on your machine.
A sandbox runtime does the opposite: it takes a whole command line and
runs it inside a remote machine, a local Docker container or a cloud
sandbox. Reach for it when a line needs a real OS, heavy packages, or a GPU
that the in-process interpreters cannot give it.
Three sandbox runtimes ship from @struktoai/mirage-node, all with the same
surface:
Routing: what goes to the sandbox
A sandbox runtime does not claim every line. Each runtime captures a set of commands; a captured line runs in the sandbox, everything else stays on the local vfs. You list the runtimes in order, andvfs is the catch-all:
grep, cat, and ls run locally as always, while python3 train.py
runs on the cloud box. Mirage never creates, provisions, or deletes
sandboxes: you bring your own (a running container, a live Daytona or
E2B sandbox), the first captured line connects to it, and every captured
line execs inside it verbatim.
The workspace inside the sandbox
For the job to see your mounts as ordinary files, the sandbox must serve the workspace itself, and provisioning that is yours, like everything else about the sandbox. Run Mirage inside it, with the same mounts at the same prefixes as the host workspace, each FUSE-mounted at its own prefix:/data backed by S3
is a real directory. Because you write the sandbox-side config, it can
differ from the host’s where it should: the endpoint that is
127.0.0.1:9000 on your laptop is host.docker.internal:9000 from inside
a container, credentials can be scoped down, and none of it ever travels
over the provider’s exec API. The flip side is that keeping the prefixes
in step with the host workspace is your job; a sandbox serving different
mounts fails loud only when a path misses.
This needs an image with fuse3 and Mirage plus your backends installed
(see The sandbox image). The in-sandbox mount is
served by the Python Mirage build, so the image is the same for both
language SDKs.
Paths inside the sandbox
Mirage rewrites nothing: the line, its cwd, and every path in it pass through verbatim. With the sandbox serving the same prefixes,cd /data
then python3 train.py works, and so does an absolute path like
python3 /data/train.py, because /data means the same thing on both
sides.
The sandbox image
The sandbox needsfuse3 plus Mirage with the backends you mount. The repo
ships a Dockerfile that builds this image from the current checkout, so it
always matches your code:
image/snapshot source, or as an e2b
template base.
Docker
Start the container yourself, provision the workspace inside it, then pointDockerRuntime at it. Live FUSE mounts need --cap-add SYS_ADMIN --device /dev/fuse (Linux hosts) and the fuse image:
config: how to reach the sandbox. Each
provider’s config carries exactly the fields that provider needs, and an
unknown field fails loud. The docker CLI is the transport (Docker Desktop,
colima, or a podman alias), so there is no SDK and no daemon socket
wiring; the container gets real stdin and separated stderr. Sizing, GPUs,
networks, and binds are all yours: pass them to your own docker run.
Daytona and e2b
Create the sandbox with the provider’s own tooling (dashboard, CLI, or SDK), boot it from an image or snapshot carrying the fuse image, provision the workspace inside it (the provider’s exec or a terminal session), and hand mirage the id:Selecting in YAML
Sandbox runtimes are ordinaryruntimes entries: a name, its captures, and
a config block describing the machine (mirroring a mount’s config
block), with vfs as the in-process catch-all. Only the selected runtime
consumes its entry, so one file stays portable:
Resource limits
A captured line is a command like any other: the samecommand_safeguards
that guard cat or grep guard python3, including in the sandbox. A run
that exceeds timeoutSeconds answers exit 124; maxBytes and maxLines
cap its output the same way. There is no sandbox-specific limit surface.