BLE Provisioning Protocol
Admiral OS uses Bluetooth Low Energy exclusively for initial device setup. This page covers the full provisioning protocol — advertising, GATT service and characteristic definitions, the provisioning state machine, security model, error codes, and implementation notes for both firmware and mobile clients.
BLE is active only during out-of-box setup. Once a device reaches FULLY_PROVISIONED, the BLE radio advertisement is permanently disabled until a physical factory reset is performed. The device presents zero BLE attack surface post-setup.
1. Overview
The provisioning protocol allows a mobile companion app to:
- Discover one or more unconfigured Admiral devices in proximity.
- Identify a specific physical device via LED and on-screen pairing animation.
- Configure Wi-Fi and/or Ethernet credentials (individually or in batch across 10+ devices).
- Monitor multi-interface network connection status in real time.
- Retrieve a cloud-issued pairing key for account association.
- Read device metadata (firmware version, serial number, hardware revision).
Design Principles
| Principle | Description |
|---|---|
| Single-Use BLE | BLE is for initial setup only. After FULLY_PROVISIONED, the stack shuts down. Physical reset is required for re-entry. |
| Multi-device Batching | A mobile app can discover 10+ devices, push the same Wi-Fi config to all of them, and asynchronously harvest their pairing keys. |
| Stateless Mobile | The mobile app does not persist BLE connection state. Admiral devices are the source of truth. |
| Minimal MTU | Payloads fit within a 512-byte ATT MTU, falling back to 23-byte MTU with chunked transfers if needed. |
| Secure Link | All sensitive data (Wi-Fi passwords, pairing keys) is protected via application-layer X25519 ECDH key exchange and AES-GCM encryption. |
2. BLE Advertising & Discovery
2.1 Advertising Modes
Admiral devices advertise over BLE only when unprovisioned or when an error occurs before final setup completes.
| Mode | When Active | Interval | Connectable | Notes |
|---|---|---|---|---|
| Setup | Factory reset / first boot / no Wi-Fi config | 100 ms | Yes | Enters power-save after 30 min; wakes on button press. With TimeoutMode enabled, drops advertisement after 1 min with no peers or 5 min of connection inactivity. |
| Configuring | Wi-Fi credentials received, device connecting | 200 ms | Yes | Continues advertising state changes. |
| Error | Wi-Fi connected but cloud unreachable, etc. | 150 ms | Yes | Allows mobile app to reconnect and correct the network config. |
| Provisioned | Setup complete (FULLY_PROVISIONED) | OFF | No | Advertisement permanently disabled until physical reset. |
2.2 Advertising Packet (ADV_IND — 31 bytes)
MAC Address: To avoid duplicate OUI collisions common in bulk generic microcontrollers, the device overrides the controller's primary physical MAC on boot with a Locally Administered Address (LAA) starting with 0x0A. The remaining 5 bytes are populated from a SHA-256 fingerprint of the true hardware serial number.
| Offset | Length | Type | Field | Value |
|---|---|---|---|---|
| 0 | 3 | 0x01 | Flags | 0x06 (LE General Discoverable) |
| 3 | 4 | 0x03 | 16-bit Service UUID | 0xFDA0 |
| 7 | 14 | 0xFF | Manufacturer Data | See §2.3 |
| 21 | 12 | 0x08 | Short Local Name | UTF-8 (e.g., ADM-QWERT123) |
The advertisement broadcasts the 16-bit short UUID 0xFDA0. The full 128-bit GATT service UUID is A0FD0001-C2E1-4F9C-8D2A-0B6C3E4D5F60 — 0xFDA0 is the little-endian byte-reversed form of the A0FD prefix. Mobile clients must use the full 128-bit UUID when discovering and connecting to GATT characteristics.
2.3 Manufacturer Specific Data (9 bytes payload)
Byte 0: Protocol Version — 0x01
Byte 1: Device Type — 0x01 (Standard), 0x02 (Pro)
Byte 2: Provisioning State — See §2.4
Byte 3: Capability Flags — Bitmask (Bit 3: LED, Bit 4: Display Framebuffer)
Byte 4–7: Device ID Hash — Last 4 bytes of SHA-256(hardware serial number)
Byte 8: Reserved — 0x00
The 2-byte Company Identifier is transmitted as a separate BLE Manufacturer Data header field and is not included in the 9-byte payload above.
2.4 Provisioning State Byte
| Value | State | Description |
|---|---|---|
0x00 | UNPROVISIONED | Ready for setup. BLE active. |
0x01 | WIFI_CONFIGURED | Credentials received, attempting to connect. |
0x02 | WIFI_CONNECTED | Connected to Wi-Fi. Resolving cloud connection. |
0x03 | CLOUD_PAIRED | Pairing key received from cloud, waiting for mobile confirmation. |
0x04 | FULLY_PROVISIONED | Mobile confirmed registration. BLE advertising shuts down. |
0xFF | ERROR | Setup failed. Check Error characteristic (§4.11). |
3. Provisioning State Machine
The provisioning lifecycle ensures the device can safely traverse from unconfigured to cloud-paired without getting stuck.
Organic Connections: The Admiral OS runs a background daemon monitoring active network interfaces. If an Ethernet cable is attached, or a previously saved Wi-Fi network reconnects organically during the setup loop, the device asynchronously advances to WIFI_CONNECTED (0x02) and then CLOUD_PAIRED (0x03), pushing notifications to the mobile app. The mobile app must listen for state notifications and dynamically skip the Wi-Fi configuration UI if the device is already online.
State Transitions
| From | To | Trigger |
|---|---|---|
UNPROVISIONED | WIFI_CONFIGURED | Mobile writes Wi-Fi credentials via Network Config characteristic |
UNPROVISIONED | WIFI_CONNECTED | Organic Ethernet or saved Wi-Fi connects |
WIFI_CONFIGURED | WIFI_CONNECTED | Successful Wi-Fi authentication and DHCP lease |
WIFI_CONNECTED | CLOUD_PAIRED | Admiral Cloud issues ephemeral pairing key |
CLOUD_PAIRED | FULLY_PROVISIONED | Mobile writes COMPLETE to Prov Control characteristic |
| Any | ERROR (0xFF) | Authentication failure, DHCP timeout, cloud unreachable |
FULLY_PROVISIONED | UNPROVISIONED | Physical factory reset (10-second pinhole button hold) |
Re-entering Setup
- Physical Factory Reset: Holding the pinhole reset button for 10 seconds clears all saved configurations (Wi-Fi credentials under
/admrl/conf/, custom local settings). The device rolls back toUNPROVISIONEDand restarts BLE advertising on next boot. - Setup Mode Recovery: If pairing is aborted or fails before full provisioning, the system remains in or reverts to setup mode and re-initializes the setup-mode BLE advertisement automatically.
4. GATT Service & Characteristic Definitions
4.1 Services
| # | Service Name | UUID | Type |
|---|---|---|---|
| 1 | Generic Access | 0x1800 | Standard BLE |
| 2 | Admiral Provisioning | A0FD0001-C2E1-4F9C-8D2A-0B6C3E4D5F60 | Custom Primary |
4.2 Characteristics
Base UUID: A0FD????-C2E1-4F9C-8D2A-0B6C3E4D5F60
| Name | Short UUID | Properties | Security | Description |
|---|---|---|---|---|
| Device State | 0010 | Read, Notify | Encrypted | Current provisioning and network status. |
| Command | 0011 | Write | Encrypt+Auth | Send commands (Identify, Scan Wi-Fi, etc.). |
| Cmd Response | 0012 | Read, Notify | Encrypt+Auth | Responses to commands, chunked. |
| Network Config | 0020 | Write | Encrypt+Auth | Push SSID / password / IP settings / link type. |
| Network Status | 0021 | Read, Notify | Encrypted | Current interface state (IP, link type, label). |
| Wi-Fi Scan | 0022 | Read, Notify | Encrypted | Paginated list of discovered SSIDs. |
| Pairing Key | 0030 | Read, Notify | App-Encrypted | Ephemeral token from the Admiral Cloud. |
| Prov Control | 0040 | Write | Encrypted | Session lifecycle (Start, Complete, Abort). |
| Session PubKey | 0050 | Read | None | Device's ephemeral X25519 public key. |
| Timezone Get | 0070 | Read | Encrypted | Current system timezone string (e.g., America/New_York). |
| Timezone Set | 0071 | Write | Encrypted | Sets the system timezone; updates 0070 immediately. |
| Error Info | 00F0 | Read, Notify | Encrypted | Detailed error domains, codes, and messages. |
All Notify characteristics require the client to write 0x0001 to the CCCD descriptor 0x2902.
4.3 Device State — A0FD0010
20 bytes. Notified on any change.
| Byte(s) | Field | Values |
|---|---|---|
| 0 | Provisioning State | See §2.4 |
| 1 | Wi-Fi State | 0x00 Idle · 0x01 Scanning · 0x02 Connecting · 0x03 Connected · 0x04 Has IP |
| 2 | Cloud State | 0x00 Idle · 0x01 Connecting · 0x04 Key Ready |
| 3 | Last Error Code | 0x00 if none |
| 4–7 | Uptime | uint32_t, seconds |
4.4 Command & Response — A0FD0011 / A0FD0012
Write to 0011, read or subscribe to 0012.
Request format: [Cmd ID] [Seq Num] [Payload Length (uint16)] [Payload…]
Response format: [Cmd ID] [Seq Num] [Chunk Index (uint8)] [Total Chunks (uint8)] [Payload Slice…]
All responses use application-layer chunking (max 128 bytes per slice) to prevent truncation across varying MTU sizes.
Commands
| ID | Command | Payload | Description |
|---|---|---|---|
0x01 | IDENTIFY | Duration uint8 (seconds) | Triggers physical setup indicators and system display animation. |
0x02 | SCAN_WIFI | None | Triggers an async Wi-Fi network scan. |
0x03 | GET_WIFI_PAGE | Page number uint8 | Requests a page of scan results from A0FD0022. |
0x04 | GET_SAVED_WIFI | None | Returns a JSON array of previously saved network profiles. |
0x05 | GET_HARDWARE_INFO | None | Returns device metadata and active display topology. |
0x06 | GET_ETHERNET_CFG | None | Returns saved Ethernet IP/DHCP configuration. |
0x07 | GET_WIFI_IP_CFG | SSID string | Returns saved IP/DHCP configuration for a specific Wi-Fi network. |
Response Payloads
0x01(IDENTIFY) and0x03(GET_WIFI_PAGE): Empty acknowledgment. Chunk Index0x00, Total Chunks0x01, empty payload.0x02(SCAN_WIFI):[Status uint8 (0x00 = OK)] [Network Count uint8]0x04(GET_SAVED_WIFI): JSON array of saved network objects.[{ "ssid": "Network1", "priority": 100 }]0x05(GET_HARDWARE_INFO): JSON object with device metadata and display topology.{
"mfg": "Admiral Edge",
"mdl": "ADM-DEVX",
"sn": "ADM-CA2B61A9",
"fw": "0.1.7",
"displays": "HDMI-A-1 @ 3840x2160 @ 60 Hz | DP-1 @ 1920x1080 @ 144 Hz"
}noteHardware identifiers are transmitted via this command rather than the standard
0x180AGATT Device Information Service due to platform-specific system profile limitations.0x06(GET_ETHERNET_CFG): JSON IP settings object. An empty{}signifies default DHCP.{ "dhcp": false, "ip": "192.168.1.50/24", "gw": "192.168.1.1", "dns": "8.8.8.8" }0x07(GET_WIFI_IP_CFG): Same JSON format as0x06, scoped to the requested SSID.
4.5 Network Config — A0FD0020
Accepts a chunked, application-layer encrypted JSON payload containing network credentials or configuration actions.
Chunking
Payloads must be fragmented into 128-byte chunks with a ~30 ms delay between writes:
[Chunk Index (uint8, 0-based)] [Total Chunks (uint8)] [Payload Fragment…]
Reassembled payload (minimum 28 bytes):
[Nonce (12 bytes)] [AES-GCM Ciphertext + 16-byte Auth Tag]
Session Key Exchange
- Mobile reads the device's ephemeral X25519 public key from
A0FD0050. - Mobile derives the shared AES-256 secret via ECDH and transmits its own public key via the
PROV_CTRL STARTcommand (A0FD0040) to open the crypto session. - Mobile encrypts the JSON payload with AES-GCM and pushes the chunked blob.
Payload Actions
Payloads require an "action" key:
save — Push or update a network configuration.
{
"action": "save",
"priority": 100,
"type": "wifi",
"ssid": "MyNetwork",
"psk": "password",
"ip": "192.168.1.50/24",
"gateway": "192.168.1.1",
"dns": "8.8.8.8, 1.1.1.1"
}
- Set
"type": "ethernet"to skip Wi-Fi authentication and apply settings directly to the Ethernet adapter. - Transmitting
"psk": ""on an update preserves the existing stored passphrase while merging other fields. - Static
ipfields must use CIDR notation (e.g.,192.168.1.50/24). Bare IP addresses without a prefix length are rejected by the client before transmission.
delete — Remove a saved network profile.
{ "action": "delete", "ssid": "MyNetwork" }
reset — Wipe all Wi-Fi networks and reset Ethernet to DHCP.
{ "action": "reset" }
4.6 Network Status — A0FD0021
Returns the active network state across all concurrent live interfaces. Proactively notifies the client when an organic connection (Ethernet or saved Wi-Fi) is detected, allowing the mobile app to bypass the Wi-Fi setup step.
Format: [Interface Count (uint8)] followed by per-interface records:
[Link Type (uint8)] [IPv4 Address (4 bytes)] [RSSI (1 byte)] [Label Length (uint8)] [Label (UTF-8)]
- Link Type
0x02= hardwired Ethernet. Other values represent Wi-Fi. - If fully disconnected, the interface list is empty:
[0x00]. - SD-WAN failover events (gateway priority shifts from L4 health checks) update this characteristic and push a notification automatically.
4.7 Wi-Fi Scan Results — A0FD0022
Returns paginated results after a SCAN_WIFI command, triggered page-by-page with GET_WIFI_PAGE.
Header: [Page Number (uint8)] [List Count (uint8)]
Per network record (repeated List Count times):
[SSID Length (uint8)] [SSID (UTF-8)] [RSSI (int8)] [Security (uint8: 0x00=Open, 0x02=Encrypted)]
4.8 Pairing Key — A0FD0030
Returns the cloud pairing key sealed with the established session secret.
Format: [Nonce (12 bytes)] [AES-GCM Ciphertext + 16-byte Auth Tag]
The Device State characteristic (§4.3) transitions Cloud State to 0x04 (Key Ready) when the key is available. The mobile client decrypts it using the existing session secret.
Pairing key validity is tied to the device's installation lifecycle, not a time-based TTL. The key is invalidated immediately upon successful cloud account association to prevent replay attacks.
4.9 Provisioning Control — A0FD0040
Controls the session lifecycle.
| Byte(s) | Field | Values |
|---|---|---|
| 0 | Control Action | 0x01 START · 0x02 COMPLETE · 0x03 ABORT |
| 1–32 | Mobile Public Key | 32-byte X25519 public key (on START only) |
- START (
0x01): Transmits the mobile's X25519 public key and opens the encrypted session. Must be called before writing to any encrypted characteristic (Network Config). Establishing the session immediately on initialization also ensures the device can encrypt and push the Cloud Pairing Key even if it connects organically via Ethernet and skips the Wi-Fi setup. - COMPLETE (
0x02): Signals successful account registration. Triggers immediate shutdown of the BLE stack. - ABORT (
0x03): Tears down the active session. The device remains in or reverts to setup mode.
4.10 Timezone Configuration — A0FD0070 / A0FD0071
- Timezone Get (
0070): Returns the current system timezone as a plain-text UTF-8 string (e.g.,America/New_York,UTC). Defaults toUTCif not yet configured. - Timezone Set (
0071): Accepts a raw UTF-8 string matching a standard IANA timezone database name. Writes trigger an immediate system timezone update, reflected instantly on0070.
4.11 Error Info — A0FD00F0
Notified on failure. See §6 for the full error code table.
5. Identify & Display Integration
Admiral devices feature an LED and an integrated display.
When the mobile app writes the IDENTIFY command (0x01) to A0FD0011:
- The BLE daemon parses the requested duration (default: 10 seconds).
- The system LED switches to an active pulsing pattern.
- The device updates the system display, entering an active Pairing Animation (such as a pulsing connection ring, animated PIN, or avatar).
- After the specified duration, the LED and display revert to their default states.
This ensures a user in a room with multiple identical Admiral devices can identify exactly which unit they are configuring.
6. Error Codes
Delivered via the Error Info characteristic (A0FD00F0).
| Domain | Code | Category | Description | Resolution |
|---|---|---|---|---|
0x11 | 0x11 | Wi-Fi | SSID Not Found | Trigger a new scan and prompt the user. |
0x11 | 0x12 | Network | Configuration / Payload Error | Check credential payload formatting or valid IP ranges. |
0x11 | 0x13 | Network | DHCP Timeout | Connected to network but failed to obtain IP. Verify the backend router. |
0x11 | 0x14 | Wi-Fi | Authentication Failed | Password rejected by access point. Prompt user to re-enter. |
0x11 | 0x15 | Network | Delete Network Failed | Verify the target SSID matches stored profiles. |
0x11 | 0x16 | Network | Reset Network Failed | OS failed to safely clear previous configuration state. |
0x21 | 0x21 | Cloud | Unreachable | Verify the network has internet access and retry. |
0x21 | 0x22 | Cloud | Key Generation Failed | Admiral Cloud backend failure. |
0x30 | 0x30 | System | Display Error | System could not render to display. |
7. Timing & Constraints
| Parameter | Value | Description |
|---|---|---|
| Max Concurrent BLE Clients | 1 | Admiral device accepts only one BLE central at a time. |
| Setup Timeout (No Peers) | 60 s | With TimeoutMode enabled, drops advertisement after 1 min with no peer connections. |
| Setup Timeout (With Peers) | 5 min | Drops advertisement after 5 min of connection inactivity. |
| Connection Idle Timeout | 30 s | Device disconnects if no writes occur within 30 seconds. |
| Wi-Fi Scan Timeout | 5 s | Scan results must be returned within 5 seconds. |
| Identify Default Duration | 10 s | LED/splash sequence duration if not explicitly specified. |
Power-Save Supervisor
Admiral OS uses a multi-tiered power-save supervisor to balance discoverability with radio-silence best practices:
- Unconnected Idle (1 min): If no peer connection is formed within 1 minute while
TimeoutModeis active, the supervisor shuts down BLE advertising and setsadvertising: falsein system state. - Connected Inactivity (5 min): If a client connects to the GATT server but commits no writes (commands or config payloads) for 5 continuous minutes, the supervisor disconnects the link, tears down the advertiser, and enters a low-power quiet state.
- Activity Reset: Any standard activity — new central connection, payload chunk write, identify command — resets the supervisor countdown to zero.
8. Security
Application-Layer Encryption (X25519 ECDH + AES-GCM)
BLE OS-level bonding is bypassed in favor of application-layer encryption to avoid cross-platform bonding complexities.
- The Admiral device generates an ephemeral X25519 keypair on boot and exposes its public key on
A0FD0050. - The mobile app generates its own ephemeral keypair, derives the AES-256 shared secret via ECDH, and transmits its public key strictly via the
PROV_CTRL STARTcommand. This ordering prevents crypto-deadlocks on organic network connections. - Subsequent encrypted payloads (Wi-Fi credentials) omit public keys entirely — they are sealed with AES-GCM using the negotiated secret only.
- Wi-Fi credentials are never readable back to the mobile app over BLE.
Zero Post-Provisioning Attack Surface
Once the mobile app writes COMPLETE to A0FD0040, the OS disables Bluetooth advertising. Re-enablement requires a deliberate physical hardware event — holding a recessed reset button for 10 seconds. There is no software path to re-enable BLE post-provisioning.
9. Multi-Device Batch Provisioning
For deployments with many Admiral devices in the same space:
- Mobile app scans for ~10 seconds, accumulating unique Device IDs from advertisement packets.
- User selects multiple target devices in the mobile app.
- Mobile app provisions devices sequentially: connect → write Wi-Fi config → disconnect → next device.
- Devices connect to Wi-Fi and the Admiral Cloud in parallel asynchronously.
- Mobile app round-robins reconnections to read the Pairing Key from each device as they become ready.
- Mobile app sends a single batch API request to the Admiral Cloud with all collected keys.
- Mobile app round-robins reconnections to send
PROVISIONING_COMPLETEto all devices, triggering their BLE shutdown.
Do not hold a BLE connection open while a device is working through a long Wi-Fi connection phase. Disconnect, move to the next device, and return. Holding connections blocks the single-client limit unnecessarily.
10. Implementation Notes
Firmware (admiral-os-ble-daemon)
- Monitor provisioning state on startup. Only enable the HCI interface if
provisioning_status != FULLY_PROVISIONED. - The
IDENTIFYcommand should trigger the device's physical identity indicators. - MAC Address Override: Before initiating any Bluetooth services, open a raw HCI management socket (
AF_BLUETOOTH,BTPROTO_HCI) to issue the physical controller address override, enforcing unique LAAs across hardware variants. - GATT Cache Purging on Disconnect: To mitigate the iOS Bluetooth GATT service caching bug (where iOS fails to discover updated characteristics on reconnect), remove the device from the system's active device registry and momentarily cycle the advertiser. This forces iOS to prune its GATT service cache.
Mobile Application
- iOS: Use
CBCentralManager. Android: UseBluetoothAdapter.getDefaultAdapter().getBluetoothLeScanner(). - Filter scans on Service UUID
0xFDA0. - Subscribe to Device State (
A0FD0010) and Network Status (A0FD0021) notifications immediately after connecting — both can fire before the UI has progressed past the discovery screen, particularly when Ethernet is already plugged in. - Always send
PROV_CTRL STARTwith the mobile's X25519 public key before writing to any encrypted characteristic.
11. Data Serialization
-
Endianness: Little-endian for all multi-byte integers.
-
Strings / JSON: UTF-8 encoded.
-
Chunking: Large payloads (such as chunked command responses) use application-layer chunking to work around the BLE GATT 512-byte hard limit and OS driver-level packet queue limits. Maximum fragment size: 128 bytes. Enforce a ~30 ms delay between chunk writes.
Byte 0: Chunk Index (0-based)
Byte 1: Total Chunks
Byte 2…: Payload Fragment