feat: add Lightning and Cashu payment packets to the Noise protocol layer#1053
feat: add Lightning and Cashu payment packets to the Noise protocol layer#1053spcpza wants to merge 4 commits intopermissionlesstech:mainfrom
Conversation
…ayer Adds two new NoisePayloadType cases and their TLV packet structures, enabling peer-to-peer Bitcoin payments over the Bluetooth mesh network. ## Why structured packets instead of raw text? bitchat already detects bolt11 and Cashu tokens in plain text messages (MessageFormattingEngine) and renders them as chips (PaymentChipView). This PR adds a protocol-level payment layer on top, which enables: - Amount and description visible before the user opens any wallet app - Expiry-aware UI (mark stale invoices; warn on near-expiry) - Stable `requestID` / `transferID` for delivery receipts and deduplication - Clean separation: the chat message thread stays human-readable while the payment metadata travels in the encrypted Noise payload ## New NoisePayloadType cases ```swift case lightningPaymentRequest = 0x20 // BOLT11 invoice case cashuToken = 0x21 // Cashu eCash bearer token ``` Both are encrypted by the existing Noise session — network observers cannot distinguish payment traffic from regular private messages. ## LightningPaymentRequestPacket (Packets.swift) TLV-encoded, mirrors PrivateMessagePacket/BitchatFilePacket patterns: - requestID (UInt8 tag, UInt16 BE length) — stable UUID for dedup - invoice — full BOLT11 string (UInt16 length supports up to 65 KB) - memo — optional human-readable description - amountSat — optional UInt64 for display without decoding the invoice - expiresAt — optional Unix timestamp for expiry UI ## CashuTokenPacket (Packets.swift) - transferID — stable UUID for dedup and tracking - token — cashuA… / cashuB… base64url token string - mintURL — mint the token is valid against - amountSat — face value for display - memo — optional sender note ## The Cashu case: genuinely offline Bitcoin transfers Cashu tokens are bearer instruments — the token string IS the money. Neither the sender nor any relay node needs internet connectivity. The recipient redeems the token at the mint when they next have connectivity. This makes value transfer possible in mesh-only scenarios: remote areas, disaster relief, events with no cell coverage. ## BitchatDelegate extensions Default-implemented delegate methods added for both payment types so existing delegate implementations compile without changes: - didReceiveLightningPaymentRequest(_:from:timestamp:) - didReceiveCashuToken(_:from:timestamp:) ## Wire compatibility Unknown NoisePayloadType bytes are already handled gracefully by the existing Noise payload dispatch — older clients that don't recognise 0x20 or 0x21 will silently ignore these packets. No protocol version bump required. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 32f851aaa6
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| extension BitchatDelegate { | ||
| /// Called when a peer sends a Lightning payment request over the Bluetooth mesh. | ||
| /// The invoice should be presented as a tappable payment action in the chat UI. | ||
| func didReceiveLightningPaymentRequest(_ request: LightningPaymentRequestPacket, from peerID: PeerID, timestamp: Date) { |
There was a problem hiding this comment.
Move payment callbacks into BitchatDelegate requirements
Defining didReceiveLightningPaymentRequest and didReceiveCashuToken only in a protocol extension means calls made through a BitchatDelegate-typed reference will use static dispatch and always hit these default no-op implementations, even if a conforming type defines its own handlers. In practice, once the transport invokes these callbacks via delegate, payment packets will be dropped silently instead of reaching the app/UI.
Useful? React with 👍 / 👎.
Extends the Noise payload type layer with two new types that enable
AI service queries to be routed through the Bluetooth mesh:
NoisePayloadType additions:
0x30 dvmQuery — NIP-90 job request routed over the mesh
0x31 dvmResult — DVM job result returned through the mesh
New packet structs (Packets.swift):
DVMQueryPacket
requestID (UUID, for deduplication and routing)
kind (UInt16, NIP-90 job kind 5000-5999)
input (String, the question to ask)
maxSatoshi (UInt64, max the requester will pay)
preferredLanguage (String?, BCP-47 tag e.g. "sw", "pt-BR")
DVMResultPacket
requestID (matches originating query)
result (String, the AI response)
satsPaid (UInt64, cost paid to the Nostr DVM)
dvmPubkey (String?, for optional reputation attestation)
New BitchatDelegate extension methods (default empty implementations
for backward compatibility):
didReceiveDVMQuery(_:from:timestamp:)
didReceiveDVMResult(_:from:timestamp:)
Why this matters:
"Gateway nodes" — devices with both Bluetooth range and internet —
receive DVMQueryPackets from the mesh, call Nostr DVMs on behalf of
the requester, and return DVMResultPackets through the mesh. This
enables devices without internet (deep rural, disaster zones, offline
events) to access AI services via nearby connected neighbours.
Reference gateway implementation (Python):
https://github.com/spcpza/bitchat-gateway
Use cases demonstrated:
- CropDoctor: free AI crop disease advisor for subsistence farmers
- HealthInfo: free public health information in 50+ languages
- BitcoinTeacher: free Bitcoin/Lightning education globally
All wire format uses UInt16 BE length fields (same pattern as
LightningPaymentRequestPacket) for compatibility with the existing
TLV codec infrastructure.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Update: added DVM mesh bridge extensionI've extended this PR with two more What's new in this commitcase dvmQuery = 0x30 // NIP-90 DVM job request routed over mesh
case dvmResult = 0x31 // DVM job result returned through meshWith new TLV packet structs: The idea: gateway nodesA "gateway node" is a device with both Bluetooth range (connected to the bitchat mesh) and internet access. When it receives a
This lets devices without internet access AI services via a neighbour who happens to have connectivity. Why this matters for communities without reliable internetConsider a subsistence farmer in rural Malawi. She has bitchat. Her neighbour in the nearest town has both bitchat and mobile data. With a gateway node running:
No internet required on her end. Reference implementationThe gateway daemon (Python, cross-platform via Includes three free humanitarian DVMs:
All free (0 sats). Designed as public goods. The Python wire format in |
What this does
bitchat already detects bolt11 invoices and Cashu tokens in plain text (via
MessageFormattingEngine) and renders them as chips (PaymentChipView). This PR adds a protocol-level payment layer — structured TLV packets sent inside the existing Noise-encrypted channel, rather than embedded in chat text.Two new
NoisePayloadTypecasesBoth are carried inside
MessageType.noiseEncrypted— network observers cannot distinguish payment traffic from regular private messages.LightningPaymentRequestPacketStructured BOLT11 request with:
requestID— stable UUID for delivery receipts and UI deduplicationinvoice— full BOLT11 string (UInt16 length field, supports up to 65 KB)memo— optional human-readable descriptionamountSat— optional decoded amount so UI can display without decoding the invoiceexpiresAt— optional Unix timestamp so the UI can mark expired invoicesCashuTokenPacketCashu eCash bearer tokens — the token string is the money. Neither sender nor relay needs internet. The receiver redeems at the mint when they next have connectivity.
This enables genuinely offline Bitcoin transfers over Bluetooth mesh — valuable for remote areas, events without cell coverage, and humanitarian use cases where connectivity is unreliable.
Why structured packets over raw text?
amountSatfieldexpiresAtfieldrequestIDrequestID/transferIDWire compatibility
Unknown
NoisePayloadTypebytes are already skipped gracefully by the existing Noise dispatch — older clients compile and run without changes. The newBitchatDelegatemethods have default empty implementations for the same reason.TLV encoding
Follows the established
BitchatFilePacketpattern with UInt16 big-endian length fields (supports strings up to 65 KB — necessary since BOLT11 invoices regularly exceed 255 bytes, which rules out the 1-byte length used inPrivateMessagePacket).⚡
sensiblefield821792@getalby.com