crates/microsandbox/README.md
Lightweight VM sandboxes for running AI agents and untrusted code with hardware-level isolation.
microsandbox is the core Rust library for the microsandbox project. It provides a high-level async API for creating, managing, and interacting with microVM sandboxes — real virtual machines that boot in under 100ms and run standard OCI (Docker) images.
[dependencies]
microsandbox = "0.4"
| Feature | Default | Description |
|---|---|---|
prebuilt | yes | Use pre-built runtime binaries |
net | yes | Networking: port publishing, policies, TLS, secrets |
To disable networking:
[dependencies]
microsandbox = { version = "0.4", default-features = false, features = ["prebuilt"] }
use microsandbox::Sandbox;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create a sandbox from an OCI image.
let sandbox = Sandbox::builder("my-sandbox")
.image("alpine")
.cpus(1)
.memory(512)
.create()
.await?;
// Run a command.
let output = sandbox.shell("echo 'Hello from microsandbox!'").await?;
println!("{}", output.stdout()?);
// Stop the sandbox.
sandbox.stop_and_wait().await?;
Ok(())
}
use microsandbox::Sandbox;
// Collected output.
let output = sandbox.exec("python3", ["-c", "print(1 + 1)"]).await?;
println!("stdout: {}", output.stdout()?);
println!("exit code: {}", output.status().code);
// Streaming output.
let mut handle = sandbox.exec_stream("tail", ["-f", "/var/log/app.log"]).await?;
while let Some(event) = handle.recv().await {
match event {
ExecEvent::Stdout(data) => print!("{}", String::from_utf8_lossy(&data)),
ExecEvent::Stderr(data) => eprint!("{}", String::from_utf8_lossy(&data)),
ExecEvent::Exited { code } => break,
_ => {}
}
}
let fs = sandbox.fs();
// Write a file.
fs.write("/tmp/config.json", b"{\"debug\": true}").await?;
// Read it back.
let data = fs.read("/tmp/config.json").await?;
println!("{}", String::from_utf8_lossy(&data));
// List a directory.
for entry in fs.list("/etc").await? {
println!("{} ({:?})", entry.path, entry.kind);
}
use microsandbox::{Sandbox, Volume, size::SizeExt};
// Create a 100 MiB named volume.
let data = Volume::builder("my-data").quota(100.mib()).create().await?;
// Mount it in a sandbox.
let sandbox = Sandbox::builder("writer")
.image("alpine")
.volume("/data", |v| v.named(data.name()))
.create()
.await?;
sandbox.shell("echo 'hello' > /data/message.txt").await?;
sandbox.stop_and_wait().await?;
// Mount the same volume in another sandbox (read-only).
let reader = Sandbox::builder("reader")
.image("alpine")
.volume("/data", |v| v.named(data.name()).readonly())
.create()
.await?;
let output = reader.shell("cat /data/message.txt").await?;
println!("{}", output.stdout()?); // "hello"
use microsandbox::{Sandbox, NetworkPolicy};
// Default: public internet only (blocks private ranges).
let sandbox = Sandbox::builder("public")
.image("alpine")
.create()
.await?;
// Fully airgapped.
let sandbox = Sandbox::builder("isolated")
.image("alpine")
.network(|n| n.policy(NetworkPolicy::none()))
.create()
.await?;
// Domain blocking via policy rules (refused at DNS and at TCP egress).
use microsandbox_network::policy::{Destination, Rule};
let mut policy = NetworkPolicy::default();
policy.rules.push(Rule::deny_egress(Destination::Domain(
"blocked.example.com".parse()?,
)));
policy.rules.push(Rule::deny_egress(Destination::DomainSuffix(
".evil.com".parse()?,
)));
let sandbox = Sandbox::builder("filtered")
.image("alpine")
.network(|n| n.policy(policy))
.create()
.await?;
let sandbox = Sandbox::builder("web")
.image("python")
.port(8080, 80) // host:8080 → guest:80
.create()
.await?;
Secrets use placeholder substitution — the real value never enters the VM. It is only swapped in at the network layer for HTTPS requests to allowed hosts.
let sandbox = Sandbox::builder("agent")
.image("python")
.secret_env("API_KEY", "sk-real-secret-123", "api.openai.com")
.create()
.await?;
// Guest sees: API_KEY=$MSB_API_KEY (a placeholder)
// HTTPS to api.openai.com: placeholder is transparently replaced with the real key
// HTTPS to any other host with the placeholder: request is blocked
Modify the filesystem before the VM boots:
let sandbox = Sandbox::builder("patched")
.image("alpine")
.patch(|p| {
p.text("/etc/greeting.txt", "Hello!\n", None, false)
.mkdir("/app", Some(0o755))
.append("/etc/hosts", "127.0.0.1 myapp.local\n")
})
.create()
.await?;
Sandboxes in detached mode survive the parent process:
// Create and detach.
let sandbox = Sandbox::builder("background")
.image("python")
.create_detached()
.await?;
// Later, from another process:
let sandbox = Sandbox::start("background").await?;
let output = sandbox.shell("echo reconnected").await?;
use microsandbox::Sandbox;
// OCI image (most common).
Sandbox::builder("a").image("python")
// Local bind-mounted rootfs.
Sandbox::builder("b").image("/path/to/rootfs")
// QCOW2 disk image.
Sandbox::builder("c").image_with(|img| img.disk("/path/to/disk.qcow2").fstype("ext4"))
| Type | Description |
|---|---|
Sandbox | Live handle to a running sandbox — lifecycle, execution, filesystem |
SandboxBuilder | Fluent builder for configuring and creating sandboxes |
SandboxConfig | Serializable sandbox configuration |
SandboxHandle | Lightweight metadata handle from the database |
Volume | Persistent named volume |
VolumeBuilder | Fluent builder for creating volumes |
Image | OCI image metadata and inspection |
| Type | Description |
|---|---|
ExecOutput | Captured stdout/stderr with exit status |
ExecHandle | Streaming execution handle with event channel |
ExecOptions / ExecOptionsBuilder | Execution configuration (args, env, cwd, timeout, rlimits) |
ExecEvent | Stream event: Started, Stdout, Stderr, Exited |
ExecSink | Writable stdin channel for streaming exec |
ExitStatus | Exit code and success flag |
| Type | Description |
|---|---|
SandboxFs | Gateway for guest filesystem operations |
FsEntry | Directory entry (name, kind, size, mode) |
FsMetadata | File metadata (size, mode, timestamps) |
| Type | Description |
|---|---|
RootfsSource | Image source: Oci, Bind, or DiskImage |
VolumeMount | Mount type: Bind, Named, or Tmpfs |
Patch / PatchBuilder | Pre-boot filesystem modifications |
NetworkPolicy | Network access control (requires net feature) |
RegistryAuth | Docker registry credentials |
PullPolicy | Image pull strategy: Always, IfMissing, Never |
LogLevel | Logging verbosity |
All fallible operations return MicrosandboxResult<T>, which uses MicrosandboxError — an enum covering I/O, network, database, configuration, runtime, and timeout errors.
Apache-2.0