Vetrix Docs

Admin

Reference for the instance-administration endpoints under /api/v1/admin: user accounts, user groups, instance settings, delegated admin-scope grants, the instance audit log, system health, CI runners and runner hosts, and rate-limit administration. Every endpoint on this page requires an instance administrator.

Resource overview

Administration endpoints hang off /api/v1/admin/.... This page covers:

  • users — list, fetch, create, update, and delete accounts, plus password reset, token revocation, repository-ownership listing, user impersonation, and the registration-approval queue;
  • user groups — the seeded and custom groups that carry a default role and rate-limit tier, and their membership;
  • settings — the instance configuration key/value store;
  • admin-scope grants — delegating individual admin capabilities to non-super-admin operators;
  • audit log — the append-only record of administrative and state-changing actions;
  • system health and logs — a point-in-time snapshot of database, disk, runner, and queue health, plus listing and rotating the server log files;
  • CI runners and runner hosts — registering and managing CI runners and the CICD runner-host pool;
  • rate-limit administration — viewing and adjusting the per-group budgets and the per-repository, per-application, per-token, and per-user overrides.

Repository transfer endpoints also live under /api/v1/admin (/api/v1/admin/repos/{owner}/{repo}/transfer and /api/v1/admin/transfers). They are documented with the repository resource on repos.md. The budgets and behavior of the rate limiter itself are described in rate-limits.md; this page documents only the endpoints that read and change those limits.

Auth & scopes

See conventions.md for the accepted credential types. Access to these endpoints is gated two ways, in order:

  1. The request must be authenticated. An unauthenticated request is answered 401 Unauthorized.
  2. The caller must be an instance administrator. A user holding the super-admin flag (is_admin) passes every check. A non-super-admin must hold the specific admin scope the endpoint requires, delegated through an admin-scope grant; otherwise the response is 403 Forbidden with the body { "error": "admin scope required: <scope>" }.

The admin scopes used on this page:

Scope Gates
admin:users User accounts and user groups
admin:impersonation Impersonating a user (in addition to admin:users)
admin:settings Instance settings
admin:system System health and log management
admin:roles Admin-scope grants (delegation)
admin:runners CI runners and runner hosts
admin:rate_limit Rate-limit configuration and overrides
admin:audit_read Audit-log access-drop counter on system health

The impersonation endpoints compose two scopes: a non-super-admin caller must hold both admin:users and admin:impersonation. A super-admin satisfies both. When a scope is missing the response is 403 Forbidden naming the first missing scope.

The instance audit log itself (/api/v1/admin/audit-log) requires the super-admin flag rather than a delegatable scope. Granting admin:roles to another user is restricted to a super-admin, so a scoped administrator cannot bootstrap their own privileges.

Endpoints

Method Path Summary
GET /api/v1/admin/users List users (paginated)
POST /api/v1/admin/users Create a user
GET /api/v1/admin/users/{uid} Fetch a user
PATCH /api/v1/admin/users/{uid} Update a user
DELETE /api/v1/admin/users/{uid} Delete a user
POST /api/v1/admin/users/{uid}/password Set a user's password
POST /api/v1/admin/users/{uid}/revoke-tokens Revoke a user's tokens
GET /api/v1/admin/users/pending List accounts awaiting approval
POST /api/v1/admin/users/{uid}/approve Approve a pending account
POST /api/v1/admin/users/{uid}/reject Reject a pending account
GET /api/v1/admin/users/{uid}/repos List a user's repositories
POST /api/v1/admin/users/{uid}/impersonate Start impersonating a user
DELETE /api/v1/admin/users/{uid}/impersonate Stop impersonating a user
GET /api/v1/admin/user-groups List user groups
POST /api/v1/admin/user-groups Create a user group
GET /api/v1/admin/user-groups/{id} Fetch a user group
PATCH /api/v1/admin/user-groups/{id} Update a user group
DELETE /api/v1/admin/user-groups/{id} Delete a user group
GET /api/v1/admin/user-groups/{id}/members List group members
POST /api/v1/admin/user-groups/{id}/members Add a group member
DELETE /api/v1/admin/user-groups/{id}/members/{uid} Remove a group member
GET /api/v1/admin/settings List all settings
GET /api/v1/admin/settings/{key} Read one setting
PUT /api/v1/admin/settings/{key} Write one setting
POST /api/v1/admin/settings Write several settings at once
GET /api/v1/admin/admin-scopes List grants for one scope (paginated)
GET /api/v1/admin/users/{uid}/admin-scopes List a user's scope grants
POST /api/v1/admin/users/{uid}/admin-scopes Grant a scope to a user
DELETE /api/v1/admin/users/{uid}/admin-scopes/{scope} Revoke a scope
GET /api/v1/admin/audit-log List audit-log entries (paginated)
GET /api/v1/admin/audit-log/{id} Fetch one audit-log entry
GET /api/v1/admin/health System health snapshot
GET /api/v1/admin/logs List server log files
POST /api/v1/admin/logs/rotate Rotate server log files
GET /api/v1/admin/runners List CI runners
POST /api/v1/admin/runners Register a CI runner
DELETE /api/v1/admin/runners/{id} Delete a CI runner
POST /api/v1/admin/runners/{id}/rotate-token Rotate a runner's token
GET /api/v1/admin/hosts List runner hosts (paginated)
GET /api/v1/admin/hosts/{id} Fetch one runner host
GET /api/v1/admin/rate-limits Read the per-group budget matrix
PATCH /api/v1/admin/rate-limits/groups/{group_id}/{scope} Update one group/scope budget

