Skip to main content
Create and control a microVM sandbox: boot it from an image, run commands, stream logs and metrics, then shut it down. See Overview for configuration examples and Lifecycle for state management. The TypeScript SDK uses a builder-only entry point. Start every new sandbox from Sandbox.builder(name) and chain configuration calls before terminating with .create(). Use .detached(true) before .create() for detached/background mode. Sandbox names must be non-empty and no longer than 128 UTF-8 bytes. libkrunfw selection is process-level, not per sandbox. To use a custom library, call setRuntimeLibkrunfwPath(path) before creating local sandboxes, set MSB_LIBKRUNFW_PATH, or configure paths.libkrunfw.

Typical flow

import { Sandbox } from "microsandbox";

await using sandbox = await Sandbox.builder("api")  // 1. configure
  .image("python")
  .memory(1024)
  .create();                                        // 2. boot the microVM

const out = await sandbox.exec("python", ["-V"]);   // 3. run
console.log(out.stdout);

await sandbox.stop();                               // 4. shut down

Static methods

Sandbox.builder()

static builder(name: string): SandboxBuilder
await using sandbox = await Sandbox.builder("api")
  .image("python")
  .create();
Begin building a new sandbox. Configure it with chainable setters, then call .create() to boot it. Sandbox names must be non-empty and no longer than 128 UTF-8 bytes. See SandboxBuilder for all available options.

Parameters

namestring
Sandbox name, up to 128 UTF-8 bytes.

Returns

Fluent builder for configuring the sandbox.

Sandbox.get()

static get(name: string): Promise<SandboxHandle>
const handle = await Sandbox.get("api");
console.log(handle.status);
Get a live handle to an existing sandbox (running or stopped). The handle provides status, configuration, and lifecycle control without requiring a full connection to the guest agent.

Parameters

namestring
Sandbox name, up to 128 UTF-8 bytes.

Returns

Live handle with status and lifecycle control.

Sandbox.list()

static list(): Promise<SandboxHandle[]>
for (const h of await Sandbox.list()) {
  console.log(`${h.name} - ${h.status}`);
}
List all sandboxes (running, stopped, and crashed). Handles returned from list() are read-only - call Sandbox.get(name) to get a live handle for lifecycle calls.

Returns

All sandboxes (read-only handles).

Sandbox.listWith()

static listWith(filter: { labels?: Record<string, string> }): Promise<SandboxHandle[]>
const workers = await Sandbox.listWith({ labels: { role: "worker" } });
List sandboxes filtered to those carrying all of the given labels (AND-matched). Like list(), the returned handles are read-only.

Parameters

filter{ labels?: Record<string, string> }
Labels that a sandbox must all carry to be included.

Returns

Matching sandboxes (read-only handles).

Sandbox.remove()

static remove(name: string): Promise<void>
await Sandbox.remove("api");
Delete a stopped sandbox and all its state from disk (configuration, logs, runtime directory). Fails if the sandbox is still running - stop it first.

Parameters

namestring
Sandbox name, up to 128 UTF-8 bytes.

Sandbox.start()

static start(name: string): Promise<Sandbox>
await using sandbox = await Sandbox.start("api");
Restart a previously stopped sandbox. The VM reboots using the persisted configuration. The sandbox enters attached mode - it stops when the binding goes out of scope (via await using) or when stop() is called.

Parameters

namestring
Name of a stopped sandbox, up to 128 UTF-8 bytes.

Returns

Running sandbox.

Sandbox.startDetached()

static startDetached(name: string): Promise<Sandbox>
const sandbox = await Sandbox.startDetached("worker");
Restart a stopped sandbox in detached mode. The sandbox survives after your process exits.

Parameters

namestring
Name of a stopped sandbox, up to 128 UTF-8 bytes.

Returns

Running sandbox.

Instance methods

A running Sandbox also exposes two read-only properties: name (string, the sandbox name) and ownsLifecycle (boolean, true in attached mode where the auto-disposer stops the sandbox, false in detached mode). Command execution (exec, execWith, execStream, execStreamWith, shell, shellStream) and foreground attachment (attach, attachWith, attachShell) live on the Execution page.

sandbox.config()

config(): Promise<SandboxConfig>
const config = await sandbox.config();
console.log(`${config.memoryMib} MiB`);
Get the full configuration the sandbox was created with - image, cpus, memory, env, mounts, and the rest. The shape mirrors SandboxBuilder.build().

Returns

Sandbox configuration.

sandbox.detach()

detach(): Promise<void>
await sandbox.detach(); // keeps running in the background
Release the handle without stopping the sandbox. The sandbox continues running as a background process. Reconnect later with Sandbox.get().

sandbox.fs()

fs(): SandboxFsOps
await sandbox.fs().writeFile("/tmp/hello.txt", "hi");
Get a filesystem handle for reading and writing files inside the running sandbox. See Filesystem for API details.

Returns

Filesystem handle.

sandbox.kill()

kill(): Promise<void>
await sandbox.kill(); // no graceful shutdown
Force-terminate the sandbox and wait until stopped state is observed. No graceful shutdown - use when the sandbox is unresponsive. Pending writes that the workload hasn’t fsync’d may be lost, same durability semantics as a sudden power loss on a physical machine. Prefer stop() for graceful shutdown that gives the workload a chance to flush.

sandbox.killWithTimeout()

killWithTimeout(timeoutMs: number): Promise<void>
await sandbox.killWithTimeout(2000);
Force-terminate the sandbox and wait up to timeoutMs for stopped-state observation.

Parameters

timeoutMsnumber
Milliseconds to wait for the stopped state to be observed.

sandbox.logs()

