Back to Microsandbox

Sandbox

docs/sdk/python/sandbox.mdx

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

For local runtime installation and verification, see Runtime setup.

Sandbox

<p className="msb-member-group">Instance properties</p>

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

<Tooltip tip="On microsandbox cloud this is false; the cloud worker owns the sandbox process, not your handle."><span className="msb-badge-note">On cloud <Icon icon="circle-info" size={11} /></span></Tooltip>

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>

python
@property
def fs(self) -> SandboxFsOps
<Accordion title="Example">
python
await sb.fs.write("/tmp/hello.txt", b"hi")
</Accordion>

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> <p className="msb-member-group">Static methods</p>

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

python
@staticmethod
async def create(name: str, **kwargs) -> Sandbox
<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>

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>

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

python
@staticmethod
def create_with_progress(name: str, **kwargs) -> PullSession
<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>

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>

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

python
@staticmethod
async def start(name: str, *, detached: bool = False) -> Sandbox
<Accordion title="Example">
python
sb = await Sandbox.start("api")
</Accordion>

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>

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

python
@staticmethod
async def get(name: str) -> SandboxHandle
<Accordion title="Example">
python
handle = await Sandbox.get("api")
print(handle.status)
</Accordion>

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>

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

python
@staticmethod
async def list() -> SandboxPage
<Accordion title="Example">
python
page = await Sandbox.list()
for h in page.sandboxes:
    print(h.name, h.status)
</Accordion>

Return the first page of sandboxes (running, stopped, and crashed), ordered newest first. The default page size is 20.

<p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><span className="msb-type">SandboxPage</span></div> <div className="msb-param-desc">Handles in this page and an optional cursor for the next page.</div> </div> </div>

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

python
@staticmethod
async def list_with(
    *,
    cursor: str | None = None,
    limit: int | None = None,
    labels: Mapping[str, str] | None = None,
) -> SandboxPage
<Accordion title="Example">
python
page = await Sandbox.list_with(limit=50, labels={"role": "worker"})
if page.next_cursor is not None:
    next_page = await Sandbox.list_with(
        cursor=page.next_cursor,
        limit=50,
        labels={"role": "worker"},
    )
</Accordion>

Return a configured page of sandboxes. Label filters are applied before pagination and match every supplied key/value pair.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>cursor</code><span className="msb-type">str | None</span></div> <div className="msb-param-desc">Opaque <code>next_cursor</code> from the preceding page.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>limit</code><span className="msb-type">int | None</span></div> <div className="msb-param-desc">Page size from 1 through 100. Defaults to 20.</div> </div> <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"><span className="msb-type">SandboxPage</span></div> <div className="msb-param-desc">Matching handles and an optional cursor for the next page.</div> </div> </div>

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

python
@staticmethod
async def remove(name: str) -> None
<Accordion title="Example">
python
await Sandbox.remove("api")
</Accordion>

Delete a stopped sandbox by name. See Remove for the exact local deletion scope and the external resources that are preserved. 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> <p className="msb-member-group">Instance methods</p>

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">name()</span>

python
async def name(self) -> str
<Accordion title="Example">
python
print(await sb.name())
</Accordion>

Return the sandbox 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>

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

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
<Accordion title="Example">
python
code = await sb.attach("python", ["-i"])
</Accordion>

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>

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

python
async def attach_shell(self) -> int
<Accordion title="Example">
python
await sb.attach_shell()
</Accordion>

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>

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

<Tooltip tip="Not available on microsandbox cloud."><span className="msb-badge-local">Local-only <Icon icon="circle-info" size={11} /></span></Tooltip>

python
async def ping(self) -> SandboxPingResult
<Accordion title="Example">
python
health = await sb.ping()
print(f"{health.name}: {health.latency_ms:.1f} ms")
</Accordion>

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.

<p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><a className="msb-type" href="#sandboxpingresult">SandboxPingResult</a></div> <div className="msb-param-desc">Sandbox name and agent round-trip latency.</div> </div> </div>

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

<Tooltip tip="Not available on microsandbox cloud."><span className="msb-badge-local">Local-only <Icon icon="circle-info" size={11} /></span></Tooltip>

python
async def touch(self) -> SandboxTouchResult
<Accordion title="Example">
python
keepalive = await sb.touch()
print(f"{keepalive.name}: {keepalive.activity_seq}")
</Accordion>

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.

