Overview
All keys in Sovran are deterministically derived from a 12-word BIP-39 mnemonic using standardized derivation paths:BIP-39
12-word mnemonic seed phrase (128 bits entropy)
BIP-32
Hierarchical Deterministic (HD) key derivation
NIP-06
Nostr key derivation from mnemonic
NUT-13
Cashu wallet derivation (unofficial)
Key Derivation Architecture
BIP-39 Mnemonic Generation
Sovran generates a 12-word mnemonic (128 bits of entropy) for new wallets:Secure Storage
Mnemonics are stored usingexpo-secure-store:
- iOS: Keychain Services (encrypted in Secure Enclave)
- Android: Keystore System (hardware-backed encryption)
The mnemonic never leaves secure storage except during key derivation operations.
Nostr Key Derivation (NIP-06)
Nostr keys follow NIP-06 standard derivation:Derivation Path
44': BIP-44 purpose (HD wallets)1237': Nostr coin type (registered in SLIP-0044)<account>': Account index (default: 0)0/0: Change index / Address index (always 0 for Nostr)
Implementation
Derived Key Formats
| Format | Description | Example |
|---|---|---|
| npub | Bech32 public key | npub1abc...xyz |
| nsec | Bech32 private key | nsec1def...uvw |
| pubkey | Hex public key (32 bytes) | a1b2c3d4... |
| privateKey | Raw bytes (32 bytes) | Uint8Array(32) |
Cashu Wallet Derivation
Cashu wallets use a custom derivation path (not yet standardized in NUT-13):Derivation Path
44': BIP-44 purpose129372': Cashu coin type (unofficial, may change)0': Wallet index (always 0)<account>': Account index (matches Nostr account)0/0: Change / Address (always 0)
Two-Stage Derivation
Cashu derivation is a two-stage process:- Derive child private key (32 bytes) from BIP-32 path
- Re-encode as BIP-39 mnemonic (24 words) for Cashu wallet
The Cashu mnemonic (24 words) is derived deterministically and can be re-derived from the root mnemonic at any time. It is cached in secure storage for performance.
Key Caching & Performance
To avoid expensive re-derivation, Sovran caches derived keys in secure storage:Cache Structure
Cache Types (helper/secureStorage.ts:16-22)
Cache Invalidation
Caches are invalidated when:- Root mnemonic changes (detected via hash)
- User explicitly clears secure storage
- App is uninstalled
Fast Path vs Slow Path
Fromproviders/NostrKeysProvider.tsx:300-345:
Cache Logic
Multi-Account Support
Sovran supports multiple accounts (profiles) from a single mnemonic:Account Derivation
- Unique Nostr identity (different npub/nsec)
- Separate Cashu wallet (different ecash proofs)
- Independent mint trust lists
- Isolated transaction histories
See Multi-Account for user-facing documentation.
Security Considerations
Mnemonic Entropy
- 12 words = 128 bits entropy = 2^128 possible seeds
- Cryptographically secure randomness via
crypto.getRandomValues() - Wordlist: BIP-39 English (2048 words)
Key Storage
Mnemonic stored in device secure enclave (iOS Keychain / Android Keystore)
Derived keys cached in secure storage with mnemonic hash validation
Private keys never leave secure storage except for signing operations
No keys transmitted over network
Attack Surface
Mitigations:
- Hardware-backed encryption (Secure Enclave / Keystore)
- Passcode lock (app-level protection)
- No cloud backups of keys
- Open source code (auditable)
Standards Compliance
| Standard | Purpose | Compliance |
|---|---|---|
| BIP-39 | Mnemonic generation | ✅ Full (12-word, English wordlist) |
| BIP-32 | HD key derivation | ✅ Full (@scure/bip32) |
| BIP-44 | Multi-account hierarchy | ✅ Full (purpose = 44’) |
| NIP-06 | Nostr key derivation | ✅ Full (coin type 1237’) |
| NUT-13 | Cashu deterministic secrets | ⚠️ Partial (custom path, subject to change) |
| SLIP-0044 | Coin type registry | ✅ Registered (Nostr = 1237) |
Cashu derivation path (
m/44'/129372'/0'/<account>'/0/0) is not yet standardized in NUT-13. This may change in future versions.Code References
keyDerivation.ts
Core derivation functions (NIP-06, Cashu)
secureStorage.ts
Mnemonic storage and caching
NostrKeysProvider.tsx
Key derivation orchestration
useSecureStore.ts
React hooks for secure storage
Related Documentation
Wallet Recovery
Restore wallet from seed phrase
Multi-Account
Using multiple accounts from one seed