Core structure
- Maintains all loaded Nostr keypairs in memory
- Coordinates between database (public keys) and keystore (private keys)
- Handles gift wrap decryption for private messages
- Supports multiple accounts per user
Key management
Loading keys
From src/account_manager.rs:88:- Query database for all saved public keys
- For each public key, retrieve private key from platform keystore
- Parse private key bytes into
SecretKey - Create
Keysobject combining public and private keys - Store all keys in
loaded_keysvector
Generating new keys
- Generate new random keypair using
nostr::Keys::generate() - Store private key in platform keystore using public key as identifier
- Store public key in database
pubkeystable - Add keys to in-memory
loaded_keysvector
Saving existing keys
Deleting keys
From src/account_manager.rs:121:- Public key from database
- Private key from platform keystore
- Keys from in-memory
loaded_keysvector
Validating nsec input
Secure storage integration
The AccountManager uses thekeyring crate for platform-specific secure storage:
- Linux: Secret Service API (libsecret) or file-based fallback
- macOS: Keychain API via
security-frameworkcrate - Windows: Credential Manager
- Service name:
STORAGE_NAMEconstant (“hoot”) - Username: Public key in hex format
- Secret: Raw 32-byte private key
Gift wrap unwrapping
The AccountManager handles decryption of NIP-59 gift-wrapped events:- Extract recipient public key from gift wrap’s
ptag - Find matching keypair in
loaded_keys - Decrypt gift wrap using NIP-59 protocol
- Return
UnwrappedGiftcontaining the inner rumor event
UnwrappedGift structure:
pollster::block_on() to handle async decryption in sync context.
NIP-42 authentication
From src/account_manager.rs:152:- Takes keypair, relay URL, and challenge string
- Builds kind 22242 auth event
- Signs with provided keys
- Returns ready-to-send event
AUTH challenges requiring proof of identity.
Multi-account support
The AccountManager supports multiple accounts:- All accounts loaded simultaneously into memory
- User can switch between accounts in UI
- Each account has independent key storage
- Gift wraps checked against all loaded keys
- Separate profile metadata per account
Security considerations
- Private key separation: Private keys never stored in database
- Platform keystore: Leverages OS-level security features
- In-memory only: Keys loaded into RAM, cleared on exit
- Encrypted database: Database encrypted with user password
- No key logging: Private keys excluded from debug logs
- Verification: Gift wrap sender verified against seal signature