Skip to content

Conversation

@avillagran
Copy link

Summary

  • Route Nostr mesh packets to gateway instead of displaying as encrypted gibberish in public chat
  • Add event ID deduplication to prevent multiple online devices publishing the same event to relays
  • Fix signature invalidation by signing events AFTER PoW mining completes
  • Add PoW requirement notification for offline #me messages

Changes

Core

  • MessageHandler.kt: Detect Nostr packets by header (0x7E/0x00) and route to NostrMeshGateway
  • NostrMeshGateway.kt: Add LRU deduplication cache (500 entries, 5min TTL)
  • NostrProfileManager.kt (new): Manages #me publishing with PublishResult sealed class, proper sign-after-mine flow

UI/UX

  • ChatViewModel.kt: Handle PublishResult and show system message when PoW is required offline
  • NostrAccountSheet.kt (new): Nostr account management UI
  • Enhanced image viewer with pinch-to-zoom and pan gestures
  • Various UI improvements

Tests

  • NostrMeshGatewayTest.kt (new): Unit tests for NostrMeshSerializer

Flow

Offline Device                    Online Device
     |                                 |
     | 1. Create unsigned event        |
     | 2. Mine PoW (add nonce tag)     |
     | 3. Sign mined event             |
     | 4. Serialize with 0x7E header   |
     | 5. Broadcast via mesh           |
     |-------------------------------->|
     |                                 | 6. Detect 0x7E header
     |                                 | 7. Route to NostrMeshGateway
     |                                 | 8. Validate signature & PoW
     |                                 | 9. Check dedup cache
     |                                 | 10. Publish to Nostr relays

Testing

  • Unit tests pass
  • Verified in logs: mesh packets correctly routed and published to relays

Core changes:
- Filter Nostr packets in MessageHandler to route to gateway instead of displaying in public chat
- Add event ID deduplication cache to prevent multiple online devices publishing same event
- Add PoW requirement notification for offline #me messages
- Fix signature invalidation by signing AFTER PoW mining completes

New files:
- NostrProfileManager: Manages #me channel message publishing with PublishResult sealed class
- NostrAccountSheet: UI for Nostr account management
- NostrMeshGatewayTest: Unit tests for serializer

UI/UX improvements:
- Enhanced image viewer with pinch-to-zoom and pan gestures
- Improved message components styling
- Location channels sheet updates
- About sheet enhancements
@callebtc
Copy link
Collaborator

hi. what is "Nostr mesh packets" and what is a "gateway"? could you explain the idea behind this feature?

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7afab46dc0

ℹ️ 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".

Comment on lines 533 to 546
if (!resolved && event.kind == NostrKind.METADATA && event.pubkey == pubkeyHex) {
resolved = true
try {
val profileJson = com.google.gson.JsonParser.parseString(event.content).asJsonObject
val name = profileJson.get("name")?.asString?.takeIf { it.isNotBlank() }
?: profileJson.get("display_name")?.asString?.takeIf { it.isNotBlank() }

relayManager.unsubscribe(subscriptionId)

GlobalScope.launch(Dispatchers.Main) {
onResult(name)
}
} catch (e: Exception) {
Log.w(TAG, "Failed to parse profile for $pubkeyHex: ${e.message}")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Ensure profile-name resolution cleans up on JSON parse failure

If a relay sends a metadata event whose content is not valid JSON (which happens in the wild), the handler sets resolved = true and then throws, so onResult is never called and the subscription is never unsubscribed. Because resolveNostrReferences removes pendingResolutions only in the callback, that pubkey becomes permanently “pending” and the dangling subscription stays active. This means future references to the same npub will never be resolved on that device. Consider moving resolved = true after a successful parse and/or invoking onResult(null) and unsubscribe inside the catch.

Useful? React with 👍 / 👎.

@avillagran
Copy link
Author

avillagran commented Jan 15, 2026

hi. what is "Nostr mesh packets" and what is a "gateway"? could you explain the idea behind this feature?

Hi, the mesh-to-relay gateway lets offline users post to Nostr through nearby online peers.
When you post to #me without internet, the app wraps your signed event with a 0x7E header and broadcasts it over Bluetooth. Online devices detect that header, validate signature + PoW, and publish to relays for you.
MessageHandler was treating these packets as chat messages, so the header showed as gibberish in public chat. Now it check for 0x7E first and route to NostrMeshGateway instead.
Also added dedup (so multiple online peers don't publish the same event twice).

Move resolved=true after successful parse so timeout cleanup still
runs if metadata content is invalid JSON. Fixes dangling subscriptions
and permanently pending pubkeys.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants