A3S Docs
A3S Box

Sandbox SDK

Complete API reference for Rust, Python, and TypeScript SDKs

Sandbox SDK

Create, execute, and manage MicroVM sandboxes programmatically — no CLI or daemon required. All three SDKs wrap the same Rust runtime and provide identical capabilities.

Prop

Type


Rust SDK

Installation

[dependencies]
a3s-box-sdk = "0.5.3"

# Optional: bundle the shim binary into your application
a3s-box-sdk = { version = "0.5.3", features = ["embed-shim"] }

With embed-shim, the SDK bundles a3s-box-shim into your binary and auto-extracts it to ~/.a3s/bin/ on first use. No separate installation needed.

BoxSdk

Entry point for creating and managing sandboxes.

use a3s_box_sdk::{BoxSdk, SandboxOptions};

// Use default home directory (~/.a3s/)
let sdk = BoxSdk::new().await?;

// Use custom home directory
let sdk = BoxSdk::with_home("/data/sandboxes").await?;

// Get home directory
let path: &Path = sdk.home_dir();

SandboxOptions

pub struct SandboxOptions {
    pub image: String,                          // OCI image reference (default: "alpine:latest")
    pub cpus: u32,                              // vCPU count (default: 1)
    pub memory_mb: u32,                         // Memory in MB (default: 256)
    pub env: HashMap<String, String>,           // Environment variables
    pub workdir: Option<String>,                // Working directory inside sandbox
    pub mounts: Vec<MountSpec>,                 // Host-to-guest mounts
    pub network: bool,                          // Enable networking (default: true)
    pub tee: bool,                              // Enable AMD SEV-SNP (default: false)
    pub name: Option<String>,                   // Sandbox name
    pub port_forwards: Vec<PortForward>,        // Port forwarding rules
    pub workspace: Option<WorkspaceConfig>,     // Persistent workspace
}

pub struct MountSpec {
    pub host_path: String,
    pub guest_path: String,
    pub readonly: bool,
}

pub struct PortForward {
    pub guest_port: u16,
    pub host_port: u16,    // 0 = auto-assign
    pub protocol: String,  // "tcp" or "udp"
}

pub struct WorkspaceConfig {
    pub name: String,
    pub guest_path: String,  // default: "/workspace"
}

Sandbox

// Create a sandbox
let sandbox: Sandbox = sdk.create(SandboxOptions {
    image: "python:3.12-slim".into(),
    cpus: 2,
    memory_mb: 1024,
    ..Default::default()
}).await?;

exec

Run a command and wait for it to complete.

pub async fn exec(&self, cmd: &str, args: &[&str]) -> Result<ExecResult>

pub struct ExecResult {
    pub stdout: String,
    pub stderr: String,
    pub exit_code: i32,
    pub metrics: ExecMetrics,
}

pub struct ExecMetrics {
    pub duration_ms: u64,
    pub stdout_bytes: u64,
    pub stderr_bytes: u64,
}
let result = sandbox.exec("python", &["-c", "print('hello')"]).await?;
println!("{}", result.stdout);
println!("exit: {}", result.exit_code);
println!("took: {}ms", result.metrics.duration_ms);

exec_stream

Stream stdout/stderr in real-time.

pub async fn exec_stream(&self, cmd: &str, args: &[&str]) -> Result<impl Stream<Item = ExecEvent>>

pub enum ExecEvent {
    Stdout(Vec<u8>),
    Stderr(Vec<u8>),
    Exit(i32),
}
use futures::StreamExt;

let mut stream = sandbox.exec_stream(
    "bash", &["-c", "for i in 1 2 3; do echo $i; sleep 1; done"]
).await?;

while let Some(event) = stream.next().await {
    match event {
        ExecEvent::Stdout(data) => print!("{}", String::from_utf8_lossy(&data)),
        ExecEvent::Stderr(data) => eprint!("{}", String::from_utf8_lossy(&data)),
        ExecEvent::Exit(code) => println!("exit: {}", code),
    }
}

