Skip to main content
Command execution goes through a dedicated channel between the host and the guest agent, not the network. So exec works even when a sandbox has networking fully disabled.

exec

Run a command and collect stdout, stderr, and the exit code once it completes.
let output = sb.exec("python", ["-c", "print('hello')"]).await?;
println!("{}", output.stdout()?);     // "hello\n"
println!("{}", output.status().code); // 0

shell

Run a command through the sandbox’s configured shell (defaults to /bin/sh). Useful for pipelines, redirects, and other shell syntax that exec doesn’t interpret.
let output = sb.shell("ls -la /app && echo done").await?;
println!("{}", output.stdout()?);

Execution options

Per-execution overrides for working directory, environment variables, resource limits, and timeout. These don’t change the sandbox’s defaults.
use std::time::Duration;
use microsandbox::RlimitResource;

let output = sb.exec("python", |e| e
    .args(["compute.py"])
    .cwd("/app")
    .env("PYTHONPATH", "/app/lib")
    .timeout(Duration::from_secs(30))
    .rlimit(RlimitResource::Nofile, 1024)
    .rlimit("cpu", 60)
).await?;

Streaming

exec_stream returns a handle that emits stdout, stderr, and exit events as they happen. Output shows up the moment the guest agent reads it from the process.
use microsandbox::exec::ExecEvent;

let mut handle = sb.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,
        _ => {}
    }
}

Interactive stdin

Pipe data into a running process by opening a streaming handle with stdin_pipe enabled. Combined with tty: true, this lets you drive interactive processes (REPLs, shells) programmatically.
let mut handle = sb.exec_stream("python", |e| e.stdin_pipe().tty(true)).await?;
let stdin = handle.take_stdin().unwrap();
stdin.write(b"print('hello')\n").await?;
stdin.write(b"exit()\n").await?;
handle.wait().await?;

Attach

Bridge your terminal to a fully interactive PTY session inside the sandbox. Press the detach keys (default Ctrl+]) to disconnect without stopping the process. The process keeps running and can be reattached via its session ID.
// Attach to the default shell
let exit_code = sb.attach_shell().await?;

// Attach to a specific command with options
let exit_code = sb.attach("python", |a| a
    .env("DEBUG", "1")
    .cwd("/app")
    .detach_keys("ctrl-q")
).await?;

Sessions

Each streaming exec or attach creates a session. You can list active sessions and reattach to them by ID. Up to 16 simultaneous client connections are supported, so multiple sessions can run concurrently without interfering with each other.
// Start a background process and capture its session ID
let handle = sb.exec_stream("python", ["server.py"]).await?;
let session_id = handle.id().to_string();

// List active sessions
let sessions = sb.sessions().await?;

// Reattach to a session by ID
let mut handle = sb.session(&session_id).await?;