Users

GET /api/v1/admin/users

List user accounts, paginated. Requires admin:users.

Query parameters

Name Type Default Description
q string Filter by username, email, or display name.
page integer 1 Page number. Translated to limit/offset internally.
per_page integer 50 Page size.
limit integer Alternative to per_page; explicit limit/offset take precedence when supplied.
offset integer Zero-based row offset.

Response

{
  "users": [
    {
      "id": "<uuid>",
      "username": "alice",
      "email": "alice@example.com",
      "display_name": "Alice",
      "is_admin": false,
      "status": "active",
      "created_at": "2026-01-04T12:00:00Z",
      "last_active_at": "2026-02-19T09:31:00Z"
    }
  ],
  "total": 137,
  "limit": 50,
  "offset": 0
}

status is active or suspended. last_active_at is null for an account that has never been updated since creation.

Status codes

Status When
200 OK Page returned.
401 Unauthorized No valid credential.
403 Forbidden Caller is not an administrator and lacks admin:users.

Example

curl -H "Authorization: Bearer <token>" \
  "https://api.gitvetrix.com/api/v1/admin/users?q=alice&page=1&per_page=50"
POST /api/v1/admin/users

Create a user account. Requires admin:users.

Request body

Field Type Required Description
username string yes Account username.
email string yes Account email.
password string yes Initial password.
display_name string no Display name.
is_admin boolean no Grant the super-admin flag. Defaults to false.
{
  "username": "bob",
  "email": "bob@example.com",
  "password": "...",
  "display_name": "Bob",
  "is_admin": false
}

Response

201 Created with the user record (the same shape as the fetch response below).

Status codes

Status When
201 Created User created.
400 Bad Request Invalid body, or username, email, or password is missing.
403 Forbidden Caller lacks admin:users.

Example

curl -X POST -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"username":"bob","email":"bob@example.com","password":"..."}' \
  https://api.gitvetrix.com/api/v1/admin/users
GET /api/v1/admin/users/{uid}

Fetch one user account. Requires admin:users.

Path parameters

Name Type Description
uid string User UUID.

Response

{
  "id": "<uuid>",
  "username": "alice",
  "email": "alice@example.com",
  "display_name": "Alice",
  "is_admin": false,
  "status": "active",
  "created_at": "2026-01-04T12:00:00Z",
  "last_active_at": "2026-02-19T09:31:00Z"
}

Status codes

Status When
200 OK User returned.
400 Bad Request uid is not a valid UUID.
404 Not Found No user with that id.

Example

curl -H "Authorization: Bearer <token>" \
  https://api.gitvetrix.com/api/v1/admin/users/<uuid>
PATCH /api/v1/admin/users/{uid}

Update a user account. Requires admin:users. Every field is optional; only the fields supplied are changed.

