StateSet Sandbox Architecture
Scope of this document: a component-level overview suitable for new contributors and integrators. For the deep operational architecture β warm-pool internals, GKE-specific infrastructure, data layer, and event flows β seedocs/ARCHITECTURE.md, which is the canonical reference. For the Rust-controller-specific architecture seestateset-sandbox-controller/README.md.
Overview
StateSet Sandbox provides an isolated execution environment for running Claude Code CLI and AI agents. The system uses a controller-based architecture where a central API manages ephemeral sandbox pods on Kubernetes.System Components
1. Sandbox Controller
The controller is a Node.js/Express API that manages the lifecycle of sandbox pods.- Image:
YOUR_REGISTRY/stateset/sandbox-controller:latest - Replicas: 3 (high availability)
- Namespace:
stateset-sandbox - Exposed at:
sandbox.stateset.com
- Authenticate API requests (JWT or API key)
- Create/delete sandbox pods via Kubernetes API
- Execute commands in running sandboxes
- Stream output via SSE or WebSocket
- Manage file uploads/downloads
- Enforce resource limits and quotas
2. Sandbox Image
The sandbox is a multi-language runtime environment with Claude Code pre-installed.- Image:
YOUR_REGISTRY/stateset/sandbox:latest - Not deployed directly - pulled on-demand by the controller
| Category | Tools |
|---|---|
| AI/ML | Claude Code CLI, Anthropic SDK |
| Node.js | Node 22, TypeScript, tsx, ts-node |
| Python | Python 3.12, pip, anthropic, pydantic |
| Go | Go 1.22 |
| Rust | Latest stable |
| MCP Servers | filesystem, github, puppeteer |
| Dev Tools | Git, GitHub CLI, ESLint, Prettier, Playwright |
CI/CD Pipeline Flow
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β GitLab Push β
β (master branch) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CI/CD Pipeline β
β β
β Step 1: Build Controller β
β ββββββββββββββββββββββββ β
β docker build --target controller \ β
β -t YOUR_REGISTRY/sandbox-controller:${CI_COMMIT_SHA} β
β β
β Step 2: Build Sandbox β
β βββββββββββββββββββββ β
β docker build --target sandbox \ β
β -t YOUR_REGISTRY/sandbox:${CI_COMMIT_SHA} β
β β
β Step 3: Push Both Images to Artifact Registry β
β β
β Step 4: Deploy Controller Only β
β βββββββββββββββββββββββββββββ β
β kubectl apply -k k8s/ (deploys sandbox-controller to stateset-sandbox) β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β GKE Cluster (stateset-sandbox namespace) β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β sandbox-controller (3 replicas) β β
β β Exposed at: sandbox.stateset.com β β
β β β β
β β ConfigMap: β β
β β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β β
β β β SANDBOX_IMAGE: YOUR_REGISTRY/sandbox:latestβ β β
β β β DEFAULT_CPUS: "2" β β β
β β β DEFAULT_MEMORY: "2Gi" β β β
β β β NAMESPACE: "stateset-sandbox" β β β
β β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β Sandbox pods: (none yet - created on demand) β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Runtime Flow
Creating and Using a Sandbox
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Your Application β
β β
β const client = new StateSetSandbox({ β
β baseUrl: 'https://sandbox.stateset.com', β
β authToken: 'sk-...' β
β }); β
β await client.create(); β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
β POST /api/sandbox/create
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β sandbox-controller β
β β
β 1. Receives request β
β 2. Reads SANDBOX_IMAGE from env (ConfigMap) β
β 3. Calls K8s API to create pod: β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β apiVersion: v1 β β
β β kind: Pod β β
β β metadata: β β
β β name: sandbox-org_xxx-abc123 β β
β β namespace: stateset-sandbox β β
β β spec: β β
β β containers: β β
β β - image: YOUR_REGISTRY/sandbox:latest β β
β β env: β β
β β - name: ANTHROPIC_API_KEY β β
β β valueFrom: secretKeyRef... β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β 4. Waits for pod to be ready β
β 5. Returns sandbox_id to caller β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Sandbox Pod (ephemeral) β
β ββββββββββββββββββββββββ β
β Image: YOUR_REGISTRY/sandbox:latest β
β β
β Pre-installed: β
β β’ Claude Code CLI (@anthropic-ai/claude-code) β
β β’ Node.js 22, Python 3.12, Go 1.22, Rust β
β β’ MCP servers (filesystem, github, puppeteer) β
β β’ Git, GitHub CLI β
β β
β Ready to execute commands via kubectl exec β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Command Execution Flow
ββββββββββββββββ POST /api/sandbox/{id}/execute ββββββββββββββββββββββ
β β βββββββββββββββββββββββββββββββββββββββΊ β β
β Your App β { "command": "claude -p '...'" } β sandbox-controllerβ
β β βββββββββββββββββββββββββββββββββββββββ β β
ββββββββββββββββ SSE stream: stdout/stderr/exit ββββββββββββββββββββββ
β
β kubectl exec
βΌ
ββββββββββββββββββββββ
β Sandbox Pod β
β β
β $ claude -p '...' β
β (calls Anthropic) β
β β
ββββββββββββββββββββββ
API Endpoints
Sandbox Management
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/sandbox/create | Create a new sandbox |
| GET | /api/sandbox/:id | Get sandbox details |
| GET | /api/sandbox/:id/status | Get sandbox status (lightweight) |
| GET | /api/sandboxes | List all sandboxes |
| POST | /api/sandbox/:id/stop | Stop and delete sandbox |
| DELETE | /api/sandbox/:id | Delete sandbox |
File Operations
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/sandbox/:id/files | Write files to sandbox |
| GET | /api/sandbox/:id/files?path=... | Read file (base64) |
| GET | /api/sandbox/:id/files/download?path=... | Download file (binary) |
Command Execution
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/sandbox/:id/execute | Execute command (streaming optional) |
| WS | /ws | WebSocket for real-time streaming |
Kubernetes Resources
Namespace: stateset-sandbox
| Resource | Name | Purpose |
|---|---|---|
| Deployment | sandbox-controller | Controller API (3 replicas) |
| Service | sandbox-controller | ClusterIP for internal access |
| Ingress | sandbox-controller | External access via nginx |
| ConfigMap | sandbox-controller-config | Controller configuration |
| Secret | sandbox-controller-auth | JWT secret, API keys |
| Secret | anthropic-credentials | Anthropic API key for sandboxes |
| ServiceAccount | sandbox-controller | K8s API access for pod management |
| Role/RoleBinding | sandbox-controller | RBAC for pod CRUD operations |
| NetworkPolicy | sandbox-controller | Network isolation rules |
| ResourceQuota | sandbox-quota | Limit total sandbox resources |
Docker Build Targets
The unifiedDockerfile supports multiple build targets:
# Build controller image
docker build --target controller \
-t YOUR_REGISTRY/stateset/sandbox-controller:latest .
# Build sandbox image
docker build --target sandbox \
-t YOUR_REGISTRY/stateset/sandbox:latest .
| Target | Base Image | Size | Purpose |
|---|---|---|---|
controller | node:22-slim | ~200MB | API server |
sandbox | node:22-slim | ~2.5GB | Full dev environment |
SDK Usage
TypeScript SDK
import { StateSetSandbox, AgentRunner } from '@stateset/sandbox-sdk';
// Initialize client
const client = new StateSetSandbox({
baseUrl: 'https://sandbox.stateset.com',
authToken: process.env.STATESET_API_KEY!,
orgId: 'org_xxx'
});
// Low-level: Create sandbox and execute commands
const sandbox = await client.create();
const result = await client.execute(sandbox.sandbox_id, {
command: 'claude -p "Write a hello world in Python"'
});
console.log(result.stdout);
await client.stop(sandbox.sandbox_id);
// High-level: Use AgentRunner
const runner = new AgentRunner({
sandbox: client,
anthropicApiKey: process.env.ANTHROPIC_API_KEY!
});
const { events } = await runner.runAndCleanup({
prompt: 'Create a REST API using FastAPI',
systemPrompt: 'You are a senior backend developer',
maxTurns: 10
});
Security
Sandbox Isolation
- Ephemeral pods: Each sandbox is a separate pod, destroyed after use
- Non-root user: Sandboxes run as UID 1000 (
sandboxuser) - Resource limits: CPU/memory limits enforced per sandbox
- Network policies: Sandboxes have limited network access
- Read-only filesystem: Optional, configurable per deployment
- gVisor runtime: Optional sandboxing via
RUNTIME_CLASSconfig
Authentication
- API Keys:
Authorization: ApiKey sk-... - JWT Tokens:
Authorization: Bearer eyJ... - Org isolation: Sandboxes are scoped to organizations
Configuration
Environment Variables (Controller)
| Variable | Default | Description |
|---|---|---|
SANDBOX_IMAGE | stateset/sandbox:latest | Image for sandbox pods |
DEFAULT_CPUS | 2 | Default CPU limit |
DEFAULT_MEMORY | 2Gi | Default memory limit |
DEFAULT_TIMEOUT | 600 | Sandbox timeout (seconds) |
MAX_SANDBOXES_PER_ORG | 5 | Max concurrent sandboxes per org |
NAMESPACE | stateset-sandbox | K8s namespace for pods |
LOG_LEVEL | info | Logging verbosity |
RUNTIME_CLASS | (unset) | Optional: gvisor for extra isolation |
Directory Structure
stateset-sandbox/
βββ Dockerfile # Multi-target: controller & sandbox
βββ ARCHITECTURE.md # This file
βββ controller/
β βββ src/
β β βββ index.ts # Express server entrypoint
β β βββ routes.ts # API route handlers
β β βββ sandbox-manager.ts # K8s pod management
β β βββ middleware/
β β βββ auth.ts # JWT/API key authentication
β βββ package.json
β βββ tsconfig.json
βββ sdk/
β βββ src/
β βββ client.ts # StateSetSandbox client
β βββ agent-runner.ts # High-level agent runner
β βββ types.ts # TypeScript types
βββ docker/
β βββ entrypoint.sh # Sandbox container entrypoint
β βββ health-check.sh # Sandbox health check
βββ k8s/
βββ deployment.yaml # Controller deployment
βββ service.yaml # Controller service
βββ ingress.yaml # External access
βββ configmap.yaml # Controller config
βββ secret.yaml # Auth credentials
βββ rbac.yaml # ServiceAccount & roles
βββ network-policy.yaml # Network isolation
βββ resource-quota.yaml # Resource limits
βββ kustomization.yaml # Kustomize config