Skip to content

Conversation

@grunch
Copy link
Member

@grunch grunch commented Feb 8, 2026

Fetch Nostr kind 0 profile metadata (name, picture, website, about) for each Mostro node, giving users meaningful names and avatars instead of raw pubkeys.

  • Add fetchAllNodeMetadata() for batch fetching in a single round trip
  • Add fetchNodeMetadata() for per-node refresh
  • Deduplicate events across relays, keeping latest by createdAt
  • Verify event signatures before applying metadata
  • Sanitize picture/website URLs (https-only)
  • Guard against disposed notifier after async gaps
  • Fire-and-forget on startup via unawaited(), never blocks init
  • Add MostroNode.clear sentinel for explicit field clearing
  • Add 12 new tests (49 total across P1+P2)
  • Update MULTI_MOSTRO_SUPPORT.md with phase status and implementation details

Summary by CodeRabbit

Release Notes

  • New Features

    • Node metadata is now automatically fetched and cached at app startup, providing complete node information.
    • Enhanced metadata management with improved field handling and explicit clearing capabilities.
    • URL validation ensures only secure HTTPS connections are stored.
  • Improvements

    • Refined error handling for metadata operations with comprehensive logging and resilience.

Fetch Nostr kind 0 profile metadata (name, picture, website, about) for
each Mostro node, giving users meaningful names and avatars instead of
raw pubkeys.

- Add fetchAllNodeMetadata() for batch fetching in a single round trip
- Add fetchNodeMetadata() for per-node refresh
- Deduplicate events across relays, keeping latest by createdAt
- Verify event signatures before applying metadata
- Sanitize picture/website URLs (https-only)
- Guard against disposed notifier after async gaps
- Fire-and-forget on startup via unawaited(), never blocks init
- Add MostroNode.clear sentinel for explicit field clearing
- Add 12 new tests (49 total across P1+P2)
- Update MULTI_MOSTRO_SUPPORT.md with phase status and implementation details
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 8, 2026

Walkthrough

The changes introduce metadata fetching capabilities for Mostro nodes via Nostr Kind 0 events, adding batch and single-node methods to MostroNodesNotifier with verification, sanitization, and in-memory storage. A new MostroNode.clear sentinel enables explicit field clearing in withMetadata(). Metadata fetching is triggered asynchronously during app initialization without blocking. Phase 2 implementation details are documented with comprehensive test coverage.

Changes

Cohort / File(s) Summary
Model Enhancements
lib/features/mostro/mostro_node.dart, docs/MULTI_MOSTRO_SUPPORT.md
Added MostroNode.clear sentinel for explicit field clearing in withMetadata(). Updated documentation to reflect Phase 2 completion for metadata fetching and field clearing semantics.
Metadata Fetching Implementation
lib/features/mostro/mostro_nodes_notifier.dart
Added public fetchAllNodeMetadata() and fetchNodeMetadata(String pubkey) methods with Kind 0 event fetching, verification, JSON parsing, URL sanitization (https only), and error handling via logging. Includes private helpers _applyMetadataFromEvent() and _sanitizeUrl().
App Integration
lib/shared/providers/app_init_provider.dart
Integrated fire-and-forget fetchAllNodeMetadata() call during app initialization after mostroNodes setup completes, without blocking initialization flow.
Test Coverage
test/features/mostro/mostro_node_test.dart, test/features/mostro/mostro_nodes_notifier_test.dart
Added tests for MostroNode.clear sentinel behavior and comprehensive metadata fetching scenarios (batch/single fetch, deduplication, URL validation, malformed JSON, network errors, unverified events).

Sequence Diagram(s)

sequenceDiagram
    participant App as App Initialization
    participant Notifier as MostroNodesNotifier
    participant NostrService as NostrService
    participant NostrNetwork as Nostr Network
    participant State as In-Memory State

    App->>Notifier: fetchAllNodeMetadata() [fire-and-forget]
    Notifier->>NostrService: subscribe to Kind 0 events
    NostrService->>NostrNetwork: fetch metadata for all node pubkeys
    NostrNetwork-->>NostrService: Kind 0 events
    Notifier->>Notifier: verify event signature
    Notifier->>Notifier: parse JSON metadata
    Notifier->>Notifier: sanitize URLs (https only)
    Notifier->>State: updateNodes() with withMetadata()
    Note over State: Merge metadata into existing nodes<br/>or clear fields via MostroNode.clear
    Notifier-->>App: complete (async, non-blocking)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • AndreaDiazCorreia
  • BraCR10
  • Catrya

Poem

🐰 ✨ A sentinel so clear,

Fields that vanish without fear,

Metadata fetched from the Nostr sky,

Nodes adorned as moments fly,

HTTPS guards what comes our way—

Phase Two blooms bright today! 🌱

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Feat: Add kind 0 metadata fetching for Mostro nodes (P2)' directly and specifically summarizes the main change: implementing metadata fetching via Nostr kind 0 events for Mostro nodes as part of Phase 2.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch phase-2-kind-0-fetching

No actionable comments were generated in the recent review. 🎉

🧹 Recent nitpick comments
lib/features/mostro/mostro_nodes_notifier.dart (1)

235-252: Consider guarding against null content before jsonDecode.

Line 241: jsonDecode(event.content ?? '') will throw a FormatException on the empty string, which is caught. This works, but it triggers an unnecessary error log for events with null content. A small early return would be cleaner and avoid a misleading log entry.

✨ Proposed improvement
 void _applyMetadataFromEvent(NostrEvent event) {
   try {
     if (!event.isVerified()) {
       logger.w('Ignoring unverified kind 0 event for ${event.pubkey}');
       return;
     }
+    if (event.content == null || event.content!.isEmpty) return;
     final json = jsonDecode(event.content ?? '') as Map<String, dynamic>;
test/features/mostro/mostro_nodes_notifier_test.dart (1)

422-435: Dead updateNodeMetadata call in test helper.

Line 426 calls updateNodeMetadata for a randomly-generated testKeyPair.public that never exists in the initial state (only trusted nodes are loaded). It silently no-ops, and the if block on line 428 always adds the node. The updateNodeMetadata call is misleading — consider removing it for clarity.

✨ Simplified helper
       Future<MostroNodesNotifier> createNotifierWithTestNode() async {
         final notifier = createNotifier();
         await notifier.init();
-        // Add a node matching the test keypair's pubkey
-        notifier.updateNodeMetadata(testKeyPair.public, name: 'Test Node');
-        // Ensure the node exists in state
-        if (!notifier.state.any((n) => n.pubkey == testKeyPair.public)) {
-          notifier.state = [
-            ...notifier.state,
-            MostroNode(pubkey: testKeyPair.public, name: 'Test Node'),
-          ];
-        }
+        // Add a node matching the test keypair's pubkey
+        notifier.state = [
+          ...notifier.state,
+          MostroNode(pubkey: testKeyPair.public, name: 'Test Node'),
+        ];
         return notifier;
       }

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Member

@Catrya Catrya left a comment

Choose a reason for hiding this comment

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

tACK

Catrya

This comment was marked as duplicate.

@grunch grunch merged commit 26a041c into main Feb 9, 2026
2 checks passed
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