@magicred-1/ble-mesh 1.2.0

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 ADDED
@@ -0,0 +1,394 @@
1
+ # kard-network-ble-mesh
2
+
3
+ A React Native library for BLE (Bluetooth Low Energy) mesh networking. Build decentralized, peer-to-peer messaging apps that work without internet connectivity.
4
+
5
+ ## Features
6
+
7
+ - **Cross-platform**: Works on both iOS and Android
8
+ - **Mesh networking**: Messages automatically relay through peers
9
+ - **End-to-end encryption**: Noise protocol for secure private messages
10
+ - **File transfer**: Send files over BLE mesh (chunked for reliability)
11
+ - **Solana transactions**: Multi-signature transaction signing over BLE
12
+ - **Seamless permissions**: Handles permission requests automatically
13
+ - **Message deduplication**: Built-in deduplication prevents duplicate messages
14
+ - **Peer discovery**: Automatic discovery of nearby devices
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install kard-network-ble-mesh
20
+ # or
21
+ yarn add kard-network-ble-mesh
22
+ ```
23
+
24
+ ### iOS
25
+
26
+ ```bash
27
+ cd ios && pod install
28
+ ```
29
+
30
+ Add the following to your `Info.plist`:
31
+
32
+ ```xml
33
+ <key>NSBluetoothAlwaysUsageDescription</key>
34
+ <string>This app uses Bluetooth to communicate with nearby devices</string>
35
+ <key>NSBluetoothPeripheralUsageDescription</key>
36
+ <string>This app uses Bluetooth to communicate with nearby devices</string>
37
+ <key>UIBackgroundModes</key>
38
+ <array>
39
+ <string>bluetooth-central</string>
40
+ <string>bluetooth-peripheral</string>
41
+ </array>
42
+ ```
43
+
44
+ ### Android
45
+
46
+ Add the following permissions to your `AndroidManifest.xml` (they are already included in the library manifest):
47
+
48
+ ```xml
49
+ <uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
50
+ <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
51
+ <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
52
+ <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
53
+ <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
54
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
55
+ <uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />
56
+ ```
57
+
58
+ ## Usage
59
+
60
+ ```typescript
61
+ import { BleMesh } from 'kard-network-ble-mesh';
62
+
63
+ // Start the mesh service (automatically requests permissions)
64
+ await BleMesh.start({ nickname: 'Alice' });
65
+
66
+ // Listen for events
67
+ BleMesh.onPeerListUpdated(({ peers }) => {
68
+ console.log('Connected peers:', peers);
69
+ });
70
+
71
+ BleMesh.onMessageReceived(({ message }) => {
72
+ console.log('Received message:', message);
73
+ });
74
+
75
+ // Send a public broadcast message
76
+ await BleMesh.sendMessage('Hello everyone!');
77
+
78
+ // Send a private encrypted message
79
+ await BleMesh.sendPrivateMessage('Secret message', recipientPeerId);
80
+
81
+ // Get current peers
82
+ const peers = await BleMesh.getPeers();
83
+
84
+ // Stop the service
85
+ await BleMesh.stop();
86
+ ```
87
+
88
+ ## API Reference
89
+
90
+ ### BleMesh
91
+
92
+ The main singleton instance for interacting with the BLE mesh.
93
+
94
+ #### Methods
95
+
96
+ ##### `start(config?: MeshServiceConfig): Promise<void>`
97
+
98
+ Starts the mesh service. Automatically requests required permissions.
99
+
100
+ ```typescript
101
+ interface MeshServiceConfig {
102
+ nickname?: string; // Default: 'anon'
103
+ autoRequestPermissions?: boolean; // Default: true
104
+ }
105
+ ```
106
+
107
+ ##### `stop(): Promise<void>`
108
+
109
+ Stops the mesh service and disconnects from all peers.
110
+
111
+ ##### `setNickname(nickname: string): Promise<void>`
112
+
113
+ Updates your nickname and broadcasts the change to peers.
114
+
115
+ ##### `getMyPeerId(): Promise<string>`
116
+
117
+ Returns your unique peer ID (derived from your public key fingerprint).
118
+
119
+ ##### `getMyNickname(): Promise<string>`
120
+
121
+ Returns your current nickname.
122
+
123
+ ##### `getPeers(): Promise<Peer[]>`
124
+
125
+ Returns an array of all discovered peers.
126
+
127
+ ```typescript
128
+ interface Peer {
129
+ peerId: string;
130
+ nickname: string;
131
+ isConnected: boolean;
132
+ rssi?: number;
133
+ lastSeen: number;
134
+ isVerified: boolean;
135
+ }
136
+ ```
137
+
138
+ ##### `sendMessage(content: string, channel?: string): Promise<string>`
139
+
140
+ Sends a public broadcast message. Returns the message ID.
141
+
142
+ ##### `sendPrivateMessage(content: string, recipientPeerId: string): Promise<string>`
143
+
144
+ Sends an encrypted private message to a specific peer. Returns the message ID.
145
+
146
+ ##### `sendReadReceipt(messageId: string, recipientPeerId: string): Promise<void>`
147
+
148
+ Sends a read receipt for a received message.
149
+
150
+ ##### `sendFile(filePath: string, options?: { recipientPeerId?: string; channel?: string }): Promise<string>`
151
+
152
+ Sends a file over the mesh network. Files are automatically chunked for reliable transmission.
153
+
154
+ ```typescript
155
+ // Send to all peers (broadcast)
156
+ await BleMesh.sendFile('/path/to/photo.jpg');
157
+
158
+ // Send to specific peer
159
+ await BleMesh.sendFile('/path/to/document.pdf', { recipientPeerId: 'abc123' });
160
+ ```
161
+
162
+ ##### `sendTransaction(serializedTransaction: string, options): Promise<string>`
163
+
164
+ Sends a Solana transaction for any peer to sign as the second signer. Perfect for multi-signature transactions.
165
+
166
+ **Targeted (specific peer):**
167
+ ```typescript
168
+ await BleMesh.sendTransaction(base64SerializedTx, {
169
+ recipientPeerId: 'abc123', // Send to specific peer (optional)
170
+ firstSignerPublicKey: senderPubKey,
171
+ secondSignerPublicKey: recipientPubKey, // Preferred signer (optional)
172
+ description: 'Payment for services'
173
+ });
174
+ ```
175
+
176
+ **Broadcast (any peer can sign):**
177
+ ```typescript
178
+ // Broadcast to all peers - any peer can sign
179
+ await BleMesh.sendTransaction(base64SerializedTx, {
180
+ firstSignerPublicKey: senderPubKey,
181
+ // No recipientPeerId - broadcasts to all
182
+ // No secondSignerPublicKey - open for any signer
183
+ description: 'Open offer - anyone can complete'
184
+ });
185
+ ```
186
+
187
+ ##### `respondToTransaction(transactionId: string, recipientPeerId: string, response): Promise<void>`
188
+
189
+ Responds to a received transaction with the signed transaction or an error.
190
+
191
+ ```typescript
192
+ // Approve and sign
193
+ await BleMesh.respondToTransaction(txId, senderPeerId, {
194
+ signedTransaction: fullySignedBase64Tx
195
+ });
196
+
197
+ // Reject
198
+ await BleMesh.respondToTransaction(txId, senderPeerId, {
199
+ error: 'User rejected transaction'
200
+ });
201
+ ```
202
+
203
+ ##### `hasEncryptedSession(peerId: string): Promise<boolean>`
204
+
205
+ Checks if an encrypted session exists with the specified peer.
206
+
207
+ ##### `initiateHandshake(peerId: string): Promise<void>`
208
+
209
+ Initiates a Noise handshake with a peer for encrypted communication.
210
+
211
+ ##### `getIdentityFingerprint(): Promise<string>`
212
+
213
+ Returns your identity fingerprint for verification.
214
+
215
+ ##### `getPeerFingerprint(peerId: string): Promise<string | null>`
216
+
217
+ Returns a peer's identity fingerprint for verification.
218
+
219
+ ##### `broadcastAnnounce(): Promise<void>`
220
+
221
+ Forces a broadcast announce to refresh your presence on the mesh.
222
+
223
+ #### Events
224
+
225
+ ##### `onPeerListUpdated(callback): () => void`
226
+
227
+ Called when the peer list changes.
228
+
229
+ ```typescript
230
+ BleMesh.onPeerListUpdated(({ peers }) => {
231
+ console.log('Peers updated:', peers);
232
+ });
233
+ ```
234
+
235
+ ##### `onMessageReceived(callback): () => void`
236
+
237
+ Called when a message is received.
238
+
239
+ ```typescript
240
+ BleMesh.onMessageReceived(({ message }) => {
241
+ console.log('Message from', message.senderNickname, ':', message.content);
242
+ });
243
+ ```
244
+
245
+ ##### `onConnectionStateChanged(callback): () => void`
246
+
247
+ Called when the connection state changes.
248
+
249
+ ##### `onReadReceipt(callback): () => void`
250
+
251
+ Called when a read receipt is received.
252
+
253
+ ##### `onDeliveryAck(callback): () => void`
254
+
255
+ Called when a delivery acknowledgment is received.
256
+
257
+ ##### `onFileReceived(callback): () => void`
258
+
259
+ Called when a file is received.
260
+
261
+ ```typescript
262
+ BleMesh.onFileReceived(({ file }) => {
263
+ console.log('Received file:', file.fileName);
264
+ console.log('Size:', file.fileSize);
265
+ console.log('Data (base64):', file.data);
266
+ // Save or display the file...
267
+ });
268
+ ```
269
+
270
+ ##### `onTransactionReceived(callback): () => void`
271
+
272
+ Called when a Solana transaction is received for signing.
273
+
274
+ ```typescript
275
+ BleMesh.onTransactionReceived(({ transaction }) => {
276
+ console.log('Transaction to sign:', transaction.id);
277
+ console.log('Serialized:', transaction.serializedTransaction);
278
+ console.log('First signer:', transaction.firstSignerPublicKey);
279
+
280
+ // Check if a specific second signer is required
281
+ if (transaction.secondSignerPublicKey) {
282
+ console.log('Required second signer:', transaction.secondSignerPublicKey);
283
+ // Verify this is your public key before signing
284
+ } else if (transaction.openForAnySigner) {
285
+ console.log('Any peer can sign this transaction');
286
+ }
287
+
288
+ // Sign with your wallet...
289
+ const signedTx = await yourWallet.signTransaction(transaction.serializedTransaction);
290
+
291
+ // Respond
292
+ await BleMesh.respondToTransaction(transaction.id, transaction.senderPeerId, {
293
+ signedTransaction: signedTx
294
+ });
295
+ });
296
+ ```
297
+
298
+ ##### `onTransactionResponse(callback): () => void`
299
+
300
+ Called when a transaction response is received (signed or rejected).
301
+
302
+ ```typescript
303
+ BleMesh.onTransactionResponse(({ response }) => {
304
+ if (response.signedTransaction) {
305
+ console.log('Transaction fully signed!');
306
+ // Broadcast to Solana network...
307
+ } else if (response.error) {
308
+ console.log('Transaction rejected:', response.error);
309
+ }
310
+ });
311
+ ```
312
+
313
+ ##### `onError(callback): () => void`
314
+
315
+ Called when an error occurs.
316
+
317
+ ## Solana Multi-Signature Transaction Flow
318
+
319
+ ### Targeted Transaction (Specific Peer)
320
+
321
+ ```
322
+ Alice (First Signer) Bob (Second Signer)
323
+ | |
324
+ | 1. Create transaction |
325
+ | 2. Sign with Alice's key |
326
+ | 3. Send via BleMesh.sendTransaction() |
327
+ | with recipientPeerId |
328
+ |--------------------------------------->|
329
+ | |
330
+ | 4. onTransactionReceived event |
331
+ |<---------------------------------------|
332
+ | |
333
+ | 5. Review & sign with Bob's key |
334
+ |<---------------------------------------|
335
+ | |
336
+ | 6. respondToTransaction() |
337
+ |--------------------------------------->|
338
+ | |
339
+ | 7. Broadcast fully signed tx |
340
+ v to Solana network v
341
+ ```
342
+
343
+ ### Broadcast Transaction (Any Peer Can Sign)
344
+
345
+ ```
346
+ Alice (First Signer) Bob Carol Dave
347
+ | | | |
348
+ | 1. Create tx | | |
349
+ | 2. Sign | | |
350
+ | 3. Broadcast | | |
351
+ |---------------------->|------------>|------------>|
352
+ | | | |
353
+ | | 4. All receive |
354
+ | | onTransactionReceived |
355
+ | | | |
356
+ | | 5. Carol decides to sign |
357
+ | | | |
358
+ | |<------------|------------>|
359
+ | | 6. Carol responds |
360
+ |<----------------------|-------------|-------------|
361
+ | | | |
362
+ | 7. Alice receives | | |
363
+ | signed tx | | |
364
+ v v v v
365
+ ```
366
+
367
+ ### MTU and Transaction Size Limits
368
+
369
+ The library automatically handles BLE MTU limitations:
370
+
371
+ | Feature | Max Size | Handling |
372
+ |---------|----------|----------|
373
+ | Simple messages | ~450 bytes | Single packet |
374
+ | Small transactions | ~450 bytes | Single packet |
375
+ | Large transactions | Unlimited | Automatic chunking (400 byte chunks) |
376
+ | Files | Unlimited | Chunked (180 byte chunks) |
377
+
378
+ **For Solana transactions:**
379
+ - Most transactions (simple transfers) fit in a single packet
380
+ - Complex transactions (multi-instruction, large memos) are automatically chunked
381
+ - Chunking is transparent to the application layer
382
+ - The receiver automatically reassembles chunks before decrypting
383
+
384
+ ## Protocol Compatibility
385
+
386
+ This library uses the same binary protocol as:
387
+ - [bitchat (iOS)](https://github.com/permissionlesstech/bitchat)
388
+ - [bitchat-android](https://github.com/permissionlesstech/bitchat-android)
389
+
390
+ Messages can be exchanged between all three platforms.
391
+
392
+ ## License
393
+
394
+ MIT
@@ -0,0 +1,94 @@
1
+ buildscript {
2
+ repositories {
3
+ google()
4
+ mavenCentral()
5
+ }
6
+
7
+ dependencies {
8
+ classpath "com.android.tools.build:gradle:8.1.0"
9
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.0"
10
+ }
11
+ }
12
+
13
+ def isNewArchitectureEnabled() {
14
+ return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
15
+ }
16
+
17
+ apply plugin: "com.android.library"
18
+ apply plugin: "kotlin-android"
19
+
20
+ def getExtOrDefault(name) {
21
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["BleMesh_" + name]
22
+ }
23
+
24
+ def getExtOrIntegerDefault(name) {
25
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["BleMesh_" + name]).toInteger()
26
+ }
27
+
28
+ def supportsNamespace() {
29
+ def parsed = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.')
30
+ def major = parsed[0].toInteger()
31
+ def minor = parsed[1].toInteger()
32
+ return (major == 7 && minor >= 3) || major >= 8
33
+ }
34
+
35
+ android {
36
+ if (supportsNamespace()) {
37
+ namespace "com.blemesh"
38
+
39
+ sourceSets {
40
+ main {
41
+ manifest.srcFile "src/main/AndroidManifestNew.xml"
42
+ }
43
+ }
44
+ }
45
+
46
+ compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
47
+
48
+ defaultConfig {
49
+ minSdkVersion getExtOrIntegerDefault("minSdkVersion")
50
+ targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
51
+ buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
52
+ }
53
+
54
+ buildFeatures {
55
+ buildConfig true
56
+ }
57
+
58
+ buildTypes {
59
+ release {
60
+ minifyEnabled false
61
+ }
62
+ }
63
+
64
+ lintOptions {
65
+ disable "GradleCompatible"
66
+ }
67
+
68
+ compileOptions {
69
+ sourceCompatibility JavaVersion.VERSION_17
70
+ targetCompatibility JavaVersion.VERSION_17
71
+ }
72
+
73
+ kotlinOptions {
74
+ jvmTarget = "17"
75
+ }
76
+
77
+ sourceSets {
78
+ main {
79
+ java.srcDirs = ['src/main/java']
80
+ }
81
+ }
82
+ }
83
+
84
+ repositories {
85
+ mavenCentral()
86
+ google()
87
+ }
88
+
89
+ dependencies {
90
+ implementation "com.facebook.react:react-android"
91
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:1.9.0"
92
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3"
93
+ implementation "androidx.core:core-ktx:1.12.0"
94
+ }
@@ -0,0 +1,4 @@
1
+ BleMesh_kotlinVersion=1.9.0
2
+ BleMesh_minSdkVersion=24
3
+ BleMesh_targetSdkVersion=34
4
+ BleMesh_compileSdkVersion=34
@@ -0,0 +1,18 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
2
+ package="com.blemesh">
3
+
4
+ <!-- BLE Permissions -->
5
+ <uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
6
+ <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
7
+ <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
8
+ <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
9
+ <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
10
+
11
+ <!-- Location Permissions (required for BLE scanning) -->
12
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
13
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
14
+
15
+ <!-- Declare BLE feature -->
16
+ <uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />
17
+
18
+ </manifest>
@@ -0,0 +1,17 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+
3
+ <!-- BLE Permissions -->
4
+ <uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
5
+ <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
6
+ <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
7
+ <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
8
+ <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
9
+
10
+ <!-- Location Permissions (required for BLE scanning) -->
11
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
12
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
13
+
14
+ <!-- Declare BLE feature -->
15
+ <uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />
16
+
17
+ </manifest>