<p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><a className="msb-type" href="#sandboxtouchresult">SandboxTouchResult</a></div> <div className="msb-param-desc">Sandbox name and updated activity sequence.</div> </div> </div>

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

<Tooltip tip="modify is not available on microsandbox cloud; recreate the sandbox with the new configuration."><span className="msb-badge-local">Local-only <Icon icon="circle-info" size={11} /></span></Tooltip>

python
async def modify(
    self,
    *,
    cpus: int | None = None,
    max_cpus: int | None = None,
    memory: int | None = None,
    max_memory: int | None = None,
    root_disk_size: 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,
    secrets: Mapping[str, SecretModifySpec] | None = None,
    secrets_rm: list[str] | None = None,
    policy: ModificationPolicy | None = None,
    dry_run: bool = False,
) -> SandboxModificationPlan
<Accordion title="Example">
python
from microsandbox import (
    ModificationDisposition,
    ModificationPolicy,
    ResourceConvergenceState,
)

# 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", []):
    if r["state"] is ResourceConvergenceState.APPLIED:
        print(f'{r["resource"]}: {r["requested"]} -> {r["actual"]}')

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

# Make an env change active now by restarting
await sb.modify(env={"MODE": "prod"}, policy=ModificationPolicy.RESTART)

# Grow the managed OCI root disk offline and restart
await sb.modify(root_disk_size=8192, policy=ModificationPolicy.RESTART)

# Add or rotate a host-environment secret; restart if it is newly added
await sb.modify(
    secrets={
        "API_KEY": {
            "env": "API_KEY",
            "allowed_hosts": ["api.example.com"],
        },
    },
    policy=ModificationPolicy.RESTART,
)

# Remove an existing secret
await sb.modify(secrets_rm=["OLD_API_KEY"])
</Accordion>

Plan or apply a configuration change. The returned plan uses ModificationDisposition to classify when each change takes effect, and apply is all-or-nothing.

cpus and memory resize live within the max_cpus / max_memory ceilings; raising a ceiling requires a restart. root_disk_size changes are offline: managed and flat OCI root disks grow only, tmpfs root disks can change in either direction on the next boot, and user-supplied disk images are rejected. Env and workdir changes affect future execs only. On a stopped sandbox, changes are saved for the next boot.

Secret specs are keyed by stable secret name. Each SecretModifySpec selects at most one source—env, value, or store—and may also set placeholder and allowed_hosts; omitting a source updates only the other supplied fields. Plans expose only safe references and metadata; raw secret values never appear in a plan. Removal is explicit through secrets_rm.

The returned SandboxModificationPlan is a typed dictionary:

KeyTypeDescription
sandboxstrSandbox being modified
statusSandboxStatusStatus used for classification
appliedboolWhether the changes were applied; False with dry_run=True
policyModificationPolicyPolicy used to produce the plan
changeslist[ConfigPlannedChange | SecretPlannedChange]Typed config or secret changes, discriminated by PlannedChangeKind
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[ResourceResizeStatus]Live resize outcomes after apply, using ResourceKind and ResourceConvergenceState. 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.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>cpus</code><span className="msb-type">int | None</span></div> <div className="msb-param-desc">Desired effective vCPU count. Live when within the booted <code>max_cpus</code>.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>max_cpus</code><span className="msb-type">int | None</span></div> <div className="msb-param-desc">Boot-time maximum possible vCPUs (restart-backed).</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>memory</code><span className="msb-type">int | None</span></div> <div className="msb-param-desc">Desired effective guest memory in MiB. Live when within the booted <code>max_memory</code>.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>max_memory</code><span className="msb-type">int | None</span></div> <div className="msb-param-desc">Boot-time maximum hotpluggable memory in MiB (restart-backed).</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>root_disk_size</code><span className="msb-type">int | None</span></div> <div className="msb-param-desc">Desired root disk size in MiB. Managed and flat OCI disks are grow-only; applies on restart or next start.</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 to set for future execs.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>env_rm</code><span className="msb-type">list[str] | None</span></div> <div className="msb-param-desc">Environment variable keys to remove.</div> </div> <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">Labels to set.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>labels_rm</code><span className="msb-type">list[str] | None</span></div> <div className="msb-param-desc">Label keys to remove.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>workdir</code><span className="msb-type">str | None</span></div> <div className="msb-param-desc">Working directory for future execs.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>secrets</code><span className="msb-type">Mapping[str, SecretModifySpec] | None</span></div> <div className="msb-param-desc">Desired secret specs keyed by secret name. Each spec may contain at most one of <code>env</code>, <code>value</code>, or <code>store</code>, plus optional <code>placeholder</code> and <code>allowed_hosts</code>.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>secrets_rm</code><span className="msb-type">list[str] | None</span></div> <div className="msb-param-desc">Secret names to remove explicitly.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>policy</code><a className="msb-type" href="#modificationpolicy">ModificationPolicy | None</a></div> <div className="msb-param-desc"><code>NO_RESTART</code> (default) applies only changes that can complete without restarting; <code>NEXT_START</code> persists changes for the next start without mutating a running VM; <code>RESTART</code> restarts if needed so restart-required changes become active now.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>dry_run</code><span className="msb-type">bool</span></div> <div className="msb-param-desc">When <code>True</code>, compute the plan without applying anything. 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="#sandboxmodificationplan">SandboxModificationPlan</a></div> <div className="msb-param-desc">The modification plan, applied unless <code>dry_run=True</code>.</div> </div> </div>

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

