Files

46 KiB

Amber Integration Plan: End-User Experience

⚠️ Design-only research document. This describes a planned user experience, not a shipped feature. The current system is a research prototype; it links PQ keys to an identity and anchors that link in Bitcoin, but does not by itself make the identity post-quantum secure.

Overview

This document describes the complete end-to-end user experience for migrating a Nostr identity to post-quantum security using the PQ web app and Amber signer. There are two paths depending on how the user's account was created in Amber:

  • Path A (Simple): User logged into Amber with a seed phrase (mnemonic). Amber already has the seed. PQ keys are derived from that same seed. No second account needed.
  • Path B (Migration): User logged into Amber with a raw nsec. No seed phrase exists. A new seed must be generated and linked to the old identity via a two-account migration event.

The web app automatically detects which path applies by asking Amber for the account's seed words (or checking if they're empty).


Critical Fact: Amber Does Not Tie Raw nsec to Seed Phrases

When a user enters a raw nsec in Amber, the seed words field is set to null (AccountStateViewModel.kt):

LocalPreferences.updatePrefsForLogin(
    Amber.instance, account,
    keyPair.pubKey.toHexKey(),
    keyPair.privKey!!.toHexKey(),
    null  // ← seedWords is null for raw nsec login
)
Login method nsec stored Seed words stored Relationship
Raw nsec (nsec1...) ✅ null (empty) None — nsec is random, no seed
Mnemonic (12/24 words) ✅ (derived from seed via NIP-06) ✅ nsec IS derived from seed

A raw nsec is 32 random bytes. You cannot derive a seed phrase from it (preimage problem — impossible). You cannot derive the existing nsec from a new seed (different key). The only bridge between them is a signed migration event.


Prerequisites

  • Amber installed on Android phone (from F-Droid, GitHub Releases, or ZapStore)
  • Existing Nostr account already in Amber
  • Desktop or mobile browser to access the PQ web app

Phase 1: Connect to the PQ Web App (Both Paths)

Screen 1: Landing Page

┌─────────────────────────────────────────────────────┐
│                                                     │
│         🔒 Post-Quantum Nostr Migration             │
│                                                     │
│   Make your Nostr identity quantum-resistant.       │
│   Your followers stay. Your npub doesn't change.    │
│                                                     │
│   ┌─────────────────────────────────────┐           │
│   │   Connect with Amber                │           │
│   └─────────────────────────────────────┘           │
│                                                     │
│   ┌─────────────────────────────────────┐           │
│   │   Connect with browser extension    │           │
│   └─────────────────────────────────────┘           │
│                                                     │
│   How it works  |  FAQ  |  Source code              │
│                                                     │
└─────────────────────────────────────────────────────┘

User action: Taps "Connect with Amber"

Behind the scenes:

  • Web app generates a random NIP-46 connection keypair
  • Web app displays a nostrconnect:// URI or QR code
  • Web app opens a WebSocket to a relay, listening for Amber's response

Screen 2: Connect to Amber

┌─────────────────────────────────────────────────────┐
│                                                     │
│   Connect Amber                                     │
│                                                     │
│   Scan this QR code with Amber, or open this        │
│   link on your phone:                               │
│                                                     │
│   ┌───────────────────┐                             │
│   │   ▓▓▓▓▓▓▓▓▓▓▓▓▓   │                             │
│   │   ▓▓▓▓▓▓▓▓▓▓▓▓▓   │                             │
│   │   ▓▓▓▓▓▓▓▓▓▓▓▓▓   │                             │
│   └───────────────────┘                             │
│                                                     │
│   nostrconnect://...                                │
│                                                     │
│   Waiting for Amber...                              │
│   ●●○○○                                             │
│                                                     │
└─────────────────────────────────────────────────────┘

User action: Opens the link on their phone (or scans QR code)

On Amber (Phone): Connection Request