logs(opts?: LogReadOptions): Promise<LogEntry[]>
import { Sandbox } from "microsandbox";

const handle = await Sandbox.get("web");

// Default: all user-program output, regardless of pipe/pty mode
const entries = await handle.logs();

for (const e of entries) {
  const source =
    e.source === "stdout" ? "OUT" :
    e.source === "stderr" ? "ERR" :
    e.source === "output" ? "PTY" :
    "SYS";

  console.log(
    `[${e.timestamp.toISOString()}] ${source} ${e.sessionId}: ${e.text().trimEnd()}`
  );
}

// Filtered: last 50 entries from the past hour, including system lines
const recent = await handle.logs({
  tail: 50,
  since: new Date(Date.now() - 60 * 60 * 1000),
  sources: ["stdout", "stderr", "output", "system"],
});
Read captured output from the sandbox’s exec.log. Backed by an on-disk JSON Lines file the runtime writes via the relay tap. Works on running and stopped sandboxes alike - there is no protocol traffic. The same method is available on SandboxHandle for callers that don’t want to start the sandbox first. The default sources are "stdout", "stderr", and "output" (PTY-merged). Pass "system" to also include synthetic lifecycle markers and runtime/kernel diagnostic lines, or "all" for every source.

Parameters

Filters: tail, since, until, sources. Omit for the default user-program sources.

Returns

Matching entries in chronological order.

sandbox.logStream()

logStream(opts?: LogStreamOptions): Promise<LogStream>
await using stream = await sandbox.logStream({ follow: true });
for await (const e of stream) {
  console.log(e.text().trimEnd());
}
Stream captured output as it appears, with optional follow. Backed by the same on-disk exec.log as logs(), but yields entries lazily. Pass { follow: true } to keep the stream open past current EOF and pick up new entries as they are written; otherwise the stream drains the current contents and ends. Each yielded LogEntry carries an opaque cursor that can be passed back via LogStreamOptions.fromCursor to resume.

Parameters

Filters plus follow and resume controls (since, fromCursor).

Returns

Async iterable of log entries.

sandbox.ping()

ping(): Promise<SandboxPingResult>
const health = await sandbox.ping();
console.log(`${health.name}: ${health.latencyMs.toFixed(1)} ms`);
Check that the running sandbox’s guest agent is reachable without refreshing idle activity. This sends core.ping and waits for core.pong; it does not start stopped sandboxes and returns an error if the sandbox is not running or agentd cannot respond. After upgrading from a runtime that predates protocol generation 6, restart already-running sandboxes so the guest agent understands the message.

Returns

Sandbox name and agent round-trip latency.

sandbox.touch()

touch(): Promise<SandboxTouchResult>
const keepalive = await sandbox.touch();
console.log(`${keepalive.name}: ${keepalive.activitySeq}`);
Explicitly refresh the running sandbox’s idle activity. This sends core.touch, receives core.touched, and advances the guest activity sequence used by the runtime idle-timeout monitor. It does not start stopped sandboxes and it does not bypass maxDuration.

Returns

Sandbox name and updated activity sequence.

sandbox.modify()

modify(opts?: ModifyOptions): Promise<SandboxModificationPlan>
// Live resize: applies to the running VM when within the booted capacity
const plan = await sandbox.modify({ cpus: 4, memory: 4096 });
for (const r of plan.resizeStatus) {
  console.log(`${r.resource}: ${r.requested} -> ${r.actual} (${r.state})`);
}

// Preview a change without applying it
const preview = await sandbox.modify({ maxMemory: 16384, dryRun: true });
for (const c of preview.changes) {
  console.log(`${c.field}: ${c.disposition}`);
}

// Make an env change active now by restarting
await sandbox.modify({ env: { MODE: "prod" }, policy: "restart" });
Plan or apply a configuration change. The returned plan labels each change "live", "next start", "requires restart", or "unsupported", and apply is all-or-nothing. cpus and memory resize live within the maxCpus / maxMemory ceilings; raising a ceiling requires a restart. Env and workdir changes affect future execs only. On a stopped sandbox, changes are saved for the next boot. Secret changes are Rust-SDK and CLI only for now. The CLI equivalent is msb modify. A live CPU or memory resize can take a moment to settle. The new limits are enforced immediately, and the returned plan’s resizeStatus reports when the sandbox has finished adjusting. See SandboxModificationPlan.

Parameters

Requested changes plus policy and dryRun. Omitted fields are left unchanged.

Returns

The modification plan, applied unless dryRun: true.

sandbox.metrics()

metrics(): Promise<SandboxMetrics>
const m = await sandbox.metrics();
console.log(`cpu ${m.cpuPercent.toFixed(1)}% · mem ${Math.round(m.memoryBytes / 1048576)} MiB`);
Get a point-in-time snapshot of the sandbox’s resource usage: CPU, memory, disk I/O, network I/O, optional upper disk usage, and uptime.

Returns

Resource metrics.

sandbox.metricsStream()

metricsStream(intervalMs: number): Promise<MetricsStream>
await using stream = await sandbox.metricsStream(1000);
for await (const snapshot of stream) {
  console.log(`${snapshot.cpuPercent.toFixed(1)}%`);
}
Stream resource metrics at a regular interval. The returned MetricsStream supports both recv() and for await...of.

Parameters

intervalMsnumber
Milliseconds between metric snapshots.

Returns

Async stream yielding a snapshot each interval.

sandbox.requestDrain()

requestDrain(): Promise<void>
await sandbox.requestDrain();
await sandbox.waitUntilStopped();
Request a graceful drain and return once the request is sent. Existing commands run to completion, but new exec calls are rejected. The sandbox transitions to stopped when all in-flight commands finish. Use waitUntilStopped() when the caller needs stopped-state observation. Useful for zero-downtime rotation of worker sandboxes.

