Skip to main content

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.

Single-Use BLE

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:

  1. Discover one or more unconfigured Admiral devices in proximity.
  2. Identify a specific physical device via LED and on-screen pairing animation.
  3. Configure Wi-Fi and/or Ethernet credentials (individually or in batch across 10+ devices).
  4. Monitor multi-interface network connection status in real time.
  5. Retrieve a cloud-issued pairing key for account association.
  6. Read device metadata (firmware version, serial number, hardware revision).

Design Principles

PrincipleDescription
Single-Use BLEBLE is for initial setup only. After FULLY_PROVISIONED, the stack shuts down. Physical reset is required for re-entry.
Multi-device BatchingA mobile app can discover 10+ devices, push the same Wi-Fi config to all of them, and asynchronously harvest their pairing keys.
Stateless MobileThe mobile app does not persist BLE connection state. Admiral devices are the source of truth.
Minimal MTUPayloads fit within a 512-byte ATT MTU, falling back to 23-byte MTU with chunked transfers if needed.
Secure LinkAll 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.

ModeWhen ActiveIntervalConnectableNotes
SetupFactory reset / first boot / no Wi-Fi config100 msYesEnters 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.
ConfiguringWi-Fi credentials received, device connecting200 msYesContinues advertising state changes.
ErrorWi-Fi connected but cloud unreachable, etc.150 msYesAllows mobile app to reconnect and correct the network config.
ProvisionedSetup complete (FULLY_PROVISIONED)OFFNoAdvertisement 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.

OffsetLengthTypeFieldValue
030x01Flags0x06 (LE General Discoverable)
340x0316-bit Service UUID0xFDA0
7140xFFManufacturer DataSee §2.3
21120x08Short Local NameUTF-8 (e.g., ADM-QWERT123)
UUID Short Form

The advertisement broadcasts the 16-bit short UUID 0xFDA0. The full 128-bit GATT service UUID is A0FD0001-C2E1-4F9C-8D2A-0B6C3E4D5F600xFDA0 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

ValueStateDescription
0x00UNPROVISIONEDReady for setup. BLE active.
0x01WIFI_CONFIGUREDCredentials received, attempting to connect.
0x02WIFI_CONNECTEDConnected to Wi-Fi. Resolving cloud connection.
0x03CLOUD_PAIREDPairing key received from cloud, waiting for mobile confirmation.
0x04FULLY_PROVISIONEDMobile confirmed registration. BLE advertising shuts down.
0xFFERRORSetup 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

FromToTrigger
UNPROVISIONEDWIFI_CONFIGUREDMobile writes Wi-Fi credentials via Network Config characteristic
UNPROVISIONEDWIFI_CONNECTEDOrganic Ethernet or saved Wi-Fi connects
WIFI_CONFIGUREDWIFI_CONNECTEDSuccessful Wi-Fi authentication and DHCP lease
WIFI_CONNECTEDCLOUD_PAIREDAdmiral Cloud issues ephemeral pairing key
CLOUD_PAIREDFULLY_PROVISIONEDMobile writes COMPLETE to Prov Control characteristic
AnyERROR (0xFF)Authentication failure, DHCP timeout, cloud unreachable
FULLY_PROVISIONEDUNPROVISIONEDPhysical factory reset (10-second pinhole button hold)

Re-entering Setup

  1. 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 to UNPROVISIONED and restarts BLE advertising on next boot.
  2. 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 NameUUIDType
1Generic Access0x1800Standard BLE
2Admiral ProvisioningA0FD0001-C2E1-4F9C-8D2A-0B6C3E4D5F60Custom Primary

4.2 Characteristics

Base UUID: A0FD????-C2E1-4F9C-8D2A-0B6C3E4D5F60

NameShort UUIDPropertiesSecurityDescription
Device State0010Read, NotifyEncryptedCurrent provisioning and network status.
Command0011WriteEncrypt+AuthSend commands (Identify, Scan Wi-Fi, etc.).
Cmd Response0012Read, NotifyEncrypt+AuthResponses to commands, chunked.
Network Config0020WriteEncrypt+AuthPush SSID / password / IP settings / link type.
Network Status0021Read, NotifyEncryptedCurrent interface state (IP, link type, label).
Wi-Fi Scan0022Read, NotifyEncryptedPaginated list of discovered SSIDs.
Pairing Key0030Read, NotifyApp-EncryptedEphemeral token from the Admiral Cloud.
Prov Control0040WriteEncryptedSession lifecycle (Start, Complete, Abort).
Session PubKey0050ReadNoneDevice's ephemeral X25519 public key.
Timezone Get0070ReadEncryptedCurrent system timezone string (e.g., America/New_York).
Timezone Set0071WriteEncryptedSets the system timezone; updates 0070 immediately.
Error Info00F0Read, NotifyEncryptedDetailed error domains, codes, and messages.
note

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)FieldValues
0Provisioning StateSee §2.4
1Wi-Fi State0x00 Idle · 0x01 Scanning · 0x02 Connecting · 0x03 Connected · 0x04 Has IP
2Cloud State0x00 Idle · 0x01 Connecting · 0x04 Key Ready
3Last Error Code0x00 if none
4–7Uptimeuint32_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

