Skip to main content

Nostr Overview

Sovran deeply integrates Nostr for identity, messaging, and social features. The app uses NDK (Nostr Development Kit) via the @nostr-dev-kit/ndk-mobile package.

What Sovran Uses Nostr For

Identity

Your wallet identity is your Nostr identity (npub/nsec derived from BIP-39 seed)

Messaging

Encrypted peer-to-peer messaging with NIP-17 gift-wrapped DMs

Social Graph

Follow/follower relationships, user profiles, reputation scores

Payment Requests

Send payment requests over Nostr (NUT-18) without Lightning

Key Derivation (NIP-06)

All Nostr keys derive from the wallet’s BIP-39 mnemonic using NIP-06: providers/NostrKeysProvider.tsx:244-378 Derivation path: m/44'/1237'/0'/0/{accountIndex} This produces:
  • Private key (32 bytes) → nsec (Bech32 encoded)
  • Public key (32 bytes) → npub (Bech32 encoded)

Key Caching

To avoid expensive re-derivation (200ms), derived keys are cached in expo-secure-store:
The cache is invalidated when the mnemonic changes (e.g., wallet restore).

NDK Initialization

providers/NostrNDKProvider.tsx:1-68 Initialization flow:
  1. Create NDKCacheAdapterSqlite with profile-specific database
    • Account 0: nostr
    • Account N: nostr-N
  2. Create NDKPrivateKeySigner from derived private key
  3. Initialize NDK with:
    • Cache adapter
    • Relay list (28 relays)
    • Signer for signing events
  4. Connect to relays (non-blocking)
components/ndk.ts:1-31

Relay Strategy

Sovran connects to 28 public relays for maximum reach:
  • purplepag.es - Profile pages
  • relay.primal.net - Primal cache
  • relay.damus.io - Damus relay
  • relay.snort.social - Snort relay
  • nos.lol - Nos relay
  • nostr.mutinywallet.com - Mutiny wallet relay
  • Plus 22 more (see components/ndk.ts:1-31)
NDK automatically:
  • Manages connections (auto-reconnect on failure)
  • Routes queries to optimal relays
  • Caches events in SQLite
  • Deduplicates events across relays

Profile Management

Fetching Profiles

Username Resolution

Sovran uses a username hierarchy:

NIP-05 Verification

NIP-05 allows linking Nostr identities to domain names (like user@domain.com):
Sovran shows a checkmark badge next to verified NIP-05 identities.

Direct Messages (NIP-17)

Sovran uses NIP-17 gift-wrapped DMs for private messaging:

Why NIP-17 over NIP-04?

NIP-17 provides stronger privacy by hiding who is messaging whom.

Message Structure (NIP-17)

utils/nip17.ts implements the NIP-17 gift wrap protocol:
Privacy properties:
  • Relays see: kind 1059, ephemeral pubkey, recipient pubkey
  • Relays cannot see: sender identity, message content, timestamp
  • Only recipient can unwrap and decrypt

Sending Messages

hooks/useNostrDirectMessage.ts handles message sending:

Receiving Messages

components/screens/UserMessagesScreen.tsx displays conversations:

Message Types

app/message/components/ defines custom message types:
  • TextMessage.tsx - Plain text messages
  • CashuTokenMessage.tsx - Inline ecash token transfers
  • PaymentMessage.tsx - Payment requests and invoices

Social Graph

Follower/Following Counts

Top Followers

The profile screen shows your most influential followers:
components/blocks/contacts/SearchResult.tsx handles Nostr profile search:

Payment Requests (NUT-18 + Nostr)

Sovran supports NUT-18 payment requests over Nostr transport:

Creating Payment Requests

Receiving Payment Requests

hooks/coco/useProcessPaymentString.ts:216-331 When a user scans a payment request:
  1. Decode to extract amount, mints, Nostr pubkey
  2. Calculate valid mints (balance check)
  3. Route to appropriate screen based on available data
  4. Show “Send to [username]” with their profile
  5. User confirms and sends ecash
  6. Token sent via NIP-17 DM to recipient
  7. Recipient auto-redeems inline token
Routing logic: hooks/coco/useProcessPaymentString.ts:241-331 This minimizes friction by skipping screens when data is already known.

Nostr Event Types

Sovran works with these Nostr event kinds:

NDK Caching

NDK caches all events in SQLite for fast retrieval:
Cache TTL is managed by NDK based on event kind:
  • Profiles: 1 hour
  • Contact lists: 1 hour
  • DMs: Permanent
  • Public notes: 5 minutes

Best Practices

Don’t trust nip05 field blindly - verify it:
Profiles may not be available immediately:
Include relay hints in NIP-17 gift wraps to improve delivery:
Always sanitize profile data before displaying:
NDK operations may fail when offline:

Nostr + Cashu Integration

The power of Sovran comes from combining Nostr identity with Cashu ecash:
This flow:
  • ✅ No Lightning invoices needed
  • ✅ Works offline (async messaging)
  • ✅ Private (NIP-17 encryption + Cashu blinding)
  • ✅ Permissionless (no accounts, no KYC)

Architecture Overview

See how Nostr fits into the overall architecture

Cashu Integration

Learn how payment requests integrate with Cashu operations