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:NDK Initialization
providers/NostrNDKProvider.tsx:1-68 Initialization flow:-
Create
NDKCacheAdapterSqlitewith profile-specific database- Account 0:
nostr - Account N:
nostr-N
- Account 0:
-
Create
NDKPrivateKeySignerfrom derived private key -
Initialize NDK with:
- Cache adapter
- Relay list (28 relays)
- Signer for signing events
- Connect to relays (non-blocking)
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)
- 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 (likeuser@domain.com):
Direct Messages (NIP-17)
Sovran uses NIP-17 gift-wrapped DMs for private messaging:Why NIP-17 over NIP-04?
| Feature | NIP-04 | NIP-17 |
|---|---|---|
| Encryption | NIP-04 (shared secret) | NIP-44 (XChaCha20-Poly1305) |
| Metadata leak | Sender/recipient visible | Hidden via gift wrap |
| Repudiation | Non-repudiable | Repudiable (ephemeral keys) |
| Relay hints | No | Yes (improves routing) |
Message Structure (NIP-17)
utils/nip17.ts implements the NIP-17 gift wrap protocol:- 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:Contact Search
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:- Decode to extract amount, mints, Nostr pubkey
- Calculate valid mints (balance check)
- Route to appropriate screen based on available data
- Show “Send to [username]” with their profile
- User confirms and sends ecash
- Token sent via NIP-17 DM to recipient
- Recipient auto-redeems inline token
Nostr Event Types
Sovran works with these Nostr event kinds:| Kind | Name | Purpose |
|---|---|---|
| 0 | Profile | User metadata (name, picture, about) |
| 1 | Text note | Public posts (feed) |
| 3 | Contact list | Following list |
| 4 | DM (deprecated) | Old encrypted DMs (legacy) |
| 13 | Seal | NIP-17 encrypted rumor |
| 14 | Private DM | NIP-17 actual message content |
| 1059 | Gift wrap | NIP-17 outer encryption layer |
| 10000 | Mute list | Blocked users |
| 38000 | Cashu mint | Mint recommendations (KYM) |
NDK Caching
NDK caches all events in SQLite for fast retrieval:- Profiles: 1 hour
- Contact lists: 1 hour
- DMs: Permanent
- Public notes: 5 minutes
Best Practices
Always verify NIP-05 before showing badges
Always verify NIP-05 before showing badges
Don’t trust
nip05 field blindly - verify it:Handle profile loading gracefully
Handle profile loading gracefully
Profiles may not be available immediately:
Use relay hints for DMs
Use relay hints for DMs
Include relay hints in NIP-17 gift wraps to improve delivery:
Sanitize user-generated content
Sanitize user-generated content
Always sanitize profile data before displaying:
Handle offline gracefully
Handle offline gracefully
NDK operations may fail when offline:
Nostr + Cashu Integration
The power of Sovran comes from combining Nostr identity with Cashu ecash:- ✅ No Lightning invoices needed
- ✅ Works offline (async messaging)
- ✅ Private (NIP-17 encryption + Cashu blinding)
- ✅ Permissionless (no accounts, no KYC)
Related Documentation
Architecture Overview
See how Nostr fits into the overall architecture
Cashu Integration
Learn how payment requests integrate with Cashu operations