Skip to main content

Overview

The Rust SDK is the core implementation of BoxLite. It provides async-first APIs built on Tokio for creating and managing isolated VM environments. Crate: boxlite Repository: github.com/boxlite-ai/boxlite

BoxliteRuntime

Main entry point for creating and managing boxes.
use boxlite::runtime::{BoxliteRuntime, BoxliteOptions, BoxOptions};

// Create with default options
let runtime = BoxliteRuntime::with_defaults()?;

// Create with custom options
let options = BoxliteOptions {
    home_dir: PathBuf::from("/custom/boxlite"),
    image_registries: vec!["ghcr.io/myorg".to_string()],
};
let runtime = BoxliteRuntime::new(options)?;

// Use global default runtime
let runtime = BoxliteRuntime::default_runtime();

Methods

MethodSignatureDescription
newfn new(options: BoxliteOptions) -> BoxliteResult<Self>Create runtime with options
with_defaultsfn with_defaults() -> BoxliteResult<Self>Create with default options
default_runtimefn default_runtime() -> &'static SelfGet/create global singleton
try_default_runtimefn try_default_runtime() -> Option<&'static Self>Get global if initialized
init_default_runtimefn init_default_runtime(options: BoxliteOptions) -> BoxliteResult<()>Initialize global with options
createasync fn create(&self, options: BoxOptions, name: Option<String>) -> BoxliteResult<LiteBox>Create a new box
getasync fn get(&self, id_or_name: &str) -> BoxliteResult<Option<LiteBox>>Get box by ID or name
get_infoasync fn get_info(&self, id_or_name: &str) -> BoxliteResult<Option<BoxInfo>>Get box info without handle
list_infoasync fn list_info(&self) -> BoxliteResult<Vec<BoxInfo>>List all boxes
existsasync fn exists(&self, id_or_name: &str) -> BoxliteResult<bool>Check if box exists
metricsasync fn metrics(&self) -> RuntimeMetricsGet runtime-wide metrics
removeasync fn remove(&self, id_or_name: &str, force: bool) -> BoxliteResult<()>Remove box completely

Example

use boxlite::runtime::{BoxliteRuntime, BoxOptions};
use boxlite::BoxCommand;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let runtime = BoxliteRuntime::with_defaults()?;

    // Create a box
    let options = BoxOptions::default();
    let litebox = runtime.create(options, Some("my-box".to_string())).await?;

    // Run a command
    let mut run = litebox.run(BoxCommand::new("echo").arg("Hello")).await?;
    let result = run.wait().await?;

    println!("Exit code: {}", result.exit_code);

    // Stop the box
    litebox.stop().await?;

    Ok(())
}

BoxliteOptions

Runtime configuration options.
pub struct BoxliteOptions {
    /// Home directory for runtime data (~/.boxlite by default)
    pub home_dir: PathBuf,

    /// Registries to search for unqualified image references
    /// Empty list uses docker.io as implicit default
    pub image_registries: Vec<String>,
}

Example

use boxlite::runtime::BoxliteOptions;
use std::path::PathBuf;

let options = BoxliteOptions {
    home_dir: PathBuf::from("/var/lib/boxlite"),
    image_registries: vec![
        "ghcr.io/myorg".to_string(),
        "docker.io".to_string(),
    ],
};
// "alpine" → tries ghcr.io/myorg/alpine, then docker.io/alpine

Box Handle

LiteBox

Handle to a box instance. Thin wrapper providing access to box operations.
pub struct LiteBox {
    // ... internal fields
}

Methods

MethodSignatureDescription
idfn id(&self) -> &BoxIDGet box ID
namefn name(&self) -> Option<&str>Get optional box name
infofn info(&self) -> BoxInfoGet box info (no VM init)
startasync fn start(&self) -> BoxliteResult<()>Start the box
runasync fn run(&self, command: BoxCommand) -> BoxliteResult<Execution>Run command
metricsasync fn metrics(&self) -> BoxliteResult<BoxMetrics>Get box metrics
stopasync fn stop(&self) -> BoxliteResult<()>Stop the box

Lifecycle

  • start() initializes VM for Configured or Stopped boxes
  • Idempotent: calling on Running box is a no-op
  • run() implicitly calls start() if needed
  • stop() terminates VM; box can be restarted

Example

let litebox = runtime.create(BoxOptions::default(), None).await?;

// Start explicitly (optional, run does this automatically)
litebox.start().await?;

