Skip to content

Repository files navigation

image

Cage

CI Go Version Docker API Docs

image

Cage is an open-source, self-hostable clone of E2B — a backend service for spinning up secure, isolated sandboxes to run untrusted or AI-generated code. Built in Go.

⚠️ Work in progress. Cage is a learning project and is not production-ready. Currently uses Docker containers as the isolation layer, with plans to explore Firecracker microVMs.

What is Cage?

Cage lets you programmatically create isolated environments ("sandboxes"), run commands inside them, and tear them down — all through a simple REST API. Think of it as the infrastructure layer you'd use to let an AI agent safely execute code without touching your host system.

# create a sandbox
curl -X POST http://localhost:8080/sandboxes

# → {"id":"a1b2c3...","status":"running","created_at":"2026-07-08T12:00:00Z"}

Features

Features

Core

  • Sandbox lifecycle management (create, list, get, delete)
  • Docker-backed isolation, pluggable behind a DockerClient interface
  • Command execution inside sandboxes, with streamed stdout/stderr demuxing
  • File upload/download to/from sandboxes
  • Pause / resume via container commit + recreate (frees memory, not just frozen in place)
  • Custom sandbox templates (Python, Node, or your own image)
  • Persistent storage (Postgres) for sandbox + API key metadata
  • Idle/expiry-based cleanup (background reaper + startup reconciliation)
  • Pluggable isolation backends — Docker (default) or Firecracker microVMs, see docs/firecracker-setup.md

Production readiness

  • API key authentication (hashed, never stored raw)
  • Redis-backed caching for auth checks (fail-open on cache errors)
  • Redis-backed rate limiting (token bucket, fail-open)
  • Warm sandbox pool per template — cuts cold-start latency on creation
  • Structured JSON logging (slog) + Prometheus metrics
  • Graceful shutdown (drains in-flight requests on SIGTERM)
  • Distributed locking — safe to run multiple replicas (no double-reaping)

Developer experience

  • Go SDK (sdk/go) — typed client for every endpoint
  • CLI (cage) — create, exec, files, pause/resume, TUI, all from your terminal
  • Interactive TUI (cage tui) — live sandbox dashboard, Bubble Tea-based, animated splash screen
  • OpenAPI spec (openapi.yaml) + hosted, browsable API reference (Scalar + GitHub Pages)
  • Runnable examples in curl, Go, Python, and TypeScript
  • CLI distribution — Homebrew (macOS/Linux), Scoop (Windows), and a fallback install script Not yet built
  • Firecracker microVM backend (parked — see ADR 0012)
  • Official TypeScript/Python SDKs (examples currently use raw HTTP)
  • Checksum verification in the install script

Architecture

Cage exposes a REST API that manages the lifecycle of sandboxes. Each sandbox currently maps 1:1 to a Docker container, with an in-memory (soon Postgres-backed) store tracking metadata.

image

See docs/adr for the reasoning behind each major architectural decision.

Project Structure

cage/
├── cmd/
│   ├── cage/                 # main server entrypoint
│   └── genkey/               # CLI to generate API keys
├── internal/
│   ├── api/                  # HTTP handlers, auth + rate-limit + metrics middleware
│   │  └── openapi.yaml       # full API spec for every route
│   ├── auth/                 # API key generation & hashing
│   ├── cache/                # Redis cache wrapper
│   ├── config/               # env var loading
│   ├── db/                   # migration runner (golang-migrate)
│   ├── lock/                 # Redis-backed distributed lock (leader election)
│   ├── logging/              # structured slog setup
│   ├── metrics/              # Prometheus metric definitions
│   ├── pool/                 # sandbox pre-warming pool
│   ├── ratelimit/            # token bucket rate limiter
│   ├── reaper/               # background job: expire idle sandboxes
│   ├── reconcile/            # syncs DB state with Docker on boot
│   ├── sandbox/              # Docker SDK wrapper (create/exec/pause/files)
│   └── store/                # Postgres-backed persistence
├── sdk/
│   └── go/                   # cageclient — standalone Go module, the official SDK
├── cli/
│   ├── cmd/                  # Cobra commands (sandbox, exec, files, login, tui)
│   └── tui/                  # Bubble Tea interactive dashboard
├── migrations/               # golang-migrate SQL files (paired .up/.down)
├── scripts/                  # git hook scripts
├── docs/
│   └── adr/                  # Architecture Decision Records
├── .github/
│   ├── workflows/ci.yml      # lint, build, test on every push/PR
│   └── CODEOWNERS
├── docker-compose.yml        # Postgres + Redis + app, for local dev
├── Dockerfile                # multi-stage build for the server image
├── lefthook.yml              # pre-commit hooks (format, lint, build)
└── Makefile                  # dev, lint, fmt, migrate, genkey, test targets

