Skip to content
Faber's mark, an ember-orange faceted fox mid-stride

Run untrusted code in a sandbox, over HTTP

Faber gives every request its own Linux namespaces, cgroup v2 limits and seccomp policy, runs your steps, and answers with each task's output, exit code and resource use.

One request

Send the steps as JSON. Faber runs them in order in one throwaway workspace, runs each parallel step’s tasks side by side, and reports every task separately. A crash, a timeout or an out-of-memory kill is a result, not an HTTP error.

Terminal window
curl -s http://localhost:3000/api/v1/execute \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"stop_on_failure": true,
"steps": [
{ "cmd": "gcc", "args": ["-O2", "-o", "main", "main.c"], "cache": true,
"files": { "main.c": "#include <stdio.h>\nint main(void){int a,b;scanf(\"%d %d\",&a,&b);printf(\"%d\\n\",a+b);}\n" } },
[
{ "cmd": "./main", "stdin": "1 2\n", "sandbox_profile": "native_v2" },
{ "cmd": "./main", "stdin": "40 2\n", "sandbox_profile": "native_v2" }
]
]
}'

The compile step was replayed from the step cache ("cached": true); the tests ran. Stats are trimmed here.

What you get

A sandbox per request

Mount, network, UTS and IPC namespaces per request, PID and user namespaces per task, a seccomp allowlist, no capabilities. Tasks see your toolchains read-only and nothing else of the host.

Limits that hold

Memory, processes, CPU, wall time and output per task; a deadline and an output budget per request; a fixed number of task slots per instance. Excess load gets 503 and Retry-After, not a queue.

A step cache

Mark the deterministic leading steps cacheable and Faber replays them with their workspace, from memory or from a Redis shared by every instance.

Lean images

Start from vgfractal/faber:base and add only the languages you serve. The Dockerfile builder writes the image for you.

Next