refactor: reorganize NIP-BE into subpackages and add README

Restructured nipBEBle/ into logical subpackages:
- Root: public API surface (BleNostrMesh, BleConfig, BlePeer, BleRole)
- protocol/: wire format (BleMessageChunker, BleChunkAssembler)
- transport/: platform BLE abstraction (BleTransport, AndroidBleTransport)
- relay/: Nostr protocol over BLE (BleNostrClient, BleNostrServer, BleMeshManager)

Added README.md with quick start guide, package structure overview,
advanced usage examples, and wire protocol reference.

https://claude.ai/code/session_01Tz5E73Rj7tL48A3qUGS5DT
This commit is contained in:
Claude
2026-03-30 02:23:49 +00:00
parent 28f68382dd
commit 6e7f6eccf1
11 changed files with 182 additions and 9 deletions
@@ -18,7 +18,7 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.quartz.nipBEBle
package com.vitorpamplona.quartz.nipBEBle.transport
import android.annotation.SuppressLint
import android.bluetooth.BluetoothAdapter
@@ -41,6 +41,10 @@ import android.bluetooth.le.ScanResult
import android.bluetooth.le.ScanSettings
import android.content.Context
import android.os.ParcelUuid
import com.vitorpamplona.quartz.nipBEBle.AndroidBleTransportContract
import com.vitorpamplona.quartz.nipBEBle.BleConfig
import com.vitorpamplona.quartz.nipBEBle.BlePeer
import com.vitorpamplona.quartz.nipBEBle.BleRole
import com.vitorpamplona.quartz.utils.Log
import java.util.UUID
@@ -24,6 +24,11 @@ import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.relay.client.single.IRelayClient
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.Message
import com.vitorpamplona.quartz.nip01Core.relay.commands.toRelay.Command
import com.vitorpamplona.quartz.nipBEBle.relay.BleMeshListener
import com.vitorpamplona.quartz.nipBEBle.relay.BleMeshManager
import com.vitorpamplona.quartz.nipBEBle.relay.BleNostrServer
import com.vitorpamplona.quartz.nipBEBle.transport.BleTransport
import com.vitorpamplona.quartz.nipBEBle.transport.BleTransportListener
/**
* Easy-to-use facade for NIP-BE Nostr BLE mesh networking.
@@ -0,0 +1,145 @@
# NIP-BE: Nostr BLE Communications Protocol
Kotlin Multiplatform implementation of [NIP-BE](https://github.com/nostr-protocol/nips/blob/master/BE.md)
for peer-to-peer Nostr event synchronization over Bluetooth Low Energy.
Compatible with [KoalaSat/samiz](https://github.com/KoalaSat/samiz).
## Quick Start (Android)
```kotlin
// 1. Create the mesh (auto-wires transport)
val mesh = BleNostrMesh(AndroidBleTransport(context))
// 2. Listen for events from nearby peers
mesh.onEvent { event, peer ->
Log.d("BLE", "Received kind ${event.kind} from ${peer.deviceUuid}")
myDatabase.save(event)
}
// 3. Optional: track peer connectivity
mesh.onPeerConnected { peer -> updatePeerCount(mesh.connectedPeers().size) }
mesh.onPeerDisconnected { peer -> updatePeerCount(mesh.connectedPeers().size) }
mesh.onError { error -> Log.e("BLE", error) }
// 4. Start (begins advertising + scanning)
mesh.start()
// 5. Broadcast events to all connected peers
mesh.broadcast(mySignedEvent)
// 6. Stop when done
mesh.stop()
```
### Required Android Permissions
```xml
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
```
Request these at runtime before calling `mesh.start()`.
## How It Works
When two devices running NIP-BE discover each other:
1. **Discovery** - Both devices advertise and scan for the Nostr BLE service UUID
2. **Role assignment** - The device with the higher UUID becomes the GATT Server (relay),
the other becomes the GATT Client
3. **Connection** - The client connects to the server's GATT service
4. **Messaging** - NIP-01 JSON messages are DEFLATE-compressed, split into chunks,
and exchanged via BLE characteristics
5. **Event spreading** - New events are automatically forwarded to all connected peers
## Package Structure
```
nipBEBle/
BleNostrMesh.kt # Easy-to-use facade (start here)
BleConfig.kt # NIP-BE constants (UUIDs, sizes)
BlePeer.kt # Peer data model
BleRole.kt # Role assignment (SERVER/CLIENT)
protocol/ # Wire format
BleMessageChunker.kt # DEFLATE compression + chunk splitting/joining
BleChunkAssembler.kt # Reassembles incoming chunks into messages
transport/ # Platform BLE abstraction
BleTransport.kt # Interface for BLE operations
AndroidBleTransport.kt # Android implementation (androidMain)
relay/ # Nostr relay protocol over BLE
BleNostrClient.kt # Client side (implements IRelayClient)
BleNostrServer.kt # Server side (receives commands, sends messages)
BleMeshManager.kt # Orchestrates discovery, connections, broadcasting
```
## Advanced Usage
### Use a BLE peer as a standard relay client
`BleNostrClient` implements `IRelayClient`, so you can use BLE peers alongside
WebSocket relays in the existing subscription infrastructure:
```kotlin
val relayClient = mesh.getRelayClient(peer.deviceUuid)
relayClient?.sendIfConnected(ReqCmd("sub1", listOf(filter)))
```
### Access raw protocol messages
```kotlin
mesh.onMessage { relay, msg ->
// Nostr messages received when acting as client (EVENT, EOSE, OK, etc.)
}
mesh.onCommand { server, cmd ->
// Nostr commands received when acting as server (REQ, EVENT, CLOSE, etc.)
}
```
### Implement a custom BLE transport
For platforms without a built-in transport, implement `BleTransport`:
```kotlin
class MyPlatformBleTransport : BleTransport {
override val deviceUuid = UUID.randomUUID().toString()
override fun startAdvertising() { /* ... */ }
override fun startScanning() { /* ... */ }
// ... other methods
}
```
### Use BleMeshManager directly
For full control over the relay protocol layer:
```kotlin
val transport = AndroidBleTransport(context)
val manager = BleMeshManager(transport, object : BleMeshListener {
override fun onEventReceived(event: Event, fromPeer: BlePeer) { /* ... */ }
})
transport.setListener(manager)
manager.start()
```
## Wire Protocol
Messages follow [NIP-01](https://github.com/nostr-protocol/nips/blob/master/01.md) JSON format,
compressed with DEFLATE and split into chunks:
```
[chunk index (1 byte)][payload (up to 500 bytes)][total chunks (1 byte)]
```
| GATT Characteristic | UUID | Direction |
|---------------------|------|-----------|
| Write | `87654321-0000-1000-8000-00805f9b34fb` | Client -> Server |
| Read (Notify) | `12345678-0000-1000-8000-00805f9b34fb` | Server -> Client |
Service UUID: `0000180f-0000-1000-8000-00805f9b34fb`
@@ -18,7 +18,7 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.quartz.nipBEBle
package com.vitorpamplona.quartz.nipBEBle.protocol
/**
* Accumulates incoming BLE chunks for a single message and reassembles
@@ -18,8 +18,9 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.quartz.nipBEBle
package com.vitorpamplona.quartz.nipBEBle.protocol
import com.vitorpamplona.quartz.nipBEBle.BleConfig
import com.vitorpamplona.quartz.utils.Deflate
/**
@@ -18,7 +18,7 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.quartz.nipBEBle
package com.vitorpamplona.quartz.nipBEBle.relay
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.RelayConnectionListener
@@ -27,6 +27,12 @@ import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.EventMessage
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.Message
import com.vitorpamplona.quartz.nip01Core.relay.commands.toRelay.Command
import com.vitorpamplona.quartz.nip01Core.relay.commands.toRelay.EventCmd
import com.vitorpamplona.quartz.nipBEBle.BlePeer
import com.vitorpamplona.quartz.nipBEBle.BleRole
import com.vitorpamplona.quartz.nipBEBle.assignRole
import com.vitorpamplona.quartz.nipBEBle.protocol.BleMessageChunker
import com.vitorpamplona.quartz.nipBEBle.transport.BleTransport
import com.vitorpamplona.quartz.nipBEBle.transport.BleTransportListener
import com.vitorpamplona.quartz.utils.Log
/**
@@ -18,13 +18,18 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.quartz.nipBEBle
package com.vitorpamplona.quartz.nipBEBle.relay
import com.vitorpamplona.quartz.nip01Core.core.OptimizedJsonMapper
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.RelayConnectionListener
import com.vitorpamplona.quartz.nip01Core.relay.client.single.IRelayClient
import com.vitorpamplona.quartz.nip01Core.relay.commands.toRelay.Command
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import com.vitorpamplona.quartz.nipBEBle.BleConfig
import com.vitorpamplona.quartz.nipBEBle.BlePeer
import com.vitorpamplona.quartz.nipBEBle.protocol.BleChunkAssembler
import com.vitorpamplona.quartz.nipBEBle.protocol.BleMessageChunker
import com.vitorpamplona.quartz.nipBEBle.transport.BleTransport
import com.vitorpamplona.quartz.utils.Log
/**
@@ -18,11 +18,16 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.quartz.nipBEBle
package com.vitorpamplona.quartz.nipBEBle.relay
import com.vitorpamplona.quartz.nip01Core.core.OptimizedJsonMapper
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.Message
import com.vitorpamplona.quartz.nip01Core.relay.commands.toRelay.Command
import com.vitorpamplona.quartz.nipBEBle.BleConfig
import com.vitorpamplona.quartz.nipBEBle.BlePeer
import com.vitorpamplona.quartz.nipBEBle.protocol.BleChunkAssembler
import com.vitorpamplona.quartz.nipBEBle.protocol.BleMessageChunker
import com.vitorpamplona.quartz.nipBEBle.transport.BleTransport
import com.vitorpamplona.quartz.utils.Log
/**
@@ -18,7 +18,9 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.quartz.nipBEBle
package com.vitorpamplona.quartz.nipBEBle.transport
import com.vitorpamplona.quartz.nipBEBle.BlePeer
/**
* Platform-agnostic interface for BLE transport operations.
@@ -18,7 +18,7 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.quartz.nipBEBle
package com.vitorpamplona.quartz.nipBEBle.protocol
import kotlin.test.Test
import kotlin.test.assertEquals
@@ -18,7 +18,7 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.quartz.nipBEBle
package com.vitorpamplona.quartz.nipBEBle.protocol
import kotlin.test.Test
import kotlin.test.assertEquals