cog-ha-matter (ADR-116 P4): live mDNS responder + handle

Closes the mDNS half of P4. `runtime::start_mdns_responder` binds
multicast via `mdns_sd::ServiceDaemon::new`, builds the
ServiceInfo from `MdnsService::to_service_info` (iter 9), and
registers — returning a typed handle that owns both daemon and
fullname.

Handle shape:

  pub struct MdnsResponderHandle {
      daemon: ServiceDaemon,
      fullname: String,
  }

  impl MdnsResponderHandle {
      pub fn fullname(&self) -> &str;
      pub fn shutdown(self) -> Result<(), mdns_sd::Error>;
  }
  impl Drop for MdnsResponderHandle { /* best-effort */ }

Why explicit `shutdown` + best-effort `Drop`: a clean shutdown
sends a goodbye packet so HA's discovery integration sees the
service leave (good UX — no stale device card). `Drop` is the
fallback for panics / process termination but swallows errors
since panicking-in-Drop would mask the real failure.

1 new live-I/O test:
  * mdns_responder_fullname_concatenates_instance_and_service_type
    — actually binds multicast on the loopback adapter, registers,
    asserts the fullname contains `_ruview-ha._tcp`, then
    shutdown()s. Confirmed working on Windows; CI environments
    where multicast bind is filtered will hit the gracefully-
    skipping early return rather than failing the suite.

64/64 cog tests green (63 → 64).

ADR-116 P4: mDNS half  (record-builder + ServiceInfo + live
responder), witness half  (chain + JSONL + file + Ed25519).
Last piece is the embedded rumqttd broker so external mosquitto
becomes optional.

Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
ruv
2026-05-23 18:31:38 -04:00
parent 34eced880f
commit 07b792715f
2 changed files with 94 additions and 1 deletions
+1 -1
View File
@@ -95,7 +95,7 @@ Ranked by build cost × user impact:
| **P1** | Research dossier ([`docs/research/ADR-116-ha-matter-cog-research.md`](../research/ADR-116-ha-matter-cog-research.md)) | ✅ **done** — 8 sections, 30+ citations, v1 scope ranked |
| **P2** | Cog crate scaffold (`v2/crates/cog-ha-matter/`) — Cargo.toml + `src/{lib,main,manifest}.rs`, workspace member, CLI args, `--print-manifest` flag, 2 manifest unit tests | ✅ **done**`cargo check` + `cargo test` green |
| **P3** | Wrap existing ADR-115 MQTT publisher as cog entry point | ✅ **wiring done**`main.rs` boots ADR-115's `publisher::spawn` via `runtime::spawn_publisher` thin wrapper, holds a long-lived `broadcast::Sender<VitalsSnapshot>`, awaits Ctrl-C. Live-handle test green without a broker. Next (P3.5): subscribe to sensing-server `/v1/snapshot` WS and republish into the channel. |
| **P4** | Seed-native enhancements (embedded broker, mDNS, witness) | in progress — (a) mDNS record-builder ✅. (b) Witness hash-chain ✅. (c) JSONL line serializer ✅. (d) File persistence + chain-level verify ✅. (e) Ed25519 signing layer ✅. **(f) mDNS ServiceInfo conversion ✅** — `MdnsService::to_service_info(hostname, ipv4)` produces the `mdns_sd::ServiceInfo` the responder daemon consumes; 3 tests verify service-type, port, TXT propagation. `mdns-sd = 0.11` aligned with the workspace's existing pin from `wifi-densepose-desktop`. (g) `ServiceDaemon::register` spawn + embedded rumqttd still pending — the remaining live-I/O pieces before P4 flips ✅. |
| **P4** | Seed-native enhancements (embedded broker, mDNS, witness) | in progress — **mDNS half complete:** record-builder ✅, ServiceInfo conversion ✅, **live responder** (`runtime::start_mdns_responder` binds multicast, registers, returns `MdnsResponderHandle` with explicit `shutdown()` + best-effort Drop). **Witness half complete:** hash-chain ✅, JSONL line serializer ✅, file persistence + chain-level verify ✅, Ed25519 signing ✅. **Remaining:** embedded rumqttd broker. |
| **P5** | RuVector-backed threshold learning (SONA adaptation) | pending |
| **P6** | Multi-Seed federation (cross-Seed dedup + witness) | pending |
| **P7** | Matter Bridge mode (depends on matter-rs / esp-matter readiness) | pending |