Skip to main content

Sandbox

Create, run, clean up — the basic loop

  • Sandbox.create(options?) — boots a sandbox. options.name is a caller-given identity, unique among live sandboxes and held snapshots (409 if already taken) — see byName/getOrCreate below to find it again later. options.tags, options.baseUrl, options.authToken, options.vcpuCount, options.memSizeMib (override the daemon’s configured defaults, subject to its ceiling), options.imageId (boots from a registered image instead of the default rootfs — see Image), options.rateLimit ({ bandwidthBytesPerSec?, opsPerSec? }, Firecracker’s own token-bucket I/O limiter — unlimited if omitted), options.drives (existing persistent drives to attach, see Drive). A call with no drives/rateLimit matching a configured pool’s image/resources transparently resumes a warm snapshot instead of cold-booting — see Pool.
  • sandbox.runCommand(command, args?) — returns { stdout, stderr, exitCode }.
  • sandbox.readFile(path) — returns file contents as Uint8Array.
  • sandbox.writeFile(path, content) — content is a string or Uint8Array.
  • sandbox.chmod(path, mode), sandbox.chown(path, uid, gid), sandbox.mkdir(path, options?) ({ parents?: true }), sandbox.rename(from, to), sandbox.copy(from, to), sandbox.symlink(target, linkPath), sandbox.readlink(path), sandbox.truncate(path, size), sandbox.listDir(path) (returns entries with real isDir/isSymlink/size/mode/mtime metadata) — the full filesystem operation set, all over the same vsock channel runCommand uses.
  • sandbox.pty(options?) — opens a live, interactive shell session and returns a native WebSocket (no added runtime dependency — needs Node.js ≥22 or a browser). options.cols/options.rows size the terminal once, at open time; no live resize yet. Distinct from runCommand’s request/response shape — see kiln sandbox pty for the CLI equivalent.
  • sandbox.stop(options?) — stops the sandbox. Default (options.keep omitted or true) preserves state as a resumable snapshot, returns { kept, snapshotId }. { keep: false } fully destroys instead.

Attaching to an id you already have

  • Sandbox.attach(id, options?) — wraps an already-existing sandbox id without a network round-trip.

Named sandboxes — find the same environment again later

  • Sandbox.byName(name, options?) — resolves a name to a live sandbox. Rejects (409) if the name currently belongs to a stopped (snapshotted) sandbox instead — use getOrCreate for that.
  • Sandbox.getOrCreate(options) — resolves options.name to a sandbox in one race-safe call: live as-is, resumed if stopped, created fresh otherwise. Returns { sandbox, created }.

Listing and filtering

  • Sandbox.list(options?) — lists sandboxes. options.tags filters by exact match on every given key.

Dev-server preview

  • sandbox.previewUrl(port, options?) — the URL a browser can open to reach a server listening on port inside the sandbox.

Local tunnel

The reverse of preview: expose a service running on this machine to code inside the sandbox.
  • sandbox.tunnel(guestPort, options) — options.localPort (required), options.localHost (default 127.0.0.1). Node-only (forwarding to a real local socket needs node:net, which doesn’t exist in a browser). Returns a TunnelHandle with .tunnelId and .close(). See Local tunnel.

Streamed background exec

runCommand waits for the process to exit and returns everything at once — for a long-running command you want to watch live instead, start it in the background and attach a WebSocket to its output:
  • sandbox.execStream(command, args?) — starts command in the background, returns a session id immediately.
  • sandbox.attachLogs(sessionId) — returns a WebSocket (requires Node.js >= 22 or a browser’s built-in WebSocket) that replays buffered output then live-tails new output, whether or not the process has already finished.
  • sandbox.listExecStreams() — lists every streamed session tracked against this sandbox, running or finished.
  • Not carried across resume()/fork(), and doesn’t survive the daemon restarting — a fresh sandbox (or one just resumed) has no sessions yet.

Snapshot, resume, fork

  • sandbox.snapshot() — saves full state to disk and stops; returns a snapshot id.
  • Sandbox.resume(snapshotId, options?) — boots from a snapshot, consuming it.
  • Sandbox.fork(snapshotId, options?) — boots from a snapshot without consuming it. 409 while an earlier fork is still live.
  • Sandbox.listSnapshots(options?) — lists snapshots. options.sourceSandboxId narrows to the one taken from that original sandbox id.

Remote storage mounts

  • sandbox.mount(options) — mounts an S3-compatible bucket at options.mountPath via rclone mount. options.endpoint is the full URL of the S3-compatible service, never defaulted. Needs the optional FUSE-capable rootfs setup in SELF_HOSTING.md; throws SandkilnApiError on a guest-side mount failure. Not carried across resume()/fork() — it’s a live guest FUSE process, already captured by Firecracker’s own snapshot.
  • sandbox.listMounts() — lists every remote-storage mount currently active in this sandbox.
  • sandbox.unmount(mountId) — tears down one mount. Best-effort on the guest side — a failed umount is a daemon-log warning, not a failed request.