Skip to content

codize-dev/sandbox

Repository files navigation

Codize Sandbox

A sandboxed code execution engine.

GitHub Release GitHub Actions Workflow Status Go Report Card GitHub 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

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

About

πŸ’» A sandboxed code execution engine.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors