mac-os support stopped #56

Open
opened 2026-03-01 15:05:51 +08:00 by bencium · 2 comments
bencium commented 2026-03-01 15:05:51 +08:00 (Migrated from github.com)

The wifi-densepose sensing server's --source wifi path only works on Windows (uses
netsh commands). On macOS, --source auto silently falls back to simulation. The user
wants real WiFi RSSI sensing on their M2 Pro MacBook — no simulated data.

macOS 26.3 has removed the legacy airport CLI. CoreWLAN (via Swift) is the working
API and provides real RSSI, channel, SSID, and noise data for all visible networks.
BSSIDs are redacted by macOS privacy policy, but SSID+channel uniquely identifies
each AP for the pipeline.

Goal: Add a macOS CoreWLAN adapter so --source wifi works on macOS with real RSSI
data flowing through the 8-stage sensing pipeline (motion detection, breathing
extraction, presence).

Approach

  1. Create Swift helper binary: macos-wifi-scan

A small Swift CLI that calls CoreWLAN and outputs JSON to stdout. The Rust adapter
calls this via Command::new() — same pattern as netsh on Windows.

File: rust-port/wifi-densepose-rs/tools/macos-wifi-scan/main.swift

// Calls CWWiFiClient.shared().interface().scanForNetworks()
// Outputs JSON array: [{ssid, rssi, noise, channel, band, phy_mode}, ...]
// Also includes connected network info

Build with: swiftc -framework CoreWLAN -framework Foundation -o macos-wifi-scan
main.swift

  1. Create Rust macOS adapter: MacosCoreWlanScanner

File: rust-port/wifi-densepose-rs/crates/wifi-densepose-wifiscan/src/adapter/macos_sc
anner.rs

Implements the WlanScanPort trait:

  • scan() — calls the Swift helper, parses JSON output, returns Vec
  • connected() — returns the connected network's observation
  • Uses SSID:channel as a synthetic BSSID (since macOS redacts real BSSIDs)
  • Converts RSSI dBm to amplitude via existing rssi_to_amplitude()
  • Maps channel to BandType via existing BandType::from_channel()
  • Maps PHY mode string to RadioType
  1. Register adapter in module

File: rust-port/wifi-densepose-rs/crates/wifi-densepose-wifiscan/src/adapter/mod.rs

Add pub mod macos_scanner; and re-export MacosCoreWlanScanner.

File: rust-port/wifi-densepose-rs/crates/wifi-densepose-wifiscan/src/lib.rs

Add re-export at crate root.

  1. Add macOS WiFi probe + task to sensing server

File: rust-port/wifi-densepose-rs/crates/wifi-densepose-sensing-server/src/main.rs

Changes:

  • Add probe_macos_wifi() — runs the Swift helper with --probe flag, returns true if
    it outputs valid JSON
  • Add macos_wifi_task() — same structure as windows_wifi_task() but calls the Swift
    helper instead of netsh. Feeds results into the same BssidRegistry +
    WindowsWifiPipeline (rename internally to RssiPipeline is optional but these work
    as-is)
  • Update auto-detection (line ~1697): add probe_macos_wifi() check between ESP32 and
    simulation fallback
  • Update --source wifi match (line ~1806): dispatch to macos_wifi_task() on macOS,
    windows_wifi_task() on Windows
  1. Build integration

File: rust-port/wifi-densepose-rs/crates/wifi-densepose-sensing-server/Cargo.toml

No new Rust dependencies needed — std::process::Command + serde_json (already
available) handle the Swift subprocess.

The Swift binary is built separately and expected at a known path relative to the
server binary or in $PATH.

Files to Create
File: rust-port/wifi-densepose-rs/tools/macos-wifi-scan/main.swift
Purpose: Swift CoreWLAN helper binary
────────────────────────────────────────
File: rust-port/wifi-densepose-rs/tools/macos-wifi-scan/build.sh
Purpose: Build script for the Swift binary
────────────────────────────────────────
File: rust-port/wifi-densepose-rs/crates/wifi-densepose-wifiscan/src/adapter/macos_sc
anner.rs
Purpose: Rust macOS adapter
Files to Modify
File: crates/wifi-densepose-wifiscan/src/adapter/mod.rs
Change: Add macos_scanner module + exports
────────────────────────────────────────
File: crates/wifi-densepose-wifiscan/src/lib.rs
Change: Add MacosCoreWlanScanner re-export
────────────────────────────────────────
File: crates/wifi-densepose-sensing-server/src/main.rs
Change: Add probe_macos_wifi(), macos_wifi_task(), update auto-detect + source
dispatch
Existing Code to Reuse

  • WlanScanPort trait — crates/wifi-densepose-wifiscan/src/port/scan_port.rs
  • BssidObservation, BssidId, BandType, RadioType —
    crates/wifi-densepose-wifiscan/src/domain/bssid.rs
  • BssidRegistry — crates/wifi-densepose-wifiscan/src/domain/registry.rs
  • WindowsWifiPipeline — crates/wifi-densepose-wifiscan/src/pipeline/orchestrator.rs
  • MultiApFrame — crates/wifi-densepose-wifiscan/src/domain/frame.rs
  • WifiScanError — crates/wifi-densepose-wifiscan/src/error.rs
  • windows_wifi_task() as template —
    crates/wifi-densepose-sensing-server/src/main.rs:486-663
  • rssi_to_amplitude(), pseudo_phase() —
    crates/wifi-densepose-wifiscan/src/domain/bssid.rs:191-217

