# ADR-0003 · MQTT as the spine of the system **Status**: accepted **Date**: 2026-05 ## Context The platform has many independent processes that need to communicate: cameras → Frigate → enrichers → forensic engine → console → hub. Options considered: - **REST APIs everywhere**: tight coupling, polling overhead, hard to add subscribers. - **gRPC streams**: better but still point-to-point, no topic-based filtering. - **Kafka**: too heavy for edge devices, requires Zookeeper or KRaft, persistent disk-heavy. - **NATS**: lightweight but newer, less ecosystem maturity. - **MQTT**: mature, lightweight, broker available everywhere (Mosquitto), topic-based, retain messages, QoS levels, well-known TLS bridge mechanics. ## Decision MQTT is the **spine** of the system. Mosquitto runs on the router as the local broker. Bridges to the hub are configured selectively, with topic remapping. Components communicate via MQTT topics with a documented contract (see `docs/01-architecture/mqtt-contract.md`). Key topics: ``` hai//events/ # detection events hai//state/ # retained state (online, fps) hai//snapshots/ # frame snapshots — NEVER bridged hai//telemetry/ # raw telemetry — NEVER bridged hai/_registry/announce # service discovery hai/_registry/heartbeat/ # liveness hai/healthd/health # selftest reports hai/_bridge/status # bridge health (retained) _cmd/ # commands incoming from hub ``` **Sovereignty principle**: snapshots and raw telemetry **never** bridge. Aggregated health does. Events do (with site prefix). The `bridge.conf` policy is in GitOps and visible in the UI. ## Consequences **Good**: - Adding a new subscriber is free (no config changes upstream). - Selective bridging gives operational sovereignty as a programmable property. - `mosquitto_sub` is a superb debugging tool always available. - Retained messages handle late-joining subscribers correctly. **Bad / trade-offs**: - MQTT 5 has features we want (response topics, message expiry) but Mosquitto's MQTT5 support is partial — we stay on MQTT 3.1.1 for compatibility. - No native request-response semantics — we use `req-id` correlation in payloads where needed. - Encryption at rest of the broker DB is not transparent — addressed via LUKS at the OS layer. ## Alternatives considered See above. **Kafka** was the strongest alternative; rejected primarily for edge-device cost and operational complexity.