Modal-based sandbox infrastructure for the Open-Inspect coding agent system.
This package provides the data plane for Open-Inspect:
- Sandboxes: Isolated development environments running OpenCode
- Images: Pre-built container images with all development tools
- Snapshots: Filesystem snapshots for fast startup and session persistence
- Scheduler: Cron-based rebuilds of prebuilt scope images — repositories and environments alike (every 30 minutes)
┌─────────────────────────────────────────────────────────────────┐
│ Session Sandbox │
│ ┌──────────────────┐ ┌─────────────────┐ ┌───────────────┐ │
│ │ Supervisor │ │ OpenCode │ │ Bridge │ │
│ │ (entrypoint.py) │──│ Server │──│ (bridge.py) │ │
│ └──────────────────┘ └─────────────────┘ └───────────────┘ │
│ │ │ │ │
│ └────────────────────┼────────────────────┘ │
│ │ │
│ WebSocket to │
│ Control Plane │
└─────────────────────────────────────────────────────────────────┘
Base image definition with:
- Debian slim + git, curl, build-essential
- Node.js 22, pnpm, Bun
- Python 3.12 with uv
- OpenCode CLI
- agent-browser CLI + headless Chrome
- manager.py: Sandbox lifecycle (create, warm, snapshot)
- entrypoint.py: Supervisor process (runs as PID 1)
- bridge.py: WebSocket bridge to control plane
- types.py: Event and configuration types
- github_app.py: GitHub App token generation for repo access
- internal.py: HMAC authentication for control plane requests
- web_api.py: HTTP endpoints called by the control plane
- image_builder.py: The
build_imageworker (spawned byapi_build_image) plus the 30-minute rebuild cron. A build clones every repository of its scope — a single repository or an environment's ordered set — runs each setup script in position order, snapshots the filesystem, and reports the result (per-repository SHAs + runtime version) back to the control plane.rebuild_imagesruns one pass over all prebuild-enabled scope units fromGET /image-builds/enabled, rebuilding on fingerprint mismatch, runtime-floor violation, or branch-tip drift, capped atTRIGGER_CAP_PER_TICKbuilds per tick across all units
Full deployment guide: See docs/GETTING_STARTED.md for complete setup instructions including all required secrets and configuration.
- Install Modal CLI:
pip install modal - Authenticate:
modal setup - Create secrets via Modal CLI:
# LLM API keys
modal secret create llm-api-keys ANTHROPIC_API_KEY="sk-ant-..."
# GitHub App credentials (for repo access)
modal secret create github-app \
GITHUB_APP_ID="123456" \
GITHUB_APP_PRIVATE_KEY="$(cat private-key-pkcs8.pem)" \
GITHUB_APP_INSTALLATION_ID="12345678"
# Internal API secret (for control plane authentication)
modal secret create internal-api \
MODAL_API_SECRET="$(openssl rand -hex 32)" \
ALLOWED_CONTROL_PLANE_HOSTS="your-control-plane.workers.dev"See .env.example for a full list of environment variables.
sandbox-runtime is a sibling package in this monorepo (not published to PyPI).
If you use uv, it is resolved automatically. Otherwise install it first:
pip install -e ../sandbox-runtime
pip install -e ".[dev]"# Deploy the app (recommended)
modal deploy deploy.py
# Alternative: deploy the src package directly
modal deploy -m src
# Run locally for development
modal run src/Note: Never deploy
src/app.pydirectly - it only defines the app and shared resources. Usedeploy.pyor-m srcto ensure all function modules are registered.
The control plane communicates with Modal via HTTP endpoints. All endpoints (except health)
require HMAC authentication via the Authorization header.
Endpoint URLs follow the pattern: https://{workspace}--open-inspect-{endpoint}.modal.run
| Endpoint | Method | Auth | Description |
|---|---|---|---|
api-health |
GET | No | Health check |
api-create-sandbox |
POST | Yes | Create a new sandbox |
api-snapshot-sandbox |
POST | Yes | Take filesystem snapshot |
api-restore-sandbox |
POST | Yes | Restore sandbox from snapshot |
api-build-image |
POST | Yes | Spawn an async prebuilt-image build for a scope (repo or environment); results POST back to the control plane's /image-builds/* callbacks |
api-delete-provider-image |
POST | Yes | Best-effort delete of a replaced provider image |
curl -X POST "https://${WORKSPACE}--open-inspect-api-create-sandbox.modal.run" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"session_id": "session-123",
"repo_owner": "your-org",
"repo_name": "your-repo",
"control_plane_url": "https://your-control-plane.workers.dev",
"sandbox_auth_token": "your-token"
}'curl "https://${WORKSPACE}--open-inspect-api-health.modal.run"
# {"success": true, "data": {"status": "healthy", "service": "open-inspect-modal"}}Set via Modal secrets:
| Variable | Secret | Description |
|---|---|---|
ANTHROPIC_API_KEY |
llm-api-keys |
Anthropic API key for Claude |
GITHUB_APP_ID |
github-app |
GitHub App ID for repo access |
GITHUB_APP_PRIVATE_KEY |
github-app |
GitHub App private key (PKCS#8) |
GITHUB_APP_INSTALLATION_ID |
github-app |
GitHub App installation ID |
MODAL_API_SECRET |
internal-api |
Shared secret for control plane auth |
ALLOWED_CONTROL_PLANE_HOSTS |
internal-api |
Comma-separated allowed hostnames for URL validation |
| Criterion | Test Method |
|---|---|
| App deploys successfully | modal deploy deploy.py completes without errors |
| Health endpoint responds | curl https://{workspace}--open-inspect-api-health.modal.run |
| Sandbox creation works | POST to api-create-sandbox returns success |
| Git sync completes | Verify HEAD matches origin after sandbox start |
| Snapshot/restore works | Take snapshot, restore, verify workspace state |
# Using uv (recommended — resolves sandbox-runtime automatically)
uv sync --frozen --extra dev
# Using pip (install sandbox-runtime first)
pip install -e ../sandbox-runtime
pip install -e ".[dev]"
# Run tests
pytest tests/
# Type check
mypy src/The health endpoint is available at GET /api_health (no authentication required).