sandbox.requestKill()

requestKill(): Promise<void>
await sandbox.requestKill();
Request force termination and return once the signal is sent, without waiting for the stopped state to be observed.

sandbox.requestStop()

requestStop(): Promise<void>
await sandbox.requestStop();
Request graceful shutdown and return once the request is sent, without waiting for the stopped state to be observed.

sandbox.ssh()

ssh(): SandboxSshOps
Get an SSH handle for opening interactive sessions and port forwards into the running guest. See SSH for API details.

Returns

SSH handle.

sandbox.stop()

stop(): Promise<void>
await sandbox.stop();
Gracefully shut down the sandbox and wait until stopped state is observed. Lets the sandbox finish writing any pending data to disk before it exits, so files written inside the sandbox aren’t lost across a later restart. Waits up to 10_000 ms for a clean exit; if the sandbox is still running after that, it is force-killed.

sandbox.stopWithTimeout()

stopWithTimeout(timeoutMs: number): Promise<void>
await sandbox.stopWithTimeout(5000);
Gracefully shut down the sandbox with an explicit observation timeout before force-kill escalation. 0 force-kills immediately. Resolves successfully either way - it does not throw on timeout expiry.

Parameters

timeoutMsnumber
Milliseconds to wait for a clean exit before force-killing. 0 skips the grace period.

sandbox.waitUntilStopped()

waitUntilStopped(): Promise<SandboxStopResult>
const result = await sandbox.waitUntilStopped();
console.log(result.status, result.exitCode);
Block until the sandbox is observed in a terminal non-running state, without triggering a stop or kill request.

Returns

Terminal status, exit code, and signal that were observed.

sandbox.Symbol.asyncDispose

[Symbol.asyncDispose](): Promise<void>
{
  await using sandbox = await Sandbox.builder("api").image("python").create();
  await sandbox.exec("python", ["-V"]);
} // sandbox.stop() runs here, automatically
Implements AsyncDisposable so the sandbox can be used with await using. When the binding goes out of scope, the sandbox is stopped (best-effort) - but only if ownsLifecycle is true.

SandboxBuilder

Fluent builder for configuring a sandbox before creation. Obtained via Sandbox.builder(name). Every setter returns the same builder so calls chain. Examples are shown on the methods where usage is non-obvious; simple setters are demonstrated by the Typical flow above.

.build()

build(): Promise<SandboxConfig>
Materialize the SandboxConfig without booting the sandbox. Validates the configuration and consumes the builder. For booting, use create instead - it builds internally.

Returns

Validated, ready-to-boot configuration.

.cpus()

cpus(n: number): this
Set the number of virtual CPUs. This is a limit, not a reservation.

Parameters

nnumber
Number of vCPUs.

.maxCpus()

maxCpus(n: number): this
Set the boot-time maximum possible virtual CPU capacity. This reserves the envelope a sandbox can use after restart-backed changes and future live CPU activation; it does not increase the effective vCPU count by itself.

Parameters

nnumber
Maximum possible vCPUs.

.create()

create(): Promise<Sandbox>
const sandbox = await Sandbox.builder("worker")
  .image("python")
  .detached(true)
  .create();
await sandbox.detach();
Build and boot the sandbox. By default the sandbox is attached - it stops when the await using binding goes out of scope. Call detached(true) first for background mode.

Returns

Running sandbox.

.createWithPullProgress()

createWithPullProgress(): Promise<PullProgressCreate>
const creation = await Sandbox.builder("demo")
  .image("alpine")
  .createWithPullProgress();

for await (const ev of creation) {
  if (ev.kind === "layerDownloadProgress") {
    console.log(`${ev.layerIndex}: ${ev.downloadedBytes}/${ev.totalBytes}`);
  }
}

const sandbox = await creation.awaitSandbox();
Build and boot while streaming image pull progress. Returns a PullProgressCreate that yields PullProgress events as the image is resolved, downloaded, and materialized; call awaitSandbox() after iteration to obtain the live sandbox.

Returns

Async iterable creation handle.

.detached()

detached(enabled: boolean): this
Create in detached/background mode when true. A detached sandbox survives after your process exits and does not auto-stop on await using scope exit.

Parameters

enabledboolean
Whether to boot in detached mode.

.disableMetricsSample()

disableMetricsSample(): this
Disable periodic background metrics sampling for this sandbox.

.disableNetwork()

disableNetwork(): this
Fully disable networking. No network interface is created.

.entrypoint()

entrypoint(cmd: string[]): this
Override the OCI image’s stored ENTRYPOINT. Consulted by msb exec / msb run (CLI command resolution), not by sandbox.exec / sandbox.shell - those pass cmd literally to the guest agent. Use this when configuring a sandbox via the SDK for later CLI attachment.

Parameters

cmdstring[]
Entrypoint command and arguments.

.env()

env(key: string, value: string): this
Set an environment variable visible to all commands. Can be called multiple times. Per-command env vars (via execWith) are merged on top.

Parameters

keystring
Variable name.
valuestring
Variable value.

.envs()

envs(vars: Record<string, string>): this
Add many environment variables at once.

Parameters

varsRecord<string, string>
Map of variable names to values.

.ephemeral()

ephemeral(enabled: boolean): this
When true, the sandbox and all its persisted state are removed automatically once it stops, rather than left on disk for restart.

Parameters

enabledboolean
Whether the sandbox is ephemeral.

.fromSnapshot()

fromSnapshot(pathOrName: string): this
Boot from a previously captured snapshot instead of a fresh image. The image reference and upper-layer source are pinned from the snapshot manifest. See Snapshots.

