Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/opensandbox-group/OpenSandbox/llms.txt

Use this file to discover all available pages before exploring further.

The OpenSandbox Python SDK provides both async (asyncio) and synchronous interfaces for creating, managing, and interacting with secure sandbox environments. It covers shell command execution, file management, egress policy control, Credential Vault, and a client-side sandbox pool for pre-warming idle instances.

Installation

pip install opensandbox

Quick Start (Async)

The async API is the primary interface. Use Sandbox.create() with async with to ensure local resources are released automatically.
import asyncio
from opensandbox.sandbox import Sandbox
from opensandbox.config import ConnectionConfig
from opensandbox.exceptions import SandboxException

async def main():
    # 1. Configure connection
    config = ConnectionConfig(
        domain="api.opensandbox.io",
        api_key="your-api-key"
    )

    # 2. Create a sandbox
    try:
        sandbox = await Sandbox.create(
            "ubuntu",
            connection_config=config
        )
        async with sandbox:

            # 3. Execute a shell command
            execution = await sandbox.commands.run("echo 'Hello Sandbox!'")

            # 4. Print output
            print(execution.logs.stdout[0].text)

            # 5. Terminate the remote instance explicitly
            # sandbox.close() is called automatically by async with
            await sandbox.kill()

    except SandboxException as e:
        print(f"Sandbox Error: [{e.error.code}] {e.error.message}")
        print(f"Request ID: {e.request_id}")
    except Exception as e:
        print(f"Error: {e}")

if __name__ == "__main__":
    asyncio.run(main())

Quick Start (Sync)

Use SandboxSync and ConnectionConfigSync when you prefer a blocking API or are not running inside an event loop.
from datetime import timedelta

import httpx
from opensandbox import SandboxSync
from opensandbox.config import ConnectionConfigSync

config = ConnectionConfigSync(
    domain="api.opensandbox.io",
    api_key="your-api-key",
    request_timeout=timedelta(seconds=30),
    transport=httpx.HTTPTransport(limits=httpx.Limits(max_connections=20)),
)

sandbox = SandboxSync.create("ubuntu", connection_config=config)
with sandbox:
    execution = sandbox.commands.run("echo 'Hello Sandbox!'")
    print(execution.logs.stdout[0].text)
    sandbox.kill()

ConnectionConfig Parameters

Configure the connection to the OpenSandbox API server. The async class is ConnectionConfig; the sync equivalent is ConnectionConfigSync.
ParameterDescriptionDefaultEnvironment Variable
api_keyAPI key for authenticationRequiredOPEN_SANDBOX_API_KEY
domainEndpoint domain of the sandbox serviceRequired (or localhost:8080)OPEN_SANDBOX_DOMAIN
protocolHTTP protocol (http/https)http
request_timeoutTimeout for API requests30 seconds
debugEnable debug logging for HTTP requestsFalse
headersCustom HTTP headersEmpty
transportShared httpx transport (pool/proxy/retry)SDK-created per instance
use_server_proxyRoute execd/endpoint calls through the server proxyFalse
If you create many Sandbox instances, pass a shared httpx.AsyncHTTPTransport with a connection pool to the transport parameter. The SDK default keep-alive is 30 seconds on its own transports. When you provide a custom transport, call await config.transport.aclose() when finished.

Sandbox.create() Parameters

ParameterDescriptionDefault
imageDocker image specificationRequired
timeoutAutomatic termination TTL10 minutes
entrypointContainer entrypoint command["tail", "-f", "/dev/null"]
resourceCPU and memory limits{"cpu": "1", "memory": "2Gi"}
envEnvironment variablesEmpty
metadataCustom metadata tagsEmpty
network_policyOptional outbound egress policy
credential_proxyCredential Vault proxy startup settings
ready_timeoutMax time to wait for sandbox readiness30 seconds
Metadata keys under opensandbox.io/ are reserved for system-managed labels and will be rejected by the server.
from datetime import timedelta
from opensandbox.models.sandboxes import NetworkPolicy, NetworkRule

