Skip to content

Overview

Faber is an HTTP service that runs commands in Linux sandboxes. You send it a list of steps as JSON; it creates a fresh, empty workspace, runs the steps in order, and answers with each task’s stdout, stderr, exit code and resource usage. Nothing survives the request.

It is built for workloads where the code is not yours: grading programming submissions, online judges, running snippets from users or language models, compiling and testing code in a throwaway environment.

client ──HTTP──▶ faber (API process)
│ admission: API key, size and fan-out limits, task slots
▼
jailer (the faber binary re-executed, one per request)
│ new mount, network, UTS and IPC namespaces
│ workspace /faber and /tmp as small tmpfs mounts
│ read-only toolchain paths from the image
▼
task supervisor ──▶ task process
one per task: own PID and user namespace,
own cgroup (memory, pids, cpu),
rlimits, seccomp filter, no capabilities
  1. The API process checks the API key and the request shape, and reserves one task slot per task of the widest step. If no slots are free it answers 503 with Retry-After instead of queueing.
  2. A jailer process is started for the request. It holds none of the service’s memory, environment or file descriptors, runs under a host UID leased to this request, and builds the sandbox root.
  3. Each task runs in its own PID and user namespace and cgroup, with resource limits, a seccomp policy and no capabilities. The steps share the workspace, so a later step sees the files an earlier step wrote.
  4. The results are collected, the sandbox and its cgroups are removed, and the response is sent.

The execution model covers steps, parallel tasks and failure handling. Sandbox and isolation describes the boundary in detail, and how Faber works walks through all of it with diagrams.

For each task: stdout, stderr, exit_code, whether it was replayed from the cache, and stats: peak memory, CPU time, peak process count, wall time, an outcome (exited, timed_out, out_of_memory, policy_violation, …) and whether output was truncated. A task that misbehaves never turns into an HTTP error; the request still succeeds and the task’s outcome tells you what happened.

PieceWhat it is
Faber serviceOne Rust binary (/opt/faber) in the vgfractal/faber image. Listens on port 3000, API under /api/v1
ToolchainsWhatever compilers and interpreters you install in the image. Tasks see them read-only. The Dockerfile builder writes the image for you
Step cacheOptional. Replays deterministic leading steps (typically compilation) instead of running them again; can be shared between instances through Redis
File storeContent-addressed uploads that tasks can reference by ID instead of inlining large inputs
SDKsClients for TypeScript, Rust, Go, Python and PHP
  • Linux 5.8 or newer with cgroup v2 (the unified hierarchy).
  • Rootful Docker (or Kubernetes) able to run a privileged container with the host cgroup namespace and /sys/fs/cgroup mounted read-write. Faber creates namespaces and cgroups itself and refuses to start without them.
  • x86_64 or ARM64. Both are tested on every change.

There is one API key scope: every key holder can run code, read every stored file and clear the cache. Faber does not separate tenants.

  • How Faber works: a request, the sandbox, the limits and the cache, in plain terms and with diagrams.
  • Quick start: run Faber and send a request.
  • Your first task: compile and test a program, field by field.