Parameters

pathOrNamestring
Snapshot name or filesystem path.

.hostname()

hostname(name: string): this
Set the guest hostname.

Parameters

namestring
Hostname.

.libkrunfwPath()

libkrunfwPath(path: string): this
Deprecated compatibility alias for older builder chains. This sets the same process-level override as setRuntimeLibkrunfwPath(path) and returns the builder; it is not a per-sandbox setting.

Parameters

pathstring
Path to the libkrunfw shared library.

.idleTimeout()

idleTimeout(secs: number): this
Auto-drain the sandbox after this many seconds of inactivity (no active exec sessions). Enforced on the host side.

Parameters

secsnumber
Idle timeout in seconds.

.image()

image(src: string): this
Set the root filesystem source. Accepts OCI image names ("alpine"), local directory paths, or disk image paths. The format is auto-detected. Required unless fromSnapshot is used.

Parameters

srcstring
OCI image name, local directory path, or disk image path.

.imageWith()

imageWith(configure: (b: ImageBuilder) => ImageBuilder): this
const sandbox = await Sandbox.builder("worker")
  .imageWith((i) => i.oci("python:3.12").upperSize(8192))
  .create();
Configure an explicit rootfs source. Use this for OCI-only settings such as the writable overlay upper size, or for disk images when the filesystem type can’t be auto-detected.

Parameters

configure(b: ImageBuilder) => ImageBuilder
Configure the rootfs source.

.init()

init(cmd: string, args?: string[]): this
const sandbox = await Sandbox.builder("worker")
  .image("jrei/systemd-debian:12")
  .init("auto")
  .create();
Hand off PID 1 inside the guest to cmd after agentd finishes its boot-time setup. cmd is either an absolute path inside the guest rootfs or the literal "auto", which honors a known image ENTRYPOINT init then falls back to probing common init paths. See Custom init system. For init binaries that need extra env, use initWith.

Parameters

cmdstring
Absolute path inside the guest, or “auto”.
argsstring[]?
Optional argv for the init binary.

.initWith()

initWith(cmd: string, configure: (b: InitOptionsBuilder) => InitOptionsBuilder): this
const sandbox = await Sandbox.builder("worker")
  .image("jrei/systemd-debian:12")
  .initWith("/lib/systemd/systemd", (i) =>
    i.args(["--unit=multi-user.target"]).env("container", "microsandbox"))
  .create();
Like init, but with a closure-builder for argv and env vars. The builder exposes .arg, .args, .env, and .envs. Calling init or initWith more than once overwrites.

Parameters

cmdstring
Absolute path to the init binary inside the guest.
configure(b: InitOptionsBuilder) => InitOptionsBuilder
Closure populating argv and env.

.label()

label(key: string, value: string): this
Attach a single label to the sandbox. Labels can be matched later with Sandbox.listWith().

Parameters

keystring
Label key.
valuestring
Label value.

.labels()

labels(labels: Record<string, string>): this
Attach many labels at once.

Parameters

labelsRecord<string, string>
Map of label keys to values.

.logLevel()

logLevel(level: LogLevel): this
Override the sandbox process’s log verbosity.

Parameters

Log level.

.maxDuration()

maxDuration(secs: number): this
Set the maximum sandbox lifetime in seconds. When exceeded, the sandbox is drained and stopped. Enforced on the host side - the guest cannot override it.

Parameters

secsnumber
Maximum lifetime in seconds.

.memory()

memory(mib: number): this
Set the guest memory size in MiB. Physical pages are only allocated as the guest touches them, so this is a limit, not an upfront reservation.

Parameters

mibnumber
Memory in MiB.

.maxMemory()

maxMemory(mib: number): this
Set the boot-time maximum hotpluggable guest memory in MiB. This reserves the envelope a sandbox can use after restart-backed changes and future live memory activation; it does not increase the effective memory by itself.

Parameters

mibnumber
Maximum memory in MiB.

.metricsSampleIntervalMs()

metricsSampleIntervalMs(ms: number): this
Set the interval, in milliseconds, at which the host samples background metrics for this sandbox.

Parameters

msnumber
Sampling interval in milliseconds.

.network()

network(configure: (b: NetworkBuilder) => NetworkBuilder): this
Configure DNS, TLS, policy, and secrets. See Networking for the full builder API.

Parameters

Configure the network.

.patch()

patch(configure: (b: PatchBuilder) => PatchBuilder): this
const sandbox = await Sandbox.builder("worker")
  .image("python")
  .patch((p) => p.text("/etc/app.conf", "mode=prod\n", { mode: 0o644 }))
  .create();
Modify the rootfs before the VM boots. Patches go into the writable layer - the base image is untouched. See PatchBuilder for the operations.

Parameters

Configure rootfs patches.

.port()

port(host: number, guest: number): this
Publish a TCP port from the sandbox to the host. The default host bind address is 127.0.0.1. For an explicit bind address, use portBind.

Parameters

hostnumber
Port on the host.
guestnumber
Port inside the sandbox.

.portBind()

portBind(bind: string, host: number, guest: number): this
Publish a TCP port on a specific host bind address, such as "0.0.0.0".

Parameters

bindstring
Host bind address.
hostnumber
Port on the host.
guestnumber
Port inside the sandbox.

.portUdp()

portUdp(host: number, guest: number): this
Publish a UDP port. The default host bind address is 127.0.0.1.

Parameters

hostnumber
Port on the host.
guestnumber
Port inside the sandbox.

.portUdpBind()

portUdpBind(bind: string, host: number, guest: number): this
Publish a UDP port on a specific host bind address.

Parameters

