Runtime HTTP API (v0)
Human-oriented runtime API guide.
Machine contract authority:
- OpenAPI:
schemas/http/runtime-http.openapi.v0.yaml - Contract examples:
tests/contracts/runtime-http/examples/ - Baseline:
docs/contracts/runtime-http-baseline.md
Use this file for operational semantics and practical examples.
Transport
- Base URL default:
http://127.0.0.1:8080 - JSON content type:
application/json - API prefix:
/v0 - Authentication: bearer token, off by default (
http.auth_enabled). With it on, every request needsAuthorization: Bearer <token>, including one from this host unlesshttp.auth_exempt_loopbackis set. The check runs before routing, so it covers every endpoint and the/v0/eventsstream. Without a valid token the reply is401withWWW-Authenticate: Bearerand the body{"error":"unauthorized"}, not the envelope below.install.shgenerates the token into/etc/anolis/runtime.env. See security.md.
CORS behavior comes from runtime config (http.cors_*).
Common Response Envelope
All JSON responses include status:
{
"status": {
"code": "OK",
"message": "ok"
}
}Common status-code mapping:
| Code | HTTP |
|---|---|
OK | 200 |
INVALID_ARGUMENT | 400 |
NOT_FOUND | 404 |
FAILED_PRECONDITION | 409 |
UNAVAILABLE | 503 |
DEADLINE_EXCEEDED | 504 |
INTERNAL | 500 |
Endpoint Map
Runtime and provider status
GET /v0/runtime/status- high-level runtime/provider summary.GET /v0/providers/health- per-provider lifecycle/supervision and device health.
Notes:
modeinGET /v0/runtime/statusis the control regime in effect, not a probe for the mode machine. With automation enabled it is the mode machine's current mode. Withautomation.enabled: falsethere is no mode machine andmodeis alwaysMANUAL, which is accurate: manualPOST /v0/callis permitted and nothing actuates on its own.- To tell whether a mode machine exists, call
GET /v0/mode. On an automation-disabled runtime it returns503 UNAVAILABLE(Automation layer not enabled), as doPOST /v0/mode,GET /v0/automation/treeandGET /v0/automation/status. AMANUALfrom/v0/runtime/statusalone does not distinguish the two cases.
Discovery and capabilities
GET /v0/devicesGET /v0/devices/{provider_id}/{device_id}/capabilities
State reads
GET /v0/stateGET /v0/state/{provider_id}/{device_id}(supports repeatedsignal_idfilters)
Control
POST /v0/call
Notes:
- Request currently requires
function_id. - Args use typed ADPP value encoding.
Safe-state (software stop)
POST /v0/estop- engage the latching software safe-state.POST /v0/estop/clear- release the latch.
Notes:
/v0/estopruns the declared safe-state ladder (hooks -> per-actuator setpoints, only when they cover every actuating output -> zero, only whensafety.safe_state.zero_is_safeis set -> otherwise an honestsoftware_safe_state: "none") and works whether or not automation is enabled. On an automation machine it also drivesFAULT. The response carriessafe_state_achieved:trueonly when the rung issued at least one call and every call succeeded,falsewhen any call failed, when the rung issued no call, or when no rung applies. It covers only the calls the rung issued: underhooksit can betruewhile actuating functions no hook drives remain (uncovered_actuating_functionsinGET /v0/runtime/status). A call succeeds when its provider reports it OK; that confirms the command was accepted, not that the device was seen in the commanded state. The same verdict is logged when a returning device's safe state is re-asserted.- While latched, actuating
POST /v0/callrequests are refused with409(fail-closed: a function without an explicit read-only category counts as actuating). Read-only calls are unaffected. - Latch and planned safe-state are surfaced under
estopinGET /v0/runtime/status. - What
safety.on_startupdid at startup is surfaced understartup_safe_stateinGET /v0/runtime/status:on_startup,ran,software_safe_state,safe_state_achieved(the same verdict) andfailed_actions(same shape as the/v0/estopactions), andskipped_actions: calls not issued because their provider reported the deviceUNREACHABLEand it is not registered, each withdevice_handle,functionand the provider'sreason.ranisfalseforleaveand when no safe state is declared. Runtimes before 0.1.51 do not sendskipped_actions. - This is a software stop, not a substitute for the hardware emergency stop.
Device-loss latch
GET /v0/runtime/status carries device_loss_latched: an array of device handles whose provider answered a read and reported the device itself unreachable (UNAVAILABLE or DEADLINE_EXCEEDED for that device), and that have not been re-armed since.
A provider that hangs, crashes or is restarted does not latch its devices: provider loss is not device loss (see safety.md). Its devices go unavailable, with device_availability events, and come back without a latch. Once the provider is back, a read it reports failed for a device latches that device as usual.
Notes:
- While a device is latched, behaviour-tree calls that actuate it are refused with
FAILED_PRECONDITION. ManualPOST /v0/call, mode-transition hooks and safe-state calls are unaffected — an operator commanding a device they can see is the deliberate action that "permit restarting" means. - Latches clear on a
MANUAL -> AUTOtransition, and on nothing else. Releasing an emergency stop must not restart the machine (ISO 13850 §4.1.4). - The array is always present and always an array, empty when nothing is latched.
Automation and parameters
GET /v0/modePOST /v0/modeGET /v0/parametersPOST /v0/parametersGET /v0/automation/treeGET /v0/automation/status
Streaming
GET /v0/events(SSE)
Optional query filters:
provider_iddevice_idsignal_id
Current event names:
state_updatequality_changedevice_availabilitymode_changeparameter_changeautomation_faultdevice_removed
Provider health is not streamed. Read GET /v0/providers/health for it.
device_availability carries provider_id, device_id, available and timestamp_ms. It reports edges, not state:
available: falsewhen a device becomes unreachable: a read of it fails, or its provider is found unavailable or missing (a crashed provider awaiting its supervised restart).available: trueon the first successful read after that, sent before that read'sstate_updateevents.- One event per transition. Repeated failed polls send nothing more.
- Nothing at startup or on the first poll: a device starts out available.
- No loss is sent once the runtime is shutting down, so a stop does not report its providers exiting as device losses.
- No replay. A subscriber that connects during an outage sees only the
available: true. For the current state, readGET /v0/state, where an unavailable device hasquality: "UNAVAILABLE".
device_removed carries provider_id, device_id and timestamp_ms. It is sent when a provider restart republishes an inventory without a device the runtime was tracking, whether that device was available or not. The device has no state after it and no device_availability follows, including for a device last reported available: false. The provider_id and device_id filters apply to it.
Typed Value Encoding
ADPP value payload format:
| Type | Example |
|---|---|
double | {"type":"double","double":1.23} |
int64 | {"type":"int64","int64":-42} |
uint64 | {"type":"uint64","uint64":42} |
bool | {"type":"bool","bool":true} |
string | {"type":"string","string":"AUTO"} |
bytes | {"type":"bytes","base64":"AAECAw=="} |
Quality Semantics
Signal quality values:
OKSTALEUNAVAILABLEFAULT
Device-level quality is computed as worst signal quality.
Practical Examples
Runtime status
curl -s http://127.0.0.1:8080/v0/runtime/status | jqProviders health
curl -s http://127.0.0.1:8080/v0/providers/health | jqDevices + capabilities
curl -s http://127.0.0.1:8080/v0/devices | jq
curl -s http://127.0.0.1:8080/v0/devices/sim0/motorctl0/capabilities | jqState
curl -s http://127.0.0.1:8080/v0/state | jq
curl -s "http://127.0.0.1:8080/v0/state/sim0/motorctl0?signal_id=motor1_duty" | jqCall device function
curl -s -X POST http://127.0.0.1:8080/v0/call \
-H "Content-Type: application/json" \
-d '{
"provider_id": "sim0",
"device_id": "motorctl0",
"function_id": 1,
"args": {
"motor_index": {"type": "int64", "int64": 1},
"duty": {"type": "double", "double": 0.75}
}
}' | jqMode and parameters
curl -s http://127.0.0.1:8080/v0/mode | jq
curl -s -X POST http://127.0.0.1:8080/v0/mode \
-H "Content-Type: application/json" \
-d '{"mode":"MANUAL"}' | jq
curl -s http://127.0.0.1:8080/v0/parameters | jq
curl -s -X POST http://127.0.0.1:8080/v0/parameters \
-H "Content-Type: application/json" \
-d '{"name":"temp_setpoint","value":30.0}' | jqSoftware safe-state (software stop)
# Engage: runs the safe-state ladder and latches actuation.
curl -s -X POST http://127.0.0.1:8080/v0/estop | jq
# While latched, actuating /v0/call returns 409. Release the latch:
curl -s -X POST http://127.0.0.1:8080/v0/estop/clear | jqSSE stream
curl -N http://127.0.0.1:8080/v0/eventsError Payload
Example:
{
"status": {
"code": "NOT_FOUND",
"message": "Device not found: sim0/nonexistent"
}
}Related
docs/http/README.md- HTTP contract validation workflow.tests/contracts/runtime-http/examples/manifest.yaml- fixture inventory.
