Back to Microsandbox

Sandbox

docs/sdk/python/sandbox.mdx

0.5.1061.9 KB
Original Source

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.

<div className="msb-glance"> <p className="msb-gl"><span className="msb-dot static"></span>Static<span className="msb-ct">7</span></p> <a className="msb-row" href="#sandbox-create"><span className="msb-rn">Sandbox.create()</span><span className="msb-rg">configure and boot a sandbox</span></a> <a className="msb-row" href="#sandbox-create_with_progress"><span className="msb-rn">Sandbox.create_with_progress()</span><span className="msb-rg">boot with pull progress</span></a> <a className="msb-row" href="#sandbox-start"><span className="msb-rn">Sandbox.start()</span><span className="msb-rg">restart a stopped one</span></a> <a className="msb-row" href="#sandbox-get"><span className="msb-rn">Sandbox.get()</span><span className="msb-rg">handle to an existing one</span></a> <a className="msb-row" href="#sandbox-list"><span className="msb-rn">Sandbox.list()</span><span className="msb-rg">all sandboxes</span></a> <a className="msb-row" href="#sandbox-list_with"><span className="msb-rn">Sandbox.list_with()</span><span className="msb-rg">filter by labels</span></a> <a className="msb-row" href="#sandbox-remove"><span className="msb-rn">Sandbox.remove()</span><span className="msb-rg">delete a stopped one</span></a> <p className="msb-gl"><span className="msb-dot instance"></span>Properties<span className="msb-ct">3</span></p> <a className="msb-row" href="#sb-name"><span className="msb-rn">sb.name()</span><span className="msb-rg">sandbox name (async method)</span></a> <a className="msb-row" href="#sb-owns_lifecycle"><span className="msb-rn">sb.owns_lifecycle</span><span className="msb-rg">attached vs detached (async)</span></a> <a className="msb-row" href="#sb-fs"><span className="msb-rn">sb.fs</span><span className="msb-rg">read / write files</span></a> <p className="msb-gl"><span className="msb-dot instance"></span>Instance<span className="msb-ct">18</span></p> <a className="msb-row" href="/sdk/python/execution"><span className="msb-rn">sb.exec()</span><span className="msb-rg">run a command</span></a> <a className="msb-row" href="/sdk/python/execution"><span className="msb-rn">sb.exec_stream()</span><span className="msb-rg">run, stream events</span></a> <a className="msb-row" href="/sdk/python/execution"><span className="msb-rn">sb.shell()</span><span className="msb-rg">run through a shell</span></a> <a className="msb-row" href="/sdk/python/execution"><span className="msb-rn">sb.shell_stream()</span><span className="msb-rg">shell, stream events</span></a> <a className="msb-row" href="/sdk/python/ssh"><span className="msb-rn">sb.ssh()</span><span className="msb-rg">SSH client / server</span></a> <a className="msb-row" href="#sb-attach"><span className="msb-rn">sb.attach()</span><span className="msb-rg">interactive PTY session</span></a> <a className="msb-row" href="#sb-attach_shell"><span className="msb-rn">sb.attach_shell()</span><span className="msb-rg">attach to the shell</span></a> <a className="msb-row" href="#sb-metrics"><span className="msb-rn">sb.metrics()</span><span className="msb-rg">resource snapshot</span></a> <a className="msb-row" href="#sb-metrics_stream"><span className="msb-rn">sb.metrics_stream()</span><span className="msb-rg">stream metrics</span></a> <a className="msb-row" href="#sb-logs"><span className="msb-rn">sb.logs()</span><span className="msb-rg">captured output</span></a> <a className="msb-row" href="#sb-log_stream"><span className="msb-rn">sb.log_stream()</span><span className="msb-rg">stream / follow logs</span></a> <a className="msb-row" href="#sb-stop"><span className="msb-rn">sb.stop()</span><span className="msb-rg">graceful shutdown</span></a> <a className="msb-row" href="#sb-request_stop"><span className="msb-rn">sb.request_stop()</span><span className="msb-rg">request stop, don't wait</span></a> <a className="msb-row" href="#sb-kill"><span className="msb-rn">sb.kill()</span><span className="msb-rg">force terminate</span></a> <a className="msb-row" href="#sb-request_kill"><span className="msb-rn">sb.request_kill()</span><span className="msb-rg">signal kill, don't wait</span></a> <a className="msb-row" href="#sb-request_drain"><span className="msb-rn">sb.request_drain()</span><span className="msb-rg">graceful drain</span></a> <a className="msb-row" href="#sb-wait_until_stopped"><span className="msb-rn">sb.wait_until_stopped()</span><span className="msb-rg">block until it exits</span></a> <a className="msb-row" href="#sb-detach"><span className="msb-rn">sb.detach()</span><span className="msb-rg">release, keep running</span></a> <p className="msb-gl"><span className="msb-dot builder"></span>Patch factory · Patch<span className="msb-ct">7</span></p> <div className="msb-chiprow"> <a className="msb-chip" href="#patch-text">Patch.text()</a> <a className="msb-chip" href="#patch-append">Patch.append()</a> <a className="msb-chip" href="#patch-mkdir">Patch.mkdir()</a> <a className="msb-chip" href="#patch-remove">Patch.remove()</a> <a className="msb-chip" href="#patch-copy_file">Patch.copy_file()</a> <a className="msb-chip" href="#patch-copy_dir">Patch.copy_dir()</a> <a className="msb-chip" href="#patch-symlink">Patch.symlink()</a> </div> <p className="msb-gl"><span className="msb-dot type"></span>Types</p> <div className="msb-chiprow"> <a className="msb-typepill" href="#sandboxconfig">SandboxConfig</a> <a className="msb-typepill" href="#initconfig">InitConfig</a> <a className="msb-typepill" href="#securityprofile">SecurityProfile</a> <a className="msb-typepill" href="#sandboxhandle">SandboxHandle</a> <a className="msb-typepill" href="#sandboxstopresult">SandboxStopResult</a> <a className="msb-typepill" href="#sandboxstatus">SandboxStatus</a> <a className="msb-typepill" href="#sandboxmetrics">SandboxMetrics</a> <a className="msb-typepill" href="#metricsstream">MetricsStream</a> <a className="msb-typepill" href="#logentry">LogEntry</a> <a className="msb-typepill" href="#logstream">LogStream</a> <a className="msb-typepill" href="#logsource">LogSource</a> <a className="msb-typepill" href="#loglevel">LogLevel</a> <a className="msb-typepill" href="#pullpolicy">PullPolicy</a> <a className="msb-typepill" href="#registryauth">RegistryAuth</a> <a className="msb-typepill" href="#patchconfig">PatchConfig</a> <a className="msb-typepill" href="#pullsession">PullSession</a> <a className="msb-typepill" href="#pullevent">PullEvent</a> </div> </div> <p className="msb-label" id="typical-flow">Typical flow</p>
python
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


