> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stateset.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Sandbox Production Guide

> Running the StateSet Sandbox in production.

# StateSet Sandbox - Production Guide

## Overview

StateSet Sandbox is a secure, isolated execution environment for running code on behalf of AI agents. It provides containerized sandboxes with full lifecycle management, checkpointing, artifact storage, and usage tracking.

***

## Architecture

```
┌─────────────────────────────────────────────────────────────────────────┐
│                              DEVELOPER                                   │
└─────────────────────────────────────────────────────────────────────────┘
         │                           │                          │
         │ 1. Register               │ 2. Use SDK              │ 3. Dashboard
         ▼                           ▼                          ▼
┌─────────────────────────────────────────────────────────────────────────┐
│                         SANDBOX CONTROLLER                               │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐  ┌─────────────┐  │
│  │ Registration │  │   Sandbox    │  │  Checkpoint  │  │   Webhook   │  │
│  │   Service    │  │   Manager    │  │   Manager    │  │   Manager   │  │
│  └──────┬───────┘  └──────┬───────┘  └──────┬───────┘  └──────┬──────┘  │
└─────────┼─────────────────┼─────────────────┼─────────────────┼─────────┘
          │                 │                 │                 │
          ▼                 ▼                 ▼                 ▼
┌─────────────────┐  ┌─────────────┐  ┌─────────────┐  ┌─────────────────┐
│   PostgreSQL    │  │ Kubernetes  │  │ Cloud       │  │ External        │
│ (Managed DB)   │  │ (Pods)      │  │ Storage     │  │ Webhooks        │
│                 │  │             │  │ (GCS/S3)    │  │                 │
│ • organizations │  │ • sandbox   │  │ • artifacts │  │ • your-app.com  │
│ • api_keys      │  │   pods      │  │ • checkpts  │  │                 │
│ • usage_events  │  │             │  │             │  │                 │
│ • checkpoints   │  │             │  │             │  │                 │
└─────────────────┘  └─────────────┘  └─────────────┘  └─────────────────┘
```

### Component Details

| Component  | Technology           | Purpose                     |
| ---------- | -------------------- | --------------------------- |
| Controller | Rust + Axum          | API server, orchestration   |
| Database   | PostgreSQL (managed) | Persistent storage          |
| Sandboxes  | Kubernetes Pods      | Isolated execution          |
| Storage    | GCS/S3               | Artifact & checkpoint files |
| Dashboard  | Next.js 14           | Web UI for management       |
| SDK        | TypeScript           | Client library              |

### Exec Agent Runtime

The sandbox base image includes both Node.js and Go exec agents. Set `EXEC_AGENT_RUNTIME=go` for a lower memory footprint (\~8 MB vs \~45 MB RSS). The two runtimes are wire-compatible, so no controller changes are needed.

***

## Database Tables

| Table                       | Purpose                        |
| --------------------------- | ------------------------------ |
| `organizations`             | Customer accounts              |
| `users`                     | Organization members           |
| `api_keys`                  | Authentication tokens (hashed) |
| `checkpoints`               | Sandbox state snapshots        |
| `artifacts`                 | Uploaded/downloaded files      |
| `usage_events`              | Per-operation usage records    |
| `usage_aggregates`          | Rolled-up usage stats          |
| `vcpu_allocations`          | CPU allocation tracking        |
| `billing_records`           | Invoice line items             |
| `plans`                     | Subscription tiers             |
| `stripe_subscription_items` | Stripe integration             |
| `organization_secrets`      | Encrypted secrets              |
| `secret_access_log`         | Secret access audit            |
| `schema_migrations`         | Migration tracking             |

***

## User Flow

### 1. Registration

Developer signs up via API or dashboard:

```bash theme={null}
curl -X POST https://api.sandbox.stateset.com/api/v1/register \
  -H "Content-Type: application/json" \
  -d '{
    "first_name": "Ada",
    "last_name": "Lovelace",
    "email": "dev@company.com",
    "organization_name": "Acme Corp",
    "password": "YourSecurePassword123!",
    "use_case": "CI automation"
  }'
```

