Runner
Included from Basic tier
The runner as the fleet/admin API serves it: one enrolled runner identity with its advertised capability, group placement, liveness, drain state and current jobs.
The v2 OpenAPI document declares this resource's schema; the resource-field coverage gate holds this page to it.
Two endpoints serve it (both admin-token gated):
GET /api/runners— the list view:{"runners": [...], "server_version": ..., "server_commit": ...}, one row per enrolled, unrevoked runner, with the server's own build identity alongside (empty on an unstamped build).GET /api/runners/{id}— the detail view: the list row plus registration identity, the self-reported host inventory and the server-observed peer address. Revoked ids answer 404 exactly like unknown ones — the revocation tombstone is never exposed.
The registry behind both is durable (DBRegistry) or in-memory
(MemRegistry) depending on composition; the payload shape is the same.
Narrative on enrolment, liveness and drain lives at
opschain docs runner-registry.
List row fields
| Field | Type | Meaning |
|---|---|---|
id | string | The registration id (an ocr_… credential minted at enrolment). The Ed25519 keypair, not the name, is the identity: re-enrolling the same key supersedes the previous registration under a new id. |
name | string | Display label from registration. Not unique. |
group | string | The runner group the registration token placed it in. |
labels | string array | Capability labels advertised at registration. Absent when empty. |
os | string | Self-reported OS, from registration. Absent when empty. |
arch | string | Self-reported architecture, from registration. Absent when empty. |
runner_version | string | The runner binary's version, read from the host inventory (host.runner_version); absent when no inventory was reported. |
runner_commit | string | The runner binary's commit, read from the host inventory (host.runner_commit); absent when no inventory was reported. |
version_skew | string | The runner's build identity against the server's own: none (they match), skewed (they differ — commits compare for identity, never for order), or unknown (the runner reported no identity — a pre- binary — or the server has none). A skewed or unknown runner also records one warn:runner:version_skew event per (runner, version-pair) at registration and session establishment. |
executors | string array | Executor kinds the runner advertises (e.g. hostexec, containerexec). Absent when empty. |
ephemeral | bool | The registration token's ephemeral flag. Absent when false. |
online | bool | Computed at read time: stopped_at is null AND last_seen_at is present AND younger than the 3-minute liveness window (fleetOfflineAfter). |
last_seen_at | timestamp | The runner's last poll, recorded by TouchRunner. Absent until the runner has polled. |
stopped_at | timestamp | Set by the runner's graceful goodbye; cleared by its next poll. Absent while running. |
draining | bool | Effective drain: an operator drain of the runner or of its group is in force. |
drain_requested_at | timestamp | When the runner's own drain was requested (POST /api/runners/{id}/drain). Absent when no per-runner drain is in force; a group drain does not set it. |
group_draining | bool | A drain of the whole group is in force (POST /api/runner_groups/{name}/drain). Absent when false. |
current_jobs | array | The runner's in-flight job bindings (below). Absent when idle. |
group_attributes | string map | The stored group's placement attributes, echoed for selector debugging. Absent when the group row has none (or no group row exists). |
group_exclusive | bool | The stored group's exclusive flag — the runner only receives work targeted at its group. Absent when false. |
current_jobs entries
| Field | Type | Meaning |
|---|---|---|
run_id | string | The owning run (change or workflow run). |
step_id | string | The dispatched step. |
attempt | int | The attempt number of the lease. |
acked | bool | Whether the runner has acknowledged delivery of the job. |
action | string | The owning run's action — enrichment from the run record; absent when the run cannot be read (the ids still render). |
target_path | string | The owning run's target node path — same enrichment and absence rule. |
Detail-only fields (GET /api/runners/{id})
| Field | Type | Meaning |
|---|---|---|
registered_at | timestamp | When the identity enrolled. Always present. |
observed_remote | string | The server-observed peer address of the runner's connection, refreshed on poll. Absent when never observed. |
host | object | The runner's self-reported host inventory, captured at (re-)registration and refreshed on the session-open/poll rail: hostname, addresses ([{ip, mac}]), cpu_count, go_version, runner_version, runner_commit, pid, started_at, state_dir — every member omitted when empty. Absent when nothing was reported. Admin-surface data only; never logged. |
group_bindings | string array | The stored group's node-path bindings. Absent when the group row has none or does not exist. |
public_key_fingerprint | string | SHA256: + base64 (raw, std) of the SHA-256 of the runner's Ed25519 public key. The key itself is never rendered. Absent when no key is stored. |
State vocabulary
There is no single state field; a runner's condition is read from the
booleans above. The placement verdict in the fleet queue names the offline
condition verbatim as "offline (stopped, or silent past the liveness
window)" — i.e. stopped_at set, or last_seen_at null or older than 3
minutes. Offline and draining are separate truths: both can be true at once.
What changes it
- Enrolment (registration token redemption) creates or supersedes the
row and sets identity, capability and
registered_at. - Polling refreshes
last_seen_atandobserved_remote, and clearsstopped_at. - Goodbye sets
stopped_at. POST /api/runners/{id}/drain//resumeset and clear the per-runner drain (eventsinfo:runner:drain_changed).POST /api/runners/{id}/forgetdeletes a stale registration outright (not revocation — the identity may re-register). Refused with 409 while the runner is online or holds a live job binding.- Revocation (runner delete) tombstones the id and key; the runner then vanishes from both views (detail answers 404).
Related pages
opschain docs runner-registry— enrolment, identity and liveness narrative.opschain docs fleet-queue— how the fields above feed placement verdicts.opschain docs platform-halt— the emergency posture that stops dispatch fleet-wide.