internal/ is Go-enforced: nothing outside this module can import it. sdk/go and cli are deliberately separate Go modules (each has its own go.mod) so the SDK can be imported independently without pulling in server-only dependencies like the Docker SDK or pgx.

Getting Started

Prerequisites

Run the server

git clone https://github.com/harshalvk/cage.git
cd cage
go mod tidy
cp .env.example .env      # fill in real values
make setup                # installs git hooks
docker compose up -d cage-postgres cage-redis
make migrate-up
make dev                   # live-reloading server on :8080

Or the full containerized stack:

docker compose up --build

Use the CLI

cd cli
go build -o cage .

./cage login --api-key=<key-from-make-genkey>
./cage sandbox create --template python-3.12
./cage sandbox ls
./cage exec <id> -- python3 --version
./cage tui              # interactive dashboard

Install the CLI

macOS/Linux (Homebrew):

brew install harshalvk/cage/cage

Windows (Scoop):

scoop bucket add cage https://github.com/harshalvk/scoop-cage
scoop install cage

Any platform (install script):

curl -sSL https://raw.githubusercontent.com/harshalvk/cage/main/install.sh | bash

Use the SDK

go get github.com/harshalvk/cage/sdk/go
client := cageclient.New("http://localhost:8080", "your-api-key")
sb, _ := client.CreateSandbox(ctx, cageclient.CreateSandboxOptions{Template: "python-3.12"})
result, _ := client.Exec(ctx, sb.ID, []string{"python3", "-c", "print('hello')"})

Authentication

All /sandboxes routes require an API key as a Bearer token. /health and /templates are public.

make genkey name=local-dev
curl -X POST http://localhost:8080/sandboxes \
  -H "Authorization: Bearer <your-api-key>"

Isolation Backends

Cage supports two backends for actually running sandboxes, selected via ISOLATION_BACKEND:

Backend Isolation Status
docker (default) Container (shared kernel) Stable
firecracker microVM (KVM-based) Available, not yet merged to master — see docs/firecracker-setup.md

Both backends support the full sandbox lifecycle, exec, and file transfer identically. Pause/resume works on both, via different mechanisms — see ADR 0005 and ADR 0015.

API Reference

Method Endpoint Description
GET /health Health check
GET /templates List available sandbox templates
POST /sandboxes Create a sandbox
GET /sandboxes List sandboxes
GET /sandboxes/{id} Get sandbox details
DELETE /sandboxes/{id} Kill and remove a sandbox
POST /sandboxes/{id}/exec Run a command inside a sandbox
POST /sandboxes/{id}/files Write a file into a sandbox
GET /sandboxes/{id}/files Read a file from a sandbox
POST /sandboxes/{id}/pause Pause a running sandbox
POST /sandboxes/{id}/resume Resume a paused sandbox
GET /metrics Prometheus metrics

Full request/response schemas: openapi.yaml, or browse them at the hosted API reference.

Development

Command Description
make dev Run with live reload (Air)
make build Build the binary
make lint Run golangci-lint
make fmt Format code
make migrate-up Apply DB migrations
make migrate-down Roll back last migration
make migrate-create name=X Create a new migration pair
make test Run tests
make genkey name=X Generate a new API key labeled X

More runnable examples (curl, Go, Python): examples/

This prints the raw key once - it is never shown again and only its hash is stored

License

MIT

Acknowledgements

Inspired by E2B, an excellent open-source sandbox infrastructure platform. Cage is an independent educational project and is not affiliated with E2B/FoundryLabs.

About

Cage lets you programmatically create isolated environments ("sandboxes"), run commands inside them, and tear them down — all through a simple REST API.

Topics

Resources

Stars

5 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages