Concepts
The two things lightbox manages — snapshots and sandboxes — and how they relate.
Two concepts
Lightbox is built around two things:
- Snapshot — a frozen disk image built from a base OCI image plus your setup steps. Lives on disk under
~/.microsandbox/snapshots/<name>. Reusable forever. Built once, mounted on every launch. - Sandbox — a running microVM booted from a snapshot. Hardware-isolated (its own Linux kernel). Boots in under 100 ms because the snapshot’s upper layer is pre-populated. You exec commands in it, then stop and remove it — or detach and let it keep running.
If you know Docker, the mapping is almost 1:1:
| Docker | lightbox |
|---|---|
docker build | buildSnapshot() |
docker image ls / docker image rm | listSnapshots() / removeSnapshot() |
docker run | launchSandbox() |
docker exec | sb.exec() / sb.shell() |
docker ps / docker rm | Sandbox.list() / removeSandbox() |
docker volume | named volumes in mounts |
The big difference is isolation: Docker containers share the host kernel; lightbox sandboxes each run their own kernel in a microVM. Boot is comparably fast because the snapshot’s filesystem is already populated — there’s no per-launch install cost.
The middle box — the builder VM — is an implementation detail you usually don’t think about: buildSnapshot() boots a throwaway sandbox named <your-snapshot>-builder, runs your setup steps inside it, takes a snapshot of the result, and removes the builder. You only see it if you opt into debugBuilder: true to keep it around for inspection.
Volumes
A third, smaller concept: named volumes. They’re persistent storage you can mount into sandboxes — package caches, model weights, build artifacts. They survive sandbox restarts and can be mounted into many sandboxes at once. Created on demand when first referenced in launchSandbox’s mounts option. Same idea as Docker volumes.
Sandbox lifecycle ownership
A sandbox booted with launchSandbox is owned by the process that launched it: when that process exits, the SDK stops the VM. Three patterns cover almost everything:
| Pattern | Use when |
|---|---|
const sb = await launchSandbox(opts) + await sb.detach() | You want the VM to outlive this script. Connect later from another process with connectToSandbox(name). |
runInSandbox(opts, async (sb) => { ... }) | Scoped, one-off workloads. The sandbox is stopped after the callback resolves (or throws). |
const sb = await launchSandbox(opts) (no detach, no run helper) | The script keeps running and drives the sandbox itself; on exit the SDK cleans up. |
How the runtime is provisioned
The library depends on the official microsandbox Node SDK, which ships its own msb host binary and libkrunfw for the supported platforms. On the first call into the SDK, it extracts those artifacts into ~/.microsandbox/ (via the memoized ensureRuntime() helper). Subsequent calls are instant — no global msb install needed on the host.