**Response:**

```json theme={null}
{
  "organization": {
    "id": "org_abc123",
    "name": "Acme Corp",
    "slug": "acme-corp",
    "plan": "hobby"
  },
  "user": {
    "id": "user_abc123",
    "email": "dev@company.com",
    "role": "owner"
  },
  "api_key": {
    "key": "sk-sandbox-xxxxxxxxxxxxxxxxxxxx",
    "key_prefix": "sk-sandbox-xxxx",
    "name": "Default API Key"
  }
}
```

### 2. SDK Installation

Node.js:

```bash theme={null}
npm install @stateset/sandbox-sdk
```

Python:

```bash theme={null}
pip install stateset-sandbox
```

Additional preview SDKs are available in this repo:

* Rust: `sdk-rust/README.md`
* Ruby: `sdk-ruby/README.md`
* Go: `sdk-go/README.md`
* PHP: `sdk-php/README.md`
* Java: `sdk-java/README.md`
* Kotlin: `sdk-kotlin/README.md`
* Swift: `sdk-swift/README.md`

### 3. Basic Usage

```typescript theme={null}
import { StateSetSandbox } from '@stateset/sandbox-sdk';

// Initialize client
const client = new StateSetSandbox({
  baseUrl: 'https://api.sandbox.stateset.com',
  authToken: 'sk-sandbox-xxxxxxxxxxxx'
});

// Create a sandbox
const sandbox = await client.create({
  timeout_seconds: 300, // 5 minutes
  cpus: '1',           // 1 vCPU
  memory: '1Gi',       // 1GB RAM
  env: {
    NODE_ENV: 'production'
  }
});

console.log(`Sandbox created: ${sandbox.sandbox_id}`);
console.log(`Expires at: ${sandbox.expires_at}`);

// Execute commands
const result = await client.execute(sandbox.sandbox_id, {
  command: ['node', '-e', 'console.log("Hello from sandbox!")']
});

console.log(result.stdout);  // "Hello from sandbox!"
console.log(result.exit_code); // 0

// Write files
await client.writeFiles(sandbox.sandbox_id, [
  { path: '/workspace/app.js', content: 'console.log("app");' }
]);

// Read files
const content = await client.readFile(sandbox.sandbox_id, '/workspace/app.js');

// Stop sandbox when done
await client.stop(sandbox.sandbox_id);
```

Advanced endpoints (checkpoints, artifacts, webhooks, templates) are available via `StateSetSandboxExtended`:

```typescript theme={null}
import { StateSetSandboxExtended } from '@stateset/sandbox-sdk';

const client = new StateSetSandboxExtended({
  baseUrl: 'https://api.sandbox.stateset.com',
  authToken: 'sk-sandbox-xxxxxxxxxxxx'
});
```

### 4. Advanced: Checkpoints

Save and restore sandbox state:

```typescript theme={null}
// Save current state
const checkpoint = await client.createCheckpoint(sandbox.sandbox_id, {
  name: 'after-npm-install',
  description: 'All dependencies installed'
});

console.log(`Checkpoint created: ${checkpoint.id}`);

// Later: Restore to that state (same sandbox)
await client.restoreCheckpoint(sandbox.sandbox_id, checkpoint.id, {
  restore_files: true,
  restore_env: true,
  overwrite: true
});

// Clone a checkpoint
const cloned = await client.cloneCheckpoint(checkpoint.id, 'feature-branch-base');

// Compare checkpoints
const diff = await client.compareCheckpoints(
  checkpoint1.id,
  checkpoint2.id
);
```

### 5. Advanced: Artifacts

Upload and download files:

