Files

197 lines
5.3 KiB
Markdown

# MQTT contract
The MQTT topic schema is the inter-process contract of the platform. All publishers and subscribers must respect this schema. Versioned in `packages/proto-mqtt/` of the future code repo as JSON Schema files.
See [ADR-0003](../../decisions/0003-mqtt-como-espina-dorsal.md) for why MQTT is the spine.
## Naming conventions
- Lowercase, slash-separated.
- Site prefix only when bridged to hub: `sites/<site_id>/...`.
- Local topics (within a site) **don't** include the site prefix.
- Underscore prefix (`_registry`, `_cmd`, `_bridge`) marks system-level topics, distinct from device topics.
- `+` is single-level wildcard, `#` is multi-level (MQTT standard).
## Topic catalog
### Device events
```
hai/<cam_id>/events/<event_type>
```
- `event_type``person | car | truck | motorcycle | bicycle | animal | package | license-plate | custom`.
- Payload (JSON):
```json
{
"id": "evt-8a2f",
"label": "person",
"conf": 0.92,
"zone": "entrance",
"ts": "2026-05-09T19:34:22.142Z",
"ts_nts": "2026-05-09T19:34:22.142Z",
"sha_clip": "a1f7...",
"sha_keyframe": "b2c8..."
}
```
- QoS: 1.
- Retained: no.
- Bridged: yes, with prefix `sites/<site_id>/`.
### Device state
```
hai/<cam_id>/state/<key>
```
- `key` ∈ `online | fps | npu_pct | last_keepalive | error`.
- Retained: yes.
- Bridged: yes (status info needed at hub).
### Snapshots
```
hai/<cam_id>/snapshots/<n>
```
- Binary JPEG, ~50-100KB.
- Retained: yes (last frame).
- **Never bridged** (sovereignty rule, see [ADR-0005](../../decisions/0005-sovereignty-vs-hyperscaler.md)).
### Telemetry (raw)
```
hai/<cam_id>/telemetry/<key>
```
- `key` ∈ `fps | bitrate | latency | drops`.
- High-frequency, low-value individually.
- Retained: no.
- **Never bridged** raw — aggregated and republished by `healthd`.
### Service registry
```
hai/_registry/announce
```
- Published by Cell containers and other discoverable services.
- Payload:
```json
{
"type": "frigate | enricher | re-id | healthd | hai-console",
"id": "cell-01",
"host": "192.168.20.10",
"port": 5000,
"caps": ["frigate", "reid"],
"version": "0.4.2",
"started_at": "2026-04-25T14:18:00Z"
}
```
- Retained: yes.
- Bridged: yes (hub needs to know what's deployed where).
```
hai/_registry/heartbeat/<id>
```
- Published every 30s by each registered service.
- Retained: no.
- Bridged: no (hub uses bridge state for liveness).
### Health reports
```
hai/healthd/health
```
- Aggregated selftest result, published every 5min.
- Payload: structured object with results per test.
- Retained: yes.
- Bridged: yes (hub needs health for fleet view).
### Bridge status
```
hai/_bridge/status
```
- Published by router to indicate bridge health.
- Payload: `{ "connected": true, "queue": 0, "rtt_ms": 38, "last_disconnect": "..." }`.
- Retained: yes.
- Special: this topic is **published locally by the router** about its own bridge state. Operators see it in the MQTT panel.
### Commands (incoming from hub)
```
_cmd/<command>
```
- Published by the hub, consumed by router or Cell services.
- Payload includes `requested_by`, `ts`, `correlation_id`.
- Bridged inbound: yes, with hub-side topic `sites/<site_id>/_cmd/<command>` mapped to local `_cmd/<command>`.
### Configuration changes (incoming from hub)
```
_config/<resource>
```
- Used by hub to push GitOps-equivalent config updates.
- In practice, GitOps does most of this; this topic is reserved for low-latency overrides.
- Retained: yes.
- Bridged inbound: yes.
### Evidence chain (post-MVP, see ADR-0007)
```
hai/_evidence/manifest
```
- Published whenever a clip is finalized with its signed manifest.
- Bridged: yes (hub keeps a second copy for hash-chain verification).
## QoS guidelines
- **QoS 0**: telemetry, snapshots, frequent low-value data.
- **QoS 1**: events, state, registry, health, commands. Default for anything that should not be lost.
- **QoS 2**: not used. Cost not justified for our use cases.
## ACL guidelines
- Cell containers can `pub/sub` only `hai/<their_id>/...` and `hai/_registry/announce`.
- Router can `pub/sub` everything locally.
- Bridge writes only to remapped namespaces, can't pub locally.
- Operator console (read-only) can subscribe to `hai/#` but not publish.
- Future: per-operator ACLs based on Keycloak roles.
## Bridge policy (sovereignty matrix)
What goes UP (site → hub):
```
hai/+/events/# → sites/<site_id>/hai/+/events/# QoS 1
hai/+/state/# → sites/<site_id>/hai/+/state/# QoS 1, retained
hai/_registry/announce → sites/<site_id>/_registry/announce QoS 1, retained
hai/+/health → sites/<site_id>/health/+ QoS 0
hai/_bridge/status → sites/<site_id>/_bridge/status QoS 1, retained
hai/_evidence/manifest → sites/<site_id>/_evidence/manifest QoS 1
```
What goes DOWN (hub → site):
```
sites/<site_id>/_cmd/# → _cmd/# QoS 1
sites/<site_id>/_config/# → _config/# QoS 1, retained
```
What is **never bridged**:
```
hai/+/snapshots/# (binary JPEGs, sovereignty)
hai/+/telemetry/# (raw, aggregated only)
$SYS/# (broker internals)
```
This policy lives in `/etc/mosquitto/conf.d/bridge.conf` on the router, version-controlled in the site-config repo. The MQTT panel in the console renders it from the live config.