<span className="msb-recv">Sandbox.</span><span className="msb-hn">create()</span>

<div className="msb-tags"><span className="msb-tag is-static">static</span><span className="msb-tag is-async">async</span></div>
python
@staticmethod
async def create(name: str, **kwargs) -> Sandbox

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.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>name</code><span className="msb-type">str</span></div> <div className="msb-param-desc">Sandbox name, up to 128 UTF-8 bytes.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>**kwargs</code><a className="msb-type" href="#sandboxconfig">SandboxConfig</a></div> <div className="msb-param-desc">Configuration fields: <code>image</code>, <code>cpus</code>, <code>memory</code>, <code>volumes</code>, <code>ports</code>, <code>network</code>, <code>secrets</code>, <code>detached</code>, and more.</div> </div> </div> <p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><a className="msb-type" href="#instance-methods">Sandbox</a></div> <div className="msb-param-desc">Running sandbox, usable as an async context manager.</div> </div> </div> <Accordion title="Example">
python
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
</Accordion>

<span className="msb-recv">Sandbox.</span><span className="msb-hn">create_with_progress()</span>

<div className="msb-tags"><span className="msb-tag is-static">static</span></div>
python
@staticmethod
def create_with_progress(name: str, **kwargs) -> PullSession

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.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>name</code><span className="msb-type">str</span></div> <div className="msb-param-desc">Sandbox name, up to 128 UTF-8 bytes.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>**kwargs</code><a className="msb-type" href="#sandboxconfig">SandboxConfig</a></div> <div className="msb-param-desc">Same configuration fields as <a href="#sandbox-create">create()</a>.</div> </div> </div> <p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><a className="msb-type" href="#pullsession">PullSession</a></div> <div className="msb-param-desc">Session for tracking pull progress and obtaining the final sandbox.</div> </div> </div> <Accordion title="Example">
python
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()
</Accordion>

