Checkpoints

Save and restore sandbox state with checkpoints.

Checkpoints Guide

Checkpoints allow you to save and restore sandbox state. They capture the filesystem, environment variables, and metadata at a point in time, enabling you to:

  • Save work in progress
  • Restore a sandbox to a known good state
  • Clone sandbox configurations for new runs
  • Compare changes between different states

Quick Start Tutorial

This hands-on tutorial walks you through the complete checkpoint lifecycle in under 5 minutes.

Prerequisites

bash
# Set your API key
export STATESET_API_KEY="sk_live_your_api_key"
export STATESET_API_URL="https://api.sandbox.stateset.app/api/v1"

# Helper function for API calls
api() {
  curl -s -H "Authorization: ApiKey $STATESET_API_KEY" \
       -H "Content-Type: application/json" \
       "$STATESET_API_URL$1" "${@:2}"
}

Step 1: Create a Sandbox

bash
SANDBOX_RESPONSE=$(api /sandbox/create -X POST -d '{}')
SANDBOX_ID=$(echo $SANDBOX_RESPONSE | jq -r '.id')
echo "Created sandbox: $SANDBOX_ID"

Step 2: Create Some Files

bash
# Create a Python file
api /sandbox/$SANDBOX_ID/execute -X POST -d '{
  "command": "cat > /workspace/app.py << '\''EOF'\''\nimport json\n\nclass Counter:\n    def __init__(self):\n        self.value = 0\n    \n    def increment(self):\n        self.value += 1\n        return self.value\n\nif __name__ == \"__main__\":\n    c = Counter()\n    print(f\"Count: {c.increment()}\")\nEOF"
}'

# Create a config file
api /sandbox/$SANDBOX_ID/execute -X POST -d '{
  "command": "echo '\''{ \"version\": \"1.0.0\", \"debug\": true }'\'' > /workspace/config.json"
}'

# Verify files exist
api /sandbox/$SANDBOX_ID/execute -X POST -d '{"command": "ls -la /workspace/"}'

Step 3: Create a Checkpoint

bash
CHECKPOINT_RESPONSE=$(api /sandbox/$SANDBOX_ID/checkpoints -X POST -d '{
  "name": "initial-setup",
  "description": "App and config files created",
  "include_env": true
}')
CHECKPOINT_ID=$(echo $CHECKPOINT_RESPONSE | jq -r '.id')
echo "Created checkpoint: $CHECKPOINT_ID"

Step 4: Make Destructive Changes

Now let's break things intentionally:

bash
# Corrupt the app file
api /sandbox/$SANDBOX_ID/execute -X POST -d '{
  "command": "echo \"BROKEN\" > /workspace/app.py"
}'

# Delete the config
api /sandbox/$SANDBOX_ID/execute -X POST -d '{
  "command": "rm /workspace/config.json"
}'

# Verify damage
api /sandbox/$SANDBOX_ID/execute -X POST -d '{
  "command": "cat /workspace/app.py && ls /workspace/"
}'
# Output: BROKEN (config.json is gone)

Step 5: Restore from Checkpoint

bash
api /sandbox/$SANDBOX_ID/checkpoints/restore -X POST -d "{
  \"checkpoint_id\": \"$CHECKPOINT_ID\",
  \"restore_files\": true,
  \"restore_env\": true,
  \"overwrite\": true
}"

Step 6: Verify Restoration

bash
# Check app.py is restored
api /sandbox/$SANDBOX_ID/execute -X POST -d '{"command": "head -5 /workspace/app.py"}'
# Output: import json...

# Check config.json is back
api /sandbox/$SANDBOX_ID/execute -X POST -d '{"command": "cat /workspace/config.json"}'
# Output: { "version": "1.0.0", "debug": true }

Step 7: Clean Up

bash
# Delete the sandbox
api /sandbox/$SANDBOX_ID -X DELETE

Complete E2E Demo Script