Request body

Field Type Description
email string New email.
display_name string New display name.
is_admin boolean Set or clear the super-admin flag.
is_active boolean Activate or suspend the account.
status string active or suspended; an alternative to is_active.
{ "status": "suspended" }

Response

200 OK with the updated user record.

Status codes

Status When
200 OK User updated.
400 Bad Request Invalid body or a malformed uid.
404 Not Found No user with that id.

Example

curl -X PATCH -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"status":"suspended"}' \
  https://api.gitvetrix.com/api/v1/admin/users/<uuid>
DELETE /api/v1/admin/users/{uid}

Delete a user account. Requires admin:users. The user's outstanding tokens are revoked first, then the account and its dependent records are removed.

A user who has authored content that other records still reference — issues, merge requests, comments, reviews, or workflow activity — cannot be deleted while that content exists. The request is answered 409 Conflict with a reason, and the administrator must reassign or remove the authored content first.

Path parameters

Name Type Description
uid string User UUID.

Status codes

Status When
204 No Content User deleted.
400 Bad Request uid is not a valid UUID.
409 Conflict The user has authored content that blocks deletion. The body carries error and a reason.

Example

curl -X DELETE -H "Authorization: Bearer <token>" \
  https://api.gitvetrix.com/api/v1/admin/users/<uuid>
Other user operations

These operations are also gated on admin:users:

  • POST /api/v1/admin/users/{uid}/password — set a user's password. Body { "password": "..." }. Returns 204 No Content; 404 for an unknown id.
  • POST /api/v1/admin/users/{uid}/revoke-tokens — revoke all of a user's access tokens. Returns 204 No Content.
  • GET /api/v1/admin/users/pending — list accounts awaiting approval (paginated with limit/offset).
  • POST /api/v1/admin/users/{uid}/approve and POST /api/v1/admin/users/{uid}/reject — act on a pending registration.
GET /api/v1/admin/users/{uid}/repos

List the repositories owned by one user. Requires admin:users.

Path parameters

Name Type Description
uid string User UUID.

Response

A JSON array, empty when the user owns no repositories.

[
  {
    "id": "<uuid>",
    "name": "backend",
    "description": "Service API",
    "is_private": true,
    "default_branch": "main",
    "created_at": "2026-01-04T12:00:00Z",
    "updated_at": "2026-02-19T09:31:00Z"
  }
]

Status codes

Status When
200 OK Repositories returned.
400 Bad Request uid is not a valid UUID.
403 Forbidden Caller lacks admin:users.

Example

curl -H "Authorization: Bearer <token>" \
  https://api.gitvetrix.com/api/v1/admin/users/<uuid>/repos
POST /api/v1/admin/users/{uid}/impersonate

Begin impersonating a user. The response carries an access token that authenticates as the target user for subsequent API calls. Requires both admin:users and admin:impersonation (a super-admin satisfies both). The request also records a server-side impersonation session, scoped to the acting administrator and the target, which the DELETE below revokes.

This endpoint is exempt from rate limiting: it is an incident-recovery surface, so a misconfigured rate-limit rule can never lock an administrator out of it.

Path parameters

Name Type Description
uid string Target user UUID.

Response

201 Created. access_token is the token that authenticates as the target; expires_at is when the impersonation session lapses. The remaining fields are retained for older clients. The user object is the same shape returned by the authentication endpoints (login, refresh, /auth/me) — not the richer status/last_active_at shape returned by GET/LIST /admin/users. approval_state is omitted when the target account has no recorded approval state.

{
  "access_token": "<jwt>",
  "user": {
    "id": "<uuid>",
    "username": "alice",
    "email": "alice@example.com",
    "display_name": "Alice",
    "bio": "",
    "avatar_url": "",
    "is_admin": false,
    "approval_state": "active",
    "created_at": "2026-01-15T09:12:00Z"
  },
  "token": "imp_...",
  "session_id": "<uuid>",
  "expires_at": "2026-02-19T13:31:00Z"
}

Status codes

Status When
201 Created Impersonation session created; token returned.
400 Bad Request uid is not a valid UUID, or the target is itself an administrator (an administrator cannot be impersonated).
403 Forbidden Caller lacks admin:users or admin:impersonation; or a target gate refused the token (see below).
404 Not Found No user with that id.