```typescript theme={null}
// Upload file from sandbox to cloud storage
const artifact = await client.uploadArtifact(sandbox.sandbox_id, {
  path: '/workspace/output/report.pdf',
  remote_path: 'reports/2024/report.pdf', // stored under artifacts/<organization-id>/...
  content_type: 'application/pdf',
  expires_in: 86400 * 7  // 7 days
});

// Download artifact to sandbox
await client.downloadArtifact(
  sandbox.sandbox_id,
  artifact.id,
  '/workspace/downloads/report.pdf'
);

// Get presigned URL for direct download
const { url } = await client.getArtifactUrl(artifact.id, 3600);
console.log(`Artifact URL: ${url}`);
```

### 6. Advanced: Webhooks

Get notified of sandbox events:

```typescript theme={null}
// Register webhook
const webhook = await client.createWebhook({
  url: 'https://your-app.com/webhooks/sandbox',
  events: [
    'sandbox.created',
    'sandbox.ready',
    'sandbox.error',
    'sandbox.stopped',
    'command.completed',
    'checkpoint.created'
  ],
  secret: 'whsec_your_secret_key'
});

// Test webhook
const testResult = await client.testWebhook(webhook.id);
console.log(`Test successful: ${testResult.success}`);

// List webhook deliveries
const deliveries = await client.getWebhookDeliveries(webhook.id, 10);
```

**Webhook Payload:**

```json theme={null}
{
  "id": "evt_abc123",
  "event": "sandbox.ready",
  "timestamp": "2024-01-15T12:00:00Z",
  "sandboxId": "sbx_xyz789",
  "orgId": "org_abc123",
  "data": {
    "podIp": "10.0.1.5",
    "startupMs": 1234
  }
}
```

**Signature Verification:**

```typescript theme={null}
import { createHmac } from 'crypto';

function verifyWebhook(payload: string, signature: string, secret: string): boolean {
  const expected = createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
  return `sha256=${expected}` === signature;
}
```

### 7. Templates

Create sandboxes from pre-configured templates:

```typescript theme={null}
// List available templates
const templates = await client.listTemplates();

// Create from template
const sandbox = await client.createFromTemplate('python-data-science', {
  timeout_seconds: 600,
  env: {
    DATASET_URL: 'https://example.com/data.csv'
  }
});
```

**Available Templates:**

| Template              | Description    | Pre-installed             |
| --------------------- | -------------- | ------------------------- |
| `python-basic`        | Python 3.11    | pip, venv                 |
| `node-basic`          | Node.js 22     | npm, yarn                 |
| `go-basic`            | Go 1.22        | go mod                    |
| `rust-basic`          | Rust stable    | cargo                     |
| `computer-use`        | GUI automation | xdotool, Xvfb, Firefox    |
| `python-data-science` | Data analysis  | pandas, numpy, matplotlib |
| `node-express-api`    | Web APIs       | express, typescript       |
| `claude-agent`        | AI agents      | claude-code, MCP servers  |

### 8. REPL Sessions

Interactive Python REPL with persistent session state. Variables, imports, and objects survive across execute calls — ideal for data exploration, iterative development, and AI agent workflows.

**Enable:** Set `REPL_ENABLED=true` on the controller.

```typescript theme={null}
const session = await client.createReplSession(sandboxId, { language: 'python' });
const result = await client.executeRepl(sandboxId, session.id, { code: 'x = 42' });
// Variables persist across calls
const result2 = await client.executeRepl(sandboxId, session.id, { code: 'print(x * 2)' });
await client.destroyReplSession(sandboxId, session.id);
```

***

## API Reference

### Authentication

All API requests require authentication via header:

```
Authorization: ApiKey sk-sandbox-xxxxxxxxxxxx
```

The organization is inferred from the API key or JWT claims; no org header is required.

Or with JWT:

```
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
```

### Endpoints

#### Registration & API Keys

| Method | Endpoint               | Description                   |
| ------ | ---------------------- | ----------------------------- |
| POST   | `/api/v1/register`     | Create organization + API key |
| GET    | `/api/v1/api-keys`     | List API keys                 |
| POST   | `/api/v1/api-keys`     | Create new API key            |
| DELETE | `/api/v1/api-keys/:id` | Revoke API key                |

#### Sandboxes

