Skip to main content

Universal library for AI code execution sandboxes

Project description

sandboxes

Universal library for AI code execution sandboxes.

Python Version License: MIT

Overview

sandboxes provides a unified interface for sandboxed code execution across multiple providers:

  • Current providers: E2B, Modal, Daytona
  • Experimental: Cloudflare (requires self-hosted Worker deployment)

Write your code once and switch between providers with a single line change, or let the library automatically select the best available option.

Installation

Add to your project:

uv add cased-sandboxes

or install with your preferred Python package manager and use the CLI for any language, e.g.,:

uv pip install cased-sandboxes

Quick Start

One-line Execution + Auto-select Provider

import asyncio
from sandboxes import run

async def main():
    # Creates a temporary sandbox, runs the command, then destroys the sandbox
    result = await run("echo 'Hello from sandbox!'")
    print(result.stdout)

    # Behind the scenes, run() does this:
    # 1. Auto-detects available providers (e.g., E2B, Modal, Daytona)
    # 2. Creates a new sandbox with the first available provider
    # 3. Executes your command in that isolated environment
    # 4. Returns the result
    # 5. Automatically destroys the sandbox

asyncio.run(main())

Multiple Commands

import asyncio
from sandboxes import run_many

async def main():
    # Execute multiple commands in one sandbox
    results = await run_many([
        "pip install requests",
        "python -c 'import requests; print(requests.__version__)'"
    ])
    for result in results:
        print(result.stdout)

asyncio.run(main())

Persistent Sandbox Sessions

import asyncio
from sandboxes import Sandbox

async def main():
    # Create a sandbox that persists for multiple operations
    async with Sandbox.create() as sandbox:
        # Install dependencies
        await sandbox.execute("pip install numpy pandas")

        # Run your code
        result = await sandbox.execute("python analyze.py")
        print(result.stdout)

        # Upload/download files
        await sandbox.upload("data.csv", "/tmp/data.csv")
        await sandbox.download("/tmp/results.csv", "results.csv")
    # Automatically cleaned up on exit

asyncio.run(main())

Smart Sandbox Reuse

import asyncio
from sandboxes import Sandbox

async def main():
    # First call creates a new sandbox
    sandbox1 = await Sandbox.get_or_create(
        labels={"project": "ml-training", "gpu": "true"}
    )

    # Later calls reuse the same sandbox
    sandbox2 = await Sandbox.get_or_create(
        labels={"project": "ml-training", "gpu": "true"}
    )

    assert sandbox1.id == sandbox2.id  # Same sandbox

asyncio.run(main())

Provider Selection with Automatic Failover

import asyncio
from sandboxes import Sandbox, run

async def main():
    # Control where your code runs
    sandbox = await Sandbox.create(
        provider="e2b",  # Try E2B first
        fallback=["modal", "cloudflare", "daytona"],  # Automatic failover
    )

    # The library automatically tries the next provider if one fails
    print(f"Using: {sandbox._provider_name}")

    # Or specify directly with run()
    result = await run("python script.py", provider="modal")  # Runs on Modal

asyncio.run(main())

Custom Images and Templates

import asyncio
from sandboxes import Sandbox, SandboxConfig
from sandboxes.providers import ModalProvider, E2BProvider, DaytonaProvider

async def main():
    # High-level API - works with any provider
    sandbox = await Sandbox.create(image="python:3.12-slim")

    # Or with specific providers
    daytona_provider = DaytonaProvider()
    config = SandboxConfig(image="daytonaio/ai-test:0.2.3")
    sandbox = await daytona_provider.create_sandbox(config)

asyncio.run(main())

# Via CLI
# sandboxes run "python --version" --image python:3.12-alpine

API Reference

Core Classes

  • Sandbox: High-level interface with automatic provider management
  • SandboxConfig: Configuration for sandbox creation (labels, timeout, image)
  • ExecutionResult: Standardized result object (stdout, stderr, exit_code)
  • Manager: Multi-provider orchestration with failover
  • SandboxProvider: Abstract base class for provider implementations

Key Methods

# High-level functions
await run(command: str, provider: str = None) -> ExecutionResult
await run_many(commands: list[str], provider: str = None) -> list[ExecutionResult]

