# 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.