sandbox = await Sandbox.create(
    "python:3.11",
    connection_config=config,
    timeout=timedelta(minutes=30),
    resource={"cpu": "2", "memory": "4Gi"},
    env={"PYTHONPATH": "/app"},
    metadata={"project": "demo"},
    network_policy=NetworkPolicy(
        defaultAction="deny",
        egress=[NetworkRule(action="allow", target="pypi.org")],
    ),
)

Key Operations

Lifecycle

Manage sandbox state without recreating the environment.
from datetime import timedelta

# Reset the expiration timer
await sandbox.renew(timedelta(minutes=30))

# Suspend all processes
await sandbox.pause()

# Resume a paused sandbox — returns a new connected Sandbox instance
sandbox = await Sandbox.resume(
    sandbox_id=sandbox.id,
    connection_config=config,
)

# Inspect current status
info = await sandbox.get_info()
print(f"State: {info.status.state}")
print(f"Expires: {info.expires_at}")  # None when no automatic expiration is set

# Kill the remote instance immediately
await sandbox.kill()
Create a sandbox with no automatic expiration by omitting timeout:
manual = await Sandbox.create(
    "ubuntu",
    connection_config=config,
)

Custom Health Check

Override the default ping check with your own readiness logic.
async def custom_health_check(sbx: Sandbox) -> bool:
    try:
        endpoint = await sbx.get_endpoint(80)
        # Perform your own check (HTTP request, socket connect, etc.)
        return True
    except Exception:
        return False

sandbox = await Sandbox.create(
    "nginx:latest",
    connection_config=config,
    health_check=custom_health_check,
)

Command Execution with Streaming

All ExecutionHandlers callbacks must be async functions.
from opensandbox.models.execd import ExecutionHandlers

async def handle_stdout(msg):
    print(f"STDOUT: {msg.text}")

async def handle_stderr(msg):
    print(f"STDERR: {msg.text}")

async def handle_complete(complete):
    print(f"Command finished in {complete.execution_time_in_millis}ms")

handlers = ExecutionHandlers(
    on_stdout=handle_stdout,
    on_stderr=handle_stderr,
    on_execution_complete=handle_complete,
)

result = await sandbox.commands.run(
    "for i in {1..5}; do echo \"Count $i\"; sleep 0.5; done",
    handlers=handlers,
)

File Operations

Read, write, search, and delete files inside the sandbox.
from opensandbox.models.filesystem import WriteEntry, SearchEntry

# Write a file
await sandbox.files.write_files([
    WriteEntry(path="/tmp/hello.txt", data="Hello World", mode=644)
])

# Read a file
content = await sandbox.files.read_file("/tmp/hello.txt")
print(f"Content: {content}")

# Search files by glob pattern
files = await sandbox.files.search(
    SearchEntry(path="/tmp", pattern="*.txt")
)
for f in files:
    print(f"Found: {f.path}")

# Delete files
await sandbox.files.delete_files(["/tmp/hello.txt"])

Egress Policy

Read and update the sandbox’s outbound network policy at runtime. Patches use merge semantics: incoming rules override rules for the same target; other existing rules are preserved; defaultAction is unchanged.
policy = await sandbox.get_egress_policy()

await sandbox.patch_egress_rules([
    NetworkRule(action="allow", target="www.github.com"),
    NetworkRule(action="deny", target="pypi.org"),
])

Credential Vault

Inject outbound credentials via the egress sidecar without exposing secrets in environment variables, commands, or logs.
from opensandbox.models.sandboxes import (
    Credential,
    CredentialBinding,
    CredentialProxyConfig,
    NetworkPolicy,
    NetworkRule,
)

sandbox = await Sandbox.create(
    "python:3.11",
    connection_config=config,
    network_policy=NetworkPolicy(
        defaultAction="deny",
        egress=[NetworkRule(action="allow", target="api.example.com")],
    ),
    credential_proxy=CredentialProxyConfig(enabled=True),
)