<span className="msb-recv">Sandbox.</span><span className="msb-hn">start()</span>

<div className="msb-tags"><span className="msb-tag is-static">static</span><span className="msb-tag is-async">async</span></div>
python
@staticmethod
async def start(name: str, *, detached: bool = False) -> Sandbox

Restart a previously stopped sandbox. The VM reboots using the persisted configuration.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>name</code><span className="msb-type">str</span></div> <div className="msb-param-desc">Name of a stopped sandbox, up to 128 UTF-8 bytes.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>detached</code><span className="msb-type">bool</span></div> <div className="msb-param-desc">When <code>True</code>, the sandbox survives after your process exits. Default <code>False</code>.</div> </div> </div> <p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><a className="msb-type" href="#instance-methods">Sandbox</a></div> <div className="msb-param-desc">Running sandbox.</div> </div> </div> <Accordion title="Example">
python
sb = await Sandbox.start("api")
</Accordion>

<span className="msb-recv">Sandbox.</span><span className="msb-hn">get()</span>

<div className="msb-tags"><span className="msb-tag is-static">static</span><span className="msb-tag is-async">async</span></div>
python
@staticmethod
async def get(name: str) -> SandboxHandle

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.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>name</code><span className="msb-type">str</span></div> <div className="msb-param-desc">Sandbox name, up to 128 UTF-8 bytes.</div> </div> </div> <p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><a className="msb-type" href="#sandboxhandle">SandboxHandle</a></div> <div className="msb-param-desc">Handle with status and lifecycle control.</div> </div> </div> <Accordion title="Example">
python
handle = await Sandbox.get("api")
print(handle.status)
</Accordion>

<span className="msb-recv">Sandbox.</span><span className="msb-hn">list()</span>

<div className="msb-tags"><span className="msb-tag is-static">static</span><span className="msb-tag is-async">async</span></div>
python
@staticmethod
async def list() -> list[SandboxHandle]

List all sandboxes (running, stopped, and crashed).

<p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><a className="msb-type" href="#sandboxhandle">list[SandboxHandle]</a></div> <div className="msb-param-desc">All sandbox handles.</div> </div> </div> <Accordion title="Example">
python
for h in await Sandbox.list():
    print(h.name, h.status)
</Accordion>

<span className="msb-recv">Sandbox.</span><span className="msb-hn">list_with()</span>

<div className="msb-tags"><span className="msb-tag is-static">static</span><span className="msb-tag is-async">async</span></div>
python
@staticmethod
async def list_with(*, labels: Mapping[str, str] | None = None) -> list[SandboxHandle]

List sandboxes filtered by label. Only sandboxes whose labels match every supplied key/value pair are returned.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>labels</code><span className="msb-type">Mapping[str, str] | None</span></div> <div className="msb-param-desc">Label key/value pairs to match. <code>None</code> returns every sandbox, like <a href="#sandbox-list">list()</a>.</div> </div> </div> <p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><a className="msb-type" href="#sandboxhandle">list[SandboxHandle]</a></div> <div className="msb-param-desc">Matching sandbox handles.</div> </div> </div> <Accordion title="Example">
python
workers = await Sandbox.list_with(labels={"role": "worker"})
</Accordion>

