otari-sandbox-container
August 10, 2026 · View on GitHub
Code-execution sandbox container — a Python REPL behind a multi-session HTTP
API. Pairs with mozilla-ai/gateway to
provide code_execution tool support, but the contract is plain HTTP so any
client that speaks it can use the container directly.
Built images are published to Docker Hub:
mzdotai/otari-sandbox-container:latest
mzdotai/otari-sandbox-container:<sha>
Each session has its own /var/sandbox/sessions/<id>/ tree with a private
workspace and Python REPL subprocess. State (variables, imports) persists
across /exec calls within a session and is wiped on DELETE /sessions/{id}.
The wire shapes returned by POST /exec match Anthropic's
code_execution_20250825 content blocks (code_execution_tool_result,
bash_code_execution_tool_result, text_editor_code_execution_tool_result)
so consumers that already parse Anthropic shapes work without translation.
Layout
sandbox/
models.py # Pydantic shapes (request, result blocks)
runner.py # Long-lived Python REPL with sentinel protocol
exec_server.py # FastAPI app: /sessions, /exec, /files, /health
text_editor.py # view/create/str_replace/insert/undo_edit handlers
tests/
Dockerfile # python:3.12-slim base, pinned package set
Makefile # build, test, run
Local development
make install # uv sync
make test # run pytest
make build # build the Docker image (otari-sandbox-container:dev)
make run # docker run -p 8080:8080
Pulling the published image
docker pull mzdotai/otari-sandbox-container:latest
docker run --rm -p 8080:8080 mzdotai/otari-sandbox-container:latest
API
POST /sessions -> create session
GET /sessions/{id} -> session metadata
POST /sessions/{id}/exec -> run code in session
DELETE /sessions/{id} -> destroy session
GET /sessions/{id}/files -> download a file from the workspace
GET /sessions/{id}/files/list -> list workspace files
GET /health -> 200 if server alive
A streaming exec endpoint (SSE stdout/stderr deltas) is a planned follow-up;
the current /exec is request/response only.
The full request/response schemas live in sandbox/models.py.
Observability
Telemetry export (traces, metrics, logs) is off by default — this image is pulled and run by arbitrary third parties, so it should never phone home unless an operator explicitly opts in. Structured JSON logging is available independently of telemetry export.
| Env var | Default | Description |
|---|---|---|
TELEMETRY_ENABLED | false | Enables OTLP export of traces, metrics, and logs. |
TELEMETRY_SERVICE_NAME | otari-sandbox-container | OTel service.name resource attribute. |
TELEMETRY_OTLP_ENDPOINT | http://localhost:4318 | Base OTLP/HTTP endpoint; /v1/traces, /v1/metrics, /v1/logs are appended per signal. |
TELEMETRY_OTLP_HEADERS | (unset) | Raw OTLP exporter headers, e.g. an auth header for a hosted collector. Leave unset for collectors that don't require auth. |
ENVIRONMENT | production | Reported as the deployment.environment resource attribute and in JSON log lines. |
LOG_FORMAT | text | text or json. |
LOG_LEVEL | INFO | DEBUG, INFO, WARNING, ERROR, or CRITICAL. |
Enabling telemetry requires an OTLP/HTTP-compatible collector reachable at
TELEMETRY_OTLP_ENDPOINT. Deploying an actual collector (as a sidecar,
a cluster-level agent, or a hosted endpoint) is the responsibility of
whatever deployment runs this image — this repo only emits telemetry, it
doesn't provision anywhere to send it.
Security notes
- The container runs as a non-root user (uid 1000).
- One container hosts many isolated sessions; sessions can't see each other's workspaces.
/var/sandbox/sessionsis intended to be mounted as tmpfs in production so session state stays ephemeral.- Don't expose the HTTP port to untrusted networks. There's no
authentication on the API — the assumption is that the gateway (or
another trusted component) is the sole client, reached over a private
Docker network or similar. The gateway's
docker-compose.ymlbinds the sandbox port to127.0.0.1for this reason.
License
Apache 2.0. See LICENSE.