<Tooltip tip="Resource metrics are not available on microsandbox cloud; use an external monitoring system."><span className="msb-badge-local">Local-only <Icon icon="circle-info" size={11} /></span></Tooltip>

python
async def metrics(self) -> SandboxMetrics
<Accordion title="Example">
python
m = await sb.metrics()
print(f"cpu {m.cpu_percent:.1f}% · mem {m.memory_bytes // 1_048_576} MiB")
</Accordion>

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>

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

<Tooltip tip="Resource metrics are not available on microsandbox cloud; use an external monitoring system."><span className="msb-badge-local">Local-only <Icon icon="circle-info" size={11} /></span></Tooltip>

python
async def metrics_stream(self, interval: float = 1.0) -> MetricsStream
<Accordion title="Example">
python
stream = await sb.metrics_stream(1.0)
async for snapshot in stream:
    print(f"{snapshot.cpu_percent:.1f}%")
</Accordion>

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>

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

<Tooltip tip="Bounded log reads are not available on microsandbox cloud; follow live with log streaming and persist output externally."><span className="msb-badge-local">Local-only <Icon icon="circle-info" size={11} /></span></Tooltip>

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]
<Accordion title="Example">
python
import time
from microsandbox import LogReadSource, 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=[
        LogReadSource.STDOUT,
        LogReadSource.STDERR,
        LogReadSource.OUTPUT,
        LogReadSource.SYSTEM,
    ],
)
</Accordion>

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). Add LogReadSource.SYSTEM to include synthetic lifecycle markers and runtime/kernel diagnostic lines, or use LogReadSource.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="#logreadsource">list[LogReadSource] | None</a></div> <div className="msb-param-desc">Sources to include. <code>None</code> selects <code>STDOUT</code>, <code>STDERR</code>, and <code>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>

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

<Tooltip tip="On microsandbox cloud, log streaming is follow-only; set follow. Bounded, non-follow reads are not available."><span className="msb-badge-limited">Limited on cloud <Icon icon="circle-info" size={11} /></span></Tooltip>

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
<Accordion title="Example">
python
stream = await sb.log_stream(follow=True)
async for entry in stream:
    print(entry.text().rstrip())
</Accordion>

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="#logreadsource">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>

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

python
async def stop(self, timeout: float | None = None) -> None
<Accordion title="Example">
python
await sb.stop()
</Accordion>

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>

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

python
async def request_stop(self) -> None
<Accordion title="Example">
python
await sb.request_stop()
</Accordion>

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.

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

<Tooltip tip="Not available on microsandbox cloud; use a graceful stop."><span className="msb-badge-local">Local-only <Icon icon="circle-info" size={11} /></span></Tooltip>

python
async def kill(self, timeout: float | None = None) -> None
<Accordion title="Example">
python
await sb.kill()  # SIGKILL, no graceful shutdown
</Accordion>

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>

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

<Tooltip tip="Not available on microsandbox cloud; use a graceful stop."><span className="msb-badge-local">Local-only <Icon icon="circle-info" size={11} /></span></Tooltip>

python
async def request_kill(self) -> None
<Accordion title="Example">
python
await sb.request_kill()
</Accordion>

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

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

<Tooltip tip="Not available on microsandbox cloud; use a graceful stop."><span className="msb-badge-local">Local-only <Icon icon="circle-info" size={11} /></span></Tooltip>

