Sandboxes
A sandbox is a Firecracker microVM that stays around between calls. Its root filesystem is a disk of its own, so files and installed packages survive a stop. It can mount other disks, serve ports, and run for up to 24 hours per session. After 30 minutes without a connection it pauses, keeping its memory and processes, and the next connection resumes it.
A long-lived dev box
Section titled “A long-lived dev box”Declare a sandbox in your Stack to keep it across deploys. Each mounts entry names a disk and a path:
export const Workspace = Archil.Disk("Workspace", {});
export const DevBox = Archil.Sandbox("DevBox", { baseImage: "node:24-bookworm", mounts: [{ disk: Workspace, path: "/workspace" }], ports: [3000],});A mount change updates the same sandbox: it stops, then starts with the new mounts.
Mount modes
Section titled “Mount modes”An entry takes Archil’s own options: mode (exclusive, conditional, or read-only), subdirectory, and queueMs:
export const Box = Archil.Sandbox("Box", { mounts: [ { disk: Workspace, path: "/workspace", mode: "conditional" }, { disk: Dataset, path: "/data", mode: "read-only", subdirectory: "exports" }, ],});exclusive (the default) lets one client write the whole disk. conditional lets several write at once. Branches and the shared mode belong to the FUSE client, so they work in mounts on your own hosts, not in a sandbox.
Create sandboxes at run time
Section titled “Create sandboxes at run time”Bind an image, then create a sandbox per task. A mount’s disk is a handle, such as one from CreateDisk:
export const Agent = Archil.Image("Agent", { image: "node:24-bookworm" });
const sandboxes = yield* Archil.CreateSandbox(Agent);const sandbox = yield* sandboxes.create({ name: "task-1", mounts: [{ disk: home, path: "/home/user" }], idleTtlSeconds: 300,});Run commands
Section titled “Run commands”exec runs a command to completion. A non-zero exit comes back in exitCode:
const result = yield* sandbox.exec("npm test", { cwd: "/home/user/app" });const upper = yield* sandbox.exec(["tr", "a-z", "A-Z"], { stdin: "hello" });Start a background process
Section titled “Start a background process”start returns a process whose output you can stream. Closing the scope detaches; the process keeps running, and attach picks it up again by id:
const server = yield* sandbox.start("npm run dev", { cwd: "/home/user/app" });yield* server.output.pipe(Stream.take(10), Stream.runDrain);Open a terminal
Section titled “Open a terminal”pty.open runs a shell on a pseudo-terminal:
const pty = yield* sandbox.pty.open({ cols: 120, rows: 40 });yield* pty.write("ls -la\n");yield* pty.resize({ cols: 160, rows: 50 });Move files
Section titled “Move files”yield* sandbox.files.write("/home/user/app/.env", "MODE=test");const env = yield* sandbox.files.readText("/home/user/app/.env");Archil’s process API moves whole files. List, remove or create directories with exec.
Pause, resume, fork
Section titled “Pause, resume, fork”yield* sandbox.pause();yield* sandbox.resume();const copy = yield* sandbox.fork({ name: "task-1-retry" });A fork copies the sandbox’s memory and disk into a new one. Archil only forks a sandbox with mounts while it is stopped. stop() and boot() stop and start it.
Serve a port
Section titled “Serve a port”A declared sandbox’s ports, or exposePort on any sandbox, serves a port at its own https://<port>-<id>.<region>.archil.app hostname. getTcpPort returns that port, and its fetch sends a request to it:
yield* sandbox.exposePort(3000);const port = yield* sandbox.getTcpPort(3000);const response = yield* port.fetch(HttpClientRequest.get("/health"));Pass fetch the request a handler received, and it proxies the method, path, query, headers and body, then returns the response:
return yield* port.fetch(yield* HttpServerRequest);Anyone with the URL can call a public port. Give a browser a port token instead, which it sends in the X-Archil-Token header:
const token = yield* sandbox.createPortToken(3000, "1h");Drive a declared sandbox
Section titled “Drive a declared sandbox”ConnectSandbox returns the same handle for a sandbox in your Stack:
const devbox = yield* Archil.ConnectSandbox(DevBox);yield* devbox.exec("pnpm build", { cwd: "/workspace" });Boot an image
Section titled “Boot an image”A sandbox boots node:24-bookworm unless you give it a baseImage (any public linux/amd64 image) or an image from Archil.Image, for private registries and Dockerfiles.
Run your own program
Section titled “Run your own program”Give a sandbox a main and it runs your program at a public URL. See Programs.