GitHub

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

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-relay

Peer 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>
ResultHTTPAction
Accepted204 (final), 202 (non-final chunk)Message delivered / continue.
Retryable408, 409, 429, 5xxBack off and retry (1 s → 5 min, ±25% jitter).
Permanent400, 401, 404, 413, 415, 422Drop.

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, the bluetooth-central background mode. No native background upload module ships in 0.1.0 — call drainUploads() from the foreground.
  • Android: BLUETOOTH_SCAN / BLUETOOTH_CONNECT (and location where required by your OS version), plus androidx.work:work-runtime for 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.

honch.

Product analytics for consumer hardware.