python
async def request_drain(self) -> None
<Accordion title="Example">
python
await sb.request_drain()
</Accordion>

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.

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

python
async def wait_until_stopped(self) -> SandboxStopResult
<Accordion title="Example">
python
result = await sb.wait_until_stopped()
print(result.status, result.exit_code)
</Accordion>

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>

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

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

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

Patch

Factory for pre-boot root filesystem patches.

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

python
@staticmethod
def text(path: str, content: str, *, mode: int | None = None, replace: bool = False) -> PatchConfig
<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>

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>

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

python
@staticmethod
def file(path: str, content: bytes, *, mode: int | None = None, replace: bool = False) -> PatchConfig

Write arbitrary bytes at path. Use text() for UTF-8 text.

<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">bytes</span></div> <div className="msb-param-desc">Binary file 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>

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

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>

<Tooltip tip="On microsandbox cloud, host sources resolve against your organization's host volume, not the computer running the SDK or CLI."><span className="msb-badge-note">On cloud <Icon icon="circle-info" size={11} /></span></Tooltip>

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>

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>

<Tooltip tip="On microsandbox cloud, host sources resolve against your organization's host volume, not the computer running the SDK or CLI."><span className="msb-badge-note">On cloud <Icon icon="circle-info" size={11} /></span></Tooltip>

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>

<Tooltip tip="On microsandbox cloud, host sources resolve against your organization's host volume, not the computer running the SDK or CLI."><span className="msb-badge-note">On cloud <Icon icon="circle-info" size={11} /></span></Tooltip>

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>

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>

SandboxHandle

<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 metadata and lifecycle handle for an existing sandbox.

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

str

Sandbox name, up to 128 UTF-8 bytes

<span className="msb-recv">handle.</span><span className="msb-hn">status</span>

SandboxStatus

Current status

<span className="msb-recv">handle.</span><span className="msb-hn">config_json</span>

str

Raw JSON configuration

<span className="msb-recv">handle.</span><span className="msb-hn">created_at</span>

float \| None

Creation timestamp (ms since epoch)

<span className="msb-recv">handle.</span><span className="msb-hn">updated_at</span>

float \| None

Last update timestamp (ms since epoch)

<span className="msb-recv">handle.</span><span className="msb-hn">config()</span>

python
config()

Parsed configuration

<p className="msb-label">Returns</p>

dict[str, Any]

<span className="msb-recv">handle.</span><span className="msb-hn">refresh()</span>

python
refresh()

Re-fetch status and metadata, returning a fresh handle

<p className="msb-label">Returns</p>

Awaitable[SandboxHandle]

<span className="msb-recv">handle.</span><span className="msb-hn">ping()</span>

python
ping()

Check agent reachability without refreshing idle activity; does not start stopped sandboxes

<p className="msb-label">Returns</p>

Awaitable[SandboxPingResult]

<span className="msb-recv">handle.</span><span className="msb-hn">touch()</span>

python
touch()

Explicitly refresh idle activity; does not start stopped sandboxes

<p className="msb-label">Returns</p>

Awaitable[SandboxTouchResult]

<span className="msb-recv">handle.</span><span className="msb-hn">modify()</span>

python
modify(...)

Plan or apply a configuration change; same kwargs as modify(). Does not start stopped sandboxes; changes persist for the next boot

<p className="msb-label">Returns</p>

Awaitable[SandboxModificationPlan]

<span className="msb-recv">handle.</span><span className="msb-hn">connect()</span>

python
connect(timeout=None)

Connect to a running sandbox, optionally with an explicit timeout in seconds

<p className="msb-label">Returns</p>

Awaitable[Sandbox]

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

python
start(*, detached=False)

Start in attached or detached mode

<p className="msb-label">Returns</p>

Awaitable[Sandbox]

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

python
stop(timeout=None)

Gracefully shut down and wait until stopped state is observed

<p className="msb-label">Returns</p>

Awaitable[None]

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

python
request_stop()

Request graceful shutdown without waiting

<p className="msb-label">Returns</p>

Awaitable[None]

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

python
kill(timeout=None)

Force terminate and wait until stopped state is observed

<p className="msb-label">Returns</p>

Awaitable[None]

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

python
request_kill()

Request force termination without waiting

<p className="msb-label">Returns</p>

Awaitable[None]

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

python
request_drain()

Request graceful drain without waiting

<p className="msb-label">Returns</p>

Awaitable[None]

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