# Sandbox methods
await Sandbox.create(provider=None, fallback=None, labels=None, image=None) -> Sandbox
await Sandbox.get_or_create(labels: dict) -> Sandbox
await Sandbox.find(labels: dict) -> Sandbox | None
await sandbox.execute(command: str) -> ExecutionResult
await sandbox.execute_many(commands: list[str]) -> list[ExecutionResult]
await sandbox.stream(command: str) -> AsyncIterator[str]
await sandbox.upload(local_path: str, remote_path: str)
await sandbox.download(remote_path: str, local_path: str)
await sandbox.destroy()

Command Line Interface

sandboxes includes a powerful CLI for running code in any language from your terminal. Execute TypeScript, Go, Rust, Python, or any other language in isolated sandboxes.

Quick Start

# Run TypeScript from a file
sandboxes run --file script.ts

# Run Go code from stdin
cat main.go | sandboxes run --lang go

# Direct command execution
sandboxes run "python3 -c 'print(sum(range(100)))'"

# Run with specific provider
sandboxes run "python3 --version" --provider e2b

# List all sandboxes
sandboxes list

Commands

run - Execute Code

# 1. From file (auto-detects language)
sandboxes run --file script.py
sandboxes run --file main.go

# 2. From stdin/pipe
cat script.py | sandboxes run --lang python
echo 'console.log("Hello!")' | sandboxes run --lang node

# 3. Direct command
sandboxes run "python3 -c 'print(42)'"

Options:

# Specify provider
sandboxes run --file app.py -p e2b

# Environment variables
sandboxes run --file script.py -e API_KEY=secret -e DEBUG=1

# Labels for reuse
sandboxes run --file app.py -l project=myapp --reuse

# Keep sandbox (don't auto-destroy)
sandboxes run --file script.py --keep

# Timeout
sandboxes run --file script.sh -t 600

Supported languages: python, node/javascript, typescript, go, rust, bash/sh

list - List Sandboxes

View all active sandboxes:

# List all sandboxes
sandboxes list

# Filter by provider
sandboxes list -p e2b

# Filter by labels
sandboxes list -l env=prod

# JSON output
sandboxes list --json

exec - Execute in Existing Sandbox

sandboxes exec sb-abc123 "ls -la" -p modal
sandboxes exec sb-abc123 "python script.py" -p e2b -e DEBUG=1

destroy - Remove Sandbox

sandboxes destroy sb-abc123 -p e2b

providers - Check Providers

sandboxes providers

test - Test Provider Connectivity

sandboxes test          # Test all
sandboxes test -p e2b   # Test specific

CLI Examples

Development Workflow

# Create development sandbox
sandboxes run "git clone https://github.com/user/repo.git /app" \
  -l project=myapp \
  -l env=dev \
  --keep

# List to get sandbox ID
sandboxes list -l project=myapp

# Run commands in the sandbox
sandboxes exec sb-abc123 "cd /app && npm install" -p e2b
sandboxes exec sb-abc123 "cd /app && npm test" -p e2b

# Cleanup when done
sandboxes destroy sb-abc123 -p e2b

Multi-Language Code Testing

# TypeScript
echo 'const x: number = 42; console.log(x)' > test.ts
sandboxes run --file test.ts

# Go with automatic dependency installation
sandboxes run --file main.go --deps

# Go from stdin
cat main.go | sandboxes run --lang go

# Python from remote URL
curl -s https://example.com/script.py | sandboxes run --lang python

Auto-Dependency Installation (golang only for now): Use --deps to automatically install dependencies from go.mod (located in the same directory as your code file). The CLI will upload go.mod and go.sum (if present) and run go mod download before executing your code.

Provider Configuration

Automatic Configuration

The library automatically detects available providers from environment variables:

# Set any of these environment variables:
export E2B_API_KEY="..."
export MODAL_TOKEN_ID="..."  # Or use `modal token set`
export DAYTONA_API_KEY="..."
export CLOUDFLARE_SANDBOX_BASE_URL="https://your-worker.workers.dev"
export CLOUDFLARE_API_TOKEN="..."

Then just use:

import asyncio
from sandboxes import Sandbox

async def main():
    sandbox = await Sandbox.create()  # Auto-selects first available provider

asyncio.run(main())

How Auto-Detection Works

When you call Sandbox.create() or run(), the library checks for providers in this priority order:

  1. Daytona - Looks for DAYTONA_API_KEY
  2. E2B - Looks for E2B_API_KEY
  3. Modal - Looks for ~/.modal.toml or MODAL_TOKEN_ID
  4. Cloudflare (experimental) - Looks for CLOUDFLARE_SANDBOX_BASE_URL + CLOUDFLARE_API_TOKEN