Verification

  1. Build the Swift helper: cd tools/macos-wifi-scan && ./build.sh
  2. Test it standalone: ./macos-wifi-scan — should print JSON with real RSSI values
  3. Build the Rust workspace: cargo build --release
  4. Run tests: cargo test --workspace
  5. Start server: ./target/release/sensing-server --source wifi --ui-path ../../ui
  6. Verify real data: curl http://localhost:8080/api/v1/sensing/latest — should show
    source: "wifi:" with real RSSI values, not simulated
  7. Open UI: http://localhost:8080/ui/index.html — signal field should respond to
    physical movement near the Mac
  8. Verify vitals: curl http://localhost:8080/api/v1/vital-signs — breathing/motion
    from real RSSI variance
The wifi-densepose sensing server's --source wifi path only works on Windows (uses netsh commands). On macOS, --source auto silently falls back to simulation. The user wants real WiFi RSSI sensing on their M2 Pro MacBook — no simulated data. macOS 26.3 has removed the legacy airport CLI. CoreWLAN (via Swift) is the working API and provides real RSSI, channel, SSID, and noise data for all visible networks. BSSIDs are redacted by macOS privacy policy, but SSID+channel uniquely identifies each AP for the pipeline. Goal: Add a macOS CoreWLAN adapter so --source wifi works on macOS with real RSSI data flowing through the 8-stage sensing pipeline (motion detection, breathing extraction, presence). Approach 1. Create Swift helper binary: macos-wifi-scan A small Swift CLI that calls CoreWLAN and outputs JSON to stdout. The Rust adapter calls this via Command::new() — same pattern as netsh on Windows. File: rust-port/wifi-densepose-rs/tools/macos-wifi-scan/main.swift // Calls CWWiFiClient.shared().interface().scanForNetworks() // Outputs JSON array: [{ssid, rssi, noise, channel, band, phy_mode}, ...] // Also includes connected network info Build with: swiftc -framework CoreWLAN -framework Foundation -o macos-wifi-scan main.swift 2. Create Rust macOS adapter: MacosCoreWlanScanner File: rust-port/wifi-densepose-rs/crates/wifi-densepose-wifiscan/src/adapter/macos_sc anner.rs Implements the WlanScanPort trait: - scan() — calls the Swift helper, parses JSON output, returns Vec<BssidObservation> - connected() — returns the connected network's observation - Uses SSID:channel as a synthetic BSSID (since macOS redacts real BSSIDs) - Converts RSSI dBm to amplitude via existing rssi_to_amplitude() - Maps channel to BandType via existing BandType::from_channel() - Maps PHY mode string to RadioType 3. Register adapter in module File: rust-port/wifi-densepose-rs/crates/wifi-densepose-wifiscan/src/adapter/mod.rs Add pub mod macos_scanner; and re-export MacosCoreWlanScanner. File: rust-port/wifi-densepose-rs/crates/wifi-densepose-wifiscan/src/lib.rs Add re-export at crate root. 4. Add macOS WiFi probe + task to sensing server File: rust-port/wifi-densepose-rs/crates/wifi-densepose-sensing-server/src/main.rs Changes: - Add probe_macos_wifi() — runs the Swift helper with --probe flag, returns true if it outputs valid JSON - Add macos_wifi_task() — same structure as windows_wifi_task() but calls the Swift helper instead of netsh. Feeds results into the same BssidRegistry + WindowsWifiPipeline (rename internally to RssiPipeline is optional but these work as-is) - Update auto-detection (line ~1697): add probe_macos_wifi() check between ESP32 and simulation fallback - Update --source wifi match (line ~1806): dispatch to macos_wifi_task() on macOS, windows_wifi_task() on Windows 5. Build integration File: rust-port/wifi-densepose-rs/crates/wifi-densepose-sensing-server/Cargo.toml No new Rust dependencies needed — std::process::Command + serde_json (already available) handle the Swift subprocess. The Swift binary is built separately and expected at a known path relative to the server binary or in $PATH. Files to Create File: rust-port/wifi-densepose-rs/tools/macos-wifi-scan/main.swift Purpose: Swift CoreWLAN helper binary ──────────────────────────────────────── File: rust-port/wifi-densepose-rs/tools/macos-wifi-scan/build.sh Purpose: Build script for the Swift binary ──────────────────────────────────────── File: rust-port/wifi-densepose-rs/crates/wifi-densepose-wifiscan/src/adapter/macos_sc anner.rs Purpose: Rust macOS adapter Files to Modify File: crates/wifi-densepose-wifiscan/src/adapter/mod.rs Change: Add macos_scanner module + exports ──────────────────────────────────────── File: crates/wifi-densepose-wifiscan/src/lib.rs Change: Add MacosCoreWlanScanner re-export ──────────────────────────────────────── File: crates/wifi-densepose-sensing-server/src/main.rs Change: Add probe_macos_wifi(), macos_wifi_task(), update auto-detect + source dispatch Existing Code to Reuse - WlanScanPort trait — crates/wifi-densepose-wifiscan/src/port/scan_port.rs - BssidObservation, BssidId, BandType, RadioType — crates/wifi-densepose-wifiscan/src/domain/bssid.rs - BssidRegistry — crates/wifi-densepose-wifiscan/src/domain/registry.rs - WindowsWifiPipeline — crates/wifi-densepose-wifiscan/src/pipeline/orchestrator.rs - MultiApFrame — crates/wifi-densepose-wifiscan/src/domain/frame.rs - WifiScanError — crates/wifi-densepose-wifiscan/src/error.rs - windows_wifi_task() as template — crates/wifi-densepose-sensing-server/src/main.rs:486-663 - rssi_to_amplitude(), pseudo_phase() — crates/wifi-densepose-wifiscan/src/domain/bssid.rs:191-217 Verification 1. Build the Swift helper: cd tools/macos-wifi-scan && ./build.sh 2. Test it standalone: ./macos-wifi-scan — should print JSON with real RSSI values 3. Build the Rust workspace: cargo build --release 4. Run tests: cargo test --workspace 5. Start server: ./target/release/sensing-server --source wifi --ui-path ../../ui 6. Verify real data: curl http://localhost:8080/api/v1/sensing/latest — should show source: "wifi:<your-SSID>" with real RSSI values, not simulated 7. Open UI: http://localhost:8080/ui/index.html — signal field should respond to physical movement near the Mac 8. Verify vitals: curl http://localhost:8080/api/v1/vital-signs — breathing/motion from real RSSI variance
ruvnet commented 2026-03-01 15:11:49 +08:00 (Migrated from github.com)