When auth.require_email_verification is enabled and the target's email is unverified, the request is refused 403 with { "error": "email_not_verified" }. When signup.require_approval is enabled and the target is not yet active, the request is refused 403 with { "error": "approval_pending" } or { "error": "approval_rejected" } and a message. Both gates exist so that impersonation cannot route around a policy that would otherwise block the target from signing in themselves. Each refusal writes an audit entry (user.login_blocked_email_not_verified / user.login_blocked_approval) with details.surface set to impersonation; the acting administrator is the audit actor and the target id is recorded under details.target_user_id.

Example

curl -X POST -H "Authorization: Bearer <token>" \
  https://api.gitvetrix.com/api/v1/admin/users/<uuid>/impersonate
DELETE /api/v1/admin/users/{uid}/impersonate

End impersonation by revoking every active impersonation session the calling administrator holds for the target user. Requires both admin:users and admin:impersonation, and is exempt from rate limiting for the same incident-recovery reason as the POST. Idempotent: returns 204 No Content even when no session was open.

Status codes

Status When
204 No Content Sessions revoked (or none were open).
400 Bad Request uid is not a valid UUID.
403 Forbidden Caller lacks admin:users or admin:impersonation.

Example

curl -X DELETE -H "Authorization: Bearer <token>" \
  https://api.gitvetrix.com/api/v1/admin/users/<uuid>/impersonate

User groups

User groups carry a default role and a rate-limit tier. The seeded groups are anonymous, general, and admin; an operator can create custom groups. All user-group endpoints require admin:users, and return 503 when the user-group store is not configured on the deployment.

GET /api/v1/admin/user-groups

List all groups with live member counts. System groups are returned first.

Response

{
  "groups": [
    {
      "id": "<uuid>",
      "slug": "general",
      "name": "General",
      "description": "Authenticated users",
      "role": "user",
      "is_system": true,
      "member_count": 42,
      "created_at": "2026-01-04T12:00:00Z",
      "updated_at": "2026-01-04T12:00:00Z"
    }
  ],
  "total": 3
}

Status codes

Status When
200 OK Groups returned.
403 Forbidden Caller lacks admin:users.
503 Service Unavailable User-group store not configured.

Example

curl -H "Authorization: Bearer <token>" \
  https://api.gitvetrix.com/api/v1/admin/user-groups
POST /api/v1/admin/user-groups

Create a custom group. The slug is derived from name.

Request body

Field Type Required Description
name string yes Group name. Must contain at least one alphanumeric character.
description string no Free-text description.
role string no Default role for members.
grant_admin boolean no Required to be true when role is instance_admin.

Status codes

Status When
201 Created Group created (returns the group record).
400 Bad Request Invalid body, missing name, or a name with no alphanumeric character.
409 Conflict The slug or name is already in use.
422 Unprocessable Entity Invalid role.
GET /api/v1/admin/user-groups/{id}

Fetch one group with its current member count. 400 for a malformed id, 404 when the group does not exist.

PATCH /api/v1/admin/user-groups/{id}

Update a group's name, description, or role. The slug of a system group is immutable (409). 404 for an unknown id; 422 for an invalid role.

DELETE /api/v1/admin/user-groups/{id}

Delete a custom group. A system group cannot be deleted (409). Returns 204 No Content on success; 404 for an unknown id.

Group membership
  • GET /api/v1/admin/user-groups/{id}/members — list a group's members.
  • POST /api/v1/admin/user-groups/{id}/members — add a member.
  • DELETE /api/v1/admin/user-groups/{id}/members/{uid} — remove a member.

Settings

Instance settings are a key/value store. Sensitive values (for example SMTP and OAuth client secrets) are masked on read, and a write that echoes the mask back is a no-op so the stored secret survives. Setting reads and writes require admin:settings.

GET /api/v1/admin/settings

Return every setting as an array of entries.

Response

[
  { "key": "oauth2.dcr.enabled", "value": "false", "description": "Allow dynamic client registration" }
]

Status codes

Status When
200 OK Settings returned.
403 Forbidden Caller lacks admin:settings.
GET /api/v1/admin/settings/{key}