bindstring
Host bind address.
hostnumber
Port on the host.
guestnumber
Port inside the sandbox.

.pullPolicy()

pullPolicy(policy: PullPolicy): this
Control when the OCI image is pulled from the registry.

Parameters

Pull behavior.

.quietLogs()

quietLogs(): this
Suppress sandbox process log output.

.registry()

registry(configure: (b: RegistryBuilder) => RegistryBuilder): this
const sandbox = await Sandbox.builder("worker")
  .image("registry.internal/app:latest")
  .registry((r) => r.auth("user", "token"))
  .create();
Configure the OCI registry connection: authentication, insecure transport, and CA certificates.

Parameters

configure(b: RegistryBuilder) => RegistryBuilder
Configure the registry.

.replace()

replace(): this
If a sandbox with the same name already exists, stop it (10s SIGTERM grace, then SIGKILL), remove it, and create a fresh one. Without this, creation fails on name conflict.

.replaceWithTimeout()

replaceWithTimeout(timeoutMs: number): this
Same as replace() with a custom SIGTERM timeout in milliseconds. 0 skips SIGTERM and force-kills immediately. Implies replace().

Parameters

timeoutMsnumber
Milliseconds to wait after SIGTERM before escalating to SIGKILL.

.rlimit()

rlimit(resource: string, limit: number): this
Set a guest resource limit (a soft and hard limit at the same value). For separate soft/hard values, use rlimitRange.

Parameters

resourcestring
Resource name, e.g. “nofile”.
limitnumber
Limit value applied to both soft and hard.

.rlimitRange()

rlimitRange(resource: string, soft: number, hard: number): this
Set a guest resource limit with explicit soft and hard values.

Parameters

resourcestring
Resource name, e.g. “nofile”.
softnumber
Soft limit.
hardnumber
Hard limit.

.script()

script(name: string, content: string): this
Add a named script at /.msb/scripts/ inside the guest. Scripts are added to PATH and can be called by name via exec() or shell().

Parameters

namestring
Script name (becomes the filename).
contentstring
Script content.

.scripts()

scripts(scripts: Record<string, string>): this
Add many named scripts at once.

Parameters

scriptsRecord<string, string>
Map of script names to contents.

.security()

security(profile: "default" | "restricted"): this
Set the in-guest security profile.

Parameters

profile”default” | “restricted”
Security profile.

.secret()

secret(configure: (b: SecretBuilder) => SecretBuilder): this
Add a secret with full configuration. See Secrets for the builder API. Automatically enables TLS interception.

Parameters

Configure the secret.

.secretEnv()

secretEnv(envVar: string, value: string, allowedHost: string): this
Auto-placeholder shorthand for adding a header-injected secret. Generates a $MSB_<env_var> placeholder usable in headers.

Parameters

envVarstring
Environment variable name (non-empty, no = or NUL).
valuestring
Secret value.
allowedHoststring
Allowed destination host.

.shell()

shell(shell: string): this
Set the shell binary used by sandbox.shell().

Parameters

shellstring
Shell path (e.g. “/bin/bash”).

.user()

user(user: string): this
Set the default guest user for all commands.

Parameters

userstring
User name or UID.

.volume()

volume(guest: string, configure: (b: MountBuilder) => MountBuilder): this
Add a volume mount. See Volumes for mount types.

Parameters

gueststring
Mount point inside the sandbox.
Configure the mount.

.workdir()

workdir(path: string): this
Set the default working directory for all commands.

Parameters

pathstring
Absolute path inside the guest.

PatchBuilder

Fluent builder for the ordered list of pre-boot rootfs patches. Used in SandboxBuilder.patch(p => p...). Each method appends one operation; calls are chainable. By default a method that targets a path already present in the image errors at boot; pass { replace: true } on the operation to allow overwriting. mkdir and remove are idempotent. See Patches for conceptual context.

.append()

append(path: string, content: string): this
Append content to an existing file at path. If the file lives in a lower image layer, it’s copied up first.

Parameters

pathstring
Absolute path inside the guest.
contentstring
Text to append.

.copyDir()

copyDir(src: string, dst: string, opts?: { replace?: boolean }): this
Recursively copy a host directory at src into the guest rootfs at dst.

Parameters

srcstring
Host source directory.
dststring
Absolute destination path inside the guest.
opts.replaceboolean
When true, overwrite an existing path at dst.

.copyFile()

copyFile(src: string, dst: string, opts?: { mode?: number; replace?: boolean }): this
Copy a single host file at src into the guest rootfs at dst.

Parameters

srcstring
Host source file.
dststring
Absolute destination path inside the guest.
opts.modenumber
File mode, e.g. 0o644. Omit to keep the source mode.
opts.replaceboolean
When true, overwrite an existing path at dst.

.file()

file(path: string, content: Buffer, opts?: { mode?: number; replace?: boolean }): this
Write raw bytes at path.

Parameters

pathstring
Absolute path inside the guest.
contentBuffer
Raw byte content.
opts.modenumber
File mode, e.g. 0o644.
opts.replaceboolean
When true, overwrite an existing path.

.mkdir()

mkdir(path: string, opts?: { mode?: number }): this
Create a directory at path. Idempotent: a no-op if the directory already exists.

Parameters

pathstring
Absolute path inside the guest.
opts.modenumber
Directory mode, e.g. 0o755.

.remove()

remove(path: string): this
Delete a file or directory at path. Idempotent: a no-op if the path doesn’t exist.

Parameters

pathstring
Absolute path inside the guest.
symlink(target: string, link: string, opts?: { replace?: boolean }): this
Create a symlink at link pointing to target.

Parameters

