Compare commits

...

1 Commits

Author SHA1 Message Date
rUv b6420ac9ba fix(server): make synthetic CSI opt-in only (sibling fix to #937) (#979)
Background

Issue #937 in the cognitum-v0 appliance repo flagged that the
`cognitum-csi-capture` systemd unit shipped `--simulate` by default,
silently serving synthetic CSI tagged as production telemetry on
`/api/v1/sensor/stream`. That's a textbook trust-eroding pattern — the
single most-cited "where's the real data?" evidence external reviewers
(#943, #934) point at when they call the project AI-slop.

A grep across THIS tree surfaced the exact same anti-pattern in three
places:

  docker/docker-compose.yml:27        # auto (default) — probe ESP32, fall back to simulation
  docker/docker-entrypoint.sh:14      # CSI_SOURCE — data source: auto (default), ...
  main.rs:6435                        info!("No hardware detected, using simulation"); "simulate"

The sensing-server's `auto` source resolver at main.rs:6425-6440
silently fell back to synthetic with only an `info!` log line as the
signal. Downstream consumers calling `/api/v1/sensing/latest` or
`/ws/sensing` had no in-band way to know they were being served fake
data.

Fix

`auto` now refuses to fall back. When neither ESP32 UDP nor host WiFi
is detected, the server logs a clear `error!` explaining the situation
and exits 78 (EX_CONFIG). The error message names the two ways to
proceed: provision real hardware, or set `--source simulated` /
`CSI_SOURCE=simulated` explicitly. Existing operators who already use
`--source simulated` (or its legacy `simulate` alias) are unaffected —
the alias is preserved for back-compat.

Docker entrypoint comment, docker-compose comment, and the Tauri
desktop app's source-default path also updated to reflect the new
posture. The desktop app keeps its `simulated` default because it's
an explicit demo product — the value passed downstream is the
*explicit* `simulated`, not `auto`, so the server tags it correctly
and never lies about its data source.

Validation

  cargo build  -p wifi-densepose-sensing-server --no-default-features
  cargo test   -p wifi-densepose-sensing-server --no-default-features
  → 122 / 122 pass, build clean (existing pre-fix warnings unchanged).

Deployment

⚠ Breaking change for unattended deployments that relied on the
`auto → simulated` silent fallback. That is exactly the failure mode
this PR fixes: pretending to serve real sensing data when the source
is fake. Operators who genuinely want demo mode set
`CSI_SOURCE=simulated` explicitly; the error message and the
docker-compose comment both point them there.
2026-06-08 18:07:39 +02:00
4 changed files with 50 additions and 9 deletions
+5 -2
View File
@@ -24,10 +24,13 @@ services:
environment: environment:
- RUST_LOG=info - RUST_LOG=info
# CSI_SOURCE controls the data source for the sensing server. # CSI_SOURCE controls the data source for the sensing server.
# Options: auto (default) — probe for ESP32 UDP then fall back to simulation # Options: auto (default) — probe for ESP32 UDP then host WiFi; **fail
# hard with exit 78 if neither is detected**.
# Synthetic data is no longer a silent fallback
# (issue #937 fix) — operators must opt in.
# esp32 — receive real CSI frames from an ESP32 on UDP port 5005 # esp32 — receive real CSI frames from an ESP32 on UDP port 5005
# wifi — use host Wi-Fi RSSI/scan data (Windows netsh) # wifi — use host Wi-Fi RSSI/scan data (Windows netsh)
# simulated — generate synthetic CSI data (no hardware required) # simulated — explicitly generate synthetic CSI for demo mode
- CSI_SOURCE=${CSI_SOURCE:-auto} - CSI_SOURCE=${CSI_SOURCE:-auto}
# MODELS_DIR controls where the server scans for .rvf model files. # MODELS_DIR controls where the server scans for .rvf model files.
# Mount a host directory and set this to make models visible: # Mount a host directory and set this to make models visible:
+10 -1
View File
@@ -11,7 +11,16 @@
# docker run ruvnet/wifi-densepose:latest --model /app/models/my.rvf # docker run ruvnet/wifi-densepose:latest --model /app/models/my.rvf
# #
# Environment variables: # Environment variables:
# CSI_SOURCE — data source: auto (default), esp32, wifi, simulated # CSI_SOURCE — data source. Valid values:
# auto — try ESP32 then Windows WiFi, **fail-loud if no
# real hardware is detected** (issue #937 fix:
# the server no longer silently falls back to
# synthetic data — that's now opt-in only).
# esp32 — listen for UDP CSI on the configured port.
# wifi — Windows-native WiFi capture.
# simulated — explicit demo mode with synthetic CSI.
# Default is `auto`. Set CSI_SOURCE=simulated when you want
# fake data tagged as such; never set it implicitly.
# MODELS_DIR — directory to scan for .rvf model files (default: data/models) # MODELS_DIR — directory to scan for .rvf model files (default: data/models)
set -e set -e
@@ -108,8 +108,14 @@ pub async fn start_server(
cmd.args(["--log-level", log_level]); cmd.args(["--log-level", log_level]);
} }
// Set data source (default to "simulate" if not specified for demo mode) // Default to explicit "simulated" demo mode when the desktop user hasn't
let source = config.source.as_deref().unwrap_or("simulate"); // chosen a source — this is the *Tauri demo* app, not a production
// sensing endpoint, so the demo default is correct here. Critically, the
// value passed downstream is the **explicit** "simulated", not "auto",
// which means the sensing-server will tag the data as synthetic in its
// API responses rather than silently fall back (issue #937 fix in
// sensing-server's `auto` handler).
let source = config.source.as_deref().unwrap_or("simulated");
cmd.args(["--source", source]); cmd.args(["--source", source]);
// Redirect stdout/stderr to pipes for monitoring // Redirect stdout/stderr to pipes for monitoring
@@ -317,7 +323,7 @@ pub async fn restart_server(
log_level: None, log_level: None,
bind_address: None, bind_address: None,
server_path: None, server_path: None,
source: None, // Use default (simulate) source: None, // Falls through to explicit "simulated" — Tauri demo default.
} }
}; };
@@ -6421,7 +6421,17 @@ async fn main() {
info!(" UI path: {}", args.ui_path.display()); info!(" UI path: {}", args.ui_path.display());
info!(" Source: {}", args.source); info!(" Source: {}", args.source);
// Auto-detect data source // Auto-detect data source.
//
// Issue #937 / sibling fix: previously `auto` silently fell back to the
// synthetic data source when no ESP32 or Windows WiFi was reachable, with
// only an `info!` log line as the signal. Downstream API consumers
// (`/api/v1/sensing/latest`, `/ws/sensing`) had no in-band way to know they
// were being served fake CSI tagged as production telemetry. That is the
// exact "where's the real data?" pattern external reviewers (#943, #934)
// cited as the most damaging evidence of the project misrepresenting its
// posture. Synthetic-data is now opt-in only — operators who want demo
// mode must explicitly set `--source simulated` or `CSI_SOURCE=simulated`.
let source = match args.source.as_str() { let source = match args.source.as_str() {
"auto" => { "auto" => {
info!("Auto-detecting data source..."); info!("Auto-detecting data source...");
@@ -6432,10 +6442,23 @@ async fn main() {
info!(" Windows WiFi detected"); info!(" Windows WiFi detected");
"wifi" "wifi"
} else { } else {
info!(" No hardware detected, using simulation"); error!(
"simulate" "No real CSI source detected. Auto-detection refuses to silently \
fall back to synthetic data because that would expose downstream \
consumers (/api/v1/sensing/latest, /ws/sensing) to fake telemetry \
tagged as production. To run with synthetic data, set the source \
explicitly: --source simulated (or CSI_SOURCE=simulated in Docker). \
To use real hardware: provision an ESP32 to emit CSI on UDP :{} or \
install the Windows WiFi capture driver. See \
https://github.com/ruvnet/RuView/issues/937 for context.",
args.udp_port
);
std::process::exit(78); // EX_CONFIG
} }
} }
// "simulate" is a synonym for "simulated" (back-compat alias kept so
// existing operators who already opted in don't get broken by this fix).
"simulate" => "simulated",
other => other, other => other,
}; };