# Bluehound Continuous BLE and Classic Bluetooth scanner with time-searchable logging, a REST API, and a web dashboard. Built for macOS and embedded/sensor-node deployments with WiFi streaming support. Bluehound scans nearby Bluetooth devices in a loop, enriches each observation (manufacturer, service UUIDs, device class, estimated distance, Apple Continuity data), and persists everything to SQLite. A built-in HTTP server exposes the data for the bundled dashboard or any client. ## Features - **Dual-stack scanning** — BLE via [SimpleBLE](https://github.com/OpenBluetoothToolbox/SimpleBLE), plus Classic Bluetooth inquiry and SDP service discovery on macOS via IOBluetooth. - **Time-searchable history** — every observation is stamped with date, time, hour, and minute and indexed for fast range queries. - **Device enrichment** — company/manufacturer lookup, standard service and characteristic UUID decoding, device-class decoding, and GATT characteristic enumeration on connect. - **Distance estimation** — RSSI + Tx power path-loss model, with per-device distance history. - **Apple Continuity decoding** — parses proximity/pairing advertisements. - **Passive security assessment** — flags risky services (OBEX, SPP, HID, audio) and known-vulnerable device signatures, assigning a HIGH/MEDIUM/LOW/NONE risk level. - **Device nicknames** — assign and manage human-readable labels per address. - **ESP32 ingestion** — remote sensor nodes can POST scan batches over WiFi to `/esp32/scan`. - **REST API + web dashboard** — a single-file HTML dashboard served over HTTP. ## Requirements - macOS (Classic Bluetooth support uses IOBluetooth/CoreBluetooth frameworks) - CMake >= 3.15 - SQLite3 - Vendored dependencies (included in the repo): - SimpleBLE static library at `simpleble/simpleble/build/lib/libsimpleble.a` - cpp-httplib header at `third_party/httplib.h` ## Build ```sh ./build.sh ``` This verifies the vendored dependencies, configures with CMake into `build/`, and compiles. The resulting binary is `build/bluehound`. To build manually: ```sh mkdir -p build && cd build cmake .. make -j ``` Install system-wide with `sudo make install` from the `build/` directory. ## Run ```sh ./build/bluehound ``` On start, Bluehound initializes `ble_scans.db`, launches the HTTP server on port `8080` (bound to `0.0.0.0`), and begins scanning. Shut down cleanly with `Ctrl-C` (SIGINT/SIGTERM are handled gracefully). Default configuration (compiled into `Config` in [bluehound.cpp](bluehound.cpp)): | Setting | Default | | --- | --- | | Scan duration | 5000 ms per cycle | | Scan interval | 1000 ms between cycles | | Database path | `ble_scans.db` | | HTTP port | 8080 | | Max DB entries | 1,000,000 (rotates) | ## Web dashboard Serve the dashboard with the helper script (static server on port 8090): ```sh python3 serve_dashboard.py ``` Then open http://127.0.0.1:8090. The dashboard queries the scanner's API on port 8080, so run the `bluehound` binary alongside it. ## REST API The scanner serves the following endpoints on port 8080: | Method | Path | Description | | --- | --- | --- | | GET | `/health` | Health check | | GET | `/scans/recent?limit=N` | Most recent observations | | GET | `/scans/date/:date` | Observations for `YYYY-MM-DD` | | GET | `/scans/hour/:hour` | Observations for hour `HH` | | GET | `/stats` | Aggregate statistics | | GET | `/device/:address/count` | Observation count for a device | | GET | `/distance/:address` | Distance history for a device | | GET | `/characteristics/:address` | Discovered GATT characteristics | | GET | `/read/:address/:service/:characteristic` | Read a characteristic value | | GET | `/nickname/:address` | Get a device nickname | | POST | `/nickname/:address` | Set a device nickname | | DELETE | `/nickname/:address` | Remove a device nickname | | GET | `/nicknames` | List all nicknames | | GET | `/security/:address` | Security assessment for a device | | GET | `/security` | All security assessments | | GET | `/tracking/:address` | Tracking data (device info, Apple Continuity) | | POST | `/esp32/scan` | Ingest a scan batch from a remote ESP32 node | CORS is enabled for all origins. ## Data model Observations are stored in `ble_scans.db` (SQLite). Key tables: - `ble_scans` — every observation, with timestamps, RSSI, manufacturer/company, service UUIDs, estimated distance, protocol type, and Classic device-class/SDP fields. - `device_distance_history` — per-device RSSI/distance samples over time. - `device_characteristics` — discovered GATT services and characteristics. - `device_nicknames` — user-assigned labels. - `security_assessments` — risk level, vulnerabilities, and recommendations per device. - `device_tracking_data` — device model/firmware/serial, Apple Continuity, pairing status, and tracking identifiers. ## Project layout | Path | Purpose | | --- | --- | | [bluehound.cpp](bluehound.cpp) | Scanner core: scan loop, SQLite logging, HTTP API, BLE decoding, security assessment | | [classic_bluetooth.h](classic_bluetooth.h) / [classic_bluetooth.mm](classic_bluetooth.mm) | Classic Bluetooth inquiry and SDP via IOBluetooth (Objective-C++) | | [web_dashboard.html](web_dashboard.html) | Single-file web dashboard | | [serve_dashboard.py](serve_dashboard.py) | Static server for the dashboard | | [CMakeLists.txt](CMakeLists.txt) / [build.sh](build.sh) | Build configuration | | `simpleble/` | Vendored SimpleBLE | | `third_party/httplib.h` | Vendored cpp-httplib | ## Notes - Classic Bluetooth scanning and the IOBluetooth-backed features are macOS-only. BLE scanning via SimpleBLE is portable, but the build as configured links macOS frameworks. - The ESP32 endpoint uses lightweight string-based JSON parsing; use a proper JSON library for production ingestion. - Security assessments are heuristic and intended for authorized inventory and awareness, not as a substitute for a full audit.