Complete API reference for the BoxLite Python SDK.
Python: 3.10+
Platforms: macOS (Apple Silicon), Linux (x86_64, ARM64)
The main runtime for creating and managing boxes.
from boxlite import Boxlite, Options, BoxOptions, ImageRegistry
| Method | Signature | Description |
|---|
default() | () -> Boxlite | Create runtime with default settings (~/.boxlite) |
__init__() | (options: Options) -> Boxlite | Create runtime with custom options |
| Method | Signature | Description |
|---|
create() | (options: BoxOptions, name: str = None) -> Box | Create a new box (async) |
get() | (box_id: str) -> Box | Reattach to an existing box by ID (async) |
list() | () -> List[BoxInfo] | List all boxes (async) |
metrics() | () -> RuntimeMetrics | Get runtime-wide metrics (async) |
# Default runtime
runtime = Boxlite.default()
# Custom runtime
runtime = Boxlite(Options(home_dir="/custom/path"))
# Registry host config with auth
runtime = Boxlite(Options(
image_registries=[
ImageRegistry(
host="registry.example.com",
username="user",
password="password",
)
]
))
# Create a box
box = await runtime.create(BoxOptions(image="alpine:latest"))
# List all boxes
for info in await runtime.list():
print(f"{info.id}: {info.status}")
Runtime configuration options.
| Field | Type | Default | Description |
|---|
home_dir | str | ~/.boxlite | Base directory for runtime data |
image_registries | List[ImageRegistry] | [] | Registry transport, TLS, search, and auth configuration |
ImageRegistry(
host="registry.local:5000",
transport="http",
search=True,
)
ImageRegistry(
host="registry.example.com",
skip_verify=True,
username="user",
password="password",
)
ImageRegistry(
host="ghcr.io",
bearer_token="token",
)
| Field | Type | Default | Description |
|---|
host | str | Required | Registry host, optionally with port. Do not include http:// or https:// |
transport | str | "https" | "https" or "http" |
skip_verify | bool | False | Disable TLS certificate and hostname verification for HTTPS registries |
search | bool | False | Include this host when resolving unqualified image references |
username / password | str | None | None | Basic auth credentials. Provide both together |
bearer_token | str | None | None | Bearer token auth |
Configuration options for creating a box.
| Field | Type | Default | Description |
|---|
image | str | Required | OCI image URI (e.g., "python:slim", "alpine:latest") |
cpus | int | 1 | Number of CPU cores (1 to host CPU count) |
memory_mib | int | 512 | Memory limit in MiB (128-65536) |
disk_size_gb | int | None | None | Persistent disk size in GB (None = ephemeral) |
working_dir | str | "/root" | Working directory inside container |
env | List[Tuple[str, str]] | [] | Environment variables as (key, value) pairs |
volumes | List[Tuple[str, str, str]] | [] | Volume mounts as (host_path, guest_path, mode) |
network | NetworkSpec | None | None | Structured network configuration. Omit for default enabled networking. |
ports | List[Tuple[int, int, str]] | [] | Port forwarding as (host_port, guest_port, protocol) |
secrets | List[Secret] | [] | Outbound HTTP(S) secret substitution rules |
auto_remove | bool | True | Auto cleanup when stopped |
detach | bool | False | Survive parent process exit |
from boxlite import NetworkSpec
network = NetworkSpec(
mode="enabled",
allow_net=["api.openai.com"],
)
| Field | Type | Default | Description |
|---|
mode | str | Required | "enabled" or "disabled" |
allow_net | List[str] | [] | Outbound allowlist used only when mode="enabled" |
mode="disabled" removes the guest network interface entirely.
volumes=[
("/host/config", "/etc/app/config", "ro"), # Read-only
("/host/data", "/mnt/data", "rw"), # Read-write
]
ports=[
(8080, 80, "tcp"), # HTTP
(5432, 5432, "tcp"), # PostgreSQL
(53, 53, "udp"), # DNS
]
from boxlite import Secret
secrets=[
Secret(
name="openai",
value="sk-...",
hosts=["api.openai.com"],
# placeholder defaults to "<BOXLITE_SECRET:openai>"
)
]
Handle to a running or stopped box.
| Property | Type | Description |
|---|
id | str | Unique box identifier (ULID format) |
| Method | Signature | Description |
|---|
exec() | (cmd, args, env, tty) -> Execution | Execute command (async) |
stop() | () -> None | Stop the box gracefully (async) |
remove() | () -> None | Delete box and its data (async) |
info() | () -> BoxInfo | Get box metadata (async) |
metrics() | () -> BoxMetrics | Get resource usage metrics (async) |
Metadata about a box.
| Field | Type | Description |
|---|
id | str | Unique box identifier (ULID) |
name | str | None | Optional user-assigned name |
status | str | Current status: "running", "stopped", "created" |
created_at | datetime | Creation timestamp |
pid | int | None | Process ID (if running) |
image | str | OCI image used |
cpus | int | Allocated CPU cores |
memory_mib | int | Allocated memory in MiB |
Detailed state information for a box.
| Value | Description |
|---|
Created | Box created but not yet started |
Starting | Box is initializing |
Running | Box is running and ready |
Stopping | Box is shutting down |
Stopped | Box is stopped |
Failed | Box encountered an error |
Represents a running command execution.
| Method | Signature | Description |
|---|
stdout() | () -> ExecStdout | Get stdout stream (async iterator) |
stderr() | () -> ExecStderr | Get stderr stream (async iterator) |
stdin() | () -> ExecStdin | Get stdin writer |
wait() | () -> ExecResult | Wait for completion (async) |
kill() | (signal: int = 9) -> None | Send signal to process (async) |
resize_tty() | (rows: int, cols: int) -> None | Resize PTY terminal for executions started with tty=True (async) |
# Execute with streaming output
execution = await box.exec("python", ["-c", "for i in range(5): print(i)"])
# Stream stdout
async for line in execution.stdout():
print(f"Output: {line}")
# Wait for completion
result = await execution.wait()
print(f"Exit code: {result.exit_code}")
Async iterators for streaming output.
# Stream stdout line by line
stdout = execution.stdout()
async for line in stdout:
print(line)
# Stream stderr
stderr = execution.stderr()
async for line in stderr:
print(f"Error: {line}", file=sys.stderr)
Note: Each stream can only be iterated once. After iteration, the stream is consumed.
Writer for sending input to a running process.
| Method | Signature | Description |
|---|
send_input() | (data: bytes) -> None | Write bytes to stdin (async) |
# Interactive input
execution = await box.exec("cat")
stdin = execution.stdin()
# Send data
await stdin.send_input(b"Hello\n")
await stdin.send_input(b"World\n")
# Wait for completion
result = await execution.wait()
Result of a completed execution.
| Field | Type | Description |
|---|
exit_code | int | Process exit code (0 = success) |
Note: For higher-level APIs (SimpleBox.exec()), the result also includes stdout and stderr strings.
Context manager for basic command execution with automatic cleanup.
from boxlite import SimpleBox
SimpleBox(
image: str,
memory_mib: int = None,
cpus: int = None,
runtime: Boxlite = None,
name: str = None,
auto_remove: bool = True,
**kwargs
)
| Parameter | Type | Default | Description |
|---|
image | str | Required | Container image to use |
memory_mib | int | System default | Memory limit in MiB |
cpus | int | System default | Number of CPU cores |
runtime | Boxlite | Global default | Runtime instance |
name | str | None | Optional unique name |
auto_remove | bool | True | Remove box when stopped |
**kwargs | | | Additional options: env, volumes, ports, working_dir, network, secrets |
| Property | Type | Description |
|---|
id | str | Box ID (raises if not started) |
| Method | Signature | Description |
|---|
start() | () -> Self | Explicitly start the box (async) |
exec() | (cmd, *args, env=None, user=None, timeout=None, cwd=None) -> ExecResult | Execute command and wait (async) |
info() | () -> BoxInfo | Get box metadata |
shutdown() | () -> None | Shutdown and release resources |
async with SimpleBox(image="python:slim") as box:
result = await box.exec("python", "-c", "print('Hello!')")
print(result.stdout) # "Hello!\n"
print(result.exit_code) # 0
# Per-command options
result = await box.exec("pwd", cwd="/tmp") # working directory
result = await box.exec("whoami", user="nobody") # run as user
result = await box.exec("sleep", "60", timeout=5) # timeout in seconds
Specialized box for Python code execution with package management.
from boxlite import CodeBox
CodeBox(
image: str = "python:slim",
memory_mib: int = None,
cpus: int = None,
runtime: Boxlite = None,
**kwargs
)
| Method | Signature | Description |
|---|
run() | (code: str, timeout: int = None) -> str | Execute Python code (async) |
run_script() | (script_path: str) -> str | Execute Python script file (async) |
install_package() | (package: str) -> str | Install package with pip (async) |
install_packages() | (*packages: str) -> str | Install multiple packages (async) |
async with CodeBox() as cb:
# Install packages
await cb.install_package("requests")
# Run code
result = await cb.run("""
import requests
print(requests.get('https://api.github.com/zen').text)
""")
print(result)
Box configured for browser automation with Chrome DevTools Protocol.
from boxlite import BrowserBox, BrowserBoxOptions
| Field | Type | Default | Description |
|---|
browser | str | "chromium" | Browser type: "chromium", "firefox", "webkit" |
memory | int | 2048 | Memory in MiB |
cpu | int | 2 | Number of CPU cores |
| Browser | Port |
|---|
| chromium | 9222 |
| firefox | 9223 |
| webkit | 9224 |
| Method | Signature | Description |
|---|
endpoint() | () -> str | Get CDP endpoint URL |
from boxlite import BrowserBox, BrowserBoxOptions
opts = BrowserBoxOptions(browser="chromium", memory=4096)
async with BrowserBox(opts) as browser:
endpoint = browser.endpoint() # "http://localhost:9222"
# Connect with Puppeteer or Playwright
# puppeteer.connect({ browserURL: endpoint })
Box with full desktop environment and GUI automation capabilities.
from boxlite import ComputerBox
ComputerBox(
cpu: int = 2,
memory: int = 2048,
gui_http_port: int = 3000,
gui_https_port: int = 3001,
runtime: Boxlite = None,
**kwargs
)
| Method | Signature | Description |
|---|
mouse_move() | (x: int, y: int) -> None | Move cursor to coordinates (async) |
left_click() | () -> None | Left click at current position (async) |
right_click() | () -> None | Right click at current position (async) |
middle_click() | () -> None | Middle click at current position (async) |
double_click() | () -> None | Double left click (async) |
triple_click() | () -> None | Triple left click (async) |
left_click_drag() | (start_x, start_y, end_x, end_y) -> None | Drag from start to end (async) |
cursor_position() | () -> Tuple[int, int] | Get current cursor (x, y) (async) |
| Method | Signature | Description |
|---|
type() | (text: str) -> None | Type text characters (async) |
key() | (text: str) -> None | Press key or key combination (async) |
The key() method uses xdotool key syntax:
| Key | Syntax |
|---|
| Enter | Return |
| Tab | Tab |
| Escape | Escape |
| Backspace | BackSpace |
| Delete | Delete |
| Arrow keys | Up, Down, Left, Right |
| Function keys | F1, F2, ... F12 |
| Modifiers | ctrl, alt, shift, super |
| Combinations | ctrl+c, ctrl+shift+s, alt+Tab |
Examples:
await computer.key("Return") # Press Enter
await computer.key("ctrl+c") # Copy
await computer.key("ctrl+shift+s") # Save As
await computer.key("alt+Tab") # Switch window
await computer.key("ctrl+a Delete") # Select all and delete
| Method | Signature | Description |
|---|
wait_until_ready() | (timeout: int = 60) -> None | Wait for desktop ready (async) |
screenshot() | () -> dict | Capture screen (async) |
scroll() | (x, y, direction, amount=3) -> None | Scroll at position (async) |
get_screen_size() | () -> Tuple[int, int] | Get screen dimensions (async) |
{
"data": str, # Base64-encoded PNG
"width": int, # 1024 (default)
"height": int, # 768 (default)
"format": str # "png"
}
| Direction | Description |
|---|
"up" | Scroll up |
"down" | Scroll down |
"left" | Scroll left |
"right" | Scroll right |
async with ComputerBox() as desktop:
await desktop.wait_until_ready()
# Take screenshot
screenshot = await desktop.screenshot()
# Mouse interaction
await desktop.mouse_move(100, 200)
await desktop.left_click()
# Type text
await desktop.type("Hello, World!")
await desktop.key("Return")
# Get screen size
width, height = await desktop.get_screen_size()
Box for interactive terminal sessions with PTY support.
from boxlite import InteractiveBox
InteractiveBox(
image: str,
shell: str = "/bin/sh",
tty: bool = None,
memory_mib: int = None,
cpus: int = None,
runtime: Boxlite = None,
name: str = None,
auto_remove: bool = True,
**kwargs
)
| Parameter | Type | Default | Description |
|---|
image | str | Required | Container image |
shell | str | "/bin/sh" | Shell to run |
tty | bool | None | None | TTY mode (see below) |
| Value | Behavior |
|---|
None | Auto-detect from sys.stdin.isatty() |
True | Force TTY mode with I/O forwarding |
False | No I/O forwarding (programmatic control) |
| Method | Signature | Description |
|---|
wait() | () -> None | Wait for shell to exit (async) |
# Interactive shell session
async with InteractiveBox(image="alpine:latest") as box:
# You're now in an interactive shell
# Type commands, see output in real-time
# Type "exit" to close
await box.wait()
Synchronous wrappers using greenlet fiber switching. Requires pip install boxlite[sync].
from boxlite import SyncBoxlite, SyncBox, SyncSimpleBox, SyncCodeBox
| Use Case | API |
|---|
| New async applications | Async API (default) |
| Existing sync codebase | Sync API |
| Jupyter notebooks | Sync API |
| REPL/interactive use | Sync API |
| Inside async functions | Async API only |
| Async API | Sync API | Notes |
|---|
Boxlite | SyncBoxlite | |
Box | SyncBox | |
Execution | SyncExecution | |
ExecStdout | SyncExecStdout | Regular iterator |
ExecStderr | SyncExecStderr | Regular iterator |
SimpleBox | SyncSimpleBox | |
CodeBox | SyncCodeBox | |
from boxlite import SyncBoxlite, BoxOptions
with SyncBoxlite.default() as runtime:
box = runtime.create(BoxOptions(image="alpine:latest"))
execution = box.exec("echo", ["Hello"])
for line in execution.stdout():
print(line)
box.stop()
from boxlite import SyncSimpleBox
with SyncSimpleBox(image="python:slim") as box:
result = box.exec("python", "-c", "print('Hello!')")
print(result.stdout)
from boxlite import SyncCodeBox
with SyncCodeBox() as cb:
result = cb.run("print('Hello, World!')")
print(result)
The sync API uses greenlet fiber switching:
- A dispatcher fiber runs the asyncio event loop
- User code runs in the main fiber
- Sync methods switch to dispatcher, await async operations, then switch back
Limitation: Cannot be used inside an async context (when an event loop is already running).
from boxlite import BoxliteError, ExecError, TimeoutError, ParseError
BoxliteError (base)
├── ExecError # Command execution failed
├── TimeoutError # Operation timed out
└── ParseError # Output parsing failed
Base exception for all BoxLite errors.
try:
async with SimpleBox(image="invalid:image") as box:
pass
except BoxliteError as e:
print(f"BoxLite error: {e}")
Raised when a command execution fails (non-zero exit code).
| Attribute | Type | Description |
|---|
command | str | The command that failed |
exit_code | int | Non-zero exit code |
stderr | str | Standard error output |
try:
result = await box.exec("false") # Exit code 1
except ExecError as e:
print(f"Command: {e.command}")
print(f"Exit code: {e.exit_code}")
print(f"Stderr: {e.stderr}")
Raised when an operation times out.
try:
await computer.wait_until_ready(timeout=5)
except TimeoutError:
print("Desktop did not become ready in time")
Raised when output parsing fails.
try:
x, y = await computer.cursor_position()
except ParseError:
print("Failed to parse cursor position")
Aggregate metrics across all boxes.
| Field | Type | Description |
|---|
boxes_created | int | Total boxes created |
boxes_destroyed | int | Total boxes destroyed |
total_exec_calls | int | Total command executions |
active_boxes | int | Currently running boxes |
runtime = Boxlite.default()
metrics = await runtime.metrics()
print(f"Boxes created: {metrics.boxes_created}")
print(f"Active boxes: {metrics.active_boxes}")
Per-box resource usage metrics.
| Field | Type | Description |
|---|
cpu_time_ms | int | Total CPU time in milliseconds |
memory_usage_bytes | int | Current memory usage in bytes |
network_bytes_sent | int | Total bytes sent |
network_bytes_received | int | Total bytes received |
metrics = await box.metrics()
print(f"CPU time: {metrics.cpu_time_ms}ms")
print(f"Memory: {metrics.memory_usage_bytes / (1024**2):.2f} MB")
Default values used by BoxLite.
| Constant | Value | Description |
|---|
DEFAULT_CPUS | 1 | Default CPU cores |
DEFAULT_MEMORY_MIB | 2048 | Default memory in MiB |
COMPUTERBOX_CPUS | 2 | ComputerBox default CPUs |
COMPUTERBOX_MEMORY_MIB | 2048 | ComputerBox default memory |
COMPUTERBOX_DISPLAY_WIDTH | 1024 | Screen width in pixels |
COMPUTERBOX_DISPLAY_HEIGHT | 768 | Screen height in pixels |
COMPUTERBOX_GUI_HTTP_PORT | 3000 | HTTP GUI port |
COMPUTERBOX_GUI_HTTPS_PORT | 3001 | HTTPS GUI port |
DESKTOP_READY_TIMEOUT | 60 | Desktop ready timeout (seconds) |