microsandbox
Lightweight VM sandboxes for Python applications that need hardware-level isolation for AI agents, tools, tests, and untrusted code.
The microsandbox Python package provides async Python bindings to the microsandbox runtime. It creates microVM-backed sandboxes from OCI images or other rootfs sources, then exposes command execution, guest filesystem access, networking, secrets, volumes, metrics, logs, snapshots, and SSH/SFTP through Python-friendly classes and dataclasses.
For the full API reference and longer guides, use the docs site:
A complete runtime in the configured home (MSB_HOME, or ~/.microsandbox by default) takes precedence over wheel binaries. Explicit binary paths still win. A partial home installation errors instead of falling back to the wheel. This also applies to the packaged CLI entry points.
Features
- Hardware VM isolation with a guest Linux kernel
- Async sandbox lifecycle, execution, filesystem, metrics, and logs APIs
- OCI image, bind-rootfs, disk-image, and snapshot-based sandboxes
- Named volumes, bind mounts, tmpfs mounts, and disk-image mounts
- Network policies, DNS filtering, TLS interception, secrets, and port publishing
- Rootfs patches before boot
- Detached sandboxes that can outlive the Python process
- Typed Python surface with
StrEnums, frozen dataclasses, event objects,.pyistubs, andpy.typed
Requirements
- Python 3.10+
- Linux with KVM, macOS with Apple Silicon, or Windows 11 with WHP enabled
- Windows support is currently preview; see the Windows troubleshooting guide for WHP and runtime setup notes.
Supported Platforms
| Platform | Architecture | Notes |
|---|---|---|
| macOS | ARM64 / Apple Silicon | Wheel bundles msb and libkrunfw |
| Linux | x86_64 | Wheel bundles msb and libkrunfw |
| Linux | ARM64 | Wheel bundles msb and libkrunfw |
| Windows | x86_64, ARM64 | Preview; requires WHP |
Python wheels bundle the matching msb runtime and libkrunfw library. Source checkouts and unreleased local builds can override runtime paths with MSB_PATH, MSB_LIBKRUNFW_PATH, or microsandbox.set_libkrunfw_path(...).
Installation
pip install microsandbox
Quick Start
import asyncio
from microsandbox import Sandbox
async def main() -> None:
async with await Sandbox.create("python-readme", image="alpine", replace=True) as sandbox:
output = await sandbox.shell("echo 'Hello from microsandbox!'")
print(output.stdout_text.strip())
asyncio.run(main())
async with stops and removes the sandbox when the block exits. Use Sandbox.create(...) without a context manager when you want to control stop(), kill(), or remove() yourself.
Reusable Lifecycle Convergence
Use connect_or_create when a stable name should converge on one persisted sandbox. Existing configuration wins; creation arguments are used only if creation is necessary. Handles retain a stable id, so lifecycle calls on stale receivers refuse to act on a replacement that reused the name.
from microsandbox import SandboxStatus
sandbox = await Sandbox.connect_or_create("worker", image="python", memory=1024)
print(f"{await sandbox.name}: {await sandbox.id}")
running = await (await Sandbox.get("worker")).connect_or_start()
await running.request_stop()
stopped = await running.wait_for_status(SandboxStatus.STOPPED)
restarted = await stopped.restart()
await restarted.destroy()
Common Examples
These snippets assume you already have a live sandbox: Sandbox.
Fork a Live Sandbox
Forking copies a running or paused local sandbox's disk and execution state into an independent child. Memory uses copy-on-write automatically. The source keeps its previous running or paused state. Host resources require explicit bindings; see forking and resource bindings.
child = await sandbox.fork("experiment")
await child.stop()
Use await sandbox.fork_many(["alice", "bob"]) to capture once for several children. Inspect every returned outcome: one child's startup failure does not remove successful siblings. See the fork API reference.
Restoring starts from a saved snapshot instead. Use cow_memory=True to request copy-on-write memory for a full-snapshot restore. A generation describes snapshot-history progression; a branch describes a distinct path through that history. The former live branch APIs and old CoW restore names remain deprecated aliases. See restore migration notes for the old-to-new names and language-specific deprecation notices.
Command Execution
import sys
from microsandbox import ExecEventType
output = await sandbox.exec("python3", ["-c", "print(1 + 1)"])
print(output.stdout_text)
print(output.exit_code)
output = await sandbox.shell("echo hello && pwd")
print(output.stdout_text)
output = await sandbox.exec(
"python3",
["script.py"],
cwd="/app",
env={"PYTHONPATH": "/app/lib"},
timeout=30.0,
)
handle = await sandbox.exec_stream("tail", ["-f", "/var/log/app.log"])
async for event in handle:
match event.event_type:
case ExecEventType.STDOUT:
sys.stdout.buffer.write(event.data)
case ExecEventType.STDERR:
sys.stderr.buffer.write(event.data)
case ExecEventType.EXITED:
break
Filesystem Operations
fs = sandbox.fs
await fs.write("/tmp/config.json", b'{"debug": true}')
print(await fs.read_text("/tmp/config.json"))
for entry in await fs.list("/etc"):
print(f"{entry.path} ({entry.kind})")
await fs.copy_from_host("./local-file.txt", "/tmp/file.txt")
await fs.copy_to_host("/tmp/output.txt", "./output.txt")
if await fs.exists("/tmp/config.json"):
meta = await fs.stat("/tmp/config.json")
print(f"size: {meta.size}, kind: {meta.kind}")
Named Volumes
from microsandbox import Sandbox, Volume
data = await Volume.create("python-readme-data", quota_mib=100)
writer = await Sandbox.create(
"python-readme-writer",
image="alpine",
volumes={"/data": Volume.named(data.name)},
replace=True,
)
await writer.shell("echo 'hello' > /data/message.txt")
await writer.stop()
reader = await Sandbox.create(
"python-readme-reader",
image="alpine",
volumes={"/data": Volume.named(data.name, readonly=True)},
replace=True,
)
output = await reader.shell("cat /data/message.txt")
print(output.stdout_text.strip())
await reader.stop()
Network, DNS, and Ports
from microsandbox import Network, NetworkProfile, Sandbox
from microsandbox.types import DnsConfig
isolated = await Sandbox.create(
"python-readme-isolated",
image="alpine",
network=Network.none(),
replace=True,
)
filtered = await Sandbox.create(
"python-readme-filtered",
image="alpine",
network=Network(
deny_domains=("blocked.example.com",),
deny_domain_suffixes=(".evil.com",),
dns=DnsConfig(nameservers=("1.1.1.1:53",)),
),
replace=True,
)
web = await Sandbox.create(
"python-readme-web",
image="python",
ports={8080: 80},
network=Network.from_profiles(NetworkProfile.PUBLIC),
replace=True,
)
Secrets
Secrets use placeholder substitution. The real value stays on the host and is substituted only for allowed network destinations.
import os
from microsandbox import Sandbox, Secret
sandbox = await Sandbox.create(
"python-readme-agent",
image="python",
secrets=[
Secret.env(
"OPENAI_API_KEY",
value=os.environ["OPENAI_API_KEY"],
allow=["api.openai.com"],
),
],
replace=True,
)
Rootfs Patches
from microsandbox import Patch, Sandbox
sandbox = await Sandbox.create(
"python-readme-patched",
image="alpine",
patches=[
Patch.text("/etc/greeting.txt", "Hello!\n"),
Patch.mkdir("/app", mode=0o755),
Patch.text("/app/config.json", '{"debug": true}', mode=0o644),
Patch.append("/etc/hosts", "127.0.0.1 myapp.local\n"),
],
replace=True,
)
Detached Mode
sandbox = await Sandbox.create(
"python-readme-background",
image="python",
detached=True,
replace=True,
)
handle = await Sandbox.get("python-readme-background")
reconnected = await handle.connect()
output = await reconnected.shell("echo reconnected")
print(output.stdout_text.strip())
TLS Interception
from microsandbox import (
Network,
Sandbox,
ScopedUpstreamCACert,
ScopedVerifyUpstream,
TlsConfig,
)
sandbox = await Sandbox.create(
"tls-inspect",
image="python",
network=Network(
tls=TlsConfig(
bypass=("*.googleapis.com",),
verify_upstream=True,
intercepted_ports=(443,),
upstream_ca_certs=("/etc/ssl/corp-root.pem",),
scoped_upstream_ca_certs=(
ScopedUpstreamCACert("api.internal", "./certs/api-ca.pem"),
),
scoped_verify_upstream=(
ScopedVerifyUpstream("*.preview.internal", False),
),
),
),
)
Metrics
from microsandbox import MiB, all_sandbox_metrics
metrics = await sandbox.metrics()
print(f"CPU: {metrics.cpu_percent:.1f}%")
print(f"Memory: {metrics.memory_bytes // MiB} MiB")
async for sample in sandbox.metrics_stream(interval=1.0):
print(f"CPU: {sample.cpu_percent:.1f}%")
break
for name, sample in (await all_sandbox_metrics()).items():
print(f"{name}: {sample.cpu_percent:.1f}%")
Storage Usage and Runtime Cache Cleanup
Storage.usage() reports the selected local backend's aggregate storage. Sandbox and snapshot handles also provide storage_usage() for their managed files. Reports contain raw integer byte counts; None means unknown. Logical sizes and allocated-block observations do not measure exclusive physical ownership on filesystems that share copy-on-write blocks.
from microsandbox import Storage
usage = await Storage.usage()
print(usage.branch_memory.logical_bytes)
preview = await Storage.prune(dry_run=True, older_than_seconds=600)
for entry in preview.entries:
print(entry.path, entry.state, entry.logical_bytes)
# Explicitly remove currently unused runtime RAM after rechecking ownership.
result = await Storage.prune(older_than_seconds=600)
print(result.logical_bytes_removed)
Pruning preserves durable snapshots, sandbox disks, named volumes, and stable lock files. Pending handoffs, live or paused VMs, and retained baselines protect their RAM. The report lists skipped entries and per-file errors, including partial success; physical_bytes_reclaimed remains None. Remote backend storage operations raise UnsupportedError. Static storage calls retain the backend selected when called, and handle methods retain the backend that created the handle.
Typed Errors
Python exports typed errors for the common SDK categories and falls back to MicrosandboxError for unmapped runtime variants. Catch specific errors when you need category-specific handling, and catch MicrosandboxError as the broad SDK base class.
from microsandbox import MicrosandboxError, Sandbox, SandboxAlreadyExistsError
try:
await Sandbox.create("worker", image="alpine")
except SandboxAlreadyExistsError:
print("already exists; resume it or pass replace=True")
except MicrosandboxError as exc:
print(f"microsandbox error: {exc}")
Runtime Setup
Installed wheels bundle the runtime files. The setup helpers are useful for source checkouts, shared runtime installs, and surfacing setup failures at process startup.
from microsandbox import ensure_runtime
runtime = await ensure_runtime()
print(runtime.msb_path, runtime.libkrunfw_path)
More Documentation
Development
From sdk/python:
uv sync --group dev
uv run maturin develop --release
uv run pytest tests
uv run ruff check .
From the repository root, run an example against the SDK project:
uv run --project sdk/python python examples/python/root-oci/main.py
Runtime integration tests require local virtualization support and runtime artifacts:
cd sdk/python
uv run pytest integration/test_create_kwargs.py integration/test_exec.py
License
Apache-2.0
Metadata
Release files for microsandbox 0.7.7
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| microsandbox-0.7.7-cp310-abi3-win_arm64.whl | CPython 3.10 | abi3 | Windows ARM64 | Details |
| microsandbox-0.7.7-cp310-abi3-win_amd64.whl | CPython 3.10 | abi3 | Windows x86-64 | Details |
| microsandbox-0.7.7-cp310-abi3-manylinux_2_28_x86_64.whl | CPython 3.10 | abi3 | Linux glibc 2.28+ x86-64 | Details |
| microsandbox-0.7.7-cp310-abi3-manylinux_2_28_aarch64.whl | CPython 3.10 | abi3 | Linux glibc 2.28+ ARM64 | Details |
| microsandbox-0.7.7-cp310-abi3-macosx_11_0_arm64.whl | CPython 3.10 | abi3 | macOS 11.0+ ARM64 | Details |
Total release size: 224.4 MB
Release files / microsandbox-0.7.7-cp310-abi3-win_arm64.whl
| Download URL | microsandbox-0.7.7-cp310-abi3-win_arm64.whl |
|---|---|
| Size | 44.7 MB |
| Tags | CPython 3.10 Windows ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
f8a48bb5a98ce070425bdafc27a430de7a515d4169bb03e76dea8f420fe032dc
|
|
BLAKE2b-256 checksum How to use checksums |
af91f1a7f81727301e1ded593da9ef87b8936da24aed3492eda82ce8a65a24a7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.23 {"installer":{"name":"uv","version":"0.12.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / microsandbox-0.7.7-cp310-abi3-win_amd64.whl
| Download URL | microsandbox-0.7.7-cp310-abi3-win_amd64.whl |
|---|---|
| Size | 42.2 MB |
| Tags | CPython 3.10 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
1702c1bf6b617c548fa1fa70de43d94330e1e1080f158420a8ab97f93ec023f7
|
|
BLAKE2b-256 checksum How to use checksums |
be995cd4a2d6571d8d3ff5f816e1f0d91dc117e96e648304179763ad538348df
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.23 {"installer":{"name":"uv","version":"0.12.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / microsandbox-0.7.7-cp310-abi3-manylinux_2_28_x86_64.whl
| Download URL | microsandbox-0.7.7-cp310-abi3-manylinux_2_28_x86_64.whl |
|---|---|
| Size | 45.3 MB |
| Tags | CPython 3.10 Linux glibc 2.28+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
2cf12392970081cb27d4ee133addaa6f43d91b4aec293bbef7753889914cdde2
|
|
BLAKE2b-256 checksum How to use checksums |
8be69160c71ac12d972ebe9eabdd23d64c7cdcda1da108ad9b4eb32c7b024dda
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.23 {"installer":{"name":"uv","version":"0.12.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / microsandbox-0.7.7-cp310-abi3-manylinux_2_28_aarch64.whl
| Download URL | microsandbox-0.7.7-cp310-abi3-manylinux_2_28_aarch64.whl |
|---|---|
| Size | 47.5 MB |
| Tags | CPython 3.10 Linux glibc 2.28+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
7fa16f2029209ce395bb6c46ddd3b95e339e77aa8df68dc48e65a0d534ba3503
|
|
BLAKE2b-256 checksum How to use checksums |
2f07b527f5e1f75f8b229df5675f014eb6fe5f9dd73ebe350b4b31ad4964a3b7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.23 {"installer":{"name":"uv","version":"0.12.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / microsandbox-0.7.7-cp310-abi3-macosx_11_0_arm64.whl
| Download URL | microsandbox-0.7.7-cp310-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 44.8 MB |
| Tags | CPython 3.10 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
135d51504acbc8feefc0ef2cb9d0a587c6f8c532c9cfdb947f8a510a9c9b4e63
|
|
BLAKE2b-256 checksum How to use checksums |
29d3a92407e909bac0c19df1d8094d6f507b1be92b9fe634b19bd575a0f8e066
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.23 {"installer":{"name":"uv","version":"0.12.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|