<span className="msb-recv">Sandbox.</span><span className="msb-hn">remove()</span>

<div className="msb-tags"><span className="msb-tag is-static">static</span><span className="msb-tag is-async">async</span></div>
python
@staticmethod
async def remove(name: str) -> None

Delete a stopped sandbox and all its state from disk (configuration, logs, runtime directory). Fails if the sandbox is still running; stop it first.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>name</code><span className="msb-type">str</span></div> <div className="msb-param-desc">Sandbox name, up to 128 UTF-8 bytes.</div> </div> </div> <Accordion title="Example">
python
await Sandbox.remove("api")
</Accordion>

Instance properties


<span className="msb-recv">sb.</span><span className="msb-hn">name</span>

<div className="msb-tags"><span className="msb-tag is-instance">instance</span><span className="msb-tag is-async">async</span></div>
python
async def name(self) -> str

The sandbox name. This is an async method; call await sb.name().

<p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><span className="msb-type">str</span></div> <div className="msb-param-desc">Sandbox name, up to 128 UTF-8 bytes.</div> </div> </div> <Accordion title="Example">
python
print(await sb.name())
</Accordion>

<span className="msb-recv">sb.</span><span className="msb-hn">owns_lifecycle</span>

<div className="msb-tags"><span className="msb-tag is-instance">property</span><span className="msb-tag is-async">async</span></div>
python
@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.

<p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><span className="msb-type">bool</span></div> <div className="msb-param-desc"><code>True</code> if this handle owns the lifecycle.</div> </div> </div>

<span className="msb-recv">sb.</span><span className="msb-hn">fs</span>

<div className="msb-tags"><span className="msb-tag is-instance">property</span></div>
python
@property
def fs(self) -> SandboxFsOps

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.

<p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><a className="msb-type" href="/sdk/python/filesystem">SandboxFsOps</a></div> <div className="msb-param-desc">Filesystem handle.</div> </div> </div> <Accordion title="Example">
python
await sb.fs.write("/tmp/hello.txt", b"hi")
</Accordion>

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.


<span className="msb-recv">sb.</span><span className="msb-hn">attach()</span>

<div className="msb-tags"><span className="msb-tag is-instance">instance</span><span className="msb-tag is-async">async</span></div>
python
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

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.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>cmd</code><span className="msb-type">str</span></div> <div className="msb-param-desc">Command to run.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>args</code><span className="msb-type">list[str] | None</span></div> <div className="msb-param-desc">Command arguments.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>cwd</code><span className="msb-type">str | None</span></div> <div className="msb-param-desc">Working directory.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>user</code><span className="msb-type">str | None</span></div> <div className="msb-param-desc">Guest user.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>env</code><span className="msb-type">Mapping[str, str] | None</span></div> <div className="msb-param-desc">Environment variables.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>detach_keys</code><span className="msb-type">str | None</span></div> <div className="msb-param-desc">Custom detach key sequence.</div> </div> </div> <p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><span className="msb-type">int</span></div> <div className="msb-param-desc">Exit code of the process.</div> </div> </div> <Accordion title="Example">
python
code = await sb.attach("python", ["-i"])
</Accordion>

<span className="msb-recv">sb.</span><span className="msb-hn">attach_shell()</span>

<div className="msb-tags"><span className="msb-tag is-instance">instance</span><span className="msb-tag is-async">async</span></div>
python
async def attach_shell(self) -> int

Attach your terminal to the sandbox's default shell for an interactive session.

<p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><span className="msb-type">int</span></div> <div className="msb-param-desc">Exit code.</div> </div> </div> <Accordion title="Example">
python
await sb.attach_shell()
</Accordion>

<span className="msb-recv">sb.</span><span className="msb-hn">metrics()</span>

<div className="msb-tags"><span className="msb-tag is-instance">instance</span><span className="msb-tag is-async">async</span></div>
python
async def metrics(self) -> SandboxMetrics

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.