Great write-up — the approach is solid and aligns well with the existing adapter architecture.

Confirming the current state:

  • The wifi-densepose-wifiscan crate has a clean adapter pattern in src/adapter/ — currently NetshBssidScanner (Tier 1, netsh subprocess) and WlanApiScanner (Tier 2, async wrapper). A MacosCoreWlanScanner following the same Command::new() + parse JSON pattern is the right fit.
  • No macOS/Darwin code exists anywhere in the Rust workspace today — this is a net-new platform path.
  • The tools/ directory doesn't exist yet, so the Swift helper creates a new convention for platform-specific helper binaries.

Notes on the proposed approach:

  1. Swift helper as subprocess — correct call. CoreWLAN is ObjC/Swift-only, and FFI bridging is not worth the complexity. A Swift CLI outputting JSON to stdout, called via Command::new(), matches the netsh pattern exactly.

  2. SSID:channel as synthetic BSSID — good workaround for Apple's BSSID redaction. Worth noting in the adapter docs that this means two APs on the same channel with the same SSID will collapse to one observation. Rare in practice but should be documented.

  3. --probe flag on the Swift helper — clean way to handle auto-detection without a full scan. The probe can check CoreWLAN availability and return a minimal JSON response.

  4. Pipeline reuse — the existing WindowsWifiPipeline / BssidRegistry / 8-stage pipeline works as-is since it operates on BssidObservation structs regardless of source. No changes needed there.

One addition to consider:

  • A #[cfg(target_os = "macos")] gate on the macos_scanner module and the macos_wifi_task() in main.rs so the macOS code doesn't compile on Windows/Linux (mirroring how netsh_scanner is Windows-specific).

Build integration:

The Swift binary build (swiftc -framework CoreWLAN ...) should probably be a build.sh as proposed, documented in the crate README, and noted as a prerequisite for --source wifi on macOS. A cargo build alone won't produce the Swift binary — that's fine as long as the server gives a clear error message when the helper isn't found in $PATH or alongside the binary.

