Skip to content

chekusu/sandbank

Repository files navigation

Sandbank

Unified sandbox SDK for AI agents — write once, run on any cloud.

Website | 中文文档 | 日本語ドキュメント

Sandbank provides a single TypeScript interface for creating, managing, and orchestrating cloud sandboxes. Switch between providers without changing your application code.

Why Sandbank?

AI agents need isolated execution environments. But every cloud provider has a different API — Daytona, Fly.io, Cloudflare Workers all speak different languages. Sandbank unifies them behind one interface:

import { createProvider } from '@sandbank.dev/core'
import { DaytonaAdapter } from '@sandbank.dev/daytona'

const provider = createProvider(new DaytonaAdapter({ apiKey: '...' }))
const sandbox = await provider.create({ image: 'node:22' })

const result = await sandbox.exec('echo "Hello from the sandbox"')
console.log(result.stdout) // Hello from the sandbox

await provider.destroy(sandbox.id)

Swap DaytonaAdapter for FlyioAdapter or CloudflareAdapter — zero code changes.

Architecture

┌──────────────────────────────────────────────────────┐
│  Your Application / AI Agent                         │
├──────────────────────────────────────────────────────┤
│  @sandbank.dev/core         Unified Provider Interface   │
│  @sandbank.dev/skills       Skill Registry & Injection   │
│  @sandbank.dev/agent        In-sandbox Agent Client      │
│  @sandbank.dev/relay        Multi-agent Communication    │
├──────────────────────────────────────────────────────┤
│  @sandbank.dev/daytona  @sandbank.dev/flyio  @sandbank.dev/cloudflare  │
│  @sandbank.dev/boxlite                                   │
│  Provider Adapters (Compute)                         │
├──────────────────────────────────────────────────────┤
│  @sandbank.dev/db9       Service Adapter (Data)      │
├──────────────────────────────────────────────────────┤
│  Daytona    Fly.io Machines    Cloudflare Workers     │
│  BoxLite (self-hosted Docker)    db9.ai (PostgreSQL)  │
└──────────────────────────────────────────────────────┘

Packages

Package Description
@sandbank.dev/core Provider abstraction, capability system, error types
@sandbank.dev/skills Skill registry and local filesystem loader
@sandbank.dev/daytona Daytona cloud sandbox adapter
@sandbank.dev/flyio Fly.io Machines adapter
@sandbank.dev/cloudflare Cloudflare Workers adapter
@sandbank.dev/boxlite BoxLite self-hosted Docker adapter
@sandbank.dev/db9 db9.ai serverless PostgreSQL adapter (ServiceProvider)
@sandbank.dev/relay WebSocket relay for multi-agent communication
@sandbank.dev/agent Lightweight client for agents running inside sandboxes

Provider Support

Core Operations

All providers implement these — the minimum contract:

Operation Daytona Fly.io Cloudflare BoxLite
Create / Destroy
List sandboxes
Execute commands
Read / Write files
Skill injection

Extended Capabilities

Capabilities are opt-in. Use withVolumes(provider), withPortExpose(sandbox), etc. to safely check and access them at runtime.

Capability Daytona Fly.io Cloudflare BoxLite db9 Description
volumes ⚠️* Persistent volume management
port.expose ⚠️** Expose sandbox ports to the internet
exec.stream Stream stdout/stderr in real-time
snapshot Snapshot and restore sandbox state
terminal Interactive web terminal (ttyd)
sleep Hibernate and wake sandboxes
skills Load and inject skill definitions into sandboxes
services Bind data services (PostgreSQL) to sandboxes

* Cloudflare volumes requires storage option in adapter config.

** Cloudflare reserves port 3000 for its sandbox control plane. Use any port in 1024–65535 except 3000.

Provider Characteristics

Daytona Fly.io Cloudflare BoxLite
Runtime Full VM Firecracker microVM V8 isolate + container Docker container
Cold start ~10s ~3-5s ~1s ~2-5s
File I/O Native SDK Via exec (base64) Native SDK Via exec (base64)
Regions Multi Multi Global edge Self-hosted
External deps @daytonaio/sdk None (pure fetch) @cloudflare/sandbox BoxLite API

Multi-Agent Sessions

Sandbank includes a built-in orchestration layer for multi-agent workflows. The Relay handles real-time messaging and shared context between sandboxes.

import { createSession } from '@sandbank.dev/core'

const session = await createSession({
  provider,
  relay: { type: 'memory' },
})

// Spawn agents in isolated sandboxes
const architect = await session.spawn('architect', {
  image: 'node:22',
  env: { ROLE: 'architect' },
})

const developer = await session.spawn('developer', {
  image: 'node:22',
  env: { ROLE: 'developer' },
})

// Shared context — all agents can read/write
await session.context.set('spec', { endpoints: ['/users', '/posts'] })

// Wait for all agents to complete
await session.waitForAll()
await session.close()

Inside the sandbox, agents use @sandbank.dev/agent:

import { connect } from '@sandbank.dev/agent'

const session = await connect() // reads SANDBANK_* env vars

session.on('message', async (msg) => {
  if (msg.type === 'task') {
    // do work...
    await session.send(msg.from, 'done', result)
  }
})

await session.complete({ status: 'success', summary: 'Built 5 API endpoints' })

Quick Start

# Install
pnpm add @sandbank.dev/core @sandbank.dev/daytona  # or @sandbank.dev/flyio, @sandbank.dev/cloudflare

# Set up provider
export DAYTONA_API_KEY=your-key
import { createProvider } from '@sandbank.dev/core'
import { DaytonaAdapter } from '@sandbank.dev/daytona'

const provider = createProvider(
  new DaytonaAdapter({ apiKey: process.env.DAYTONA_API_KEY! })
)

// Create a sandbox
const sandbox = await provider.create({
  image: 'node:22',
  resources: { cpu: 2, memory: 2048 },
  autoDestroyMinutes: 30,
})

// Run commands
const { stdout } = await sandbox.exec('node --version')

// File operations
await sandbox.writeFile('/app/index.js', 'console.log("hi")')
await sandbox.exec('node /app/index.js')

// Clean up
await provider.destroy(sandbox.id)

Development

git clone https://github.com/chekusu/sandbank.git
cd sandbank
pnpm install

# Run all unit tests
pnpm test

# Run cross-provider conformance tests
pnpm test:conformance

# Typecheck
pnpm typecheck

Running Integration Tests

Integration tests hit real APIs and are gated by environment variables:

# Daytona
DAYTONA_API_KEY=... pnpm test

# Fly.io
FLY_API_TOKEN=... FLY_APP_NAME=... pnpm test

# Cloudflare
E2E_WORKER_URL=... pnpm test

# db9
DB9_TOKEN=... pnpm --filter @sandbank.dev/db9 test:e2e

Test Coverage

Package Stmts Branch Funcs Lines Unit Integration
@sandbank.dev/core 84% 77% 74% 88% 98
@sandbank.dev/db9 100% 97% 93% 100% 35 3

Run coverage locally:

pnpm --filter @sandbank.dev/db9 test -- --coverage

Design Principles

  1. Minimal interface, maximum interop — only the true common denominator (exec + files + lifecycle)
  2. Explicit over implicit — no auto-fallback, no caching, no hidden retries
  3. Capability detection, not fake implementations — if a provider doesn't support it, it errors
  4. Idempotent operations — destroying an already-destroyed sandbox is a no-op
  5. Full decoupling — provider layer and session layer are independent, compose freely

License

MIT

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Packages

 
 
 

Languages