HTTP surfaces
Native run clients use
POST /api/v1/runs; the CLI reads the run from GET /api/v1/runs/{run_id} and can stream events.ndjson, logs, or live logs. The runner-facing results and broker routes use job-scoped credentials rather than the CLI’s native credential.
Authentication boundaries
- Engine/system token: the administrator credential used by the native API and system-only routes. It is supplied through the configured credential store or
PRELOOP_SYSTEM_TOKEN. - Local JWTs: the engine signs short-lived runner and job credentials with its HMAC key. Set
PRELOOP_HMAC_KEYto the same 32-byte hex key on every node sharing a database; otherwise a node persists a generated key in its state directory. - Runner registration: registration accepts the system credential, a RunnerManage local JWT, or a short-lived pool provision token. The provision token is consumed when the runner identity is created.
- Runner listen token: identifies a registered runner for session and message traffic. A job’s runtime JWT is not accepted for listener-only acquisition.
- Job runtime and results tokens: the acquired job carries a token for renew/complete, repository reads, OIDC requests, and Results/Twirp calls. Results routes accept only the system credential or a job-scoped results identity.
- Webhooks: GitHub webhook requests must carry a valid HMAC
X-Hub-Signature-256. With multiple GitHub Apps, the signature is also checked against the installation that covers the claimed repository.
The ControlBackend contract
The control database is authoritative for durable runs, jobs, runner records, sessions, queue claims, webhook deliveries, check-run projections, timelines, and retention metadata. HTTP handlers map requests to typed ControlBackend commands; they do not hold SQL transactions or maintain a process-local working copy of durable state.
The backend runs one short transaction per command. Conditional updates use the affected-row count as the race result. Postgres claims queue rows with FOR UPDATE SKIP LOCKED, while both backends call the same backend-neutral scheduling logic so dependency, concurrency, and runner-matching decisions stay consistent.
preloop serve initializes a brand-new local store (absent or empty) and otherwise checks the migration ledger, refusing an older, newer, incomplete, divergent, or incompatible control schema rather than migrating it; run preloop store migrate (see Storage) to upgrade an existing store. The supported first-run paths are preloop serve on a fresh $PRELOOP_HOME, preloop init, and preloop server install. Postgres lets several engine nodes share durable run and queue state. Node-local state still includes live log feeds, cache/artifact reservations, runner liveness observations, debug sessions, and the App-setup session (github_app_setup/session.rs: one live setup at a time, its one-time token and state nonce held in memory), so a shared database alone does not make every HTTP surface active-active.
Transactional events and check runs
A run or job mutation and its versioned event are committed together inoutbox_events. The check-run projector owns a durable consumer_offsets bookmark, ignores older versions, and coalesces the desired state for each run/job into check_run_updates.
preloop_events wake-up channel; SQLite and the recovery path use a local dirty signal plus polling, so a lost notification does not lose an event.
OIDC and scheduling concurrency
The server signs GitHub-compatible OIDC ID tokens with a persisted RSA keypair and exposes discovery and JWKS documents. Tokens are short-lived (300 seconds). The issuer defaults to the runner base URL with/oidc appended; set an explicit issuer when a relying service needs a stable public URL. A job must have id-token: write permission before the runner can request one.
Workflow and job concurrency: groups are evaluated by the control plane. The default single queue keeps one pending holder and replaces an older pending holder; max allows up to 100 pending holders in FIFO order. Runners do not implement this policy: they receive cancellation or dispatch decisions from the control plane.