Architecture
Overviewβ
HTTP client / CLI / SDK
β
βΌ
βββββββββββββββββββββββββββββββββ
β rc (host: macOS or Linux) β
β ββ domain: validates job/specβ
β ββ pool: ready VMs per β
β β language (jobs only) β
β ββ machines: registry, disk β
β ββ launcher: spawns the vmm β
ββββββββββββββββ¬βββββββββββββββββ
β spawn (never fork)
βΌ
βββββββββββββββββββββββββββββββββ
β rc vmm --spec (1 per VM) β
β libkrun (purego) + β
β libkrunfw (Linux kernel) β
β HVF on macOS / KVM on Linux β
β network gateway (machines β
β with --net), screen viewer β
ββββββββββββββββ¬βββββββββββββββββ
β Unix socket β vsock port 5000
βΌ
βββββββββββββββββββββββββββββββββ
β microVM β
β job: read-only ext4 root, β
β /tmp tmpfs, no network β
β machine: image overlay + β
β own disk (/dev/vdb) β
β /opt/runner/guest (static) β
βββββββββββββββββββββββββββββββββ
Binariesβ
| Binary | Where it runs | What it does |
|---|---|---|
rc | host | run, shell, machine ..., pack ..., serve (API + pool), image build/prune, languages, doctor and the internal vmm subcommand |
runner-guest | inside the VM | listens on vsock port 5000 and serves each operation: running code, exec, file copy, PTY, workload, published port, secrets, SSH agent, S3 mount. Stored as bin/rc-guest-linux-<arch> |
There are two binaries because purego forces dynamic linking against glibc on Linux, and the language images are Alpine
(musl). A dynamic guest fails inside the VM with not found; the guest is compiled statically, and scripts/build.sh
fails if it comes out dynamic.
Host layersβ
The host code follows a hexagonal architecture; cmd/rc only wires the pieces together.
| Layer | Package | Responsibility |
|---|---|---|
| domain | internal/domain | job, result, limits (stdin, files, size, timeout) and E00x errors |
| application | internal/app | job executor and pool; machine lifecycle, branch, staged volumes, credentials, secrets |
| ports | internal/ports | interfaces (VMSpec and its validation, launcher, catalog, images) |
| adapters | internal/adapters/krun | libkrun loading, VM configuration, launcher, vmm process and confinement on Linux |
internal/adapters/oci | OCI image pull and ext4 build in the build VM | |
internal/adapters/catalog | embedded languages.yaml | |
internal/adapters/guestproto | vsock protocol client | |
internal/adapters/httpapi | HTTP API (jobs and machines) | |
internal/adapters/machinestore | each machine's registry and disk (a clone of the base disk) | |
internal/adapters/runfile | reading the runner.code file (TOML) | |
internal/adapters/netgw | userspace network gateway: egress policy, DNS, ports, ICMP, named networks, credential interception | |
internal/adapters/screen, viewer | the VM's screen in the browser | |
internal/adapters/hostfs | host side of volumes (file modes for :staged) | |
| others | internal/credca, internal/objectstore, internal/s3fs, internal/pack | per-machine CA, S3 client, core of the s3:// volume, pack format |
| guest | internal/guest | vsock server and per-language executor |
SDKs: pkg/client (Go) and sdk/python (Python), standard library only.
Path of a job through the APIβ
POST /api/v1/runreacheshttpapi, which builds the job.- The domain validates it: language in the catalog (otherwise E006), stdin β€ 1,000,000 bytes (otherwise E005), up to 32 files, total code β€ 4 MiB, valid file names, timeout between 0 and 300 s. A rejection becomes HTTP 400.
- The pool hands over an already-booted VM for that language. If the pool is empty, it boots a VM on the spot (cold boot): it never refuses.
- The host connects to the Unix socket bound to the VM's vsock port 5000 and sends the job as a frame (4-byte length + JSON).
- The guest writes the files to a directory under
/tmp, compiles if the language requires it, runs with the job's stdin and timeout, and replies with another frame. - The VM is destroyed (SIGKILL on the process group) and the pool replenishes a new VM in the background.
- The API returns
status,stdout,stderr,exception,executionTime.
Path of a machineβ
rc machine createwrites the registry to<cache>/machines/<nome>/machine.jsonand an ext4 disk cloned from the base disk (clonefile on APFS, FICLONE on btrfs/XFS, a sparse copy everywhere else).rc machine startlaunchesrc vmmdetached (its own session, with no tie to the parent): the machine stays up after the command exits. The pid lives invmm.pid.- The VM's first process is
/opt/runner/machine.sh: it mounts the disk at/persist, an overlayfs with the image underneath andupper/on top, binds/workspaceand runs the guest in achroot. exec,shell,cp,logstalk to the guest through the same port 5000 socket. With--net, the VM getseth0and traffic goes through the gateway in thevmmprocess itself, which applies the egress policy.rc machine stopasks the guest toshutdown; the guest runssyncand exits; if it does not finish within--timeout, SIGKILL.
Details in machine and Network and volumes.
VM lifecycleβ
- The launcher creates the
rc vmm --spec <file>process by spawn. A fork of a multi-threaded process is killed by Hypervisor.framework, which is why fork is never used. - The
vmmprocess loads libkrun, configures the VM from the spec and callskrun_start_enter, which takes over the process until the VM ends. - Job timeout: SIGKILL on the process group.
- A job VM dies with its parent:
Pdeathsigon Linux; on macOS, a watch pipe on fd 3. A machine VM is detached. - If the
vmmdies during boot, start ends immediately, quoting the last line ofvmm.logwritten during this start.
Details in libkrun.
Imagesβ
Each stack has a read-only ext4 generated from the official OCI image, with the guest, start.sh and the warmup
baked in. The build runs in a dedicated Alpine VM. See Images.
Configuration through the environmentβ
| Variable | Default | Purpose |
|---|---|---|
RUNNER_LISTEN | 127.0.0.1:8080 | API address (serve) |
RUNNER_CACHE_DIR | ~/Library/Caches/runner-codes (macOS), ~/.cache/runner-codes (Linux) | images, build VM, machines, packs and sockets |
RUNNER_KRUN_LIB | ~/.nix-profile/lib/libkrun.dylib (macOS); on Linux ~/.nix-profile/lib/libkrun.so.1 or the libkrun.so.1 soname | libkrun path |
RUNNER_GUEST_BIN | rc-guest-linux-<arch> next to the host binary | guest injected into the images |
RUNNER_SANDBOX | on | off disables Landlock + seccomp on Linux, for diagnostics only |