Skip to main content

OpenRouter for Sandboxes - Unified Python API for cloud sandbox providers

Project description

Bespoke Labs Logo

OpenRouter for Sandboxes

One API. Many sandbox providers.


Just like OpenRouter gives you a single API across LLM providers, bespokelabs-sandbox gives you a unified interface across sandbox providers. Write your code once, swap backends with a single parameter.

Why?

  • No lock-in — Your code works across all backends. Switch providers without rewriting a single line.
  • Easily move between providers — If one provider has an outage or capacity issue, change one string and keep running.
  • Cost tracking — Monitor and compare spend across providers. (coming soon)
  • Automatic scheduling to lowest cost provider — Let the library route your workloads to the cheapest available backend. (coming soon)

Install

pip install bespokelabs-sandbox

With a specific backend:

pip install bespokelabs-sandbox[docker]
pip install bespokelabs-sandbox[daytona]
pip install bespokelabs-sandbox[tensorlake]
pip install bespokelabs-sandbox[modal]
pip install bespokelabs-sandbox[e2b]
pip install bespokelabs-sandbox[ray]
pip install bespokelabs-sandbox[all]

The Safehouse backend has no Python extra. Install the CLI separately on macOS:

brew install eugene1g/safehouse/agent-safehouse

Supported Backends

Local

No API keys, no cloud accounts. Just works.

Backend Extra Requires
Local subprocess (none) Python installed
Agent Safehouse (none) macOS + safehouse CLI
Docker [docker] Docker daemon running
Ray [ray] Ray installed (local or remote cluster)

Cloud

Backend Extra Auth
Daytona [daytona] DAYTONA_API_KEY
Tensorlake [tensorlake] tl login
Modal [modal] MODAL_TOKEN_ID + MODAL_TOKEN_SECRET
E2B [e2b] E2B_API_KEY

You only need to install the backend you use. The others are lazily imported.

Quickstart

from bespokelabs.sandbox import Sandbox

# Zero setup — runs locally
with Sandbox("local") as sb:
    result = sb.execute_code('print("hello")')
    print(result.stdout)

# Or use Safehouse on macOS
with Sandbox("safehouse") as sb:
    result = sb.execute_code('print("hello from safehouse")')
    print(result.stdout)

# Or use Docker
with Sandbox("docker") as sb:
    result = sb.execute_code('print("hello from a container")')
    print(result.stdout)

# Or any cloud provider — same interface
with Sandbox("e2b") as sb:
    result = sb.execute_code('print("hello from the cloud")')
    print(result.stdout)

Switch backends by changing one string:

for backend in ["local", "safehouse", "docker", "modal", "e2b", "daytona", "tensorlake", "ray"]:
    with Sandbox(backend) as sb:
        sb.execute_code('print("same code, any backend")')

API Reference

Creating a Sandbox

from bespokelabs.sandbox import Sandbox

sb = Sandbox(
    backend,              # "local" | "safehouse" | "docker" | "ray" | "daytona" | "tensorlake" | "modal" | "e2b"
    *,
    preset=None,          # Preset name or SandboxPreset object
    cpu=1.0,              # vCPUs (Tensorlake, Modal, Docker)
    memory_mb=1024,       # RAM in MB (Tensorlake, Modal, Docker)
    disk_mb=None,         # Disk in MB (Daytona)
    timeout_secs=600,     # Max lifetime / subprocess timeout
    image=None,           # Container image (Docker, Modal, Daytona)
    template=None,        # Template ID (E2B)
    env_vars=None,        # dict of environment variables
    allow_internet=True,  # Network access (Docker, Tensorlake, Daytona)
    app_name=None,        # App name (Modal)
    snapshot_id=None,     # Restore from snapshot (Tensorlake, Modal)
    workdir=None,         # Host directory to use as sandbox root (Safehouse)
)

Not every backend uses every parameter. Unsupported params are silently ignored.

Executing Code

result = sb.execute_code('print(1 + 1)', language="python")

print(result.stdout)     # "2"
print(result.stderr)     # ""
print(result.exit_code)  # 0

language defaults to "python". Daytona also supports "typescript", "javascript", "ruby", and "go". Safehouse, Docker, Tensorlake, Modal, Local, and Ray accept any installed binary name.

Running Shell Commands

result = sb.execute_command("ls -la /tmp")
result = sb.execute_command("grep", args=["-r", "TODO", "/app"])

File Operations

# List files
files = sb.list_files("/home")
for f in files:
    print(f.path, f.is_dir, f.size)

# Read / write in-memory content
sb.write_file("/tmp/config.json", '{"key": "value"}')
data = sb.read_file("/tmp/config.json")  # returns bytes

# Upload a local file into the sandbox
sb.upload_file("./local_data.csv", "/home/user/data.csv")

# Download a file from the sandbox to local disk
sb.download_file("/home/user/results.json", "./results.json")

Presets

Presets are predefined sandbox configurations with setup commands that run after creation. Presets that install tools with npm, such as codex, claude-code, and web-dev, assume the sandbox image already includes Node.js and npm.

# Sandbox with Codex CLI installed
with Sandbox("docker", preset="codex") as sb:
    sb.execute_command("codex --version")

# Sandbox with Claude Code installed
with Sandbox("docker", preset="claude-code") as sb:
    sb.execute_command("claude --version")

# Python data science stack
with Sandbox("e2b", preset="python-data-science") as sb:
    sb.execute_code("import pandas as pd; print(pd.__version__)")

