From 9228bd720e3d0b1bc25080994488c51da72d1c70 Mon Sep 17 00:00:00 2001 From: Wasiu Bakare Date: Wed, 3 Jun 2026 16:55:50 +0100 Subject: [PATCH] docs(gateway): specify MQTT runtime exports --- gateway-api/v1.md | 210 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 192 insertions(+), 18 deletions(-) diff --git a/gateway-api/v1.md b/gateway-api/v1.md index c566674..2261175 100644 --- a/gateway-api/v1.md +++ b/gateway-api/v1.md @@ -1,28 +1,47 @@ # Gateway API v1 -> Status: Design target (not yet implemented end-to-end in `ori-runtime`) -> Related open gaps: G-04, G-05 +> Status: Runtime data exports implemented in `ori-runtime`; Tier 3 reasoning +> request/response remains a gateway milestone. +> Related open gaps: G-04, G-05, GW-020 -This contract defines intended Tier 3 request/response integration between runtime and gateway. +This contract defines the MQTT boundary between `ori-runtime` and `ori-gateway`. +It covers two message families: + +- Tier 3 reasoning request/response, used when runtime escalates reasoning to a LAN gateway. +- Runtime data export request/response, used by the gateway to generate reports and sync bounded runtime-owned data. + +The gateway must not read runtime SQLite files directly. Runtime owns data semantics; +gateway owns product/reporting behavior. ## Transport - Protocol: MQTT - Broker: `gateway.broker_url` - Pattern: request/response with correlation by `request_id` +- Runtime export responses are read-only, device-scoped, bounded, and paginated. +- HTTP export endpoints are not part of the runtime/gateway boundary. ## Topics -| Direction | Topic | -|---|---| -| Runtime -> Gateway | `ori/{device_id}/reasoning/request` | -| Gateway -> Runtime | `ori/{device_id}/reasoning/response` | -| Gateway heartbeat | `ori/gateway/health` | +| Message family | Direction | Topic | +|---|---|---| +| Reasoning | Runtime -> Gateway | `ori/{device_id}/reasoning/request` | +| Reasoning | Gateway -> Runtime | `ori/{device_id}/reasoning/response` | +| Gateway heartbeat | Gateway -> Runtime | `ori/gateway/health` | +| Runtime export | Gateway -> Runtime | `ori/{device_id}/export/request` | +| Runtime export | Runtime -> Gateway | `ori/{device_id}/export/response/{request_id}` | Gateway subscribes to `ori/+/reasoning/request` to serve every runtime on the LAN. -The device owns the request/response namespace; the gateway owns only its health topic. +For runtime data exports, the gateway publishes a request to the target device's +export request topic and subscribes to `ori/+/export/response/+` or the specific +response topic for the request. + +The device owns the `ori/{device_id}/...` namespace. The gateway owns only its +health topic. -## Request Payload +--- + +## Tier 3 Reasoning Request Payload ```json { @@ -42,7 +61,7 @@ The device owns the request/response namespace; the gateway owns only its health } ``` -## Response Payload +## Tier 3 Reasoning Response Payload ```json { @@ -58,7 +77,9 @@ The device owns the request/response namespace; the gateway owns only its health } ``` -`request_id` must match a pending runtime request. On provider timeout or failure, the gateway still publishes an error response with the original `request_id` and an `error` string so the runtime does not wait for an unanswered request. +`request_id` must match a pending runtime request. On provider timeout or failure, +the gateway still publishes an error response with the original `request_id` and +an `error` string so the runtime does not wait for an unanswered request. ## Heartbeat Payload @@ -68,7 +89,7 @@ Published on `ori/gateway/health` every 30 seconds by default. { "status": "starting|healthy|degraded", "uptime_s": 12.5, - "provider": "echo|llama_cpp|claude", + "provider": "echo|llama_cpp|claude|other", "sim_available": false, "timestamp_ms": 0 } @@ -76,7 +97,7 @@ Published on `ori/gateway/health` every 30 seconds by default. `uptime_s` is a floating-point number of seconds to match runtime health semantics. -## Timeout and Retry (Target Behavior) +## Tier 3 Timeout and Retry Target Behavior | Parameter | Target | |---|---| @@ -84,7 +105,7 @@ Published on `ori/gateway/health` every 30 seconds by default. | retry | 1 retry | | fallback | local Tier 2 (`local_slm`) on timeout/unreachable | -## Availability Check +## Gateway Availability Check Target behavior: @@ -92,7 +113,160 @@ Target behavior: - gateway publishes heartbeat every 30s - runtime marks gateway unavailable after heartbeat silence window -Current runtime note: +--- + +## Runtime Export Request Payload + +Published by gateway to `ori/{device_id}/export/request`. + +```json +{ + "request_id": "uuid4-string", + "export_type": "sensor_history", + "device_id": "site-a-edge-01", + "since_ms": 1717000000000, + "until_ms": 1717600000000, + "limit": 500, + "page_token": "", + "params": { + "sensor_id": "current-main", + "bucket_ms": 3600000 + } +} +``` + +### Request Fields + +| Field | Required | Notes | +|---|---:|---| +| `request_id` | yes | Correlates request and response. Must be non-empty. | +| `export_type` | yes | One of `health`, `sensor_history`, `action_log`, `tier_c_decision_log`. | +| `device_id` | yes | Must match the target runtime device ID. | +| `since_ms` | export-specific | Unix milliseconds lower bound. Required for `sensor_history`. | +| `until_ms` | export-specific | Unix milliseconds upper bound. Required for `sensor_history`. | +| `limit` | no | Runtime caps page size. Clients should use `<= 1000`. | +| `page_token` | no | Empty string for first page. Subsequent value comes from response `next_page_token`. | +| `params` | no | Export-specific object. Defaults to `{}`. | + +`until_ms` must be greater than or equal to `since_ms` when both are present. +Invalid or mismatched requests return an error response envelope instead of +raising transport-level failures. + +## Runtime Export Response Payload + +Published by runtime to `ori/{device_id}/export/response/{request_id}`. + +```json +{ + "request_id": "uuid4-string", + "export_type": "sensor_history", + "device_id": "site-a-edge-01", + "items": [], + "next_page_token": "", + "complete": true, + "error": null +} +``` + +Every response, including validation failures, includes the envelope fields above. +When `error` is non-null, `items` is empty and `complete` is true. + +## Supported Export Types + +### `health` + +Returns a single runtime health snapshot in `items[0]`. The snapshot is the same +runtime-owned diagnostic shape used by local health consumers and includes device +identity, uptime, capability posture, sensor status, alert timestamps, +DevicePolicy state, and advisory remote-command lockout state when available. + +Request notes: + +- `since_ms`, `until_ms`, and `params` are ignored. + +### `sensor_history` + +Returns bounded sensor history for one sensor. + +Request `params`: + +| Field | Required | Notes | +|---|---:|---| +| `sensor_id` | yes | Runtime sensor ID to export. | +| `bucket_ms` | no | Aggregation bucket width in milliseconds. `3600000` means hourly buckets. `0` or omitted returns runtime-selected source rows. | + +Response item shape for unbucketed rows: + +```json +{ + "sensor_id": "current-main", + "sensor_type": "current_clamp", + "timestamp": 1717000000000, + "value": 4.2, + "unit": "ampere", + "quality": 0.99, + "sample_count": 1, + "tier": "raw|5min|hourly|daily" +} +``` + +Response item shape for bucketed rows: + +```json +{ + "sensor_id": "current-main", + "sensor_type": "current_clamp", + "timestamp": 1717000000000, + "start_ms": 1717000000000, + "end_ms": 1717003600000, + "value": 4.2, + "avg_value": 4.2, + "min_value": 3.8, + "max_value": 4.8, + "unit": "ampere", + "quality": null, + "sample_count": 12, + "bucket_ms": 3600000, + "tier": "bucketed" +} +``` + +Bucket averages are weighted by `sample_count` so compacted runtime rows remain +statistically meaningful when re-aggregated for weekly reports. + +### `action_log` + +Returns bounded action-log rows for the runtime device. + +Optional request `params`: + +| Field | Required | Notes | +|---|---:|---| +| `tier` | no | Optional `A`, `B`, `C`, or `D` filter. | + +Response items include action name, tier, execution status, approval status, +action taken, operator response, proposal ID, safe-default usage, device ID, +sensor ID, sensor type, trigger name, and timestamp. + +### `tier_c_decision_log` + +Returns bounded Tier C proposal/decision rows for the runtime device. + +Response items include dataset-ready Tier C context: device ID, site type, +location, timezone, sensor reading, bounded history window, skill name, trigger +name, proposed action, confidence, reasoning tier/model, prompt summary, operator +decision, decision latency, safe-default usage, final action result, later +outcome, proposal ID, and creation timestamp. + +## Runtime Export Invariants -- runtime currently uses internet reachability probe in tier selection path -- broker-heartbeat based availability is pending gateway milestone work +- Runtime export requests are read-only. They must never mutate state, change + policy, trigger actions, or affect Tier C/Tier D paths. +- Runtime owns data semantics. Gateway must not infer runtime SQLite table names + or read runtime database files directly. +- Export responses must be bounded and paginated. +- Requests must be scoped to the target `device_id`. +- Runtime must return error envelopes for malformed requests rather than leaving + request/response calls unanswered. +- Cloud/provider SDKs do not belong in the runtime export implementation. + Gateway/product layers own reporting providers.