| Method | Endpoint                             | Description                      |
| ------ | ------------------------------------ | -------------------------------- |
| POST   | `/api/v1/sandbox/create`             | Create sandbox                   |
| GET    | `/api/v1/sandbox/:id`                | Get sandbox details              |
| GET    | `/api/v1/sandbox/:id/status`         | Get sandbox status (lightweight) |
| POST   | `/api/v1/sandbox/:id/execute`        | Execute command                  |
| POST   | `/api/v1/sandbox/:id/files`          | Write files                      |
| GET    | `/api/v1/sandbox/:id/files?path=...` | Read file                        |
| POST   | `/api/v1/sandbox/:id/stop`           | Stop sandbox                     |
| DELETE | `/api/v1/sandbox/:id`                | Delete sandbox                   |
| GET    | `/api/v1/sandboxes`                  | List sandboxes                   |

#### Checkpoints

| Method | Endpoint                                  | Description         |
| ------ | ----------------------------------------- | ------------------- |
| GET    | `/api/v1/checkpoints`                     | List checkpoints    |
| GET    | `/api/v1/checkpoints/:id`                 | Get checkpoint      |
| POST   | `/api/v1/sandbox/:id/checkpoints`         | Create checkpoint   |
| POST   | `/api/v1/sandbox/:id/checkpoints/restore` | Restore checkpoint  |
| POST   | `/api/v1/checkpoints/:id/clone`           | Clone checkpoint    |
| POST   | `/api/v1/checkpoints/compare`             | Compare checkpoints |
| DELETE | `/api/v1/checkpoints/:id`                 | Delete checkpoint   |

#### Artifacts

| Method | Endpoint                                 | Description          |
| ------ | ---------------------------------------- | -------------------- |
| GET    | `/api/v1/artifacts`                      | List artifacts       |
| GET    | `/api/v1/artifacts/:id`                  | Get artifact details |
| GET    | `/api/v1/artifacts/:id/url`              | Get presigned URL    |
| POST   | `/api/v1/sandbox/:id/artifacts/upload`   | Upload artifact      |
| POST   | `/api/v1/sandbox/:id/artifacts/download` | Download artifact    |
| DELETE | `/api/v1/artifacts/:id`                  | Delete artifact      |

#### Webhooks

| Method | Endpoint                          | Description          |
| ------ | --------------------------------- | -------------------- |
| GET    | `/api/v1/webhooks`                | List webhooks        |
| POST   | `/api/v1/webhooks`                | Create webhook       |
| DELETE | `/api/v1/webhooks/:id`            | Delete webhook       |
| POST   | `/api/v1/webhooks/:id/test`       | Test webhook         |
| GET    | `/api/v1/webhooks/:id/deliveries` | Get delivery history |

#### Usage & Billing

| Method | Endpoint                        | Description             |
| ------ | ------------------------------- | ----------------------- |
| GET    | `/api/v1/usage/current`         | Get usage summary       |
| GET    | `/api/v1/usage/history`         | Get usage history       |
| GET    | `/api/v1/usage/sandboxes`       | Get usage by sandbox    |
| GET    | `/api/v1/pricing`               | Get pricing info        |
| POST   | `/api/v1/pricing/estimate`      | Estimate cost           |
| POST   | `/api/v1/pricing/bulk-estimate` | Bulk estimate costs     |
| GET    | `/api/v1/pricing/plans`         | List subscription plans |
| GET    | `/api/v1/billing/subscription`  | Get subscription status |
| POST   | `/api/v1/billing/upgrade`       | Upgrade plan            |
| GET    | `/api/v1/billing/invoices`      | List invoices           |
| POST   | `/api/v1/billing/portal`        | Open billing portal     |

#### Templates

| Method | Endpoint                               | Description          |
| ------ | -------------------------------------- | -------------------- |
| GET    | `/api/v1/templates`                    | List templates       |
| GET    | `/api/v1/templates/:id`                | Get template details |
| GET    | `/api/v1/templates/categories`         | List categories      |
| POST   | `/api/v1/sandbox/create-from-template` | Create from template |

