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.

Typical flow

from microsandbox import Sandbox

# 1. configure + 2. boot the microVM
async with await Sandbox.create("api", image="python", memory=1024) as sb:
    # 3. run
    out = await sb.exec("python", ["-V"])
    print(out.stdout_text)
# 4. on exit the sandbox is killed and its state removed

Static methods

Sandbox.create()

@staticmethod
async def create(name: str, **kwargs) -> Sandbox
async with await Sandbox.create("my-sandbox", image="alpine") as sb:
    output = await sb.shell("echo hello")
    print(output.stdout_text)
# sandbox is automatically killed and removed on exit
Create and boot a sandbox. Keyword arguments provide individual config fields; see SandboxConfig for the full set. Pulls the image if needed, boots the VM, starts the guest agent, and waits until it is ready to accept commands. Sandbox names must be non-empty and no longer than 128 UTF-8 bytes. The returned Sandbox is an async context manager. Use async with to guarantee cleanup; on exit the sandbox is killed and its persisted state removed.

Parameters

namestr
Sandbox name, up to 128 UTF-8 bytes.
Configuration fields: image, cpus, memory, volumes, ports, network, secrets, detached, and more.

Returns

Running sandbox, usable as an async context manager.

Sandbox.create_with_progress()

@staticmethod
def create_with_progress(name: str, **kwargs) -> PullSession
session = Sandbox.create_with_progress("my-sandbox", image="ubuntu:latest")
async with session:
    async for event in session.progress:
        print(event.event_type)
    sb = await session.result()
Same parameters as create() but returns a PullSession that lets you track image pull progress before the sandbox is ready. This method is synchronous (not awaitable); the async work happens through the PullSession.

Parameters

namestr
Sandbox name, up to 128 UTF-8 bytes.
Same configuration fields as create().

Returns

Session for tracking pull progress and obtaining the final sandbox.

Sandbox.start()

@staticmethod
async def start(name: str, *, detached: bool = False) -> Sandbox
sb = await Sandbox.start("api")
Restart a previously stopped sandbox. The VM reboots using the persisted configuration.

Parameters

namestr
Name of a stopped sandbox, up to 128 UTF-8 bytes.
detachedbool
When True, the sandbox survives after your process exits. Default False.

Returns

Running sandbox.

Sandbox.get()

@staticmethod
async def get(name: str) -> SandboxHandle
handle = await Sandbox.get("api")
print(handle.status)
Get a 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

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

Returns

Handle with status and lifecycle control.

Sandbox.list()

@staticmethod
async def list() -> list[SandboxHandle]
for h in await Sandbox.list():
    print(h.name, h.status)
List all sandboxes (running, stopped, and crashed).

Returns

All sandbox handles.

Sandbox.list_with()

@staticmethod
async def list_with(*, labels: Mapping[str, str] | None = None) -> list[SandboxHandle]
workers = await Sandbox.list_with(labels={"role": "worker"})
List sandboxes filtered by label. Only sandboxes whose labels match every supplied key/value pair are returned.

Parameters

labelsMapping[str, str] | None
Label key/value pairs to match. None returns every sandbox, like list().

Returns

Matching sandbox handles.

Sandbox.remove()

@staticmethod
async def remove(name: str) -> None
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

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

Instance properties

sb.name

async def name(self) -> str
print(await sb.name())
The sandbox name. This is an async method; call await sb.name().

Returns

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

sb.owns_lifecycle

@property
async def owns_lifecycle(self) -> bool
Whether this handle owns the sandbox lifecycle. A sandbox returned directly by create() or start() owns lifecycle, including when created with detached=True, until you call detach(). Handles upgraded via SandboxHandle.connect() do not own lifecycle. This is an async property; use await sb.owns_lifecycle.

Returns

bool
True if this handle owns the lifecycle.

sb.fs

@property
def fs(self) -> SandboxFsOps
await sb.fs.write("/tmp/hello.txt", b"hi")
Get a filesystem handle for reading and writing files inside the running sandbox. This is a synchronous property; use sb.fs (no await). See Filesystem for API details.

Returns

Filesystem handle.

Instance methods

