Files
ruvnet--RuView/ui
Dragan Spiridonov c72bbc15dd fix(auth): close /api/field bypass, two fail-opens, and a self-disarming test
Findings from a qe-court adversarial round (4 prosecutors across 2 vendors).
Each was verified against the code before being accepted; the ones below
reproduced, the rest are reported in the PR thread rather than acted on.

FATAL — `/api/field` was reachable with no credential, on both listeners.
The gate protected `/api/v1/*` by prefix. `/api/field` is the REST sibling of
`/ws/field` and serves the same signed FieldEvent stream — live presence, pose,
vitals. `/ws/field` was gated in this PR; its twin one path segment over was
not. Measured with RUVIEW_API_TOKEN set and no credential supplied:

    /api/v1/models  401      (control)
    /ws/field       401      (gated by this PR)
    /api/field      200      on :8080 AND :8765

Fixed by inverting the gate to deny-by-default with an explicit anonymous
allowlist (`/`, `/ui`, `/health`, `/oauth/`). A route added at a new path is
now gated because nobody exposed it, rather than exposed because nobody
protected it — the same inversion already applied to the scope gate.

FATAL — the wiring test disarmed itself exactly when it mattered.
`Server::start` returned an Option that all five tests turned into `return`,
so a server that failed to boot produced "5 passed" with zero assertions run,
and cargo swallows the skip line without --nocapture. The one test that
observes real wiring — the guard against both shipped bypasses — was silent
for any change that breaks startup, including a boot panic in the auth path.
It now panics with the child's stderr.

MAJOR — a malformed client-id list silently disabled the audience check.
An empty allowlist is the opt-out sentinel in verify.rs. `RUVIEW_OAUTH_CLIENT_IDS=","`
is non-empty, passes the guard, then filters to an empty Vec — turning the
audience boundary off with no log and admitting a token minted for any other
Cognitum product. Only a literal `*` may opt out now; anything else that parses
to nothing warns and falls back to the default. Same fail-open shape as the
scope denylist this PR already had to invert.

MAJOR — credentials were world-readable for a window on every refresh.
`fs::write` creates at 0666 & !umask (0644 by default), and both writers
chmodded afterwards. The existing permissions test asserted on the FINAL file
and passed throughout. Affected the CLI refresh token (rotated with reuse
detection — a thief who presents it first takes the session family) and the
browser session secret (the HMAC key for every session; stealing it forges any
account at any scope). Both now create with mode 0600 via OpenOptions.

MAJOR — ui/sw.js cached authenticated API responses.
Closing the /oauth/ leg left the /api/ leg open. `networkFirst` cached every
successful response, keyed by URL alone, purged by nothing at sign-out: sign in
as A, load sensing data, sign out, sign in as B, lose the network, and B is
served A's data with no authorization check. API responses are now network-only
— which is also the correct behaviour for a live sensing dashboard, where
replaying a stale reading can show a room occupied after the person left — plus
a cache purge on sign-out.

CI — 40 of ruview-auth's 87 tests never ran.
The workspace runs --no-default-features, which switches off the `login` and
`pkce` features. Measured: 47 tests vs 87. The whole interactive sign-in path —
credential storage, single-flight refresh, the file lock, the loopback callback
— was green locally and never executed in CI. Added an --all-features step.
(Checked the sensing-server for the same problem and did NOT find it:
bearer_auth's 49 and browser_session's 15 do run under CI flags.)

Tests: +2 wiring tests (one fails against the old gate with
"http port served /api/field to an anonymous caller", passes after), +3 UI
service-worker tests, +3 CLI scope tests, +1 temp-file permission test, +1
client-id parsing test. wifi-densepose-cli/src/auth.rs had zero tests and
builds its own scope string, so the library's least-privilege test said nothing
about what the CLI requests.

Verified: ruview-auth 61+25+2 pass (--all-features), sensing-server bearer_auth
50, browser_session 15, auth_wiring 7, workspace 25 suites clean, UI 22.

Co-Authored-By: Ruflo & AQE
2026-07-23 11:48:59 +02:00
..

WiFi DensePose UI

A modular, modern web interface for the WiFi DensePose human tracking system. Provides real-time monitoring, WiFi sensing visualization, and pose estimation from CSI (Channel State Information).

Architecture

The UI follows a modular architecture with clear separation of concerns:

ui/
├── app.js                    # Main application entry point
├── index.html                # HTML shell with tab structure
├── style.css                 # Complete CSS design system
├── config/
│   └── api.config.js         # API endpoints and configuration
├── services/
│   ├── api.service.js        # HTTP API client
│   ├── websocket.service.js  # WebSocket connection manager
│   ├── websocket-client.js   # Low-level WebSocket client
│   ├── pose.service.js       # Pose estimation API wrapper
│   ├── sensing.service.js    # WiFi sensing data service (live + simulation fallback)
│   ├── health.service.js     # Health monitoring API wrapper
│   ├── stream.service.js     # Streaming API wrapper
│   └── data-processor.js     # Signal data processing utilities
├── components/
│   ├── TabManager.js         # Tab navigation component
│   ├── DashboardTab.js       # Dashboard with live system metrics
│   ├── SensingTab.js         # WiFi sensing visualization (3D signal field, metrics)
│   ├── LiveDemoTab.js        # Live pose detection with setup guide
│   ├── HardwareTab.js        # Hardware configuration
│   ├── SettingsPanel.js      # Settings panel
│   ├── PoseDetectionCanvas.js # Canvas-based pose skeleton renderer
│   ├── gaussian-splats.js    # 3D Gaussian splat signal field renderer (Three.js)
│   ├── body-model.js         # 3D body model
│   ├── scene.js              # Three.js scene management
│   ├── signal-viz.js         # Signal visualization utilities
│   ├── environment.js        # Environment/room visualization
│   └── dashboard-hud.js      # Dashboard heads-up display
├── utils/
│   ├── backend-detector.js   # Auto-detect backend availability
│   ├── mock-server.js        # Mock server for testing
│   └── pose-renderer.js      # Pose rendering utilities
└── tests/
    ├── test-runner.html       # Test runner UI
    ├── test-runner.js         # Test framework and cases
    └── integration-test.html  # Integration testing page