#### REPL Sessions

| Method | Endpoint                                               | Description         |
| ------ | ------------------------------------------------------ | ------------------- |
| POST   | `/api/v1/sandbox/:id/repl/sessions`                    | Create REPL session |
| GET    | `/api/v1/sandbox/:id/repl/sessions`                    | List REPL sessions  |
| POST   | `/api/v1/sandbox/:id/repl/sessions/:sessionId/execute` | Execute code        |
| DELETE | `/api/v1/sandbox/:id/repl/sessions/:sessionId`         | Destroy session     |

***

## Webhook Events

| Event                 | Description                   |
| --------------------- | ----------------------------- |
| `sandbox.created`     | Sandbox pod created           |
| `sandbox.ready`       | Sandbox is ready for commands |
| `sandbox.stopped`     | Sandbox stopped               |
| `sandbox.error`       | Sandbox encountered error     |
| `sandbox.timeout`     | Sandbox timed out             |
| `command.started`     | Command execution started     |
| `command.completed`   | Command finished successfully |
| `command.failed`      | Command failed                |
| `file.written`        | File written to sandbox       |
| `checkpoint.created`  | Checkpoint created            |
| `checkpoint.restored` | Checkpoint restored           |
| `checkpoint.cloned`   | Checkpoint cloned             |
| `checkpoint.deleted`  | Checkpoint deleted            |
| `artifact.uploaded`   | Artifact uploaded             |
| `artifact.deleted`    | Artifact deleted              |
| `resource.warning`    | Resource usage warning        |
| `resource.critical`   | Resource usage critical       |
| `mcp.started`         | MCP server started            |
| `mcp.stopped`         | MCP server stopped            |

***

## Pricing

### Plans

| Plan       | Price   | Sandboxes/Month | Concurrent | Max Duration | Storage |
| ---------- | ------- | --------------- | ---------- | ------------ | ------- |
| Free       | \$0     | 50              | 2          | 5 min        | 1 GB    |
| Pro        | \$29/mo | 1,000           | 10         | 1 hour       | 50 GB   |
| Team       | \$99/mo | 10,000          | 50         | 2 hours      | 500 GB  |
| Enterprise | Custom  | Unlimited       | Custom     | Custom       | Custom  |

### Usage-Based Pricing

| Resource | Rate               |
| -------- | ------------------ |
| CPU      | \$0.05 / vCPU-hour |
| Memory   | \$0.02 / GB-hour   |
| Network  | \$0.10 / GB egress |
| Storage  | \$0.20 / GB-month  |

***

## Dashboard

Access at: `https://sandbox.stateset.com/dashboard`

### Pages

| Page        | Path                     | Description               |
| ----------- | ------------------------ | ------------------------- |
| Overview    | `/dashboard`             | Active sandboxes, metrics |
| Sandboxes   | `/dashboard/sandboxes`   | List and manage sandboxes |
| API Keys    | `/dashboard/api-keys`    | Create/revoke keys        |
| Checkpoints | `/dashboard/checkpoints` | Manage checkpoints        |
| Artifacts   | `/dashboard/artifacts`   | File storage              |
| Webhooks    | `/dashboard/webhooks`    | Event notifications       |
| Audit Logs  | `/dashboard/audit-logs`  | Activity history          |
| Usage       | `/dashboard/usage`       | Usage and billing         |
| Settings    | `/dashboard/settings`    | Organization settings     |

***

## Security

### API Key Security

* Keys are hashed (SHA-256) before storage
* Original key shown only once at creation
* Keys can have scopes and expiration
* Rate limiting per key

### Sandbox Isolation

* Each sandbox runs in isolated Kubernetes pod
* Network policies restrict pod communication
* Resource limits enforced (CPU, memory)
* Read-only root filesystem
* Non-root user execution

### Data Protection

* All data encrypted in transit (TLS)
* Database encrypted at rest
* Secrets stored encrypted with KMS
* Audit logging for compliance

***

