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
# 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
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
# 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
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:
# 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
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
# 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
# Delete the sandbox
api /sandbox/$SANDBOX_ID -X DELETEComplete E2E Demo Script
We provide a full end-to-end demo script you can run:
# 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.shThe script demonstrates:
- Creating a sandbox with multiple files
- Creating a checkpoint
- Making destructive changes (corrupting files, deleting directories)
- Restoring from checkpoint
- Verifying all files are correctly restored
- Cloning checkpoints
- 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
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:
{
"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:
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
| Field | Type | Default | Description |
|---|---|---|---|
name | string | required | Human-readable checkpoint name |
description | string | - | Optional description |
include_paths | string[] | all | Only include these paths |
exclude_paths | string[] | none | Exclude these paths |
include_env | boolean | true | Include environment variables |
metadata | object | - | Custom metadata (max 10KB) |
Restoring Checkpoints
Restore to Same Sandbox
Restore a checkpoint to reset a sandbox to a previous state:
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:
# 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
| Field | Type | Default | Description |
|---|---|---|---|
checkpoint_id | string | required | Checkpoint to restore |
restore_files | boolean | true | Restore filesystem state |
restore_env | boolean | true | Restore environment variables |
overwrite | boolean | true | Overwrite existing files |
Listing Checkpoints
List All Organization Checkpoints
curl https://api.sandbox.stateset.app/api/v1/checkpoints \
-H "Authorization: ApiKey $API_KEY"Filter by Sandbox
curl "https://api.sandbox.stateset.app/api/v1/checkpoints?sandbox_id=sb-xyz789" \
-H "Authorization: ApiKey $API_KEY"Search by Name
curl "https://api.sandbox.stateset.app/api/v1/checkpoints?name=setup" \
-H "Authorization: ApiKey $API_KEY"Response:
{
"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:
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:
{
"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:
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:
{
"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
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:
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:
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:
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:
// 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:
# 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)
raise4. Template Creation
Create checkpoints to use as templates:
// 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
// Good
await client.createCheckpoint(sandboxId, {
name: 'After data preprocessing - cleaned nulls',
});
// Bad
await client.createCheckpoint(sandboxId, {
name: 'checkpoint1',
});2. Use Metadata for Context
await client.createCheckpoint(sandboxId, {
name: 'Training complete',
metadata: {
accuracy: 0.95,
epochs: 100,
model: 'resnet50',
commit: 'abc123',
},
});3. Exclude Large/Temporary Files
await client.createCheckpoint(sandboxId, {
name: 'Source code',
exclude_paths: [
'/workspace/node_modules',
'/workspace/.cache',
'/workspace/dist',
'/workspace/*.log',
],
});4. Clean Up Old Checkpoints
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:
| Event | Description | Data |
|---|---|---|
checkpoint.created | New checkpoint saved | checkpointId, name |
checkpoint.restored | Checkpoint restored to sandbox | checkpointId |
checkpoint.cloned | Checkpoint cloned | originalId, newId, name |
checkpoint.deleted | Checkpoint deleted | checkpointId |
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)