The first provider with valid credentials becomes the default. Cloudflare requires deploying your own Worker.

Customizing the Default Provider

You can override the auto-detected default:

from sandboxes import Sandbox

# Option 1: Set default provider explicitly
Sandbox.configure(default_provider="modal")

# Option 2: Specify provider per call
sandbox = await Sandbox.create(provider="e2b")

# Option 3: Use fallback chain
sandbox = await Sandbox.create(
    provider="daytona",
    fallback=["e2b", "modal"]
)

# Check which providers are available
Sandbox._ensure_manager()
print(f"Available: {list(Sandbox._manager.providers.keys())}")
print(f"Default: {Sandbox._manager.default_provider}")

Manual Provider Configuration

For more control, you can configure providers manually:

from sandboxes import Sandbox

# Configure providers programmatically
Sandbox.configure(
    e2b_api_key="your-key",
    cloudflare_config={
        "base_url": "https://your-worker.workers.dev",
        "api_token": "your-token",
    },
    default_provider="e2b"
)

Direct Provider Usage (Low-Level API)

For advanced use cases, you can work with providers directly:

from sandboxes.providers import (
    E2BProvider,
    ModalProvider,
    DaytonaProvider,
    CloudflareProvider,
)

# E2B - Uses E2B_API_KEY env var
provider = E2BProvider()

# Modal - Uses ~/.modal.toml for auth
provider = ModalProvider()

# Daytona - Uses DAYTONA_API_KEY env var
provider = DaytonaProvider()

# Cloudflare - Requires base_url and token
provider = CloudflareProvider(
    base_url="https://your-worker.workers.dev",
    api_token="your-token",
)

Each provider requires appropriate authentication:

  • E2B: Set E2B_API_KEY environment variable
  • Modal: Run modal token set to configure
  • Daytona: Set DAYTONA_API_KEY environment variable
  • Cloudflare (experimental): Deploy the Cloudflare sandbox Worker and set CLOUDFLARE_SANDBOX_BASE_URL, CLOUDFLARE_API_TOKEN, and (optionally) CLOUDFLARE_ACCOUNT_ID

Cloudflare setup tips (experimental)