## Environment Variables

### Controller Configuration

| Variable                        | Description                                        | Default              |
| ------------------------------- | -------------------------------------------------- | -------------------- |
| `DATABASE_URL`                  | PostgreSQL connection string                       | Required             |
| `AUTO_MIGRATE`                  | Run database migrations on startup (`true` or `1`) | `false`              |
| `JWT_SECRET`                    | Secret for JWT signing                             | Required             |
| `NAMESPACE`                     | Kubernetes namespace                               | Deployment namespace |
| `SANDBOX_IMAGE`                 | Docker image for sandboxes                         | Required             |
| `DEFAULT_CPUS`                  | Default CPU allocation                             | `0.5`                |
| `DEFAULT_MEMORY`                | Default memory allocation                          | `512Mi`              |
| `DEFAULT_TIMEOUT`               | Default timeout (seconds)                          | `300`                |
| `MAX_SANDBOXES_PER_ORG`         | Concurrent sandbox limit                           | `5`                  |
| `SANDBOX_EXEC_BACKEND`          | Exec backend (`k8s` or `kubectl`)                  | `kubectl`            |
| `STRIPE_SECRET_KEY`             | Stripe API key for billing                         | Optional             |
| `STORAGE_PROVIDER`              | `local`, `s3`, `gcs`, or `azure`                   | `local`              |
| `STORAGE_BUCKET`                | Cloud storage bucket name                          | Required if cloud    |
| `EXEC_AGENT_RUNTIME`            | Exec agent runtime (`node` or `go`)                | `node`               |
| `SANDBOX_BACKEND`               | Sandbox backend (`imperative` or `crd`)            | `imperative`         |
| `REPL_ENABLED`                  | Enable REPL sessions                               | `false`              |
| `REPL_JUPYTER_PORT`             | Jupyter port inside sandbox                        | `8888`               |
| `REPL_SESSION_TIMEOUT_SECONDS`  | Session idle timeout                               | `3600`               |
| `REPL_MAX_SESSIONS_PER_SANDBOX` | Max sessions per sandbox                           | `5`                  |

***

## Metrics Access

`/metrics` is protected in production. Configure your monitoring stack to include the auth header you use for metrics access.

If you use Prometheus Operator, apply `k8s/prometheus/service-monitor.yaml` and add your metrics auth header to the scrape config:

```bash theme={null}
kubectl apply -f k8s/prometheus/service-monitor.yaml
```

If you keep network policies enabled, label the monitoring namespace to allow scraping:

```bash theme={null}
kubectl label namespace monitoring your-domain.com/monitoring=true
```

***

## Warm Pool Profiles

Use warm pool profiles to pre‑warm the most common CPU/memory shapes and reduce cold starts. Profiles are matched on
`cpus` + `memory` with optional `isolation`.

Example configuration:

```
WARM_POOL_ENABLED=true
WARM_POOL_PROFILES='[
  {"cpus":"1","memory":"2Gi","size":6,"isolation":"container"},
  {"cpus":"2","memory":"2Gi","size":4,"isolation":"microvm"}
]'
```

If you pass `isolation` on sandbox creation, ensure the matching warm pool profile sets the same `isolation`.

Internal stats endpoint (production requires `X-Internal-Token`):

```
curl -H "X-Internal-Token: $INTERNAL_API_KEY" \
  "https://api.sandbox.yourdomain.com/api/v1/pool/stats?cpus=1&memory=2Gi&isolation=microvm"
```

***

## Tunnel Configuration

Tunnels expose a sandbox port through the API gateway.

Optional settings:

* `TUNNEL_BASE_URL=https://api.sandbox.yourdomain.com`
* `TUNNEL_DEFAULT_TTL_SECONDS=3600`
* `TUNNEL_MAX_TTL_SECONDS=86400`

## Artifact Storage (Cloud)

### Google Cloud Storage (GCS)

Required settings:

* `STORAGE_PROVIDER=gcs`
* `STORAGE_BUCKET=your-bucket`

