Files
ruvnet--RuView/docs/research/sota-2026-05-22/R7-multilink-consistency.md
T
rUv bb92419ccb research(R7): Stoer-Wagner mincut detects adversarial CSI nodes 3/3 in synthetic (#704)
Premise: in a multi-node CSI mesh, all nodes see the same physical
scene through slightly different multipath. Their per-window CSI
vectors cluster tightly under cosine similarity. An adversarial node
(replay / shift / noise injection) sits *outside* that cluster. The
Stoer-Wagner minimum cut on the inter-node similarity graph isolates
it cleanly when the cut is sharp.

Demo synthesises 4 honest nodes (one real CSI window from the paired
data + per-node Gaussian noise 6 dB below signal) and 1 adversarial
node under three attack modes. Cosine-similarity matrix, then
Stoer-Wagner mincut, then check whether partition_B is the singleton
{4} — the adversarial node.

  Attack       Mincut value   Partition_B   Isolated?
  -------      ------------   -----------   ---------
  replay       3.4513         {4}           YES
  shift        3.5724         {4}           YES
  noise        2.5586         {4}           YES

Detection rate: 3/3 = 100%.

Architectural payoff: this is the primitive that fills the stub at
. ADR-103 v0.2.0
can wire it in directly. The mincut value also becomes a continuous
'mesh trustworthiness' metric for the cog-gateway dashboard.

Honest scope: the demo uses sloppy attackers. Adaptive attackers who
have read this note can almost certainly evade by adding calibrated
noise that keeps cosine similarity above the cluster floor. The next
research step is the Stackelberg-game extension. See the
'Honest scope of this result' section in the research note.

Connections:
* R5 — top-8 saliency subcarriers are the priority list for a
  more-targeted per-subcarrier consistency check.
* R8 — same primitive likely works at lower SNR with RSSI-only
  metrics; cluster structure is preserved by the band integral.

Files:
* examples/research-sota/r7_multilink_consistency.py — pure-NumPy
  Stoer-Wagner mincut + synthetic-adversary harness.
* examples/research-sota/r7_multilink_consistency_results.json —
  full result JSON for cross-tick reproducibility.
* docs/research/sota-2026-05-22/R7-multilink-consistency.md — note.
* docs/research/sota-2026-05-22/PROGRESS.md — updated index + Done.
2026-05-21 23:28:46 -04:00

5.6 KiB
Raw Blame History

R7 — Multi-link consistency detection via Stoer-Wagner mincut

Status: first measurement landed · 2026-05-22

Premise

The Cog fleet deployment story (ADR-100 + ADR-102 + ADR-103) puts multiple ESP32-S3 nodes in the same physical space, each reporting CSI to the same sensing-server. Today, the server trusts every node equally. That's fine when the adversary is "an indifferent universe", but the WiFi-CSI literature has known supply-chain attacks:

  • Replay — attacker captures a CSI stream from earlier and pumps it back in to fake "empty room" / "no fall" / "all-clear" states.
  • Constant shift — attacker biases one node's CSI by a constant, hoping the fusion stage averages it away while still poisoning per-node decisions.
  • Noise injection — attacker jams or otherwise produces pure-noise CSI that crosses the legitimate-traffic threshold of wDev_ProcessFiq-based packet filters.

A learned multi-node fusion (ADR-103 §"Multi-node fusion") will average these out if the adversary is the minority. But we need a primitive that detects the adversary so the fusion stage can drop them before averaging.

Algorithm (this thread)

Key insight: N honest observers of the same physical scene produce CSI vectors that cluster tightly under cosine similarity (their windows differ only by per-channel multipath noise). An adversarial node, regardless of attack mode, sits outside that cluster.

The cluster-outlier-detection primitive that fits this problem exactly is the Stoer-Wagner minimum cut on the inter-node cosine-similarity graph:

for each pair of nodes (i, j):
  W[i, j] = cos(flatten(csi_i), flatten(csi_j))

(value, partition_B) = stoer_wagner_mincut(W)

# partition_B is the "less-similar" side of the minimum cut.
# When the cut is sharp, partition_B is a singleton — the adversarial node.

ruvector-mincut already vendors this algorithm in the workspace (used by cog-pose-estimation for person-separable subcarrier grouping, see #491). The fusion stage in cog-person-count (fuse_with_mincut_clip()) has a stub that's exactly the consumer this primitive needs.

Demo measurement

examples/research-sota/r7_multilink_consistency.py — pure NumPy, no framework deps. Synthesises 4 honest CSI nodes (real scene from data/paired/... + per-node Gaussian noise 6 dB below signal) and 1 adversarial node under each of 3 attack modes:

Attack mode Description Mincut value Partition_B Adversarial isolated?
replay Stale window from earlier in the recording, +1% jitter 3.4513 {4} YES
shift Constant +3σ offset on every subcarrier 3.5724 {4} YES
noise Pure Gaussian noise at honest-node signal magnitude 2.5586 {4} YES

Detection rate: 3/3 = 100% on this synthetic scenario, with mincut value gaps that are well-separated from the within-honest-cluster connectivity (honest nodes have pairwise similarities >0.95, the adversarial node's similarity to any honest node is ≤0.5).

Honest scope of this result

This is a clean synthetic scenario with strong adversary signals. Real-world attacks are subtler:

  • A clever replay attacker would time the replay to overlap with stable empty-room periods, when honest-node CSI is also nearly-identical to the stale window. Detection rate degrades.
  • A partial-spectrum shift on a few subcarriers (instead of all 56) leaves enough true CSI that cosine similarity stays high. Need a per-subcarrier check, not whole-window.
  • An adaptive attacker who has read this research note and adds calibrated noise to evade the cluster check.

What this demo proves: the primitive works when the adversary is sloppy. The next research step is the adaptive-attacker version — Stackelberg game between detector and adversary on the same similarity-cut framework.

What this unlocks for the Cog stack

  • The stub at cog-person-count::fusion::fuse_with_mincut_clip() can become a real primitive: at each frame, run mincut on the cross-node CSI similarity graph, drop any node that gets isolated, then run the count head on the remaining nodes' fused features.
  • Same approach extends to cog-pose-estimation once we have a multi-node pose deployment.
  • The mincut value itself is a continuous "mesh trustworthiness score" that can be exposed as a mesh.trust metric in the cog-gateway dashboard.

10-year horizon

The "RF radio-democracy" story: every WiFi receiver in a building (phones, laptops, smart speakers — see R8's RSSI-only result) becomes a witness in a Byzantine-fault-tolerant mesh. The mincut consistency check generalises to N=many heterogeneous nodes. A single compromised phone can't poison the building-scale sensing state because mincut isolates it. This is the spatial-intelligence analogue of Byzantine consensus in distributed systems — published-2026-SOTA hasn't framed CSI security this way yet.

Connections back

  • R5 (subcarrier saliency) provides the priority list of subcarriers a detector should over-weight in the similarity metric — top-8 are [41, 52, 30, 31, 10, 35, 2, 38].
  • R8 (RSSI-only) shows the same primitive likely works at lower SNR with RSSI-only metrics; the cluster structure is preserved by the band integral.
  • ADR-103 (cog-person-count v0.2.0 plan) — this primitive is the explicit content of the fuse_with_mincut_clip() stub.

What's next on this thread

  • Adversarial-game framing: detector + attacker as a two-player Stackelberg game.
  • Per-subcarrier consistency check (not just whole-window cosine). Falls out of R5's saliency map naturally.
  • Live demo on real multi-node data once seed-1 comes back online or seed-2-5 get provisioned.