First-time run of ruff-format via pre-commit hook normalises quote style, trailing commas, and whitespace across 188 Python files. No logic changes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
9.8 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
INTERCEPT is a web-based Signal Intelligence (SIGINT) platform providing a unified Flask interface for software-defined radio (SDR) tools. It supports pager decoding, 433MHz sensors, ADS-B aircraft tracking, ACARS messaging, WiFi/Bluetooth scanning, satellite tracking, ISS SSTV decoding, AIS vessel tracking, weather satellite imagery (NOAA APT & Meteor LRPT), and Meshtastic mesh networking.
Common Commands
Docker (Primary)
# Build and run (basic profile)
docker compose --profile basic up -d
# Build and run with ADS-B history (Postgres)
docker compose --profile history up -d
# Rebuild after code changes
docker compose --profile basic up -d --build
# Multi-arch build (amd64 + arm64 for RPi)
./build-multiarch.sh
Local Setup (Alternative)
# First-time setup (interactive wizard with install profiles)
./setup.sh
# Or headless full install
./setup.sh --non-interactive
# Or install specific profiles
./setup.sh --profile=core,weather
# Run with production server (gunicorn + gevent, handles concurrent SSE/WebSocket)
sudo ./start.sh
# Or for quick local dev (Flask dev server)
sudo -E venv/bin/python intercept.py
# Other setup utilities
./setup.sh --health-check # Verify installation
./setup.sh --postgres-setup # Set up ADS-B history database
./setup.sh --menu # Force interactive menu
Testing
# Run all tests
pytest
# Run specific test file
pytest tests/test_bluetooth.py
# Run with coverage
pytest --cov=routes --cov=utils
# Run a specific test
pytest tests/test_bluetooth.py::test_function_name -v
Linting and Formatting
# Lint with ruff
ruff check .
# Auto-fix linting issues
ruff check --fix .
# Format with black
black .
# Type checking
mypy .
Architecture
Entry Points
setup.sh- Menu-driven installer with profile system (wizard, health check, PostgreSQL setup, env configurator, update, uninstall). Sources.envon startup viastart.sh.start.sh- Production startup script (gunicorn + gevent auto-detection, CLI flags, HTTPS,.envsourcing, fallback to Flask dev server)intercept.py- Direct Flask dev server entry point (quick local development)app.py- Flask application initialization, global state management, process lifecycle, SSE streaming infrastructure, conditional gevent monkey-patch
Route Blueprints (routes/)
Each signal type has its own Flask blueprint:
pager.py- POCSAG/FLEX decoding via rtl_fm + multimon-ngsensor.py- 433MHz IoT sensors via rtl_433adsb.py- Aircraft tracking via dump1090 (SBS protocol on port 30003)acars.py- Aircraft datalink messages via acarsdecwifi.py,wifi_v2.py- WiFi scanning (legacy and unified APIs)bluetooth.py,bluetooth_v2.py- Bluetooth scanning (legacy and unified APIs)satellite.py- Pass prediction using TLE datasstv.py- ISS SSTV image decoding via slowrxweather_sat.py- NOAA APT & Meteor LRPT via SatDumpais.py- AIS vessel tracking and VHF DSC distress monitoringaprs.py- Amateur packet radio via direwolfrtlamr.py- Utility meter readingmeshtastic_routes.py- Meshtastic LoRa mesh networking
Core Utilities (utils/)
SDR Abstraction Layer (utils/sdr/):
SDRFactorywith factory pattern for multiple SDR types (RTL-SDR, LimeSDR, HackRF, Airspy, SDRPlay)- Each type has a
CommandBuilderfor generating CLI commands
Bluetooth Module (utils/bluetooth/):
- Multi-backend: DBus/BlueZ primary, fallback for systems without BlueZ
aggregator.py- Merges observations across timetracker_signatures.py- 47K+ known tracker fingerprints (AirTag, Tile, SmartTag)heuristics.py- Behavioral analysis for device classification
TSCM (Counter-Surveillance) (utils/tscm/):
baseline.py- Snapshot "normal" RF environmentdetector.py- Compare current scan to baseline, flag anomaliesdevice_identity.py- Track devices despite MAC randomizationcorrelation.py- Cross-reference Bluetooth and WiFi observations
WiFi Utilities (utils/wifi/):
- Platform-agnostic scanner with parsers for airodump-ng, nmcli, iw, iwlist, airport (macOS)
channel_analyzer.py- Frequency band analysis
Weather Satellite (utils/weather_sat.py):
- Singleton
WeatherSatDecoderusing SatDump CLI for NOAA APT and Meteor LRPT - Subprocess management with stdout parsing, image watcher via rglob
- Pass prediction using skyfield TLE data
SSTV Decoder (utils/sstv.py):
- ISS SSTV reception via slowrx with Doppler tracking
- Singleton pattern, image gallery with timestamped filenames
Key Patterns
Server-Sent Events (SSE): All real-time features stream via SSE endpoints (/stream_pager, /stream_sensor, etc.). Pattern uses queue.Queue with timeout and keepalive messages. Under gunicorn + gevent, each SSE connection is a lightweight greenlet instead of an OS thread.
Process Management: External decoders run as subprocesses with output threads feeding queues. Use safe_terminate() for cleanup. Global locks prevent race conditions.
Data Stores: DataStore class with TTL-based automatic cleanup (WiFi: 10min, Bluetooth: 5min, Aircraft: 5min).
Input Validation: Centralized in utils/validation.py - always validate frequencies, gains, device indices before spawning processes.
External Tool Integrations
| Tool | Purpose | Integration |
|---|---|---|
| rtl_fm | FM demodulation | Subprocess, pipes to multimon-ng |
| multimon-ng | Pager decoding | Reads from rtl_fm stdout |
| rtl_433 | 433MHz sensors | JSON output parsing |
| dump1090 | ADS-B decoding | SBS protocol socket (port 30003) |
| acarsdec | ACARS messages | Output parsing |
| airmon-ng/airodump-ng | WiFi scanning | Monitor mode, CSV parsing |
| bluetoothctl/hcitool | Bluetooth | Fallback when DBus unavailable |
| slowrx | SSTV decoding | Subprocess with audio pipe |
| SatDump | Weather satellites | CLI live mode, NOAA APT + Meteor LRPT |
| AIS-catcher | AIS vessel tracking | JSON output parsing |
| direwolf | APRS | TNC modem for packet radio |
Frontend Structure
- UI direction (decided 2026-06-12): map-heavy modes get dedicated dashboard
pages (
/adsb/dashboard,/ais/dashboard,/satellite/dashboard); the SPA inindex.htmlkeeps text/scan modes. APRS and Meshtastic are map-centric and should migrate to dashboards under their own plans — do not grow their SPA footprint. - Templates:
templates/index.html(main SPA),templates/partials/modes/*.html(sidebar panels),templates/partials/nav.html(global nav) - JS Modules:
static/js/modes/*.js- IIFE pattern per mode (e.g.,WeatherSat,SSTV,Meshtastic) - CSS:
static/css/modes/*.css- scoped styles per mode, CSS variables for theming (--bg-card,--accent-cyan,--font-mono) - Mode Integration: Each mode is declared once in
static/js/mode-registry.js(label, group, elementId, module, init/destroy hooks, visuals flag). The catalog, sidebar toggles, destroy map, visuals list, and init dispatch intemplates/index.htmlare all derived from it. A new mode additionally needs: its partial intemplates/partials/modes/, entries in the CSS/JS lazy-load asset maps inindex.html, and its include in the partials block.tests/test_mode_registry.pyenforces registry/asset consistency.
Docker
Dockerfile- Single-stage build with all SDR tools compiled from source (dump1090, AIS-catcher, slowrx, SatDump, etc.). CMD runsstart.sh(gunicorn + gevent)docker-compose.yml- Two profiles:basic(standalone) andhistory(with Postgres for ADS-B)build-multiarch.sh- Multi-arch build script for amd64 + arm64 (RPi5)- Data persisted via
./data:/app/datavolume mount
Configuration
config.py- Environment variable support withINTERCEPT_prefix (e.g.,INTERCEPT_PORT,INTERCEPT_WEATHER_SAT_GAIN)- Database: SQLite in
instance/directory for settings, baselines, history
Testing Notes
Tests use pytest with extensive mocking of external tools. Key fixtures in tests/conftest.py. Mock subprocess calls when testing decoder integration.
Think Before Coding
Don't assume. Don't hide confusion. Surface tradeoffs.
Before implementing:
State your assumptions explicitly. If uncertain, ask. If multiple interpretations exist, present them - don't pick silently. If a simpler approach exists, say so. Push back when warranted. If something is unclear, stop. Name what's confusing. Ask. 2. Simplicity First
Minimum code that solves the problem. Nothing speculative.
No features beyond what was asked. No abstractions for single-use code. No "flexibility" or "configurability" that wasn't requested. No error handling for impossible scenarios. If you write 200 lines and it could be 50, rewrite it. Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify.
Surgical Changes
Touch only what you must. Clean up only your own mess.
When editing existing code:
Don't "improve" adjacent code, comments, or formatting. Don't refactor things that aren't broken. Match existing style, even if you'd do it differently. If you notice unrelated dead code, mention it - don't delete it. When your changes create orphans:
Remove imports/variables/functions that YOUR changes made unused. Don't remove pre-existing dead code unless asked. The test: Every changed line should trace directly to the user's request.
Goal-Driven Execution
Define success criteria. Loop until verified.
Transform tasks into verifiable goals:
"Add validation" → "Write tests for invalid inputs, then make them pass" "Fix the bug" → "Write a test that reproduces it, then make it pass" "Refactor X" → "Ensure tests pass before and after" For multi-step tasks, state a brief plan:
- [Step] → verify: [check]
- [Step] → verify: [check]
- [Step] → verify: [check]