Implement reliable tracker detection for AirTag, Tile, Samsung SmartTag,
and other BLE trackers based on manufacturer data patterns, service UUIDs,
and advertising payload analysis.
Key changes:
- Add TrackerSignatureEngine with signatures for major tracker brands
- Device fingerprinting to track devices across MAC randomization
- Suspicious presence heuristics (persistence, following patterns)
- New API endpoints: /api/bluetooth/trackers, /diagnostics
- UI updates with tracker badges, confidence, and evidence display
- TSCM integration updated to use v2 tracker detection data
- Unit tests and smoke test scripts for validation
Detection is heuristic-based with confidence scoring (high/medium/low)
and evidence transparency. Backwards compatible with existing APIs.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add v2 capabilities, quick scan, deep scan, and status endpoints
- Add v2 networks, clients, probes, and channels endpoints
- Add v2 SSE stream, export (CSV/JSON), and baseline management
- Add recommendation_rank field to ChannelRecommendation model
The frontend was already wired up to call these v2 endpoints but they
were missing from the backend. This completes the WiFi module v2 API.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Backend:
- New utils/wifi/ package with models, scanner, parsers, channel analyzer
- Quick Scan mode using system tools (nmcli, iw, iwlist, airport)
- Deep Scan mode using airodump-ng with monitor mode
- Hidden SSID correlation engine
- Channel utilization analysis with recommendations
- v2 API endpoints at /wifi/v2/* with SSE streaming
- TSCM integration updated to use new scanner (backwards compatible)
Frontend:
- WiFi mode controller (wifi.js) with dual-mode support
- Channel utilization chart component (channel-chart.js)
- Updated wifi.html template with scan mode tabs and export
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix Signal Timeline not receiving events by using SignalTimeline.create()
for TSCM mode to maintain backward compatibility with addEvent() calls
- Lower RF detection thresholds for RTL-SDR compatibility (6dB margin,
-90dBm floor instead of 10dB/-70dBm)
- Reduce RF scan interval from 60s to 30s for quicker feedback
- Enable RF/SDR checkbox by default to match WiFi and Bluetooth
- Update status message when no signals detected
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The unified get_tscm_bluetooth_snapshot() no longer accepts a bt_interface
parameter as it handles interface selection internally.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Backend:
- Add device_key.py for stable device identification (identity > public MAC > fingerprint)
- Add distance.py with DistanceEstimator class (path-loss formula, EMA smoothing, confidence scoring)
- Add ring_buffer.py for time-windowed RSSI observation storage
- Extend BTDeviceAggregate with proximity_band, estimated_distance_m, distance_confidence, rssi_ema
- Add new API endpoints: /proximity/snapshot, /heatmap/data, /devices/<key>/timeseries
- Update TSCM integration to include new proximity fields
Frontend:
- Add proximity-radar.js: SVG radar with concentric rings, device dots positioned by distance
- Add timeline-heatmap.js: RSSI history grid with time buckets and color-coded signal strength
- Update bluetooth.js to initialize and feed data to new components
- Replace zone counters with radar visualization and zone summary
- Add proximity-viz.css for component styling
Tests:
- Add test_bluetooth_proximity.py with unit tests for device key stability, EMA smoothing,
distance estimation, band classification, and ring buffer functionality
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The SSE stream was sending events without proper event names.
Frontend uses addEventListener('device_update', ...) which only
works with named events. Now maps internal event types to proper
SSE event names:
- device -> device_update
- status/started -> scan_started
- status/stopped -> scan_stopped
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Major changes:
- Add utils/bluetooth/ package with DBus scanner, fallback scanners
(bleak, hcitool, bluetoothctl), device aggregation, and heuristics
- New unified API at /api/bluetooth/ with REST endpoints and SSE streaming
- Device observation aggregation with RSSI statistics and range bands
- Behavioral heuristics: new, persistent, beacon-like, strong+stable
- Frontend components: DeviceCard, MessageCard, RSSISparkline
- TSCM integration via get_tscm_bluetooth_snapshot() helper
- Unit tests for aggregator, heuristics, and API endpoints
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Set min-height: 300px on main grid
- Set min-height: 120px on individual panels
- Set min-height: 80px on panel content
- Change dashboard from height: 100% to min-height: 100%
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add verbose_results flag to store full device details in sweep results
- Add non-interactive mode (--non-interactive) to setup.sh
- Add ask_yes_no helper for interactive prompts with TTY detection
- Update reports.py to handle new results structure with fallbacks
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Added rtlamr mode for decoding utility meters (water, gas, electric)
- Starts rtl_tcp server first, then connects rtlamr to it
- Supports multiple message types: SCM, SCM+, IDM, NetIDM, R900, R900 BCD
- Added frequency presets for 912 MHz (NA) and 868 MHz (EU)
- Includes meter ID filtering and unique message options
- Updated setup.sh to check and install rtlamr and rtl_tcp
- Added UI components: navigation button, mode template, JavaScript functions
- Integrated into SDR/RF dropdown menu with lightning bolt icon
- Updates mode indicator with frequency when listening
- Added help documentation and requirements section
The previous detection logic incorrectly matched '-o' in help text for
version 4.x, causing startup failures. Now properly detects version:
- Version 4.0+: uses -j for JSON stdout
- Version 3.x: uses -o 4 for JSON stdout
Parses version from acarsdec output (e.g., "Acarsdec v4.3.1" or
"Acarsdec/acarsserv 3.7") to determine the correct flag.
Fixes: "invalid option -- 'o'" error on modern acarsdec builds
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The get_acarsdec_json_flag() function was defaulting to the obsolete '-o'
flag when detection failed, causing "invalid option -- 'o'" errors with
modern acarsdec builds from TLeconte repository.
Changes:
- Try both -h and --help flags for better compatibility
- Improve -j flag detection patterns
- Default to -j (modern standard) instead of -o
- Only use -o if explicitly documented in help text
This fixes ACARS decoder startup failures on systems where acarsdec was
built from source using setup.sh.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Change /tscm/playbooks to return array instead of dict
- Add id, name, category fields to each playbook for JS compatibility
- Fix tscmViewPlaybook JS to use correct field names (action/details/safety_note)
- Display when_to_escalate and documentation_required sections
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implement 9 major TSCM feature enhancements:
1. Capability & Coverage Reality Panel - Exposes what sweeps can/cannot
detect based on OS, privileges, adapters, and SDR limits
2. Baseline Diff & Health - Shows changes vs baseline with health scoring
(healthy/noisy/stale) based on age and device churn
3. Per-Device Timelines - Time-bucketed observations with RSSI stability,
movement patterns, and meeting correlation
4. Whitelist/Known-Good Registry + Case Grouping - Global and per-location
device registry with case management for sweeps/threats/notes
5. Meeting-Window Summary Enhancements - Tracks devices first seen during
meetings with scoring modifiers
6. Client-Ready PDF Report + Technical Annex - Executive summary, findings
by risk tier, JSON/CSV annex export
7. WiFi Advanced Indicators - Evil twin detection, probe request tracking,
deauth burst detection (auto-disables without monitor mode)
8. Bluetooth Risk Explainability - Proximity estimates, tracker brand
explanations, human-readable risk descriptions
9. Operator Playbooks - Procedural guidance by risk level with steps,
safety notes, and documentation requirements
All features include mandatory disclaimers, preserve existing architecture,
and follow TSCM best practices (no packet capture, no surveillance claims).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Objects (;) and items ()) were identified but position data was never
extracted, causing them to appear without location on the map. Added
parse_object() and parse_item() functions to properly extract name,
status, and coordinates.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Return 503 instead of 500 when grgsm_scanner not found
- Show clearer error message in UI when gr-gsm unavailable
- Update status display to show "Not Available" state
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add spectrum monitoring via rtl_power with configurable presets
- Add OpenCelliD tower integration with Leaflet map display
- Add grgsm_scanner integration for passive GSM cell detection (alpha)
- Add rules engine for anomaly detection and findings
- Add baseline recording and comparison system
- Add setup.sh support for gr-gsm installation on Debian/Ubuntu
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix emergency alerts triggering for non-emergency squawk codes (VFR 1200/7000, etc.)
by checking squawkInfo.type === 'emergency' before alerting
- Fix emergency filter to only show actual emergency squawk codes
- Add acarsdec version detection to support both -j (newer) and -o 4 (older) JSON flags
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add propagate=False to prevent child loggers from duplicating
messages through parent handler
- Only log SBS connection errors once until successful reconnect
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Capture local reference to audio_process at generator start to prevent
'NoneType' object has no attribute 'stdout' error when stop is called
concurrently from another request.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Problems fixed:
1. Added start_new_session=True to dump1090 Popen - creates proper process
group for clean shutdown
2. Use os.killpg() to kill entire process group when stopping ADS-B -
ensures child processes are terminated and device is released
3. Track active device index in adsb_active_device for debugging
4. Add device info to /adsb/status endpoint
5. Add logging when starting/stopping ADS-B with device info
These changes ensure the RTL-SDR device is properly released when ADS-B
stops, allowing another process (e.g., airband) to use a different device.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Backend changes (routes/aprs.py):
- Remove -q h flag from direwolf to enable audio level output
- Add parse_audio_level() to extract levels from direwolf output
- Add rate-limiting (max 10 updates/sec, min 2-level change)
- Push meter events to SSE queue as type='meter'
Frontend changes:
- Add signal meter widget to APRS sidebar
- Horizontal bar gauge with gradient (green->cyan->yellow->red)
- Numeric level display (0-100)
- "BURST" indicator for levels >70
- Status text (weak/moderate/strong signal)
- "No RF activity" state after 5 seconds of silence
- CSS styles in static/css/modes/aprs.css
Also added UK region to dropdown (same freq as Europe: 144.800)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Major improvements to APRS decoding reliability:
Process piping fixes (prevent deadlocks):
- rtl_fm stderr -> DEVNULL (was blocking on unbuffered stderr)
- decoder stderr -> STDOUT (merged, single stream to read)
- decoder uses text=True, bufsize=1 for line-buffered reading
- Proper EOF detection in stream thread
rtl_fm command improvements:
- Use -M nfm (narrowband FM) for APRS
- Add -E dc (DC blocking filter) for cleaner audio
- Add -A fast (fast AGC) for packet bursts
- Sample rate 22050 Hz matches direwolf -r 22050
Parsing robustness:
- Strip direwolf bracket prefixes like "[0.4] " before parsing
- Handle multimon-ng "AFSK1200:" prefix
- Better error handling for early process exit
New /aprs/spectrum endpoint:
- Runs rtl_power to scan around APRS frequency
- Returns peak detection, noise floor, signal analysis
- Provides advice for antenna/signal debugging
- Supports region selection and custom frequency
Also added UK to region list (same freq as Europe: 144.800 MHz)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Direwolf requires a config file to run. Create a minimal receive-only
config at startup that configures stdin input with AFSK1200 modem.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Changed -q d to -q h flag. The -q d option was suppressing APRS packet
descriptions (the decoded output we need), while -q h only suppresses
the audio level heard line.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Change direwolf flags from -D 1 to correct flags for stdin input
- Add -n 1 (mono), -b 16 (16-bit), -t 0 (no PTT), -q d (quiet)
- Add -M fm for explicit FM demodulation in rtl_fm
- Add explicit stdout output (-) to rtl_fm command
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add updateAirbandSquelch() to restart audio when squelch slider changes
- Remove verbose diagnostic logging from audio streaming
- Remove tee diagnostic for raw rtl_fm output
- Keep error logging for troubleshooting
- Simplify audio stream generator
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add /settings/rtlsdr/driver-status endpoint to check for loaded DVB modules
- Add /settings/rtlsdr/blacklist-drivers endpoint to unload modules and create blacklist
- Show warning banner on dashboard when DVB conflict detected
- Provide "Fix Now" button to automatically resolve the issue
- Warn users that their RTL-SDR devices may not work until drivers are blacklisted
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add '-' flag to explicitly specify stdout output
- Some rtl_fm versions/devices require this explicitly
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Use tee to capture rtl_fm raw output to /tmp/rtl_fm_raw.bin
- Log raw file size during stream timeouts
- Helps determine if rtl_fm is producing any data at all
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Capture both rtl_fm and ffmpeg stderr to separate log files
- Log ffmpeg errors at stream request and during timeouts
- Helps identify if ffmpeg is the source of zero-byte streaming
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Log when generator starts
- Track iterations and bytes sent
- Log select timeouts to diagnose data flow issues
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Log rtl_fm stderr to /tmp/rtl_fm_stderr.log instead of /dev/null
- Add detailed logging for audio start requests and parameters
- Log audio stream status and bytes transferred
- Help diagnose SDR1 airband audio issues
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Use -j instead of -o 4 for JSON output, which is the correct
flag for acarsdec v4.3.1+ (Thibaut Varene fork).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Import device identity functions (get_identity_engine, ingest_ble_dict, etc.)
- Initialize and clear identity engine at sweep start
- Feed BLE observations to identity engine during Bluetooth scan
- Feed WiFi observations to identity engine during WiFi scan
- Finalize sessions and emit identity_clusters event at sweep completion
- Include identity cluster statistics in sweep results
The device identity engine provides MAC-randomization resistant detection
by clustering observations using fingerprinting, timing patterns, and
RSSI trajectory analysis.
- Add startup check in app.py for root/sudo privileges
- Show warning in terminal if not running as root
- Add running_as_root flag to TSCM devices API response
- Display privilege warning in TSCM UI when not running as root
- Show command to run with sudo in the warning
- Add CSS styling for privilege warning banner
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove requirement for sdr_device to be set before RF scanning
- Add RTL-SDR device detection check with rtl_test before scanning
- Lower signal detection threshold from -50dBm to -70dBm
- Lower noise floor threshold from 15dB to 10dB above noise
- Add rf_status event for frontend feedback when RF unavailable
- Show status message in RF panel explaining why scanning isn't working
- Add CSS styling for status messages
- Reset RF status message when sweep starts
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- New device_identity.py: Clusters BLE/WiFi observations into probable
physical devices using passive fingerprinting (not MAC addresses)
- Fingerprinting based on manufacturer data, service UUIDs, capabilities,
timing patterns, and RSSI trajectories
- Session tracking with automatic gap detection
- Risk indicators: stable RSSI, MAC rotation, ESP32 chipsets, audio-capable
- Full audit trail for all clustering decisions
- New ble_scanner.py: Cross-platform BLE scanning with bleak library
- Detects AirTags, Tile, SmartTags, ESP32 by manufacturer ID
- Fallback to system tools (btmgmt, hcitool, system_profiler)
- Added API endpoints for device identity clustering (/tscm/identity/*)
- Updated setup.sh with bleak dependency
- Updated documentation with TSCM features and hardware requirements
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
WiFi Scanning:
- Add 'iw' scan method as primary (sometimes works without root)
- Auto-detect wireless interface from /sys/class/net
- Better error logging for permission issues
- Fall back to iwlist if iw fails
UI Updates:
- Replace Critical/High/Medium/Low cards with new scoring model
- Now shows: High Interest (6+), Needs Review (3-5), Informational (0-2)
- Add Correlations count card
- Update counts based on device classification scores
Tracker Detection:
- Add detection for Apple AirTag (by OUI and name)
- Add detection for Tile trackers
- Add detection for Samsung SmartTag
- Add detection for ESP32/ESP8266 devices (Espressif chipset)
- Add generic chipset vendor detection
- New indicator types with appropriate scoring weights
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The bt_device event was including 'type': device.get('type') which
overwrote the SSE event type 'bt_device' with 'ble', causing the
frontend to not recognize the events.
- Rename device type field from 'type' to 'device_type' in bt_device events
- Update frontend to use device_type for display
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Restore airodump-ng check for WiFi tools
- Add /sys/class/net/*/wireless fallback for WiFi detection
- Add /sys/class/bluetooth/hci* fallback for Bluetooth detection
- Add hciconfig to Bluetooth tool checks
- Add SubprocessError to exception handling
- Multiple fallback layers ensure detection works even with partial tools
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add macOS-specific WiFi detection using airport utility
- Add macOS-specific Bluetooth detection using system_profiler
- Add fallback to 'iw' command on Linux when iwconfig unavailable
- Properly handle platform differences for device availability checks
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>