NUT-27: Nostr Mint Backup¶
optional
This document describes a method for wallets to backup their mint list as Nostr events on one or more relays. This allows users to restore their mint configuration across different devices or wallet instances using their mnemonic seed phrase.
Description¶
Wallet users can optionally enable backup of their mint list to Nostr relays. The mint list is encrypted using NIP-44 encryption and published as addressable (parameterized replaceable) Nostr events. The backup keys are deterministically derived from the wallet's mnemonic seed phrase, ensuring that the same seed can restore the mint list on any compatible wallet.
Key Derivation¶
The private key for Nostr mint backup is derived from the wallet's mnemonic seed phrase using the following method:
- Generate a 64-byte seed from the mnemonic using BIP39
mnemonicToSeedSync(mnemonic) - Create a domain separator:
UTF-8("cashu-mint-backup") - Concatenate the seed and domain separator:
combined_data = seed || domain_separator - Hash the combined data:
private_key = SHA256(combined_data) - Derive the public key:
public_key = secp256k1_generator * private_key
Example Implementation¶
import { mnemonicToSeedSync } from "@scure/bip39";
import { sha256 } from "@noble/hashes/sha256";
import { bytesToHex } from "@noble/hashes/utils";
import { getPublicKey } from "nostr-tools";
function deriveMintBackupKeys(mnemonic: string): { privateKeyHex: string; publicKeyHex: string } {
// Derive seed from mnemonic
const seed: Uint8Array = mnemonicToSeedSync(mnemonic);
const domainSeparator = new TextEncoder().encode("cashu-mint-backup");
const combinedData = new Uint8Array(seed.length + domainSeparator.length);
combinedData.set(seed);
combinedData.set(domainSeparator, seed.length);
// Use SHA256 of combined data as private key
const privateKeyBytes = sha256(combinedData);
const privateKeyHex = bytesToHex(privateKeyBytes);
const publicKeyHex = getPublicKey(privateKeyBytes);
return { privateKeyHex, publicKeyHex };
}
Nostr Event Specification¶
Event Kind¶
The mint backup uses Nostr event kind 30078 (addressable event) as specified in NIP-78.
Event Structure¶
{
"kind": 30078,
"content": "<encrypted_backup_data>",
"tags": [
["d", "mint-list"],
["client", "cashu.me"]
],
"created_at": <timestamp>,
"pubkey": "<mint_backup_public_key>"
}
Event Fields¶
kind: MUST be30078(addressable event)content: Encrypted mint backup data using NIP-44 encryptiontags:dtag with value"mint-list"(addressable event identifier)clienttag with the client name (optional but recommended)created_at: Unix timestamp of the backup creationpubkey: The derived public key for mint backup
Backup Data Format¶
The plaintext data that gets encrypted in the content field has the following JSON structure:
{
"mints": ["<mint_url_1>", "<mint_url_2>", ...],
"timestamp": <unix_timestamp>
}
Encryption¶
The backup data is encrypted using NIP-44 v2 encryption:
- Generate a conversation key using NIP-44:
conversation_key = nip44.v2.utils.getConversationKey(private_key, public_key) - Encrypt the JSON string:
encrypted_content = nip44.v2.encrypt(JSON.stringify(backup_data), conversation_key) - Use the encrypted content as the event's
contentfield
Note: The same private and public key pair is used for both sides of the conversation key generation, creating a self-encrypted message.
Wallet Implementation¶
Backup Flow¶
- Enable Backup: User enables Nostr mint backup in wallet settings
- Key Derivation: Wallet derives backup keys from the mnemonic seed
- Data Preparation: Wallet collects current mint URLs and creates backup data structure
- Encryption: Backup data is encrypted using NIP-44 v2
- Event Creation: Create Nostr event with encrypted content and appropriate tags
- Publishing: Sign and publish the event to configured Nostr relays
Restore Flow¶
- Key Derivation: Derive backup keys from the provided mnemonic seed
- Event Discovery: Search for events with:
kind:30078authors:[<derived_public_key>]#d:["mint-list"]- Decryption: Decrypt event content using NIP-44 v2
- Mint Addition: Present discovered mints to user for selective restoration
Example Backup Event¶
{
"id": "...",
"kind": 30078,
"content": "AgAQJBIqWKEEBjGHfoozgDBgVQAAAAC5lQJtJQV_psHckN2KxWsUq7Q-B_twv4M2P3_vJrPMWg",
"tags": [
["d", "mint-list"],
["client", "cashu.me"]
],
"created_at": 1703721600,
"pubkey": "02bc9097997d81afb2cc7346b5e4345a9346bd2a506eb7958598a72f0cf85163ea",
"sig": "..."
}
When decrypted, the content would reveal:
{
"mints": ["https://mint.example.com", "https://another-mint.org"],
"timestamp": 1703721600
}