From ab8d7a858342fc58821501ddf9831ebfb2e5c5e2 Mon Sep 17 00:00:00 2001 From: ruv Date: Sun, 24 May 2026 19:58:12 -0400 Subject: [PATCH] docs(adr-118/p6.14): crate README.md + Cargo.toml readme field (327/327 GREEN) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Iter 49. Ships the crate's first README — genuinely missing artifact. crates.io renders this file; the rendered page is what downstream operators see when they `cargo doc --open` or browse the registry. Added: - v2/crates/wifi-densepose-bfld/README.md (~135 lines): * Three structural invariants (I1/I2/I3) table with enforcement mechanism per invariant * Quickstart snippet: in-process consumer (BfldPipeline::process) * Quickstart snippet: production worker (BfldPipelineHandle + bootstrap helpers) * Feature flag matrix (std / serde-json / mqtt / soul-signature) * Two runnable example invocations * Testing matrix (no_default / default / mqtt) * Companion artifacts pointer (ADRs, research bundle, HA blueprints, CI workflow) * ADR cross-reference table (ADR-118 through ADR-123) * BFLD_MQTT_BROKER env-var doc for live mosquitto opt-in - v2/crates/wifi-densepose-bfld/Cargo.toml: readme = "README.md" (so crates.io picks it up on publish) - v2/crates/wifi-densepose-bfld/tests/crate_readme.rs (8 tests): readme_documents_three_structural_invariants readme_documents_feature_flag_matrix readme_documents_both_runnable_examples readme_documents_three_test_invocations readme_references_companion_adrs_118_through_123 readme_quickstart_uses_canonical_public_api (8 symbol-presence checks: BfldPipeline::new, BfldConfig::new, SignatureHasher::new, SensingInputs, IdentityEmbedding::from_raw, pipeline.process, publish_availability_online, publish_discovery, BfldPipelineHandle::spawn, PipelineInput) readme_points_at_research_bundle_and_blueprints readme_documents_env_gated_mosquitto_integration ADR-124 status (iter step 0 sibling check): - docs/adr/ADR-124-rvagent-mcp-ruvector-npm-integration.md unchanged at 431 lines. SENSE-BRIDGE scope remains orthogonal. ACs progressed: - ADR-118 §2.1 documentation surface — crates.io / cargo doc landing page now exists. Operators encountering wifi-densepose-bfld for the first time get the three structural invariants, quickstart snippets for both deployment patterns, feature matrix, and ADR map without having to read source. Test config: - cargo test --no-default-features → 101 passed (crate_readme cfg-out) - cargo test → 327 passed (319 + 8) Out of scope (next iter target): - PR-readiness pivot. CHANGELOG, witness bundle, AC closeout table. External-resource-gated work (KIT BFId, Pi5/Nexmon) still skipped. Co-Authored-By: claude-flow --- v2/crates/wifi-densepose-bfld/Cargo.toml | 1 + v2/crates/wifi-densepose-bfld/README.md | 116 ++++++++++++++++++ .../wifi-densepose-bfld/tests/crate_readme.rs | 80 ++++++++++++ 3 files changed, 197 insertions(+) create mode 100644 v2/crates/wifi-densepose-bfld/README.md create mode 100644 v2/crates/wifi-densepose-bfld/tests/crate_readme.rs diff --git a/v2/crates/wifi-densepose-bfld/Cargo.toml b/v2/crates/wifi-densepose-bfld/Cargo.toml index fa626cdb..beaca229 100644 --- a/v2/crates/wifi-densepose-bfld/Cargo.toml +++ b/v2/crates/wifi-densepose-bfld/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "wifi-densepose-bfld" description = "BFLD — Beamforming Feedback Layer for Detection. Privacy-gated WiFi BFI sensing primitives. See ADR-118." +readme = "README.md" version.workspace = true edition.workspace = true authors.workspace = true diff --git a/v2/crates/wifi-densepose-bfld/README.md b/v2/crates/wifi-densepose-bfld/README.md new file mode 100644 index 00000000..bd77a924 --- /dev/null +++ b/v2/crates/wifi-densepose-bfld/README.md @@ -0,0 +1,116 @@ +# wifi-densepose-bfld + +**BFLD — Beamforming Feedback Layer for Detection.** Privacy-gated WiFi sensing primitives derived from 802.11ac/ax Beamforming Feedback Information (BFI). See [ADR-118](../../../docs/adr/ADR-118-bfld-beamforming-feedback-layer-for-detection.md) for the umbrella architecture decision and [`docs/research/BFLD/`](../../../docs/research/BFLD/) for the full design dossier. + +## Three structural invariants + +The crate enforces three privacy invariants **structurally** (via the type system + memory hygiene), not by policy text: + +| ID | Invariant | Enforced by | +|----|-----------|-------------| +| **I1** | Raw BFI never exits the node | [`Sink`] marker-trait hierarchy + [`PrivacyClass::Raw.allows_network() == false`] | +| **I2** | Identity embedding is in-RAM-only | [`IdentityEmbedding`] has no `Serialize` / `Clone` / `Copy` + `Drop` zeroizes storage | +| **I3** | Cross-site identity correlation is cryptographically impossible | [`SignatureHasher`] per-site BLAKE3-keyed hash with daily epoch rotation | + +## Quickstart + +Minimal in-process consumer (see `examples/bfld_minimal.rs`): + +```rust +use wifi_densepose_bfld::{ + BfldConfig, BfldPipeline, IdentityEmbedding, SensingInputs, + SignatureHasher, EMBEDDING_DIM, SITE_SALT_LEN, +}; + +let mut pipeline = BfldPipeline::new( + BfldConfig::new("seed-01") + .with_signature_hasher(SignatureHasher::new([0xAB; SITE_SALT_LEN])), +); + +let event = pipeline + .process( + SensingInputs { /* timestamp, presence, motion, ... */ + timestamp_ns: 1_700_000_000_000_000_000, presence: true, + motion: 0.42, person_count: 1, sensing_confidence: 0.91, + sep: 0.2, stab: 0.2, consist: 0.2, risk_conf: 0.2, + rf_signature_hash: None, + }, + Some(IdentityEmbedding::from_raw([0.05; EMBEDDING_DIM])), + ) + .expect("low-risk emit"); + +println!("{}", event.to_json().unwrap()); +``` + +Production worker-thread + HA-DISCO publishing (see `examples/bfld_handle.rs`): + +```rust +use wifi_densepose_bfld::{ + publish_availability_online, publish_discovery, BfldConfig, BfldPipeline, + BfldPipelineHandle, PipelineInput, PrivacyClass, SignatureHasher, +}; + +// Bootstrap: retained "online" + 6 retained HA-DISCO config payloads. +publish_availability_online(&mut publisher, "seed-01")?; +publish_discovery(&mut publisher, "seed-01", PrivacyClass::Anonymous)?; + +// Spawn worker. Per-frame: handle.send(PipelineInput { inputs, embedding }). +let handle = BfldPipelineHandle::spawn( + BfldPipeline::new(BfldConfig::new("seed-01") + .with_signature_hasher(SignatureHasher::new(salt))), + publisher, +); +handle.send(PipelineInput { inputs, embedding })?; +``` + +## Feature flags + +| Feature | Default | Pulls in | Enables | +|---------|---------|----------|---------| +| `std` | ✅ | (no extra deps) | `BfldFrame`, `BfldPayload`, `BfldPipeline`, `BfldPipelineHandle`, `BfldEvent`, `BfldEmitter`, `PrivacyGate`, MQTT topic router, HA discovery | +| `serde-json` | ✅ | `serde` + `serde_json` | `BfldEvent::to_json()`, custom `rf_signature_hash: "blake3:"` serializer, `privacy_class` string encoding | +| `mqtt` | — | `rumqttc 0.24` (`use-rustls`) | `RumqttPublisher`, `connect_with_lwt`, live broker integration | +| `soul-signature`| — | — | `--features` gate signaling Soul Signature deployment (ADR-118 §1.4, ADR-120 §2.7, ADR-121 §2.6) | + +Stripping to `--no-default-features` keeps the no_std-compatible core (`BfldFrameHeader`, `PrivacyClass`, `Sink` traits, `CoherenceGate`, `SignatureHasher`, `IdentityEmbedding`, `EmbeddingRing`, risk-score function + `GateAction`). + +## Examples + +```sh +cargo run -p wifi-densepose-bfld --example bfld_minimal # in-process consumer +cargo run -p wifi-densepose-bfld --example bfld_handle # worker-thread + HA-DISCO +``` + +## Companion artifacts + +| Path | Purpose | +|------|---------| +| `docs/adr/ADR-118` through `ADR-123` | Architecture decisions | +| `docs/research/BFLD/` | 13,544-word design bundle (11 files) | +| `v2/crates/cog-ha-matter/blueprints/bfld/` | Three HA operator blueprints (presence-lighting, motion-HVAC, identity-risk-anomaly) | +| `.github/workflows/bfld-mqtt-integration.yml` | CI matrix incl. live mosquitto Docker service | + +## ADR cross-reference + +| ADR | Scope | +|-----|-------| +| [118](../../../docs/adr/ADR-118-bfld-beamforming-feedback-layer-for-detection.md) | Umbrella + invariants I1/I2/I3 | +| [119](../../../docs/adr/ADR-119-bfld-frame-format-and-wire-protocol.md) | Wire format (86-byte header + payload sections + CRC-32/ISO-HDLC) | +| [120](../../../docs/adr/ADR-120-bfld-privacy-class-and-hash-rotation.md) | 4 privacy classes + per-site keyed hash with daily rotation | +| [121](../../../docs/adr/ADR-121-bfld-identity-risk-scoring.md) | Multiplicative risk score + coherence-gate hysteresis + Soul Signature exemption | +| [122](../../../docs/adr/ADR-122-bfld-ruview-ha-matter-exposure.md) | HA-DISCO + Matter cluster boundary + MQTT topic routing | +| [123](../../../docs/adr/ADR-123-bfld-capture-path-nexmon-and-esp32.md) | Pi 5 / Nexmon capture adapter + ESP32 self-only mode | + +## Testing + +```sh +cargo test -p wifi-densepose-bfld --no-default-features # no_std-compatible core +cargo test -p wifi-densepose-bfld # default std + serde-json +cargo test -p wifi-densepose-bfld --features mqtt # incl. rumqttc smoke +``` + +A `BFLD_MQTT_BROKER=tcp://localhost:1883` env var unlocks the live-broker `mosquitto_integration` test suite (see `tests/mosquitto_integration.rs`). + +## License + +MIT — same as the wifi-densepose workspace. diff --git a/v2/crates/wifi-densepose-bfld/tests/crate_readme.rs b/v2/crates/wifi-densepose-bfld/tests/crate_readme.rs new file mode 100644 index 00000000..fdea4df1 --- /dev/null +++ b/v2/crates/wifi-densepose-bfld/tests/crate_readme.rs @@ -0,0 +1,80 @@ +//! Validate the crate README. Same `include_str!` pattern iter-30/47/48 used +//! for HA blueprints / examples. crates.io renders this file, so doc drift +//! against the actual public API is operator-visible. + +#![cfg(feature = "std")] + +const README: &str = include_str!("../README.md"); + +#[test] +fn readme_documents_three_structural_invariants() { + for needle in [ + "**I1**", + "**I2**", + "**I3**", + "Raw BFI never exits the node", + "Identity embedding is in-RAM-only", + "Cross-site identity correlation", + ] { + assert!(README.contains(needle), "README missing invariant text: {needle}"); + } +} + +#[test] +fn readme_documents_feature_flag_matrix() { + for needle in ["`std`", "`serde-json`", "`mqtt`", "`soul-signature`"] { + assert!(README.contains(needle), "feature flag {needle} missing from README"); + } +} + +#[test] +fn readme_documents_both_runnable_examples() { + assert!(README.contains("cargo run -p wifi-densepose-bfld --example bfld_minimal")); + assert!(README.contains("cargo run -p wifi-densepose-bfld --example bfld_handle")); +} + +#[test] +fn readme_documents_three_test_invocations() { + assert!(README.contains("cargo test -p wifi-densepose-bfld --no-default-features")); + assert!(README.contains("cargo test -p wifi-densepose-bfld --features mqtt")); +} + +#[test] +fn readme_references_companion_adrs_118_through_123() { + for adr in ["118", "119", "120", "121", "122", "123"] { + assert!(README.contains(adr), "README must cite ADR-{adr}"); + } +} + +#[test] +fn readme_quickstart_uses_canonical_public_api() { + // The quickstart snippets must reference the actual operator-facing + // surface — drift here would mislead first-time users. + for needle in [ + "BfldPipeline::new", + "BfldConfig::new", + "SignatureHasher::new", + "SensingInputs", + "IdentityEmbedding::from_raw", + "pipeline\n .process", + "publish_availability_online", + "publish_discovery", + "BfldPipelineHandle::spawn", + "PipelineInput", + ] { + assert!(README.contains(needle), "quickstart missing canonical API: {needle}"); + } +} + +#[test] +fn readme_points_at_research_bundle_and_blueprints() { + assert!(README.contains("docs/research/BFLD/")); + assert!(README.contains("cog-ha-matter/blueprints/bfld/")); + assert!(README.contains("bfld-mqtt-integration.yml")); +} + +#[test] +fn readme_documents_env_gated_mosquitto_integration() { + assert!(README.contains("BFLD_MQTT_BROKER=tcp://localhost:1883")); + assert!(README.contains("mosquitto_integration")); +}