⚠️ Note: Cloudflare support is experimental and requires self-hosting a Worker.

  1. Clone the Cloudflare sandbox-sdk repository and deploy the examples/basic Worker with wrangler.
  2. Provision a Workers Paid plan and enable Containers + Docker Hub registry for your account.
  3. Define a secret (e.g. SANDBOX_API_TOKEN) in Wrangler and reuse the same value for CLOUDFLARE_API_TOKEN locally.
  4. Set CLOUDFLARE_SANDBOX_BASE_URL to the Worker URL (e.g. https://cf-sandbox.your-subdomain.workers.dev).

Advanced Usage

Multi-Provider Orchestration

import asyncio
from sandboxes import Manager, SandboxConfig
from sandboxes.providers import E2BProvider, ModalProvider, DaytonaProvider, CloudflareProvider

async def main():
    # Initialize manager and register providers
    manager = Manager(default_provider="e2b")

    manager.register_provider("e2b", E2BProvider, {})
    manager.register_provider("modal", ModalProvider, {})
    manager.register_provider("daytona", DaytonaProvider, {})
    manager.register_provider(
        "cloudflare",
        CloudflareProvider,
        {"base_url": "https://your-worker.workers.dev", "api_token": "..."}
    )

    # Manager handles failover automatically
    sandbox = await manager.create_sandbox(
        SandboxConfig(labels={"task": "test"}),
        fallback_providers=["modal", "daytona"]  # Try these if primary fails
    )

asyncio.run(main())

Sandbox Reuse (Provider-Level)

For advanced control, work directly with providers instead of the high-level Sandbox API:

import asyncio
from sandboxes import SandboxConfig
from sandboxes.providers import E2BProvider

async def main():
    provider = E2BProvider()

    # Sandboxes can be reused based on labels
    config = SandboxConfig(
        labels={"project": "ml-training", "gpu": "true"}
    )

    # This will find existing sandbox or create new one
    sandbox = await provider.get_or_create_sandbox(config)

    # Later in another process...
    # This will find the same sandbox
    sandbox = await provider.find_sandbox({"project": "ml-training"})

asyncio.run(main())

Streaming Execution

import asyncio
from sandboxes.providers import E2BProvider

async def main():
    provider = E2BProvider()
    sandbox = await provider.create_sandbox()

    # Stream output as it's generated
    async for chunk in provider.stream_execution(
        sandbox.id,
        "for i in range(10): print(i); time.sleep(1)"
    ):
        print(chunk, end="")

asyncio.run(main())

Connection Pooling

import asyncio
from sandboxes import SandboxConfig
from sandboxes.pool import ConnectionPool
from sandboxes.providers import E2BProvider

async def main():
    # Create a connection pool for better performance
    pool = ConnectionPool(
        provider=E2BProvider(),
        max_connections=10,
        max_idle_time=300,
        ttl=3600
    )

    # Get or create connection
    conn = await pool.get_or_create(
        SandboxConfig(labels={"pool": "ml"})
    )

    # Return to pool when done
    await pool.release(conn)

asyncio.run(main())

Architecture

Core Components

  • Sandbox: High-level interface with automatic provider management
  • SandboxProvider: Abstract base class for all providers
  • SandboxConfig: Configuration for sandbox creation
  • ExecutionResult: Standardized execution results
  • Manager: Multi-provider orchestration
  • ConnectionPool: Connection pooling with TTL
  • RetryPolicy: Configurable retry logic
  • CircuitBreaker: Fault tolerance

Environment Variables

# E2B
export E2B_API_KEY="e2b_..."

# Daytona
export DAYTONA_API_KEY="dtn_..."

# Modal (or use modal token set)
export MODAL_TOKEN_ID="..."
export MODAL_TOKEN_SECRET="..."

# Cloudflare
export CLOUDFLARE_SANDBOX_BASE_URL="https://your-worker.workers.dev"
export CLOUDFLARE_API_TOKEN="..."
export CLOUDFLARE_ACCOUNT_ID="..."  # Optional

Multi-Language Support

While sandboxes is a Python library, it can execute code in any language available in the sandbox environment. The sandboxes run standard Linux containers, so you can execute TypeScript, Go, Rust, Java, or any other language.

Running TypeScript

import asyncio
from sandboxes import Sandbox

async def run_typescript():
    """Execute TypeScript code in a sandbox."""
    async with Sandbox.create() as sandbox:
        # TypeScript code
        ts_code = '''
const greeting: string = "Hello from TypeScript!";
const numbers: number[] = [1, 2, 3, 4, 5];
const sum: number = numbers.reduce((a, b) => a + b, 0);

console.log(greeting);
console.log(`Sum of numbers: ${sum}`);
console.log(`Type system ensures safety at compile time`);
'''

        # Run with ts-node (npx auto-installs)
        result = await sandbox.execute(
            f"echo '{ts_code}' > /tmp/app.ts && npx -y ts-node /tmp/app.ts"
        )

        print(result.stdout)
        # Output:
        # Hello from TypeScript!
        # Sum of numbers: 15
        # Type system ensures safety at compile time

asyncio.run(run_typescript())

Running Go

import asyncio
from sandboxes import Sandbox

async def run_go():
    """Execute Go code in a sandbox."""
    async with Sandbox.create() as sandbox:
        # Go code
        go_code = '''package main

import (
    "fmt"
    "math"
)

func main() {
    fmt.Println("Hello from Go!")

    // Calculate fibonacci
    n := 10
    fmt.Printf("Fibonacci(%d) = %d\\n", n, fibonacci(n))

    // Demonstrate type safety
    radius := 5.0
    area := math.Pi * radius * radius
    fmt.Printf("Circle area (r=%.1f): %.2f\\n", radius, area)
}

func fibonacci(n int) int {
    if n <= 1 {
        return n
    }
    return fibonacci(n-1) + fibonacci(n-2)
}
'''

        # Save and run Go code
        result = await sandbox.execute(f'''
cat > /tmp/main.go << 'EOF'
{go_code}
EOF
go run /tmp/main.go
''')

        print(result.stdout)
        # Output:
        # Hello from Go!
        # Fibonacci(10) = 55
        # Circle area (r=5.0): 78.54

asyncio.run(run_go())

Common Use Cases

AI Agent Code Execution

import asyncio
from sandboxes import Sandbox

async def execute_agent_code(code: str, language: str = "python"):
    """Safely execute AI-generated code."""
    async with Sandbox.create() as sandbox:
        # Install any required packages first
        if "import" in code:
            # Extract and install imports (simplified)
            await sandbox.execute("pip install requests numpy")

        # Execute the code
        result = await sandbox.execute(f"{language} -c '{code}'")

        if result.exit_code != 0:
            return f"Error: {result.stderr}"
        return result.stdout

# Example usage
asyncio.run(execute_agent_code("print('Hello!')", "python"))

Data Processing Pipeline

import asyncio
from sandboxes import Sandbox

async def process_dataset(dataset_url: str):
    """Process data in isolated environment."""
    async with Sandbox.create(labels={"task": "data-pipeline"}) as sandbox:
        # Setup environment
        await sandbox.execute_many([
            "pip install pandas numpy scikit-learn",
            f"wget {dataset_url} -O data.csv"
        ])

        # Upload processing script
        await sandbox.upload("process.py", "/tmp/process.py")

        # Run processing with streaming output
        async for output in sandbox.stream("python /tmp/process.py"):
            print(output, end="")

        # Download results
        await sandbox.download("/tmp/results.csv", "results.csv")

# Example usage
asyncio.run(process_dataset("https://example.com/data.csv"))

Code Testing and Validation

import asyncio
from sandboxes import Sandbox

async def test_solution(code: str, test_cases: list):
    """Test code against multiple test cases."""
    results = []

    async with Sandbox.create() as sandbox:
        # Save the code
        await sandbox.upload("solution.py", "/tmp/solution.py")

        # Run each test case
        for i, test in enumerate(test_cases):
            result = await sandbox.execute(
                f"python /tmp/solution.py < {test['input']}"
            )
            results.append({
                "test": i + 1,
                "passed": result.stdout.strip() == test['expected'],
                "output": result.stdout.strip()
            })

    return results

# Example usage
asyncio.run(test_solution("print(sum(map(int, input().split())))", [
    {"input": "1 2 3", "expected": "6"}
]))

Troubleshooting

No Providers Available

# If you see: "No provider specified and no default provider set"

# Solution 1: Set environment variables
export E2B_API_KEY="your-key"

# Solution 2: Configure manually
from sandboxes import Sandbox
Sandbox.configure(e2b_api_key="your-key")

# Solution 3: Use low-level API
from sandboxes.providers import E2BProvider
provider = E2BProvider(api_key="your-key")

Provider Failures

import asyncio
from sandboxes import Sandbox
from sandboxes.exceptions import ProviderError

async def main():
    # Enable automatic failover
    sandbox = await Sandbox.create(
        provider="e2b",
        fallback=["modal", "cloudflare", "daytona"]
    )

    # Or handle errors manually
    try:
        sandbox = await Sandbox.create(provider="e2b")
    except ProviderError:
        sandbox = await Sandbox.create(provider="modal")

asyncio.run(main())

Debugging

import asyncio
import logging
from sandboxes import Sandbox

async def main():
    # Enable debug logging
    logging.basicConfig(level=logging.DEBUG)

    # Check provider health
    Sandbox._ensure_manager()
    for name, provider in Sandbox._manager.providers.items():
        health = await provider.health_check()
        print(f"{name}: {'✅' if health else '❌'}")

asyncio.run(main())

License

MIT License - see LICENSE file for details.

Acknowledgments

Built by Cased

Special thanks to the teams at E2B, Modal, Daytona, and Cloudflare for their excellent sandbox platforms.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cased_sandboxes-0.2.0.tar.gz (39.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

cased_sandboxes-0.2.0-py3-none-any.whl (46.8 kB view details)

Uploaded Python 3

File details

Details for the file cased_sandboxes-0.2.0.tar.gz.

File metadata

  • Download URL: cased_sandboxes-0.2.0.tar.gz
  • Upload date:
  • Size: 39.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.8

File hashes

Hashes for cased_sandboxes-0.2.0.tar.gz
Algorithm Hash digest
SHA256 1aa3166c772074903fac868f19f88320671dd08333488848a5cbfe57441b01b5
MD5 da20d030c6cd8b98fe584cff53776a19
BLAKE2b-256 4b2da842c8848d97ad5a67fe7e27b2969205ee97862f4eb6e2e6c2fcec31801f

See more details on using hashes here.

File details

Details for the file cased_sandboxes-0.2.0-py3-none-any.whl.

File metadata

File hashes

Hashes for cased_sandboxes-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 abec8679e53adb18c0e1707d9f72c1e1a40bd427828876e08be5c0deb83d8916
MD5 7ec0a83bfdad6f27fc554435b1f9f576
BLAKE2b-256 2992789c97c0c600076a4670301ae7f2f518aa4f780fdebfe451230b2acf8a4a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page