Features

WiFi Sensing Tab

  • 3D Gaussian-splat signal field visualization (Three.js)
  • Real-time RSSI, variance, motion band, breathing band metrics
  • Presence/motion classification with confidence scores
  • Data source banner: green "LIVE - ESP32", yellow "RECONNECTING...", or red "SIMULATED DATA"
  • Sparkline RSSI history graph
  • "About This Data" card explaining CSI capabilities per sensor count

Live Demo Tab

  • WebSocket-based real-time pose skeleton rendering
  • Estimation Mode badge: green "Signal-Derived" or blue "Model Inference"
  • Setup Guide panel showing what each ESP32 count provides:
    • 1 ESP32: presence, breathing, gross motion
    • 2-3 ESP32s: body localization, motion direction
    • 4+ ESP32s + trained model: individual limb tracking, full pose
  • Debug mode with log export
  • Zone selection and force-reconnect controls
  • Performance metrics sidebar (frames, uptime, errors)

Dashboard

  • Live system health monitoring
  • Real-time pose detection statistics
  • Zone occupancy tracking
  • System metrics (CPU, memory, disk)
  • API status indicators

Hardware Configuration

  • Interactive antenna array visualization
  • Real-time CSI data display
  • Configuration panels
  • Hardware status monitoring

Data Sources

The sensing service (sensing.service.js) supports three connection states:

State Banner Color Description
LIVE - ESP32 Green Connected to the Rust sensing server receiving real CSI data
RECONNECTING Yellow (pulsing) WebSocket disconnected, retrying (up to 20 attempts)
SIMULATED DATA Red Fallback to client-side simulation after 5+ failed reconnects

Simulated frames include a _simulated: true marker so code can detect synthetic data.

Backends

Rust Sensing Server (primary)

The Rust-based wifi-densepose-sensing-server serves the UI and provides:

  • GET /health — server health
  • GET /api/v1/sensing/latest — latest sensing features
  • GET /api/v1/vital-signs — vital sign estimates (HR/RR)
  • GET /api/v1/model/info — RVF model container info
  • WS /ws/sensing — real-time sensing data stream
  • WS /api/v1/stream/pose — real-time pose keypoint stream

Python FastAPI (legacy)

The original Python backend on port 8000 is still supported. The UI auto-detects which backend is available via backend-detector.js.

Quick Start

cd docker/

# Default: auto-detects ESP32 on UDP 5005, falls back to simulation
docker-compose up

# Force real ESP32 data
CSI_SOURCE=esp32 docker-compose up

# Force simulation (no hardware needed)
CSI_SOURCE=simulated docker-compose up

Open http://localhost:3000/ui/index.html

With local Rust binary

cd v2
cargo build -p wifi-densepose-sensing-server --no-default-features

# Run with simulated data
../../target/debug/sensing-server --source simulated --tick-ms 100 --ui-path ../../ui --http-port 3000

# Run with real ESP32
../../target/debug/sensing-server --source esp32 --tick-ms 100 --ui-path ../../ui --http-port 3000

Open http://localhost:3000/ui/index.html

With Python HTTP server (legacy)

# Start FastAPI backend on port 8000
wifi-densepose start

# Serve the UI on port 3000
cd ui/
python -m http.server 3000

Open http://localhost:3000

Pose Estimation Modes

Mode Badge Requirements Accuracy
Signal-Derived Green 1+ ESP32, no model needed Presence, breathing, gross motion
Model Inference Blue 4+ ESP32s + trained .rvf model Full 17-keypoint COCO pose

To use model inference, start the server with a trained model:

sensing-server --source esp32 --model path/to/model.rvf --ui-path ./ui

Configuration

API Configuration

Edit config/api.config.js:

export const API_CONFIG = {
  BASE_URL: window.location.origin,
  API_VERSION: '/api/v1',
  WS_CONFIG: {
    RECONNECT_DELAY: 5000,
    MAX_RECONNECT_ATTEMPTS: 20,
    PING_INTERVAL: 30000
  }
};

Testing

Open tests/test-runner.html to run the test suite:

cd ui/
python -m http.server 3000
# Open http://localhost:3000/tests/test-runner.html

Test categories: API configuration, API service, WebSocket, pose service, health service, UI components, integration.

Styling

Uses a CSS design system with custom properties, dark/light mode, responsive layout, and component-based styling. Key variables in :root of style.css.

License

Part of the WiFi-DensePose system. See the main project LICENSE file.