Read one setting. A sensitive key returns a masked placeholder rather than the stored value.

Response

{ "key": "oauth2.dcr.enabled", "value": "false", "description": "Allow dynamic client registration" }

Example

curl -H "Authorization: Bearer <token>" \
  https://api.gitvetrix.com/api/v1/admin/settings/oauth2.dcr.enabled
PUT /api/v1/admin/settings/{key}

Write one setting. The value is always a string; coerce booleans and numbers to their string form. The change takes effect immediately — no restart is required.

Path parameters

Name Type Description
key string Setting key.

Request body

{ "value": "true" }

Response

{ "key": "oauth2.dcr.enabled", "value": "true" }

Status codes

Status When
200 OK Setting written (or a no-op when the masked placeholder was echoed back).
400 Bad Request Invalid body, or a value the setting rejects as malformed.
404 Not Found Unknown setting key.
422 Unprocessable Entity Value is out of the setting's allowed bounds.

Example

curl -X PUT -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"value":"true"}' \
  https://api.gitvetrix.com/api/v1/admin/settings/oauth2.dcr.enabled
Batch write

POST /api/v1/admin/settings writes several keys in one request, applying the same validation and masking rules as the single-key PUT. POST /api/v1/admin/settings/smtp/test sends a test message with the current SMTP configuration.

Admin-scope grants

Delegate an individual admin capability to a non-super-admin user. All four endpoints require admin:roles, and return 503 when the admin-grants store is not configured on the deployment.

GET /api/v1/admin/admin-scopes

List every grant of one scope — useful for answering "who currently holds admin:users". This endpoint uses limit/offset pagination (see Pagination).

Query parameters

Name Type Default Description
scope string The admin scope to list grants for. Required.
limit integer 50 Page size, clamped to a maximum of 500.
offset integer 0 Zero-based row offset.

Response

{
  "grants": [
    {
      "user_id": "<uuid>",
      "scope": "admin:users",
      "granted_by": "<uuid>",
      "created_at": "2026-02-19T09:31:00Z"
    }
  ],
  "total": 1,
  "limit": 50,
  "offset": 0
}

Status codes

Status When
200 OK Page returned.
400 Bad Request scope query parameter is missing.
422 Unprocessable Entity scope is not a recognized admin scope.
503 Service Unavailable Admin-grants store not configured.

Example

curl -H "Authorization: Bearer <token>" \
  "https://api.gitvetrix.com/api/v1/admin/admin-scopes?scope=admin:users&limit=50"
GET /api/v1/admin/users/{uid}/admin-scopes

List every scope granted to one user.

Response

{ "grants": [ { "user_id": "<uuid>", "scope": "admin:users", "granted_by": "<uuid>", "created_at": "2026-02-19T09:31:00Z" } ], "total": 1 }
POST /api/v1/admin/users/{uid}/admin-scopes

Grant a scope to a user. Idempotent: re-granting an existing scope returns 200.

Request body

{ "scope": "admin:users" }

Status codes

Status When
200 OK Scope granted (or already present).
400 Bad Request Invalid body or a malformed uid.
403 Forbidden Granting admin:roles without the super-admin flag.
422 Unprocessable Entity Unknown scope.
DELETE /api/v1/admin/users/{uid}/admin-scopes/{scope}

Revoke a scope. Idempotent: returns 204 No Content even when the grant was already absent. 422 for an unrecognized scope value.

Audit log

The instance audit log is the append-only record of administrative and state-changing actions. Both endpoints require the super-admin flag.

GET /api/v1/admin/audit-log

List audit entries, most recent first, paginated.

Query parameters

Name Type Default Description
page integer 1 Page number.
per_page integer 100 Page size, clamped to a maximum of 500.
actor string Filter by actor username. An unknown username returns an empty page rather than 404.
action string Filter by action prefix (for example admin.).
from string Lower bound — an RFC 3339 timestamp or a YYYY-MM-DD date.
to string Upper bound (inclusive on a bare date) — same formats as from.

Legacy limit/offset parameters are also accepted when page is not supplied.

Response