Built-in presets:

Preset What it installs Defaults
claude-code @anthropic-ai/claude-code via npm 2GB RAM, 30min timeout
codex @openai/codex via npm 2GB RAM, 30min timeout
python-data-science numpy, pandas, matplotlib, scikit-learn 2GB RAM
python-ml torch, transformers, datasets, accelerate 2 vCPU, 4GB RAM, 30min timeout
node Verifies node/npm are present defaults
web-dev typescript, ts-node, prettier, eslint 2GB RAM
empty Nothing defaults

Create your own:

from bespokelabs.sandbox import Sandbox, SandboxPreset

Sandbox.register_preset(SandboxPreset(
    name="my-stack",
    description="My custom environment",
    setup_commands=["pip install my-library", "npm install -g my-tool"],
    cpu=2.0,
    memory_mb=4096,
))

with Sandbox("docker", preset="my-stack") as sb:
    ...

Explicit kwargs always override preset defaults.

Snapshots

snap = sb.snapshot()
print(snap.snapshot_id)

# Restore later
sb2 = Sandbox("tensorlake", snapshot_id=snap.snapshot_id)
Backend Snapshot support
Docker Yes (container.commit())
Tensorlake Yes (filesystem + memory)
Modal Yes (filesystem)
Daytona, E2B, Local, Ray, Safehouse No

Lifecycle

# Context manager (recommended) — auto-destroys on exit
with Sandbox("local") as sb:
    sb.execute_code("print('hi')")

# Manual cleanup
sb = Sandbox("docker")
sb.execute_code("print('hi')")
sb.destroy()

# Check state
sb.is_alive       # True/False
sb.backend_name   # "docker"

Feature Support Matrix

Feature Local Safehouse Docker Ray Daytona Tensorlake Modal E2B
execute_code Any binary Any binary Any binary Any binary Python, TS, JS, Ruby, Go Any binary Any binary Python
execute_command Shell Shell Shell Shell Shell Shell Shell Shell
list_files Native Native find / ls Native Native SDK via ls Native SDK Native SDK
read_file Native Native get_archive Native Native SDK via cat Native SDK Native SDK
write_file Native Native put_archive Native Native SDK via base64 Native SDK Native SDK
upload_file shutil.copy shutil.copy put_archive ray.put Native SDK via base64 Native SDK Native SDK
download_file shutil.copy shutil.copy get_archive ray.get Native SDK via base64 Native SDK Native SDK
snapshot No No Yes No No Yes Yes No
Resource limits No No cpu, memory cpu (Ray) Defaults cpu, memory cpu, memory, gpu Tier-based
Network control No No Yes No Firewall, VPN Yes Tunnels No
Isolation Process-level macOS sandbox-exec Container Process Full VM Container Container Full VM
GPU No No No Via Ray No No Yes No
Needs install Nothing safehouse CLI Docker daemon ray API key tl login API key API key

Exceptions

from bespokelabs.sandbox import (
    SandboxError,              # Base class for all errors
    SandboxCreationError,      # Sandbox failed to start
    SandboxExecutionError,     # Code or command execution failed
    BackendNotInstalledError,  # pip package missing for chosen backend
    FeatureNotSupportedError,  # Backend doesn't support this operation
)

All exceptions inherit from SandboxError, so you can catch broadly or narrowly:

try:
    sb.snapshot()
except FeatureNotSupportedError:
    print("This backend doesn't support snapshots")
except SandboxError as e:
    print(f"Something else went wrong: {e}")

Environment Variables

# Docker — no auth needed, just a running Docker daemon

# Local — no auth needed

# Ray — optional remote cluster
export RAY_ADDRESS=ray://head-node:10001  # omit for local cluster

# Daytona
export DAYTONA_API_KEY=your_key
export DAYTONA_API_URL=https://app.daytona.io/api   # optional
export DAYTONA_TARGET=us                              # optional

# Tensorlake (authenticate via CLI)
tl login

# Modal
export MODAL_TOKEN_ID=your_id
export MODAL_TOKEN_SECRET=your_secret

# E2B
export E2B_API_KEY=your_key

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

bespokelabs_sandbox-0.1.1.tar.gz (31.8 kB view details)

Uploaded Source

Built Distribution

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

bespokelabs_sandbox-0.1.1-py3-none-any.whl (35.0 kB view details)

Uploaded Python 3

File details

Details for the file bespokelabs_sandbox-0.1.1.tar.gz.

File metadata

  • Download URL: bespokelabs_sandbox-0.1.1.tar.gz
  • Upload date:
  • Size: 31.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.7

File hashes

Hashes for bespokelabs_sandbox-0.1.1.tar.gz
Algorithm Hash digest
SHA256 dee90da99ba1a24a8a93533b5602a123087952ed7c0ec93725a53a24fa850970
MD5 97b15a086f4045385c203c034f93cdc6
BLAKE2b-256 faa1609206a2ab381f6a11e06e9c6bfe2de09eb45f4804b0f9dc6deb540d43f3

See more details on using hashes here.

File details

Details for the file bespokelabs_sandbox-0.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for bespokelabs_sandbox-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 fc4dcfa3f0a62c6dca5b99602edfe6ad5d98a411620a9306ab4cd4d70a1e0a09
MD5 a0c8b0162952d6d08456b0334825fa88
BLAKE2b-256 c87a62ccf5f285bfd939e17f76bc31ec84a69bdfbb8f768fbeb6cc7b3b5e35f5

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