Quickstart Guide
Get Sandkasten running in 5 minutes. This guide walks through build → images → config → daemon → first session so nothing is skipped.
Prerequisites
- Linux (kernel 5.11+) or WSL2 with Ubuntu 22.04+
- cgroups v2 (default on modern systems)
- Go 1.24+ (for building)
Note
Complete setup (step by step)
1. Build
git clone https://github.com/p-arndt/sandkasten
cd sandkasten
task build
This produces:
bin/sandkasten— daemon and CLIbin/runner— runs inside sandboxes (embedded when creating images)bin/imgbuilder— legacy image import tool
2. Preflight and bootstrap
# Check kernel, cgroups, overlayfs, data-dir
./bin/sandkasten doctor
# Security baseline (api key, seccomp, limits)
./bin/sandkasten security --config sandkasten.yaml
# Create sandkasten.yaml + data dirs + pull first image (name: base)
sudo ./bin/sandkasten init --config sandkasten.yaml
By default, init pulls alpine:latest as image name base. For Python/Node sessions or the example agents you need to pull more images (next step).
3. Create images
Sessions run from an image (a rootfs). You must have at least one image. Pull from OCI registries without a Docker daemon:
# Python (required for quickstart/agent examples)
sudo ./bin/sandkasten image pull --name python python:3.12-slim
# Optional: Node.js
sudo ./bin/sandkasten image pull --name node node:22-slim
# List and validate
./bin/sandkasten image list
./bin/sandkasten image validate base
./bin/sandkasten image validate python
Image names (e.g. python, node, base) are what you use in config and API.
4. Configuration
If you used sandkasten init, you already have sandkasten.yaml. Edit it and set default_image to an image you created (e.g. python), and set api_key:
listen: "127.0.0.1:8080"
api_key: "sk-test"
data_dir: "/var/lib/sandkasten"
default_image: "python"
Note
init, create the data dirs manually: sudo mkdir -p /var/lib/sandkasten/{images,sessions,workspaces} and create sandkasten.yaml with the content above. See Configuration for all options.5. Start the daemon
# Foreground (logs in terminal; Ctrl+C to stop)
sudo ./bin/sandkasten --config sandkasten.yaml
# Or background (like Docker)
sudo ./bin/sandkasten daemon -d --config sandkasten.yaml
Stop when running in background:
sudo ./bin/sandkasten stop
6. Verify
curl http://localhost:8080/healthz
./bin/sandkasten ps
Once you see a healthy response and ps works, you can create sessions and run the example agent.
Your First Session
Important
sandkasten.yaml (e.g. sk-test) and an image you pulled (e.g. python or base).Using cURL
# Create session (use your api_key and an image you pulled)
SESSION_ID=$(curl -s -X POST http://localhost:8080/v1/sessions \
-H "Authorization: Bearer sk-test" \
-d '{"image":"python"}' | jq -r .id)
echo "Session ID: $SESSION_ID"
# Execute command
curl -X POST http://localhost:8080/v1/sessions/$SESSION_ID/exec \
-H "Authorization: Bearer sk-test" \
-d '{"cmd":"echo hello world"}'
# Write a file
curl -X POST http://localhost:8080/v1/sessions/$SESSION_ID/fs/write \
-H "Authorization: Bearer sk-test" \
-d '{"path":"/workspace/test.txt","text":"Hello from sandbox!"}'
# Read it back
curl "http://localhost:8080/v1/sessions/$SESSION_ID/fs/read?path=/workspace/test.txt" \
-H "Authorization: Bearer sk-test"
# Clean up
curl -X DELETE http://localhost:8080/v1/sessions/$SESSION_ID \
-H "Authorization: Bearer sk-test"
Using Python SDK
pip install sandkasten
import asyncio
from sandkasten import SandboxClient
async def main():
client = SandboxClient(
base_url="http://localhost:8080",
api_key="sk-test"
)
# Use an image you pulled (e.g. "python"); or omit for default_image from config
async with await client.create_session(image="python") as session:
result = await session.exec("echo hello")
print(result.output)
await session.write("test.py", "print('Hello!')")
result = await session.exec("python3 test.py")
print(result.output)
asyncio.run(main())
Run the example agent
cd quickstart/agent
export OPENAI_API_KEY="sk-..."
uv run enhanced_agent.py
The agent uses the daemon’s default_image and API key from config. See OpenAI Agents SDK for wiring Sandkasten as tools.
Next Steps
- Configuration Guide — Customize settings
- API Reference — Full API docs
- OpenAI Agents SDK — Use Sandkasten as tools in an agent
- Build custom images — Pull more images or build from rootfs
Building Custom Images
Python Image (registry pull)
sudo ./bin/sandkasten image pull --name my-python python:3.12-slim
Node.js Image (registry pull)
sudo ./bin/sandkasten image pull --name node node:22-slim
Manual Rootfs Build (no registry)
sudo debootstrap --variant=minbase bookworm /tmp/custom-rootfs
sudo chroot /tmp/custom-rootfs apt-get update
sudo chroot /tmp/custom-rootfs apt-get install -y python3 python3-pip
tar -czf /tmp/custom-rootfs.tar.gz -C /tmp/custom-rootfs .
sudo ./bin/imgbuilder import --name custom --tar /tmp/custom-rootfs.tar.gz
Troubleshooting
“cgroup v2 not mounted”
Ensure cgroups v2 is enabled:
mount | grep cgroup2
# Should show: cgroup2 on /sys/fs/cgroup type cgroup2
If not, check your kernel boot parameters or use a newer distro.
“overlayfs: upper fs does not support xattrs”
Warning
data_dir is on NTFS (e.g. /mnt/c in WSL2). Store data inside WSL’s Linux filesystem: data_dir: "/var/lib/sandkasten". Do not use /mnt/c/....“permission denied” / namespace errors
The daemon needs root (or CAP_SYS_ADMIN) to create namespaces:
sudo ./bin/sandkasten --config sandkasten.yaml
“image not found”
List images and pull the one your config or request uses:
./bin/sandkasten image list
sudo ./bin/sandkasten image pull --name python python:3.12-slim
Ensure default_image in sandkasten.yaml matches a name you pulled (e.g. python or base).
“Invalid API key”
Use the same API key in sandkasten.yaml and in your requests (e.g. Authorization: Bearer sk-test).
WSL2 Tips
Tip
- Use WSL2, not WSL1 (WSL1 doesn’t support cgroups v2).
- Store data in the Linux filesystem:
/var/lib/sandkasten, not/mnt/c/.... - Run the daemon in WSL; access from Windows at
http://localhost:8080.