┌─────────────────────────────────────────────────────┐
│                                                     │
│   pq-nostr.app wants to connect                     │
│                                                     │
│   This app is requesting to:                        │
│   • Read your public key                            │
│   • Sign events                                     │
│   • Encrypt and decrypt messages                    │
│                                                     │
│   Account: npub1abc... (Account #1)                 │
│                                                     │
│   ┌─────────────┐    ┌─────────────┐                │
│   │   Approve   │    │   Reject    │                │
│   └─────────────┘    └─────────────┘                │
│                                                     │
└─────────────────────────────────────────────────────┘

User action: Taps "Approve"

Behind the scenes:

  • Amber sends a NIP-46 connect response to the web app via the relay
  • Web app now has a NIP-46 session with Amber
  • Web app requests get_public_key → receives the user's npub

Screen 3: Account Detected — Path Selection

The web app asks Amber if the account has seed words. This determines which path to show.

Behind the scenes:

  • Web app requests seed words from Amber (via a new NIP-46 method, or by asking the user to check Amber's backup screen)
  • If seed words exist → Path A (Simple)
  • If seed words are empty → Path B (Migration)

Path A: Simple Path (User Has Seed Phrase in Amber)

This path applies to users who logged into Amber with a 12/24-word mnemonic. Amber already stores their seed phrase. PQ keys are derived from that same seed. No second account needed.

Screen A1: Seed Phrase Retrieval

┌─────────────────────────────────────────────────────┐
│                                                     │
│   ✅ Connected to Amber                             │
│                                                     │
│   Your identity: npub1abc...xyz                     │
│   Account name: Alice                               │
│   Seed phrase: ✅ Found in Amber                    │
│                                                     │
│   Great! Your account already has a seed phrase.    │
│   We can derive quantum-resistant keys from it      │
│   without creating a new account.                   │
│                                                     │
│   To proceed, we need your seed phrase to           │
│   derive the post-quantum keys in your browser.     │
│   The seed phrase will NOT be sent to any server.   │
│                                                     │
│   Option 1: Export from Amber                       │
│   Open Amber → Account → Backup → Show Seed Words   │
│   Then type or paste them below.                    │
│                                                     │
│   ┌─────────────────────────────────────────┐       │
│   │  Enter your 12/24 seed words here...    │       │
│   │                                         │       │
│   └─────────────────────────────────────────┘       │
│                                                     │
│   ┌─────────────────────────────────────┐           │
│   │   Continue                           │           │
│   └─────────────────────────────────────┘           │
│                                                     │
└─────────────────────────────────────────────────────┘

User action: Opens Amber's backup screen, copies seed words, pastes into web app

Behind the scenes:

  • Seed phrase is handled entirely client-side (WASM), never transmitted
  • Web app validates the mnemonic (BIP39 checksum)
  • Web app verifies the derived secp256k1 pubkey matches the connected Amber account

Screen A2: Generating Quantum Keys

┌─────────────────────────────────────────────────────┐
│                                                     │
│   Generating Quantum Keys...                        │
│                                                     │
│   Deriving post-quantum keys from your seed...      │
│                                                     │
│   ✅ ML-DSA-65 (Dilithium) signature key            │
│      Public key: 1952 bytes                         │
│                                                     │
│   ✅ SLH-DSA-128s (SPHINCS+) signature key          │
│      Public key: 32 bytes                           │
│                                                     │
│   ✅ ML-KEM-768 (Kyber) encryption key              │
│      Public key: 1184 bytes                         │
│                                                     │
│   All keys derived from your seed phrase.           │
│   Same seed → same keys, every time.                │
│                                                     │
│   ┌─────────────────────────────────────┐           │
│   │   Continue                           │           │
│   └─────────────────────────────────────┘           │
│                                                     │
└─────────────────────────────────────────────────────┘

Behind the scenes:

  • Web app uses liboqs-wasm to generate PQ keypairs deterministically from the BIP39 seed
  • PQ private keys exist only in browser memory (WASM), never sent anywhere

Screen A3: Signing the NIP-QR Event

┌─────────────────────────────────────────────────────┐
│                                                     │
│   Signing the Migration Event                       │
│                                                     │
│   PQ signatures (done in browser):                  │
│   ✅ ML-DSA-65 signature                            │
│   ✅ SLH-DSA-128s signature                         │
│                                                     │
│   Amber signature (need your approval):             │
│   ⏳ Signing the NIP-QR event...                    │
│                                                     │
│   Please approve the signing request in Amber.      │
│                                                     │
└─────────────────────────────────────────────────────┘

Behind the scenes:

  • Web app constructs the link statement:
    "Identity <npub> is linked to the following PQ keys,
     all derived from the same BIP39 seed. This link is
     established pre-quantum."
    
  • Web app PQ-signs the statement with each PQ private key (client-side WASM)
  • Web app sends a signEvent request to Amber to sign the NIP-QR event

On Amber (Phone): Sign NIP-QR Event

┌─────────────────────────────────────────────────────┐
│                                                     │
│   pq-nostr.app wants to sign                        │
│                                                     │
│   Account: npub1abc...xyz                           │
│                                                     │
│   Event kind: <NIP-QR kind>                         │
│                                                     │
│   This is your quantum migration event.             │
│   It links your identity to quantum-resistant       │
│   keys derived from your seed phrase.               │
│                                                     │
│   ┌─────────────┐    ┌─────────────┐                │
│   │   Approve   │    │   Reject    │                │
│   └─────────────┘    └─────────────┘                │
│                                                     │
└─────────────────────────────────────────────────────┘

User action: Taps "Approve"

Behind the scenes:

  • Amber signs the NIP-QR event with the secp256k1 private key
  • Since the secp256k1 key and PQ keys are all derived from the same seed, this single signature links them all
  • No second account needed — the existing account's signature is sufficient

Screen A4: Publishing → Go to Phase 5

The web app publishes the event and requests OpenTimestamps (see Phase 5 below).


Path B: Migration Path (User Has Raw nsec, No Seed Phrase)

This path applies to users who logged into Amber with a raw nsec. No seed phrase exists. A new seed must be generated and linked to the old identity via a two-account migration.

Screen B1: Migration Required

┌─────────────────────────────────────────────────────┐
│                                                     │
│   ✅ Connected to Amber                             │
│                                                     │
│   Your identity: npub1abc...xyz                     │
│   Account name: Alice                               │
│   Seed phrase: ❌ Not found                         │
│                                                     │
│   Your account was created with a raw private key   │
│   (nsec), not a seed phrase. To make your identity  │
│   quantum-resistant, we need to:                    │
│                                                     │
│   1. Generate a new seed phrase (your quantum-safe  │
│      backup)                                        │
│   2. Add it to Amber as a second account            │
│   3. Link your old identity to the new account      │
│      and its quantum keys                           │
│                                                     │
│   Your npub won't change. Your followers stay.      │
│                                                     │
│   ┌─────────────────────────────────────┐           │
│   │   Start Migration                    │           │
│   └─────────────────────────────────────┘           │
│                                                     │
└─────────────────────────────────────────────────────┘

User action: Taps "Start Migration"

Screen B2: Generate Seed Phrase

┌─────────────────────────────────────────────────────┐
│                                                     │
│   Step 1 of 4: Your Quantum-Safe Backup             │
│                                                     │
│   We're generating a new seed phrase. This will     │
│   become your quantum-safe root of trust.           │
│   Write it down on paper. Never store it            │
│   digitally. Never share it with anyone.            │
│                                                     │
│   ┌─────────────────────────────────────────┐       │
│   │                                         │       │
│   │   1. apple     7.  river               │       │
│   │   2. garden    8.  bridge              │       │
│   │   3. forest    9.  mountain            │       │
│   │   4. ocean    10.  sunset              │       │
│   │   5. desert   11.  valley              │       │
│   │   6. meadow   12.  horizon             │       │
│   │                                         │       │
│   └─────────────────────────────────────────┘       │
│                                                     │
│   📋 Copy    🖨️ Print                               │
│                                                     │
│   ☐ I have written down my seed phrase              │
│                                                     │
│   ┌─────────────────────────────────────┐           │
│   │   Continue                           │           │
│   └─────────────────────────────────────┘           │
│                                                     │
└─────────────────────────────────────────────────────┘

Behind the scenes:

  • Web app generates 128 bits of random entropy (crypto.getRandomValues)
  • Web app converts entropy to 12-word BIP39 mnemonic (client-side, WASM)
  • Seed phrase exists only in browser memory — never sent to any server
  • Web app derives the BIP39 seed: PBKDF2-HMAC-SHA512(mnemonic, "mnemonic", 2048)
  • Web app derives Account #2's secp256k1 private key via NIP-06: m/44'/1237'/0'/0/0

User action: Writes down seed phrase, checks the box, taps "Continue"

Screen B3: Verify Seed Phrase

┌─────────────────────────────────────────────────────┐
│                                                     │
│   Step 1 of 4: Verify Your Backup                   │
│                                                     │
│   Type word #3, #7, and #11 to confirm you          │
│   wrote it down correctly.                          │
│                                                     │
│   Word #3:  [ forest          ]                     │
│   Word #7:  [ river           ]                     │
│   Word #11: [ valley          ]                     │
│                                                     │
│   ┌─────────────────────────────────────┐           │
│   │   Verify                             │           │
│   └─────────────────────────────────────┘           │
│                                                     │
└─────────────────────────────────────────────────────┘

User action: Types the three words, taps "Verify"

Screen B4: Import Seed into Amber

┌─────────────────────────────────────────────────────┐
│                                                     │
│   Step 2 of 4: Add to Amber                         │
│                                                     │
│   You need to add this seed phrase to Amber as      │
│   a new account. This creates Account #2 —          │
│   your quantum-safe signing identity.               │
│                                                     │
│   On your phone, in Amber:                          │
│                                                     │
│   1. Tap the account switcher (top left)            │
│   2. Tap "+ Add Account"                            │
│   3. Select "Recovery Phrase"                       │
│   4. Enter your 12 words                            │
│   5. Tap "Login"                                    │
│                                                     │
│   ┌─────────────────────────────────────────┐       │
│   │                                         │       │
│   │   Account #2 npub:                      │       │
│   │   npub1def...uvw                        │       │
│   │                                         │       │
│   └─────────────────────────────────────────┘       │
│                                                     │
│   ☐ I've added this account to Amber                │
│                                                     │
│   ┌─────────────────────────────────────┐           │
│   │   Continue                           │           │
│   └─────────────────────────────────────┘           │
│                                                     │
└─────────────────────────────────────────────────────┘

Behind the scenes:

  • Web app displays Account #2's npub (derived from the seed via NIP-06)
  • User needs to manually import the seed into Amber as a new account

On Amber (Phone): Add Account

┌─────────────────────────────────────────────────────┐
│                                                     │
│   Add Account                                       │
│                                                     │
│   ┌─────────────┐    ┌─────────────┐                │
│   │  Private Key │    │ Recovery    │                │
│   │              │    │  Phrase     │                │
│   └─────────────┘    └─────────────┘                │
│                                                     │
└─────────────────────────────────────────────────────┘

User action: Taps "Recovery Phrase", enters 12 words, taps "Login"

Behind the scenes:

  • Amber derives secp256k1 key from mnemonic via NIP-06
  • Amber stores the nsec AND seed words in encrypted storage
  • Amber switches to Account #2

Screen B5: Connect Account #2 to Web App

┌─────────────────────────────────────────────────────┐
│                                                     │
│   Step 2 of 4: Connect Account #2                   │
│                                                     │
│   We need Amber to sign with Account #2 as well.    │
│                                                     │
│   In Amber, switch to Account #2 (npub1def...uvw),  │
│   then approve the connection request.              │
│                                                     │
│   ┌───────────────────┐                             │
│   │   ▓▓▓▓▓▓▓▓▓▓▓▓▓   │                             │
│   │   ▓▓▓▓▓▓▓▓▓▓▓▓▓   │                             │
│   │   ▓▓▓▓▓▓▓▓▓▓▓▓▓   │                             │
│   └───────────────────┘                             │
│                                                     │
│   nostrconnect://...                                │
│                                                     │
│   Waiting for Amber (Account #2)...                 │
│   ●●○○○                                             │
│                                                     │
└─────────────────────────────────────────────────────┘

User action: Switches to Account #2 in Amber, approves connection

Behind the scenes:

  • Web app opens a second NIP-46 session for Account #2
  • Web app now has signing access to both Account #1 and Account #2 via Amber

Screen B6: Generating Quantum Keys

┌─────────────────────────────────────────────────────┐
│                                                     │
│   Step 3 of 4: Generating Quantum Keys              │
│                                                     │
│   Deriving post-quantum keys from your seed...      │
│                                                     │
│   ✅ ML-DSA-65 (Dilithium) signature key            │
│      Public key: 1952 bytes                         │
│                                                     │
│   ✅ SLH-DSA-128s (SPHINCS+) signature key          │
│      Public key: 32 bytes                           │
│                                                     │
│   ✅ ML-KEM-768 (Kyber) encryption key              │
│      Public key: 1184 bytes                         │
│                                                     │
│   All keys derived from your seed phrase.           │
│   Same seed → same keys, every time.                │
│                                                     │
│   ┌─────────────────────────────────────┐           │
│   │   Continue                           │           │
│   └─────────────────────────────────────┘           │
│                                                     │
└─────────────────────────────────────────────────────┘

Behind the scenes:

  • Web app uses liboqs-wasm to generate PQ keypairs deterministically from the BIP39 seed
  • PQ private keys exist only in browser memory (WASM), never sent anywhere

Screen B7: Signing — Account #2 Signs the PQ Key Statement

┌─────────────────────────────────────────────────────┐
│                                                     │
│   Step 3 of 4: Signing                              │
│                                                     │
│   PQ signatures (done in browser):                  │
│   ✅ ML-DSA-65 signature                            │
│   ✅ SLH-DSA-128s signature                         │
│                                                     │
│   Amber signatures (need your approval):            │
│   ⏳ Account #2 signs the PQ key statement...       │
│                                                     │
│   Please approve the signing request in Amber.      │
│   (Make sure Account #2 is active in Amber)         │
│                                                     │
└─────────────────────────────────────────────────────┘

Behind the scenes:

  • Web app constructs the link statement:
    "Identity <npub1> is migrating to successor <npub2>.
     All PQ keys listed below are derived from the same
     BIP39 seed as <npub2>. This link is established
     pre-quantum."
    
  • Web app PQ-signs the statement with ML-DSA-65 and SLH-DSA-128s private keys (client-side WASM)
  • Web app sends a signEvent request to Amber (Account #2) to sign the statement

On Amber (Phone): Sign with Account #2

┌─────────────────────────────────────────────────────┐
│                                                     │
│   pq-nostr.app wants to sign                        │
│                                                     │
│   Account: npub1def...uvw (Account #2)              │
│                                                     │
│   Content:                                          │
│   "Identity npub1abc... is migrating to             │
│    successor npub1def... All PQ keys..."            │
│                                                     │
│   ┌─────────────┐    ┌─────────────┐                │
│   │   Approve   │    │   Reject    │                │
│   └─────────────┘    └─────────────┘                │
│                                                     │
└─────────────────────────────────────────────────────┘

User action: Taps "Approve"

Screen B8: Signing — Account #1 Signs the Migration Event

┌─────────────────────────────────────────────────────┐
│                                                     │
│   Step 3 of 4: Signing                              │
│                                                     │
│   ✅ ML-DSA-65 signature (browser)                  │
│   ✅ SLH-DSA-128s signature (browser)               │
│   ✅ Account #2 signature (Amber)                   │
│                                                     │
│   ⏳ Account #1 signs the migration event...        │
│                                                     │
│   Please switch to Account #1 in Amber and          │
│   approve the signing request.                      │
│                                                     │
└─────────────────────────────────────────────────────┘

User action: Switches to Account #1 in Amber

On Amber (Phone): Sign with Account #1

┌─────────────────────────────────────────────────────┐
│                                                     │
│   pq-nostr.app wants to sign                        │
│                                                     │
│   Account: npub1abc...xyz (Account #1)              │
│                                                     │
│   Event kind: <NIP-QR kind>                         │
│                                                     │
│   This is your quantum migration event.             │
│   It links your current identity to                 │
│   quantum-resistant keys.                           │
│                                                     │
│   ┌─────────────┐    ┌─────────────┐                │
│   │   Approve   │    │   Reject    │                │
│   └─────────────┘    └─────────────┘                │
│                                                     │
└─────────────────────────────────────────────────────┘

User action: Taps "Approve"

Behind the scenes:

  • Amber signs the NIP-QR event with Account #1's secp256k1 private key (the old nsec)
  • This is the critical signature — the old identity authorizing the migration
  • The event is now fully signed: Account #1's sig + Account #2's sig + PQ sigs

Phase 5: Publish and Timestamp (Both Paths)

Screen 9: Publishing

┌─────────────────────────────────────────────────────┐
│                                                     │
│   Step 4 of 4: Publishing                           │
│                                                     │
│   Publishing migration event to relays...           │
│                                                     │
│   ✅ wss://relay.damus.io                           │
│   ✅ wss://nos.lol                                  │
│   ✅ wss://relay.primal.net                         │
│                                                     │
│   Requesting OpenTimestamps attestation...          │
│   ✅ Anchored to Bitcoin block 890,123              │
│                                                     │
│   ┌─────────────────────────────────────┐           │
│   │   View Migration Event               │           │
│   └─────────────────────────────────────┘           │
│                                                     │
└─────────────────────────────────────────────────────┘

Behind the scenes:

  • Web app assembles the complete NIP-QR event with all signatures
  • Web app publishes the event to multiple relays via WebSocket
  • Web app submits the event hash to OpenTimestamps (NIP-03)
  • OpenTimestamps anchors the event to a Bitcoin block, proving it existed at this time
  • This prevents a future quantum attacker from backdating a fraudulent migration event

Screen 10: Success

┌─────────────────────────────────────────────────────┐
│                                                     │
│              🎉 Migration Complete!                 │
│                                                     │
│   Your Nostr identity is now quantum-resistant.     │
│                                                     │
│   What happened:                                    │
│   • Your identity (npub1abc...xyz) is now linked    │
│     to post-quantum keys                            │
│   • The link is timestamped on the Bitcoin          │
│     blockchain (cannot be forged retroactively)     │
│   • Your followers, NIP-05, and social graph        │
│     are unchanged                                   │
│                                                     │
│   What to do now:                                   │
│   • Keep your seed phrase safe — it's your          │
│     quantum-safe backup                             │
│   • No other action needed — PQ-aware clients       │
│     will recognize your new keys automatically      │
│                                                     │
│   Event ID: <hex event id>                          │
│   OTS proof: <download .ots file>                   │
│                                                     │
│   ┌─────────────────────────────────────┐           │
│   │   Done                               │           │
│   └─────────────────────────────────────┘           │
│                                                     │
└─────────────────────────────────────────────────────┘

User action: Downloads the .ots file (optional backup), taps "Done"


Comparison: Path A vs Path B

Aspect Path A (Has Seed) Path B (Raw nsec)
Amber accounts needed 1 (existing) 2 (existing + new)
Seed phrase generation Not needed (already have one) Generated by web app
Amber import step Not needed Manual import of seed as Account #2
NIP-46 sessions 1 2 (one per account)
Amber signing requests 1 (sign NIP-QR event) 2 (Account #2 signs statement, Account #1 signs event)
NIP-QR event structure Single-key link (secp256k1 + PQ keys from same seed) Two-key migration (old nsec → new seed-derived key → PQ keys)
Time required ~2 minutes ~5 minutes
User complexity Low Medium

NIP-QR Event Structure Differences

Path A (Simple) — single identity, seed-derived:

{
  "kind": <NIP-QR kind>,
  "pubkey": "<secp256k1 pubkey (seed-derived)>",
  "content": {
    "statement": "Identity <npub> is linked to the following PQ keys, all derived from the same BIP39 seed. This link is established pre-quantum.",
    "pq_keys": [
      { "algorithm": "ml-dsa-65", "public_key": "...", "signature": "..." },
      { "algorithm": "slh-dsa-128s", "public_key": "...", "signature": "..." },
      { "algorithm": "ml-kem-768", "public_key": "..." }
    ]
  },
  "sig": "<secp256k1 Schnorr signature over event id>"
}

Path B (Migration) — two identities, old + new:

{
  "kind": <NIP-QR kind>,
  "pubkey": "<Account #1 secp256k1 pubkey (old nsec)>",
  "tags": [
    ["successor", "<Account #2 secp256k1 pubkey (seed-derived)>"]
  ],
  "content": {
    "statement": "Identity <npub1> is migrating to successor <npub2>. All PQ keys listed below are derived from the same BIP39 seed as <npub2>. This link is established pre-quantum.",
    "successor_pubkey": "<Account #2 pubkey>",
    "successor_signature": "<Account #2 Schnorr signature over statement+pq_keys>",
    "pq_keys": [
      { "algorithm": "ml-dsa-65", "public_key": "...", "signature": "..." },
      { "algorithm": "slh-dsa-128s", "public_key": "...", "signature": "..." },
      { "algorithm": "ml-kem-768", "public_key": "..." }
    ]
  },
  "sig": "<Account #1 Schnorr signature over event id>"
}

The key difference: Path B includes a successor tag and successor_signature in the content, because the old identity (Account #1) is different from the seed-derived identity (Account #2). Path A doesn't need this because the identity IS the seed-derived key.


Post-Migration: What Changes for the User

Immediately (nothing changes)

  • Their npub is the same
  • Their followers, follows, NIP-05, profile — all unchanged
  • Existing clients work exactly as before
  • They can still sign events with their existing nsec

When PQ-aware clients adopt the NIP

  • PQ-aware clients find the NIP-QR event for their npub
  • Path A: clients see secp256k1 key + PQ keys, all from the same seed
  • Path B: clients see Account #1 → Account #2 (successor) → PQ keys
  • Clients can use their PQ encryption key (ML-KEM-768) for PQ-secure messaging
  • Clients can verify PQ signatures on future events

If a quantum computer breaks secp256k1

  • An attacker can forge secp256k1 signatures
  • But they CANNOT:
    • Forge PQ signatures (ML-DSA, SLH-DSA remain secure)
    • Backdate a fraudulent NIP-QR event (OTS proof anchors the real one)
    • Decrypt PQ-encrypted messages (ML-KEM remains secure)
  • The user's identity is preserved through the OTS-anchored migration event
  • For Path B: the user continues signing with Account #2 and PQ keys
  • For Path A: the user continues signing with PQ keys (secp256k1 is broken but PQ keys are safe)

Error Handling and Edge Cases

User loses seed phrase

  • Path A: PQ keys cannot be re-derived. The NIP-QR event is already published and timestamped — the link still exists. User should re-run migration with a new seed phrase (publishes an updated NIP-QR event).
  • Path B: Account #2's nsec is still in Amber (can be exported via NIP-49 encrypted backup). But PQ keys cannot be re-derived without the seed. Same recovery: publish a new NIP-QR event with a new seed.

User loses phone (Amber)

  • If seed phrase was written down: import into new Amber installation, re-derive PQ keys
  • If seed phrase was lost too: PQ keys are unrecoverable, but the NIP-QR event still proves the link existed. User publishes a new NIP-QR event with a new seed.

User doesn't have Amber

  • Fall back to NIP-07 browser extension (nos2x, etc.) for secp256k1 signing
  • Seed phrase is generated and handled entirely in the browser
  • Less secure (seed in browser) but still functional
  • Recommend installing Amber for better security

User already has a seed phrase in Amber (Path A) but wants to use a different seed

  • They can choose to generate a new seed (switching to Path B flow)
  • This might be desirable if they want a dedicated PQ-only seed phrase

Technical Summary

Component Where it runs What it does
Seed generation Browser (WASM) BIP39 mnemonic generation (Path B only)
Seed retrieval Amber → user → browser Manual export from Amber's backup screen
NIP-06 key derivation Browser (WASM) secp256k1 key from seed (verify it matches Amber account)
PQ key derivation Browser (WASM, liboqs) ML-DSA, SLH-DSA, ML-KEM from seed
PQ signing Browser (WASM, liboqs) Sign link statement with PQ keys
secp256k1 signing Amber (Android) Sign NIP-QR event (and successor statement for Path B)
Event assembly Browser Combine all signatures into NIP-QR event
Relay publishing Browser (WebSocket) Publish event to Nostr relays
OpenTimestamps Browser → OTS API Anchor event to Bitcoin blockchain
Seed phrase storage Amber (Android encrypted storage) BIP39 mnemonic stored encrypted
nsec storage Amber (Android encrypted storage) Account private key(s) stored encrypted

No server-side secrets

The web app is a static site — HTML, JS, and WASM files served from a CDN or GitHub Pages. There is no backend that handles keys. The only server interactions are:

  • Nostr relays (publishing the event)
  • OpenTimestamps API (requesting timestamp proof)
  • NIP-46 relay (communicating with Amber)

All cryptographic operations happen either in the browser (WASM) or in Amber (Android). No private key ever touches a server.