# Presence Daemon — Person Presence Intelligence

Passive multi-signal WiFi/ARP/DHCP/BLE person presence tracking daemon for BigBrother drop implant.

## Quick Start

```bash
cd /path/to/bigbrother/presence
sudo bash install.sh
```

This creates `/opt/sensor/sensor.py`, initializes the database, and starts the systemd service.

## Configuration

Environment variables:
- `PRESENCE_MONITOR_IFACE` — WiFi monitor mode interface (default: wlan1)
- `PRESENCE_DATA_IFACE` — Connected network interface for ARP/DHCP (default: wlan0)
- `PRESENCE_DB_PATH` — SQLite database location (default: /opt/presence/presence.db)
- `PRESENCE_MATRIX_WEBHOOK` — Matrix webhook URL for alerts (default: empty, no alerts)

Example systemd override:
```bash
sudo systemctl edit sensor
# Add to [Service] section:
# Environment=PRESENCE_MONITOR_IFACE=wlan2
# Environment=PRESENCE_MATRIX_WEBHOOK=https://matrix.example.com/hook/...
```

## Architecture

### Sensor Threads (4 concurrent)

1. **Probe Sniffer** — WiFi probe request capture on monitor interface
   - Passive 802.11 frame parsing (type 0, subtype 4)
   - Extracts source MAC → +0.60 certainty bump
   - Runs on PRESENCE_MONITOR_IFACE

2. **ARP Listener** — netlink RTM_NEWNEIGH/RTM_DELNEIGH events
   - Kernel neighbor discovery events
   - Filters infrastructure IPs
   - Extracts MAC → +0.50 certainty bump

3. **DHCP Sniffer** — AF_PACKET raw socket on data interface
   - Captures DHCP DISCOVER/REQUEST packets
   - Extracts client MAC (chaddr field)
   - Extracts hostname (option 12)
   - MAC → +0.80 certainty bump

4. **BLE Scanner** — Passive BLE device discovery
   - Uses `bleak` library if available
   - Gracefully disables if ImportError
   - Device address → +0.40 certainty bump

### Core Logic

**Signal Fusion** (per person):
- Collect all device certainties
- Apply exponential decay: `certainty × e^(-0.08 × age_minutes)`
- Aggregate: `max + 0.08 × second_max`
- Check state machine

**Presence Anchor** (critical):
- Once PRESENT (certainty ≥ 0.40), hold PRESENT for 45 minutes from last signal
- Prevents false ABSENT from temporary signal loss (iOS suppression, WiFi roam, power save)
- After 45 minutes without signal ≥ 0.40 → ABSENT

**State Machine**:
```
UNKNOWN  →  PRESENT  →  ABSENT  →  PRESENT
  ↓        agg_cert           ↓
  └─────────≥ 0.40    45 min no signal
```

**Alerting**:
- UNKNOWN → PRESENT: "ARRIVED" alert
- PRESENT → ABSENT: "DEPARTED" alert
- Via Matrix webhook if configured

### Database Schema

**persons** — Named people
- id (PK)
- name — Person name
- notes — Optional metadata

**devices** — MAC-to-person mapping
- mac (PK)
- person_id (FK)
- label — Device name (iPhone, Laptop, etc.)
- added_at — Timestamp

**signals** — Raw signal observations
- id (PK)
- mac — Device MAC
- signal_type — "probe", "arp", "dhcp", or "ble"
- certainty — Aggregated certainty after this signal
- ts — Observation timestamp

**occupancy_log** — State transitions
- id (PK)
- person_id (FK)
- old_status — Previous state
- new_status — New state
- ts — Transition timestamp

## HTTP Status Endpoint

GET http://127.0.0.1:9191/

Returns JSON:
```json
{
  "persons": [
    {
      "id": 1,
      "name": "Alice",
      "status": "PRESENT",
      "last_change": 1712761234.5
    }
  ]
}
```

## Limitations (Phase 1)

- **No auto-enrollment**: Device-to-person mapping must be set up manually in database
- **No mDNS parsing**: Hostnames are not extracted or used for identity
- **BLE optional**: Requires `bleak` library; gracefully disabled if missing
- **Monitor mode required**: Probe sniffer needs separate WiFi adapter in monitor mode
- **Root access needed**: Raw sockets require CAP_NET_ADMIN or root
- **Single location**: No multi-room support

## Files

- `presence_daemon.py` — Main daemon (1000+ lines)
- `presence_schema.sql` — SQLite schema
- `install.sh` — Deployment script
- `AUDIT_impl.md` — Implementation notes and audit checklist
- `README.md` — This file

## Logging

Logs to `/var/log/sensor.log` and stdout. Log level: INFO (debug messages only on errors).

Example log output:
```
2026-04-10 14:54:32,123 [INFO] Database initialized at /opt/sensor/sensor.db
2026-04-10 14:54:32,124 [INFO] Loaded 2 persons from database
2026-04-10 14:54:32,125 [INFO] Probe sniffer started on wlan1
2026-04-10 14:54:32,126 [INFO] ARP listener started
2026-04-10 14:54:32,127 [INFO] DHCP sniffer started on wlan0
2026-04-10 14:54:32,128 [INFO] BLE scanner started
2026-04-10 14:54:32,129 [INFO] Status server started on http://127.0.0.1:9191
2026-04-10 14:54:32,130 [INFO] Sensor daemon started
```

## Testing

1. Verify systemd service is running:
   ```bash
   sudo systemctl status sensor
   ```

2. Check database initialization:
   ```bash
   sqlite3 /opt/sensor/sensor.db ".tables"
   ```

3. Query persons:
   ```bash
   sqlite3 /opt/sensor/sensor.db "SELECT * FROM persons;"
   ```

4. Query recent signals:
   ```bash
   sqlite3 /opt/sensor/sensor.db "SELECT * FROM signals ORDER BY ts DESC LIMIT 10;"
   ```

5. Check status endpoint:
   ```bash
   curl http://127.0.0.1:9191/
   ```

6. Check logs:
   ```bash
   tail -f /var/log/sensor.log
   ```

## Phase 2 Roadmap

- Auto-enrollment from mDNS hostnames
- BLE MAC signature recognition (Apple Watch, AirPods)
- Device quorum (multi-device presence confirmation)
- Tentative device linking via co-occurrence windows

## Phase 3 Roadmap

- Pattern of life analytics
- Arrival/departure time learning
- Occupancy forecasting
- Multi-location aggregation
