
Single Go binary for Bluetooth Low Energy security testing on Linux/BlueZ: scan, enumerate GATT, read/write/notify, fuzz characteristics, and run structured assessments with JSON output.
One Go binary for the entire Bluetooth Low Energy assessment workflow on Linux/BlueZ: scan, enumerate, read/write/notify, fuzz, and structured security assessment, with JSON output for scripts and agents.
Caeruleus consolidates Bluetooth Low Energy (BLE) security testing into a single Go binary built on the Linux BlueZ stack. It talks to peripherals over BlueZ D-Bus and raw L2CAP/HCI sockets and covers the whole lifecycle: discover devices, browse and read/write the GATT tree, capture notifications, fuzz writable characteristics, and run repeatable security-assessment workflows. Every command emits structured -o json / -o jsonl output for scripting, reporting, and LLM agents.
Built by offensive security engineers to replace the usual pile of bettercap, gatttool, hcitool, bluetoothctl, and one-off Bleak scripts, Caeruleus keeps the abstractions at the level a human or agent actually works at.
caeruleus: Latin for the "blue" in Bluetooth Low Energy
The standard workflow for BLE security testing is a scavenger hunt across tools that were never designed to work together:
hcitool / hciconfig for adapter config and discovery, both deprecated by BlueZ and missing from many modern distros.bettercap for ble.recon and ble.enum. Powerful, but you install a full network-attack framework just to browse a GATT tree.gatttool for reading and writing handles, deprecated for years but still in every tutorial.Each speaks its own dialect, and none emit machine-readable output you can pipe into the next step, so you become the integration layer, copying MAC addresses and handles between terminals by hand. BlueZ's own supported frontends (bluetoothctl, btmgmt) are general-purpose management tools pitched at the wrong abstraction level for assessment work.
Caeruleus replaces that entire pile with one consistent command surface:
| Use case | Previous method | With Caeruleus |
|---|---|---|
| Discover nearby devices | hcitool lescan / bettercap ble.recon / bluetoothctl scan on | caeruleus scan |
| List and read services/characteristics | bettercap ble.enum <mac> | caeruleus enumerate -b <mac> --values |
| Interactive session | gatttool -I / bluetoothctl | caeruleus shell -b <mac> |
| Read a handle | gatttool --char-read-hnd 0x0013 | caeruleus read -b <mac> -a 0x0013 |
| Write a handle | gatttool -b <mac> --char-write-req -a 0x002c -n $(...) | caeruleus write -b <mac> -a 0x002c --req -s "value" |
| Capture notifications | custom Bleak logger | caeruleus listen -b <mac> -a <handle> |
| Audit unauthenticated exposure | custom Bleak audit scripts | caeruleus recon / caeruleus assess ... |
| Fuzz a characteristic | custom fuzzers / boofuzz | caeruleus fuzz write -b <mac> -a <handle> |
| Connection params, MTU | hcitool con / btmgmt con-info | caeruleus conn-params -b <mac> |
| Adapter power and recovery | btmgmt power / hciconfig reset / rfkill | caeruleus doctor / caeruleus adapter power cycle |
Structured output everywhere. Every command that produces output supports -o text|json|jsonl. JSON is the canonical representation, not a secondary export; jsonl streams one record per event for scan --live and listen. enumerate --compact emits one key=value line per characteristic for grep or an LLM context window.
BLE stack health. caeruleus doctor walks the BlueZ and kernel state and prints an OK/WARN/FAIL checklist: bluetoothd running, adapter powered, no leaked discovery session, BlueZ-cached address vs the MGMT chip-live address, a live 2-second scan probe, and ExchangeMTU in a safe band. Each non-OK finding carries a concrete fix command, and the exit code follows the grep/diff convention (0 clear, 2 on failure).
Clean teardown. On SIGTERM/SIGHUP the tool runs the disconnect path and polls until Connected=false propagates through BlueZ, so the peripheral re-advertises immediately instead of stalling for its supervision timeout. No more "ghost connections" after a crash.
Scripting and automation. serve/send hold one GATT link open over a Unix socket so a script or agent avoids the ~1.5s reconnect cost per command; batch runs commands from stdin over a single persistent connection; listen --trigger-value sets up a notification, fires a write, and captures the response atomically.
Convenience details. Device-type inference from GAP Appearance plus advertised services (a watch shows as "Smartwatch," not a raw appearance code); forgiving input parsing (handles as 0x002a, 0X2A, or 42; hex as deadbeef, de:ad:be:ef, or 0xdeadbeef); caeruleus recipes lists and keyword-searches 19 common workflows; conn-params surfaces the negotiated interval, latency, and supervision timeout that neither gatttool nor bettercap expose.
Caeruleus treats LLM agents as first-class users. -o json / -o jsonl give token-efficient, structured output an agent parses without scraping human-formatted tables, and the repo ships a portable Agent Skill (skills/caeruleus/SKILL.md) that teaches any Agent Skills-compatible assistant the command surface and a recommended assessment methodology. Point an agent at a device and it runs the whole methodology end to end.
In our benchmark, Opus 4.8 (xHigh) with Caeruleus and its skill completed the task in 62% of the time and 70% of the tokens of the same model with free tool choice (which reached for hcitool and Bleak scripts).
Caeruleus targets Linux with BlueZ (bluetoothd) and a standard BLE adapter. It does not build or run on macOS or Windows. A handful of raw-socket commands need root.
With the Go toolchain:
go install github.com/praetorian-inc/caeruleus/cmd/caeruleus@latest
Prebuilt release binary (x86_64 and arm64): download the archive for your architecture from the Releases page, extract, and put caeruleus on your $PATH.
From source:
git clone https://github.com/praetorian-inc/caeruleus
cd caeruleus
make build # -> ./caeruleus
make test # unit tests + shell tests
Sanity-check the adapter, then discover what's nearby: