mirror of
https://github.com/ruvnet/RuView
synced 2026-08-11 20:41:44 +00:00
feat(bfld): ADR-291 — wifi-veil emission-shaping countermeasure as advisory dependency
Adds wifi-veil (pinned git rev, dependency-free, MIT/Apache-2.0) to the workspace and gates it behind a new 'veil' feature in wifi-densepose-bfld. The bfld::veil module exposes deterministic attacker-vs-protector shield assessments (re-ID collapse, throughput ratio, energy-conservation audit) with a mandatory SYNTHETIC/L0 evidence label. Advisory only: no RF emission, no frame mutation, BFLD invariants I1-I3 untouched. Default build unchanged; 6 new feature-gated tests pass. Co-Authored-By: claude-flow <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_015TcKegTS7QqhWPC2L2SzaS
This commit is contained in:
@@ -0,0 +1,83 @@
|
|||||||
|
# ADR-291: WiFi Veil integration — emission-shaping countermeasure as an advisory BFLD dependency
|
||||||
|
|
||||||
|
- **Status**: Accepted — initial implementation (this PR)
|
||||||
|
- **Date**: 2026-08-10
|
||||||
|
- **Deciders**: ruv
|
||||||
|
- **Tags**: privacy, bfld, bfi, wifi-veil, countermeasure, dependency
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
RuView's BFLD layer (ADR-118, ADR-141) senses via beamforming feedback while
|
||||||
|
enforcing structural privacy invariants on data entering the node. The 2026
|
||||||
|
research sweep identified the complementary, unaddressed surface: a node's own
|
||||||
|
*outgoing* BFI is unencrypted and enables passive third-party
|
||||||
|
re-identification (BFId, ACM CCS 2025); IEEE 802.11bf-2025 shipped with no
|
||||||
|
privacy mechanism; and no commercial product occupies the countermeasure
|
||||||
|
category.
|
||||||
|
|
||||||
|
[`wifi-veil`](https://github.com/ruvnet/wifi-veil) (codename VEIL, extracted
|
||||||
|
from this monorepo as a standalone crate) models a compliant emission-shaping
|
||||||
|
defense: keyed Givens rotations over the fine subspace of compressed
|
||||||
|
beamforming reports, energy-preserving (never jamming), reversible by a
|
||||||
|
keyed legitimate receiver. The crate is dependency-free, deterministic,
|
||||||
|
std-only, WASM-ready, dual MIT/Apache-2.0, and explicitly SYNTHETIC/L0: it
|
||||||
|
models waveform controls and never drives a radio.
|
||||||
|
|
||||||
|
RuView should consume this capability rather than re-implement it, giving the
|
||||||
|
sensing stack a defensive counterpart under one evidence regime.
|
||||||
|
|
||||||
|
## Options considered
|
||||||
|
|
||||||
|
1. **Vendor the veil sources into a RuView crate.** Rejected: forks the
|
||||||
|
witness-pinned upstream and duplicates maintenance.
|
||||||
|
2. **crates.io dependency.** Not yet available (v0.1.0 unpublished at
|
||||||
|
decision time); revisit when released.
|
||||||
|
3. **Git dependency pinned to an exact rev, feature-gated in
|
||||||
|
`wifi-densepose-bfld`.** Chosen.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
- Add `wifi-veil` to `v2/Cargo.toml` `[workspace.dependencies]` as a git
|
||||||
|
dependency pinned to rev `018468b5d2bf41f35c552910f35659830af0eb91`
|
||||||
|
(v0.1.0). Exact-rev pinning preserves provenance and reproducibility for a
|
||||||
|
pre-release upstream; bumping the rev is an explicit, reviewable change.
|
||||||
|
- Gate it in `wifi-densepose-bfld` behind a new `veil` feature
|
||||||
|
(`veil = ["std", "dep:wifi-veil"]`), off by default — the default build
|
||||||
|
remains dependency-light and unchanged.
|
||||||
|
- New `bfld::veil` module (advisory-only):
|
||||||
|
- `ShieldAssessment`: stable projection of wifi-veil's deterministic
|
||||||
|
attacker-vs-protector `ExperimentReport` (re-ID accuracy shield-off/on,
|
||||||
|
chance level, throughput ratio, energy-conservation audit), always
|
||||||
|
carrying the `SYNTHETIC/L0` evidence label.
|
||||||
|
- `assess` / `assess_default`: run the deterministic experiment.
|
||||||
|
- `optimized_shield`: wrap `hyper_optimize` to derive the
|
||||||
|
optimizer-shipped shield config plus its verifying assessment.
|
||||||
|
- Boundaries, stated structurally and in docs:
|
||||||
|
- **Advisory only.** Nothing in the integration emits RF, alters frames,
|
||||||
|
or relaxes any BFLD gate/invariant (I1–I3 untouched).
|
||||||
|
- **Evidence honesty.** Every veil-derived figure is labeled
|
||||||
|
`SYNTHETIC/L0`; no MEASURED claim is possible from this path (hardware
|
||||||
|
validation lives in wifi-veil's own P5 roadmap).
|
||||||
|
- ESP32 nodes cannot shield their own feedback (per wifi-veil's platform
|
||||||
|
matrix); the integration therefore informs posture and reporting, not
|
||||||
|
on-node emission control.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- RuView gains a sense-and-defend posture no commercial offering has, under
|
||||||
|
a single claim taxonomy.
|
||||||
|
- First git dependency in the workspace: builds now fetch one pinned
|
||||||
|
external rev. Acceptable: the crate is dependency-free, small, witness-
|
||||||
|
pinned upstream, and license-compatible (MIT OR Apache-2.0 into MIT).
|
||||||
|
- Feature-gated consumers (e.g. sensing-server privacy reporting, the
|
||||||
|
desktop UI) can surface shield assessments later without new deps.
|
||||||
|
- When wifi-veil publishes to crates.io, switch the workspace entry to a
|
||||||
|
version requirement in a follow-up ADR amendment.
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
|
||||||
|
- `cargo test -p wifi-densepose-bfld --features veil` — determinism,
|
||||||
|
shield-reduces-re-ID, compliance (energy conservation), chance-band
|
||||||
|
attainment, evidence labeling, optimizer wrapper.
|
||||||
|
- `cargo test -p wifi-densepose-bfld` (default features) — unchanged
|
||||||
|
behavior with the feature off.
|
||||||
@@ -148,6 +148,7 @@ Statuses: **Proposed** (under discussion), **Accepted** (approved and/or impleme
|
|||||||
| [ADR-288](ADR-288-public-benchmark-evaluation-harness.md) | Public-benchmark evaluation harness — Widar3.0 ingest, split protocols, leakage guards | Accepted (initial implementation) |
|
| [ADR-288](ADR-288-public-benchmark-evaluation-harness.md) | Public-benchmark evaluation harness — Widar3.0 ingest, split protocols, leakage guards | Accepted (initial implementation) |
|
||||||
| [ADR-289](ADR-289-wideband-80211ax-csi-ingest.md) | Wideband 802.11ax CSI ingest — FeitCSI/AX210 adapter, subcarrier-agnostic plumbing | Accepted (initial implementation) |
|
| [ADR-289](ADR-289-wideband-80211ax-csi-ingest.md) | Wideband 802.11ax CSI ingest — FeitCSI/AX210 adapter, subcarrier-agnostic plumbing | Accepted (initial implementation) |
|
||||||
| [ADR-290](ADR-290-vitals-ground-truth-rig.md) | Vitals ground-truth rig — reference ingest, alignment, agreement metrics | Accepted (initial implementation) |
|
| [ADR-290](ADR-290-vitals-ground-truth-rig.md) | Vitals ground-truth rig — reference ingest, alignment, agreement metrics | Accepted (initial implementation) |
|
||||||
|
| [ADR-291](ADR-291-wifi-veil-integration.md) | WiFi Veil integration — emission-shaping countermeasure as advisory BFLD dependency | Accepted (initial implementation) |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
Generated
+6
@@ -11365,6 +11365,7 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"static_assertions",
|
"static_assertions",
|
||||||
"thiserror 2.0.18",
|
"thiserror 2.0.18",
|
||||||
|
"wifi-veil",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -11804,6 +11805,11 @@ dependencies = [
|
|||||||
"wifi-densepose-geo",
|
"wifi-densepose-geo",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wifi-veil"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "git+https://github.com/ruvnet/wifi-veil?rev=018468b5d2bf41f35c552910f35659830af0eb91#018468b5d2bf41f35c552910f35659830af0eb91"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
|
|||||||
@@ -121,6 +121,10 @@ categories = ["science", "computer-vision", "wasm"]
|
|||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
# Core utilities
|
# Core utilities
|
||||||
thiserror = "2.0"
|
thiserror = "2.0"
|
||||||
|
# WiFi Veil — compliant-waveform countermeasure against unauthorized WiFi
|
||||||
|
# sensing (ADR-291). Dependency-free, deterministic, SYNTHETIC-only leaf;
|
||||||
|
# pinned to an exact rev because the crate is consumed pre-crates.io-release.
|
||||||
|
wifi-veil = { git = "https://github.com/ruvnet/wifi-veil", rev = "018468b5d2bf41f35c552910f35659830af0eb91" }
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
|||||||
@@ -25,6 +25,10 @@ mqtt = ["std", "dep:rumqttc"]
|
|||||||
# enables privacy_class = 1 (derived) mode and the SoulMatchOracle gate
|
# enables privacy_class = 1 (derived) mode and the SoulMatchOracle gate
|
||||||
# exemption. Disabled by default per the structural class-2 default.
|
# exemption. Disabled by default per the structural class-2 default.
|
||||||
soul-signature = []
|
soul-signature = []
|
||||||
|
# WiFi Veil advisory integration (ADR-291): deterministic attacker-vs-
|
||||||
|
# protector assessment of BFI identity leakage and emission-shaping shield
|
||||||
|
# configs. All numbers it produces are SYNTHETIC / L0 by construction.
|
||||||
|
veil = ["std", "dep:wifi-veil"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
thiserror.workspace = true
|
thiserror.workspace = true
|
||||||
@@ -36,6 +40,7 @@ serde_json = { workspace = true, optional = true }
|
|||||||
# MQTT publisher backend (optional). Matches the `rumqttc` choice already in
|
# MQTT publisher backend (optional). Matches the `rumqttc` choice already in
|
||||||
# `wifi-densepose-sensing-server` so both crates share TLS / version posture.
|
# `wifi-densepose-sensing-server` so both crates share TLS / version posture.
|
||||||
rumqttc = { version = "0.24", default-features = false, features = ["use-rustls"], optional = true }
|
rumqttc = { version = "0.24", default-features = false, features = ["use-rustls"], optional = true }
|
||||||
|
wifi-veil = { workspace = true, optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
proptest.workspace = true
|
proptest.workspace = true
|
||||||
|
|||||||
@@ -53,6 +53,9 @@ pub mod signature_hasher;
|
|||||||
pub mod sink;
|
pub mod sink;
|
||||||
pub mod soul_channels;
|
pub mod soul_channels;
|
||||||
pub mod soul_match;
|
pub mod soul_match;
|
||||||
|
/// WiFi Veil advisory integration (ADR-291). Feature-gated: `veil`.
|
||||||
|
#[cfg(feature = "veil")]
|
||||||
|
pub mod veil;
|
||||||
|
|
||||||
pub use coherence_gate::{CoherenceGate, MatchOutcome, NullOracle, SoulMatchOracle};
|
pub use coherence_gate::{CoherenceGate, MatchOutcome, NullOracle, SoulMatchOracle};
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
|
|||||||
@@ -0,0 +1,187 @@
|
|||||||
|
//! WiFi Veil advisory integration (ADR-291).
|
||||||
|
//!
|
||||||
|
//! Bridges BFLD's privacy layer to the [`wifi-veil`](https://github.com/ruvnet/wifi-veil)
|
||||||
|
//! countermeasure crate: a deterministic, dependency-free attacker-vs-protector
|
||||||
|
//! model of BFI identity leakage and keyed emission-shaping ("shield")
|
||||||
|
//! configurations.
|
||||||
|
//!
|
||||||
|
//! # Evidence discipline
|
||||||
|
//!
|
||||||
|
//! Everything this module produces is **`SYNTHETIC` / evidence level L0** by
|
||||||
|
//! construction: `wifi-veil` models compliant waveform controls on synthetic
|
||||||
|
//! scenes and never touches a radio. Assessments quantify the *modeled*
|
||||||
|
//! re-identification risk of unprotected beamforming feedback and the *modeled*
|
||||||
|
//! effect of a shield; they are advisory inputs to privacy posture, never
|
||||||
|
//! measured hardware claims. See ADR-291 and the wifi-veil README.
|
||||||
|
//!
|
||||||
|
//! # Relationship to BFLD invariants
|
||||||
|
//!
|
||||||
|
//! BFLD's structural invariants (I1–I3, see the crate README) govern data that
|
||||||
|
//! *enters* this node. WiFi Veil addresses the complementary surface: what this
|
||||||
|
//! node's own *outgoing* feedback leaks to passive third parties. The
|
||||||
|
//! integration is advisory-only — nothing here emits RF, alters frames, or
|
||||||
|
//! relaxes a BFLD gate.
|
||||||
|
|
||||||
|
use wifi_veil::{experiment, ExperimentConfig};
|
||||||
|
|
||||||
|
/// Evidence label attached to every veil-derived figure.
|
||||||
|
///
|
||||||
|
/// Matches the repository-wide claim taxonomy (CLAUDE.md): synthetic model
|
||||||
|
/// output, reproduced by `cargo test`, not measured on hardware.
|
||||||
|
pub const VEIL_EVIDENCE: &str = "SYNTHETIC/L0";
|
||||||
|
|
||||||
|
/// Summary of one deterministic attacker-vs-protector experiment.
|
||||||
|
///
|
||||||
|
/// A thin, stable projection of [`wifi_veil::ExperimentReport`] carrying only
|
||||||
|
/// the figures BFLD consumers need, plus the mandatory evidence label.
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct ShieldAssessment {
|
||||||
|
/// Number of candidate identities in the synthetic scene.
|
||||||
|
pub identities: usize,
|
||||||
|
/// Ideal chance-level re-identification accuracy (`1 / identities`).
|
||||||
|
pub chance_level: f32,
|
||||||
|
/// Modeled passive re-identification accuracy with the shield **off**.
|
||||||
|
pub reid_accuracy_off: f32,
|
||||||
|
/// Modeled passive re-identification accuracy with the shield **on**.
|
||||||
|
pub reid_accuracy_on: f32,
|
||||||
|
/// Modeled protected-link throughput as a fraction of baseline.
|
||||||
|
pub throughput_ratio: f64,
|
||||||
|
/// `output_energy / input_energy` of a representative protected frame.
|
||||||
|
/// ~1.0 means the control is energy-preserving (compliant, not jamming).
|
||||||
|
pub energy_ratio: f32,
|
||||||
|
/// True iff the energy ratio is within tolerance of 1.0.
|
||||||
|
pub energy_conserving: bool,
|
||||||
|
/// True iff the shield drove re-identification into the accepted
|
||||||
|
/// chance band.
|
||||||
|
pub drives_to_chance: bool,
|
||||||
|
/// Evidence label; always [`VEIL_EVIDENCE`].
|
||||||
|
pub evidence: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ShieldAssessment {
|
||||||
|
/// Residual re-identification margin above chance with the shield on.
|
||||||
|
///
|
||||||
|
/// `0.0` (or below) means the modeled attacker is at or below chance;
|
||||||
|
/// larger values mean residual identity leakage in the model.
|
||||||
|
#[must_use]
|
||||||
|
pub fn residual_reid_margin(&self) -> f32 {
|
||||||
|
self.reid_accuracy_on - self.chance_level
|
||||||
|
}
|
||||||
|
|
||||||
|
/// One-line human-readable summary, evidence-tagged.
|
||||||
|
#[must_use]
|
||||||
|
pub fn summary(&self) -> String {
|
||||||
|
format!(
|
||||||
|
"[{}] re-ID {:.1}% -> {:.1}% (chance {:.1}%, {} identities), \
|
||||||
|
throughput {:.1}%, energy ratio {:.6} ({})",
|
||||||
|
self.evidence,
|
||||||
|
self.reid_accuracy_off * 100.0,
|
||||||
|
self.reid_accuracy_on * 100.0,
|
||||||
|
self.chance_level * 100.0,
|
||||||
|
self.identities,
|
||||||
|
self.throughput_ratio * 100.0,
|
||||||
|
self.energy_ratio,
|
||||||
|
if self.energy_conserving {
|
||||||
|
"energy-conserving"
|
||||||
|
} else {
|
||||||
|
"NOT energy-conserving"
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<wifi_veil::ExperimentReport> for ShieldAssessment {
|
||||||
|
fn from(r: wifi_veil::ExperimentReport) -> Self {
|
||||||
|
Self {
|
||||||
|
identities: r.identities,
|
||||||
|
chance_level: r.chance_level,
|
||||||
|
reid_accuracy_off: r.accuracy_shield_off,
|
||||||
|
reid_accuracy_on: r.accuracy_shield_on,
|
||||||
|
throughput_ratio: r.throughput_ratio,
|
||||||
|
energy_ratio: r.compliance.energy_ratio,
|
||||||
|
energy_conserving: r.compliance.energy_conserving,
|
||||||
|
drives_to_chance: r.drives_to_chance(),
|
||||||
|
evidence: VEIL_EVIDENCE,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Run the deterministic attacker-vs-protector experiment for `cfg`.
|
||||||
|
///
|
||||||
|
/// Fully deterministic: identical configs produce identical assessments.
|
||||||
|
#[must_use]
|
||||||
|
pub fn assess(cfg: &ExperimentConfig) -> ShieldAssessment {
|
||||||
|
experiment::run(cfg).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Run the experiment with wifi-veil's shipped default scene and shield.
|
||||||
|
#[must_use]
|
||||||
|
pub fn assess_default() -> ShieldAssessment {
|
||||||
|
assess(&ExperimentConfig::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Derive the optimizer-shipped shield configuration and its verifying
|
||||||
|
/// assessment for `base`.
|
||||||
|
///
|
||||||
|
/// Wraps [`wifi_veil::hyper_optimize`]: the returned shield uses the
|
||||||
|
/// spec-allowed throughput-optimal feedback resolution and a Givens-pass
|
||||||
|
/// count grown by the privacy margin factor.
|
||||||
|
#[must_use]
|
||||||
|
pub fn optimized_shield(base: &ExperimentConfig) -> (wifi_veil::ShieldConfig, ShieldAssessment) {
|
||||||
|
let hyper = wifi_veil::hyper_optimize(base);
|
||||||
|
let assessment = hyper.report.into();
|
||||||
|
(hyper.shield, assessment)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn default_assessment_is_deterministic() {
|
||||||
|
let a = assess_default();
|
||||||
|
let b = assess_default();
|
||||||
|
assert_eq!(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn shield_reduces_modeled_reid_accuracy() {
|
||||||
|
let a = assess_default();
|
||||||
|
assert!(
|
||||||
|
a.reid_accuracy_on < a.reid_accuracy_off,
|
||||||
|
"shield-on accuracy {} must be below shield-off {}",
|
||||||
|
a.reid_accuracy_on,
|
||||||
|
a.reid_accuracy_off
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn default_shield_is_compliant_and_at_chance() {
|
||||||
|
let a = assess_default();
|
||||||
|
assert!(a.energy_conserving, "veil must be energy-preserving");
|
||||||
|
assert!(a.drives_to_chance, "shipped default must reach chance band");
|
||||||
|
assert!(a.throughput_ratio > 0.9, "throughput ratio {} too low", a.throughput_ratio);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn evidence_label_is_synthetic_l0() {
|
||||||
|
let a = assess_default();
|
||||||
|
assert_eq!(a.evidence, VEIL_EVIDENCE);
|
||||||
|
assert!(a.summary().starts_with("[SYNTHETIC/L0]"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn residual_margin_matches_fields() {
|
||||||
|
let a = assess_default();
|
||||||
|
let m = a.residual_reid_margin();
|
||||||
|
assert!((m - (a.reid_accuracy_on - a.chance_level)).abs() < f32::EPSILON);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn optimized_shield_verifies() {
|
||||||
|
let (shield, assessment) = optimized_shield(&ExperimentConfig::default());
|
||||||
|
assert!(shield.enabled);
|
||||||
|
assert!(assessment.drives_to_chance);
|
||||||
|
assert!(assessment.energy_conserving);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user