<p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><a className="msb-type" href="#sandboxmetrics">SandboxMetrics</a></div> <div className="msb-param-desc">Resource metrics.</div> </div> </div> <Accordion title="Example">
python
m = await sb.metrics()
print(f"cpu {m.cpu_percent:.1f}% · mem {m.memory_bytes // 1_048_576} MiB")
</Accordion>

<span className="msb-recv">sb.</span><span className="msb-hn">metrics_stream()</span>

<div className="msb-tags"><span className="msb-tag is-instance">instance</span><span className="msb-tag is-async">async</span></div>
python
async def metrics_stream(self, interval: float = 1.0) -> MetricsStream

Stream resource metrics at a regular interval. The returned MetricsStream supports both recv() and async for.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>interval</code><span className="msb-type">float</span></div> <div className="msb-param-desc">Seconds between metric snapshots. Default <code>1.0</code>.</div> </div> </div> <p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><a className="msb-type" href="#metricsstream">MetricsStream</a></div> <div className="msb-param-desc">Async stream yielding a snapshot each interval.</div> </div> </div> <Accordion title="Example">
python
stream = await sb.metrics_stream(1.0)
async for snapshot in stream:
    print(f"{snapshot.cpu_percent:.1f}%")
</Accordion>

<span className="msb-recv">sb.</span><span className="msb-hn">logs()</span>

<div className="msb-tags"><span className="msb-tag is-instance">instance</span><span className="msb-tag is-async">async</span></div>
python
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]

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.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>tail</code><span className="msb-type">int | None</span></div> <div className="msb-param-desc">Show only the last N entries after other filters apply.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>since_ms</code><span className="msb-type">float | None</span></div> <div className="msb-param-desc">Inclusive lower bound on entry timestamp (ms since epoch).</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>until_ms</code><span className="msb-type">float | None</span></div> <div className="msb-param-desc">Exclusive upper bound on entry timestamp (ms since epoch).</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>sources</code><a className="msb-type" href="#logsource">list[LogReadSource] | None</a></div> <div className="msb-param-desc">Sources to include. <code>None</code> = <code>["stdout", "stderr", "output"]</code>. Add <code>"system"</code> to merge runtime/kernel diagnostics, or use <code>"all"</code> for all four.</div> </div> </div> <p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><a className="msb-type" href="#logentry">list[LogEntry]</a></div> <div className="msb-param-desc">Matching entries in chronological order.</div> </div> </div> <Accordion title="Example">
python
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"],
)
</Accordion>

<span className="msb-recv">sb.</span><span className="msb-hn">log_stream()</span>

<div className="msb-tags"><span className="msb-tag is-instance">instance</span><span className="msb-tag is-async">async</span></div>
python
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 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.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>sources</code><a className="msb-type" href="#logsource">list[LogReadSource] | None</a></div> <div className="msb-param-desc">Sources to include. Same semantics as <a href="#sb-logs">logs()</a>.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>since_ms</code><span className="msb-type">float | None</span></div> <div className="msb-param-desc">Inclusive lower bound on entry timestamp (ms since epoch).</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>from_cursor</code><span className="msb-type">str | None</span></div> <div className="msb-param-desc">Resume after this opaque cursor (from a prior <a href="#logentry">LogEntry.cursor</a>).</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>until_ms</code><span className="msb-type">float | None</span></div> <div className="msb-param-desc">Exclusive upper bound on entry timestamp (ms since epoch).</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>follow</code><span className="msb-type">bool</span></div> <div className="msb-param-desc">When <code>True</code>, keep the stream open and yield new entries as they arrive. Default <code>False</code>.</div> </div> </div> <p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><a className="msb-type" href="#logstream">LogStream</a></div> <div className="msb-param-desc">Async stream of log entries.</div> </div> </div> <Accordion title="Example">
python
stream = await sb.log_stream(follow=True)
async for entry in stream:
    print(entry.text().rstrip())
</Accordion>

<span className="msb-recv">sb.</span><span className="msb-hn">stop()</span>