{
  "entries": [
    {
      "id": "4821",
      "actor_id": "<uuid>",
      "actor_username": "alice",
      "action": "admin.settings_changed",
      "resource_type": "setting",
      "resource_id": "oauth2.dcr.enabled",
      "resource": "setting:oauth2.dcr.enabled",
      "ip_address": "203.0.113.7",
      "user_agent": "curl/8.0",
      "details": { "key": "oauth2.dcr.enabled", "old": "false", "new": "true" },
      "created_at": "2026-02-19T09:31:00Z"
    }
  ],
  "total": 512,
  "page": 1,
  "per_page": 100
}

id is a string. resource_type and resource_id are derived by splitting the stored resource on the first :. Secret values inside details are redacted.

Status codes

Status When
200 OK Page returned.
403 Forbidden Caller is not a super-admin.

Example

curl -H "Authorization: Bearer <token>" \
  "https://api.gitvetrix.com/api/v1/admin/audit-log?action=admin.&per_page=50"
GET /api/v1/admin/audit-log/{id}

Fetch one audit entry by its numeric id. Returns the same entry shape as the list endpoint.

Status codes

Status When
200 OK Entry returned.
400 Bad Request id is not a positive integer.
403 Forbidden Caller is not a super-admin.
404 Not Found No entry with that id.

System health

GET /api/v1/admin/health

Return a point-in-time health snapshot. Requires admin:system.

Response (abridged)

{
  "stats": { "users_count": 137, "repos_count": 88, "ci_jobs_count": 12043, "storage_bytes": 9663676416 },
  "db": {
    "reachable": true,
    "pool_open": 5,
    "pool_max": 25,
    "conns_by_application": {
      "vetrix-server": 5,
      "vetrix-worker": 2,
      "vetrix-runner-controller": 1
    }
  },
  "disk": { "...": "..." },
  "runners": { "...": "..." },
  "queue_depth": 0,
  "uptime_seconds": 86400
}

db.conns_by_application groups live PostgreSQL connections by the application_name each pool stamps, so an operator can see the cross-process idle-connection floor and attribute each connection to the process that holds it. It is best-effort: it is omitted if the pool is unreachable or the query errors, and the rest of the snapshot still renders. Connections that set no application name collapse under the empty-string key.

The optional access_log_drops counter is included only when the caller also holds admin:audit_read; it is omitted otherwise. Service-probe blocks (redis, opensearch, rabbitmq, child_zombies) appear only when the corresponding probe is wired on the deployment.

Status codes

Status When
200 OK Snapshot returned.
403 Forbidden Caller lacks admin:system.

Example

curl -H "Authorization: Bearer <token>" \
  https://api.gitvetrix.com/api/v1/admin/health

Log management

The server writes three on-disk log files (access.log, error.log, and exceptions.log). These endpoints list and rotate those files and require admin:system. On a deployment where file logging is not configured they degrade gracefully rather than erroring: the list returns [] and the rotate returns { "rotated": 0 }.

GET /api/v1/admin/logs

List the server log files with their current size and last-modified time.

Response

A JSON array, one entry per log file.

[
  { "name": "access.log", "size_bytes": 1048576, "modified_at": "2026-02-19T09:31:00Z" },
  { "name": "error.log", "size_bytes": 4096, "modified_at": "2026-02-19T09:30:00Z" }
]

Status codes

Status When
200 OK Files returned (or [] when file logging is not configured).
403 Forbidden Caller lacks admin:system.
500 Internal Server Error The log directory could not be read.

Example

curl -H "Authorization: Bearer <token>" \
  https://api.gitvetrix.com/api/v1/admin/logs
POST /api/v1/admin/logs/rotate

Rotate the server log files immediately, returning the number of files rotated.

Response

{ "rotated": 3 }

Status codes

Status When
200 OK Rotation completed (rotated is 0 when file logging is not configured).
403 Forbidden Caller lacks admin:system.
500 Internal Server Error Rotation failed; the body carries the reason.

Example

curl -X POST -H "Authorization: Bearer <token>" \
  https://api.gitvetrix.com/api/v1/admin/logs/rotate

CI runners

CI-runner endpoints require admin:runners.

GET /api/v1/admin/runners

List registered CI runners. Returns a JSON array; the runner token hash is never included.

