diff --git a/README.md b/README.md index 42af424..3cfd17c 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ This repository is documentation-only. It contains no runtime implementation cod | Gateway API | [gateway-api/v1.md](gateway-api/v1.md) | Implemented baseline | | CLI commands | [cli-commands/v1.md](cli-commands/v1.md) | Contract target; runtime entrypoint implemented | | Runtime health socket RPC | [runtime-health/v1.md](runtime-health/v1.md) | Implemented | +| Runtime telemetry HTTP export | [runtime-telemetry/v1.md](runtime-telemetry/v1.md) | Implemented baseline | | Device policy | [device-policy/v1.md](device-policy/v1.md) | Implemented baseline | | Offline Tier C tokens | [offline-tokens/v1.md](offline-tokens/v1.md) | Implemented baseline | | Runtime config surface | [runtime-config/v1.md](runtime-config/v1.md) | Implemented baseline | diff --git a/runtime-config/v1.md b/runtime-config/v1.md index 694ea12..bda0b7e 100644 --- a/runtime-config/v1.md +++ b/runtime-config/v1.md @@ -36,6 +36,22 @@ attested by `state.encryption`. | `max_clock_skew_s` | `int` | Must be `>=1` | | `refresh_interval_s` | `int` | Must be `>=60` | +## `telemetry_export` + +| Key | Type | Notes | +|---|---|---| +| `enabled` | `bool` | Disabled by default. Enables direct HTTP export to a product backend. | +| `endpoint` | `string` | Absolute HTTP(S) URL. Must use `https://` unless targeting loopback. | +| `api_key_env` | `string` | Environment variable name holding the device API key. | +| `flush_interval_s` | `float` | `1..300` seconds. | +| `batch_size` | `int` | `1..500` events per POST. | +| `timeout_ms` | `int` | `100..30000` request timeout. | +| `max_queue_size` | `int` | Must be greater than or equal to `batch_size`. | + +This config surface implements `runtime-telemetry/v1.md`. It mirrors +`sensor.reading` events only and must not be used for runtime mutation or +actuator authority. + ## `health_socket` | Key | Type | Notes | diff --git a/runtime-telemetry/v1.md b/runtime-telemetry/v1.md new file mode 100644 index 0000000..b0fe3bb --- /dev/null +++ b/runtime-telemetry/v1.md @@ -0,0 +1,144 @@ +# Runtime Telemetry v1 + +> Status: Implemented baseline in `ori-runtime` +> Primary consumer: `ori-energy apps/api` during Phone Starter/XPRIZE path. + +This contract defines the direct HTTP telemetry boundary from `ori-runtime` to a +product backend. It exists for phone and lightweight deployments where running a +LAN `ori-gateway` is unnecessary or unavailable. + +This is not the runtime/gateway MQTT boundary. Gateway exports remain defined in +`gateway-api/v1.md`. + +## Authority Boundary + +Runtime telemetry export is observational only. + +- It mirrors real runtime `sensor.reading` events to a product backend. +- Backend availability must never affect Tier B, Tier C, or Tier D action + authority. +- The backend must not use this endpoint to mutate runtime configuration, + thresholds, relay state, DevicePolicy, update intent, or actuator settings. +- Tier D remains deterministic and local. + +## Transport + +| Field | Value | +|---|---| +| Protocol | HTTPS POST | +| Content type | `application/json` | +| Runtime config | `telemetry_export` | +| Default state | Disabled | +| Loopback exception | `http://localhost` or `http://127.0.0.1` allowed for local development | + +Production and public endpoints must use HTTPS. + +## Request Headers + +| Header | Required | Notes | +|---|---:|---| +| `Authorization` | Yes | `Bearer ` | +| `Content-Type` | Yes | `application/json` | +| `X-Ori-Device-Id` | Yes | Must match payload `device_id`. | +| `X-Ori-Timestamp-Ms` | Yes | Unix milliseconds UTC when the request was signed. | +| `X-Ori-Signature` | Yes | `v1=` | + +The device API key is read from the runtime environment variable named by +`telemetry_export.api_key_env`. The secret must not be committed to `ori.yaml`. + +## Signature + +The runtime signs: + +```text +timestamp_ms + "." + canonical_json_body +``` + +Using HMAC-SHA256 with the device API key as the secret. + +The JSON body must be canonicalized with sorted keys and compact separators +before signing. + +The backend must: + +- verify the bearer token/device API key; +- verify `X-Ori-Device-Id` matches payload `device_id`; +- verify timestamp skew within backend policy; +- verify HMAC signature before accepting the batch; +- reject replayed or duplicate batches using `device_id`, `sequence`, and/or + event IDs. + +## Request Body + +```json +{ + "schema_version": "runtime.telemetry.v1", + "device_id": "phone-gateway-ikeja-01", + "sequence": 1, + "sent_at_ms": 1719000000000, + "events": [ + { + "event_id": "uuid4-string", + "event_type": "sensor.reading", + "device_id": "phone-gateway-ikeja-01", + "sensor_id": "phone-main-power", + "timestamp": 1719000000000, + "source": "usb_serial", + "fingerprint": "sha256-hex-or-empty", + "context": {}, + "reading": { + "sensor_id": "phone-main-power", + "sensor_type": "usb_power", + "value": 1240.5, + "unit": "watt", + "timestamp": 1719000000000, + "quality": 1.0, + "metadata": { + "source": "usb_serial" + } + } + } + ] +} +``` + +`reading.raw` is intentionally omitted from the HTTP payload. + +## Runtime Config + +```yaml +telemetry_export: + enabled: false + endpoint: "https://api.ori.energy/runtime/telemetry" + api_key_env: ORI_ENERGY_DEVICE_API_KEY + flush_interval_s: 30 + batch_size: 50 + timeout_ms: 3000 + max_queue_size: 1000 +``` + +## Retry And Backpressure + +Runtime behavior: + +- queue writes are non-blocking; +- queue size is bounded by `max_queue_size`; +- overflow drops new telemetry and increments a runtime drop counter; +- failed POST batches are retained in memory when queue capacity permits; +- durable historical backfill is outside the v1 runtime contract. + +The backend should treat event IDs as idempotency keys. + +## Relationship To ori-gateway + +`runtime.telemetry.v1` is for direct product ingestion. `gateway-api/v1.md` +remains the Edge Node and site-gateway contract for MQTT reasoning, runtime +health, and bounded historical exports. + +An Ori deployment may use both: + +- direct telemetry export for phone/product dashboards; +- gateway MQTT exports for certified Edge Node reporting and local gateway + services. + +The two contracts must not share mutation semantics.