Go backend for managing short‑lived development sandboxes backed by Docker and PostgreSQL.
It exposes:
/health– service health check./hello– simple hello world endpoint./api/sandboxes– CRUD + heartbeat API for sandbox lifecycle./api/sandboxes/{id}/files/upload– upload a file into a sandbox container./api/sandboxes/{id}/files/download– download a file from a sandbox container.
The service creates Docker containers from devcontainer.json templates, tracks them in PostgreSQL, and automatically cleans up idle sandboxes after a configurable TTL (15 minutes by default).
-
Entry point
cmd/server/main.go- Reads configuration (e.g.
DATABASE_URL,SERVER_ADDR,DEVCONTAINERS_PATH). - Runs database migrations on startup with retry.
- Creates the DB connection and Docker client.
- Instantiates the sandbox
Managerwith a 15‑minute session TTL. - Starts the HTTP server.
- Reads configuration (e.g.
-
HTTP server
internal/httpserver/server.go,internal/httpserver/routes.go- Uses
http.ServeMux. - Registers:
GET /healthGET /helloGET /api/sandboxesPOST /api/sandboxesGET /api/sandboxes/{id}DELETE /api/sandboxes/{id}POST /api/sandboxes/{id}/heartbeatPOST /api/sandboxes/{id}/executePOST /api/sandboxes/{id}/files/uploadGET /api/sandboxes/{id}/files/download
- Uses
-
Handlers
internal/handlers/health.go– JSON health response.internal/handlers/hello.go– JSON hello response.internal/handlers/sandboxes.go– sandbox REST API:- Parses and validates requests.
- Loads devcontainer templates.
- Delegates to the sandbox manager.
- Returns JSON responses and errors.
internal/handlers/sandbox_execute.go– run commands inside a sandbox (POST /api/sandboxes/{id}/execute).internal/handlers/sandbox_files.go– file upload and download to/from a sandbox container.
-
Sandbox domain
internal/sandbox/*,pkg/models/sandbox.goManagerorchestrates:- Creating containers from devcontainer templates.
- Persisting sandbox metadata.
- Tracking last activity and expiry.
- Deleting containers and marking sandboxes deleted/expired.
- Background cleanup worker:
- Runs every minute (configurable).
- Expires and deletes idle sandboxes older than the TTL.
-
Persistence
internal/db/*,internal/db/repository/*- PostgreSQL connection + migrations.
- Repositories for sandboxes and port mappings.
-
Devcontainers & Docker
internal/devcontainer/*- Parses
devcontainer.jsontemplates from:DEVCONTAINERS_PATH(if set), or- local
./devcontainersdirectory.
- Parses
internal/docker/*- Thin wrapper around the Docker SDK for creating, starting, and cleaning up containers, and for copying files in/out via
CopyToContainerandCopyFromContainer.
- Thin wrapper around the Docker SDK for creating, starting, and cleaning up containers, and for copying files in/out via
For a more narrative overview, also see SANDBOX.md.
- Go 1.22 or later
- Docker daemon reachable from the backend
- PostgreSQL database
Environment variables:
DATABASE_URL– required, PostgreSQL connection string.SERVER_ADDR– optional, listen address (default:8080).DEVCONTAINERS_PATH– optional, base directory for devcontainer templates (defaultdevcontainers).
From the backend directory:
# Ensure DATABASE_URL points to a running Postgres instance
export DATABASE_URL="postgres://user:password@localhost:5432/monza?sslmode=disable"
go run ./cmd/serverBy default, the server listens on http://localhost:8080.
To use a custom address:
SERVER_ADDR=":9000" go run ./cmd/serverIf you want to use a custom devcontainers directory:
DEVCONTAINERS_PATH="/absolute/path/to/devcontainers" go run ./cmd/serverOn startup the server will:
- Run database migrations (with retry) against
DATABASE_URL. - Connect to PostgreSQL.
- Create a Docker client.
- Start the HTTP server and the sandbox cleanup worker.
-
GET
/health-
Description: Liveness / readiness probe.
-
Response
200 OK:{ "status": "ok" }
-
-
GET
/hello-
Description: Simple hello world endpoint.
-
Response
200 OK:{ "message": "hello, world" }
-
Sandboxes are represented as:
{
"id": "00000000-0000-0000-0000-000000000000",
"name": "sandbox-go",
"status": "running",
"container_id": "docker-container-id",
"image": "registry/image:tag",
"workspace_mount": "/path/in/container",
"devcontainer_config": { /* original devcontainer.json as JSON */ },
"env_vars": {
"EXAMPLE": "value"
},
"port_mappings": [
{
"id": "11111111-1111-1111-1111-111111111111",
"sandbox_id": "00000000-0000-0000-0000-000000000000",
"host_port": 12345,
"container_port": 8080
}
],
"last_activity": "2024-01-01T12:00:00Z",
"created_at": "2024-01-01T12:00:00Z",
"expires_at": "2024-01-01T12:15:00Z",
"deleted_at": null
}Key status values:
creating,running,expired,deleted,error.
-
GET
/api/sandboxes-
Description: Returns all known sandboxes.
-
Response
200 OK:[ { "id": "00000000-0000-0000-0000-000000000000", "name": "sandbox-go", "status": "running" // ... see full model above ... } ]
-
Example:
curl -X GET http://localhost:8080/api/sandboxes-
POST
/api/sandboxes-
Description: Creates a new sandbox from a devcontainer template.
-
Request body:
{ "name": "optional-sandbox-name", "template": "go" }template:- Name of a directory under
DEVCONTAINERS_PATH(or localdevcontainers) that containsdevcontainer.json. - Defaults to
"go"if omitted or empty.
- Name of a directory under
name:- Optional; defaults to
"sandbox-" + templateif omitted or empty.
- Optional; defaults to
-
Responses:
201 Createdwith sandbox JSON on success.400 Bad Requestif the body is invalid or the template cannot be loaded.500 Internal Server Errorfor unexpected errors.
-
Example:
curl -X POST http://localhost:8080/api/sandboxes \
-H "Content-Type: application/json" \
-d '{
"name": "sandbox-go",
"template": "go"
}'-
GET
/api/sandboxes/{id}- Description: Fetch a single sandbox by its UUID.
- Path params:
id– sandbox UUID.
- Responses:
200 OKwith sandbox JSON.404 Not Foundif the sandbox does not exist.
Example:
curl -X GET http://localhost:8080/api/sandboxes/<sandbox-id>-
DELETE
/api/sandboxes/{id}- Description: Deletes a sandbox and its underlying Docker container.
- Path params:
id– sandbox UUID.
- Responses:
204 No Contenton success.500 Internal Server Erroron failure.
Example:
curl -X DELETE http://localhost:8080/api/sandboxes/<sandbox-id>-
POST
/api/sandboxes/{id}/heartbeat- Description: Marks the sandbox as active, extending its expiry.
- Path params:
id– sandbox UUID.
- Request body: none.
- Responses:
204 No Contenton success.500 Internal Server Erroron failure.
Example:
curl -X POST http://localhost:8080/api/sandboxes/<sandbox-id>/heartbeat-
POST
/api/sandboxes/{id}/files/upload- Description: Uploads a single file into the sandbox’s container filesystem.
- Path params:
id– sandbox UUID.
- Request:
multipart/form-datawith:file– the file to upload (required).path– destination directory inside the container (optional; default/workspace).
- Responses:
200 OKwith JSON:{"status":"ok","path":"/workspace/filename"}.400 Bad Requestif the sandbox id is invalid, no file is provided, or the request body exceeds the limit (100 MB).500 Internal Server Errorif the sandbox/container is not found or the copy fails.
Example:
curl -X POST http://localhost:8080/api/sandboxes/<sandbox-id>/files/upload \
-F "file=@./main.go" \
-F "path=/workspace"-
GET
/api/sandboxes/{id}/files/download?path=...- Description: Streams a single file from the sandbox’s container to the client.
- Path params:
id– sandbox UUID.
- Query params:
path– full path to the file inside the container (e.g./workspace/main.go) (required).
- Responses:
200 OKwith the raw file content,Content-Disposition: attachment, andContent-Typeset from the file.400 Bad Requestifpathis missing or the sandbox id is invalid.404 Not Foundif the path does not exist or the archive is empty.500 Internal Server Errorif the sandbox/container is not found or the copy fails.
Example:
curl -o main.go "http://localhost:8080/api/sandboxes/<sandbox-id>/files/download?path=/workspace/main.go"High‑level lifecycle (also described in SANDBOX.md):
- Create –
POST /api/sandboxeswith atemplatename. - Provision – backend reads
devcontainers/{template}/devcontainer.json(or fromDEVCONTAINERS_PATH) and creates a Docker container. - Persist – sandbox metadata, ports, and timestamps are stored in PostgreSQL.
- Use – clients connect to mapped ports (e.g. editors, terminals, HTTP services inside the container), and can upload/download files via
POST /api/sandboxes/{id}/files/uploadandGET /api/sandboxes/{id}/files/download. - Heartbeat – clients periodically call
POST /api/sandboxes/{id}/heartbeatto keep the sandbox alive. - Cleanup – background worker expires and deletes sandboxes idle longer than the configured TTL (15 minutes by default), updating their status to
expired/deleted.
cmd/server– main server binary.internal/httpserver– HTTP server setup and routing.internal/handlers– HTTP handlers (/health,/hello,/api/sandboxes, sandbox execute, sandbox files).internal/sandbox– sandbox manager, heartbeat handling, cleanup worker.internal/devcontainer– devcontainer.json parsing.internal/docker– Docker client wrapper.internal/db– DB connection and migrations.internal/db/repository– database repositories.pkg/models– shared domain models (e.g.Sandbox).
Structured Go HTTP server with:
/health– health check endpoint returning{"status":"ok"}./hello– hello world endpoint returning{"message":"hello, world"}.
cmd/server– main entrypoint binary.internal/httpserver– HTTP server wiring and routing.internal/handlers– individual HTTP handlers and response helpers.
- Go 1.22 or later
cd backend
go run ./cmd/serverBy default the server listens on http://localhost:8080.
You can override the address with the SERVER_ADDR environment variable, for example:
SERVER_ADDR=":9000" go run ./cmd/servercurl http://localhost:8080/health
curl http://localhost:8080/hello