Skip to content

Repository files navigation

Runlet

Minimal REST API for executing single-file code in a sandboxed environment. Supports Python, JavaScript (Node.js), C++, and Java. Built to support CodeAlong. A platform for single file real-time collaborative code editing and execution.

This project was originally inspired by EngineerMan's Piston API. Check out his video breakdown!

This API is free for public use and does not require any authentication, but please be mindful of the rate limits and resource constraints. If you need higher limits or more features, consider hosting your own instance.

Architecture

Architecture

The service is hosted on a Digital Ocean Droplet, where code changes are deployed through CI/CD pipelines managed by GitHub Actions. On the Droplet, Caddy sits in front of the app as a reverse proxy and handles HTTPS automatically, so the app itself only has to speak plain HTTP internally.

The Docker image deployed on the Droplet bakes in the runtimes for every supported language (Python, Node.js, g++, and the JDK), so no language installation happens at request time.

Inside the app container, code isn't run directly on the host or in a per-request Docker container; it runs inside one of the three isolated sandboxes built around isolate, a sandbox originally built for the IOI programming contest.

API Reference

API URL: https://runlet.codealong.live

You can also access the OpenAPI spec at https://runlet.codealong.live/openapi.json or the Swagger UI at https://runlet.codealong.live/docs.

Method Path Description Rate limit
POST /execute Run a single file of code 10/minute
GET /runtimes List supported languages and versions 10/minute
GET /health Health check 10/minute

POST /execute

Runs one submission inside a sandbox and returns its result.

Request schema:

{
  "language": "python | javascript | cpp | java",
  "code": "string",
  "stdin": "string, optional, defaults to \"\""
}

Response schema:

{
  "status": "OK | TLE | MLE | RE | OLE | CE",
  "stdout": "string",
  "stderr": "string",
  "time": "float | null, seconds",
  "memory": "int | null, KB"
}

Example request:

curl -X POST https://runlet.codealong.live/execute \
  -H "Content-Type: application/json" \
  -d '{
    "language": "python",
    "code": "print(input())",
    "stdin": "hello"
  }'

Example response:

{
  "status": "OK",
  "stdout": "hello\n",
  "stderr": "",
  "time": 0.031,
  "memory": 8192
}

status values:

Status Meaning
OK Ran successfully
TLE Time limit exceeded
MLE Memory limit exceeded (only enforced when USE_CGROUPS=true)
RE Runtime error (non-zero exit, signal, etc.)
OLE Output limit exceeded (stdout/stderr exceeded OUTPUT_LIMIT)
CE Compile error (C++ and Java only)

Other responses:

  • 422 — body failed validation (e.g. language isn't one of the four supported values, or code/stdin exceed CODE_LIMIT/STDIN_LIMIT):

    {
      "detail": [
        {
          "type": "string_too_long",
          "loc": ["body", "code"],
          "msg": "String should have at most 1024 characters",
          "input": "...",
          "ctx": { "max_length": 1024 }
        }
      ]
    }
  • 500 — the sandbox itself failed to run the submission, {"detail": "<error>"}

GET /runtimes

Lists the language runtimes baked into the Docker image. No request body.

Response schema:

[
  {
    "language_name": "string",
    "language_version": "string"
  }
]

Example request:

curl https://runlet.codealong.live/runtimes

Example response:

[
  { "language_name": "Python", "language_version": "3.13.14" },
  { "language_name": "JavaScript (Node.js)", "language_version": "20.19.2" },
  { "language_name": "C++ (g++)", "language_version": "14.2.0" },
  { "language_name": "Java", "language_version": "21.0.11" }
]

GET /health

Health check. No request body.

Example request:

curl https://runlet.codealong.live/health

Example response:

{ "status": "healthy" }

Local Development

Make sure you have the following installed:

  1. Clone the repo and install dependencies (needed for running tests and lint/format locally; the API itself runs inside Docker)
git clone https://github.com/GiridharRNair/Runlet.git
cd Runlet
uv sync
  1. Start the API in development mode
docker compose -f docker-compose.dev.yml up

The container mounts the repo into /app and runs fastapi dev, so the API hot-reloads on code changes. It'll be available at http://localhost:8000.

  1. Verify it's up
curl http://localhost:8000/health
  1. Stop the API
docker compose -f docker-compose.dev.yml down

Note

docker-compose.dev.yml overrides two environment variables from their production defaults:

  • USE_CGROUPS=falseIsolate uses cgroups, a Linux kernel feature, to enforce memory limits, but Docker Desktop on macOS (used for local development) doesn't expose cgroup control the way a native Linux host does. With cgroups disabled, memory limits aren't enforced and MLE is never returned locally. In production, USE_CGROUPS is set to true and enforces memory limits normally.

  • CODE_EXECUTION_RATE_LIMIT=100 — a looser rate limit than production's 10/minute, so manual testing and the test suite don't get throttled.

The container also runs with privileged: true, which isolate needs to set up its sandboxes. Docker Desktop allows this by default.

Configuration

Set via environment variables — see app/config.py:

Variable Default Description
TIME_LIMIT 5.0 Execution wall time limit (seconds)
MEMORY_LIMIT 256 Execution memory limit (MB)
COMPILE_TIME_LIMIT 30.0 Compile step time limit (seconds), C++/Java
COMPILE_MEMORY_LIMIT 512 Compile step memory limit (MB), C++/Java
OUTPUT_LIMIT 1 Max stdout/stderr a submission may write (KB), enforced via isolate's --fsize
CODE_LIMIT 1 Max size of the code field accepted by /execute (KB)
STDIN_LIMIT 1 Max size of the stdin field accepted by /execute (KB)
MAX_BOXES 3 Number of concurrent isolate sandboxes
USE_CGROUPS true Enforce memory limits via cgroups (disabled in docker-compose.dev.yml)
CODE_EXECUTION_RATE_LIMIT 10 Requests per minute allowed to /execute per IP

Tests and other commands

Common tasks are run through Poe the Poet. Task definitions are in pyproject.toml.

uv run poe format                  # format code with ruff
uv run poe lint                    # lint code with ruff
uv run poe test_python             # run Python language tests
uv run poe test_js                 # run JavaScript language tests
uv run poe test_cpp                # run C++ language tests
uv run poe test_java               # run Java language tests
uv run poe test_all_langs          # run all language tests
uv run poe test_memory_limit       # run memory limit tests against the local API
uv run poe test_prod_memory_limit  # run memory limit tests against the production API
uv run poe test_input_limit        # run code/stdin size limit tests

The tests hit a running instance of the API over HTTP. They use the API_URL environment variable, defaulting to http://localhost:8000 if it isn't set, so start the API locally first (see "Local Development" above) before running them.

License

This project is licensed under the MIT License.

About

REST API for sandboxed code execution

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Contributors

Languages