await sandbox.credential_vault.create(
    credentials=[Credential(name="api-token", source={"value": "<token>"})],
    bindings=[
        CredentialBinding(
            name="api-token",
            match={
                "schemes": ["https"],
                "ports": [443],
                "hosts": ["api.example.com"],
                "paths": ["/v1/*"],
            },
            auth={"type": "apiKey", "name": "x-api-key", "credential": "api-token"},
        )
    ],
)

Sandbox Pool

Pre-warm a buffer of idle sandboxes to reduce acquire latency in high-throughput workloads.

Sync Pool (Single Process)

from datetime import timedelta
from opensandbox import (
    AcquirePolicy,
    InMemoryPoolStateStore,
    PoolCreationSpec,
    SandboxPoolSync,
)
from opensandbox.config import ConnectionConfigSync

pool = SandboxPoolSync(
    pool_name="demo-pool",
    owner_id="worker-1",
    max_idle=2,
    state_store=InMemoryPoolStateStore(),  # single-process only
    connection_config=ConnectionConfigSync(domain="api.opensandbox.io"),
    creation_spec=PoolCreationSpec(image="ubuntu:22.04"),
    reconcile_interval=timedelta(seconds=5),
)

pool.start()
try:
    sandbox = pool.acquire(
        sandbox_timeout=timedelta(minutes=30),
        policy=AcquirePolicy.FAIL_FAST,
    )
    try:
        result = sandbox.commands.run("echo pool-ok")
        print(result.logs.stdout[0].text)
    finally:
        sandbox.kill()
        sandbox.close()
finally:
    pool.shutdown(graceful=True)

Async Pool

Use SandboxPoolAsync in asyncio applications so pool operations do not block the event loop.
from datetime import timedelta
from opensandbox import (
    AcquirePolicy,
    InMemoryAsyncPoolStateStore,
    PoolCreationSpec,
    SandboxPoolAsync,
)
from opensandbox.config import ConnectionConfig

pool = SandboxPoolAsync(
    pool_name="demo-pool",
    owner_id="worker-1",
    max_idle=2,
    state_store=InMemoryAsyncPoolStateStore(),  # single-event-loop only
    connection_config=ConnectionConfig(domain="api.opensandbox.io"),
    creation_spec=PoolCreationSpec(image="ubuntu:22.04"),
)

await pool.start()
try:
    sandbox = await pool.acquire(
        sandbox_timeout=timedelta(minutes=30),
        policy=AcquirePolicy.FAIL_FAST,
    )
    try:
        result = await sandbox.commands.run("echo pool-ok")
        print(result.logs.stdout[0].text)
    finally:
        await sandbox.kill()
        await sandbox.close()
finally:
    await pool.shutdown(graceful=True)

Redis-Backed Pool (Distributed)

For multi-process or multi-pod deployments, use RedisPoolStateStore. Install the optional dependency first:
pip install "opensandbox[pool-redis]"
import redis
from opensandbox import PoolCreationSpec, SandboxPoolSync
from opensandbox.config import ConnectionConfigSync
from opensandbox.pool_redis import RedisPoolStateStore

redis_client = redis.Redis.from_url(
    "redis://user:password@redis.example.com:6379/0",
    decode_responses=True,
)

pool = SandboxPoolSync(
    pool_name="prod-pool",
    owner_id="worker-1",
    max_idle=10,
    state_store=RedisPoolStateStore(redis_client, key_prefix="opensandbox:pool:prod"),
    connection_config=ConnectionConfigSync(domain="api.opensandbox.io"),
    creation_spec=PoolCreationSpec(image="ubuntu:22.04"),
    primary_lock_ttl=timedelta(seconds=60),
)
For async pools pass a redis.asyncio client to AsyncRedisPoolStateStore.
InMemoryPoolStateStore is for single-process development only — it is not suitable for gunicorn workers, Celery, or Kubernetes pods. All nodes sharing one logical pool must use the same pool_name, key_prefix, and sandbox creation spec. Use a new pool_name when changing the spec, and call resize(0) to drain an old pool before decommissioning it.

Build docs developers (and LLMs) love