RF_SENTINEL

rf_sentinel / how-it-works

How it works

From radio packet to alert. Every rule and threshold here is in the public source; file names point at the Kotlin classes.

1. Scanning (receive only)

Nothing is ever transmitted to, connected to or paired with a scanned device.

2. Signatures

detect/SignatureEngine.kt matches payload tags (BWCDEVICE), Bluetooth SIG company IDs, 16 and 128-bit service UUIDs, service data, names and SSIDs. The watchlist matches exact addresses, IEEE MA-L / MA-M / MA-S prefixes and name / vendor rules, seeded by regional presets (assets/oui_presets/: Global, Canada, US). Every prefix used in code is checked against its IEEE registrant by tools/gen_assets.py, which fails the build data if one changes.

TierConfidenceMeaning
Weak< 50Shared identifier; amber, silent by default
Probable50-79Vendor-specific identifier; alerts at the default threshold
Strong80+Payload tag or vendor-assigned network name

Rejected on purpose: bare 10-digit BLE names, the -FALCON SSID suffix, Liteon module blocks, Motorola Mobility blocks (consumer phones), Sierra Wireless / Cradlepoint blocks as standalone matches, and company ID 0x058E alone.

3. Evidence fusion

detect/EvidenceFusion.kt, every 2 s per device. Different rules of the same category combine by noisy-OR with each supporting hit at half weight:

fused = 1 - (1 - top) × Π (1 - 0.5 × support_i)      support_i ≥ 30, result ≤ 90

Example: watchlisted address 70 + Zebra serial name 50 → 78. The patrol-vehicle hit is never fused, because it is derived from the device's own matches.

4. Patrol-vehicle grouping

detect/PatrolCluster.kt. Each device gets a role (body camera, plate reader, two-way radio, vehicle cellular router, mobile printer, in-car computer, rugged laptop, police camera) from its matches or IEEE registrant. Two or more different roles form a vehicle when their RSSI correlates (Pearson r ≥ 0.6 over ≥ 8 aligned seconds) or both are parked (flat) and appeared within 30 s of each other. Score: 24 + 12 × roles, +10 if moving together, cap 88. A device's own match is held 2 minutes after its evidence was last seen.

5. Address-rotation linking

detect/AdvertFingerprint.kt. Fingerprint = names, service UUIDs, service-data and company-ID payload sizes, TX power, connectable flag. A new private address with the same fingerprint as exactly one device that went silent 1.5-30 s earlier inherits its matches (−5), follow history (time with you, GPS path) and ignore state. Apple-Continuity-only adverts are never linked.

6. Follow detection

A tracker or flagged device heard continuously (gaps under 3 minutes) for at least 10 minutes while you moved at least 800 m raises may be following you, once per device. Both thresholds are adjustable.

7. Identifying ordinary devices

  1. Decoded vendor protocols (Apple Continuity incl. exact AirPods / Beats model, iBeacon, Eddystone, Fast Pair, Remote ID)
  2. GAP appearance
  3. Standard SIG services (heart rate, HID, LE Audio, medical...)
  4. Name patterns
  5. Member-service owner
  6. IEEE registrant
  7. Company ID

8. WiFi fingerprinting

detect/WifiFingerprint.kt: WPS element (manufacturer 0x1021, model name 0x1023, model number 0x1024, device name 0x1011, primary device type 0x1054), Cisco CCX AP name (IE 133, bytes 10-25), BSSID registrant (unless chipset-only), vendor IE owners split into equipment and chipset makers. BSSIDs are the same AP when octets 2-5 match, the last octet is within 16 and the first is equal or locally administered.

9. Known cameras

alpr/: OpenStreetMap elements tagged surveillance:type=ALPR, speed and red-light cameras, fetched via Overpass (overpass-api.de, with overpass.kumi.systems and overpass.private.coffee as fallbacks) and DeFlock's region snapshots, cached privately, refreshed weekly. Warnings when a camera is ~20 s ahead (150-600 m) and getting closer.

Sources

Full tables: docs/SIGNATURES.md.

Join the DiscordHelp, ideas, new signatures, field captures and ESP32 builds. CIS-C0/RFSentinel on GitHubSource code, releases, issues. A star helps others find it.