@syntera-ai/sandbox
v1.0.1
Published
Syntera Sandbox SDK — Provider-based sandbox management for coding agents. Supports Docker and Kubernetes backends with SSH access, snapshots, health monitoring, metrics, interactive terminals, and file watching.
Maintainers
Readme
@syntera-ai/sandbox
Provider-based sandbox SDK for coding agents. Drop-in replacement for Daytona with Docker and Kubernetes backends. Deploy on AWS, GCP, or your own datacenter.
Features
- 🐳 Docker Provider — Single-node, development, CI/CD
- ☸️ Kubernetes Provider — Production, multi-tenant, auto-scaling
- 🔑 SSH Access — Full SSH into sandboxes with auto-key management, tunneling, interactive shell
- 📸 Snapshots — Save and restore workspace state for checkpointing and rollback
- 💊 Health Monitoring — Exec/TCP/HTTP health checks with continuous monitoring
- 📊 Resource Metrics — CPU, memory, disk, network usage collection
- 💻 Interactive Terminals — PTY sessions with resize, multiple concurrent shells
- 👁️ File Watching — Real-time filesystem change events via inotify
- 🏊 Sandbox Pool — Pre-warmed sandboxes for sub-second creation
- 📁 File System — Read, write, list, stat, copy, move files inside sandboxes
- 🔧 Command Execution — exec with timeout, streaming, env vars, working directory
- 🌐 Port Management — Discover listening ports, generate preview URLs
- 🔒 Security — Drop capabilities, seccomp, resource limits, non-root user
- 🏷️ Labels & Discovery — Tag sandboxes, find by labels
- 📡 Events — Subscribe to lifecycle events (created, stopped, health changes, etc.)
- 🚀 Deploy Anywhere — Terraform for AWS EKS, GCP GKE, Docker Compose, bare K8s
Quick Start
npm install @syntera-ai/sandboxDocker (Development)
import { createSandbox } from '@syntera-ai/sandbox';
const client = await createSandbox({
provider: 'docker',
defaults: {
image: 'syntera/sandbox-base:latest',
cpu: 2,
memory: 4096,
},
});
// Create a sandbox
const sandbox = await client.create({
ports: [3000],
env: { NODE_ENV: 'development' },
});
// Execute commands
const result = await sandbox.exec('echo "Hello!"');
console.log(result.stdout); // "Hello!"
// File operations
await sandbox.fs.write('/home/sandbox/app.js', 'console.log("hi")');
const content = await sandbox.fs.read('/home/sandbox/app.js');
// Cleanup
await sandbox.destroy();
await client.shutdown();Kubernetes (Production)
const client = await createSandbox({
provider: 'kubernetes',
kubernetes: {
namespace: 'syntera-sandboxes',
serviceAccount: 'syntera-sandbox-manager',
nodeSelector: { dedicated: 'syntera-sandbox' },
},
defaults: {
image: 'your-registry/syntera/sandbox-swe-agent:latest',
cpu: 2,
memory: 4096,
},
pool: {
enabled: true,
min: 5,
max: 30,
idleTimeout: 300,
},
});API Reference
createSandbox(config): Promise<SynteraSandbox>
Creates and initializes a sandbox client.
interface SandboxConfig {
provider: 'docker' | 'kubernetes';
docker?: DockerProviderConfig;
kubernetes?: KubernetesProviderConfig;
defaults?: SandboxDefaults;
pool?: PoolConfig;
logger?: LoggerConfig;
}SynteraSandbox
| Method | Description |
|--------|-------------|
| create(params?) | Create a new sandbox (or acquire from pool) |
| get(id) | Get sandbox by ID |
| list(filter?) | List sandboxes (optionally filtered) |
| findOne(filter) | Find first sandbox matching filter |
| stop(id) | Stop sandbox (or release to pool) |
| destroy(id) | Permanently destroy sandbox |
| on(event, handler) | Subscribe to events |
| shutdown() | Clean up all resources |
ISandboxInstance
| Method | Description |
|--------|-------------|
| exec(cmd, opts?) | Execute a command, return { exitCode, stdout, stderr } |
| execStream(cmd, opts?) | Streaming execution with stdin/stdout callbacks |
| start() | Start a stopped sandbox |
| stop() | Stop the sandbox |
| destroy() | Permanently destroy |
| getState() | Get current state |
| waitUntilRunning(timeout?) | Block until sandbox is running |
| setLabels(labels) | Set metadata labels |
| getLabels() | Get metadata labels |
| fs.read(path) | Read file content |
| fs.write(path, content) | Write file |
| fs.exists(path) | Check if path exists |
| fs.list(path) | List directory contents |
| fs.delete(path, recursive?) | Delete file/directory |
| fs.mkdir(path, recursive?) | Create directory |
| fs.stat(path) | Get file stats |
| fs.copy(src, dest) | Copy file/directory |
| fs.move(src, dest) | Move file/directory |
| getPortUrl(port) | Get URL for an exposed port |
| listPorts() | List all listening ports |
Security Profiles
import {
DEFAULT_SECURITY_PROFILE, // Balanced — drops dangerous caps
STRICT_SECURITY_PROFILE, // Maximum isolation
PERMISSIVE_SECURITY_PROFILE, // Dev/testing only
} from '@syntera-ai/sandbox';
const sandbox = await client.create({
securityProfile: STRICT_SECURITY_PROFILE,
});Resource Limits
import {
DEFAULT_RESOURCE_LIMITS, // 2 CPU, 4GB RAM, 10GB disk
SMALL_RESOURCE_LIMITS, // 1 CPU, 2GB RAM, 5GB disk
LARGE_RESOURCE_LIMITS, // 4 CPU, 8GB RAM, 20GB disk
} from '@syntera-ai/sandbox';Deployment
Option 1: Docker Compose (Dev / Single Node)
# Build images
cd images/base && docker build -t syntera/sandbox-base:latest .
cd ../swe-agent && docker build -t syntera/sandbox-swe-agent:latest .
# Start infrastructure
cd deploy/docker-compose && docker compose up -dSDK config:
{ provider: 'docker', docker: { socketPath: '/var/run/docker.sock', network: 'syntera-sandbox-net' } }Option 2: Kubernetes (On-Prem Datacenter)
# Apply manifests to your cluster
kubectl apply -f deploy/kubernetes/namespace.yaml
kubectl apply -f deploy/kubernetes/rbac.yaml
kubectl apply -f deploy/kubernetes/configmap.yaml
kubectl apply -f deploy/kubernetes/resource-limits.yamlSDK config:
{ provider: 'kubernetes', kubernetes: { namespace: 'syntera-sandboxes' } }Option 3: AWS (EKS)
cd deploy/aws/terraform
terraform init && terraform apply -var="environment=prod"
# Follow output instructionsSee deploy/aws/README.md for details.
Option 4: GCP (GKE)
cd deploy/gcp/terraform
terraform init && terraform apply -var="gcp_project=YOUR_PROJECT"
# Follow output instructionsSee deploy/gcp/README.md for details.
On-Premises / Datacenter Deployment Guide
For running on your own cluster:
Prerequisites
- Kubernetes 1.25+ cluster (K3s, RKE2, kubeadm, etc.)
- Container registry (Harbor, Docker Registry, etc.)
kubectlaccess with cluster-admin
Steps
# 1. Set up a local registry (if you don't have one)
docker run -d -p 5000:5000 --restart=always --name registry registry:2
# 2. Build and push images to your registry
docker build -t your-registry:5000/syntera/sandbox-base:latest images/base/
docker push your-registry:5000/syntera/sandbox-base:latest
docker build -t your-registry:5000/syntera/sandbox-swe-agent:latest images/swe-agent/
docker push your-registry:5000/syntera/sandbox-swe-agent:latest
# 3. Apply K8s manifests
kubectl apply -f deploy/kubernetes/
# 4. (Optional) Dedicate nodes for sandboxes
kubectl label nodes <node-name> dedicated=syntera-sandbox
kubectl taint nodes <node-name> dedicated=syntera-sandbox:NoScheduleSDK config for on-prem:
const client = await createSandbox({
provider: 'kubernetes',
kubernetes: {
namespace: 'syntera-sandboxes',
serviceAccount: 'syntera-sandbox-manager',
nodeSelector: { dedicated: 'syntera-sandbox' },
tolerations: [{
key: 'dedicated',
operator: 'Equal',
value: 'syntera-sandbox',
effect: 'NoSchedule',
}],
imagePullSecrets: ['registry-creds'], // If using private registry
},
defaults: {
image: 'your-registry:5000/syntera/sandbox-swe-agent:latest',
cpu: 2,
memory: 4096,
},
pool: {
enabled: true,
min: 3,
max: 20,
idleTimeout: 300,
},
});Hardware Recommendations
| Scale | Sandbox Nodes | CPU | RAM | Disk | Concurrent Sandboxes | |-------|--------------|-----|-----|------|---------------------| | Small | 2 | 8 cores | 32 GB | 200 GB SSD | ~8 | | Medium | 5 | 16 cores | 64 GB | 500 GB SSD | ~30 | | Large | 10+ | 32 cores | 128 GB | 1 TB NVMe | ~100+ |
Migration from Daytona
See examples/migration-from-daytona.ts for a complete migration guide.
Quick Mapping
| Daytona API | @syntera-ai/sandbox Equivalent |
|-------------|---------------------------|
| new Daytona({ apiKey }) | createSandbox({ provider: 'docker' }) |
| client.create(params) | client.create(params) |
| client.get(id) | client.get(id) |
| client.stop(sandbox) | client.stop(id) |
| client.delete(sandbox) | client.destroy(id) |
| client.findOne({ labels }) | client.findOne({ labels }) |
| sandbox.process.executeCommand(cmd, dir) | sandbox.exec(cmd, { workdir: dir }) |
| sandbox.state === SandboxState.STARTED | sandbox.state === SandboxState.RUNNING |
| sandbox.start() | sandbox.start() |
| sandbox.setLabels(labels) | sandbox.setLabels(labels) |
| sandbox.waitUntilStarted(timeout) | sandbox.waitUntilRunning(timeout) |
Architecture
┌─────────────────────────────────────────────────────────────┐
│ @syntera-ai/sandbox SDK │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ SynteraSandbox (Client) │ │
│ │ create() | get() | list() | stop() | destroy() │ │
│ └────────────────────────┬─────────────────────────────┘ │
│ │ │
│ ┌────────────────────────┼─────────────────────────────┐ │
│ │ SandboxPool (optional) │ │
│ │ acquire() | release() | maintain() │ │
│ └────────────────────────┬─────────────────────────────┘ │
│ │ │
│ ┌────────────┬───────────┴──────────┬──────────────────┐ │
│ │ ISandbox │ ISandboxProvider │ │ │
│ │ Instance │ │ │ │
│ │ │ ┌─────────────────┐ │ │ │
│ │ exec() │ │ DockerProvider │ │ SandboxFileSystem│ │
│ │ fs.* │ │ K8sProvider │ │ PortManager │ │
│ │ ports │ │ (your custom) │ │ Security │ │
│ └────────────┘ └─────────────────┘ └──────────────────┘ │
└─────────────────────────────────────────────────────────────┘License
MIT
