Docs
Search docs

Register a runner

POST/api/runners

registerRunner

Requires authentication using a bearer token — see tokens and scopes.

curl

curl -X POST 'https://neosource.dev/api/runners' \
  -H 'Authorization: Bearer $NEOSOURCE_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"labels":[],"vm_backend":"string"}'

fetch

fetch("https://neosource.dev/api/runners", {
  method: "POST",
  headers: {
    Authorization: "Bearer $NEOSOURCE_TOKEN",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"labels":[],"vm_backend":"string"}),
});

Request bodyrequired

application/json

RegisterRunnerRequest

object

actor_bound

boolean | null

Bind this runner to the registering account (ADR 0056): it then only claims jobs of runs *triggered by* that account. Needs only workspace membership (workspace-bound registration stays admin-only). The bound account is always the authenticated caller — never chosen by the request. Absent is `false` with a user credential.

ephemeral

boolean | null

Session runner (`neo push --runner`, ADR 0056 §5): registered for one push, deregistered on exit, swept server-side when its presence goes stale. Only meaningful for self-hosted scopes; rejected for hosted registrations. Absent is `false` with a user credential.

host_id

string | null

A stable identity of the registering host (ADR 0146): a UUID the agent persists beside its cache disks and sends at every registration. The server files the host's cache inventory under it, within this registration's workspace and bound account, so the inventory survives a re-registration. Absent is "did not say": the runner is its own host. 400 when not a UUID.

labelsrequired

array

items

string

machine
one of
  • RunnerMachineInfo

    object

    What a runner agent reports about the machine it runs on (issue #777): `POST /api/runners` (`machine`) and `PUT /api/runners/{runner_id}/machine`. Every field is optional. A value of the wrong JSON type (a string for `cpu_cores`, a negative `memory_bytes`) fails request extraction like any other malformed body; a well-typed value is never rejected for its content — strings are trimmed, stripped of control and invisible formatting characters and truncated, and counts are clamped — since this is untrusted tenant-hardware input.

    agent_version

    string | null

    Agent build that reported, e.g. `neo 0.1.0 (abc1234)`.

    arch

    string | null

    CPU architecture as Rust's `std::env::consts::ARCH` spells it (`x86_64`, `aarch64`, …). Open vocabulary — render, don't match.

    cpu_cores

    integer | nullint32

    Logical CPUs available to the agent.

    cpu_model

    string | null

    CPU model string, e.g. `AMD Ryzen 9 7950X 16-Core Processor`.

    memory_bytes

    integer | nullint64

    Total physical memory in bytes.

    name

    string | null

    Display name shown in the web UI and `neo runner list`. Defaults to the machine's hostname agent-side. On a report, `null` keeps the name already stored.

    os

    string | null

    Operating system as Rust's `std::env::consts::OS` spells it (`linux`, `macos`, `windows`, …). Open vocabulary — render, don't match.

  • null

node_id

string | null

The machine's identity within its enrollment (ADR 0153 §4): an opaque string (1–253 printable ASCII characters) the agent persists and reuses, so a re-registration from the same machine replaces its previous runner instead of adding one. Required with an enrollment token; ignored with any other credential.

scope
one of
  • RunnerScope

    string

    Whether a runner is hosted by the platform (per [ADR 0014]) or long-poll-attached from outside.

    "hosted""self_hosted""local"

  • null

vm_backendrequired

VmBackendKind

string

"docker_shell""kata_clh""kata_fc""lib_krun""k8s_runc""cloud_hypervisor"

workspace_id

string | null

Optional workspace association (self-hosted runners typically pin to one).

Responses

201Runner registered

application/json

RunnerResponse

object

actor_id

string | null

Actor binding (ADR 0056): the account whose runs this runner is restricted to, or `null` for workspace-bound / hosted runners.

created_atrequired

integerint64

idrequired

string

labelsrequired

array

items

string

last_seenrequired

integerint64

scoperequired

RunnerScope

string

Whether a runner is hosted by the platform (per [ADR 0014]) or long-poll-attached from outside.

"hosted""self_hosted""local"

statusrequired

RunnerStatus

string

"online""draining""offline"

tokenrequired

string

Per-runner bearer token, `nsr_<hex>`. Returned **exactly once** in the `POST /api/runners` registration response — only its hash is stored server-side, so it can never be re-read. The agent persists it and presents it as `Authorization: Bearer <token>` on every claim / heartbeat / complete / log call.

vm_backendrequired

VmBackendKind

string

"docker_shell""kata_clh""kata_fc""lib_krun""k8s_runc""cloud_hypervisor"

workspace_id

string | null

Standard errors

Bodies documented once for the whole API — see standard errors.

  • 400Bad Request — one of: invalid_input
  • 401Authentication required
  • 403Forbidden — one of: forbidden
  • 404Not Found — one of: not_found
  • 409Conflict — one of: conflict
  • 429Rate limited — retry after the `Retry-After` header
  • 500Internal server error
  • 503Service temporarily unavailable / at capacity — retry after the `Retry-After` header
  • 504Gateway timeout — the request exceeded the server's handling budget