python
wait_until_stopped()

Block until the sandbox reaches terminal state

<p className="msb-label">Returns</p>

Awaitable[SandboxStopResult]

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

python
remove()

Delete sandbox and state

<p className="msb-label">Returns</p>

Awaitable[None]

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

python
metrics()

Point-in-time resource metrics

<p className="msb-label">Returns</p>

Awaitable[SandboxMetrics]

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

python
logs(...)

Read captured exec.log (works without starting)

<p className="msb-label">Returns</p>

Awaitable[list[LogEntry]]

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

python
log_stream(...)

Stream captured log entries (works without starting)

<p className="msb-label">Returns</p>

Awaitable[LogStream]

<span className="msb-recv">handle.</span><span className="msb-hn">snapshot()</span>

python
snapshot(name)

Create a named snapshot of the sandbox

<p className="msb-label">Returns</p>

Awaitable[Snapshot]

MetricsStream

<p className="msb-backref">Returned by <a href="#sb-metrics_stream">metrics_stream()</a></p>

Async stream for receiving periodic metrics snapshots.

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

python
__aiter__()

Use with async for

<p className="msb-label">Returns</p>

SandboxMetrics

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

python
__anext__()

Use with async for

<p className="msb-label">Returns</p>

SandboxMetrics

LogEntry

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

<span className="msb-recv">entry.</span><span className="msb-hn">timestamp_ms</span>

float

Wall-clock capture time (ms since Unix epoch, UTC)

<span className="msb-recv">entry.</span><span className="msb-hn">source</span>

LogSource

Where the chunk came from

<span className="msb-recv">entry.</span><span className="msb-hn">session_id</span>

int \| None

Relay-monotonic session id; None for "system" entries

<span className="msb-recv">entry.</span><span className="msb-hn">cursor</span>

str

Opaque resume token; pass back via log_stream(from_cursor=...)

<span className="msb-recv">entry.</span><span className="msb-hn">data</span>

bytes

The chunk's raw bytes

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

python
text()

Convenience: UTF-8 decode of data (lossy; invalid bytes are replaced)

<p className="msb-label">Returns</p>

str

LogStream

<p className="msb-backref">Returned by <a href="#sb-log_stream">log_stream()</a></p>

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

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

python
__aiter__()

Use with async for

<p className="msb-label">Returns</p>

LogEntry

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

python
__anext__()

Use with async for

<p className="msb-label">Returns</p>

LogEntry

PullSession

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

<span className="msb-recv">session.</span><span className="msb-hn">progress</span>

AsyncIterator[PullEvent]

Async iterator of pull progress events

<span className="msb-recv">session.</span><span className="msb-hn">result()</span>

python
result()

Await once to get the final running sandbox. A second call raises RuntimeError

<p className="msb-label">Returns</p>

Awaitable[Sandbox]

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

Types

SandboxConfig

<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 | os.PathLike[str] | ImageSource-OCI image, local path, or disk image. Required unless from_snapshot= is passed. Use Image.oci("python:3.12", root_disk=RootDisk.managed(8192)) to set a managed OCI root-disk size
from_snapshotstr | os.PathLike[str]-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
thp"always" | "madvise" | "never""madvise"Guest transparent huge-page policy selected at boot
workdirstr-Default working directory for commands
shellstr"/bin/sh"Shell for shell() calls
securitySecurityProfileDEFAULTIn-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
entrypointSequence[str]-Override the image's stored ENTRYPOINT used by exec_default; literal exec and shell ignore it
cmdSequence[str]-Override the image's stored CMD used by exec_default; an empty sequence clears CMD
initstr | InitConfig|InitOptions-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
ephemeralboolFalseIf True, the sandbox and all its persisted state are removed automatically once it stops, rather than left on disk for restart
envMapping[str, str]{}Environment variables visible to all commands
labelsMapping[str, str]{}User-defined labels attached to the sandbox. Filter with list_with(); see Select in bulk. Keys starting with sandbox., microsandbox., or service. are reserved and rejected
scriptsMapping[str, str]{}Named scripts mounted at /.msb/scripts/ and added to PATH
pull_policyPullPolicyIF_MISSINGImage pull behavior
log_levelLogLevel-Override log verbosity
registry_authRegistryAuth-Private registry credentials
registry_insecureboolFalsePull images over plain HTTP instead of HTTPS (local or self-hosted registries)
registry_ca_certslist[bytes | bytearray | str | os.PathLike][]Additional CA roots for image pulls. File paths are read when create() is called.
volumesMapping[str, MountConfig]{}Volume mounts. See Volumes
patchesSequence[PatchConfig][]Rootfs modifications applied before boot
portsMapping[int, int] | Sequence[PortBinding]{}Port mappings. Mapping form is TCP and binds to 127.0.0.1; use PortBinding for explicit bind addresses or UDP
networkNetworkpublic profileNetwork policy and configuration
secretsSequence[SecretEntry][]Secret injection
on_secret_violationViolationAction|ViolationPolicyBLOCK_AND_LOGSandbox-wide default action when a secret placeholder would leak to a non-eligible destination. Shorthand for Network.on_secret_violation; if both are provided, this top-level value takes precedence. See Violation policy
detachedboolFalseIf True, spawn the sandbox in detached mode; call detach() before dropping the returned handle when it should keep running