upload / download

pub async fn upload(&self, host_path: &str, guest_path: &str) -> Result<()>
pub async fn download(&self, guest_path: &str, host_path: &str) -> Result<()>
sandbox.upload("./config.yaml", "/app/config.yaml").await?;
sandbox.download("/app/output.json", "./output.json").await?;

pause / resume

pub async fn pause(&self) -> Result<()>
pub async fn resume(&self) -> Result<()>

pty

Open an interactive terminal session.

pub async fn pty(&self) -> Result<PtySession>
// PtySession provides read/write streams for terminal I/O

stop

pub async fn stop(&self) -> Result<()>

Complete Example

use a3s_box_sdk::{BoxSdk, SandboxOptions, MountSpec, PortForward};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let sdk = BoxSdk::new().await?;

    let sandbox = sdk.create(SandboxOptions {
        image: "node:20-slim".into(),
        cpus: 2,
        memory_mb: 512,
        env: [("NODE_ENV".into(), "production".into())].into(),
        mounts: vec![MountSpec {
            host_path: "./app".into(),
            guest_path: "/app".into(),
            readonly: false,
        }],
        port_forwards: vec![PortForward {
            guest_port: 3000,
            host_port: 0,  // auto-assign
            protocol: "tcp".into(),
        }],
        ..Default::default()
    }).await?;

    let result = sandbox.exec("node", &["/app/server.js"]).await?;
    println!("{}", result.stdout);

    sandbox.stop().await?;
    Ok(())
}

Python SDK

Installation

pip install a3s-box

BoxSdk

from a3s_box import BoxSdk

# Default home directory (~/.a3s/)
sdk = BoxSdk()

# Custom home directory
sdk = BoxSdk(home_dir="/data/sandboxes")

# Get home directory
print(sdk.home_dir)  # str

SandboxOptions

from a3s_box import SandboxOptions, MountSpec, PortForward, WorkspaceConfig

options = SandboxOptions(
    image="python:3.12-slim",   # str, default: "alpine:latest"
    cpus=2,                      # int, default: 1
    memory_mb=1024,              # int, default: 256
    env={"KEY": "VALUE"},        # dict[str, str], default: {}
    workdir="/app",              # str | None
    mounts=[                     # list[MountSpec], default: []
        MountSpec(host_path="./data", guest_path="/data", readonly=False)
    ],
    network=True,                # bool, default: True
    tee=False,                   # bool, default: False
    name="my-sandbox",           # str | None
    port_forwards=[              # list[PortForward], default: []
        PortForward(guest_port=8080, host_port=0, protocol="tcp")
    ],
    workspace=WorkspaceConfig(   # WorkspaceConfig | None
        name="my-project",
        guest_path="/workspace"
    ),
)

MountSpec / PortForward / WorkspaceConfig

MountSpec(host_path: str, guest_path: str, readonly: bool = False)
PortForward(guest_port: int, host_port: int = 0, protocol: str = "tcp")
WorkspaceConfig(name: str, guest_path: str = "/workspace")

Sandbox

create

sandbox = sdk.create(options)           # sync
sandbox = await sdk.create_async(options)  # async

exec

result = sandbox.exec("python", ["-c", "print('hello')"])
# result.stdout: str
# result.stderr: str
# result.exit_code: int
# result.metrics.duration_ms: int
# result.metrics.stdout_bytes: int
# result.metrics.stderr_bytes: int

exec_stream

import sys

async for event in sandbox.exec_stream("bash", ["-c", "for i in 1 2 3; do echo $i; sleep 1; done"]):
    if event.stdout:
        sys.stdout.write(event.stdout)
    elif event.stderr:
        sys.stderr.write(event.stderr)
    elif event.exit_code is not None:
        print(f"exit: {event.exit_code}")

upload / download

sandbox.upload("./input.csv", "/app/input.csv")
sandbox.download("/app/output.csv", "./output.csv")

pause / resume / stop

sandbox.pause()
sandbox.resume()
sandbox.stop()