Command execution (exec, exec_stream, shell, shell_stream) is documented on the Execution page; SSH (ssh) on the SSH page. The lifecycle, attach, metrics, and logs methods follow.

sb.attach()

async def attach(
    self,
    cmd: str,
    args: list[str] | None = None,
    *,
    cwd: str | None = None,
    user: str | None = None,
    env: Mapping[str, str] | None = None,
    detach_keys: str | None = None,
) -> int
code = await sb.attach("python", ["-i"])
Bridge your terminal directly to a process inside the sandbox for a fully interactive PTY session. Returns the process exit code once the session ends.

Parameters

cmdstr
Command to run.
argslist[str] | None
Command arguments.
cwdstr | None
Working directory.
userstr | None
Guest user.
envMapping[str, str] | None
Environment variables.
detach_keysstr | None
Custom detach key sequence.

Returns

int
Exit code of the process.

sb.attach_shell()

async def attach_shell(self) -> int
await sb.attach_shell()
Attach your terminal to the sandbox’s default shell for an interactive session.

Returns

int
Exit code.

sb.ping()

async def ping(self) -> SandboxPingResult
health = await sb.ping()
print(f"{health.name}: {health.latency_ms:.1f} 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 raises 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.

sb.touch()

async def touch(self) -> SandboxTouchResult
keepalive = await sb.touch()
print(f"{keepalive.name}: {keepalive.activity_seq}")
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 max_duration.

Returns

Sandbox name and updated activity sequence.

sb.modify()

async def modify(
    self,
    *,
    cpus: int | None = None,
    max_cpus: int | None = None,
    memory: int | None = None,
    max_memory: int | None = None,
    env: Mapping[str, str] | None = None,
    env_rm: list[str] | None = None,
    labels: Mapping[str, str] | None = None,
    labels_rm: list[str] | None = None,
    workdir: str | None = None,
    policy: str | None = None,
    dry_run: bool = False,
) -> dict[str, Any]
# Live resize: applies to the running VM when within the booted capacity
plan = await sb.modify(cpus=4, memory=4096)
for r in plan.get("resize_status", []):
    print(f'{r["resource"]}: {r["requested"]} -> {r["actual"]} ({r["state"]})')

# Preview a change without applying it
plan = await sb.modify(max_memory=16384, dry_run=True)
for change in plan["changes"]:
    print(f'{change["field"]}: {change["disposition"]}')

# Make an env change active now by restarting
await sb.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 max_cpus / max_memory 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. The returned plan dict mirrors the canonical JSON shape:
KeyTypeDescription
sandboxstrSandbox being modified
statusstrStatus used for classification ("running", "stopped", …)
appliedboolWhether the changes were applied; False with dry_run=True
policystrPolicy used to produce the plan
changeslist[dict]One entry per field: field, change ("added", "updated", "removed"), before, after, disposition, optional reason
conflictslist[dict]Conflicts (field + message) that must be resolved before the patch can apply
warningslist[dict]Non-fatal warnings (field + message), e.g. the future-execs-only env caveat
resize_statuslist[dict]Live resize outcomes after apply, per resource: resource ("cpus" / "memory"), requested, actual, enforced, state ("accepted", "converging", "applied", "guest-refused", "failed"). Omitted when no live resize ran
A live CPU or memory resize can take a moment to settle. The new limits are enforced immediately, and resize_status reports when the sandbox has finished adjusting.

Parameters

cpusint | None
Desired effective vCPU count. Live when within the booted max_cpus.
max_cpusint | None
Boot-time maximum possible vCPUs (restart-backed).
memoryint | None
Desired effective guest memory in MiB. Live when within the booted max_memory.
max_memoryint | None
Boot-time maximum hotpluggable memory in MiB (restart-backed).
envMapping[str, str] | None
Environment variables to set for future execs.
env_rmlist[str] | None
Environment variable keys to remove.
labelsMapping[str, str] | None
Labels to set.
labels_rmlist[str] | None
Label keys to remove.
workdirstr | None
Working directory for future execs.
policystr | None
”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.
dry_runbool
When True, compute the plan without applying anything. Default False.

Returns

dict[str, Any]
The modification plan, applied unless dry_run=True.

sb.metrics()

async def metrics(self) -> SandboxMetrics
m = await sb.metrics()
print(f"cpu {m.cpu_percent:.1f}% · mem {m.memory_bytes // 1_048_576} 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.

sb.metrics_stream()

async def metrics_stream(self, interval: float = 1.0) -> MetricsStream
stream = await sb.metrics_stream(1.0)
async for snapshot in stream:
    print(f"{snapshot.cpu_percent:.1f}%")
Stream resource metrics at a regular interval. The returned MetricsStream supports both recv() and async for.

Parameters

intervalfloat
Seconds between metric snapshots. Default 1.0.

Returns

Async stream yielding a snapshot each interval.

sb.logs()

async def logs(
    self,
    tail: int | None = None,
    since_ms: float | None = None,
    until_ms: float | None = None,
    sources: list[LogReadSource] | None = None,
) -> list[LogEntry]
import time
from microsandbox import Sandbox

handle = await Sandbox.get("web")

# Default: all user-program output, regardless of pipe/pty mode
entries = await handle.logs()
for e in entries:
    label = {"stdout": "OUT", "stderr": "ERR", "output": "PTY", "system": "SYS"}[e.source]
    print(f"[{e.timestamp_ms / 1000:.3f}] {label} {e.session_id}: {e.text().rstrip()}")

# Filtered: last 50 entries from the past hour, including system lines
recent = await handle.logs(
    tail=50,
    since_ms=(time.time() - 3600) * 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" as shorthand for all four. Timestamps are exposed as float ms since the Unix epoch (UTC) for parity with SandboxMetrics.timestamp_ms.

Parameters

tailint | None
Show only the last N entries after other filters apply.
since_msfloat | None
Inclusive lower bound on entry timestamp (ms since epoch).
until_msfloat | None
Exclusive upper bound on entry timestamp (ms since epoch).
Sources to include. None = [“stdout”, “stderr”, “output”]. Add “system” to merge runtime/kernel diagnostics, or use “all” for all four.

Returns

Matching entries in chronological order.

sb.log_stream()

async def log_stream(
    self,
    sources: list[LogReadSource] | None = None,
    since_ms: float | None = None,
    from_cursor: str | None = None,
    until_ms: float | None = None,
    follow: bool = False,
) -> LogStream
stream = await sb.log_stream(follow=True)
async for entry in stream:
    print(entry.text().rstrip())
Stream captured log entries as a LogStream. With follow=True the stream stays open and yields new entries as they are written, like tail -f. Resume an earlier stream by passing the cursor of the last entry you saw as from_cursor. Also available on SandboxHandle.

Parameters

Sources to include. Same semantics as logs().
since_msfloat | None
Inclusive lower bound on entry timestamp (ms since epoch).
from_cursorstr | None
Resume after this opaque cursor (from a prior LogEntry.cursor).
until_msfloat | None
Exclusive upper bound on entry timestamp (ms since epoch).
followbool
When True, keep the stream open and yield new entries as they arrive. Default False.

Returns

Async stream of log entries.

sb.stop()

async def stop(self, timeout: float | None = None) -> None
await sb.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 ten seconds by default; pass timeout to override the graceful shutdown window before force-kill escalation.

Parameters

timeoutfloat | None
Seconds to wait for graceful exit before force-kill. None uses the ten-second default.

sb.request_stop()

async def request_stop(self) -> None
await sb.request_stop()
Request graceful shutdown and return once the request is sent, without waiting for stopped state. Pair with wait_until_stopped() when the caller needs to observe the terminal state.

sb.kill()

async def kill(self, timeout: float | None = None) -> None
await sb.kill()  # SIGKILL, 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.

Parameters

timeoutfloat | None
Seconds to wait for the stopped state to be observed.

sb.request_kill()

async def request_kill(self) -> None
await sb.request_kill()
Request force termination and return once the signal is sent, without waiting for stopped state.

sb.request_drain()

async def request_drain(self) -> None
await sb.request_drain()
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. Useful for zero-downtime rotation of worker sandboxes. Use wait_until_stopped() when the caller needs stopped-state observation.

sb.wait_until_stopped()

async def wait_until_stopped(self) -> SandboxStopResult
result = await sb.wait_until_stopped()
print(result.status, result.exit_code)
Block until the sandbox is observed in a terminal non-running state, without triggering a stop or kill request.

Returns

Terminal status and optional observed exit code.

sb.detach()

async def detach(self) -> None
sb = await Sandbox.create("worker", image="python", detached=True)
await sb.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().

Patch

Factory class for rootfs patches passed to Sandbox.create(..., patches=[...]). Each static method returns a PatchConfig. By default a patch 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.

Patch.text()

@staticmethod
def text(path: str, content: str, *, mode: int | None = None, replace: bool = False) -> PatchConfig
from microsandbox import Patch, Sandbox

sb = await Sandbox.create(
    "api",
    image="python",
    patches=[Patch.text("/etc/app.conf", "debug=1\n", mode=0o644)],
)
Write UTF-8 text content at path.

Parameters

pathstr
Absolute path inside the guest.
contentstr
Text content.
modeint | None
File mode, e.g. 0o644.
replacebool
When True, overwrite an existing path.

Patch.append()

@staticmethod
def append(path: str, content: str) -> PatchConfig
Append content to an existing file at path. If the file lives in a lower image layer, it’s copied up first.

Parameters

pathstr
Absolute path inside the guest.
contentstr
Text to append.

Patch.mkdir()

@staticmethod
def mkdir(path: str, *, mode: int | None = None) -> PatchConfig
Create a directory at path. Idempotent: a no-op if the directory already exists.

Parameters

pathstr
Absolute path inside the guest.
modeint | None
Directory mode, e.g. 0o755.

Patch.remove()

@staticmethod
def remove(path: str) -> PatchConfig
Delete a file or directory at path. Idempotent: a no-op if the path doesn’t exist.

Parameters

pathstr
Absolute path inside the guest.

Patch.copy_file()

@staticmethod
def copy_file(src: str, dst: str, *, mode: int | None = None, replace: bool = False) -> PatchConfig
Copy a single host file at src into the guest rootfs at dst.

Parameters

srcstr
Host source file.
dststr
Absolute destination path inside the guest.
modeint | None
File mode, e.g. 0o644. None keeps the source mode.
replacebool
When True, overwrite an existing path at dst.

Patch.copy_dir()

@staticmethod
def copy_dir(src: str, dst: str, *, replace: bool = False) -> PatchConfig
Recursively copy a host directory at src into the guest rootfs at dst.

Parameters

srcstr
Host source directory.
dststr
Absolute destination path inside the guest.
replacebool
When True, overwrite an existing path at dst.
@staticmethod
def symlink(target: str, link: str, *, replace: bool = False) -> PatchConfig
Create a symlink at link pointing to target.

Parameters

targetstr
What the symlink points to (literal symlink target text).
linkstr
Absolute path of the symlink itself.
replacebool
When True, overwrite an existing path at link.

Types

SandboxConfig

Used by create() · create_with_progress()

The keyword arguments accepted by create() and create_with_progress(). There is no SandboxConfig object you construct directly; these are passed as **kwargs.
FieldTypeDefaultDescription
imagestr | ImageSource-OCI image, local path, or disk image. Required unless snapshot= is passed. Use Image.oci("python:3.12", upper_size_mib=8192) to set an OCI upper size
snapshotstr | os.PathLike-Snapshot artifact to boot from instead of image=. Mutually exclusive with image=
cpusint1Virtual CPUs. This is a limit, not a reservation
max_cpusintsame as cpusBoot-time maximum possible virtual CPUs
memoryint512Guest memory in MiB. This is a limit, not a reservation
max_memoryintsame as memoryBoot-time maximum hotpluggable memory in MiB
workdirstr-Default working directory for commands
shellstr"/bin/sh"Shell for shell() calls
securitySecurityProfile | str"default"In-guest security profile. "restricted" sets no_new_privs, drops mount-admin capability from user commands, and forces nosuid,nodev on user mounts
hostnamestr-Guest hostname
userstr-Default guest user
entrypointlist[str]-Override the image’s stored ENTRYPOINT. Consulted by msb exec / msb run (CLI command resolution), not by sb.exec / sb.shell which pass cmd literally
initstr | dict | InitConfig-Hand off PID 1 to a guest init binary. See Custom init system and InitConfig for accepted shapes
replaceboolFalseReplace an existing sandbox with the same name (10s SIGTERM grace, then SIGKILL)
replace_with_timeoutfloat10Seconds to wait after SIGTERM before escalating to SIGKILL (0 skips SIGTERM). Implies replace=True
max_durationfloat-Maximum sandbox lifetime in seconds
idle_timeoutfloat-Idle timeout in seconds
envdict[str, str]{}Environment variables visible to all commands
scriptsdict[str, str]{}Named scripts mounted at /.msb/scripts/ and added to PATH
pull_policystr | PullPolicy"if-missing"Image pull behavior
log_levelstr | LogLevel-Override log verbosity
registry_authRegistryAuth-Private registry credentials
volumesdict[str, MountConfig]{}Volume mounts. See Volumes
patcheslist[PatchConfig][]Rootfs modifications applied before boot
portsdict[int, int] | Sequence[PortBinding]{}Port mappings. Dict form is TCP and binds to 127.0.0.1; use PortBinding for explicit bind addresses or UDP
networkNetworkpublic_onlyNetwork policy and configuration
secretslist[SecretEntry][]Secret injection
detachedboolFalseIf True, spawn the sandbox in detached mode; call detach() before dropping the returned handle when it should keep running

InitConfig

Used by create(init=…)

Custom init specification. Pass it (or one of the equivalent shorthand shapes) as the init= kwarg to create() to hand PID 1 inside the guest off to your own init binary after agentd’s setup. Frozen dataclass. See Custom init system for image picks, shutdown semantics, and tradeoffs.
FieldTypeDefaultDescription
cmdstr-Absolute path or "auto" to the init binary. Auto honors a known image ENTRYPOINT init before probing /sbin/init, /lib/systemd/systemd, and /usr/lib/systemd/systemd, and preserves attached init-entrypoint commands
argstuple[str, ...]()Supplemental argv (argv[0] is implicitly cmd)
envMapping[str, str]{}Extra env vars merged on top of the inherited env
The init= kwarg follows the same shape as other structured create kwargs: a bare scalar for the simple case, or a dataclass / dict for the rich case.
FormEquivalent to
init="auto" or init="/sbin/init"InitConfig(cmd=...)
init={"cmd": ..., "args": [...], "env": {...}}dict equivalent of InitConfig
init=InitConfig(cmd="/sbin/init", args=("--foo",))itself
from microsandbox import InitConfig, Sandbox

# Common case: bare string.
sb = await Sandbox.create("worker", image="jrei/systemd-debian:12", init="auto")

# Argv / env: dataclass.
sb = await Sandbox.create(
    "worker",
    image="jrei/systemd-debian:12",
    init=InitConfig(
        cmd="/lib/systemd/systemd",
        args=("--unit=multi-user.target",),
        env={"container": "microsandbox"},
    ),
)

SecurityProfile

Used by create(security=…)

Sandbox-wide in-guest security profile. A StrEnum, so the string values are accepted directly.
ValueDescription
"default"Standard profile
"restricted"Sets no_new_privs, drops mount-admin capability from user commands, and forces nosuid,nodev on user mounts

SandboxHandle

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

A lightweight handle to an existing sandbox (running or stopped). Provides status, configuration, and lifecycle control without an active connection to the guest agent. You cannot exec or fs on a handle; call .start() or .connect() to upgrade to a full Sandbox.
Property / MethodTypeDescription
namestrSandbox name, up to 128 UTF-8 bytes
statusstrCurrent status. See SandboxStatus
config_jsonstrRaw JSON configuration
created_atfloat | NoneCreation timestamp (ms since epoch)
updated_atfloat | NoneLast update timestamp (ms since epoch)
config()dict[str, Any]Parsed configuration
refresh()Awaitable[SandboxHandle]Re-fetch status and metadata, returning a fresh handle
ping()Awaitable[SandboxPingResult]Check agent reachability without refreshing idle activity; does not start stopped sandboxes
touch()Awaitable[SandboxTouchResult]Explicitly refresh idle activity; does not start stopped sandboxes
modify(…)Awaitable[dict[str, Any]]Plan or apply a configuration change; same kwargs as modify(). Does not start stopped sandboxes; changes persist for the next boot
connect(timeout=None)Awaitable[Sandbox]Connect to a running sandbox, optionally with an explicit timeout in seconds
start(*, detached=False)Awaitable[Sandbox]Start in attached or detached mode
stop(timeout=None)Awaitable[None]Gracefully shut down and wait until stopped state is observed
request_stop()Awaitable[None]Request graceful shutdown without waiting
kill(timeout=None)Awaitable[None]Force terminate and wait until stopped state is observed
request_kill()Awaitable[None]Request force termination without waiting
request_drain()Awaitable[None]Request graceful drain without waiting
wait_until_stopped()Awaitable[SandboxStopResult]Block until the sandbox reaches terminal state
remove()Awaitable[None]Delete sandbox and state
metrics()Awaitable[SandboxMetrics]Point-in-time resource metrics
logs(…)Awaitable[list[LogEntry]]Read captured exec.log (works without starting)
log_stream(…)Awaitable[LogStream]Stream captured log entries (works without starting)
snapshot(name)Awaitable[Snapshot]Create a named snapshot of the sandbox
snapshot_to(path)Awaitable[Snapshot]Create a snapshot at a path

SandboxPingResult

Returned by ping()

Agent reachability result.
PropertyTypeDescription
namestrSandbox name
latency_msfloatRound-trip latency in milliseconds

SandboxTouchResult

Returned by touch()

Explicit idle-refresh result.
PropertyTypeDescription
namestrSandbox name
activity_seqintMonotonic activity sequence after the touch

SandboxStopResult

Returned by wait_until_stopped()

Observed terminal sandbox state returned by wait_until_stopped().
PropertyTypeDescription
namestrSandbox name
statusstrTerminal status that was observed. See SandboxStatus
exit_codeint | NoneProcess exit code when it is available
signalint | NoneTerminating signal number when the sandbox was killed by a signal
observed_atfloatWhen the terminal state was observed (ms since epoch)
sourcestr | NoneWhere the terminal observation came from

SandboxStatus

Used by SandboxHandle.status · SandboxStopResult.status

The string status values a sandbox can report. A StrEnum, exposed as plain strings on status fields.
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
"paused"VM paused

SandboxMetrics

Returned by metrics() · metrics_stream()

Point-in-time resource usage snapshot.
FieldTypeDescription
cpu_percentfloatCPU usage as a percentage
vcpu_time_nsintCumulative vCPU time consumed since boot, in nanoseconds
memory_bytesintCurrent memory usage in bytes
memory_available_bytesint | NoneGuest-reported available memory in bytes when known
memory_host_resident_bytesint | NoneHost-resident memory backing the guest in bytes when known
memory_limit_bytesintMemory limit in bytes
disk_read_bytesintTotal bytes read from disk since boot
disk_write_bytesintTotal bytes written to disk since boot
net_rx_bytesintTotal bytes received over the network since boot
net_tx_bytesintTotal bytes sent over the network since boot
upper_used_bytesint | NoneGuest-visible OCI upper filesystem used bytes when the protected reporter is available and fresh
upper_free_bytesint | NoneGuest-visible OCI upper filesystem free bytes when the protected reporter is available and fresh
upper_host_allocated_bytesint | NoneHost-allocated bytes for the writable OCI upper image when available
uptime_msintTime since the sandbox was created (ms)
timestamp_msfloatWhen this measurement was taken (ms since epoch)

MetricsStream

Returned by metrics_stream()

Async stream for receiving periodic metrics snapshots.
MethodReturnsDescription
__aiter__ / __anext__SandboxMetricsUse with async for

LogEntry

Returned by logs() · iterated from LogStream

A single captured log entry returned by logs() or iterated from a LogStream.
Property / MethodTypeDescription
timestamp_msfloatWall-clock capture time (ms since Unix epoch, UTC)
sourceLogSourceWhere the chunk came from
session_idint | NoneRelay-monotonic session id; None for "system" entries
cursorstrOpaque resume token; pass back via log_stream(from_cursor=...)
databytesThe chunk’s raw bytes
text()strConvenience: UTF-8 decode of data (lossy; invalid bytes are replaced)

LogStream

Returned by log_stream()

Async stream of LogEntry values, returned by log_stream().
MethodReturnsDescription
__aiter__ / __anext__LogEntryUse with async for

LogSource

Used by LogEntry.source · logs(sources=…)

The string values the source field on a LogEntry can take, also accepted by logs(sources=[...]) and log_stream(sources=[...]). The read form additionally accepts "all".
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 mislabeled as "stdout"
"system"Synthetic entry: lifecycle markers in exec.log plus runtime/kernel diagnostic lines merged in at read time when "system" is requested
"all"Read shorthand for all four sources (accepted by sources=, never returned on an entry)

LogLevel

Used by create(log_level=…)

Sandbox process log verbosity. A StrEnum, so the string values are accepted directly.
ValueDescription
"trace"Most verbose, all diagnostic output
"debug"Debug and higher
"info"Info and higher
"warn"Warnings and errors only
"error"Errors only

PullPolicy

Used by create(pull_policy=…)

Controls when the SDK fetches an OCI image from the registry. A StrEnum, so the string values are accepted directly.
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

RegistryAuth

Used by create(registry_auth=…)

Credentials for authenticating to a private container registry. Frozen dataclass; construct directly or via RegistryAuth.basic(username, password).
FieldTypeDescription
usernamestrRegistry username
passwordstrRegistry password

PatchConfig

Returned by Patch.* factory methods · used by create(patches=…)

A single rootfs patch. Produced by the Patch factory; you’d normally not construct one directly. Frozen dataclass.
FieldTypeDescription
kindstrOne of "text", "file", "copy_file", "copy_dir", "symlink", "mkdir", "remove", "append"
pathstr | NoneAbsolute guest path (text / mkdir / remove / append)
contentstr | NoneText content (text / append)
srcstr | NoneHost source path (copy_file / copy_dir)
dststr | NoneGuest destination path (copy_file / copy_dir)
targetstr | NoneSymlink target
linkstr | NoneSymlink path
modeint | NoneFile / directory mode (e.g. 0o644)
replaceboolWhen True, overwrite an existing path at the destination. Defaults to False

PullSession

Returned by create_with_progress()

Returned by create_with_progress(). The factory itself is synchronous; use the returned session as an async context manager to track image pull progress.
Property / MethodTypeDescription
progressAsyncIterator[PullEvent]Async iterator of pull progress events
result()Awaitable[Sandbox]Await once to get the final running sandbox. A second call raises RuntimeError
session = Sandbox.create_with_progress("my-sandbox", image="ubuntu:latest")
async with session:
    async for event in session.progress:
        print(event)
    sb = await session.result()

PullEvent

Iterated from PullSession.progress

Native event object emitted by PullSession.progress. Inspect event_type and the fields relevant to that event; fields that do not apply to a particular event are None.
FieldTypeDescription
event_typestrEvent tag, e.g. "resolving", "resolved", "layer_download_progress", "complete"
referencestr | NoneImage reference being pulled
manifest_digeststr | NoneResolved manifest digest
layer_countint | NoneNumber of layers
total_download_bytesint | NoneTotal bytes to download across layers
layer_indexint | NoneIndex of the layer this event concerns
digeststr | NoneLayer blob digest
diff_idstr | NoneLayer diff id
downloaded_bytesint | NoneBytes downloaded so far for the layer
total_bytesint | NoneTotal bytes for the layer
bytes_readint | NoneBytes read during materialization
session = Sandbox.create_with_progress("my-sandbox", image="ubuntu:latest")
async with session:
    async for event in session.progress:
        if event.event_type == "resolved":
            print(f"{event.layer_count} layers, {event.total_download_bytes} bytes")
        elif event.event_type == "layer_download_progress":
            print(f"layer {event.layer_index}: {event.downloaded_bytes}/{event.total_bytes}")
    sb = await session.result()