InitConfig

<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 accepts a bare scalar for the simple case, an InitConfig dataclass, or an InitOptions typed dictionary 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"},
    ),
)

InitOptions

<p className="msb-backref">Used by <a href="#sandboxconfig">create(init=...)</a></p>

Typed-dictionary form of InitConfig. cmd is required; args and env are optional.

KeyTypeDescription
cmdstrAbsolute path or "auto" to the init binary
argslist[str]Supplemental argv
envdict[str, str]Extra environment variables

SecurityProfile

<p className="msb-backref">Used by <a href="#sandboxconfig">create(security=...)</a></p>

Sandbox-wide in-guest security profile.

MemberValueDescription
SecurityProfile.DEFAULT"default"Standard profile
SecurityProfile.RESTRICTED"restricted"Sets no_new_privs, drops mount-admin capability from user commands, and forces nosuid,nodev on user mounts

SandboxPage

One stable, newest-first page returned by Sandbox.list() or Sandbox.list_with().

PropertyTypeDescription
sandboxeslist[SandboxHandle]Handles in this page
next_cursorstr | NoneOpaque continuation cursor, or None on the final page

SandboxPingResult

<p className="msb-backref">Returned by <a href="#sb-ping">ping()</a></p>

Agent reachability result.

PropertyTypeDescription
namestrSandbox name
latency_msfloatRound-trip latency in milliseconds

SandboxTouchResult

<p className="msb-backref">Returned by <a href="#sb-touch">touch()</a></p>

Explicit idle-refresh result.

PropertyTypeDescription
namestrSandbox name
activity_seqintMonotonic activity sequence after the touch

SandboxStopResult

<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
statusSandboxStatusTerminal status that was observed
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

<p className="msb-backref">Used by <a href="#sandboxhandle">SandboxHandle.status</a> · <a href="#sandboxstopresult">SandboxStopResult.status</a></p>

Sandbox lifecycle status returned by status fields.

MemberValueDescription
SandboxStatus.CREATED"created"Sandbox metadata exists but the VM has not started
SandboxStatus.STARTING"starting"A start request is in progress
SandboxStatus.RUNNING"running"Guest agent is ready; exec, shell, and fs work
SandboxStatus.STOPPED"stopped"VM shut down; configuration persisted; can be restarted
SandboxStatus.CRASHED"crashed"VM exited unexpectedly (kernel panic, OOM, etc.)
SandboxStatus.DRAINING"draining"Graceful shutdown in progress; existing commands finish, new ones rejected
SandboxStatus.PAUSED"paused"VM paused

BackendKind

<p className="msb-backref">Used by <code>set_default_backend()</code> · <code>backend_scope()</code> · returned by <code>default_backend_kind()</code></p>

Selected sandbox backend.

MemberValueDescription
BackendKind.LOCAL"local"Local libkrun backend
BackendKind.CLOUD"cloud"Microsandbox cloud backend; requires an API key or named profile
python
from microsandbox import BackendKind, set_default_backend

set_default_backend(BackendKind.CLOUD, profile="production")

ModificationPolicy

<p className="msb-backref">Used by <a href="#sb-modify">modify(policy=...)</a></p>

Controls how sandbox modifications that cannot apply live are handled.

MemberValueDescription
ModificationPolicy.NO_RESTART"no_restart"Apply only changes that do not require a restart
ModificationPolicy.NEXT_START"next_start"Persist changes for the next start without restarting a running VM
ModificationPolicy.RESTART"restart"Restart when required so changes become active immediately

