Why not Docker on every request?
Container startup latency (300–800ms) is unacceptable for an interactive editor. We use Node.js child_process.spawn with strict constraints instead, reserving Docker for long-running or high-privilege execution.
The execution contract
Every run gets: 8-second wall-clock timeout (SIGTERM → SIGKILL), 50KB stdout/stderr cap, a temp directory cleaned up post-execution, and no network access (resolved at OS level). Language processes are spawned as a restricted user.
const child = spawn(cmd, args, {
timeout: 8000,
cwd: tmpDir,
env: {
PATH: SAFE_PATH,
HOME: tmpDir,
// no network, no secrets
},
})
child.stdout.on('data', (d) => {
output += d
if (output.length > 50_000) child.kill('SIGKILL')
})Supporting 8 languages
Python (3.12), JavaScript (Node 22), TypeScript (ts-node), Go (1.22), Rust (1.77), Java (21), C++ (g++14), Bash. Each language has its own runner config — compilation languages get a compile step with separate timeout, then execution.