How macOS Bluetooth works (a fully closed bluetoothd + IOBluetooth/CoreBluetooth + IOBluetoothFamily.kext stack), why there is zero Apple open-source for it, why FreeBSD's ancient Netgraph stack can't do BLE, and what an Apple-shape Bluetooth path would actually require. Companion to the WiFi management and Keychain plans.
eap8021x); Keychain had published-but-unbuildable source. Bluetooth has nothing — no daemon source, no kext source, no framework implementation. Only the public API headers exist.CoreBluetooth (BLE/GATT) or IOBluetooth (Classic) → those proxy over XPC/Mach IPC to bluetoothd, the userspace management brain → which drives IOBluetoothFamily.kext → which speaks HCI to the controller. Same shape as wifid, mapped almost one-to-one.MagicPairing (iCloud-synced keys for AirPods / Magic peripherals) and the Continuity BLE-advertisement protocol (Handoff, AirDrop discovery, proximity pairing) are Apple-only protocols riding on standard HCI — documented only via reverse engineering.ng_hci/ng_l2cap/ng_btsocket + hcsecd/sdpd/bthidd) dates to 2002 and supports Classic Bluetooth only — no BLE, no GATT, no SMP. Unlike WiFi, the gap is in the kernel, and there is no wpa_supplicant-equivalent BSD-licensed engine to lean on.com.apple.bluetoothd brain on top. Much heavier than WiFi. Out of scope now; documented so the option space is settled.macOS Bluetooth is layered exactly like the WiFi stack: a kernel driver family, a closed userspace management daemon that owns all the policy, and public framework facades that proxy to it over IPC. From the controller up:
| Component | Role | WiFi analog | Source |
|---|---|---|---|
IOBluetoothFamily.kext + transport drivers |
Kernel Bluetooth family. Owns the HCI transport to the controller (USB on Intel; a custom transport on Apple Silicon), exposes host-stack primitives (L2CAP channels, HCI command/event flow) to userspace via an IOKit user client (Mach port). | IO80211Family.kext |
Closed |
bluetoothd (/usr/sbin/bluetoothd) |
The management brain. Owns device inquiry/discovery, pairing & bonding, link-key / LTK management, connection management, the GATT client/server machinery, profile coordination (HID, A2DP, HFP), and the proprietary MagicPairing/Continuity logic. Runs as a launchd job. |
wifid |
Closed |
IOBluetooth.framework |
Public Classic Bluetooth API (since macOS 10.2): device inquiry, RFCOMM, SDP, pairing, IOBluetoothHostController. Marshals to bluetoothd. |
Apple80211 |
Headers public, impl closed |
CoreBluetooth.framework |
Public BLE / GATT API: CBCentralManager (central role), CBPeripheralManager (peripheral role), CBService/CBCharacteristic. A thin proxy that forwards everything to bluetoothd over XPC. |
CoreWLAN |
Headers public, impl closed |
blued-era tooling / system_profiler SPBluetoothDataType / blueutil |
CLIs and prefpane talking to bluetoothd. |
airport(8) |
Closed (blueutil is 3rd-party) |
The key structural fact: like wifid, bluetoothd is a single monolithic policy daemon. The frameworks do no Bluetooth work themselves — they are XPC stubs. Everything that matters (pairing state machines, GATT, profile logic, the proprietary protocols) lives inside the one closed binary.
This trips people up, so it's worth stating plainly. "Bluetooth" is really two mostly-separate protocol families sharing a radio:
IOBluetooth.framework.CoreBluetooth.framework.They have different pairing security (Classic link keys vs BLE's SMP/LTK), different L2CAP usage, and different discovery (inquiry vs LE advertising/scanning). bluetoothd implements both. This split is why the FreeBSD gap is so damaging: FreeBSD has the Classic half and none of the BLE half — and CoreBluetooth, the API the modern world targets, is the BLE half.
bluetoothd actually owns| Function | What it does |
|---|---|
| Discovery | Classic inquiry; BLE advertising scan; maintaining the discovered-device list and RSSI. |
| Pairing & bonding | SSP (Secure Simple Pairing) for Classic; SMP for BLE; numeric-comparison / passkey UX; generating and persisting link keys / LTKs. |
| Key storage | Link keys + LTKs persisted (historically in /Library/Preferences/com.apple.Bluetooth.plist; sensitive material increasingly keychain-backed). See §1.5. |
| Connection mgmt | ACL/LE connection setup, role switching, low-power modes, reconnect policy for known devices. |
| GATT | The attribute database: service/characteristic discovery, read/write/notify, both as client (CoreBluetooth central) and server (peripheral). |
| Profiles | Coordinates HID (keyboard/mouse/trackpad), A2DP/AVRCP (audio to CoreAudio), HFP (headset). Some ride helper daemons, but bluetoothd brokers. |
| Proprietary | MagicPairing and Continuity (§1.4) — the Apple-only value-add. |
Beyond standard Bluetooth, bluetoothd implements two Apple-only protocols that are the actual "magic" and are entirely undocumented (known only through reverse engineering of bluetoothd strings and device firmware):
bluetoothd and the AirPods' RTKit firmware, and found ten security flaws across the iOS/macOS/RTKit implementations. (Heinze et al., MagicPairing, WiSec 2020)Neither is portable — they depend on iCloud key infrastructure (MagicPairing) and the broader Continuity daemon ecosystem. Even with a perfect BLE stack, AirDrop-grade interop would require reimplementing these closed protocols.
Bluetooth bonding produces long-lived secrets (Classic link keys, BLE LTKs) that must persist across reboots and be available before login (so a paired keyboard works at the login window). On macOS these live in a mix of com.apple.Bluetooth.plist and keychain-protected storage. This is the same problem the Keychain plan addresses for WiFi PSKs: a com.apple.bluetoothd would want a system-scoped secret store for bond keys — exactly the System-keychain consumer that plan calls out. Bluetooth and WiFi both push toward the same secd-equivalent.
This is the headline difference from every other component we've researched. Apple publishes no Bluetooth implementation at all.
| Component | Open? | Notes |
|---|---|---|
bluetoothd | Closed | No repo, anywhere. The entire stack's intelligence. |
IOBluetoothFamily.kext | Closed | No kernel source. |
IOBluetooth.framework / CoreBluetooth.framework | Closed | Public headers only (it's a documented app API); zero implementation. |
| MagicPairing / Continuity | Closed | Undocumented proprietary protocols; only third-party reverse-engineering exists. |
eap8021x). Bluetooth — nothing open; headers only. So there is no Apple code to even study for a port, let alone vendor. Any FreeBSD Bluetooth work is clean-room by necessity.Because "port Bluetooth" means rebuilding these layers, here's what each is — and which half (Classic / BLE) it belongs to.
| Layer | What it is |
|---|---|
| HCI (Host Controller Interface) | The standard command/event interface between the host stack and the Bluetooth controller chip. Everything above it is "the host stack." FreeBSD has this (ng_hci); it's the one layer that's fine. |
| L2CAP (Logical Link Control & Adaptation) | Multiplexing/segmentation layer over HCI — the "TCP-ish" substrate everything else runs on. FreeBSD has Classic L2CAP (ng_l2cap); LE L2CAP (and LE signalling) is missing. |
| SDP (Service Discovery Protocol) | Classic service discovery — "what can this device do?" FreeBSD has it (sdpd). |
| RFCOMM | Classic serial-port emulation; carries legacy profiles and HFP signalling. FreeBSD has it. |
| ATT (Attribute Protocol) | BLE foundation — a simple client/server protocol for reading/writing a remote attribute table. Missing on FreeBSD. |
| GATT (Generic Attribute Profile) | BLE structure on top of ATT: services → characteristics → descriptors. This is what CoreBluetooth exposes. Missing on FreeBSD. |
| SMP (Security Manager Protocol) | BLE pairing/bonding & key (LTK) generation — the BLE analog of Classic SSP/link keys. Missing on FreeBSD. |
| GAP (Generic Access Profile) | Roles & discovery: for BLE, the advertising/scanning model (central/peripheral). FreeBSD has Classic GAP; LE advertising/scanning missing. |
| Profiles (HID, A2DP, AVRCP, HFP) | Application profiles. FreeBSD has Classic HID (bthidd) and partial others; modern audio (LE Audio) absent. |
Notice the pattern: every "missing on FreeBSD" row is a BLE row. The Classic stack is largely complete; the entire LE side (ATT/GATT/SMP/LE-L2CAP/LE-GAP) does not exist.
A Netgraph-based design committed in 2002 (shipped in 5.0), split across kernel Netgraph nodes and userland daemons — structurally not unlike a host stack, and notably it already separates transport / link / management much as a clean design would.
| Piece | Role |
|---|---|
ng_ubt, ng_bt3c, ng_h4 | Transport drivers — USB dongles, PC Card, UART. HCI into the kernel. |
ng_hci | HCI layer Netgraph node (one per controller). |
ng_l2cap | Classic L2CAP. |
ng_btsocket | Socket layer — exposes Bluetooth as AF_BLUETOOTH sockets to userland. |
hccontrol(8) | HCI command CLI (inquiry, connection, controller info). |
hcsecd(8) | The security daemon — handles authentication, PIN codes, and link-key storage (/etc/bluetooth/hcsecd.conf). This is the closest existing analog to bluetoothd's pairing role. |
sdpd(8) | Service Discovery Protocol server. |
bthidd(8) | HID daemon — Bluetooth keyboards/mice. |
So FreeBSD's "brain" is split across hcsecd (pairing/keys) + sdpd (discovery) + bthidd (HID) rather than one bluetoothd. That's actually a cleaner, more BSD-shaped decomposition — but it covers Classic only.
BlueZ is Linux's stack and the obvious "wpa_supplicant of Bluetooth" — full BLE/GATT/mesh, actively maintained. But it's the iwd problem, worse:
bluetooth subsystem: AF_BLUETOOTH sockets, the mgmt API, kernel L2CAP/SMP). bluetoothd(BlueZ) is a thin manager over that kernel stack — it does not contain a portable host stack. Porting it means porting or emulating the Linux kernel Bluetooth subsystem, not just building a daemon.So BlueZ gives you a manager whose engine lives in a kernel you don't have. There is no clean, BSD-licensed, net80211-style userland BLE engine to adopt — the single biggest difference from the WiFi situation.
Unlike WiFi (where the kernel was ready and only a userland brain was needed), Bluetooth needs the kernel/stack BLE layer built first. Three ways to get there:
| Option | Approach | Verdict |
|---|---|---|
| A. Extend Netgraph | Add LE to the existing stack: LE HCI commands/events, LE L2CAP, and new ng_att/GATT + ng_smp (or userland equivalents). Keeps the BSD-native, in-tree design. |
Most BSD-shaped Large kernel+userland effort, but license-clean, in-tree, and matches the existing decomposition. The "right" long-term answer. |
| B. Port/adapt BlueZ | Bring BlueZ to FreeBSD by emulating the Linux kernel BT subsystem or re-backing it onto Netgraph; ship as a port. | Poor fit GPL (ports-only), huge Linux-kernel-emulation surface, D-Bus. The iwd lesson, amplified. |
| C. Greenfield userland LE stack | New userland host stack speaking raw HCI to the controller (bypassing Netgraph's missing LE), implementing ATT/GATT/SMP/LE-L2CAP, exposing a CoreBluetooth-shape API over DO/Mach. | Most Apple-shaped, most work Full control, BSD-licensable, presents the exact API we want — but it's a from-scratch BLE stack. Viable for a focused BLE-central subset (e.g. just GATT client) without boiling the ocean. |
com.apple.bluetoothdWhichever engine option wins, the Apple-shape layer on top is a daemon that — per the shipped system's convention — keeps Apple's own launchd label com.apple.bluetoothd (the running image labels its daemons com.apple.configd, com.apple.hostnamed, … outright; see the Keychain plan's naming note). It exposes CoreBluetooth/IOBluetooth-equivalent APIs over DO/Mach (consistent with the project's IPC choice):
@protocol NCCentralManager
- (void)scanForPeripheralsWithServices:(NSArray<NCUUID *> *)services;
- (void)connectPeripheral:(NCPeripheral *)peripheral;
@end
@protocol NCPeripheral
- (NSString *)identifier; // CBPeripheral analog
- (NSNumber *)RSSI;
- (void)discoverServices:(NSArray<NCUUID *> *)services;
@end
@protocol NCCharacteristic
- (NCUUID *)UUID;
- (NSData *)value;
- (void)setNotifyValue:(BOOL)enabled;
@end
Backed by the chosen engine for the actual GATT/SMP work, persisting bond keys in the System keychain. This is the direct analog of wifid-fbsd — a thin Apple-shape brain over a protocol engine — except the engine has to be built, not adopted.
Bluetooth isn't independent of the WiFi work. Apple's proximity magic is a two-radio dance:
So full AirDrop interop requires all three gaps closed — BLE (here), AWDL (WiFi), and Bonjour — plus the closed Continuity/MagicPairing protocols reimplemented. That makes AirDrop a long-horizon, cross-cutting goal, not a single deliverable. The realistic near-term scope here is standard BLE (connect to a heart-rate monitor, a BLE keyboard, a sensor) — not Apple-proprietary peer interop.
secd) once it exists; until then hcsecd-style config files. Bluetooth is a second consumer arguing for the keychain plan.ng_bluetooth, hcsecd, sdpd, bthidd)