IDCommandPayloadDescription
0x01IDENTIFYDuration uint8 (seconds)Triggers physical setup indicators and system display animation.
0x02SCAN_WIFINoneTriggers an async Wi-Fi network scan.
0x03GET_WIFI_PAGEPage number uint8Requests a page of scan results from A0FD0022.
0x04GET_SAVED_WIFINoneReturns a JSON array of previously saved network profiles.
0x05GET_HARDWARE_INFONoneReturns device metadata and active display topology.
0x06GET_ETHERNET_CFGNoneReturns saved Ethernet IP/DHCP configuration.
0x07GET_WIFI_IP_CFGSSID stringReturns saved IP/DHCP configuration for a specific Wi-Fi network.

Response Payloads

  • 0x01 (IDENTIFY) and 0x03 (GET_WIFI_PAGE): Empty acknowledgment. Chunk Index 0x00, Total Chunks 0x01, 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"
    }
    note

    Hardware identifiers are transmitted via this command rather than the standard 0x180A GATT 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 as 0x06, 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

  1. Mobile reads the device's ephemeral X25519 public key from A0FD0050.
  2. Mobile derives the shared AES-256 secret via ECDH and transmits its own public key via the PROV_CTRL START command (A0FD0040) to open the crypto session.
  3. 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 ip fields 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)FieldValues
0Control Action0x01 START · 0x02 COMPLETE · 0x03 ABORT
1–32Mobile Public Key32-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 to UTC if 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 on 0070.

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:

  1. The BLE daemon parses the requested duration (default: 10 seconds).
  2. The system LED switches to an active pulsing pattern.
  3. The device updates the system display, entering an active Pairing Animation (such as a pulsing connection ring, animated PIN, or avatar).
  4. 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).

DomainCodeCategoryDescriptionResolution
0x110x11Wi-FiSSID Not FoundTrigger a new scan and prompt the user.
0x110x12NetworkConfiguration / Payload ErrorCheck credential payload formatting or valid IP ranges.
0x110x13NetworkDHCP TimeoutConnected to network but failed to obtain IP. Verify the backend router.
0x110x14Wi-FiAuthentication FailedPassword rejected by access point. Prompt user to re-enter.
0x110x15NetworkDelete Network FailedVerify the target SSID matches stored profiles.
0x110x16NetworkReset Network FailedOS failed to safely clear previous configuration state.
0x210x21CloudUnreachableVerify the network has internet access and retry.
0x210x22CloudKey Generation FailedAdmiral Cloud backend failure.
0x300x30SystemDisplay ErrorSystem could not render to display.

7. Timing & Constraints

ParameterValueDescription
Max Concurrent BLE Clients1Admiral device accepts only one BLE central at a time.
Setup Timeout (No Peers)60 sWith TimeoutMode enabled, drops advertisement after 1 min with no peer connections.
Setup Timeout (With Peers)5 minDrops advertisement after 5 min of connection inactivity.
Connection Idle Timeout30 sDevice disconnects if no writes occur within 30 seconds.
Wi-Fi Scan Timeout5 sScan results must be returned within 5 seconds.
Identify Default Duration10 sLED/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 TimeoutMode is active, the supervisor shuts down BLE advertising and sets advertising: false in 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 START command. 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:

  1. Mobile app scans for ~10 seconds, accumulating unique Device IDs from advertisement packets.
  2. User selects multiple target devices in the mobile app.
  3. Mobile app provisions devices sequentially: connect → write Wi-Fi config → disconnect → next device.
  4. Devices connect to Wi-Fi and the Admiral Cloud in parallel asynchronously.
  5. Mobile app round-robins reconnections to read the Pairing Key from each device as they become ready.
  6. Mobile app sends a single batch API request to the Admiral Cloud with all collected keys.
  7. Mobile app round-robins reconnections to send PROVISIONING_COMPLETE to all devices, triggering their BLE shutdown.
tip

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 IDENTIFY command 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: Use BluetoothAdapter.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 START with 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