// Check metrics
let metrics = litebox.metrics().await?;
println!("CPU: {:?}%", metrics.cpu_percent());

// Stop when done
litebox.stop().await?;

BoxInfo

Public metadata about a box (returned by list operations).
pub struct BoxInfo {
    /// Unique box identifier (ULID)
    pub id: BoxID,

    /// User-defined name (optional)
    pub name: Option<String>,

    /// Current lifecycle status
    pub status: BoxStatus,

    /// Creation timestamp (UTC)
    pub created_at: DateTime<Utc>,

    /// Last state change timestamp (UTC)
    pub last_updated: DateTime<Utc>,

    /// Process ID of VMM subprocess (None if not running)
    pub pid: Option<u32>,

    /// Image reference or rootfs path
    pub image: String,

    /// Allocated CPU count
    pub cpus: u8,

    /// Allocated memory in MiB
    pub memory_mib: u32,

    /// User-defined labels
    pub labels: HashMap<String, String>,
}

BoxStatus

Lifecycle status of a box.
pub enum BoxStatus {
    /// Cannot determine state (error recovery)
    Unknown,

    /// Created and persisted, VM not started
    Configured,

    /// Running and accepting commands
    Running,

    /// Shutting down gracefully (transient)
    Stopping,

    /// Not running, can be restarted
    Stopped,
}

Methods

MethodSignatureDescription
is_activefn is_active(&self) -> boolTrue if VM process running
is_runningfn is_running(&self) -> boolTrue if Running
is_configuredfn is_configured(&self) -> boolTrue if Configured
is_stoppedfn is_stopped(&self) -> boolTrue if Stopped
is_transientfn is_transient(&self) -> boolTrue if Stopping
can_startfn can_start(&self) -> boolTrue if Configured or Stopped
can_stopfn can_stop(&self) -> boolTrue if Running
can_removefn can_remove(&self) -> boolTrue if Configured, Stopped, or Unknown
can_runfn can_run(&self) -> boolTrue if Configured, Running, or Stopped

State Machine

create() → Configured (persisted to DB, no VM)
start()  → Running (VM initialized)
stop()   → Stopped (VM terminated, can restart)

BoxState

Dynamic box state (changes during lifecycle).
pub struct BoxState {
    /// Current lifecycle status
    pub status: BoxStatus,

    /// Process ID (None if not running)
    pub pid: Option<u32>,

    /// Container ID (64-char hex)
    pub container_id: Option<ContainerID>,

    /// Last state change timestamp (UTC)
    pub last_updated: DateTime<Utc>,

    /// Lock ID for multiprocess-safe locking
    pub lock_id: Option<LockId>,
}

Complete Example

use boxlite::runtime::{BoxliteRuntime, BoxOptions};
use boxlite::runtime::options::{RootfsSpec, SecurityOptions, VolumeSpec};
use boxlite::BoxCommand;
use futures::StreamExt;
use std::time::Duration;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Initialize runtime
    let runtime = BoxliteRuntime::with_defaults()?;

    // Configure box
    let options = BoxOptions {
        cpus: Some(2),
        memory_mib: Some(1024),
        rootfs: RootfsSpec::Image("python:3.11-slim".to_string()),
        volumes: vec![
            VolumeSpec {
                host_path: "/home/user/code".to_string(),
                guest_path: "/app".to_string(),
                read_only: true,
            },
        ],
        security: SecurityOptions::standard(),
        ..Default::default()
    };

    // Create and name the box
    let litebox = runtime.create(options, Some("python-sandbox".to_string())).await?;
    println!("Created box: {}", litebox.id());

    // Run Python code
    let cmd = BoxCommand::new("python3")
        .args(["-c", "import sys; print(f'Python {sys.version}')"])
        .timeout(Duration::from_secs(30))
        .working_dir("/app");

    let mut run_handle = litebox.run(cmd).await?;

    // Stream output
    if let Some(mut stdout) = run_handle.stdout() {
        while let Some(line) = stdout.next().await {
            println!("{}", line);
        }
    }

    // Check result
    let result = run_handle.wait().await?;
    if !result.success() {
        eprintln!("Command failed with exit code: {}", result.exit_code);
    }

    // Check metrics
    let metrics = litebox.metrics().await?;
    if let Some(boot_ms) = metrics.guest_boot_duration_ms() {
        println!("Boot time: {}ms", boot_ms);
    }

    // Cleanup
    litebox.stop().await?;

    Ok(())
}

See Also