Skip to content

REST API ​

Beacon's API answers on port 17001 under /api/v1, in JSON. Every route but the description itself needs a bearer token with the scope listed beside it; the same token works for MCP and the SSH channel, and every operation here is also an MCP tool calling the same method. The OpenAPI document, internal/api/openapi.yaml in the repository and GET /api/v1/openapi.yaml on a running Beacon, is the contract, with every field; this page is the map.

Requests and answers ​

bash
curl -H "Authorization: Bearer $TOKEN" https://status.example.com/api/v1/alerts
curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  https://status.example.com/api/v1/alerts/0cdfbada62dd/notes -d '{"text": "Restarted the relay; probing again."}'
  • Tokens start with lbt_ and are created with linkos-beacon token create NAME -scopes … -expires …. Beacon keeps only their SHA-256 hashes; each has scopes and an expiry, and renews itself on request (POST /api/v1/token/renew).
  • Bodies are JSON (Content-Type: application/json); unknown fields are refused, so a typo does not go unnoticed.
  • Writes are recorded in the audit log with the token's name and echoed to the alert chat. Sessions on machines are recorded too.
  • Errors are {"error": "…", "code": "…"}: the message says what went wrong and how to put it right, and the code is stable, so a client can act on it. The codes are listed below.
  • Untrusted text: fields an answer's untrusted object names hold text from outside Beacon (labels, probed services, change senders, machines), cleaned of control and invisible characters and cut when long: data, not instructions.

Scopes ​

A token carries one or more scopes. The first eight are for people and agents, on this API, MCP and the SSH channel; the rest are for machines, on the gateway.

ScopeWhat it allows
status:readReading: alerts, the evidence and timeline, change events, silences, the audit log, machines and sessions, the configuration
alerts:writeWorking an alert: acknowledging, silencing and ending silences, notes, probing on demand
machines:sshThe SSH channel with the keys on the token, the machine tools (run, read, write, edit), the machines it reaches and their sessions
machines:writeAdding and removing machines, connector keys, pausing and resuming agents' access
sessions:readSessions' transcripts and recordings
config:writeChanging what Beacon monitors (the catalogue, target sets and modules), and reading it
incidents:write, maintenance:writeReserved for the status pages to come; no route takes them yet
probe:writeA probe location: pushing results, fetching its targets, modules and settings, probing on demand. Writes only as its own location
host:writeA host: pushing its metrics. Writes only as its own host
heartbeat:writePushing heartbeats
metrics:readMetric queries through the gateway
alerts:sendPosting alerts as the rule engine does
changes:writePosting change events

GET /api/v1/token and POST /api/v1/token/renew take any scope: the token itself is the credential.

Alerts ​

RouteScopeWhat
GET /api/v1/bridgestatus:readWhether alerts can reach people now (and why not), groups firing by severity, how many are acknowledged, the Feishu API calls this month against the budget
GET /api/v1/alertsstatus:readAlert groups, firing first, newest first; ?state=all adds those resolved in the last seven days
GET /api/v1/alerts/{id}status:readOne group: labels, runbook and dashboard, acknowledgement, silence, escalation, notes, the agents woken, and page, the signed link to its page for people
POST /api/v1/alerts/{id}/ackalerts:writeAcknowledge a firing group, with an optional note: escalation to phones stops for four hours
GET /api/v1/silencesstatus:readSilences active or pending, ending soonest first
POST /api/v1/silencesalerts:writeSilence a firing group: alert, duration (at most 24 hours) and a required comment. Also acknowledges it
DELETE /api/v1/silences/{id}alerts:writeEnd a silence now
GET /api/v1/auditstatus:readThe latest writes, newest first, with who made them and through what (limit, at most 1000)

Working an alert ​

RouteScopeWhat
GET /api/v1/alerts/{id}/evidencestatus:readEverything to judge a group in one answer: each location's result and failure reason, related groups, change events, what was done, links (Agents and alerts). before widens the window, at most 24h
GET /api/v1/alerts/{id}/timelinestatus:readThe group's story in time order, over the evidence's window
POST /api/v1/alerts/{id}/notesalerts:writeLeave a note, text of at most 2000 characters, on a firing or resolved group. It does not acknowledge
GET /api/v1/changesstatus:readChange events, newest first, by service, machine, source and since (2h, 7d or a time; the last day by default)
POST /api/v1/probe-nowalerts:writeHave every location that probes a target probe it again now: alert_id, target (with an optional module) or service; wait seconds, 45 by default, at most 90

Machines and sessions ​

