@offline-protocol/mesh-sdk 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,14 +1,36 @@
1
1
  # @offline-protocol/mesh-sdk
2
2
 
3
- Offline-first mesh networking SDK with intelligent transport switching for React Native. Built with Rust for maximum performance and reliability.
3
+ Offline-first mesh networking SDK for React Native. Enables peer-to-peer messaging over BLE, WiFi Direct, and Internet with intelligent transport switching.
4
4
 
5
- ## Features
5
+ ## Table of Contents
6
6
 
7
- - **Offline-First**: Messages delivered even without internet connectivity
8
- - **Intelligent Transport Switching**: DORS automatically selects the best transport (Internet, BLE, WiFi Direct)
9
- - **Mesh Networking**: Multi-hop routing with automatic relay selection
10
- - **Cross-Platform**: Works on iOS and Android
11
- - **Type-Safe**: Full TypeScript support
7
+ - [Requirements](#requirements)
8
+ - [Installation](#installation)
9
+ - [Platform Setup](#platform-setup)
10
+ - [Quick Start](#quick-start)
11
+ - [Protocol Lifecycle](#protocol-lifecycle)
12
+ - [Configuration](#configuration)
13
+ - [API Reference](#api-reference)
14
+ - [Events](#events)
15
+ - [Types](#types)
16
+ - [DORS (Transport Switching)](#dors-dynamic-offline-relay-switch)
17
+ - [Mesh Networking](#mesh-networking)
18
+ - [Reliability Layer](#reliability-layer)
19
+ - [File Transfer](#file-transfer)
20
+ - [Troubleshooting](#troubleshooting)
21
+
22
+ ---
23
+
24
+ ## Requirements
25
+
26
+ | Platform | Version |
27
+ |----------|---------|
28
+ | React Native | >= 0.70.0 |
29
+ | iOS | >= 13.0 |
30
+ | Android | >= API 26 (Android 8.0) |
31
+ | Node.js | >= 16 |
32
+
33
+ ---
12
34
 
13
35
  ## Installation
14
36
 
@@ -16,15 +38,63 @@ Offline-first mesh networking SDK with intelligent transport switching for React
16
38
  npm install @offline-protocol/mesh-sdk
17
39
  ```
18
40
 
19
- ### iOS Setup
41
+ ### iOS
20
42
 
21
43
  ```bash
22
44
  cd ios && pod install
23
45
  ```
24
46
 
25
- ### Android Setup
47
+ ### Android
48
+
49
+ Pre-built native libraries are included. No additional setup required.
50
+
51
+ ---
52
+
53
+ ## Platform Setup
54
+
55
+ ### iOS Permissions
56
+
57
+ Add to `Info.plist`:
58
+
59
+ ```xml
60
+ <key>NSBluetoothAlwaysUsageDescription</key>
61
+ <string>Required for offline mesh communication</string>
26
62
 
27
- No additional setup needed. Pre-built libraries are included.
63
+ <key>NSBluetoothPeripheralUsageDescription</key>
64
+ <string>Required for offline mesh communication</string>
65
+
66
+ <key>UIBackgroundModes</key>
67
+ <array>
68
+ <string>bluetooth-central</string>
69
+ <string>bluetooth-peripheral</string>
70
+ </array>
71
+ ```
72
+
73
+ ### Android Permissions
74
+
75
+ Add to `AndroidManifest.xml`:
76
+
77
+ ```xml
78
+ <!-- Bluetooth (Android 12+) -->
79
+ <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
80
+ <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
81
+ <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
82
+
83
+ <!-- Bluetooth (Android 11 and below) -->
84
+ <uses-permission android:name="android.permission.BLUETOOTH" />
85
+ <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
86
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
87
+
88
+ <!-- WiFi Direct -->
89
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
90
+ <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
91
+ <uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES" />
92
+
93
+ <!-- Internet -->
94
+ <uses-permission android:name="android.permission.INTERNET" />
95
+ ```
96
+
97
+ ---
28
98
 
29
99
  ## Quick Start
30
100
 
@@ -47,62 +117,404 @@ const messageId = await protocol.sendMessage({
47
117
  content: 'Hello!',
48
118
  priority: MessagePriority.High,
49
119
  });
120
+
121
+ await protocol.stop();
122
+ await protocol.destroy();
123
+ ```
124
+
125
+ ---
126
+
127
+ ## Protocol Lifecycle
128
+
129
+ ### Complete Flow Example
130
+
131
+ ```typescript
132
+ import {
133
+ OfflineProtocol,
134
+ MessagePriority,
135
+ ProtocolEvent,
136
+ MessageReceivedEvent,
137
+ MessageDeliveredEvent,
138
+ NeighborDiscoveredEvent,
139
+ } from '@offline-protocol/mesh-sdk';
140
+
141
+ // 1. CREATE PROTOCOL INSTANCE
142
+ const protocol = new OfflineProtocol({
143
+ appId: 'my-chat-app',
144
+ userId: 'alice-device-001',
145
+ });
146
+
147
+ // 2. REGISTER EVENT LISTENERS (before starting)
148
+
149
+ // Track discovered peers
150
+ const discoveredPeers = new Map<string, number>(); // peerId -> rssi
151
+
152
+ protocol.on('neighbor_discovered', (event: NeighborDiscoveredEvent) => {
153
+ console.log(`[PEER FOUND] ${event.peer_id} via ${event.transport}, RSSI: ${event.rssi}`);
154
+ discoveredPeers.set(event.peer_id, event.rssi ?? -100);
155
+ });
156
+
157
+ protocol.on('neighbor_lost', (event) => {
158
+ console.log(`[PEER LOST] ${event.peer_id}`);
159
+ discoveredPeers.delete(event.peer_id);
160
+ });
161
+
162
+ // Track outgoing messages
163
+ const pendingMessages = new Map<string, { recipient: string; content: string }>();
164
+
165
+ protocol.on('message_sent', (event) => {
166
+ console.log(`[SENT] Message ${event.message_id} to ${event.recipient}`);
167
+ pendingMessages.set(event.message_id, {
168
+ recipient: event.recipient,
169
+ content: event.content,
170
+ });
171
+ });
172
+
173
+ protocol.on('message_delivered', (event: MessageDeliveredEvent) => {
174
+ console.log(`[DELIVERED] Message ${event.message_id} in ${event.latency_ms}ms, ${event.hop_count} hops`);
175
+ pendingMessages.delete(event.message_id);
176
+ });
177
+
178
+ protocol.on('message_failed', (event) => {
179
+ console.log(`[FAILED] Message ${event.message_id}: ${event.reason} (${event.retry_count} retries)`);
180
+ pendingMessages.delete(event.message_id);
181
+ });
182
+
183
+ // Handle incoming messages
184
+ protocol.on('message_received', (event: MessageReceivedEvent) => {
185
+ console.log(`[RECEIVED] From ${event.sender}: ${event.content}`);
186
+ console.log(` - Message ID: ${event.message_id}`);
187
+ console.log(` - Hop count: ${event.hop_count}`);
188
+ console.log(` - Transport: ${event.transport}`);
189
+
190
+ // Process the message in your app
191
+ handleIncomingMessage(event);
192
+ });
193
+
194
+ // Monitor transport changes
195
+ protocol.on('transport_switched', (event) => {
196
+ console.log(`[TRANSPORT] Switched from ${event.from} to ${event.to}: ${event.reason}`);
197
+ });
198
+
199
+ // 3. START THE PROTOCOL
200
+ await protocol.start();
201
+ // At this point:
202
+ // - BLE scanning begins (discovers nearby devices)
203
+ // - BLE advertising begins (makes this device discoverable)
204
+ // - neighbor_discovered events will start firing as peers are found
205
+
206
+ // 4. WAIT FOR PEERS (optional helper)
207
+ async function waitForPeer(peerId: string, timeoutMs = 30000): Promise<boolean> {
208
+ if (discoveredPeers.has(peerId)) return true;
209
+
210
+ return new Promise((resolve) => {
211
+ const timeout = setTimeout(() => resolve(false), timeoutMs);
212
+
213
+ const handler = (event: NeighborDiscoveredEvent) => {
214
+ if (event.peer_id === peerId) {
215
+ clearTimeout(timeout);
216
+ protocol.off('neighbor_discovered', handler);
217
+ resolve(true);
218
+ }
219
+ };
220
+
221
+ protocol.on('neighbor_discovered', handler);
222
+ });
223
+ }
224
+
225
+ // 5. SEND A MESSAGE
226
+ async function sendChatMessage(recipientId: string, text: string) {
227
+ try {
228
+ const messageId = await protocol.sendMessage({
229
+ recipient: recipientId,
230
+ content: text,
231
+ priority: MessagePriority.High,
232
+ });
233
+ console.log(`Message queued with ID: ${messageId}`);
234
+ return messageId;
235
+ } catch (error) {
236
+ console.error('Failed to send message:', error);
237
+ throw error;
238
+ }
239
+ }
240
+
241
+ // 6. CLEANUP ON APP EXIT
242
+ async function cleanup() {
243
+ await protocol.stop();
244
+ await protocol.destroy();
245
+ }
246
+ ```
247
+
248
+ ### Event Sequence Timeline
249
+
250
+ ```
251
+ ┌─────────────────────────────────────────────────────────────────────┐
252
+ │ PROTOCOL LIFECYCLE │
253
+ ├─────────────────────────────────────────────────────────────────────┤
254
+ │ │
255
+ │ new OfflineProtocol(config) │
256
+ │ │ │
257
+ │ ▼ │
258
+ │ protocol.on('...', handler) ← Register all event listeners │
259
+ │ │ │
260
+ │ ▼ │
261
+ │ await protocol.start() │
262
+ │ │ │
263
+ │ ├──► BLE advertising starts (device becomes discoverable) │
264
+ │ ├──► BLE scanning starts (looking for other devices) │
265
+ │ │ │
266
+ │ ▼ │
267
+ │ ┌─────────────────────────────────────────────────────────────┐ │
268
+ │ │ PEER DISCOVERY PHASE │ │
269
+ │ │ │ │
270
+ │ │ neighbor_discovered { peer_id, transport, rssi } │ │
271
+ │ │ neighbor_discovered { peer_id, transport, rssi } │ │
272
+ │ │ ... │ │
273
+ │ │ │ │
274
+ │ │ MeshController evaluates peers, establishes connections │ │
275
+ │ │ (MEMBER for same cluster, BRIDGE for different clusters) │ │
276
+ │ └─────────────────────────────────────────────────────────────┘ │
277
+ │ │ │
278
+ │ ▼ │
279
+ │ ┌─────────────────────────────────────────────────────────────┐ │
280
+ │ │ MESSAGING PHASE │ │
281
+ │ │ │ │
282
+ │ │ protocol.sendMessage({ recipient, content, priority }) │ │
283
+ │ │ │ │ │
284
+ │ │ ▼ │ │
285
+ │ │ message_sent { message_id, recipient, content, ... } │ │
286
+ │ │ │ │ │
287
+ │ │ ├──► [SUCCESS] message_delivered { message_id, ... } │ │
288
+ │ │ │ │ │
289
+ │ │ └──► [FAILURE] message_failed { message_id, reason } │ │
290
+ │ │ │ │
291
+ │ │ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │ │
292
+ │ │ │ │
293
+ │ │ INCOMING: message_received { sender, content, ... } │ │
294
+ │ └─────────────────────────────────────────────────────────────┘ │
295
+ │ │ │
296
+ │ │ (peers may come and go) │
297
+ │ │ │
298
+ │ ▼ │
299
+ │ neighbor_lost { peer_id } │
300
+ │ neighbor_discovered { peer_id, ... } ← new peer appears │
301
+ │ │ │
302
+ │ ▼ │
303
+ │ await protocol.stop() │
304
+ │ │ │
305
+ │ ├──► BLE scanning stops │
306
+ │ ├──► BLE advertising stops │
307
+ │ ├──► All connections closed │
308
+ │ │ │
309
+ │ ▼ │
310
+ │ await protocol.destroy() ← Clean up resources │
311
+ │ │
312
+ └─────────────────────────────────────────────────────────────────────┘
313
+ ```
314
+
315
+ ### What Happens Under the Hood
316
+
317
+ #### On `protocol.start()`
318
+
319
+ 1. **Protocol core starts** in Rust
320
+ 2. **BLE Manager initializes**:
321
+ - Starts scanning for devices advertising the Offline Protocol service UUID
322
+ - Starts advertising this device with mesh metadata (degree, free slots, battery, uptime)
323
+ 3. **Process timer starts** - polls for outgoing fragments every 100ms
324
+
325
+ #### On Peer Discovery
326
+
327
+ 1. **BLE scan detects advertisement** from another device
328
+ 2. **MeshController.shouldInitiateOutbound()** evaluates the candidate:
329
+ - Checks connection budget (default max: 4)
330
+ - Calculates peer score (RSSI, availability, battery, uptime, stability, load)
331
+ - Determines if this is a cluster bridge opportunity
332
+ 3. **If accepted**: BLE connection established, `neighbor_discovered` fires
333
+ 4. **If at capacity**: May evict a lower-scoring peer to make room
334
+
335
+ #### On `protocol.sendMessage()`
336
+
337
+ 1. **Message created** with unique ID, TTL, timestamp, priority
338
+ 2. **message_sent event** fires immediately
339
+ 3. **Message queued** for transmission
340
+ 4. **DORS selects transport** (BLE, WiFi Direct, or Internet)
341
+ 5. **Message sent** to connected peers
342
+ 6. **ACK tracking begins** (default 5s timeout)
343
+ 7. **On ACK received**: `message_delivered` event fires
344
+ 8. **On timeout/max retries**: `message_failed` event fires
345
+
346
+ #### On Incoming Message
347
+
348
+ 1. **BLE fragment received** from peer
349
+ 2. **Deduplication check** - skip if message ID already seen
350
+ 3. **If addressed to this device**: `message_received` event fires
351
+ 4. **ACK sent back** to sender
352
+ 5. **Hop count incremented** for metrics
353
+
354
+ #### On `protocol.stop()`
355
+
356
+ 1. **BLE Manager stops** scanning and advertising
357
+ 2. **All peer connections closed**
358
+ 3. **neighbor_lost events** fire for each disconnected peer
359
+ 4. **Protocol core stops**
360
+
361
+ ### Diagnostic Events
362
+
363
+ The SDK emits diagnostic events for debugging:
364
+
365
+ ```typescript
366
+ protocol.on('diagnostic', (event) => {
367
+ console.log(`[${event.level.toUpperCase()}] ${event.message}`, event.context);
368
+ });
50
369
  ```
51
370
 
371
+ ---
372
+
52
373
  ## Configuration
53
374
 
375
+ ### ProtocolConfig
376
+
54
377
  ```typescript
55
378
  interface ProtocolConfig {
56
379
  appId: string;
57
380
  userId: string;
58
- transport?: {
59
- bleEnabled?: boolean; // default: true
60
- wifiDirectEnabled?: boolean; // default: true
61
- internetEnabled?: boolean; // default: true
381
+ transports?: TransportsConfig;
382
+ dors?: DorsConfig;
383
+ network?: NetworkConfig;
384
+ reliability?: ReliabilityConfig;
385
+ fileTransfer?: FileTransferConfig;
386
+ path?: PathConfig;
387
+ }
388
+ ```
389
+
390
+ ### TransportsConfig
391
+
392
+ ```typescript
393
+ interface TransportsConfig {
394
+ ble?: {
395
+ enabled: boolean; // default: true
62
396
  };
63
- dors?: {
64
- preferOnline?: boolean; // default: false
65
- switchHysteresis?: number; // default: 15.0
66
- switchCooldownSecs?: number; // default: 20
397
+ internet?: {
398
+ enabled: boolean; // default: false
399
+ serverAddress?: string; // WebSocket URL
400
+ autoReconnect?: boolean; // default: true
401
+ reconnectDelay?: number; // ms
67
402
  };
68
- relay?: {
69
- allowRelay?: boolean; // default: true
70
- minBatteryForRelay?: number; // default: 30
71
- relayThreshold?: number; // default: 3
403
+ wifiDirect?: {
404
+ enabled: boolean; // default: false (Android only)
405
+ deviceName?: string;
406
+ autoAccept?: boolean;
407
+ groupOwnerIntent?: number; // 0-15
72
408
  };
73
- network?: {
74
- initialTtl?: number; // default: 8
409
+ }
410
+ ```
411
+
412
+ ### DorsConfig
413
+
414
+ Controls transport switching behavior.
415
+
416
+ ```typescript
417
+ interface DorsConfig {
418
+ preferOnline?: boolean; // default: false
419
+ switchHysteresis?: number; // default: 15.0
420
+ switchCooldownSecs?: number; // default: 20
421
+ bleToWifiRetryThreshold?: number; // default: 2
422
+ rssiSwitchThreshold?: number; // default: -85 dBm
423
+ congestionQueueThreshold?: number; // default: 50
424
+ stabilityWindowSecs?: number; // default: 8
425
+ poorSignalDurationSecs?: number; // default: 10
426
+ ttlEscalationThreshold?: number; // default: 2
427
+ congestionDurationSecs?: number; // default: 10
428
+ ttlEscalationHoldSecs?: number; // default: 20
429
+ historyWindowSize?: number; // default: 10
430
+ queueRecoveryRatio?: number; // default: 0.5
431
+ }
432
+ ```
433
+
434
+
435
+ ### NetworkConfig
436
+
437
+ ```typescript
438
+ interface NetworkConfig {
439
+ initialTtl?: number; // default: 8
440
+ }
441
+ ```
442
+
443
+ ### ReliabilityConfig
444
+
445
+ ```typescript
446
+ interface ReliabilityConfig {
447
+ ack?: {
448
+ defaultTimeoutMs?: number; // default: 5000
449
+ maxPendingAcks?: number; // default: 1000
75
450
  };
451
+ retry?: {
452
+ maxRetries?: number; // default: 5
453
+ initialDelayMs?: number; // default: 1000
454
+ maxDelayMs?: number; // default: 30000
455
+ backoffMultiplier?: number; // default: 2.0
456
+ outboxMaxLifetimeMs?: number; // default: 3600000
457
+ };
458
+ dedup?: {
459
+ maxTrackedMessages?: number; // default: 10000
460
+ retentionTimeSecs?: number; // default: 3600
461
+ };
462
+ }
463
+ ```
464
+
465
+ ### PathConfig
466
+
467
+ ```typescript
468
+ interface PathConfig {
469
+ forwardToTopK?: number; // default: 3
470
+ maxCongestionLevel?: number; // default: 0.8
471
+ }
472
+ ```
473
+
474
+ ### FileTransferConfig
475
+
476
+ ```typescript
477
+ interface FileTransferConfig {
478
+ chunkSize?: number; // default: 32768 (32KB)
479
+ maxFileSize?: number; // default: 104857600 (100MB)
76
480
  }
77
481
  ```
78
482
 
79
- ## API
483
+ ---
80
484
 
81
- ### Methods
485
+ ## API Reference
82
486
 
83
- - `start(): Promise<void>` - Start the protocol
84
- - `stop(): Promise<void>` - Stop the protocol
85
- - `sendMessage(params): Promise<string>` - Send a message
86
- - `on(eventType, listener)` - Register event listener
87
- - `off(eventType, listener)` - Remove event listener
88
- - `destroy(): Promise<void>` - Clean up resources
487
+ ### Constructor
488
+
489
+ ```typescript
490
+ new OfflineProtocol(config: ProtocolConfig)
491
+ ```
89
492
 
90
- ### Events
493
+ ### Lifecycle Methods
91
494
 
92
- **Message Events:**
93
- - `message_sent` - Message was sent
94
- - `message_received` - Message was received
95
- - `message_delivered` - Message was delivered (ACK received)
96
- - `message_failed` - Message delivery failed
495
+ | Method | Returns | Description |
496
+ |--------|---------|-------------|
497
+ | `start()` | `Promise<void>` | Start the protocol and all enabled transports |
498
+ | `stop()` | `Promise<void>` | Stop the protocol and disconnect all peers |
499
+ | `pause()` | `Promise<void>` | Pause for background mode |
500
+ | `resume()` | `Promise<void>` | Resume from paused state |
501
+ | `destroy()` | `Promise<void>` | Clean up all resources |
502
+ | `getState()` | `Promise<ProtocolState>` | Get current state (`Stopped`, `Running`, `Paused`) |
97
503
 
98
- **Network Events:**
99
- - `transport_switched` - Transport changed (BLE/WiFi/Internet)
100
- - `neighbor_discovered` - New neighbor found
101
- - `neighbor_lost` - Neighbor disconnected
504
+ ### Messaging
102
505
 
103
- ### Message Priority
506
+ | Method | Returns | Description |
507
+ |--------|---------|-------------|
508
+ | `sendMessage(params: SendMessageParams)` | `Promise<string>` | Send message, returns message ID |
509
+ | `receiveMessage()` | `Promise<MessageReceivedEvent \| null>` | Poll for next received message |
104
510
 
105
511
  ```typescript
512
+ interface SendMessageParams {
513
+ recipient: string;
514
+ content: string;
515
+ priority?: MessagePriority; // default: Medium
516
+ }
517
+
106
518
  enum MessagePriority {
107
519
  Low = 0,
108
520
  Medium = 1,
@@ -111,85 +523,530 @@ enum MessagePriority {
111
523
  }
112
524
  ```
113
525
 
114
- ## How It Works
526
+ ### Transport Management
527
+
528
+ | Method | Returns | Description |
529
+ |--------|---------|-------------|
530
+ | `getActiveTransports()` | `Promise<TransportType[]>` | Get list of active transports |
531
+ | `enableTransport(type, config?)` | `Promise<void>` | Enable a transport |
532
+ | `disableTransport(type)` | `Promise<void>` | Disable a transport |
533
+ | `forceTransport(type)` | `Promise<void>` | Force specific transport (override DORS) |
534
+ | `releaseTransportLock()` | `Promise<void>` | Release forced transport, let DORS decide |
535
+ | `getTransportMetrics(type)` | `Promise<TransportMetrics \| null>` | Get transport statistics |
536
+
537
+ ```typescript
538
+ type TransportType = 'ble' | 'internet' | 'wifiDirect';
539
+
540
+ interface TransportMetrics {
541
+ packetsSent: number;
542
+ packetsReceived: number;
543
+ bytesSent: number;
544
+ bytesReceived: number;
545
+ errorRate: number;
546
+ avgLatencyMs: number;
547
+ }
548
+ ```
549
+
550
+ ### Bluetooth
551
+
552
+ | Method | Returns | Description |
553
+ |--------|---------|-------------|
554
+ | `isBluetoothEnabled()` | `Promise<boolean>` | Check if Bluetooth is enabled |
555
+ | `requestEnableBluetooth()` | `Promise<boolean>` | Request to enable Bluetooth (Android only) |
556
+ | `getBLePeerCount()` | `Promise<number>` | Get number of discovered BLE peers |
557
+
558
+ ### Network Topology
559
+
560
+ | Method | Returns | Description |
561
+ |--------|---------|-------------|
562
+ | `getTopology()` | `Promise<NetworkTopology>` | Get network topology snapshot |
563
+ | `getMessageStats()` | `Promise<MessageDeliveryStats[]>` | Get message delivery statistics |
564
+ | `getDeliverySuccessRate()` | `Promise<number>` | Get delivery success rate (0-1) |
565
+ | `getMedianLatency()` | `Promise<number \| null>` | Get median latency in ms |
566
+ | `getMedianHops()` | `Promise<number \| null>` | Get median hop count |
567
+
568
+ ### Battery
115
569
 
116
- ### DORS (Dynamic Offline Relay Switch)
570
+ | Method | Returns | Description |
571
+ |--------|---------|-------------|
572
+ | `setBatteryLevel(level)` | `Promise<void>` | Set battery level (0-100) for mesh decisions |
573
+ | `getBatteryLevel()` | `Promise<number \| null>` | Get current battery level |
117
574
 
118
- DORS automatically selects the best transport (Internet, BLE, or WiFi Direct) based on:
119
- - Signal strength (RSSI)
120
- - Bandwidth and congestion
121
- - Energy efficiency
122
- - Reliability and proximity
575
+ ### DORS Configuration
123
576
 
124
- ### Mesh Network
577
+ | Method | Returns | Description |
578
+ |--------|---------|-------------|
579
+ | `updateDorsConfig(config)` | `Promise<void>` | Update DORS settings at runtime |
580
+ | `getDorsConfig()` | `Promise<DorsConfig>` | Get current DORS configuration |
581
+ | `shouldEscalateToWifi()` | `Promise<boolean>` | Check if DORS recommends WiFi escalation |
125
582
 
126
- The SDK implements a cluster-based mesh network where devices organize into clusters and form connections based on mesh topology:
583
+ ### Reliability Configuration
127
584
 
128
- **Cluster Architecture:**
129
- - **MEMBER Role**: Devices within the same cluster (intra-cluster connections)
130
- - **BRIDGE Role**: Devices connecting different clusters (inter-cluster connections)
131
- - **Connection Budget**: Each device maintains up to 4 active connections (configurable)
585
+ | Method | Returns | Description |
586
+ |--------|---------|-------------|
587
+ | `updateAckConfig(config)` | `Promise<void>` | Update ACK settings |
588
+ | `updateRetryConfig(config)` | `Promise<void>` | Update retry settings |
589
+ | `updateDedupConfig(config)` | `Promise<void>` | Update deduplication settings |
590
+ | `getDedupStats()` | `Promise<DedupStats>` | Get deduplication statistics |
591
+ | `getPendingAckCount()` | `Promise<number>` | Get pending ACK count |
592
+ | `getRetryQueueSize()` | `Promise<number>` | Get retry queue size |
132
593
 
133
- **Connection Decision Process:**
134
- 1. Devices discover each other via BLE advertisements containing mesh metadata
135
- 2. MeshController evaluates connection candidates based on:
136
- - Available connection slots
137
- - Peer scores (RSSI, battery, uptime, stability, load)
138
- - Cluster membership and free slot estimates
139
- 3. Connection intent determines role:
140
- - `INTRA_CLUSTER` → MEMBER role (same cluster)
141
- - `INTER_CLUSTER` → BRIDGE role (different clusters)
142
- 4. When connection budget is full, the system can evict the worst peer to make room for better connections
594
+ ### Gradient Routing
143
595
 
144
- **Message Routing:**
145
- - Direct delivery when recipient is a connected peer (1 hop)
146
- - Multi-hop routing through cluster members and bridges (up to TTL hops, default 8)
147
- - Automatic path selection based on cluster topology and peer quality
148
- - Messages traverse clusters via bridge connections when needed
596
+ | Method | Returns | Description |
597
+ |--------|---------|-------------|
598
+ | `learnRoute(destination, nextHop, hopCount, quality)` | `Promise<void>` | Learn a route from incoming message |
599
+ | `getBestRoute(destination)` | `Promise<RouteEntry \| null>` | Get best route to destination |
600
+ | `getAllRoutes(destination)` | `Promise<RouteEntry[]>` | Get all routes to destination |
601
+ | `hasRoute(destination)` | `Promise<boolean>` | Check if route exists |
602
+ | `removeNeighborRoutes(neighborId)` | `Promise<void>` | Remove routes through neighbor |
603
+ | `cleanupExpiredRoutes()` | `Promise<void>` | Clean up expired routes |
604
+ | `getRoutingStats()` | `Promise<RoutingStats>` | Get routing table statistics |
605
+ | `updateRoutingConfig(config)` | `Promise<void>` | Update routing configuration |
149
606
 
150
- ## Example
607
+ ### Event Listeners
608
+
609
+ | Method | Returns | Description |
610
+ |--------|---------|-------------|
611
+ | `on(eventType, listener)` | `this` | Register event listener |
612
+ | `off(eventType, listener)` | `this` | Remove event listener |
613
+ | `once(eventType, listener)` | `this` | Register one-time listener |
614
+ | `removeAllListeners(eventType?)` | `this` | Remove all listeners |
615
+
616
+ ---
617
+
618
+ ## Events
619
+
620
+
621
+ ### Message Events
622
+
623
+ #### message_sent
151
624
 
152
625
  ```typescript
153
- import React, { useEffect, useState } from 'react';
154
- import { OfflineProtocol, MessagePriority } from '@offline-protocol/mesh-sdk';
626
+ interface MessageSentEvent {
627
+ type: 'message_sent';
628
+ message_id: string;
629
+ sender: string;
630
+ recipient: string;
631
+ content: string;
632
+ priority: 'low' | 'medium' | 'high' | 'critical';
633
+ requires_ack: boolean;
634
+ timestamp: number;
635
+ }
636
+ ```
155
637
 
156
- function ChatScreen({ userId, recipientId }) {
157
- const [protocol, setProtocol] = useState(null);
158
- const [messages, setMessages] = useState([]);
638
+ #### message_received
159
639
 
160
- useEffect(() => {
161
- const proto = new OfflineProtocol({
162
- appId: 'chat-app',
163
- userId,
164
- });
640
+ ```typescript
641
+ interface MessageReceivedEvent {
642
+ type: 'message_received';
643
+ message_id: string;
644
+ sender: string;
645
+ recipient: string;
646
+ content: string;
647
+ hop_count: number;
648
+ transport: string;
649
+ timestamp: number;
650
+ }
651
+ ```
165
652
 
166
- proto.on('message_received', (event) => {
167
- if (event.sender === recipientId) {
168
- setMessages((prev) => [...prev, {
169
- id: event.message_id,
170
- text: event.content,
171
- sender: event.sender,
172
- timestamp: event.timestamp,
173
- }]);
174
- }
175
- });
653
+ #### message_delivered
176
654
 
177
- proto.start();
178
- setProtocol(proto);
655
+ ```typescript
656
+ interface MessageDeliveredEvent {
657
+ type: 'message_delivered';
658
+ message_id: string;
659
+ latency_ms: number;
660
+ hop_count: number;
661
+ transport: string;
662
+ }
663
+ ```
179
664
 
180
- return () => proto.destroy();
181
- }, [userId, recipientId]);
665
+ #### message_failed
182
666
 
183
- const sendMessage = async (text) => {
184
- if (protocol) {
185
- await protocol.sendMessage({
186
- recipient: recipientId,
187
- content: text,
188
- priority: MessagePriority.High,
189
- });
190
- }
191
- };
667
+ ```typescript
668
+ interface MessageFailedEvent {
669
+ type: 'message_failed';
670
+ message_id: string;
671
+ reason: string;
672
+ retry_count: number;
673
+ }
674
+ ```
675
+
676
+ ### Network Events
677
+
678
+ #### transport_switched
679
+
680
+ ```typescript
681
+ interface TransportSwitchedEvent {
682
+ type: 'transport_switched';
683
+ from: string | null;
684
+ to: string;
685
+ reason: string;
686
+ }
687
+ ```
688
+
689
+ #### neighbor_discovered
690
+
691
+ ```typescript
692
+ interface NeighborDiscoveredEvent {
693
+ type: 'neighbor_discovered';
694
+ peer_id: string;
695
+ transport: string;
696
+ rssi?: number;
697
+ }
698
+ ```
699
+
700
+ #### neighbor_lost
701
+
702
+ ```typescript
703
+ interface NeighborLostEvent {
704
+ type: 'neighbor_lost';
705
+ peer_id: string;
706
+ }
707
+ ```
708
+
709
+ #### network_metrics
710
+
711
+ ```typescript
712
+ interface NetworkMetricsEvent {
713
+ type: 'network_metrics';
714
+ neighbor_count: number;
715
+ relay_count: number;
716
+ delivery_ratio: number;
717
+ avg_latency_ms: number;
718
+ }
719
+ ```
720
+
721
+
722
+ ### File Events
723
+
724
+ #### file_progress
725
+
726
+ ```typescript
727
+ interface FileProgressEvent {
728
+ type: 'file_progress';
729
+ file_id: string;
730
+ chunks_sent: number;
731
+ total_chunks: number;
732
+ percentage: number;
733
+ }
734
+ ```
735
+
736
+ #### file_received
737
+
738
+ ```typescript
739
+ interface FileReceivedEvent {
740
+ type: 'file_received';
741
+ file_id: string;
742
+ file_name: string;
743
+ file_size: number;
744
+ sender: string;
745
+ }
746
+ ```
192
747
 
193
- return (/* Your UI */);
748
+ ### Diagnostic Events
749
+
750
+ ```typescript
751
+ interface DiagnosticEvent {
752
+ type: 'diagnostic';
753
+ level: 'info' | 'warning' | 'error';
754
+ message: string;
755
+ context?: Record<string, unknown>;
194
756
  }
195
757
  ```
758
+
759
+ ---
760
+
761
+ ## Types
762
+
763
+ ### NetworkTopology
764
+
765
+ ```typescript
766
+ interface NetworkTopology {
767
+ timestamp: number;
768
+ local_user_id: string;
769
+ nodes: NetworkNode[];
770
+ links: NetworkLink[];
771
+ stats: NetworkStats;
772
+ }
773
+
774
+ interface NetworkNode {
775
+ user_id: string;
776
+ role: string; // 'Normal' or 'Relay' from topology API
777
+ connection_count: number;
778
+ battery_level?: number;
779
+ last_seen: number;
780
+ transports: TransportType[];
781
+ }
782
+
783
+ interface NetworkLink {
784
+ from: string;
785
+ to: string;
786
+ quality: number; // 0.0 - 1.0
787
+ transport: TransportType;
788
+ rssi?: number;
789
+ }
790
+
791
+ interface NetworkStats {
792
+ total_nodes: number;
793
+ relay_nodes: number; // Count of nodes with 'Relay' role in topology
794
+ total_connections: number;
795
+ avg_link_quality: number;
796
+ network_diameter?: number;
797
+ }
798
+ ```
799
+
800
+ ### MessageDeliveryStats
801
+
802
+ ```typescript
803
+ interface MessageDeliveryStats {
804
+ message_id: string;
805
+ sender: string;
806
+ recipient: string;
807
+ sent_at: number;
808
+ delivered_at?: number;
809
+ hop_count: number;
810
+ transport?: TransportType;
811
+ retry_count: number;
812
+ latency_ms?: number;
813
+ }
814
+ ```
815
+
816
+ ### RouteEntry
817
+
818
+ ```typescript
819
+ interface RouteEntry {
820
+ nextHop: string;
821
+ hopCount: number;
822
+ quality: number; // 0.0 - 1.0
823
+ lastSeenMs: number;
824
+ }
825
+ ```
826
+
827
+ ### RoutingStats
828
+
829
+ ```typescript
830
+ interface RoutingStats {
831
+ destinationCount: number;
832
+ routeCount: number;
833
+ }
834
+ ```
835
+
836
+ ### DedupStats
837
+
838
+ ```typescript
839
+ interface DedupStats {
840
+ totalTracked: number;
841
+ recentTracked: number;
842
+ capacityUsedPercent: number;
843
+ mode: 'HashMap' | 'BloomFilter';
844
+ }
845
+ ```
846
+
847
+ ### FileProgress
848
+
849
+ ```typescript
850
+ interface FileProgress {
851
+ file_id: string;
852
+ file_name: string;
853
+ file_size: number;
854
+ chunks_completed: number;
855
+ total_chunks: number;
856
+ percentage: number;
857
+ }
858
+ ```
859
+
860
+ ### ProtocolState
861
+
862
+ ```typescript
863
+ enum ProtocolState {
864
+ Stopped = 0,
865
+ Running = 1,
866
+ Paused = 2,
867
+ }
868
+ ```
869
+
870
+ ---
871
+
872
+ ## DORS (Dynamic Offline Relay Switch)
873
+
874
+ DORS automatically selects the optimal transport based on real-time conditions.
875
+
876
+ ### Scoring Factors
877
+
878
+ | Factor | Description |
879
+ |--------|-------------|
880
+ | Signal Strength | RSSI for BLE/WiFi (-50 to -100 dBm) |
881
+ | Proximity | Hop count to destination |
882
+ | Bandwidth | Transport throughput capability |
883
+ | Congestion | Queue depth and backlog |
884
+ | Energy | Battery impact of transport |
885
+ | Reliability | Historical delivery success rate |
886
+ | Load | Current processing capacity |
887
+
888
+ ### Transport Weights
889
+
890
+ **BLE**: Optimized for energy efficiency and mesh scenarios
891
+ - Signal: 30%, Energy: 30%, Congestion: 15%, Proximity: 15%
892
+
893
+ **WiFi Direct**: Optimized for high throughput
894
+ - Bandwidth: 35%, Proximity: 20%, Congestion: 20%, Reliability: 15%
895
+
896
+ **Internet**: Optimized for server connectivity
897
+ - Bandwidth: 35%, Reliability: 30%, Congestion: 15%, Energy: 10%
898
+
899
+ ### Switching Safeguards
900
+
901
+ | Safeguard | Default | Description |
902
+ |-----------|---------|-------------|
903
+ | Hysteresis | 15 points | Minimum score improvement to switch |
904
+ | Cooldown | 20 seconds | Wait time between switches |
905
+ | Stability Window | 8 seconds | Transport must be stable before switching |
906
+
907
+ ---
908
+
909
+ ## Mesh Networking
910
+
911
+ ### Cluster Architecture
912
+
913
+ Devices organize into **clusters** (groups of nearby connected peers). Connections between clusters are handled by **bridge** connections.
914
+
915
+ **Connection Roles:**
916
+ - `MEMBER` - Intra-cluster connection (devices in same neighborhood)
917
+ - `BRIDGE` - Inter-cluster connection (bridges different neighborhoods)
918
+
919
+ ### How It Works
920
+
921
+ 1. **Discovery**: Devices broadcast BLE advertisements with mesh metadata (degree, free slots, battery, uptime)
922
+ 2. **Cluster Detection**: Each device computes a cluster signature from connected peer hashes
923
+ 3. **Connection Decisions**: MeshController evaluates candidates - prioritizes bridging different clusters
924
+ 4. **Rebalancing**: Periodically swaps lower-quality peers for better candidates or bridge opportunities
925
+ 5. **Delivery**: Messages sent to connected peers
926
+
927
+ ### Connection Budget
928
+
929
+ - Default: 4 connections per device
930
+ - Minimum: 1 connection maintained
931
+ - Connections are scored and rebalanced every ~15 seconds
932
+ - Bridge candidates get priority when clusters need unifying
933
+
934
+ ### Peer Scoring
935
+
936
+ | Factor | Weight | Description |
937
+ |--------|--------|-------------|
938
+ | RSSI | 35% | Signal strength to peer |
939
+ | Availability | 20% | Free connection slots |
940
+ | Uptime | 15% | How long peer has been active |
941
+ | Battery | 15% | Peer's battery level |
942
+ | Stability | 10% | Connection reliability history |
943
+ | Load | 5% | Current processing load |
944
+
945
+ **Bridge Favor**: Candidates from different clusters get a score bonus (`bridgeFavor: 0.1`) to encourage network unification.
946
+
947
+ ### Message TTL
948
+
949
+ - Default: 8 hops
950
+ - Messages are dropped when TTL reaches 0
951
+ - Prevents infinite message circulation
952
+
953
+ ---
954
+
955
+ ## Reliability Layer
956
+
957
+ ### Acknowledgments
958
+
959
+ - Messages require ACK for delivery confirmation
960
+ - Default timeout: 5 seconds
961
+ - `message_delivered` event fires on ACK receipt
962
+
963
+ ### Retry Queue
964
+
965
+ - Failed messages are retried with exponential backoff
966
+ - Initial delay: 1 second
967
+ - Maximum delay: 30 seconds
968
+ - Maximum retries: 5
969
+
970
+ ### Deduplication
971
+
972
+ Prevents duplicate message processing:
973
+
974
+ - **Bloom Filter Mode**: Space-efficient, ~1% false positive rate
975
+ - **HashMap Mode**: Exact tracking, configurable capacity
976
+
977
+ ---
978
+
979
+ ## File Transfer
980
+
981
+ ### Sending Files
982
+
983
+ ```typescript
984
+ const fileId = await protocol.sendFile({
985
+ filePath: '/path/to/file.pdf',
986
+ recipient: 'user456',
987
+ fileName: 'document.pdf', // optional
988
+ });
989
+
990
+ protocol.on('file_progress', (event) => {
991
+ console.log(`${event.percentage}% complete`);
992
+ });
993
+ ```
994
+
995
+ ### Managing Transfers
996
+
997
+ ```typescript
998
+ const progress = await protocol.getFileProgress(fileId);
999
+ await protocol.cancelFileTransfer(fileId);
1000
+ ```
1001
+
1002
+ ---
1003
+
1004
+ ## Troubleshooting
1005
+
1006
+ ### Messages Not Delivering
1007
+
1008
+ 1. Verify both devices have protocol started
1009
+ 2. Check they're within BLE range (~10-30m)
1010
+ 3. Ensure TTL is sufficient for network size
1011
+ 4. Monitor `message_failed` events for retry information
1012
+
1013
+ ### No Peers Discovered
1014
+
1015
+ 1. Verify Bluetooth is enabled: `await protocol.isBluetoothEnabled()`
1016
+ 2. Check permissions are granted
1017
+ 3. Ensure background modes enabled (iOS)
1018
+ 4. Verify devices are within range
1019
+
1020
+ ### Frequent Disconnections
1021
+
1022
+ 1. Check signal strength via `neighbor_discovered` RSSI
1023
+ 2. Increase `stabilityWindowSecs` in DORS config
1024
+ 3. Reduce `rebalanceInterval` frequency
1025
+ 4. Check for BLE interference
1026
+
1027
+ ### High Battery Drain
1028
+
1029
+ 1. Reduce connection count (native mesh config)
1030
+ 2. Verify DORS is selecting BLE over WiFi Direct
1031
+ 3. Check for excessive retry activity
1032
+ 4. Use `setBatteryLevel()` to inform mesh decisions
1033
+
1034
+ ### Transport Not Switching
1035
+
1036
+ 1. Verify transport is enabled in config
1037
+ 2. Check hysteresis threshold isn't too high
1038
+ 3. Ensure cooldown period has elapsed
1039
+ 4. Use `forceTransport()` to test manually
1040
+
1041
+ ### Linking Error
1042
+
1043
+ If you see the linking error message:
1044
+ 1. Run `pod install` (iOS)
1045
+ 2. Rebuild the app after installing
1046
+ 3. Verify not using Expo Go (native modules required)
1047
+
1048
+ ---
1049
+
1050
+ ## License
1051
+
1052
+ ISC