react-native-ble-mesh 1.1.0 → 1.1.1
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 +328 -561
- package/docs/prompt-instructions.md +528 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,68 +1,113 @@
|
|
|
1
1
|
# react-native-ble-mesh
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
## Send messages without the internet. Like magic! ✨
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[](https://www.npmjs.com/package/react-native-ble-mesh)
|
|
6
|
+
[](https://www.npmjs.com/package/react-native-ble-mesh)
|
|
7
|
+
[](https://opensource.org/licenses/MIT)
|
|
8
|
+
[](https://www.typescriptlang.org/)
|
|
9
|
+
[](https://reactnative.dev/)
|
|
6
10
|
|
|
7
|
-
|
|
8
|
-
[](https://opensource.org/licenses/MIT)
|
|
11
|
+
---
|
|
9
12
|
|
|
10
|
-
##
|
|
13
|
+
## What is this?
|
|
11
14
|
|
|
12
|
-
|
|
13
|
-
- **Digital Signatures** - Ed25519 signatures for message authentication and identity verification
|
|
14
|
-
- **Multi-Hop Routing** - Messages can traverse multiple peers to reach their destination
|
|
15
|
-
- **Offline-First** - Works without internet connectivity using BLE
|
|
16
|
-
- **Forward Secrecy** - Automatic session rekeying for enhanced security
|
|
17
|
-
- **Replay Protection** - Sliding window algorithm prevents replay attacks
|
|
18
|
-
- **Traffic Analysis Resistance** - PKCS#7 padding to fixed block sizes
|
|
19
|
-
- **LZ4 Compression** - Efficient bandwidth usage for larger messages
|
|
20
|
-
- **Power Management** - Multiple power modes for battery optimization
|
|
21
|
-
- **Trust Management** - Peer verification, favorites, and blocking
|
|
22
|
-
- **Message Reliability** - Automatic retry with exponential backoff
|
|
15
|
+
Imagine you're at a concert, camping trip, or during a power outage - **no WiFi, no cell service**. How do you text your friends?
|
|
23
16
|
|
|
24
|
-
|
|
17
|
+
**This library lets phones talk to each other using Bluetooth!** Messages hop from phone to phone until they reach your friend - even if they're far away.
|
|
25
18
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
19
|
+
```
|
|
20
|
+
You Friend's Your
|
|
21
|
+
Phone ----Bluetooth---> Friend's ----Bluetooth---> Friend
|
|
22
|
+
Phone (300m away!)
|
|
23
|
+
```
|
|
31
24
|
|
|
32
|
-
|
|
25
|
+
**Think of it like a game of telephone, but for text messages!**
|
|
33
26
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## See It In Action
|
|
30
|
+
|
|
31
|
+
```javascript
|
|
32
|
+
import { MeshNetwork } from 'react-native-ble-mesh';
|
|
33
|
+
|
|
34
|
+
// 1. Create your mesh network
|
|
35
|
+
const mesh = new MeshNetwork({ nickname: 'Alex' });
|
|
36
|
+
|
|
37
|
+
// 2. Start it up!
|
|
38
|
+
await mesh.start();
|
|
39
|
+
|
|
40
|
+
// 3. Send a message to everyone nearby
|
|
41
|
+
await mesh.broadcast('Hello everyone! 👋');
|
|
42
|
+
|
|
43
|
+
// 4. Listen for messages
|
|
44
|
+
mesh.on('messageReceived', (msg) => {
|
|
45
|
+
console.log(`${msg.from} says: ${msg.text}`);
|
|
46
|
+
});
|
|
37
47
|
```
|
|
38
48
|
|
|
39
|
-
|
|
49
|
+
**That's it! Four lines of code and you're chatting without internet!**
|
|
40
50
|
|
|
41
|
-
|
|
51
|
+
---
|
|
42
52
|
|
|
43
|
-
|
|
44
|
-
// index.js or App.js - This MUST be the first import!
|
|
45
|
-
import 'react-native-get-random-values';
|
|
53
|
+
## Why Use This?
|
|
46
54
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
55
|
+
| Problem | Our Solution |
|
|
56
|
+
|---------|--------------|
|
|
57
|
+
| No WiFi or cell service | Works with just Bluetooth! |
|
|
58
|
+
| Friend is too far away | Messages hop through other phones |
|
|
59
|
+
| Worried about privacy? | All messages are encrypted |
|
|
60
|
+
| Phone battery dying? | Smart power saving built-in |
|
|
61
|
+
| Need to delete everything fast? | One-tap emergency wipe |
|
|
62
|
+
|
|
63
|
+
---
|
|
51
64
|
|
|
52
|
-
|
|
65
|
+
## Cool Features
|
|
66
|
+
|
|
67
|
+
### 📡 Messages That Hop
|
|
68
|
+
Your message can jump through **up to 7 phones** to reach someone far away. If Alice can't reach Dave directly, the message goes: Alice → Bob → Carol → Dave!
|
|
69
|
+
|
|
70
|
+
### 🔒 Secret Messages
|
|
71
|
+
Private messages are scrambled (encrypted) so only your friend can read them. Even if someone else's phone passes along the message, they can't peek!
|
|
72
|
+
|
|
73
|
+
### 📬 Offline Delivery
|
|
74
|
+
Friend's phone turned off? No problem! Your message waits and delivers when they come back online.
|
|
75
|
+
|
|
76
|
+
### 🔋 Battery Friendly
|
|
77
|
+
Choose how much battery to use:
|
|
78
|
+
- **High Power** = Faster messages, more battery
|
|
79
|
+
- **Balanced** = Good speed, normal battery (default)
|
|
80
|
+
- **Low Power** = Slower messages, saves battery
|
|
81
|
+
|
|
82
|
+
### 🚨 Panic Button
|
|
83
|
+
Triple-tap to instantly delete all messages and data. Everything gone in less than 0.2 seconds!
|
|
84
|
+
|
|
85
|
+
### 💬 Group Channels
|
|
86
|
+
Create chat rooms like `#camping-trip` or `#concert-squad`. Only people who join can see the messages!
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Installation
|
|
91
|
+
|
|
92
|
+
### Step 1: Install the package
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
npm install react-native-ble-mesh react-native-ble-plx react-native-get-random-values
|
|
53
96
|
```
|
|
54
97
|
|
|
55
|
-
|
|
98
|
+
### Step 2: iOS Setup
|
|
56
99
|
|
|
57
|
-
|
|
100
|
+
```bash
|
|
101
|
+
cd ios && pod install && cd ..
|
|
102
|
+
```
|
|
58
103
|
|
|
59
|
-
Add
|
|
104
|
+
Add these lines to your `ios/YourApp/Info.plist`:
|
|
60
105
|
|
|
61
106
|
```xml
|
|
62
107
|
<key>NSBluetoothAlwaysUsageDescription</key>
|
|
63
|
-
<string>
|
|
108
|
+
<string>Chat with nearby friends using Bluetooth</string>
|
|
64
109
|
<key>NSBluetoothPeripheralUsageDescription</key>
|
|
65
|
-
<string>
|
|
110
|
+
<string>Chat with nearby friends using Bluetooth</string>
|
|
66
111
|
<key>UIBackgroundModes</key>
|
|
67
112
|
<array>
|
|
68
113
|
<string>bluetooth-central</string>
|
|
@@ -70,9 +115,9 @@ Add Bluetooth permissions to your `Info.plist`:
|
|
|
70
115
|
</array>
|
|
71
116
|
```
|
|
72
117
|
|
|
73
|
-
### Android Setup
|
|
118
|
+
### Step 3: Android Setup
|
|
74
119
|
|
|
75
|
-
Add
|
|
120
|
+
Add these lines to `android/app/src/main/AndroidManifest.xml`:
|
|
76
121
|
|
|
77
122
|
```xml
|
|
78
123
|
<uses-permission android:name="android.permission.BLUETOOTH" />
|
|
@@ -83,655 +128,377 @@ Add Bluetooth permissions to your `AndroidManifest.xml`:
|
|
|
83
128
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
|
84
129
|
```
|
|
85
130
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
### Basic Setup
|
|
131
|
+
### Step 4: Add this to the TOP of your app
|
|
89
132
|
|
|
90
133
|
```javascript
|
|
91
|
-
|
|
134
|
+
// This MUST be the very first line in index.js or App.js
|
|
135
|
+
import 'react-native-get-random-values';
|
|
92
136
|
|
|
93
|
-
//
|
|
94
|
-
|
|
137
|
+
// Now add your other imports
|
|
138
|
+
import { AppRegistry } from 'react-native';
|
|
139
|
+
// ...
|
|
140
|
+
```
|
|
95
141
|
|
|
96
|
-
|
|
97
|
-
// Initialize with optional configuration
|
|
98
|
-
await mesh.initialize({
|
|
99
|
-
displayName: 'Alice',
|
|
100
|
-
storage: null, // Use in-memory storage
|
|
101
|
-
});
|
|
142
|
+
---
|
|
102
143
|
|
|
103
|
-
|
|
104
|
-
const transport = new BLETransport();
|
|
144
|
+
## Quick Start Examples
|
|
105
145
|
|
|
106
|
-
|
|
107
|
-
await mesh.start(transport);
|
|
146
|
+
### Example 1: Simple Chat
|
|
108
147
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
148
|
+
```javascript
|
|
149
|
+
import { MeshNetwork } from 'react-native-ble-mesh';
|
|
112
150
|
|
|
113
|
-
start
|
|
114
|
-
|
|
151
|
+
// Create and start
|
|
152
|
+
const mesh = new MeshNetwork({ nickname: 'YourName' });
|
|
153
|
+
await mesh.start();
|
|
115
154
|
|
|
116
|
-
|
|
155
|
+
// Send message to everyone
|
|
156
|
+
await mesh.broadcast('Hi everyone!');
|
|
117
157
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
mesh.on('peer-discovered', (peer) => {
|
|
121
|
-
console.log('Discovered peer:', peer.id);
|
|
122
|
-
console.log('Display name:', peer.getDisplayName());
|
|
123
|
-
});
|
|
158
|
+
// Send private message to one person
|
|
159
|
+
await mesh.sendDirect('friend-id-here', 'Hey, just for you!');
|
|
124
160
|
|
|
125
|
-
|
|
126
|
-
|
|
161
|
+
// Receive messages
|
|
162
|
+
mesh.on('messageReceived', ({ from, text }) => {
|
|
163
|
+
console.log(`${from}: ${text}`);
|
|
127
164
|
});
|
|
128
165
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
});
|
|
166
|
+
// When done
|
|
167
|
+
await mesh.stop();
|
|
132
168
|
```
|
|
133
169
|
|
|
134
|
-
###
|
|
170
|
+
### Example 2: Group Channels
|
|
135
171
|
|
|
136
172
|
```javascript
|
|
137
|
-
//
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
// Send
|
|
141
|
-
|
|
142
|
-
try {
|
|
143
|
-
const messageId = await mesh.sendPrivateMessage(peerId, 'Hello, this is private!');
|
|
144
|
-
console.log('Message sent:', messageId);
|
|
145
|
-
} catch (error) {
|
|
146
|
-
console.error('Failed to send:', error.message);
|
|
147
|
-
}
|
|
173
|
+
// Join a channel (like a chat room)
|
|
174
|
+
await mesh.joinChannel('#road-trip');
|
|
175
|
+
|
|
176
|
+
// Send message to everyone in the channel
|
|
177
|
+
await mesh.sendToChannel('#road-trip', 'Are we there yet?');
|
|
148
178
|
|
|
149
|
-
//
|
|
150
|
-
|
|
179
|
+
// Leave when done
|
|
180
|
+
await mesh.leaveChannel('#road-trip');
|
|
151
181
|
```
|
|
152
182
|
|
|
153
|
-
###
|
|
183
|
+
### Example 3: Save Battery
|
|
154
184
|
|
|
155
185
|
```javascript
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
console.log('From:', message.senderId);
|
|
160
|
-
console.log('Type:', message.type);
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
// Listen for private messages specifically
|
|
164
|
-
mesh.on('private-message', (message) => {
|
|
165
|
-
console.log('Private message from', message.senderId);
|
|
166
|
-
console.log('Content:', message.content);
|
|
186
|
+
const mesh = new MeshNetwork({
|
|
187
|
+
nickname: 'PowerSaver',
|
|
188
|
+
batteryMode: 'low', // Uses less battery
|
|
167
189
|
});
|
|
168
190
|
|
|
169
|
-
//
|
|
170
|
-
mesh
|
|
171
|
-
|
|
172
|
-
|
|
191
|
+
// Or let it decide automatically based on battery level
|
|
192
|
+
const mesh = new MeshNetwork({
|
|
193
|
+
nickname: 'Smart',
|
|
194
|
+
batteryMode: 'auto', // Adjusts automatically!
|
|
173
195
|
});
|
|
174
196
|
```
|
|
175
197
|
|
|
176
|
-
###
|
|
198
|
+
### Example 4: Emergency Delete
|
|
177
199
|
|
|
178
200
|
```javascript
|
|
179
|
-
//
|
|
180
|
-
mesh.
|
|
181
|
-
|
|
201
|
+
// Enable panic mode
|
|
202
|
+
mesh.enablePanicMode({
|
|
203
|
+
trigger: 'triple_tap', // Triple tap to wipe
|
|
182
204
|
});
|
|
183
205
|
|
|
184
|
-
//
|
|
185
|
-
mesh.
|
|
186
|
-
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
// Send read receipts for messages you've read
|
|
190
|
-
mesh.sendReadReceipt(['msg-id-1', 'msg-id-2']);
|
|
191
|
-
|
|
192
|
-
// Mark a single message as read
|
|
193
|
-
mesh.markMessageRead('msg-id-1');
|
|
206
|
+
// Or wipe everything immediately
|
|
207
|
+
await mesh.wipeAllData();
|
|
208
|
+
// All messages, keys, and data = GONE! 💨
|
|
194
209
|
```
|
|
195
210
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
### MeshService
|
|
199
|
-
|
|
200
|
-
The main orchestrator for the mesh network.
|
|
201
|
-
|
|
202
|
-
#### Lifecycle Methods
|
|
211
|
+
### Example 5: Check Network Health
|
|
203
212
|
|
|
204
213
|
```javascript
|
|
205
|
-
|
|
206
|
-
await mesh.initialize(options);
|
|
207
|
-
|
|
208
|
-
// Start with a transport
|
|
209
|
-
await mesh.start(transport);
|
|
210
|
-
|
|
211
|
-
// Stop the service
|
|
212
|
-
await mesh.stop();
|
|
214
|
+
const health = mesh.getNetworkHealth();
|
|
213
215
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
+
console.log(`Connected to ${health.activeNodes} people`);
|
|
217
|
+
console.log(`Network status: ${health.overallHealth}`); // 'good', 'fair', or 'poor'
|
|
216
218
|
```
|
|
217
219
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
```javascript
|
|
221
|
-
// Get your identity information
|
|
222
|
-
const identity = mesh.getIdentity();
|
|
223
|
-
// Returns: { publicKey, signingPublicKey, displayName, fingerprint }
|
|
224
|
-
|
|
225
|
-
// Set your display name
|
|
226
|
-
mesh.setDisplayName('Alice');
|
|
227
|
-
|
|
228
|
-
// Get your fingerprint for out-of-band verification
|
|
229
|
-
const fingerprint = mesh.getFingerprint();
|
|
220
|
+
---
|
|
230
221
|
|
|
231
|
-
|
|
232
|
-
const exported = mesh.exportIdentity();
|
|
222
|
+
## Using React Hooks
|
|
233
223
|
|
|
234
|
-
|
|
235
|
-
mesh.importIdentity(exported);
|
|
236
|
-
|
|
237
|
-
// Announce identity to network
|
|
238
|
-
mesh.announceIdentity();
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
#### Peer Management
|
|
224
|
+
If you're using React, we have easy hooks!
|
|
242
225
|
|
|
243
226
|
```javascript
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
const peer = mesh.getPeer(peerId);
|
|
227
|
+
import React, { useEffect } from 'react';
|
|
228
|
+
import { View, Text, Button } from 'react-native';
|
|
229
|
+
import { useMesh, useMessages, usePeers } from 'react-native-ble-mesh/hooks';
|
|
230
|
+
import { BLETransport } from 'react-native-ble-mesh';
|
|
249
231
|
|
|
250
|
-
|
|
251
|
-
|
|
232
|
+
function ChatScreen() {
|
|
233
|
+
// Manage mesh lifecycle
|
|
234
|
+
const { mesh, state, initialize, destroy } = useMesh({ displayName: 'Alex' });
|
|
252
235
|
|
|
253
|
-
//
|
|
254
|
-
mesh
|
|
236
|
+
// Message handling (pass mesh instance)
|
|
237
|
+
const { messages, sendBroadcast } = useMessages(mesh);
|
|
255
238
|
|
|
256
|
-
//
|
|
257
|
-
mesh
|
|
258
|
-
```
|
|
259
|
-
|
|
260
|
-
#### Trust Management
|
|
261
|
-
|
|
262
|
-
```javascript
|
|
263
|
-
// Verify a peer using their fingerprint
|
|
264
|
-
mesh.verifyPeer(peerId, fingerprint);
|
|
239
|
+
// Peer tracking (pass mesh instance)
|
|
240
|
+
const { peers, connectedCount } = usePeers(mesh);
|
|
265
241
|
|
|
266
|
-
//
|
|
267
|
-
|
|
242
|
+
// Start mesh on mount
|
|
243
|
+
useEffect(() => {
|
|
244
|
+
const transport = new BLETransport();
|
|
245
|
+
initialize(transport);
|
|
246
|
+
return () => destroy();
|
|
247
|
+
}, []);
|
|
268
248
|
|
|
269
|
-
|
|
270
|
-
mesh.addFavorite(peerId);
|
|
249
|
+
if (state !== 'active') return <Text>Starting mesh...</Text>;
|
|
271
250
|
|
|
272
|
-
|
|
273
|
-
|
|
251
|
+
return (
|
|
252
|
+
<View>
|
|
253
|
+
<Text>Connected to {connectedCount} people</Text>
|
|
274
254
|
|
|
275
|
-
|
|
276
|
-
|
|
255
|
+
{messages.map(msg => (
|
|
256
|
+
<Text key={msg.id}>{msg.senderId}: {msg.content}</Text>
|
|
257
|
+
))}
|
|
277
258
|
|
|
278
|
-
|
|
279
|
-
|
|
259
|
+
<Button title="Say Hi!" onPress={() => sendBroadcast('Hello!')} />
|
|
260
|
+
</View>
|
|
261
|
+
);
|
|
262
|
+
}
|
|
280
263
|
```
|
|
281
264
|
|
|
282
|
-
|
|
265
|
+
> **Note:** The hooks (`useMesh`, `useMessages`, `usePeers`) work with the lower-level `MeshService`. For simpler usage, use the `MeshNetwork` class directly as shown in the Quick Start examples above.
|
|
283
266
|
|
|
284
|
-
|
|
285
|
-
// Available power modes: PERFORMANCE, BALANCED, POWER_SAVER, ULTRA_POWER_SAVER
|
|
286
|
-
mesh.setPowerMode('BALANCED');
|
|
267
|
+
---
|
|
287
268
|
|
|
288
|
-
|
|
289
|
-
const mode = mesh.getPowerMode();
|
|
269
|
+
## All The Things You Can Do
|
|
290
270
|
|
|
291
|
-
|
|
292
|
-
mesh.setAutoPowerAdjust(true);
|
|
293
|
-
```
|
|
271
|
+
### Starting & Stopping
|
|
294
272
|
|
|
295
|
-
|
|
273
|
+
| Method | What It Does |
|
|
274
|
+
|--------|--------------|
|
|
275
|
+
| `mesh.start()` | Turn on the mesh network |
|
|
276
|
+
| `mesh.stop()` | Turn it off (can restart later) |
|
|
277
|
+
| `mesh.destroy()` | Completely shut down (can't restart) |
|
|
296
278
|
|
|
297
|
-
|
|
298
|
-
// Get service status
|
|
299
|
-
const status = mesh.getStatus();
|
|
279
|
+
### Sending Messages
|
|
300
280
|
|
|
301
|
-
|
|
302
|
-
|
|
281
|
+
| Method | What It Does |
|
|
282
|
+
|--------|--------------|
|
|
283
|
+
| `mesh.broadcast('Hi!')` | Send to everyone nearby |
|
|
284
|
+
| `mesh.sendDirect(id, 'Hey')` | Send private message to one person |
|
|
285
|
+
| `mesh.sendToChannel('#fun', 'Yo')` | Send to a group channel |
|
|
303
286
|
|
|
304
|
-
|
|
305
|
-
const stats = mesh.getStats();
|
|
306
|
-
```
|
|
287
|
+
### Channels (Group Chats)
|
|
307
288
|
|
|
308
|
-
|
|
289
|
+
| Method | What It Does |
|
|
290
|
+
|--------|--------------|
|
|
291
|
+
| `mesh.joinChannel('#name')` | Join a channel |
|
|
292
|
+
| `mesh.leaveChannel('#name')` | Leave a channel |
|
|
293
|
+
| `mesh.getChannels()` | See what channels you're in |
|
|
309
294
|
|
|
310
|
-
|
|
311
|
-
|-------|-------------|---------|
|
|
312
|
-
| `peer-discovered` | New peer found | `{ peer }` |
|
|
313
|
-
| `peer-connected` | Connected to peer | `{ peer }` |
|
|
314
|
-
| `peer-disconnected` | Disconnected from peer | `{ peer }` |
|
|
315
|
-
| `peer-secured` | Secure session established | `{ peer }` |
|
|
316
|
-
| `message` | Any message received | `{ message }` |
|
|
317
|
-
| `private-message` | Private message received | `{ message }` |
|
|
318
|
-
| `channel-message` | Channel message received | `{ message }` |
|
|
319
|
-
| `message-delivered` | Message delivery confirmed | `{ messageId, peerId, timestamp }` |
|
|
320
|
-
| `message-failed` | Message delivery failed | `{ messageId, peerId, error }` |
|
|
321
|
-
| `read-receipt` | Read receipt received | `{ messageIds, fromPeerId, timestamp }` |
|
|
322
|
-
| `identity-announced` | Peer announced identity | `{ peerId, fingerprint, nickname }` |
|
|
323
|
-
| `peer-verified` | Peer verification changed | `{ peerId, fingerprint }` |
|
|
324
|
-
| `peer-compromised` | Fingerprint mismatch detected | `{ peerId, expected, actual }` |
|
|
295
|
+
### People Management
|
|
325
296
|
|
|
326
|
-
|
|
297
|
+
| Method | What It Does |
|
|
298
|
+
|--------|--------------|
|
|
299
|
+
| `mesh.getPeers()` | See everyone nearby |
|
|
300
|
+
| `mesh.blockPeer(id)` | Block someone |
|
|
301
|
+
| `mesh.unblockPeer(id)` | Unblock someone |
|
|
327
302
|
|
|
328
|
-
###
|
|
303
|
+
### Your Identity
|
|
329
304
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
305
|
+
| Method | What It Does |
|
|
306
|
+
|--------|--------------|
|
|
307
|
+
| `mesh.getIdentity()` | Get your info |
|
|
308
|
+
| `mesh.setNickname('New Name')` | Change your display name |
|
|
334
309
|
|
|
335
|
-
|
|
336
|
-
storage: null,
|
|
310
|
+
### Safety Features
|
|
337
311
|
|
|
338
|
-
|
|
339
|
-
|
|
312
|
+
| Method | What It Does |
|
|
313
|
+
|--------|--------------|
|
|
314
|
+
| `mesh.enablePanicMode()` | Enable emergency wipe |
|
|
315
|
+
| `mesh.wipeAllData()` | Delete everything NOW |
|
|
340
316
|
|
|
341
|
-
|
|
342
|
-
compressionEnabled: true,
|
|
317
|
+
### Network Info
|
|
343
318
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
319
|
+
| Method | What It Does |
|
|
320
|
+
|--------|--------------|
|
|
321
|
+
| `mesh.getStatus()` | Get current status |
|
|
322
|
+
| `mesh.getNetworkHealth()` | Check how good the network is |
|
|
323
|
+
| `mesh.getBatteryMode()` | See current battery mode |
|
|
324
|
+
| `mesh.setBatteryMode('low')` | Change battery mode |
|
|
348
325
|
|
|
349
|
-
|
|
326
|
+
---
|
|
350
327
|
|
|
351
|
-
|
|
352
|
-
|------|---------------|--------|----------|
|
|
353
|
-
| `PERFORMANCE` | 1s | 800ms | Active foreground use |
|
|
354
|
-
| `BALANCED` | 5s | 2s | Default mode |
|
|
355
|
-
| `POWER_SAVER` | 30s | 5s | Background operation |
|
|
356
|
-
| `ULTRA_POWER_SAVER` | 60s | 5s | Minimal battery drain |
|
|
328
|
+
## Events (When Things Happen)
|
|
357
329
|
|
|
358
|
-
|
|
330
|
+
Listen for these events:
|
|
359
331
|
|
|
360
332
|
```javascript
|
|
361
|
-
//
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
requiresAck: true,
|
|
365
|
-
|
|
366
|
-
// Enable compression
|
|
367
|
-
compress: true,
|
|
368
|
-
|
|
369
|
-
// Maximum hop count (1-15)
|
|
370
|
-
maxHops: 7,
|
|
371
|
-
|
|
372
|
-
// Priority level
|
|
373
|
-
priority: 'high', // 'normal' | 'high'
|
|
374
|
-
});
|
|
375
|
-
```
|
|
333
|
+
// Network started/stopped
|
|
334
|
+
mesh.on('started', () => { });
|
|
335
|
+
mesh.on('stopped', () => { });
|
|
376
336
|
|
|
377
|
-
|
|
337
|
+
// Someone sent a message (any type)
|
|
338
|
+
mesh.on('messageReceived', ({ from, text, timestamp, type }) => { });
|
|
378
339
|
|
|
379
|
-
|
|
340
|
+
// Private message received
|
|
341
|
+
mesh.on('directMessage', ({ from, text, timestamp }) => { });
|
|
380
342
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
- **Encryption**: ChaCha20-Poly1305 AEAD
|
|
384
|
-
- **Hashing**: SHA-256, SHA-512
|
|
385
|
-
- **Key Derivation**: HKDF
|
|
343
|
+
// Channel message received
|
|
344
|
+
mesh.on('channelMessage', ({ channel, from, text, timestamp }) => { });
|
|
386
345
|
|
|
387
|
-
|
|
346
|
+
// Message was delivered successfully
|
|
347
|
+
mesh.on('messageDelivered', ({ messageId, peerId }) => { });
|
|
388
348
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
```
|
|
392
|
-
-> e
|
|
393
|
-
<- e, ee, s, es
|
|
394
|
-
-> s, se
|
|
395
|
-
```
|
|
349
|
+
// Found a new person nearby
|
|
350
|
+
mesh.on('peerDiscovered', (peer) => { });
|
|
396
351
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
- Forward secrecy
|
|
400
|
-
- Identity hiding
|
|
352
|
+
// Connected to someone
|
|
353
|
+
mesh.on('peerConnected', (peer) => { });
|
|
401
354
|
|
|
402
|
-
|
|
355
|
+
// Someone left
|
|
356
|
+
mesh.on('peerDisconnected', (peer) => { });
|
|
403
357
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
358
|
+
// Channel events
|
|
359
|
+
mesh.on('channelJoined', ({ channel }) => { });
|
|
360
|
+
mesh.on('channelLeft', ({ channel }) => { });
|
|
407
361
|
|
|
408
|
-
|
|
362
|
+
// Network quality changed
|
|
363
|
+
mesh.on('networkHealthChanged', (healthInfo) => { });
|
|
409
364
|
|
|
410
|
-
|
|
365
|
+
// Message cached for offline peer
|
|
366
|
+
mesh.on('messageCached', ({ peerId, text }) => { });
|
|
411
367
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
const myFingerprint = mesh.getFingerprint();
|
|
415
|
-
// Format: "A1B2 C3D4 E5F6 G7H8 I9J0 K1L2 M3N4 O5P6"
|
|
368
|
+
// Cached messages delivered when peer came online
|
|
369
|
+
mesh.on('cachedMessagesDelivered', ({ peerId, delivered }) => { });
|
|
416
370
|
|
|
417
|
-
//
|
|
418
|
-
mesh.
|
|
371
|
+
// Data was wiped (panic mode)
|
|
372
|
+
mesh.on('dataWiped', (result) => { });
|
|
419
373
|
|
|
420
|
-
//
|
|
421
|
-
|
|
422
|
-
console.log('Verified:', peer.verificationStatus === 'verified');
|
|
374
|
+
// Something went wrong
|
|
375
|
+
mesh.on('error', (error) => { });
|
|
423
376
|
```
|
|
424
377
|
|
|
425
|
-
|
|
378
|
+
---
|
|
426
379
|
|
|
427
|
-
|
|
380
|
+
## How Secure Is It?
|
|
428
381
|
|
|
429
|
-
|
|
382
|
+
**Very secure!** Here's what protects your messages:
|
|
430
383
|
|
|
431
|
-
|
|
|
432
|
-
|
|
433
|
-
|
|
|
434
|
-
|
|
|
435
|
-
|
|
|
436
|
-
|
|
|
437
|
-
| timestamp | 8 | Unix timestamp (ms) |
|
|
438
|
-
| payloadLength | 2 | Payload size |
|
|
439
|
-
| fragmentIndex | 1 | Fragment index |
|
|
440
|
-
| fragmentTotal | 1 | Total fragments |
|
|
441
|
-
| senderId | 8 | Truncated peer ID |
|
|
442
|
-
| recipientId | 8 | (Optional) Recipient ID |
|
|
384
|
+
| Feature | What It Means |
|
|
385
|
+
|---------|---------------|
|
|
386
|
+
| **Noise Protocol** | Military-grade handshake to verify who you're talking to |
|
|
387
|
+
| **ChaCha20 Encryption** | Your messages are scrambled so only the recipient can read them |
|
|
388
|
+
| **Forward Secrecy** | Even if someone steals your keys later, old messages stay secret |
|
|
389
|
+
| **No Permanent IDs** | You don't have a permanent identity that can be tracked |
|
|
443
390
|
|
|
444
|
-
|
|
391
|
+
---
|
|
445
392
|
|
|
446
|
-
|
|
447
|
-
|----------|-------|
|
|
448
|
-
| Data | TEXT, TEXT_ACK, BINARY, BINARY_ACK |
|
|
449
|
-
| Handshake | HANDSHAKE_INIT, HANDSHAKE_RESPONSE, HANDSHAKE_FINAL |
|
|
450
|
-
| Discovery | PEER_ANNOUNCE, PEER_REQUEST, PEER_RESPONSE, IDENTITY_ANNOUNCE |
|
|
451
|
-
| Channels | CHANNEL_JOIN, CHANNEL_LEAVE, CHANNEL_MESSAGE |
|
|
452
|
-
| Private | PRIVATE_MESSAGE, PRIVATE_ACK, READ_RECEIPT |
|
|
453
|
-
| Control | HEARTBEAT, PING, PONG |
|
|
454
|
-
| Fragments | FRAGMENT_START, FRAGMENT_CONTINUE, FRAGMENT_END |
|
|
393
|
+
## Frequently Asked Questions
|
|
455
394
|
|
|
456
|
-
|
|
395
|
+
### How far can messages travel?
|
|
396
|
+
With one hop: about 30 meters (100 feet). With 7 hops through other phones: up to 300+ meters!
|
|
457
397
|
|
|
458
|
-
|
|
398
|
+
### Does it work if Bluetooth is off?
|
|
399
|
+
No, Bluetooth must be on. But you don't need WiFi or cell service!
|
|
459
400
|
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
MeshError,
|
|
463
|
-
CryptoError,
|
|
464
|
-
ConnectionError,
|
|
465
|
-
HandshakeError,
|
|
466
|
-
MessageError,
|
|
467
|
-
FragmentError,
|
|
468
|
-
TrustError,
|
|
469
|
-
RetryError
|
|
470
|
-
} = require('react-native-ble-mesh/errors');
|
|
471
|
-
|
|
472
|
-
mesh.on('error', (error) => {
|
|
473
|
-
if (error instanceof CryptoError) {
|
|
474
|
-
console.error('Crypto error:', error.code, error.message);
|
|
475
|
-
} else if (error instanceof ConnectionError) {
|
|
476
|
-
console.error('Connection error:', error.code, error.message);
|
|
477
|
-
} else if (error instanceof TrustError) {
|
|
478
|
-
console.error('Trust error:', error.code, error.message);
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
// All errors have these properties
|
|
482
|
-
console.log('Error code:', error.code);
|
|
483
|
-
console.log('Category:', error.category);
|
|
484
|
-
console.log('Recoverable:', error.recoverable);
|
|
485
|
-
console.log('Details:', error.details);
|
|
486
|
-
});
|
|
487
|
-
```
|
|
401
|
+
### Can someone read my private messages?
|
|
402
|
+
No! Private messages are encrypted. Only you and your friend have the keys.
|
|
488
403
|
|
|
489
|
-
###
|
|
404
|
+
### How many people can be in the network?
|
|
405
|
+
The library supports 50+ connected peers at once.
|
|
490
406
|
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
| Crypto | E_CRYPTO_001 - E_CRYPTO_006 |
|
|
494
|
-
| Connection | E_CONN_001 - E_CONN_003 |
|
|
495
|
-
| Handshake | E_HAND_001 - E_HAND_003 |
|
|
496
|
-
| Message | E_MSG_001 - E_MSG_004 |
|
|
497
|
-
| Fragment | E_FRAG_001 - E_FRAG_004 |
|
|
498
|
-
| Trust | E_TRUST_001 - E_TRUST_004 |
|
|
499
|
-
| Retry | E_RETRY_001 - E_RETRY_003 |
|
|
407
|
+
### Does it drain my battery?
|
|
408
|
+
It uses some battery (Bluetooth is on), but you can use "low power" mode to minimize drain.
|
|
500
409
|
|
|
501
|
-
|
|
410
|
+
### Does it work in the background?
|
|
411
|
+
On iOS, it works with some limitations. On Android, it works fully in the background.
|
|
502
412
|
|
|
503
|
-
|
|
413
|
+
---
|
|
504
414
|
|
|
505
|
-
|
|
506
|
-
const { MeshService, BLETransport } = require('react-native-ble-mesh');
|
|
507
|
-
const { RNBLEAdapter } = require('react-native-ble-mesh/adapters');
|
|
508
|
-
|
|
509
|
-
const transport = new BLETransport({
|
|
510
|
-
adapter: new RNBLEAdapter(),
|
|
511
|
-
});
|
|
512
|
-
```
|
|
513
|
-
|
|
514
|
-
**Required permissions (iOS):**
|
|
515
|
-
- NSBluetoothAlwaysUsageDescription
|
|
516
|
-
- NSBluetoothPeripheralUsageDescription
|
|
415
|
+
## Use Cases
|
|
517
416
|
|
|
518
|
-
**
|
|
519
|
-
-
|
|
520
|
-
-
|
|
521
|
-
-
|
|
522
|
-
-
|
|
523
|
-
-
|
|
524
|
-
- ACCESS_FINE_LOCATION
|
|
417
|
+
- **Concerts & Festivals** - Text friends when cell towers are overloaded
|
|
418
|
+
- **Camping & Hiking** - Stay connected in the wilderness
|
|
419
|
+
- **Emergencies** - Communicate during power outages or disasters
|
|
420
|
+
- **Protests & Events** - When networks are restricted
|
|
421
|
+
- **Gaming** - Local multiplayer without internet
|
|
422
|
+
- **Schools** - Classroom activities without WiFi
|
|
525
423
|
|
|
526
|
-
|
|
424
|
+
---
|
|
527
425
|
|
|
528
|
-
|
|
529
|
-
const { MeshService, BLETransport } = require('react-native-ble-mesh');
|
|
530
|
-
const { NodeBLEAdapter } = require('react-native-ble-mesh/adapters');
|
|
426
|
+
## Project Structure
|
|
531
427
|
|
|
532
|
-
const transport = new BLETransport({
|
|
533
|
-
adapter: new NodeBLEAdapter(),
|
|
534
|
-
});
|
|
535
428
|
```
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
this.mesh = new MeshService();
|
|
547
|
-
this.messages = [];
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
async start(displayName) {
|
|
551
|
-
await this.mesh.initialize({
|
|
552
|
-
displayName,
|
|
553
|
-
storage: new MemoryStorage(),
|
|
554
|
-
});
|
|
555
|
-
|
|
556
|
-
const transport = new BLETransport();
|
|
557
|
-
await this.mesh.start(transport);
|
|
558
|
-
|
|
559
|
-
this.setupEventHandlers();
|
|
560
|
-
console.log('Chat started! Your fingerprint:', this.mesh.getFingerprint());
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
setupEventHandlers() {
|
|
564
|
-
this.mesh.on('peer-discovered', (peer) => {
|
|
565
|
-
console.log(`[+] Discovered: ${peer.getDisplayName()}`);
|
|
566
|
-
});
|
|
567
|
-
|
|
568
|
-
this.mesh.on('private-message', (message) => {
|
|
569
|
-
this.messages.push(message);
|
|
570
|
-
console.log(`[${message.senderId.slice(0, 8)}]: ${message.content}`);
|
|
571
|
-
});
|
|
572
|
-
|
|
573
|
-
this.mesh.on('message-delivered', ({ messageId }) => {
|
|
574
|
-
console.log(`[OK] Delivered: ${messageId.slice(0, 8)}`);
|
|
575
|
-
});
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
async sendMessage(peerId, content) {
|
|
579
|
-
return await this.mesh.sendPrivateMessage(peerId, content, {
|
|
580
|
-
requiresAck: true,
|
|
581
|
-
});
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
getPeers() {
|
|
585
|
-
return this.mesh.getPeers();
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
verifyPeer(peerId, fingerprint) {
|
|
589
|
-
this.mesh.verifyPeer(peerId, fingerprint);
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
async stop() {
|
|
593
|
-
await this.mesh.stop();
|
|
594
|
-
await this.mesh.destroy();
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
// Usage
|
|
599
|
-
const chat = new ChatApp();
|
|
600
|
-
await chat.start('Alice');
|
|
601
|
-
|
|
602
|
-
// List discovered peers
|
|
603
|
-
const peers = chat.getPeers();
|
|
604
|
-
peers.forEach(p => console.log(p.id, p.getDisplayName()));
|
|
605
|
-
|
|
606
|
-
// Send a message
|
|
607
|
-
await chat.sendMessage(peers[0].id, 'Hello!');
|
|
429
|
+
react-native-ble-mesh/
|
|
430
|
+
├── src/
|
|
431
|
+
│ ├── index.js # Main entry point
|
|
432
|
+
│ ├── MeshNetwork.js # High-level API
|
|
433
|
+
│ ├── crypto/ # Encryption stuff
|
|
434
|
+
│ ├── mesh/ # Routing & networking
|
|
435
|
+
│ ├── transport/ # Bluetooth layer
|
|
436
|
+
│ └── hooks/ # React hooks
|
|
437
|
+
├── docs/ # Documentation
|
|
438
|
+
└── __tests__/ # Tests
|
|
608
439
|
```
|
|
609
440
|
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
```javascript
|
|
613
|
-
// Messages automatically route through multiple hops
|
|
614
|
-
const messageId = await mesh.sendPrivateMessage(distantPeerId, 'Hello distant peer!', {
|
|
615
|
-
maxHops: 10, // Allow up to 10 hops
|
|
616
|
-
requiresAck: true,
|
|
617
|
-
});
|
|
441
|
+
---
|
|
618
442
|
|
|
619
|
-
|
|
620
|
-
const stats = mesh.getStats();
|
|
621
|
-
console.log('Messages relayed:', stats.messagesRelayed);
|
|
622
|
-
console.log('Average hop count:', stats.averageHopCount);
|
|
623
|
-
```
|
|
443
|
+
## More Documentation
|
|
624
444
|
|
|
625
|
-
|
|
445
|
+
- [Full API Reference](docs/API.md) - Every method explained
|
|
446
|
+
- [React Native Guide](docs/REACT_NATIVE.md) - Platform-specific setup
|
|
447
|
+
- [Security Details](docs/SECURITY.md) - How encryption works
|
|
448
|
+
- [Protocol Spec](docs/PROTOCOL.md) - Technical wire format
|
|
449
|
+
- [AI/Agent Instructions](docs/prompt-instructions.md) - For AI assistants
|
|
626
450
|
|
|
627
|
-
|
|
628
|
-
const fs = require('fs');
|
|
629
|
-
const path = require('path');
|
|
630
|
-
|
|
631
|
-
async function sendFile(mesh, peerId, filePath) {
|
|
632
|
-
const fileData = fs.readFileSync(filePath);
|
|
633
|
-
const fileName = path.basename(filePath);
|
|
634
|
-
|
|
635
|
-
// The library automatically handles:
|
|
636
|
-
// - Compression for large payloads
|
|
637
|
-
// - Fragmentation for BLE MTU limits
|
|
638
|
-
// - Encryption with peer's session key
|
|
639
|
-
// - Automatic retry on failure
|
|
640
|
-
|
|
641
|
-
const messageId = await mesh.sendPrivateMessage(peerId, {
|
|
642
|
-
type: 'file',
|
|
643
|
-
name: fileName,
|
|
644
|
-
data: fileData.toString('base64'),
|
|
645
|
-
size: fileData.length,
|
|
646
|
-
}, {
|
|
647
|
-
compress: true,
|
|
648
|
-
requiresAck: true,
|
|
649
|
-
});
|
|
650
|
-
|
|
651
|
-
console.log('File transfer initiated:', messageId);
|
|
652
|
-
}
|
|
653
|
-
```
|
|
451
|
+
---
|
|
654
452
|
|
|
655
453
|
## Testing
|
|
656
454
|
|
|
657
|
-
Run the test suite:
|
|
658
|
-
|
|
659
455
|
```bash
|
|
456
|
+
# Run all tests
|
|
660
457
|
npm test
|
|
661
|
-
```
|
|
662
458
|
|
|
663
|
-
Run with coverage
|
|
664
|
-
|
|
665
|
-
```bash
|
|
459
|
+
# Run with coverage report
|
|
666
460
|
npm run test:coverage
|
|
667
461
|
```
|
|
668
462
|
|
|
669
|
-
|
|
463
|
+
---
|
|
670
464
|
|
|
671
|
-
|
|
672
|
-
npm test -- __tests__/crypto/sha256.test.js
|
|
673
|
-
npm test -- __tests__/integration/handshake.test.js
|
|
674
|
-
```
|
|
675
|
-
|
|
676
|
-
## Project Structure
|
|
465
|
+
## Contributing
|
|
677
466
|
|
|
678
|
-
|
|
679
|
-
react-native-ble-mesh/
|
|
680
|
-
├── src/
|
|
681
|
-
│ ├── index.js # Main exports
|
|
682
|
-
│ ├── constants/ # Protocol, BLE, crypto constants
|
|
683
|
-
│ ├── errors/ # Custom error classes
|
|
684
|
-
│ ├── crypto/ # Cryptographic implementations
|
|
685
|
-
│ │ ├── noise/ # Noise Protocol (handshake, session)
|
|
686
|
-
│ │ └── keys/ # Key management
|
|
687
|
-
│ ├── protocol/ # Message serialization/deserialization
|
|
688
|
-
│ ├── mesh/ # Mesh networking logic
|
|
689
|
-
│ │ ├── router/ # Message routing
|
|
690
|
-
│ │ ├── dedup/ # Duplicate detection
|
|
691
|
-
│ │ ├── fragment/ # Message fragmentation
|
|
692
|
-
│ │ └── peer/ # Peer management
|
|
693
|
-
│ ├── transport/ # BLE transport layer
|
|
694
|
-
│ ├── storage/ # Persistence adapters
|
|
695
|
-
│ ├── utils/ # Utility functions
|
|
696
|
-
│ └── service/ # High-level service orchestration
|
|
697
|
-
├── __tests__/ # Test files
|
|
698
|
-
├── docs/ # Documentation
|
|
699
|
-
├── examples/ # Example applications
|
|
700
|
-
└── package.json
|
|
701
|
-
```
|
|
467
|
+
We love contributions! Here's how:
|
|
702
468
|
|
|
703
|
-
|
|
469
|
+
1. Fork this repository
|
|
470
|
+
2. Create a branch: `git checkout -b my-feature`
|
|
471
|
+
3. Make your changes
|
|
472
|
+
4. Run tests: `npm test`
|
|
473
|
+
5. Push and create a Pull Request
|
|
704
474
|
|
|
705
|
-
|
|
706
|
-
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
707
|
-
3. Run tests and linting (`npm run validate`)
|
|
708
|
-
4. Commit your changes (`git commit -m 'Add amazing feature'`)
|
|
709
|
-
5. Push to the branch (`git push origin feature/amazing-feature`)
|
|
710
|
-
6. Open a Pull Request
|
|
475
|
+
---
|
|
711
476
|
|
|
712
|
-
|
|
477
|
+
## Credits
|
|
713
478
|
|
|
714
|
-
-
|
|
715
|
-
- JSDoc comments on all public APIs
|
|
716
|
-
- ESLint with strict rules
|
|
717
|
-
- 100% test coverage on crypto modules
|
|
718
|
-
- >80% coverage on other modules
|
|
479
|
+
Inspired by [BitChat](https://github.com/nicegram/nicegram-bitchat) - the original decentralized mesh chat.
|
|
719
480
|
|
|
720
|
-
|
|
481
|
+
Built with:
|
|
482
|
+
- [react-native-ble-plx](https://github.com/Polidea/react-native-ble-plx) - Bluetooth Low Energy
|
|
483
|
+
- [Noise Protocol](https://noiseprotocol.org/) - Secure handshakes
|
|
721
484
|
|
|
722
|
-
|
|
723
|
-
- [Architecture](docs/ARCHITECTURE.md) - System design and module structure
|
|
724
|
-
- [Security](docs/SECURITY.md) - Threat model and security properties
|
|
725
|
-
- [Protocol](docs/PROTOCOL.md) - Wire format and message types
|
|
485
|
+
---
|
|
726
486
|
|
|
727
487
|
## License
|
|
728
488
|
|
|
729
|
-
|
|
489
|
+
MIT License - do whatever you want with it! See [LICENSE](LICENSE) for details.
|
|
490
|
+
|
|
491
|
+
---
|
|
492
|
+
|
|
493
|
+
## Get Help
|
|
730
494
|
|
|
731
|
-
|
|
495
|
+
- **Issues**: [GitHub Issues](https://github.com/suhailtajshaik/react-native-ble-mesh/issues)
|
|
496
|
+
- **Questions**: Open a Discussion on GitHub
|
|
732
497
|
|
|
733
|
-
|
|
498
|
+
---
|
|
734
499
|
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
500
|
+
<p align="center">
|
|
501
|
+
<b>Made with ❤️ for a more connected (yet private) world</b>
|
|
502
|
+
<br><br>
|
|
503
|
+
If this library helps you, give it a ⭐ on GitHub!
|
|
504
|
+
</p>
|