We provide a full end-to-end demo script you can run:

bash
# Download and run the demo
curl -O https://raw.githubusercontent.com/stateset/stateset-sandbox/main/scripts/checkpoint-e2e-demo.sh
chmod +x checkpoint-e2e-demo.sh
export STATESET_API_KEY="sk_live_your_key"
./checkpoint-e2e-demo.sh

The script demonstrates:

  1. Creating a sandbox with multiple files
  2. Creating a checkpoint
  3. Making destructive changes (corrupting files, deleting directories)
  4. Restoring from checkpoint
  5. Verifying all files are correctly restored
  6. Cloning checkpoints
  7. Listing all checkpoints

Overview

┌──────────────────────────────────────────────────────────────────┐
│                         Sandbox A                                 │
│  ┌─────────────────────────────────────────────────────────┐     │
│  │  /workspace/                                             │     │
│  │  ├── src/                                                │     │
│  │  │   └── app.py                                         │     │
│  │  ├── data/                                               │     │
│  │  └── config.json                                        │     │
│  └─────────────────────────────────────────────────────────┘     │
│                              │                                    │
│                    CREATE CHECKPOINT                              │
│                              ▼                                    │
└──────────────────────────────────────────────────────────────────┘
                               │
                               ▼
┌──────────────────────────────────────────────────────────────────┐
│                      Checkpoint Storage                           │
│  ┌────────────────────────────────────────────────────────────┐  │
│  │  checkpoint_id: cp-abc123                                   │  │
│  │  name: "After data preprocessing"                          │  │
│  │  files: [src/app.py, data/*, config.json]                  │  │
│  │  env: {API_KEY: "...", DEBUG: "true"}                      │  │
│  │  created_at: 2024-01-21T12:00:00Z                          │  │
│  └────────────────────────────────────────────────────────────┘  │
└──────────────────────────────────────────────────────────────────┘
                               │
                    RESTORE CHECKPOINT
                               ▼
┌──────────────────────────────────────────────────────────────────┐
│                    Sandbox B (or same sandbox)                    │
│  ┌─────────────────────────────────────────────────────────┐     │
│  │  Restored to exact state from checkpoint                 │     │
│  └─────────────────────────────────────────────────────────┘     │
└──────────────────────────────────────────────────────────────────┘

Creating Checkpoints

Basic Checkpoint

bash
curl -X POST https://api.sandbox.stateset.app/api/v1/sandbox/$SANDBOX_ID/checkpoints \
  -H "Authorization: ApiKey $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Initial setup complete",
    "description": "Dependencies installed and config set"
  }'

Response:

json
{
  "id": "cp-abc123",
  "sandbox_id": "sb-xyz789",
  "org_id": "org-123",
  "name": "Initial setup complete",
  "description": "Dependencies installed and config set",
  "size_bytes": 15728640,
  "file_count": 142,
  "created_at": "2024-01-21T12:00:00.000Z",
  "metadata": {}
}

Checkpoint with Path Filters

Capture only specific paths or exclude certain directories:

bash
curl -X POST https://api.sandbox.stateset.app/api/v1/sandbox/$SANDBOX_ID/checkpoints \
  -H "Authorization: ApiKey $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Source code only",
    "include_paths": ["/workspace/src", "/workspace/config"],
    "exclude_paths": ["/workspace/node_modules", "/workspace/.cache"],
    "include_env": true,
    "metadata": {
      "version": "1.2.0",
      "commit": "abc123"
    }
  }'

Checkpoint Options

FieldTypeDefaultDescription
namestringrequiredHuman-readable checkpoint name
descriptionstring-Optional description
include_pathsstring[]allOnly include these paths
exclude_pathsstring[]noneExclude these paths
include_envbooleantrueInclude environment variables
metadataobject-Custom metadata (max 10KB)

Restoring Checkpoints

Restore to Same Sandbox

Restore a checkpoint to reset a sandbox to a previous state:

bash
curl -X POST https://api.sandbox.stateset.app/api/v1/sandbox/$SANDBOX_ID/checkpoints/restore \
  -H "Authorization: ApiKey $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "checkpoint_id": "cp-abc123",
    "restore_files": true,
    "restore_env": true,
    "overwrite": true
  }'

Restore to Different Sandbox

Create a new sandbox, then restore to it:

bash
# Create new sandbox
NEW_SANDBOX=$(curl -s -X POST https://api.sandbox.stateset.app/api/v1/sandbox \
  -H "Authorization: ApiKey $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}' | jq -r '.sandbox_id')

# Restore checkpoint
curl -X POST https://api.sandbox.stateset.app/api/v1/sandbox/$NEW_SANDBOX/checkpoints/restore \
  -H "Authorization: ApiKey $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "checkpoint_id": "cp-abc123"
  }'

Restore Options

FieldTypeDefaultDescription
checkpoint_idstringrequiredCheckpoint to restore
restore_filesbooleantrueRestore filesystem state
restore_envbooleantrueRestore environment variables
overwritebooleantrueOverwrite existing files

Listing Checkpoints

List All Organization Checkpoints

bash
curl https://api.sandbox.stateset.app/api/v1/checkpoints \
  -H "Authorization: ApiKey $API_KEY"

Filter by Sandbox

bash
curl "https://api.sandbox.stateset.app/api/v1/checkpoints?sandbox_id=sb-xyz789" \
  -H "Authorization: ApiKey $API_KEY"

Search by Name

bash
curl "https://api.sandbox.stateset.app/api/v1/checkpoints?name=setup" \
  -H "Authorization: ApiKey $API_KEY"

Response:

json
{
  "checkpoints": [
    {
      "id": "cp-abc123",
      "sandbox_id": "sb-xyz789",
      "name": "Initial setup complete",
      "size_bytes": 15728640,
      "file_count": 142,
      "created_at": "2024-01-21T12:00:00.000Z"
    },
    {
      "id": "cp-def456",
      "sandbox_id": "sb-xyz789",
      "name": "After data import",
      "size_bytes": 52428800,
      "file_count": 158,
      "created_at": "2024-01-21T12:30:00.000Z"
    }
  ]
}

Cloning Checkpoints

Create a copy of a checkpoint with a new name:

bash
curl -X POST https://api.sandbox.stateset.app/api/v1/checkpoints/cp-abc123/clone \
  -H "Authorization: ApiKey $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Initial setup - backup"
  }'

Response:

json
{
  "id": "cp-ghi789",
  "sandbox_id": "sb-xyz789",
  "name": "Initial setup - backup",
  "size_bytes": 15728640,
  "file_count": 142,
  "created_at": "2024-01-21T13:00:00.000Z"
}

Comparing Checkpoints

Compare the differences between two checkpoints:

bash
curl -X POST https://api.sandbox.stateset.app/api/v1/checkpoints/compare \
  -H "Authorization: ApiKey $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "checkpoint_id_1": "cp-abc123",
    "checkpoint_id_2": "cp-def456"
  }'

Response:

json
{
  "checkpoint_1": {
    "id": "cp-abc123",
    "name": "Initial setup complete"
  },
  "checkpoint_2": {
    "id": "cp-def456",
    "name": "After data import"
  },
  "diff": {
    "files_added": [
      "/workspace/data/dataset.csv",
      "/workspace/data/metadata.json"
    ],
    "files_removed": [],
    "files_modified": [
      "/workspace/config.json",
      "/workspace/src/processor.py"
    ],
    "env_added": {
      "DATA_PATH": "/workspace/data"
    },
    "env_removed": {},
    "env_modified": {},
    "size_change_bytes": 36700160
  }
}

Deleting Checkpoints

bash
curl -X DELETE https://api.sandbox.stateset.app/api/v1/checkpoints/cp-abc123 \
  -H "Authorization: ApiKey $API_KEY"

SDK Quick Start

Node.js

Checkpoint methods are on the StateSetSandboxExtended class:

typescript
import { StateSetSandboxExtended } from '@stateset/sandbox-sdk';

const client = new StateSetSandboxExtended({
  baseUrl: 'https://api.sandbox.stateset.app',
  authToken: process.env.SANDBOX_API_KEY!,
});

// Create a sandbox and do some work
const sandbox = await client.create({ timeout_seconds: 600 });
await client.execute(sandbox.sandbox_id, { command: ['npm', 'install'] });
await client.writeFile(sandbox.sandbox_id, '/workspace/config.json',
  JSON.stringify({ debug: true }));

// Create a checkpoint
const checkpoint = await client.createCheckpoint(sandbox.sandbox_id, {
  name: 'Dependencies installed',
  description: 'npm install complete, config set',
  metadata: { version: '1.0.0' },
});
console.log(`Checkpoint created: ${checkpoint.id}`);

// Do more work...
await client.execute(sandbox.sandbox_id, { command: ['npm', 'run', 'build'] });

// Something went wrong — restore the checkpoint
await client.restoreCheckpoint(sandbox.sandbox_id, checkpoint.id);
console.log('Restored to checkpoint');

// List all checkpoints for this sandbox
const { checkpoints } = await client.listCheckpoints({
  sandbox_id: sandbox.sandbox_id,
});
console.log(`Found ${checkpoints.length} checkpoints`);

// Clone the checkpoint for a new experiment
const cloned = await client.cloneCheckpoint(checkpoint.id, 'Experiment A baseline');

// Compare two checkpoints
const diff = await client.compareCheckpoints(checkpoint.id, cloned.id);
console.log('Files modified:', diff.diff.modified);

Python

The Python SDK exposes the same operations with snake_case method names:

python
from stateset_sandbox import StateSetSandbox
import os

client = StateSetSandbox(
    base_url="https://api.sandbox.stateset.app",
    auth_token=os.environ["SANDBOX_API_KEY"],
)

# Create sandbox and do some setup work
sandbox = client.create(timeout_seconds=600)
client.execute(sandbox.sandbox_id, command=["pip", "install", "-r", "requirements.txt"])
client.write_file(sandbox.sandbox_id, "/workspace/config.yaml", "debug: true")

# Create checkpoint
checkpoint = client.create_checkpoint(
    sandbox.sandbox_id,
    name="Setup complete",
    description="All dependencies installed",
    include_paths=["/workspace"],
    exclude_paths=["/workspace/__pycache__", "/workspace/.venv"],
)
print(f"Checkpoint: {checkpoint.id}")

# Run experiment
client.execute(sandbox.sandbox_id, command=["python", "train.py"])

# Restore to try different parameters
client.restore_checkpoint(sandbox.sandbox_id, checkpoint.id)

# Try again with different config
client.write_file(sandbox.sandbox_id, "/workspace/config.yaml", "learning_rate: 0.01")
client.execute(sandbox.sandbox_id, command=["python", "train.py"])

Use Cases

1. Experiment Snapshots

Save state before experiments to enable quick iteration:

python
import json

# Save baseline
baseline = client.create_checkpoint(sandbox_id, name="Baseline")

for learning_rate in [0.001, 0.01, 0.1]:
    # Restore to baseline
    client.restore_checkpoint(sandbox_id, baseline.id)

    # Update config
    client.write_file(sandbox_id, "/workspace/config.json",
        json.dumps({"learning_rate": learning_rate}))

    # Run experiment
    result = client.execute(sandbox_id, command=["python", "train.py"])

    # Save successful experiment
    if "success" in result.stdout:
        client.create_checkpoint(
            sandbox_id,
            name=f"LR={learning_rate}",
            metadata={"learning_rate": learning_rate},
        )

2. Build Caching

Cache dependency installation:

typescript
// Check if we have a cached checkpoint
const { checkpoints } = await client.listCheckpoints({ name: 'deps-cache' });

if (checkpoints.length > 0) {
  // Restore from cache
  await client.restoreCheckpoint(sandboxId, checkpoints[0].id);
} else {
  // Install dependencies
  await client.execute(sandboxId, { command: ['npm', 'install'] });

  // Cache for future runs
  await client.createCheckpoint(sandboxId, {
    name: 'deps-cache',
    include_paths: ['/workspace/node_modules'],
    include_env: false,
  });
}

3. Error Recovery

Checkpoint before risky operations:

python
# Save before migration
pre_migration = client.create_checkpoint(
    sandbox_id,
    name="Pre-migration",
    description="Before database migration",
)

try:
    client.execute(sandbox_id, command=["python", "migrate.py"])
except Exception as e:
    print(f"Migration failed: {e}")
    # Restore to pre-migration state
    client.restore_checkpoint(sandbox_id, pre_migration.id)
    raise

4. Template Creation

Create checkpoints to use as templates:

typescript
// Set up environment
await client.execute(sandboxId, { command: ['npm', 'install'] });
await client.execute(sandboxId, { command: ['npm', 'run', 'setup'] });

// Create template checkpoint
const template = await client.createCheckpoint(sandboxId, {
  name: 'React App Template',
  description: 'Fresh React app with standard dependencies',
  metadata: {
    framework: 'react',
    version: '18.2.0',
  },
});

// Later: Create new sandbox from template
const newSandbox = await client.create();
await client.restoreCheckpoint(newSandbox.sandbox_id, template.id);

Best Practices

1. Name Checkpoints Descriptively

typescript
// Good
await client.createCheckpoint(sandboxId, {
  name: 'After data preprocessing - cleaned nulls',
});

// Bad
await client.createCheckpoint(sandboxId, {
  name: 'checkpoint1',
});

2. Use Metadata for Context

typescript
await client.createCheckpoint(sandboxId, {
  name: 'Training complete',
  metadata: {
    accuracy: 0.95,
    epochs: 100,
    model: 'resnet50',
    commit: 'abc123',
  },
});

3. Exclude Large/Temporary Files

typescript
await client.createCheckpoint(sandboxId, {
  name: 'Source code',
  exclude_paths: [
    '/workspace/node_modules',
    '/workspace/.cache',
    '/workspace/dist',
    '/workspace/*.log',
  ],
});

4. Clean Up Old Checkpoints

typescript
const { checkpoints } = await client.listCheckpoints();

// Keep only the 5 most recent
const old = checkpoints.slice(5);
for (const cp of old) {
  await client.deleteCheckpoint(cp.id);
}

Webhook Events

Checkpoints emit webhook events:

EventDescriptionData
checkpoint.createdNew checkpoint savedcheckpointId, name
checkpoint.restoredCheckpoint restored to sandboxcheckpointId
checkpoint.clonedCheckpoint clonedoriginalId, newId, name
checkpoint.deletedCheckpoint deletedcheckpointId

API Reference

POST /api/sandbox/:id/checkpoints

Create a checkpoint from a sandbox.

POST /api/sandbox/:id/checkpoints/restore

Restore a checkpoint to a sandbox.

GET /api/checkpoints

List checkpoints. Query params: sandbox_id, name.

GET /api/checkpoints/:id

Get checkpoint details.

POST /api/checkpoints/:id/clone

Clone a checkpoint.

POST /api/checkpoints/compare

Compare two checkpoints.

DELETE /api/checkpoints/:id

Delete a checkpoint.

Limitations

  • Maximum checkpoint size: 10GB
  • Maximum file count: 100,000 files
  • Checkpoint data retained for 90 days (configurable)
  • Process state is not captured (only filesystem)