Full-stack · 2026

Compilora

Online compiler, DSA curriculum and practice judge with Docker sandboxing

In development · not yet publicly deployed

Overview

  • What: an online compiler, DSA curriculum and practice judge. No installation or signup is required; anonymous use is first class.
  • Hardest problem: running untrusted code safely, with every sandbox restriction built in one place and asserted by a test.
  • How it is validated: 222 backend test functions plus a live integration suite with a hostile-program threat model.

Compilora runs each execution in a Docker sibling container behind a React and Monaco front end and a FastAPI backend. It is a complete local build and is not publicly deployed: the API needs a Docker-capable VPS. The repository is private, so there are no demo or source links. Screenshots on this page come from the local build.

Architecture

Compilora request path A React and Monaco editor calls FastAPI, which calls ExecutionService._docker_argv to start a sibling container with no network, a read-only filesystem, all capabilities dropped, a pid limit of 128, memory equal to memory-swap and a kill timeout. Standard output and error return to the client. The judge batches N inputs into one container. React + Monaco FastAPI ExecutionService._docker_argv sibling container stdout / stderr → response --network none read-only filesystem --cap-drop ALL --pids-limit 128 memory = memory-swap timeout -s KILL judge N inputs in one container
fig. 01 — compilora request path · every run is a locked-down sibling container (local build)

Highlights

  • Sandbox built from one code path, ExecutionService._docker_argv, with a test asserting each flag is present: --network none, read-only root FS, size-capped tmpfs, --cap-drop ALL, no-new-privileges, --user 1000:1000, --init, memory equal to memory-swap, --cpus, --pids-limit 128.
  • In-container timeout -s KILL plus an outer docker rm -f backstop; output capped; argv arrays from a trusted registry so user content is never shell-interpolated; per-IP token buckets plus a global concurrency semaphore, returning 429 with Retry-After.
  • The judge batches a submission into one container (compile once, N inputs), which keeps a 20-test submission at a few seconds instead of a minute of container startups; expected outputs never enter the sandbox.
  • 12 languages across 10 sandbox images plus a runner image: Python 3.12, JavaScript, TypeScript, Java 21, C17, C++20, Go, Rust, Ruby, PHP, SQL and Bash.
  • Content: 13 courses, 253 lessons (including a 64-lesson DSA track), 33 DSA topics with step-through visualisations, 622 problems across 40 topics, 42 multi-file starter templates and 116 curated free resources.
  • Auth: scrypt password hashes, HMAC-signed opaque tokens, server-side session rows, email verification, anti-enumeration errors; every user-owned query filters on the authenticated user id (another user's id is a 404, not a 403), asserted by tests.
  • Admin CMS at /admin with role re-checked server-side on every route; immutable, unlisted-by-default share snapshots; optional AI assistant with tool use and per-user memory.
  • pytest suite of 20 files and 222 test functions, plus a live integration suite that runs every language and a hostile-program threat model (fork bomb, memory bomb, output flood, disk fill, network, timeouts).

Screenshots

Compilora home page with the headline Write. Run. Learn., a code editor preview and the list of twelve supported languages
Home page, local build
Compilora compiler page with a Python file open in the editor beside an empty output panel
Compiler view, local build

What I learned

Funnelling every Docker flag through a single function and asserting each one in a test made the sandbox auditable instead of a pile of scattered options. Batching a judge submission into one container changed the cost of a 20-test run from a minute of container startups to a few seconds.

Limits

Container escape through a kernel or Docker CVE is residual risk; gVisor or Firecracker is the next step. Rate limiting is single-process and in-memory (Redis when replicas appear), and the job store is in-memory.