Tests now call cleanup_flap_state() at the start of each test to properly
reset the interface flap detection state (_flap_window, _interface_recovering,
_recovery_timer) between tests, preventing state carryover failures.
- Fix 1: Interface flap detection suppresses departure storms when wlan0 briefly loses association and triggers simultaneous RTM_DELNEIGH events for all cached neighbors
- Tracks 3-second window of departure MACs
- Locks recovery state for 60 seconds if 3+ departures occur in rapid succession
- Prevents false positive alerts during interface recovery
- Fix 2: Additional infrastructure IPs via NET_ALERTER_INFRA_IPS env var
- Allows operator to suppress alerts for devices like access points that cycle regularly
- Comma-separated list of IP addresses to be seeded as infrastructure at startup
- Example: NET_ALERTER_INFRA_IPS=10.0.0.0,10.0.0.0
Addresses DevTrack #467 (net_alerter false positive alerts)
sensor.service running at 10.0.0.0. ARP + DHCP sensors active.
Probe sniffer degraded (no wlan1). BLE degraded (BlueZ too old for or_patterns).
Untracked audit files committed for reference.
- P1 #551: Add STATUS_PORT env var (default 9191) for configurable HTTP status server port
- P1 #552: Fix SQLite connection leak — wrap all sqlite3.connect() with try/finally in lookup_person(), seed_persons_from_db(), log_signal(), log_presence_change()
- P1 #553: Fix race condition in update_person_state() — compute certainty snapshot under devices_lock, determine state transition under persons_lock, send alert outside locks to prevent stale snapshot
- P2 #559 (included): Add WAL mode + pragmas to init_db() for concurrent write safety
- Database path: Align to /opt/sensor/presence.db in both daemon and install.sh
- Increase connection timeout from 1s to 10s across all functions
- install.sh: Document PRESENCE_STATUS_PORT and PRESENCE_MATRIX_WEBHOOK env vars
Implements DevTrack #530: Person Presence Intelligence daemon for BigBrother drop implant.
Core components:
- presence_daemon.py: 4 sensor threads (probe, ARP, DHCP, BLE) with exponential decay
- presence_schema.sql: SQLite schema for persons, devices, signals, occupancy log
- install.sh: Deployment script (copies to /opt/sensor/, creates systemd service)
- AUDIT_impl.md: Implementation notes, limitations, audit checklist
Features:
- Exponential decay model (lambda=0.08/min) for signal certainty
- Presence anchor (45-min hold time prevents false VACANT)
- Person state machine: UNKNOWN → PRESENT ↔ ABSENT
- Multi-signal fusion: max + 0.08×min for device aggregate
- Thread-safe in-memory state with SQLite persistence
- Matrix webhook alerting (if configured)
- HTTP status endpoint on 127.0.0.1:9191
- Graceful BLE degrade if bleak unavailable
- No tool fingerprints in output or service names
- Reject dash/colon MAC-format device names (e.g. 45-48-39-7F-73-C7)
from open mode — were slipping past is_generic_name()
- Add exponential backoff (1s, 2s) on Matrix 429/5xx, max 3 attempts —
startup burst was rate-limiting and dropping alerts
- Commit active scanner flag (scanning_mode=active) that was in working
tree but not in prior commit c0b91e9 despite the commit message
- Add 3 tests: MAC format filter, 429 retry success, 429 give-up
Fixes DevTrack #518
Passive mode fails on Debian bookworm bleak 0.20.2 without or_patterns filter.
Active mode is functionally equivalent for building-level presence detection
and actually yields more complete advertisement data including device names.
When on_departure popped the device from known_devices, rapid RTM_NEWNEIGH
events during ARP cache rebuild each triggered on_arrival as if the device
was brand new, flooding the Matrix room with 5-10+ duplicate ARRIVED alerts.
Fix: keep device in known_devices with departing=True flag. on_arrival
silently cancels pending departure timers and returns without alerting when
it sees this flag. Timer callback checks flag before sending the alert and
only pops the device when departure is confirmed after 15 minutes.
- Bug: device departs → timer starts, but last_departed_time not set yet
If device re-arrives before timer fires, timer never cancelled
Result: false DEPARTED alert sent after re-arrival debounce expires
- Fix: Cancel any pending departure timer immediately on re-arrival,
before checking last_departed_time
- Test: Added regression test that verifies timer is cancelled when
device re-arrives before 15-min debounce expires, preventing false alerts
- All 10 tests passing
- lookup_oui() now tries SQLite first (/opt/net_alerter/oui.db)
- Falls back to inline OUI_DICT if DB missing or query fails
- No network calls, local SQLite reads only
- Debug-level logging for failures (no output noise)
- Infrastructure IPs (gateway, self, broadcast) silently ignored in
on_arrival() and on_departure(); seeded at startup from 'ip route'
- 15-minute departure debounce: alert only fires if device is still
absent after 900s, eliminating ARP cache flush false positives
- 5-minute re-arrival suppression: ARRIVED alert skipped when device
returns within 5 min of a recorded departure (covers interface flap)
- DHCP renewal dedup: known device re-announcing within 30 min skips
ARRIVED alert
- Tests added for all four suppression paths (9 total, all passing)
- Replace broken OUI file lookup with inline dict of 200+ common vendors (works on Armbian)
- Fix hostname/IP duplication when reverse DNS fails (show IP once, not twice)
- Add format_arrival() and format_departure() helpers for consistent alert formatting
- Update on_arrival/on_departure to use new formatting functions
- Add comprehensive tests for hostname dedup and OUI lookup
Replace all sensor.* logger namespaces with __name__ (generic module
identifiers instead of discoverable 'sensor.*' prefixes).
Change hardcoded 'bb' API user to 'admin' in config and code defaults.
Change hardcoded relay_user 'bb' to 'operator' — prevents network
profiling from exposing tool identity via SSH config.
Fixes#457, #458, #459
- Removed get_local_subnets() and scan_subnet() — no longer needed
- Added read_arp_cache() to read kernel ARP table from /proc/net/arp
- Added fallback to 'ip neigh show' if /proc/net/arp unavailable
- Added lookup_oui() to query system /usr/share/hwdata/oui.txt for vendor names
- Removed subprocess nmap call and re import (no longer needed)
- run() now directly reads ARP cache instead of scanning subnets
- Zero active network traffic — purely passive monitoring
- Removed sudo requirement