SecretModifySpec

<p className="msb-backref">Used by <a href="#sb-modify">modify(secrets=...)</a></p>

Desired state for one secret. env, value, and store are mutually exclusive sources. Omit all three to update only the placeholder or allowed hosts.

KeyTypeDescription
envstrHost environment variable to resolve when applying the modification
valuestrRaw secret value. Stored in the durable sandbox config until replaced by a source reference
storestrReserved for a host-side secret store reference; currently unsupported
placeholderstrExplicit guest-visible placeholder. New secrets default to $MSB_<NAME>
allowed_hostslist[str]Desired allowed host patterns. A new secret requires at least one; an empty list leaves existing hosts unchanged

SandboxModificationPlan

Typed dictionary returned by Sandbox.modify() and SandboxHandle.modify(). Config changes and secret changes use different typed shapes and are discriminated by their kind member.

KeyTypeDescription
sandboxstrSandbox being modified
statusSandboxStatusStatus used to classify the requested changes
appliedboolWhether the plan was applied
policyModificationPolicyPolicy used to produce the plan
changeslist[ConfigPlannedChange | SecretPlannedChange]Planned changes
conflictslist[ModificationConflict]Blocking conflicts, each with field and message
warningslist[ModificationWarning]Non-fatal warnings, each with field and message
resize_statuslist[ResourceResizeStatus]Optional live-resize convergence results

ConfigPlannedChange has kind, field, change, and disposition, plus optional before, after, and reason. SecretPlannedChange has kind, field, name, change, and disposition, plus optional before_ref, after_ref, allow_hosts, and reason. Secret values never appear in a plan.

PlannedChangeKind

Discriminator for entries in SandboxModificationPlan.changes.

MemberValueDescription
PlannedChangeKind.CONFIG"config"Ordinary configuration change
PlannedChangeKind.SECRET"secret"Secret metadata or material change

ChangeKind

Natural operation for a configuration change.

MemberValueDescription
ChangeKind.ADDED"added"A field is being added
ChangeKind.UPDATED"updated"A field is being updated
ChangeKind.REMOVED"removed"A field is being removed

SecretChangeKind

Natural operation for a secret change.

MemberValueDescription
SecretChangeKind.ADDED"added"A secret is being added
SecretChangeKind.ROTATED"rotated"Secret material is being rotated
SecretChangeKind.REMOVED"removed"A secret is being removed
SecretChangeKind.RENAMED"renamed"A secret is being renamed
SecretChangeKind.HOSTS_UPDATED"hosts updated"Allowed hosts are being updated
SecretChangeKind.PLACEHOLDER_UPDATED"placeholder updated"The guest-visible placeholder is being updated

ModificationDisposition

When or whether a planned change can take effect.

MemberValueDescription
ModificationDisposition.LIVE"live"Applies to the running VM now
ModificationDisposition.NEXT_START"next start"Applies the next time the sandbox starts
ModificationDisposition.REQUIRES_RESTART"requires restart"Requires a restart before taking effect
ModificationDisposition.UNSUPPORTED"unsupported"Cannot be changed through modify()

ResourceKind

Resource reported by a live-resize result.

MemberValueDescription
ResourceKind.CPUS"cpus"vCPU count
ResourceKind.MEMORY"memory"Guest memory

ResourceConvergenceState

Observed convergence state for an accepted live resize.

MemberValueDescription
ResourceConvergenceState.ACCEPTED"accepted"Runtime accepted the request
ResourceConvergenceState.CONVERGING"converging"Guest and VMM are still converging
ResourceConvergenceState.APPLIED"applied"Desired, actual, and enforced values match
ResourceConvergenceState.GUEST_REFUSED"guest-refused"Guest refused or failed to cooperate
ResourceConvergenceState.FAILED"failed"Resize failed

SandboxMetrics

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

LogSource

<p className="msb-backref">Returned by <a href="#logentry">LogEntry.source</a></p>

Source attached to each LogEntry.

MemberValueDescription
LogSource.STDOUT"stdout"Captured from a session's stdout (pipe mode, streams stayed separated)
LogSource.STDERR"stderr"Captured from a session's stderr (pipe mode)
LogSource.OUTPUT"output"Captured from a PTY session, where stdout and stderr are merged by the guest kernel
LogSource.SYSTEM"system"Synthetic lifecycle or runtime diagnostic entry

LogReadSource