targetstring
What the symlink points to (literal symlink target text).
linkstring
Absolute path of the symlink itself.
opts.replaceboolean
When true, overwrite an existing path at link.

.text()

text(path: string, content: string, opts?: { mode?: number; replace?: boolean }): this
Write UTF-8 text content at path.

Parameters

pathstring
Absolute path inside the guest.
contentstring
Text content.
opts.modenumber
File mode, e.g. 0o644.
opts.replaceboolean
When true, overwrite an existing path.

Types

LogEntry

Returned by logs() · logStream()

A class wrapping one captured log entry from exec.log. Bytes are exposed via data; use text() for a UTF-8-lossy decode.
Property / MethodTypeDescription
timestampDateWall-clock capture time on the host
sourceLogSourceWhere the chunk came from
sessionIdnumber | nullRelay-monotonic session id; null for "system" entries
dataUint8ArrayThe captured chunk’s bytes (UTF-8 lossy decoded by default)
cursorstringOpaque resume token; pass to LogStreamOptions.fromCursor to resume
text()stringConvenience: UTF-8 decode of data (lossy - invalid bytes are replaced)

LogLevel

Used by logLevel()

Sandbox process log verbosity. String literal type.
ValueDescription
"error"Errors only
"warn"Warnings and errors only
"info"Info and higher
"debug"Debug and higher
"trace"Most verbose - all diagnostic output

LogReadOptions

Used by logs()

Filters passed to logs(). All fields optional. Omit the argument entirely for the default sources (stdout + stderr + output).
FieldTypeDescription
tailnumber?Show only the last N entries after other filters apply
sinceDate?Inclusive lower bound on entry timestamp
untilDate?Exclusive upper bound on entry timestamp
sourcesReadonlyArray<LogSource | "all">?Sources to include. Omit = ["stdout", "stderr", "output"]. Add "system" or pass "all" to merge runtime/kernel diagnostics.

LogStream

Returned by logStream()

An async iterable of LogEntry values. Drain it with for await...of or call recv() directly. Implements AsyncDisposable, so it works with await using.
MethodReturnsDescription
recv()Promise<LogEntry | null>Receive the next entry. Returns null when the stream ends.
[Symbol.asyncIterator]()AsyncIterator<LogEntry>Use with for await...of
[Symbol.asyncDispose]()Promise<void>Stop iterating; safe to use with await using

LogStreamOptions

Used by logStream()

Options passed to logStream(). All fields optional. since and fromCursor are mutually exclusive - passing both rejects at the boundary.
FieldTypeDescription
sourcesReadonlyArray<LogSource | "all">?Same shape as LogReadOptions.sources
sinceDate?Start at the first entry whose timestamp is >= since. Mutually exclusive with fromCursor.
fromCursorstring?Resume strictly after the entry whose LogEntry.cursor matches. Mutually exclusive with since.
untilDate?Stop emitting at the first entry whose timestamp is >= until
followboolean?When true, keep the stream open past current EOF and yield new entries as they are written. Defaults to false.

LogSource

Used by LogEntry.source · LogReadOptions.sources

Tag indicating where a captured log entry came from. String literal type:
type LogSource = "stdout" | "stderr" | "output" | "system";
ValueDescription
"stdout"Captured from a session’s stdout (pipe mode - streams stayed separated)
"stderr"Captured from a session’s stderr (pipe mode)
"output"Captured from a session running in PTY mode. PTY allocation merges stdout and stderr at the kernel level inside the guest, so they arrive as a single stream - tagged "output" rather than mislabelled as "stdout".
"system"Synthetic entry: lifecycle markers in exec.log plus runtime/kernel diagnostic lines merged in at read time when "system" is requested.

MetricsStream

Returned by metricsStream()

Async stream for receiving periodic metrics snapshots. Implements AsyncDisposable, so it works with await using.
MethodReturnsDescription
recv()Promise<SandboxMetrics | null>Receive the next snapshot. Returns null when the stream ends.
[Symbol.asyncIterator]()AsyncIterator<SandboxMetrics>Use with for await...of
[Symbol.asyncDispose]()Promise<void>Stop iterating; safe to use with await using

ModifyOptions

Used by modify()

A requested sandbox modification. Omitted fields are left unchanged. Secret changes are Rust-SDK and CLI only for now.
FieldTypeDescription
cpusnumberDesired effective vCPU count. Live when within the booted maxCpus
maxCpusnumberBoot-time maximum possible vCPUs (restart-backed)
memorynumberDesired effective guest memory in MiB. Live when within the booted maxMemory
maxMemorynumberBoot-time maximum hotpluggable memory in MiB (restart-backed)
envRecord<string, string>Environment variables to set for future execs
envRemovestring[]Environment variable keys to remove
labelsRecord<string, string>Labels to set
labelsRemovestring[]Label keys to remove
workdirstringWorking directory for future execs
policy"no_restart" | "next_start" | "restart""no_restart" (default) applies only changes that can complete without restarting; "next_start" persists changes for the next start without mutating a running VM; "restart" restarts if needed so restart-required changes become active now
dryRunbooleanCompute the plan without applying anything. Defaults to false

PullPolicy

Used by pullPolicy()

Controls when the SDK fetches an OCI image from the registry. String literal type.
ValueDescription
"always"Pull the image every time, even if cached locally
"if-missing"Pull only if the image is not already cached. This is the default.
"never"Never pull; fail if the image is not cached locally

PullProgress

Yielded by PullProgressCreate

