All three deferred findings from the qe-court round. Each fix is guarded by a test confirmed to FAIL against the old behaviour. P1 — JWKS: self-inflicted stall, and a blocking fetch on a tokio worker. `fetched_at` advances only on SUCCESS, and the only rate limiter sat behind `if fresh`. So once the TTL elapsed after the last successful fetch, `fresh` was permanently false, the limiter was never consulted, and EVERY request performed its own blocking 3s-timeout fetch. A Pi that loses WAN stalled itself 300s later with no attacker present; an attacker could force the same state by flooding tokens with an unknown `kid`. Now: `last_attempt_at` is recorded BEFORE every fetch regardless of outcome, and gates the stale path too; a stale-but-present key is served rather than erroring, which is the offline tolerance this module always claimed. Measured by the new test: 26 outbound fetches before, 1 after. Kept as TWO independent limiters. Merging them looks tidy and is wrong — a routine refetch would then suppress the unknown-`kid` path for 30s and delay pickup of a key rotation inside the TTL. I made that mistake first; two existing tests caught it. The blocking call also now runs in `spawn_blocking` at the verify boundary, matching what `main.rs` already does for the token exchange, where the comment reads "the same mistake this codebase had to fix in jwks.rs". The hot verification path had never been given the same treatment. A panicked task fails closed. P2 — session lifetime, per decision: 1 hour, plus step-up. SESSION_TTL_SECS 12h -> 1h, and privileged (`sensing:admin`) actions now require the user to have authenticated within ADMIN_REVERIFY_SECS (5 min), tracked by a new `auth_time` claim. Reads ride the full session; only the routes where a stale session does damage are re-verified, so a dashboard whose main use is watching a live stream does not re-auth hourly. `auth_time` is `#[serde(default)]`, so a cookie issued before the field existed reads as 0 — infinitely stale. Such a session keeps working for reads and cannot perform privileged actions. Fail-closed and self-healing on next sign-in. The refusal carries an RFC 6750 `WWW-Authenticate` error code, because the client's correct response differs from a plain 401: the user IS signed in and needs to prove it again. `api.service.js` acts on that and redirects through `/oauth/start` — otherwise a stale-session delete surfaces as a generic "Request failed" with no hint that signing in again fixes it. P3 — cookie shadowing. `read_cookie` returned the FIRST match, and RFC 6265 §5.4 sends longer-`Path` cookies first. Cookies are not isolated by port or scheme, so any other service on the host — or a plain-HTTP MITM injecting Set-Cookie — could plant `ruview_session=<their own validly signed session>; Path=/ui`. The victim sent both, the attacker's first, and it verified because it genuinely was signed: silent session takeover, with `/oauth/status` reporting the attacker's account. The signature was doing its job throughout, which is why "it's signed" never answered this. `__Host-` would, but requires `Secure`, and RuView is routinely reached over plain HTTP on a LAN. So both credential paths now accept only when EXACTLY ONE candidate verifies. An attacker can still cause a refusal by planting a second valid cookie — a nuisance — but no longer a takeover. Planting junk changes nothing, so this does not become a trivial DoS. Tests: +1 jwks (26-vs-1 fetch amplification), +4 step-up, +4 shadowing, +1 duplicate-name reader. Mutation-verified: reverting the stale-path guard gives 26 fetches; reverting to first-match cookie reads fails `a_shadowing_cookie_cannot_silently_take_over_the_session`. Verified: workspace 176 suites clean under CI flags, ruview-auth 62+25+2 with --all-features, UI 22. ADR-271: P1/P2/P3 marked RESOLVED with the analysis retained, since it explains why each fix has the shape it does. Co-Authored-By: Ruflo & AQE
Architecture Decision Records
Latest proposed decisions:
- ADR-264: Versioned wire protocol for RTL8720F CFR and Range-FFT reports
- ADR-263: Adopt RTL8720F 2.4 GHz FMCW radar as an optional RuView sensing platform
This folder contains 182 Architecture Decision Records (ADRs) that document every significant technical choice in the RuView / WiFi-DensePose project. (The index tables below list a curated subset per domain; see the directory listing for the full set.)
Why ADRs?
Building a system that turns WiFi signals into human pose estimation involves hundreds of non-obvious decisions: which signal processing algorithms to use, how to bridge ESP32 firmware to a Rust pipeline, whether to run inference on-device or on a server, how to handle multi-person separation with limited subcarriers.
ADRs capture the context, options considered, decision made, and consequences for each of these choices. They serve three purposes:
-
Institutional memory — Six months from now, anyone (human or AI) can read why we chose IIR bandpass filters over FIR for vital sign extraction, not just see the code.
-
AI-assisted development — When an AI agent works on this codebase, ADRs give it the constraints and rationale it needs to make changes that align with the existing architecture. Without them, AI-generated code tends to drift — reinventing patterns that already exist, contradicting earlier decisions, or optimizing for the wrong tradeoffs.
-
Review checkpoints — Each ADR is a reviewable artifact. When a proposed change touches the architecture, the ADR forces the author to articulate tradeoffs before writing code, not after.
ADRs and Domain-Driven Design
The project uses Domain-Driven Design (DDD) to organize code into bounded contexts — each with its own language, types, and responsibilities. ADRs and DDD work together:
- ADRs define boundaries: ADR-029 (RuvSense) established multistatic sensing as a separate bounded context from single-node CSI. ADR-042 (CHCI) defined a new aggregate root for coherent channel imaging.
- DDD models define the language: The RuvSense domain model defines terms like "coherence gate", "dwell time", and "TDM slot" that ADRs reference precisely.
- Together they prevent drift: An AI agent reading ADR-039 knows that edge processing tiers are configured via NVS keys, not compile-time flags — because the ADR says so. The DDD model tells it which aggregate owns that configuration.
How ADRs are structured
Each ADR follows a consistent format:
- Context — What problem or gap prompted this decision
- Decision — What we chose to do and how
- Consequences — What improved, what got harder, and what risks remain
- References — Related ADRs, papers, and code paths
Statuses: Proposed (under discussion), Accepted (approved and/or implemented), Superseded (replaced by a later ADR).
ADR Index
Hardware and firmware
| ADR | Title | Status |
|---|---|---|
| ADR-012 | ESP32 CSI Sensor Mesh for Distributed Sensing | Accepted (partial) |
| ADR-018 | ESP32 Development Implementation Path | Proposed |
| ADR-028 | ESP32 Capability Audit and Witness Record | Accepted |
| ADR-029 | RuvSense Multistatic Sensing Mode (TDM, channel hopping) | Proposed |
| ADR-032 | Multistatic Mesh Security Hardening | Accepted |
| ADR-039 | ESP32-S3 Edge Intelligence Pipeline (on-device vitals) | Accepted (hardware-validated) |
| ADR-040 | WASM Programmable Sensing (Tier 3) | Accepted |
| ADR-041 | WASM Module Collection (65 edge modules) | Accepted (hardware-validated) |
| ADR-044 | Provisioning Tool Enhancements | Proposed |
| ADR-110 | ESP32-C6 firmware extension — Wi-Fi 6 / 802.15.4 / TWT / LP-core | Accepted, P1-P10 complete, firmware-side substrate closed at v0.7.0-esp32. Companion docs: WITNESS-LOG-110 (13 §A0.x entries · 99.56 % cross-board RX · 104.1 µs smoothed sync stdev · ≤100 µs target met), ADR-110-REVIEW-GUIDE (one-page reviewer tour), ADR-110-BRANCH-STATE (coordination map vs feat/adr-115-ha-mqtt-matter). Host decoders + tests: Python SyncPacketParser (10) + Rust wifi_densepose_hardware::SyncPacket (15), cross-language hex pin gates drift. |
Signal processing and sensing
| ADR | Title | Status |
|---|---|---|
| ADR-013 | Feature-Level Sensing on Commodity Gear | Accepted |
| ADR-014 | SOTA Signal Processing Algorithms | Accepted |
| ADR-021 | Vital Sign Detection (breathing, heart rate) | Partial |
| ADR-030 | Persistent Field Model and Drift Detection | Proposed |
| ADR-033 | CRV Signal Line Sensing Integration | Proposed |
| ADR-037 | Multi-Person Pose Detection from Single ESP32 | Proposed |
| ADR-042 | Coherent Human Channel Imaging (beyond CSI) | Proposed |
| ADR-134 | First-Class Channel Impulse Response (CIR) Support | Proposed |
| ADR-135 | Empty-Room Baseline Calibration (per-subcarrier Welford statistics) | Proposed |
Machine learning and training
| ADR | Title | Status |
|---|---|---|
| ADR-005 | SONA Self-Learning for Pose Estimation | Partial |
| ADR-006 | GNN-Enhanced CSI Pattern Recognition | Partial |
| ADR-015 | Public Dataset Strategy (MM-Fi, Wi-Pose) | Accepted |
| ADR-016 | RuVector Training Pipeline Integration | Accepted |
| ADR-017 | RuVector Signal + MAT Integration | Proposed |
| ADR-020 | Migrate AI Inference to Rust (ONNX Runtime) | Accepted |
| ADR-023 | Trained DensePose Model with RuVector Pipeline | Proposed |
| ADR-024 | Project AETHER: Contrastive CSI Embeddings | Required |
| ADR-027 | Project MERIDIAN: Cross-Environment Generalization | Proposed |
| ADR-149 | AetherArena: public spatial-intelligence benchmark on Hugging Face | Proposed |
| ADR-150 | RF Foundation Encoder: pose-preserving, subject/room/device-invariant CSI embedding | Proposed |
| ADR-151 | Per-Room Calibration & Specialized Model Training (room-first → bank of small ruVector specialists) | Proposed |
| ADR-152 | WiFi-Pose SOTA 2026 Intake: geometry-conditioned calibration, external benchmarks, foundation-encoder recipe | Proposed |
Platform and UI
| ADR | Title | Status |
|---|---|---|
| ADR-019 | Sensing-Only UI with Gaussian Splats | Accepted |
| ADR-022 | Windows WiFi Enhanced Fidelity (multi-BSSID) | Partial |
| ADR-025 | macOS CoreWLAN WiFi Sensing | Proposed |
| ADR-031 | RuView Sensing-First RF Mode | Proposed |
| ADR-034 | Expo React Native Mobile App | Accepted |
| ADR-035 | Live Sensing UI Accuracy and Data Transparency | Accepted |
| ADR-036 | Training Pipeline UI Integration | Proposed |
| ADR-043 | Sensing Server UI API Completion (14 endpoints) | Accepted |
| ADR-115 | Home Assistant integration via MQTT auto-discovery + Matter bridge (HA-DISCO + HA-FABRIC + HA-MIND) | Accepted (MQTT track) / Proposed (Matter SDK P8b) |
| ADR-169 | adam-mode — light theme toggle for the three.js realtime demo | Proposed |
| ADR-170 | yoga-mode — yoga pose detection, classification, and scoring for the three.js realtime demo | Proposed |
Architecture and infrastructure
| ADR | Title | Status |
|---|---|---|
| ADR-001 | WiFi-Mat Disaster Detection Architecture | Accepted |
| ADR-002 | RuVector RVF Integration Strategy | Superseded |
| ADR-003 | RVF Cognitive Containers for CSI | Proposed |
| ADR-004 | HNSW Vector Search for Fingerprinting | Partial |
| ADR-007 | Post-Quantum Cryptography for Sensing | Proposed |
| ADR-008 | Distributed Consensus for Multi-AP | Proposed |
| ADR-009 | RVF WASM Runtime for Edge Deployment | Proposed |
| ADR-010 | Witness Chains for Audit Trail Integrity | Proposed |
| ADR-011 | Proof-of-Reality and Mock Elimination | Proposed |
| ADR-026 | Survivor Track Lifecycle (MAT crate) | Accepted |
| ADR-038 | Sublinear GOAP for Roadmap Optimization | Proposed |
| ADR-095 | rvCSI — Edge RF Sensing Runtime Platform | Proposed |
| ADR-096 | rvCSI — Crate Topology, the napi-c Shim, and the napi-rs Node Surface | Proposed |
| ADR-097 | Adopt rvCSI as RuView's primary CSI runtime (phased adoption) | Proposed |
| ADR-098 | Evaluate ruvnet/midstream for RuView's CSI / WebSocket / mesh pipeline |
Rejected |
| ADR-099 | Adopt midstream as RuView's real-time introspection + low-latency tap | Proposed |
| ADR-263 | @ruvnet/ruview npm harness — deep review + optimization strategy |
Proposed |
| ADR-264 | @ruvnet/rvagent MCP server + @ruv/ruview-cli — deep review + optimization strategy |
Proposed |
| ADR-265 | RuView npm distribution strategy — CI gate, provenance, version single-sourcing, namespace | Proposed |
Related
- DDD Domain Models — Bounded context definitions, aggregate roots, and ubiquitous language
- User Guide — Setup, API reference, and hardware instructions
- Build Guide — Building from source