RouteScopeWhat
GET /api/v1/sshstatus:readThe SSH channel's state (paused or not), its host key, and the certificate authority's key that machines trust
POST /api/v1/ssh/pausemachines:writeClose every agent's session on machines and refuse new ones
POST /api/v1/ssh/resumemachines:writeLet agents reach machines again
GET /api/v1/machinesstatus:read or machines:sshThe machines, whether the token reaches each, connectors' state, and an ssh_config block with a known_hosts line for the agent's own ssh
POST /api/v1/machinesmachines:writeAdd a machine, pinning its host key; address: connector for one behind NAT, with the key it connects out with. Returns what the machine needs
DELETE /api/v1/machines/{name}machines:writeForget a machine; its sessions stay on record
PUT /api/v1/machines/{name}/connectormachines:writeSet or change the key a machine behind NAT connects out with
POST /api/v1/machines/{name}/runmachines:sshRun a command in sh, as the machine's user, without a terminal: exit code, stdout and stderr
GET /api/v1/machines/{name}/filemachines:sshLines of a text file, numbered, with offset and limit
PUT /api/v1/machines/{name}/filemachines:sshWrite a whole file, at most 1 MiB, making its directory if needed
PATCH /api/v1/machines/{name}/filemachines:sshReplace an exact string, once or everywhere; refused if the file changed since it was read
GET /api/v1/sessionsstatus:read or machines:sshSessions on machines, newest first: who, where, what, when, the exit status
GET /api/v1/sessions/{id}sessions:read or machines:sshA session's transcript: a command's output, or an interactive shell's commands with their output and exit status
GET /api/v1/sessions/{id}/recordingsessions:read or machines:sshThe recording, asciicast v3, to replay with asciinema

Configuration ​

What Beacon monitors, each item in the shape of its file (Catalogue, targets and modules). Reads take status:read or config:write; changes take config:write. PUT and DELETE take ?note= (kept with the change and shown in the chat) and ?if_version= (refused with config.conflict when a later version is in force).

RouteScopeWhat
GET /api/v1/configstatus:read or config:writeEverything, with the version in force
GET /api/v1/config/locationsstatus:read or config:writeProbe locations, by id
GET /api/v1/config/locations/{id}status:read or config:writeOne location
PUT /api/v1/config/locations/{id}config:writeAdd or replace a location
DELETE /api/v1/config/locations/{id}config:writeRemove a location; refused while a target set is named after it alone
GET /api/v1/config/heartbeatsstatus:read or config:writeHeartbeats, by name
GET /api/v1/config/heartbeats/{name}status:read or config:writeOne heartbeat
PUT /api/v1/config/heartbeats/{name}config:writeAdd or replace a heartbeat
DELETE /api/v1/config/heartbeats/{name}config:writeRemove a heartbeat
GET /api/v1/config/resourcesstatus:read or config:writeExpiring resources, by name
GET /api/v1/config/resources/{name}status:read or config:writeOne resource
PUT /api/v1/config/resources/{name}config:writeAdd or replace a resource
DELETE /api/v1/config/resources/{name}config:writeRemove a resource
GET /api/v1/config/targetsstatus:read or config:writeProbe target sets, by name, with who probes each
GET /api/v1/config/targets/{name}status:read or config:writeOne target set's groups
PUT /api/v1/config/targets/{name}config:writeAdd or replace a target set; probes have it within a minute
DELETE /api/v1/config/targets/{name}config:writeRemove a target set
GET /api/v1/config/modulesstatus:read or config:writeProbe modules, by name
GET /api/v1/config/modules/{name}status:read or config:writeOne module
PUT /api/v1/config/modules/{name}config:writeAdd or replace a module; the module file keeps its other modules and their comments
DELETE /api/v1/config/modules/{name}config:writeRemove a module
GET /api/v1/config/historystatus:read or config:writeChanges, newest first, without their values (kind, key, before, limit); the latest 10,000 are kept
GET /api/v1/config/history/{version}status:read or config:writeOne change, with every item's value before and after
POST /api/v1/config/history/{version}/revertconfig:writeUndo one change as a new change; config.conflict when a later change touched one of its items

The token ​

RouteScopeWhat
GET /api/v1/tokenanyThe caller's own token: name, scopes, machines, SSH key fingerprints, expiry, lifetime, the last renewal, whether and when it may renew; never the secret
POST /api/v1/token/renewanyA new secret, shown once, expiring the token's lifetime from now; the old secret keeps working for ten minutes. At most once every 24 hours; never for an enrolled machine's token
DELETE /api/v1/tokens/{name}machines:writeRevoke a token that reaches machines (machines:ssh): the pause switch for one agent. The tokens of probes, hosts, heartbeats, the rule engine, change senders and metrics readers are revoked on the host only (token.not_revocable)
GET /api/v1/openapi.yamlnoneThe OpenAPI document