Image pull and materialize progress event emitted by PullProgressCreate. A discriminated union. Narrow on kind to access variant-specific fields.
kind valueAdditional fields
"resolving"reference: string
"resolved"reference: string, manifestDigest: string, layerCount: number, totalDownloadBytes?: number
"layerDownloadProgress"layerIndex: number, digest: string, downloadedBytes: number, totalBytes?: number
"layerDownloadComplete"layerIndex: number, digest: string, downloadedBytes: number
"layerDownloadVerifying"layerIndex: number, digest: string
"layerMaterializeStarted"layerIndex: number, diffId: string
"layerMaterializeProgress"layerIndex: number, bytesRead: number, totalBytes: number
"layerMaterializeWriting"layerIndex: number
"layerMaterializeComplete"layerIndex: number, diffId: string
"stitchMergingTrees"layerCount: number
"stitchWritingFsmeta"(none)
"stitchWritingVmdk"(none)
"stitchComplete"(none)
"complete"reference: string, layerCount: number
totalDownloadBytes and totalBytes on the "resolved" / "layerDownloadProgress" variants may be absent if the manifest omits sizes.

PullProgressCreate

Returned by createWithPullProgress()

Async iterable creation handle returned by createWithPullProgress(). Yields PullProgress events as the image is resolved, downloaded, and materialized. Call awaitSandbox() after iteration to obtain the live sandbox.
Method / PropertyReturnsDescription
progressNapiPullProgressStreamThe underlying progress event stream
[Symbol.asyncIterator]()AsyncIterator<PullProgress>Use with for await...of
awaitSandbox()Promise<Sandbox>Resolve the underlying creation task and return the running sandbox

SandboxConfig

Returned by config() · build()

Configuration object produced by build() and returned by config(). You generally should not construct this by hand; use the builder.
FieldTypeDescription
namestringSandbox name, up to 128 UTF-8 bytes
imageRootfsSourceOCI / bind / disk discriminated union
cpusnumber | nullVirtual CPUs
maxCpusnumber | nullBoot-time maximum possible virtual CPUs
memoryMibnumber | nullGuest memory in MiB
maxMemoryMibnumber | nullBoot-time maximum hotpluggable memory in MiB
logLevelLogLevel | nullLog verbosity
quietLogsbooleanSuppress log output
workdirstring | nullDefault working directory
shellstring | nullShell binary
securityProfile"default" | "restricted"In-guest security profile
entrypointstring[] | nullOverride image entrypoint
cmdstring[] | nullOverride image cmd
hostnamestring | nullGuest hostname
userstring | nullDefault guest user
envArray<readonly [string, string]>Environment variables
scriptsArray<readonly [string, string]>Named scripts
mountsVolumeMount[]Volume mounts
patchesPatch[]Rootfs modifications applied before boot
pullPolicyPullPolicy | nullImage pull behavior
replacebooleanReplace existing sandbox with same name
replaceWithTimeoutMsnumberMilliseconds to wait after SIGTERM before escalating to SIGKILL (default 10000; 0 skips SIGTERM)
maxDurationSecsnumber | nullMaximum sandbox lifetime
idleTimeoutSecsnumber | nullStop after idle time
portsTcpArray<readonly [number, number]>TCP host→guest mappings
portsUdpArray<readonly [number, number]>UDP host→guest mappings
registryRegistryConfig | nullRegistry connection settings
networkNetworkConfig | nullNetwork configuration
disableNetworkbooleanDisable networking entirely
secretsSecretEntry[]Secret entries (top-level)

SandboxHandle

Returned by Sandbox.get() · Sandbox.list() · Sandbox.listWith()

A lightweight handle to an existing sandbox (running or stopped). Obtained via Sandbox.get(), Sandbox.list(), or Sandbox.listWith(). Provides status, configuration, and lifecycle control without an active connection to the guest agent. Handles from Sandbox.get(name) are live - they expose lifecycle methods (start, stop, kill, connect, etc). Handles in the arrays returned by Sandbox.list() / Sandbox.listWith() are read-only: calling lifecycle methods on them throws. Use Sandbox.get(name) to upgrade to a live handle.
Property / MethodTypeDescription
namestringSandbox name, up to 128 UTF-8 bytes
statusSandboxStatusCurrent status
configJsonstringRaw JSON configuration
createdAtDate | nullCreation timestamp
updatedAtDate | nullLast update timestamp
config()SandboxConfigParsed configuration
refresh()Promise<SandboxHandle>Re-read the handle’s state from the database
ping()Promise<SandboxPingResult>Check agent reachability without refreshing idle activity; does not start stopped sandboxes
touch()Promise<SandboxTouchResult>Explicitly refresh idle activity; does not start stopped sandboxes
modify(opts?)Promise<SandboxModificationPlan>Plan or apply a configuration change; same options as modify(). Does not start stopped sandboxes; changes persist for the next boot
metrics()Promise<SandboxMetrics>Point-in-time resource metrics
logs()Promise<LogEntry[]>Read captured exec.log (works without starting)
logStream()Promise<LogStream>Stream captured exec.log, with optional follow
start()Promise<Sandbox>Start in attached mode
startDetached()Promise<Sandbox>Start in detached mode
connect()Promise<Sandbox>Connect to a running sandbox without taking ownership. Returns an error if it doesn’t respond within 10_000 ms
connectWithTimeout(timeoutMs)Promise<Sandbox>Same as connect() with an explicit timeout in milliseconds
stop()Promise<void>Gracefully shut down. Waits up to 10_000 ms for pending writes to flush, then force-kills
stopWithTimeout(timeoutMs)Promise<void>Same as stop() with an explicit timeout in milliseconds; 0 force-kills immediately
requestStop()Promise<void>Request graceful shutdown without waiting
kill()Promise<void>Force terminate and wait until stopped state is observed
killWithTimeout(timeoutMs)Promise<void>Same as kill() with an explicit observation timeout
requestKill()Promise<void>Request force termination without waiting
requestDrain()Promise<void>Request graceful drain without waiting
waitUntilStopped()Promise<SandboxStopResult>Block until the sandbox reaches terminal state
remove()Promise<void>Delete sandbox and state
snapshot(name)Promise<Snapshot>Snapshot this stopped sandbox under a bare name. See Snapshots
snapshotTo(path)Promise<Snapshot>Snapshot this stopped sandbox to an explicit filesystem path