`remote_path` is treated as a relative path and always stored under `artifacts/<organization-id>/`.

If you are not using Workload Identity, mount a service account key and set:

* `GCS_PROJECT_ID=your-gcp-project-id`
* `GCS_KEY_FILE=/var/secrets/gcp/key.json`

Example secret creation:

```bash theme={null}
kubectl create secret generic gcs-credentials \
  --from-file=key.json=/path/to/service-account.json \
  -n your-namespace
```

Mount the secret and set env vars in your controller deployment:

```yaml theme={null}
env:
  - name: GCS_PROJECT_ID
    value: your-gcp-project-id
  - name: GCS_KEY_FILE
    value: /var/secrets/gcp/key.json
volumeMounts:
  - name: gcs-credentials
    mountPath: /var/secrets/gcp
    readOnly: true
volumes:
  - name: gcs-credentials
    secret:
      secretName: gcs-credentials

### Azure Blob Storage (SAS)

Required settings:

- `STORAGE_PROVIDER=azure`
- `STORAGE_BUCKET=your-container`
- `AZURE_STORAGE_ACCOUNT=your-account`
- `AZURE_STORAGE_SAS_TOKEN=sv=...&ss=b&srt=co&sp=rwld&se=...`

Recommended: store `AZURE_STORAGE_SAS_TOKEN` in a Kubernetes Secret and inject it via `env` or `envFrom`.

Optional:

- `AZURE_STORAGE_ENDPOINT=https://your-account.blob.core.windows.net` (sovereign/private clouds)
```

Verify artifacts end-to-end:

```bash theme={null}
STATESET_API_URL=https://api.sandbox.stateset.com \
STATESET_API_KEY=sk_sandbox_xxx \
./scripts/verify-gcs-artifacts.sh
```

***

## Deployment

### Kubernetes Resources

```yaml theme={null}
# Namespace
apiVersion: v1
kind: Namespace
metadata:
  name: your-namespace

# Controller Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sandbox-controller
  namespace: your-namespace
spec:
  replicas: 3
  template:
    spec:
      serviceAccountName: sandbox-controller
      containers:
      - name: controller
        image: REGISTRY_URL/sandbox-controller:latest
        ports:
        - containerPort: 8080
        env:
        - name: DATABASE_URL
          valueFrom:
            secretKeyRef:
              name: sandbox-db-url
              key: url
      - name: db-proxy
        image: DB_PROXY_IMAGE
        args:
        - "--structured-logs"
        - "--port=5432"
        - "YOUR_DATABASE_CONNECTION"
```

### Custom Resource Definitions (CRDs)

Apply CRDs before deploying:

```bash theme={null}
kubectl apply -f k8s/crds/
```

Optional: Set `SANDBOX_BACKEND=crd` for declarative sandbox management. This enables `kubectl get ssb` (sandboxes) and `kubectl get wp` (warm pool entries).

### Health Checks

* Liveness: `GET /health`
* Readiness: `GET /ready`

***

## Troubleshooting

### Common Issues

**401 Unauthorized**

* Check API key format: `ApiKey sk-sandbox-xxx`
* Verify key exists in the database and is not revoked

**Sandbox Creation Failed**

* Check Kubernetes connectivity
* Verify namespace exists
* Check resource quotas

**Database Connection Failed**

* Verify database proxy is running (if used)
* Check workload identity / service account bindings
* Verify database credentials

### Logs

```bash theme={null}
# Controller logs
kubectl logs -n your-namespace -l app=sandbox-controller -c controller

# Database proxy logs
kubectl logs -n your-namespace -l app=sandbox-controller -c db-proxy

# Sandbox pod logs
kubectl logs -n your-namespace <sandbox-pod-name>
```

***

## Support

* GitHub Issues: [https://github.com/stateset/sandbox/issues](https://github.com/stateset/sandbox/issues)
* Documentation: [https://docs.stateset.io/sandbox](https://docs.stateset.io/sandbox)
* Email: [support@stateset.io](mailto:support@stateset.io)