<div className="msb-tags"><span className="msb-tag is-instance">instance</span><span className="msb-tag is-async">async</span></div>
python
async def stop(self, timeout: float | None = None) -> None

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.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>timeout</code><span className="msb-type">float | None</span></div> <div className="msb-param-desc">Seconds to wait for graceful exit before force-kill. <code>None</code> uses the ten-second default.</div> </div> </div> <Accordion title="Example">
python
await sb.stop()
</Accordion>

<span className="msb-recv">sb.</span><span className="msb-hn">request_stop()</span>

<div className="msb-tags"><span className="msb-tag is-instance">instance</span><span className="msb-tag is-async">async</span></div>
python
async def request_stop(self) -> None

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.

<Accordion title="Example">
python
await sb.request_stop()
</Accordion>

<span className="msb-recv">sb.</span><span className="msb-hn">kill()</span>

<div className="msb-tags"><span className="msb-tag is-instance">instance</span><span className="msb-tag is-async">async</span></div>
python
async def kill(self, timeout: float | None = None) -> None

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.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>timeout</code><span className="msb-type">float | None</span></div> <div className="msb-param-desc">Seconds to wait for the stopped state to be observed.</div> </div> </div> <Accordion title="Example">
python
await sb.kill()  # SIGKILL, no graceful shutdown
</Accordion>

<span className="msb-recv">sb.</span><span className="msb-hn">request_kill()</span>

<div className="msb-tags"><span className="msb-tag is-instance">instance</span><span className="msb-tag is-async">async</span></div>
python
async def request_kill(self) -> None

Request force termination and return once the signal is sent, without waiting for stopped state.

<Accordion title="Example">
python
await sb.request_kill()
</Accordion>

<span className="msb-recv">sb.</span><span className="msb-hn">request_drain()</span>

<div className="msb-tags"><span className="msb-tag is-instance">instance</span><span className="msb-tag is-async">async</span></div>
python
async def request_drain(self) -> None

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.

<Accordion title="Example">
python
await sb.request_drain()
</Accordion>

<span className="msb-recv">sb.</span><span className="msb-hn">wait_until_stopped()</span>

<div className="msb-tags"><span className="msb-tag is-instance">instance</span><span className="msb-tag is-async">async</span></div>
python
async def wait_until_stopped(self) -> SandboxStopResult

Block until the sandbox is observed in a terminal non-running state, without triggering a stop or kill request.

<p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><a className="msb-type" href="#sandboxstopresult">SandboxStopResult</a></div> <div className="msb-param-desc">Terminal status and optional observed exit code.</div> </div> </div> <Accordion title="Example">
python
result = await sb.wait_until_stopped()
print(result.status, result.exit_code)
</Accordion>

<span className="msb-recv">sb.</span><span className="msb-hn">detach()</span>

<div className="msb-tags"><span className="msb-tag is-instance">instance</span><span className="msb-tag is-async">async</span></div>
python
async def detach(self) -> None

Release the handle without stopping the sandbox. The sandbox continues running as a background process. Reconnect later with Sandbox.get().

<Accordion title="Example">
python
sb = await Sandbox.create("worker", image="python", detached=True)
await sb.detach()  # keeps running in the background
</Accordion>

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.


<span className="msb-recv">Patch.</span><span className="msb-hn">text()</span>

<div className="msb-tags"><span className="msb-tag is-builder">factory</span><span className="msb-tag is-static">static</span></div>
python
@staticmethod
def text(path: str, content: str, *, mode: int | None = None, replace: bool = False) -> PatchConfig

Write UTF-8 text content at path.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>path</code><span className="msb-type">str</span></div> <div className="msb-param-desc">Absolute path inside the guest.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>content</code><span className="msb-type">str</span></div> <div className="msb-param-desc">Text content.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>mode</code><span className="msb-type">int | None</span></div> <div className="msb-param-desc">File mode, e.g. <code>0o644</code>.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>replace</code><span className="msb-type">bool</span></div> <div className="msb-param-desc">When <code>True</code>, overwrite an existing path.</div> </div> </div> <Accordion title="Example">
python
from microsandbox import Patch, Sandbox