SandboxModificationPlan

Returned by modify()

Dry-run or apply plan for a sandbox modification. Values never appear in a plan: secret entries carry only guest-visible references.
FieldTypeDescription
sandboxstringSandbox being modified
statusstringStatus used for classification ("running", "stopped", …)
appliedbooleanWhether the changes were applied; false for dry runs
policy"no_restart" | "next_start" | "restart"Policy used to produce the plan
changesPlannedChange[]Planned changes, one entry per field or secret
conflicts{ field, message }[]Conflicts that must be resolved before the patch can apply
warnings{ field, message }[]Non-fatal warnings, e.g. the future-execs-only env caveat
resizeStatusResourceResizeStatus[]Live resource resize outcomes, populated by apply when a live change ran (see below)

PlannedChange

Used by SandboxModificationPlan.changes

Discriminated union on kind. Both variants carry field, change, disposition, and reason.
VariantTypeDescription
kind: "config"ConfigPlannedChangeOrdinary config change
kind: "secret"SecretPlannedChangeSecret change. Values are omitted by construction; references are guest-visible only

ConfigPlannedChange

Variant of PlannedChange

Ordinary configuration change in a modification plan.
FieldTypeDescription
fieldstringConfig field being changed
change"added" | "updated" | "removed"Natural change type for table rendering
beforestring | nullPrevious safe visible state
afterstring | nullNew safe visible state
disposition"live" | "next start" | "requires restart" | "unsupported"When or whether the change can take effect
reasonstring | nullHuman-readable reason for the classification, when useful

SecretPlannedChange

Variant of PlannedChange

Secret change in a modification plan. Values are omitted by construction; beforeRef and afterRef are guest-visible references.
FieldTypeDescription
fieldstringAlways "secret"
namestringStable secret identity, usually the environment variable name
change"added" | "rotated" | "removed" | "renamed" | "hosts updated" | "placeholder updated"Natural change type for table rendering
beforeRefstring | nullPrevious guest-visible reference or placeholder
afterRefstring | nullNew guest-visible reference or placeholder
disposition"live" | "next start" | "requires restart" | "unsupported"When or whether the change can take effect
allowHostsstring[]Allowed hosts after the requested change
reasonstring | nullHuman-readable reason for the classification, when useful
ResourceResizeStatus reports runtime convergence for a live resize; enforcement applies immediately, the guest converges asynchronously:
FieldTypeDescription
resource"cpus" | "memory"Resource being resized
requestedstringRequested value
actualstringActual value observed in the guest/runtime
enforcedstringHost/VMM-enforced value
state"accepted" | "converging" | "applied" | "guest-refused" | "failed""applied" when requested, actual, and enforced match; "converging" while the guest is still onlining CPUs or plugging memory; "guest-refused" when the guest would not cooperate (the host enforces the new limit anyway)

SandboxPingResult

Returned by ping()

Agent reachability result.
FieldTypeDescription
namestringSandbox name
latencyMsnumberRound-trip latency in milliseconds

SandboxTouchResult

Returned by touch()

Explicit idle-refresh result.
FieldTypeDescription
namestringSandbox name
activitySeqnumberMonotonic activity sequence after the touch

SandboxMetrics

Returned by metrics() · yielded by MetricsStream

Point-in-time resource usage snapshot.
FieldTypeDescription
cpuPercentnumberCPU usage as a percentage
vcpuTimeNsnumberCumulative vCPU time in nanoseconds
memoryBytesnumberCurrent memory usage in bytes
memoryAvailableBytesnumber | nullGuest-visible available memory in bytes when reported
memoryHostResidentBytesnumber | nullHost-resident memory backing the guest in bytes when reported
memoryLimitBytesnumberMemory limit in bytes
diskReadBytesnumberTotal bytes read from disk since boot
diskWriteBytesnumberTotal bytes written to disk since boot
netRxBytesnumberTotal bytes received over the network since boot
netTxBytesnumberTotal bytes sent over the network since boot
upperUsedBytesnumber | nullGuest-visible OCI upper filesystem used bytes when the protected reporter is available and fresh
upperFreeBytesnumber | nullGuest-visible OCI upper filesystem free bytes when the protected reporter is available and fresh
upperHostAllocatedBytesnumber | nullHost-allocated bytes for the writable OCI upper image when available
uptimeMsnumberTime since the sandbox was created (ms)
timestampDateWhen this measurement was taken

SandboxStopResult

Returned by waitUntilStopped()

Observed terminal sandbox state returned by waitUntilStopped().
FieldTypeDescription
namestringSandbox name
statusSandboxStatusTerminal status that was observed
exitCodenumber | nullProcess exit code when it is available
signalnumber | nullTerminating signal when the process was killed
observedAtDateWhen the terminal state was observed
sourcestring | nullOrigin of the observation when reported

SandboxStatus

Used by SandboxHandle.status · SandboxStopResult.status

Current lifecycle state of a sandbox. String literal type.
ValueDescription
"running"Guest agent is ready; exec, shell, fs work
"stopped"VM shut down; configuration persisted; can be restarted
"crashed"VM exited unexpectedly (kernel panic, OOM, etc.)
"draining"Graceful shutdown in progress; existing commands finish, new ones rejected