Response

[
  {
    "id": "<uuid>",
    "name": "linux-amd64-1",
    "tags": ["linux", "docker"],
    "status": "online",
    "last_seen_at": "2026-02-19T09:31:00Z",
    "registered_at": "2026-01-04T12:00:00Z",
    "isolation_kind": "docker",
    "ssh_capable": false
  }
]

Example

curl -H "Authorization: Bearer <token>" \
  https://api.gitvetrix.com/api/v1/admin/runners
POST /api/v1/admin/runners

Register a runner. The raw token is returned once in the response and stored only as a hash thereafter.

Request body

Field Type Required Description
name string yes Runner name.
tags string[] no Scheduling tags.

Response

{
  "raw_token": "vetrix_...",
  "token": "vetrix_...",
  "runner": { "id": "<uuid>", "name": "linux-amd64-1", "...": "..." }
}

raw_token and token carry the same value (token is a legacy alias).

Status codes

Status When
201 Created Runner registered.
400 Bad Request Invalid body or missing name.
DELETE /api/v1/admin/runners/{id}

Delete a runner. 400 for a malformed id; 204 No Content on success.

POST /api/v1/admin/runners/{id}/rotate-token

Rotate a runner's token, invalidating the previous value atomically. The runner must re-authenticate with the new token on its next heartbeat.

Response

{ "raw_token": "vetrix_..." }

404 when no runner has that id.

Runner hosts

The runner-host endpoints expose the CICD runner-host pool and reuse the admin:runners scope.

  • GET /api/v1/admin/hosts — list hosts in the { items, total, page, per_page } page envelope (see Pagination). Each item carries the host's capacity, in-use count, state, last heartbeat, and a derived healthy flag.
  • GET /api/v1/admin/hosts/{id} — fetch one host, field-for-field identical to a list item.
  • POST /api/v1/admin/hosts/{id}/decommission — remove a drained host from the fleet.
  • POST /api/v1/admin/hosts/{id}/drain and GET /api/v1/admin/hosts/{id}/drain — start draining a host and read drain progress.

Rate-limit administration

These endpoints read and adjust the rate limiter described in rate-limits.md. They require admin:rate_limit — a scope deliberately separate from admin:settings so the "raise a limit during an incident" operator role does not inherit the rest of instance settings. Limits can only be narrowed by overrides, never widened past the group budget.

GET /api/v1/admin/rate-limits

Read the full per-group budget matrix.

PATCH /api/v1/admin/rate-limits/groups/{group_id}/{scope}

Update one group's budget for one scope. Every field is optional; only the fields supplied are changed.

Request body

Field Type Description
requests_per_window integer Allowed requests per window.
window_seconds integer Window length in seconds.
burst integer Burst allowance.
action string throttle, allow, or disabled.
enabled boolean Enable or disable the rule.
notes string Free-form audit note (not persisted on the row).

Related endpoints

  • GET /api/v1/admin/rate-limits/groups/{group_id} — read one group's rules.
  • POST /api/v1/admin/rate-limits/groups/{group_id}/{scope}/reset — reset a group/scope rule to its seeded default.
  • Per-target overrides, each with list / upsert / delete operations under /api/v1/admin/rate-limits/overrides/{repos,apps,tokens,users}/.... An override row carries requests_per_window, window_seconds, burst, action, enabled, and a reason.
  • GET /api/v1/admin/rate-limits/violations — the recent-violations feed (.csv for export).

Errors

These endpoints use the shared error envelope and status-code conventions in errors.md. Two patterns recur on this page:

  • 401 vs 403. An unauthenticated request is 401; an authenticated caller missing the required admin scope is 403 with { "error": "admin scope required: <scope>" }.
  • 503 for unwired features. Endpoints whose backing store is optional (user groups, admin-scope grants) answer 503 with a message naming the missing component when that store is not configured on the deployment.

Deleting a user that still owns referenced content returns 409 Conflict with a reason, as described under the delete endpoint above.

Rate limits

These endpoints are metered under the standard scopes in rate-limits.md: reads under api.read and mutations under api.write. The administrator group is metered with the allow action, so an administrator is never throttled out of these endpoints, but requests are still counted.