CPS MQTT ingest contract
AuroraSOC CPS firmware nodes (environmental sentinels, the ESP32-S3 environmental and presence node, the STM32F401 RFID access bench node, and the STM32F401 door node with a real PN532 reader and solenoid lock) talk to the backend over MQTT. This page documents the wire contract and how the backend ingests it, so firmware and backend stay in agreement.
The message shapes are produced by the no_std aurora-firmware-contracts crate
on the device and parsed by MQTTEdgeConsumer (aurorasoc/events/mqtt_consumer.py)
on the backend. Both sides treat this contract as the single source of truth.
Transport
- Broker: Mosquitto, mutual TLS (TLS 1.3), client certificate per device.
- The backend subscribes as a single consumer, supervised by
run_cps_mqtt_consumer(aurorasoc/tools/cps/mqtt_bridge.py), which reconnects with backoff and shuts down cleanly with the API. - Ingest is opt-in. It runs only when
MQTT_ENABLED=true(settings.mqtt.enabled); broker host, port, and certificate paths come from the otherMQTT_*settings.
Topics
| Topic | Direction | Purpose |
|---|---|---|
aurora/sensors/<device_id>/status | device to backend | Heartbeat plus firmware identity |
aurora/sensors/<device_id>/telemetry | device to backend | One numeric reading |
aurora/sensors/<device_id>/alerts | device to backend | Device-raised alert |
aurora/access/<device_id>/badge_event | device to backend | RFID badge read |
aurora/access/<device_id>/command | backend to device | Lock verb (unlock, lock, lockdown, lockdown_clear, keepalive) |
aurora/attestation/<device_id>/response | device to backend | Attestation reply to a challenge |
aurora/attestation/<device_id>/request | backend to device | Attestation challenge (nonce) |
<device_id> is validated against ^[A-Za-z0-9_-]{1,64}$; any other shape is
rejected before processing.
Payloads
All payloads are compact JSON objects.
Status:
{"device_type":"environmental_presence","board_family":"esp32s3","firmware_stack":"rust_embassy","firmware_version":"0.1.0","device_id":"esp32s3-env-01"}
A node that performs measured boot adds a firmware_hash (SHA-256 of its flash
image, lowercase hex), which the backend records as the trusted baseline (see
ADR 052):
{"device_type":"access_control_node","board_family":"stm32f401","firmware_stack":"rust_embassy","firmware_version":"0.1.0","firmware_hash":"<64 hex>","device_id":"stm32f401-door-01"}
Telemetry:
{"device_id":"esp32s3-env-01","metric":"temperature_c","value":-4.25,"unit":"C"}
Alert:
{"device_id":"esp32s3-env-01","type":"sensor_unhealthy","severity":"critical","event_details":"AHT21B unhealthy: repeated sensor timeout"}
Badge event:
{"device_id":"stm32f401-door-03","uid":"0455AF21","event":"badge_read"}
Backend behaviour
- status: an unknown device is auto-registered in
cps_devices(so a booting node appears inGET /api/v1/cps/devicesand the CPS console without manual registration); a known device has itslast_seenrefreshed. A device that reports a measuredfirmware_hashis markedtrustedat that baseline (trust-on-first-use, ADR 052); a hash that later changes flips it todrift_detectedand raises a tamper alert. - telemetry: the reading is range-checked against the per-metric thresholds in
settings.mqtt.telemetry_thresholds; an out-of-range value raises atelemetry_anomalyCPS alert. Every reading is also written to the audit stream for time-series consumers. - alerts: forwarded verbatim to the CPS alert stream for the CPS Security agent.
- badge_event: the reader is registered or refreshed, the UID is authorized
against the access policy, and the read is written to the audit trail for
physical-cyber correlation. On a grant the backend dispatches an
unlockcommand onaurora/access/<device_id>/command, so the door node never opens on its own; the device is the actuator, the backend is the authority. A denial raises anaccess_deniedCPS alert and the lock stays shut. Operator commands from the console use the same command topic. - attestation response: the reported firmware hash is compared to the expected
hash and the certificate validity is checked. The result is stored in
attestation_resultsand the device status is set to one of theattestation_status_enummembers (verified,failed,expired); a failure raises a critical CPS alert and bumps the risk score.
Security notes
- Device identity is pinned by the mTLS client certificate; the topic
device_idis treated as an untrusted label and validated by pattern. - Attestation statuses are constrained to the database enum, so a malformed or hostile response cannot smuggle an unexpected status value through.
- Routine badge reads go to the audit trail, not the alert stream, to keep alert volume meaningful; only denials and anomalies are alerts.