sb = await Sandbox.create(
    "api",
    image="python",
    patches=[Patch.text("/etc/app.conf", "debug=1\n", mode=0o644)],
)
</Accordion>

<span className="msb-recv">Patch.</span><span className="msb-hn">append()</span>

<div className="msb-tags"><span className="msb-tag is-builder">factory</span><span className="msb-tag is-static">static</span></div>
python
@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.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>path</code><span className="msb-type">str</span></div> <div className="msb-param-desc">Absolute path inside the guest.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>content</code><span className="msb-type">str</span></div> <div className="msb-param-desc">Text to append.</div> </div> </div>

<span className="msb-recv">Patch.</span><span className="msb-hn">mkdir()</span>

<div className="msb-tags"><span className="msb-tag is-builder">factory</span><span className="msb-tag is-static">static</span></div>
python
@staticmethod
def mkdir(path: str, *, mode: int | None = None) -> PatchConfig

Create a directory at path. Idempotent: a no-op if the directory already exists.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>path</code><span className="msb-type">str</span></div> <div className="msb-param-desc">Absolute path inside the guest.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>mode</code><span className="msb-type">int | None</span></div> <div className="msb-param-desc">Directory mode, e.g. <code>0o755</code>.</div> </div> </div>

<span className="msb-recv">Patch.</span><span className="msb-hn">remove()</span>

<div className="msb-tags"><span className="msb-tag is-builder">factory</span><span className="msb-tag is-static">static</span></div>
python
@staticmethod
def remove(path: str) -> PatchConfig

Delete a file or directory at path. Idempotent: a no-op if the path doesn't exist.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>path</code><span className="msb-type">str</span></div> <div className="msb-param-desc">Absolute path inside the guest.</div> </div> </div>

<span className="msb-recv">Patch.</span><span className="msb-hn">copy_file()</span>

<div className="msb-tags"><span className="msb-tag is-builder">factory</span><span className="msb-tag is-static">static</span></div>
python
@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.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>src</code><span className="msb-type">str</span></div> <div className="msb-param-desc">Host source file.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>dst</code><span className="msb-type">str</span></div> <div className="msb-param-desc">Absolute destination path inside the guest.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>mode</code><span className="msb-type">int | None</span></div> <div className="msb-param-desc">File mode, e.g. <code>0o644</code>. <code>None</code> keeps the source mode.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>replace</code><span className="msb-type">bool</span></div> <div className="msb-param-desc">When <code>True</code>, overwrite an existing path at <code>dst</code>.</div> </div> </div>

<span className="msb-recv">Patch.</span><span className="msb-hn">copy_dir()</span>

<div className="msb-tags"><span className="msb-tag is-builder">factory</span><span className="msb-tag is-static">static</span></div>
python
@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.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>src</code><span className="msb-type">str</span></div> <div className="msb-param-desc">Host source directory.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>dst</code><span className="msb-type">str</span></div> <div className="msb-param-desc">Absolute destination path inside the guest.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>replace</code><span className="msb-type">bool</span></div> <div className="msb-param-desc">When <code>True</code>, overwrite an existing path at <code>dst</code>.</div> </div> </div>

<span className="msb-recv">Patch.</span><span className="msb-hn">symlink()</span>

<div className="msb-tags"><span className="msb-tag is-builder">factory</span><span className="msb-tag is-static">static</span></div>
python
@staticmethod
def symlink(target: str, link: str, *, replace: bool = False) -> PatchConfig

Create a symlink at link pointing to target.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>target</code><span className="msb-type">str</span></div> <div className="msb-param-desc">What the symlink points to (literal symlink target text).</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>link</code><span className="msb-type">str</span></div> <div className="msb-param-desc">Absolute path of the symlink itself.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>replace</code><span className="msb-type">bool</span></div> <div className="msb-param-desc">When <code>True</code>, overwrite an existing path at <code>link</code>.</div> </div> </div>

Types

SandboxConfig

<div className="msb-tags"><span className="msb-tag is-type">kwargs</span></div> <p className="msb-backref">Used by <a href="#sandbox-create">create()</a> · <a href="#sandbox-create_with_progress">create_with_progress()</a></p>

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
memoryint512Guest memory in MiB. This is a limit, not a reservation
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

