Skip to content

Repository files navigation

Codize Sandbox

A sandboxed code execution engine.

GitHub ReleaseGitHub Actions Workflow StatusGo Report CardGitHub License

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

RuntimeIdentifier
Node.jsnode
TypeScriptnode-typescript
Rubyruby
Gogo
Pythonpython
Rustrust
Bashbash

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:latestprivileged: truecgroup: hostcommand: ["serve", "--run-timeout", "10", "--compile-timeout", "10"]ports:
- "8080:8080"
$ docker compose up

CLI Flags

FlagDefaultDescription
--port8080 (overridden by PORT env var)Listen port
--run-timeout30Run timeout in seconds
--compile-timeout30Compile timeout in seconds
--output-limit1048576 (1 MiB)Maximum combined output bytes
--max-files10Maximum number of files per request
--max-file-size262144 (256 KiB)Maximum file size in bytes
--max-stdin-size1048576 (1 MiB)Maximum stdin size in bytes (post-decode when base64_encoded is true; wire bytes otherwise)
--max-body-size5242880 (5 MiB)Maximum request body size in bytes
--max-concurrency10Maximum number of concurrent sandbox executions
--max-queue-size50Maximum number of requests waiting in the execution queue
--queue-timeout30Maximum time in seconds a request waits in the execution queue
--metricsfalseEnable 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
  • stdin (optional): stdin payload delivered to the run-step child process only. Compile steps never receive stdin.
    • content (required when stdin is specified): stdin bytes 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,
"duration_ms": 42
}
}
  • 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
    • duration_ms: wall-clock execution time for this step in milliseconds (integer). Measured around the nsjail process lifetime (from successful cmd.Start() to cmd.Wait() returning), so it includes nsjail's own startup/teardown overhead in addition to user code runtime. Always present regardless of status.

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

About

💻 A sandboxed code execution engine.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages