ados-control). It serves most routes itself and forwards a fixed set of prefixes to a residual Python app over an internal Unix socket, authenticating them first. To a client it is one HTTP API for status, telemetry, commands, configuration, video, pairing and the rest. The CLI, the terminal dashboard, the browser dashboard and Mission Control all use it.
Treat :8080 as one API. Which process answers a route is not a contract.
Base URL
http://192.168.1.50:8080 or http://ados-9f2c1a.local:8080. On the node itself use http://localhost:8080. The API is plain HTTP on the LAN; see Hardening.
Authentication
Send the pairing key in theX-ADOS-Key header. The key is created at pairing and stored in /etc/ados/pairing.json; it starts with ados_.
- Public routes. A few routes answer without a key so an unpaired node can be found and paired:
/api/pairing/info,/api/pairing/claim,/api/pairing/code,/api/ping,/api/version,/healthzand the dashboard PIN routes. The full list is in Public routes. - The unpaired gate. While a node is unpaired, its data routes answer on-box callers and the first-boot lifelines (link-local, the node’s own hotspot at
192.168.4.1and USB tether at192.168.7.1), require a dashboard PIN session from other callers on the operator LAN, and refuse remote callers. The pairing claim is refused to remote callers. - Dashboard PIN session. The browser dashboard exchanges its PIN at
POST /api/dashboard/pin/verifyand then sends the session in thex-ados-dashboard-sessionheader instead of a key. The dashboard PIN itself is covered on Agent dashboard overview.
X-ADOS-Key on a WebSocket handshake. Mint a short-lived ticket with POST /api/_ws/ticket (authenticated with the key) and pass it as a subprotocol: new WebSocket(url, ["ados-ws-ticket", ticket]).
Conventions
-
JSON. Requests and responses are JSON unless a route says otherwise (for example
GET /api/video/snapshotreturnsimage/jpeg). -
Armed guard. Actions that can drop the link or change how the vehicle flies (reboot, supervisor restart, link-carrying unit restarts, radio hop, TX power and unpair, profile or role changes, signing enrolment, parameter writes, extension install and lifecycle) answer
409while the vehicle is armed:Override with"force": truein the JSON body, or?force=1on a bodiless request. -
Residual routes. A residual prefix answers
501rather than404on a profile that does not run that feature. - Relay-forbidden routes. Pairing, unpair, MCP token, dashboard PIN set, signing disable and some setup routes refuse requests that arrive through a ground station’s relay proxy. Call them on the node’s own network.
-
Extension errors. The extension lifecycle routes use one envelope:
{"ok": false, "code": N, "kind": "...", "detail": "..."}.
Route domains
Examples
Status
Configuration
persisted; see Configuration.
Video snapshot
main path, with an X-Captured-At header. Nothing is written to disk. The route answers 503 with E_NO_FRAME when no frame is available.
WFB-ng link and parameters
Pairing
Ground station
Extension HTTP passthrough
An extension that declaresagent.http: true serves its own API on a Unix socket the plugin host creates for it. /api/plugins/{id}/x/{path} forwards GET, POST, PUT, PATCH and DELETE there as /{path}, keeping the query string, and upgrades WebSockets. The key is checked before the request is forwarded, and the route also works through a ground station’s relay proxy. When the extension is not serving, the answer is 503 with {"detail": "plugin <id> is not serving HTTP"}.
failure_reason: why the host stopped or refused the extension (for example tampered: <detail> or signer_revoked), or null when it has not failed. GET /api/plugins/{id}/locales/{lang} returns the extension’s locales/<lang>.json bundle so Mission Control can show its labels in the extension’s own words; lang is a bare tag of letters, digits, - and _.
Video attention profile
/api/video/profile sets which video profile this drone streams. POST {"profile": "hero"} or {"profile": "thumbnail"} moves the base profile, which is what a ground station’s fleet-hero assignment sends. A viewer can instead hold a lease: POST {"profile": "hero", "holder": "<id>", "lease_s": 30} creates or renews it, and DELETE /api/video/profile?holder=<id> (or a thumbnail POST with the same holder) releases it. The drone streams hero while any lease is live, and a lease lapses lease_s seconds (5 to 120, default 30) after its last renewal, so a viewer that goes away cannot leave a drone on hero. holder is 1 to 64 characters of letters, digits, ., _, : and -; vision is reserved. Every method answers 200 with the profile the encoder actually applied and the live leases, or 503 on a node with no video pipeline.
CORS
CORS is on with an allowlist, not a wildcard. The defaults cover the local Mission Control development origins (localhost:4000, localhost:4001). Add origins without dropping the defaults:
cors_origins and cors_origins_extra.
Machine-readable surfaces
The node does not serve Swagger UI or ReDoc. Use:GET /api/config/schemafor the build-time-embedded shape of the config surface, to drive a settings UI.docs/api-surface.mdin the agent repository, the generated table of every route and the process that serves it. The route reference below mirrors it.ados logs status --openapifor the OpenAPI document of the Black Box query API.
Route reference
Every route the node serves, generated from the agent’s route table.{name} matches one path segment; {*name} matches the rest of the path. Method WS marks a route that answers only a WebSocket upgrade.
Public routes
These answer without a key.Native routes
Served by the control front itself.Residual routes
Forwarded to the residual app on the same port after authentication.Next steps
Agent CLI reference
The
ados commands built on this API.Systemd services
The units behind each route domain.