smolvm sandbox
@sixb/sandboxes-smolvm runs each agent's bash inside a hardware-isolated
smolvm microVM. Reach for it in production, or whenever
you want stronger isolation than the local provider's OS sandboxing: every run gets its
own VM with a real per-host network allow list, and the guest filesystem and processes are fully
separated from the host.
import { createSixb } from "@sixb/core"
import { SmolvmSandboxFactory } from "@sixb/sandboxes-smolvm"
createSixb({ sandboxes: new SmolvmSandboxFactory() })
It implements the same Sandbox / SandboxFactory contract as every provider — see the
overview. Swapping in this factory is the only code change.
How a run works
Per agent run the factory creates a machine, boots it from an image, runs the agent's bash through
smolvm machine exec, then stops and deletes the machine on teardown. The guest filesystem is fully
isolated from the host — there is no bind mount. Files the worker needs in the guest (skills, run
context) are materialized in-guest by writeFiles, which executes a short script inside the VM
that base64-decodes each payload into place under the working directory.
Boot is fast (well under the model's first-response latency), so the VM is ready before the agent asks for it.
Preflight requirements
Host availability is probed once, lazily, on the first create(). If a requirement is missing,
create() throws a SandboxIsolationUnavailableError with a message telling you exactly what to do.
Requirements:
- The
smolvmbinary must be onPATH(or pass an absolute path viabin). - On Linux,
/dev/kvmmust be present (KVM virtualization). macOS uses Hypervisor.framework and is not probed beyond the binary.
Install the binary once:
curl -sSL https://smolmachines.com/install.sh | bash
The agent image
The VM boots from an OCI image that contains the tooling the agent uses. The package ships a
canonical agent image — Alpine plus bash curl jq git ripgrep python3, roughly 73 MB — kept lean so
boot stays fast.
Build it once with Docker or Podman. The build is the only step that needs a container builder; every run after reads the cached archive offline.
bun run agent:image
This builds agent-image/Dockerfile and caches the archive at
~/.cache/sixb/smolvm/sixb-agent.tar (honoring XDG_CACHE_HOME). To add tools, edit the Dockerfile
and rebuild — keep it lean, and remember run-time installs will not work because egress is locked to
the gateway.
For a server that has no Docker, cross-build the image elsewhere and copy it over. The build writes an arch-suffixed file so it does not clobber the host build:
bun run agent:image --platform linux/amd64
# Built agent image -> ~/.cache/sixb/smolvm/sixb-agent-amd64.tar
scp ~/.cache/sixb/smolvm/sixb-agent-amd64.tar server:/opt/sixb/agent.tar
The server then needs only the smolvm binary and the .tar.
Image modes
The image option selects what the VM boots from:
image value | Mode | Behavior |
|---|---|---|
undefined (default) | Managed archive | The cached sixb-agent.tar (or a cross-built sixb-agent-<arch>.tar in the cache). Offline, fast, strict egress |
a local .tar / .tar.gz / .tgz path | Local archive | Loaded at start with no network; boots fully offline |
a registry reference (e.g. "node:22") | Registry pull | Pulled from the registry at start, from inside the guest |
null | Bare machine | smolvm's built-in busybox rootfs; no image, fully offline |
A registry-pull image is the only mode that needs network at boot: the pull happens inside the guest,
so the machine must be able to reach the registry. With network.mode: "none" the pull fails — use a
local archive or a bare machine for fully offline boot, or a restricted policy (see below) so the
registry hosts are reachable.
Network and allow-hosts
The provider translates SandboxNetworkPolicy into smolvm flags:
{ mode: "none" }— the VM gets no network.{ mode: "restricted", allow: [...] }—--netplus one--allow-hostper allowed origin.{ mode: "all" }—--netwith no allow list (discouraged in production).
This is the provider that actually enforces the contract's per-host allow list — it is strictly stronger than the local backend's all-or-nothing network toggle.
Two security notes worth knowing:
--allow-hostis hostname-granular, not port-scoped. smolvm allows a hostname (resolved at VM start) with no port, so an allowed host is reachable on any port. If the gateway shares a host with other services, isolate it on its own host/IP when per-port egress matters.- A microVM has its own network stack. A gateway advertised on
localhostis the VM's loopback, not your host, so it is unreachable under restricted egress. Point the API at the host's LAN IP (e.g.SIXB_API_PUBLIC_ORIGIN=http://<host-lan-ip>:3002). The provider warns once if it sees alocalhosttarget. In production the gateway is already a real address, so this does not apply.
When the image is a registry reference, the provider automatically augments a restricted policy
with the registry hosts (Docker Hub by default) so the in-guest pull at start can reach the registry
while staying scoped. Override the hosts with registryHosts for other registries.
Options
Pass these to new SmolvmSandboxFactory({ ... }):
| Option | Type | Default | Notes |
|---|---|---|---|
image | string | null | managed cached archive | See image modes. A path is a local archive; a registry ref pulls at boot; null is a bare machine |
bin | string | "smolvm" | smolvm binary name (resolved on PATH) or an absolute path |
storageGiB | number | smolvm default (20) | --storage: OCI layers + container data |
overlayGiB | number | smolvm default (2) | --overlay: persistent rootfs changes; raise to avoid "no space left" |
registryHosts | string[] | Docker Hub hosts | Registry hosts added to a restricted policy so a registry-pull image can fetch at start |
env | Record<string, string> | {} | Default env merged into every sandbox the factory creates |
timeout | number | — | Default per-command timeout in milliseconds |
network | SandboxNetworkPolicy | { mode: "none" } | Default network policy; overridable per create() |
env, timeout, and network can be overridden per run by the worker; the rest are factory-level.
Configuration examples
Default — managed cached image, gateway-only egress applied by the worker:
new SmolvmSandboxFactory()
Point at a prebuilt archive copied onto a production server:
new SmolvmSandboxFactory({ image: "/opt/sixb/agent.tar" })
Pull a public registry image at boot, allowing GitHub Container Registry hosts:
new SmolvmSandboxFactory({
image: "node:22",
registryHosts: ["ghcr.io", "pkg-containers.githubusercontent.com"],
})
Bare machine (no image), with a larger writable overlay:
new SmolvmSandboxFactory({ image: null, overlayGiB: 8 })
Related
- Sandboxes overview — the shared contract and network model
- Local sandbox — OS-level isolation for development
- Agent tools and the gateway — what the bash tool reaches