This is a welcome addition. Tagged for implementation.

Great write-up — the approach is solid and aligns well with the existing adapter architecture. **Confirming the current state:** - The `wifi-densepose-wifiscan` crate has a clean adapter pattern in `src/adapter/` — currently `NetshBssidScanner` (Tier 1, `netsh` subprocess) and `WlanApiScanner` (Tier 2, async wrapper). A `MacosCoreWlanScanner` following the same `Command::new()` + parse JSON pattern is the right fit. - No macOS/Darwin code exists anywhere in the Rust workspace today — this is a net-new platform path. - The `tools/` directory doesn't exist yet, so the Swift helper creates a new convention for platform-specific helper binaries. **Notes on the proposed approach:** 1. **Swift helper as subprocess** — correct call. CoreWLAN is ObjC/Swift-only, and FFI bridging is not worth the complexity. A Swift CLI outputting JSON to stdout, called via `Command::new()`, matches the `netsh` pattern exactly. 2. **SSID:channel as synthetic BSSID** — good workaround for Apple's BSSID redaction. Worth noting in the adapter docs that this means two APs on the same channel with the same SSID will collapse to one observation. Rare in practice but should be documented. 3. **`--probe` flag on the Swift helper** — clean way to handle auto-detection without a full scan. The probe can check CoreWLAN availability and return a minimal JSON response. 4. **Pipeline reuse** — the existing `WindowsWifiPipeline` / `BssidRegistry` / 8-stage pipeline works as-is since it operates on `BssidObservation` structs regardless of source. No changes needed there. **One addition to consider:** - A `#[cfg(target_os = "macos")]` gate on the `macos_scanner` module and the `macos_wifi_task()` in `main.rs` so the macOS code doesn't compile on Windows/Linux (mirroring how `netsh_scanner` is Windows-specific). **Build integration:** The Swift binary build (`swiftc -framework CoreWLAN ...`) should probably be a `build.sh` as proposed, documented in the crate README, and noted as a prerequisite for `--source wifi` on macOS. A `cargo build` alone won't produce the Swift binary — that's fine as long as the server gives a clear error message when the helper isn't found in `$PATH` or alongside the binary. This is a welcome addition. Tagged for implementation.
ruvnet commented 2026-03-01 15:15:20 +08:00 (Migrated from github.com)

ADR-025 published: docs/adr/ADR-025-macos-corewlan-wifi-sensing.md

Codename ORCA (OS-native Radio Channel Acquisition). Key decisions:

  • Swift helper binary (tools/macos-wifi-scan/main.swift) shells out to CoreWLAN, outputs JSON — same subprocess pattern as NetshBssidScanner on Windows
  • sha256(ssid:channel)[:12] as synthetic BSSID when Apple redacts MAC addresses (no Location Services needed for basic sensing)
  • Full 8-stage pipeline reuse — macOS adapter produces BssidObservation identical to Windows path, all downstream signal intelligence runs as-is
  • #[cfg(target_os = "macos")] gating — zero impact on Windows/Linux builds
  • 3 new files, 3 modified files, 0 new Rust dependencies

Known limitation: CoreWLAN scan rate (~0.3 Hz) is slower than netsh (~2 Hz), so breathing extraction will be marginal. Presence and motion detection work fine.

Build/test target: Mac Mini (M2 Pro, macOS 26.3).

**ADR-025 published:** [`docs/adr/ADR-025-macos-corewlan-wifi-sensing.md`](https://github.com/ruvnet/wifi-densepose/blob/main/docs/adr/ADR-025-macos-corewlan-wifi-sensing.md) Codename **ORCA** (OS-native Radio Channel Acquisition). Key decisions: - **Swift helper binary** (`tools/macos-wifi-scan/main.swift`) shells out to CoreWLAN, outputs JSON — same subprocess pattern as `NetshBssidScanner` on Windows - **`sha256(ssid:channel)[:12]`** as synthetic BSSID when Apple redacts MAC addresses (no Location Services needed for basic sensing) - **Full 8-stage pipeline reuse** — macOS adapter produces `BssidObservation` identical to Windows path, all downstream signal intelligence runs as-is - **`#[cfg(target_os = "macos")]`** gating — zero impact on Windows/Linux builds - **3 new files, 3 modified files, 0 new Rust dependencies** Known limitation: CoreWLAN scan rate (~0.3 Hz) is slower than `netsh` (~2 Hz), so breathing extraction will be marginal. Presence and motion detection work fine. Build/test target: Mac Mini (M2 Pro, macOS 26.3).
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dearsky/wifi-densepose#56