Sandboxes
A sandbox is an isolated environment where an agent reads files and runs bash commands. Reach for
one whenever an agent needs file work, scripts, or curl against the sixb
API gateway. The sandbox keeps that work off your host — its filesystem, network, and processes
are walled off from the machine the runtime runs on.
You pick a provider once and wire it into createSixb. Everything above the sandbox — the agent,
its sandbox tools, its run lifecycle — is written against one provider-agnostic contract, so
swapping providers never touches agent code.
The contract
Two interfaces define the whole surface. A SandboxFactory holds the provider's defaults and is
passed once to createSixb. A Sandbox is one isolated environment that runs commands.
interface SandboxFactory {
create(options?: CreateSandboxOptions): Promise<Sandbox>
}
interface Sandbox {
readonly id: string
readonly provider: string // "local" | "apple-container" | "smolvm" | "vercel"
readonly status: "running" | "stopped" | "failed"
readonly workingDirectory: string
runCommand(
command: string,
args?: readonly string[],
options?: RunCommandOptions
): Promise<CommandResult>
// Materialize files into the sandbox; a later runCommand can read them. Parent dirs are created.
writeFiles(files: readonly SandboxFileRecord[]): Promise<void>
stop(): Promise<void> // mark stopped; later runCommand rejects. Idempotent.
destroy(): Promise<void> // stop and reclaim provider resources. Idempotent.
}
The worker calls factory.create() once per agent run, writeFiles(...) to install the run's
skills and context, and runCommand(...) per command, then destroy() on teardown.
File materialization
writeFiles is how bytes get into a sandbox — the worker never writes to the host filesystem
directly. Each provider decides how a SandboxFileRecord ({ path, contents, mode? }) reaches the
guest: the local provider writes straight to the host filesystem it shares with the guest, while
smolvm executes an in-guest script that decodes the payload inside the VM. The only guarantee the
contract makes is observable: after writeFiles, the files exist at their paths for a subsequent
runCommand.
runCommand resolves with a CommandResult rather than throwing on a non-zero exit — a failed
command is data, not an exception:
| Field | Meaning |
|---|---|
exitCode | Process exit code (0 on success) |
stdout | Captured standard output |
stderr | Captured standard error |
durationMs | Wall-clock run time |
timedOut | true when the command was killed for exceeding its timeout |
RunCommandOptions overrides the sandbox-level defaults for a single call:
| Option | Meaning |
|---|---|
cwd | Working directory for this command |
env | Env merged on top of the sandbox env; per-call wins on collision |
timeout | Timeout in milliseconds; on expiry the command is killed and timedOut is set |
signal | An AbortSignal to cancel an in-flight command |
CreateSandboxOptions sets the per-run defaults at create() time: workingDirectory, env,
timeout, and network.
Agent image requirements
The Sandbox contract is command-agnostic, but an image used by the Sixb agent worker must provide
the commands used by its built-in tools:
bashruns both built-in sandbox tools.curllets the agent call the scoped Sixb API gateway.realpath,tail,head, andbase64power the bounded, binary-safereadimplementation.
Stock or canonical provider images include these commands; the local provider uses the copies on
the host's PATH. Bake them into custom images and snapshots rather than installing them on each
run. The read tool probes its four supporting commands and returns a clear error naming a missing
command.
Wiring
Construct a factory and pass it as sandboxes:
import { createSixb } from "@sixb/core"
import { LocalSandboxFactory } from "@sixb/sandboxes-local"
export const sixb = await createSixb({
// ...broker, storage, queues, ontology, agents
sandboxes: new LocalSandboxFactory(),
})
Switching to stronger isolation is a one-line change — the rest of the app is unaffected:
import { SmolvmSandboxFactory } from "@sixb/sandboxes-smolvm"
createSixb({ sandboxes: new SmolvmSandboxFactory() })
Or use Vercel-hosted Firecracker microVMs when the agent worker already has Vercel Sandbox credentials and the Sixb API gateway is reachable from Vercel:
import { VercelSandboxFactory } from "@sixb/sandboxes-vercel"
createSixb({ sandboxes: new VercelSandboxFactory() })
On Apple silicon Macs, Apple Container is another local option:
import { AppleContainerSandboxFactory } from "@sixb/sandboxes-apple-container"
createSixb({ sandboxes: new AppleContainerSandboxFactory() })
Network policy
Every provider speaks the same SandboxNetworkPolicy. It is set per-run at create(...) (or as a
factory default) and governs what the sandbox can reach over the network:
| Mode | Meaning |
|---|---|
{ mode: "none" } | No outbound network (the default when none is set) |
{ mode: "restricted", allow: [...] } | Only the listed origins are reachable |
{ mode: "all" } | Unrestricted egress (discouraged in production) |
Each restricted entry is a { name, origin } target, for example
{ name: "sixb-api", origin: "http://10.0.0.5:3002" }.
Providers differ in how precisely they can enforce restricted. The smolvm provider
enforces a real per-host allow list inside the microVM. The Vercel provider maps
restricted HTTPS origins to Vercel's TLS/SNI firewall and IP origins to CIDR rules. The
local and Apple Container providers are all-or-nothing today:
none blocks outbound network, any other mode allows host/default network. The contract is the same;
read each provider page for what it actually enforces.
How agents use a sandbox
You rarely call runCommand yourself. The agent worker does it:
- When a run starts, the worker calls
factory.create(...)with a restricted network policy whose only allowed origin is the sixb API gateway. The agent can reach the gateway and nothing else. - Sandbox boot overlaps the model's first response — it is provisioned concurrently and each sandbox tool awaits it lazily on first use, so boot latency does not block the turn.
- Each
readcall runs a fixed, bounded script with model input passed as command arguments. Eachbashcall becomesrunCommand("bash", ["-lc", script], ...). - On run teardown the worker calls
destroy().
Because egress is locked to the gateway, the agent's only way to read or write app data is through that gateway — there is no open internet. See Agent tools and the gateway for what the gateway exposes.
Choosing a provider
| Provider | Package | Isolation | Use when |
|---|---|---|---|
| Local | @sixb/sandboxes-local | OS sandboxing (seatbelt / bwrap) or passthrough | Development and local iteration |
| Apple Container | @sixb/sandboxes-apple-container | Local Apple Container runtime | Local Mac testing with container isolation |
| smolvm | @sixb/sandboxes-smolvm | Hardware-isolated microVM | Production on hosts where you can run smolvm |
| Vercel | @sixb/sandboxes-vercel | Vercel-hosted Firecracker microVM | Production on Vercel, or hosted workers with Vercel Sandbox credentials |
Rule of thumb: local or Apple Container for dev, smolvm or Vercel for stronger isolation in prod. The local provider is friction-free and always boots. Apple Container gives Mac users a local containerized runtime. smolvm gives each run its own microVM with a true per-host egress allow list. Vercel gives you remote managed microVMs, but the Sixb API gateway must be reachable from Vercel (usually a public HTTPS origin, not localhost).
Related
- Local sandbox — OS-level isolation backends and auto-detection
- Apple Container sandbox — local Apple Container-backed sandboxes
- smolvm sandbox — hardware-isolated microVMs
- Vercel sandbox — managed Vercel-hosted microVMs
- Agent tools and the gateway — how sandbox tools reach files and the API gateway