<div className="msb-tags"><span className="msb-tag is-type">dataclass</span></div> <p className="msb-backref">Used by <a href="#sandboxconfig">create(init=...)</a></p>

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
python
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

<div className="msb-tags"><span className="msb-tag is-type">enum</span></div> <p className="msb-backref">Used by <a href="#sandboxconfig">create(security=...)</a></p>

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

<div className="msb-tags"><span className="msb-tag is-type">class</span></div> <p className="msb-backref">Returned by <a href="#sandbox-get">Sandbox.get()</a> · <a href="#sandbox-list">Sandbox.list()</a> · <a href="#sandbox-list_with">Sandbox.list_with()</a></p>

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
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

SandboxStopResult

<div className="msb-tags"><span className="msb-tag is-type">class</span></div> <p className="msb-backref">Returned by <a href="#sb-wait_until_stopped">wait_until_stopped()</a></p>

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

<div className="msb-tags"><span className="msb-tag is-type">enum</span></div> <p className="msb-backref">Used by <a href="#sandboxhandle">SandboxHandle.status</a> · <a href="#sandboxstopresult">SandboxStopResult.status</a></p>

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

<div className="msb-tags"><span className="msb-tag is-type">class</span></div> <p className="msb-backref">Returned by <a href="#sb-metrics">metrics()</a> · <a href="#sb-metrics_stream">metrics_stream()</a></p>

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

<div className="msb-tags"><span className="msb-tag is-type">class</span></div> <p className="msb-backref">Returned by <a href="#sb-metrics_stream">metrics_stream()</a></p>

Async stream for receiving periodic metrics snapshots.

MethodReturnsDescription
__aiter__ / __anext__SandboxMetricsUse with async for

LogEntry

<div className="msb-tags"><span className="msb-tag is-type">class</span></div> <p className="msb-backref">Returned by <a href="#sb-logs">logs()</a> · iterated from <a href="#logstream">LogStream</a></p>

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

<div className="msb-tags"><span className="msb-tag is-type">class</span></div> <p className="msb-backref">Returned by <a href="#sb-log_stream">log_stream()</a></p>

Async stream of LogEntry values, returned by log_stream().

MethodReturnsDescription
__aiter__ / __anext__LogEntryUse with async for

LogSource

<div className="msb-tags"><span className="msb-tag is-type">literal</span></div> <p className="msb-backref">Used by <a href="#logentry">LogEntry.source</a> · <a href="#sb-logs">logs(sources=...)</a></p>

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 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
"all"Read shorthand for all four sources (accepted by sources=, never returned on an entry)

LogLevel

<div className="msb-tags"><span className="msb-tag is-type">enum</span></div> <p className="msb-backref">Used by <a href="#sandboxconfig">create(log_level=...)</a></p>

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

<div className="msb-tags"><span className="msb-tag is-type">enum</span></div> <p className="msb-backref">Used by <a href="#sandboxconfig">create(pull_policy=...)</a></p>

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

<div className="msb-tags"><span className="msb-tag is-type">dataclass</span></div> <p className="msb-backref">Used by <a href="#sandboxconfig">create(registry_auth=...)</a></p>

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

FieldTypeDescription
usernamestrRegistry username
passwordstrRegistry password

PatchConfig

<div className="msb-tags"><span className="msb-tag is-type">dataclass</span></div> <p className="msb-backref">Returned by <a href="#patch-text">Patch.* factory methods</a> · used by <a href="#sandboxconfig">create(patches=...)</a></p>

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

<div className="msb-tags"><span className="msb-tag is-type">class</span></div> <p className="msb-backref">Returned by <a href="#sandbox-create_with_progress">create_with_progress()</a></p>

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
python
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

<div className="msb-tags"><span className="msb-tag is-type">class</span></div> <p className="msb-backref">Iterated from <a href="#pullsession">PullSession.progress</a></p>

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
python
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()