Context manager

async with sdk.create(SandboxOptions(image="alpine:latest")) as sandbox:
    result = sandbox.exec("echo", ["hello"])
    print(result.stdout)
# sandbox.stop() called automatically

Complete Example

import asyncio
from a3s_box import BoxSdk, SandboxOptions, MountSpec

async def main():
    sdk = BoxSdk()

    sandbox = sdk.create(SandboxOptions(
        image="python:3.12-slim",
        cpus=2,
        memory_mb=1024,
        env={"PYTHONPATH": "/app"},
        mounts=[MountSpec(host_path="./src", guest_path="/app")],
    ))

    # Upload a script
    sandbox.upload("./script.py", "/app/script.py")

    # Run it with streaming output
    async for event in sandbox.exec_stream("python", ["/app/script.py"]):
        if event.stdout:
            print(event.stdout, end="")

    # Download results
    sandbox.download("/app/results.json", "./results.json")

    sandbox.stop()

asyncio.run(main())

TypeScript SDK

Installation

npm install @a3s-lab/box

BoxSdk

import { BoxSdk } from '@a3s-lab/box';

// Default home directory (~/.a3s/)
const sdk = new BoxSdk();

// Custom home directory
const sdk = new BoxSdk('/data/sandboxes');

// Get home directory
console.log(sdk.homeDir); // string

SandboxOptions

interface SandboxOptions {
  image?: string;           // default: "alpine:latest"
  cpus?: number;            // default: 1
  memoryMb?: number;        // default: 256
  env?: Record<string, string>;
  workdir?: string;
  mounts?: MountSpec[];
  network?: boolean;        // default: true
  tee?: boolean;            // default: false
  name?: string;
  portForwards?: PortForward[];
  workspace?: WorkspaceConfig;
}

interface MountSpec {
  hostPath: string;
  guestPath: string;
  readonly?: boolean;       // default: false
}

interface PortForward {
  guestPort: number;
  hostPort?: number;        // default: 0 (auto-assign)
  protocol?: string;        // default: "tcp"
}

interface WorkspaceConfig {
  name: string;
  guestPath?: string;       // default: "/workspace"
}

Sandbox

create

const sandbox = await sdk.create({
  image: 'node:20-slim',
  cpus: 2,
  memoryMb: 512,
});

exec

const result = await sandbox.exec('node', ['-e', 'console.log("hello")']);
// result.stdout: string
// result.stderr: string
// result.exitCode: number
// result.metrics.durationMs: number
// result.metrics.stdoutBytes: number
// result.metrics.stderrBytes: number

execStream

const stream = await sandbox.execStream('bash', [
  '-c', 'for i in 1 2 3; do echo $i; sleep 1; done'
]);

for await (const event of stream) {
  if (event.stdout) process.stdout.write(event.stdout);
  if (event.stderr) process.stderr.write(event.stderr);
  if (event.exitCode !== undefined) console.log(`exit: ${event.exitCode}`);
}

upload / download

await sandbox.upload('./input.json', '/app/input.json');
await sandbox.download('/app/output.json', './output.json');

pause / resume / stop

await sandbox.pause();
await sandbox.resume();
await sandbox.stop();

Complete Example

import { BoxSdk } from '@a3s-lab/box';

const sdk = new BoxSdk();

const sandbox = await sdk.create({
  image: 'node:20-slim',
  cpus: 2,
  memoryMb: 512,
  env: { NODE_ENV: 'production' },
  mounts: [{ hostPath: './app', guestPath: '/app' }],
  portForwards: [{ guestPort: 3000, hostPort: 8080 }],
});

await sandbox.upload('./package.json', '/app/package.json');

const install = await sandbox.exec('npm', ['install', '--prefix', '/app']);
console.log(install.stdout);

const stream = await sandbox.execStream('node', ['/app/index.js']);
for await (const event of stream) {
  if (event.stdout) process.stdout.write(event.stdout);
}

await sandbox.stop();

Capabilities Summary

Prop

Type

On this page