Codize Sandbox
A sandboxed code execution engine.
Codize Sandbox is the code execution engine that powers Codize in production, running arbitrary code safely inside Linux namespace jails (google/nsjail). It exposes an HTTP API to receive code, execute it in an isolated environment, and return the output.
Supported Runtimes
| Runtime |
Identifier |
| Node.js |
node |
| TypeScript |
node-typescript |
| Ruby |
ruby |
| Go |
go |
| Python |
python |
| Rust |
rust |
| Bash |
bash |
Usage
The container must run in privileged mode (required for nsjail to create Linux namespaces) with --cgroupns=host (required for nsjail to manage cgroups for resource limiting).
Docker
$ docker run \
--privileged \
--cgroupns=host \
-p 8080:8080 \
ghcr.io/codize-dev/sandbox:latest serve
Behavior can be customized via CLI flags (see CLI Flags for the full list):
$ docker run \
--privileged \
--cgroupns=host \
-p 8080:8080 \
ghcr.io/codize-dev/sandbox:latest serve --run-timeout 10 --compile-timeout 10
Docker Compose
Create a compose.yml:
services:
sandbox:
image: ghcr.io/codize-dev/sandbox:latest
privileged: true
cgroup: host
command: ["serve", "--run-timeout", "10", "--compile-timeout", "10"]
ports:
- "8080:8080"
$ docker compose up
CLI Flags
| Flag |
Default |
Description |
--port |
8080 (overridden by PORT env var) |
Listen port |
--run-timeout |
30 |
Run timeout in seconds |
--compile-timeout |
30 |
Compile timeout in seconds |
--output-limit |
1048576 (1 MiB) |
Maximum combined output bytes |
--max-files |
10 |
Maximum number of files per request |
--max-file-size |
262144 (256 KiB) |
Maximum file size in bytes |
--max-body-size |
5242880 (5 MiB) |
Maximum request body size in bytes |
--max-concurrency |
10 |
Maximum number of concurrent sandbox executions |
--max-queue-size |
50 |
Maximum number of requests waiting in the execution queue |
--queue-timeout |
30 |
Maximum time in seconds a request waits in the execution queue |
--metrics |
false |
Enable the /metrics endpoint |
API
GET /healthz
Returns the service health status. Intended for load balancer health checks, Docker health checks, and Kubernetes liveness probes.
Response:
{"status":"ok"}
POST /v1/run
Request:
{
"runtime": "node",
"files": [
{
"name": "index.js",
"content": "console.log(\"Hello, World!\")"
}
]
}
runtime (required): one of "node", "node-typescript", "ruby", "go", "python", "rust", "bash"
files (required): array of source files. The first file in the array is used as the entrypoint
name (required): file name
content (required): file content as plain text (default) or Base64-encoded string
base64_encoded (optional, default: false): when true, content is treated as a Base64-encoded string and decoded by the server
Response:
{
"compile": null,
"run": {
"stdout": "SGVsbG8sIFdvcmxkIQo=",
"stderr": "",
"output": "SGVsbG8sIFdvcmxkIQo=",
"exit_code": 0,
"status": "OK",
"signal": null
}
}
compile: compilation result (same schema as run). null for interpreted runtimes (node, ruby, python, bash). When compilation fails, run is null
run: execution result. null when compilation fails
stdout / stderr / output: Base64-encoded output. output is the interleaved combination of stdout and stderr
exit_code: process exit code
status: one of "OK", "SIGNAL", "TIMEOUT", "OUTPUT_LIMIT_EXCEEDED"
signal: signal name if the process was killed by a signal (e.g. "SIGKILL"), null otherwise
GET /metrics
Returns Prometheus text exposition format metrics. Only available when the --metrics flag is enabled.
Response (Content-Type: text/plain; version=0.0.4; charset=utf-8):
# HELP sandbox_concurrency_active Number of requests currently executing.
# TYPE sandbox_concurrency_active gauge
sandbox_concurrency_active 0
# HELP sandbox_queue_length Number of requests waiting in queue.
# TYPE sandbox_queue_length gauge
sandbox_queue_length 0
# HELP sandbox_concurrency_max Configured maximum concurrent executions.
# TYPE sandbox_concurrency_max gauge
sandbox_concurrency_max 10
# HELP sandbox_queue_max Configured maximum queue size.
# TYPE sandbox_queue_max gauge
sandbox_queue_max 50
How It Works
Architecture
POST /v1/run
→ Echo HTTP server (request validation, optional Base64 decoding, write files to tmpdir)
→ nsjail (execute code in a namespace-isolated environment)
→ Return response
Sandbox Isolation
Code is isolated by google/nsjail with multiple layers of defense:
- Linux namespaces: PID, network, mount, UTS, IPC, and cgroup namespaces are all isolated. External network access is completely blocked, and loopback communication is also disabled.
- UID/GID mapping: Sandboxed processes run as nobody (65534). Only a single UID is mapped, making setuid impossible.
- Filesystem restrictions: Only the minimum required paths are mounted (shared libraries, device files, user code directory). Everything except the user code directory is read-only.
/tmp is a 64 MiB tmpfs mounted with noexec.
- Resource limits: Execution time is enforced by nsjail's
--time_limit and --rlimit_cpu. Cgroups limit PID count, memory, and CPU usage. Rlimits constrain stack size and other per-process resources.
- Seccomp-BPF: Dangerous syscalls (io_uring, bpf, mount, ptrace, unshare, etc.) are blocked at the kernel level. Clone calls with namespace creation flags are also blocked.
- Output limits: The process is killed if the combined stdout and stderr exceeds the configured limit.
License
MIT