React Native Relay
Forward BLE relay frames from offline devices to Honch from a React Native companion app.
The React Native relay is not a device analytics SDK — it has no track or identify. Use it only when firmware cannot upload directly. It receives Honch relay frames that an offline device sends over BLE, durably reassembles complete compact messages, acknowledges receipt to the device, and uploads to Honch.
Preview
0.1.0. npm package @honch/react-native-relay.The Host-Owned Bluetooth Model
Bluetooth is owned by your app, not by this package. It never scans, connects, subscribes, requests BLE permissions, or writes characteristics. Your app owns the BLE stack and hands frame bytes to the relay; the relay returns ACK bytes only after the frame is durably stored, and your app writes those bytes back to the device's ACK characteristic.
In short: Bluetooth is host-owned; durable assembly, ACK construction, and upload are relay-owned. The BLE service and characteristic UUIDs and the frame format are defined in the relay-chunks spec.
1. Install
bun add @honch/react-native-relayPeer dependency: react-native >= 0.72. For the MMKV-backed durable store, also add react-native-mmkv (optional). The package publishes TypeScript source — Metro transpiles it; there is no separate build step.
2. Wire The Relay
import { NativeModules } from "react-native";
import { createMMKV } from "react-native-mmkv";
import {
createMmkvRelayStore,
createMobileRelay,
createRelayNativeBindings,
} from "@honch/react-native-relay";
const bindings = createRelayNativeBindings(NativeModules.HonchReactNativeRelay);
export const relay = createMobileRelay({
durableStore: createMmkvRelayStore(createMMKV({ id: "honch-relay" })),
uploaderConfig: {
endpointUrl: "https://i.honch.io",
projectKey: "your-project-key",
relayId: "mobile-relay-01",
relaySdkPlatform: "react-native",
relaySdkVersion: "0.1.0",
streamId: (m) => `relay-${m.deviceId}`,
messageId: (m) => Number(m.sequence),
},
schedulerNative: bindings.schedulerNative,
});createMobileRelay returns { receiveFrame, pending, startUploadScheduler, stopUploadScheduler, drainUploads }. uploaderConfig.endpointUrl defaults to https://i.honch.io.
3. Hand Frames In And ACK
When your BLE layer receives a frame notification, pass the bytes to receiveFrame. Provide an acknowledge callback that writes the returned ACK bytes to the device's ACK characteristic — the relay calls it only after the frame is durably stored.
await relay.receiveFrame(deviceId, frameBytes, {
acknowledge: async ({ ackBytes }) => {
await writeAckCharacteristic(deviceId, ackBytes);
},
});An ACK is 9 bytes: a version byte (0x01) followed by a big-endian uint64 sequence number.
4. Upload To Capture
Uploads add relay headers on top of the standard contract:
POST /capture
Content-Type: application/vnd.honch.chunk
X-Honch-Project-Key: <project_key>
X-Honch-Stream-Id: <stream_id>
X-Honch-Relay-Id: <relay_id>
X-Honch-Relay-SDK-Platform: <platform>
X-Honch-Relay-SDK-Version: <version>| Result | HTTP | Action |
|---|---|---|
| Accepted | 204 (final), 202 (non-final chunk) | Message delivered / continue. |
| Retryable | 408, 409, 429, 5xx | Back off and retry (1 s → 5 min, ±25% jitter). |
| Permanent | 400, 401, 404, 413, 415, 422 | Drop. |
On iOS, background scheduling requires a native binding; without one, startUploadScheduler() drains immediately and uploads are foreground-only. On Android the package registers a HonchRelayUpload headless task (add androidx.work:work-runtime).
5. Native Host Requirements
- iOS:
NSBluetoothAlwaysUsageDescription, thebluetooth-centralbackground mode. No native background upload module ships in0.1.0— calldrainUploads()from the foreground. - Android:
BLUETOOTH_SCAN/BLUETOOTH_CONNECT(and location where required by your OS version), plusandroidx.work:work-runtimefor the headless upload task.
The package does not merge BLE, location, or notification permissions into your app manifest — declare what your app needs.
Public TypeScript Surface
Frame handling: decodeRelayFrame, createRelayFrameReceiver, buildRelayAck. Queues: createInMemoryRelayQueue, createDurableRelayQueue, drainRelayQueue, nextBackoffDelayMs. Durable stores: createMemoryDurableStore, createMmkvRelayStore. Uploading: buildRelayUploadBuffer, uploadRelayMessage, uploadRelayMessageOutcome. Orchestration: createMobileRelay, createRelayUploadScheduler, createRelayNativeBindings.