<p className="msb-backref">Used by <a href="#sb-logs">logs(sources=...)</a> · <a href="#sb-log_stream">log_stream(sources=...)</a></p>

Log source selector accepted by logs() and log_stream().

MemberValueDescription
LogReadSource.STDOUT"stdout"Select stdout entries
LogReadSource.STDERR"stderr"Select stderr entries
LogReadSource.OUTPUT"output"Select PTY-merged output entries
LogReadSource.SYSTEM"system"Select lifecycle and runtime diagnostic entries
LogReadSource.ALL"all"Select all four sources

LogLevel

<p className="msb-backref">Used by <a href="#sandboxconfig">create(log_level=...)</a></p>

Sandbox process log verbosity.

MemberValueDescription
LogLevel.TRACE"trace"Most verbose, all diagnostic output
LogLevel.DEBUG"debug"Debug and higher
LogLevel.INFO"info"Info and higher
LogLevel.WARN"warn"Warnings and errors only
LogLevel.ERROR"error"Errors only

PullPolicy

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

MemberValueDescription
PullPolicy.ALWAYS"always"Pull the image every time, even if cached locally
PullPolicy.IF_MISSING"if-missing"Pull only if the image is not already cached. This is the default
PullPolicy.NEVER"never"Never pull; fail if the image is not cached locally

RegistryAuth

<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

<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
kindPatchKindPatch operation
pathstr | NoneAbsolute guest path (text / file / mkdir / remove / append)
contentstr | bytes | NoneText content for TEXT / APPEND, or binary content for FILE
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 for text, file, copy-file, or mkdir patches (e.g. 0o644)
replaceboolWhen True, overwrite an existing destination for text, file, copy, or symlink patches. Defaults to False

PatchKind

<p className="msb-backref">Used by <a href="#patchconfig">PatchConfig.kind</a></p>

Rootfs patch operation.

MemberValueDescription
PatchKind.TEXT"text"Write a UTF-8 text file
PatchKind.FILE"file"Write an arbitrary binary file
PatchKind.APPEND"append"Append to a text file
PatchKind.COPY_FILE"copy_file"Copy a host file into the guest
PatchKind.COPY_DIR"copy_dir"Copy a host directory into the guest
PatchKind.SYMLINK"symlink"Create a symbolic link
PatchKind.MKDIR"mkdir"Create a directory
PatchKind.REMOVE"remove"Remove a path

PullEvent

<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_typePullEventTypeEvent discriminator
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
from microsandbox import PullEventType, Sandbox

session = Sandbox.create_with_progress("my-sandbox", image="ubuntu:latest")
async with session:
    async for event in session.progress:
        if event.event_type is PullEventType.RESOLVED:
            print(f"{event.layer_count} layers, {event.total_download_bytes} bytes")
        elif event.event_type is PullEventType.LAYER_DOWNLOAD_PROGRESS:
            print(f"layer {event.layer_index}: {event.downloaded_bytes}/{event.total_bytes}")
    sb = await session.result()

PullEventType

Discriminator returned by PullEvent.event_type.

MemberValueDescription
PullEventType.RESOLVING"resolving"Resolving the image reference
PullEventType.RESOLVED"resolved"Manifest resolved
PullEventType.LAYER_DOWNLOAD_PROGRESS"layer_download_progress"Layer download advanced
PullEventType.LAYER_DOWNLOAD_COMPLETE"layer_download_complete"Layer download completed
PullEventType.LAYER_DOWNLOAD_VERIFYING"layer_download_verifying"Layer digest is being verified
PullEventType.LAYER_MATERIALIZE_STARTED"layer_materialize_started"Layer materialization started
PullEventType.LAYER_MATERIALIZE_PROGRESS"layer_materialize_progress"Layer materialization advanced
PullEventType.LAYER_MATERIALIZE_WRITING"layer_materialize_writing"Materialized layer is being written
PullEventType.LAYER_MATERIALIZE_COMPLETE"layer_materialize_complete"Layer materialization completed
PullEventType.STITCH_MERGING_TREES"stitch_merging_trees"Layer trees are being merged
PullEventType.STITCH_WRITING_FSMETA"stitch_writing_fsmeta"Filesystem metadata is being written
PullEventType.STITCH_WRITING_VMDK"stitch_writing_vmdk"VMDK image is being written
PullEventType.STITCH_COMPLETE"stitch_complete"Image stitching completed
PullEventType.COMPLETE"complete"Image pull completed