All docsStellar LinkRF bench · by Stellar Systems v0.1.0

Control & Automation

Using the API

Base URL, versioning, errors, authentication, the WebSocket streams, and the routes that are served but not in the OpenAPI document.

satlinkd serves a REST API and a set of WebSocket streams on port 8080. Everything the CLI and the web console do goes through it, so anything they can do, a script can do.

Base URL and versioning#

text
http://<board>:8080/api/v1/...     REST
ws://<board>:8080/ws/...           WebSocket streams

Every response carries an x-api-version: v1 header. The OpenAPI 3 document is served at GET /api/v1/openapi.json, with an interactive Swagger UI at /docs. The API Reference section of this site is generated from the same document.

Shell
curl -s http://192.168.2.1:8080/api/v1/health
curl -s http://192.168.2.1:8080/api/v1/version
curl -s http://192.168.2.1:8080/api/v1/system/capabilities | jq '.symbol_rates'

Errors#

Errors are JSON with a machine-readable code and a human message:

JSON
{"code": "conflict", "message": "a scenario is already running: ..."}
HTTPcodeTypical cause
400bad_requestUnparseable YAML, a bad duration, a framing that cannot hold alignment
404not_foundUnknown profile, scenario or run; no datapath in this daemon
409conflictA scenario is already running; the chain cannot be conditioned; the datapath is held by someone else; a scenario exists and overwrite was not given
422validation_failedSemantic validation failed
501not_implementedThe daemon lacks the resource (for example no scenarios_dir)
500internalAnything else; the message says what
504—A capture or loopback did not sustain the requested blocks in time

Refusals are written to be actionable: a 409 from a transmit or a scenario launch names the first unmet prerequisite and the command that clears it.

Authentication and CORS#

Authentication is off by default. When the daemon is built with its auth feature and [api] auth_required = true, every mutating request needs a JWT (HS256, signed with [api] auth_secret) as a bearer token; satlinkctl sends it from --token or SATLINK_TOKEN.

CORS origins are set by [api] cors_origins. The shipped configuration allows every origin (["*"]), so the web console can run from any host.

Request bodies are limited to [api] max_body_bytes (20 MiB by default). Payloads sent as hex double in size; for large transfers use the /ws/tx stream.

Long-running work#

Some requests start work that outlives them:

RequestReturnsFollow with
POST /api/v1/scenarios/{name}/run202, the run idGET /api/v1/scenario-runs/{run_id}, ws://…/ws/scenario-runs/{run_id}, then GET /api/v1/reports/{run_id}
POST /api/v1/campaigns/{name}/runthe execution idGET /api/v1/campaign-runs/{id}, ws://…/ws/campaign-runs/{id}
Any of the above—GET /api/v1/jobs, GET /api/v1/jobs/{job_id}

Only one scenario runs at a time. A second launch is refused with 409 naming the run in progress; it is never queued.

WebSocket streams#

StreamContent
/ws/metricsTelemetry snapshots (metrics_bulk messages) at the telemetry cadence
/ws/eventsRuntime events (profile_applied, lock lost, …)
/ws/pl/registersEvery PL register write, as it happens (observability only)
/ws/monitor/fft?channel=rx&interval_ms=200The spectrum, pushed
/ws/scenario-runs/{run_id}A scenario run in flight: events, samples and assertion verdicts as they happen
/ws/campaign-runs/{id}A campaign execution as it advances
/ws/txA persistent feed for pushing packets into the modem (see Datapath and Transmission)
/ws/frames?framing=slot&slot_len=256&payload=trueThe receive stream in real time (see Datapath and Transmission)

WebSocket streams are not described in the OpenAPI document.

Routes served but absent from the OpenAPI document#

The following routes are served by the daemon and used by the CLI and the web console, but are not yet merged into the published OpenAPI document, so they do not appear in the API Reference:

RoutePurposeDocumented in
GET /api/v1/radio/rfAD9361 rate, device loopback, DAC source, tuning readinessChain Conditioning
POST /api/v1/radio/rf/tuneSet the AD9361 rate and tune the digital interface at that rate ({"rate_sps": 8000000})Chain Conditioning
POST /api/v1/radio/rf/mode/rfRoute through the AD9361's internal digital loopbackChain Conditioning
POST /api/v1/radio/rf/mode/plBack to the PL loopbackChain Conditioning
POST /api/v1/radio/nco-offsetSet the RX/TX NCO offsets ({"rx_hz": …, "tx_hz": …})RF Fundamentals
GET /api/v1/pl/regs, GET/POST /api/v1/pl/regs/{name}List, read and write catalogued PL registers by namePL Register Map
PUT /api/v1/monitor/fft/{channel}Switch spectrum analysis on or off for rx or txTelemetry and Metrics
POST /api/v1/scenariosCreate or replace a scenario from YAMLScenarios

POST /api/v1/radio/rf/mode/air, /radio/rf/calibrate and /radio/rf/kill are in the document.

Talking to the API from a script#

Python
import requests

BOARD = "http://192.168.2.1:8080/api/v1"

print(requests.get(f"{BOARD}/version").json())
r = requests.post(f"{BOARD}/chain/condition",
                  json={"target": "pl", "profile": "pl_loopback"})
r.raise_for_status()
run = requests.post(f"{BOARD}/scenarios/hc_qpsk/run").json()
print(run)

The Python client in examples/src/satlink_examples/board.py wraps the REST API and the ZeroMQ bridge; see Example Clients.

Stellar Link · v0.1.0

↑↓ to moveEnter to open