Release Date: 2026-08-31
Type: Minor
Codename: Zero-Trust Serial Fallback
🎯 Highlights
- Zero-Trust USB Serial Fallback: When MQTT/WiFi is unreachable the ESP32-C3 still delivers ECDSA-signed telemetry over USB CDC (
[QG:FB] frames). A pure C++ decidePath + RetentionRing core is shared between the firmware and host SIL tests, so the offline path has the same cryptographic guarantees as the MQTT data plane.
- Host-Aware Retention & Offline Wall Clock:
Serial.isConnected() (HWCDC) prevents writing to a power-only charger; events are retained in a bounded FIFO ring (100 slots) and drained in order when a path returns. Each retained event is re-signed with the current software wall clock (epochAtSync + millis()) at drain time so the backend ±300 s anti-replay window accepts retransmissions. No frame is emitted before time is valid.
- Host Serial Bridge:
firmware/tools/serial_bridge.py tails /dev/ttyACM0, filters [QG:FB] lines and POSTs them to /readings/ with X-API-Key — the same ingestion gate as the MQTT bridge — with SSRF-safe URL validation and a parser smoke test in CI.
- Automatic First-Boot Provisioning: The compile-time
SENSOR_ID shortcut is removed. On first boot the node POSTs /devices/register (public key + MAC + enrollment token + GNSS-ready coordinates) and the backend assigns sensor_id and zone; NULL geometry is accepted when no GNSS fix exists yet. Verified live on hardware.
🔧 Changes by Component
Firmware (ESP32-C3)
| Change |
Description |
SerialFallback.h (new) |
Pure C++ header: [QG:FB] marker, buildSerialFrame() (std::string, no char arrays), DeliveryPath enum, decidePath() and RetentionRing<N> — shared with host test_serial_fallback.cpp. |
main.cpp — networkTask |
Becomes a first-available-path dispatcher: MQTT publish (unchanged), else USB CDC Serial.print(buildSerialFrame(...)), else RetentionRing push. Drains retained FIFO when a path returns, re-signing with the current wall time. |
main.cpp — clock & USB |
Anchors epochAtSync/millisAtSync at first NTP sync (pool.ntp.org); Serial.isConnected() polled each loop to gate SERIAL_CDC. ARDUINO_USB_MODE=1 / ARDUINO_USB_CDC_ON_BOOT=1 via platformio.ini. |
main.cpp — provisioning |
Removes SENSOR_ID define; globalSensorID loaded from NVS (quake-config), performProvisioning() assembles /devices/register payload (host scheme stripping, optional GnssModule coords). |
firmware/tools/serial_bridge.py (new) |
Host-side bridge: reads CDC, parse_frame() filtering, _validate_api_url() SSRF guard, forward() with X-API-Key, --stdin/--dry-run for testing. |
Backend (FastAPI + Redis + TimescaleDB)
| Area |
Changes |
POST /devices/register |
Validates ENROLLMENT_TOKEN, idempotent lookup by MAC/public key, re-resolves zone when a relocated node reports new coords, accepts latitude/longitude = None (NULL PostGIS geometry → Unknown Region). |
POST /readings/ |
Unchanged ingestion gate (API key + ECDSA + 300 s anti-replay); serial-bridged payloads traverse the identical validate_iot_payload path as MQTT-bridged ones. |
| Version artifact |
FastAPI(version="1.2.2"), idempotent TimescaleDB hypertable remains unified TimescaleDB+PostGIS image. |
Mobile (React Native + Expo)
| Area |
Changes |
| Version footer |
settings.tsx already shows QuakeGuard v1.2.2; no behavioural change in this release (serial path is host-side). |
Documentation & Infrastructure
| File/Area |
Changes |
ROADMAP.md / CHANGELOG.md / README.md |
v1.2.2 promoted from Current to Released; CHANGELOG date bumped to 2026-08-31. |
CITATION.cff |
date-released: 2026-08-31, version: 1.2.2; DOI update deferred to post-release Zenodo mint. |
docs/whitepaper |
main.typ cover updated to Zero-Trust Serial Fallback; new sections in 02-hardware.typ (serial fallback, retention, offline clock), 04-broker.typ (host serial bridge), 03-security.typ (identical signing + re-sign on drain). Rebuilt QuakeGuard_Technical_Report_v1.2.2.pdf and synced to docs/web/assets/quakeguard/quakeguard-whitepaper.pdf. |
docs/web |
quakeguard.html synced: softwareVersion 1.2.2, JSON-LD description mentions USB CDC fallback, hero v1.2.2, roadmap v1.2.1 Released / v1.2.2 Current (matches quakeguard.js i18n). |
CITATION.cff / docs/whitepaper/README.md |
Guidance that Zenodo DOI will be updated after the GitHub Release is published (automatic DOI generation). |
🔒 Security
- Zero-Trust serial plane: Serial frames carry the same
value:timestamp ECDSA P-256 signature as MQTT; verification uses the same cryptography path (DER/raw r||s tolerant). The backend cannot distinguish MQTT vs serial origin — equal trust verification.
- Re-sign on drain prevents replay rejection: Retained events are not replayed with stale timestamps; they are freshly signed at drain with the current wall time, staying inside the 300 s window without weakening the anti-replay check.
- SSRF hardening in the bridge:
_validate_api_url() whitelists http/https, rejects embedded credentials and non-alphanumeric hostnames before any requests.post.
- Host-presence check:
Serial.isConnected() ensures data is never written to a dead USB port when only a charger is attached; events are retained instead.
🧪 Testing Summary
| Suite |
Status |
| Backend (pytest) |
Pass (existing unit/integration; ingestion gate exercised by serial_bridge.py POSTs via validate_iot_payload) ✅ |
| Mobile (jest / ESLint) |
Pass (no behavioural change) ✅ |
| Firmware (PlatformIO / Native) |
Build SUCCESS — test_serial_fallback.cpp covers buildSerialFrame, decidePath (MQTT vs SERIAL_CDC vs RETAIN) and RetentionRing FIFO/bounded eviction ✅ |
| Static Analysis (CodeQL/Sonar) |
Clean — prior SonarCloud gate fixes retained (CSP externalized, role="status" → <output>, char arrays removed, cognitive complexity reductions); new code uses std::string and validated URL construction ✅ |
🚀 Deployment & Artifacts
Auto-triggers post-merge:
- GitHub Container Registry push (backend Docker image)
- GitHub Pages deploy (
docs/web)
- Zenodo archive (via
CITATION.cff — DOI to be updated after release publication)
Versioned Artifacts:
CHANGELOG.md
CITATION.cff
README.md & ROADMAP.md
docs/whitepaper/QuakeGuard_Technical_Report_v1.2.2.pdf + docs/web/assets/quakeguard/quakeguard-whitepaper.pdf
🔗 Related
What's Changed
- refactor(fullstack): clear sonarcloud code smells and cpd false positive by @GiZano in #368
- fix(fullstack): sonarcloud quality gate + remove ITACA fetcher + dual-track roadmap by @GiZano in #369
- feat(iot): v1.2.2 zero-trust usb serial fallback by @GiZano in #370
- fix(fullstack): sonarcloud gate + v1.2.2 release prep by @GiZano in #371
- chore(fullstack): v1.2.2 release finalization by @GiZano in #372
Full Changelog: v1.2.1...v1.2.2