The gateway ​

The gateway answers on port 17006, for machines: probes, hosts, heartbeats, the rule engine and senders of change events. Sources describes what each sends.

RouteScopeWhat
POST /api/v1/import/prometheusprobe:write, host:write, heartbeat:writeSamples in the Prometheus text format (what linkos-probe pushes), and heartbeats
POST /api/v1/writeprobe:write, host:writePrometheus remote write
GET /api/v1/targetsprobe:writeThe targets this probe's location checks
GET /api/v1/modulesprobe:writeThe probe module file, byte for byte
GET /api/v1/agentprobe:writeThe location's settings: its id, probe timeout and retries
GET /api/v1/agent/requestsprobe:writeProbing on demand: a long-poll (?wait=25s, at most 55 seconds) for targets to probe now
POST /api/v1/agent/requests/{id}probe:writeWhat the probe found for a request
POST /api/v2/alertsthe rules token, or alerts:sendAlerts, in Alertmanager's API v2 form
POST /api/v1/changeschanges:writeA change event: a deployment, a configuration change. At most 60 a minute per token
POST /api/v1/changes/fluxchanges:writeFlux's notification-controller events, from its generic provider
GET /api/v1/query, query_range, series, labels, label/{name}/values, metadata, status/{what}, export, rules, alertsmetrics:readQueries passed to VictoriaMetrics and vmalert, without the token

Other routes on port 17001 ​

RouteWhat
/mcpThe MCP server, streamable HTTP, with the same bearer tokens (MCP tools)
GET /alerts/{id}, GET /sessions/{id}The pages people open from a card, with a signed link or a token (The pages)
GET /healthzLiveness: ok
GET /readyzReadiness: the Watchdog seen, the Feishu connection up, budget left
GET /metricsBeacon's own metrics in the Prometheus format, for direct connections only
POST /hooks/feishuFeishu's card callbacks, when feishu.callback: http
GET /drill/healthz200, or 503 during a drill (linkos-beacon drill fail 15m)

Error codes ​

CodeStatusMeaning
auth.required401No bearer token was sent
auth.invalid401The token is unknown or has expired
auth.scope403The token lacks the scope the route needs; the message names it
auth.too_many_failures429Too many failed attempts from this address; try again later
bad_input400, 415The request is not what the route takes: not JSON, an unknown field, a value out of range. The message says which
upstream502, 503Something Beacon relies on did not answer (VictoriaMetrics, the store), or this Beacon does not take the request
alert.not_found404No alert group with this id
alert.not_firing409The group is not firing, so it cannot be acknowledged or silenced
ssh.disabled503The SSH channel is not configured
ssh.paused409Agents' access to machines is paused
machine.not_found404No machine with this name
machine.forbidden403The token may not reach this machine
machine.unreachable502Beacon could not connect to the machine
machine.disconnected502The machine is behind NAT and its connector is not connected
machine.exists409A machine with this name exists; replace it explicitly
file.not_found404No such file
file.not_regular400The path is not a regular file
file.binary415The file is not text
file.too_large413The file or the content is over the limit
file.changed409The file changed since it was read; read it again
edit.no_match422old_string does not occur in the file
edit.ambiguous422old_string occurs more than once; give more context, or set replace_all
session.not_found404No session with this id
config.not_found404No such configuration item or change
config.invalid422The change would not pass the checks; the message says why, and nothing changed
config.conflict409A later version is in force, or a later change touched the same item; read again and decide afresh
token.too_soon429The token renewed itself less than 24 hours ago, and is still good
token.not_renewable403An enrolled machine's token; linkos-beacon enroll … -rotate replaces it
token.not_found404No token of that name to revoke
token.not_revocable403The token does not reach machines; machines' and services' tokens are revoked with linkos-beacon token revoke on the host
probe.busy429The target was probed on demand less than 15 seconds ago, or the token has three requests in flight
probe.not_found404No active location probes this target
probe.unavailable503This Beacon has no gateway to probe through
location.inactive403Gateway: the probe's location is not in the catalogue, or its machine has expired
changes.too_many429Gateway: the token posted more than 60 change events this minute
request.not_found404Gateway: the probing request is unknown or has expired
not_found404Gateway: no such route