Files
amethyst/desktopApp/plans/2026-08-11-new-account-key-onboarding-plan.md
nrobi144andClaude Opus 4.8 b6210e9fd0 feat(desktop): guided first-run key backup + reachable settings backup
Reworks the Desktop new-account key backup after testing surfaced that the
original card never rendered and the settings entry sat on dead code.

- Guided 3-step "Save your keys" onboarding (NewKeyOnboardingScreen), full
  window + scrollable so it never clips. Used by BOTH the cold-start login and
  the in-app "+ Add Account" generate paths. Retires NewKeyWarningCard.
- Split key generation from activation (AccountManager.buildNewAccount +
  activateAccount + begin/finish/cancelNewAccountOnboarding). generateNewAccount
  previously flipped account state immediately, tearing down the screen before
  any backup UI could show — the root cause of "no save-your-keys prompt".
- Move BackupKeysCard into the reachable Settings screen (RelaySettingsScreen);
  the old ProfileScreen host had no call sites.
- Fix the password show/hide eye (was a non-clickable Icon) in both the
  onboarding and settings encrypted-copy sections.
- Run NIP-49 encryption off the UI thread so the "Copy encrypted" button stays
  responsive and reliably flips to "Copied!"; match the plain copy button style.
- Add EncryptedKeyBackupTest: nsec -> hex -> Nip49 encrypt -> decrypt round-trip
  (and wrong-password fails).
- Plans + manual testing sheet under desktopApp/plans/.

Known follow-up: Desktop loginWithKey does not yet import ncryptsec1, so
encrypted backups can't be restored on Desktop yet (Android already handles it).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-13 06:54:55 +03:00

5.6 KiB

First-Run "Save Your Keys" Onboarding — Plan (Desktop)

Date: 2026-08-11 Branch: feat/key-backup-nsec-exposure Supersedes: the single NewKeyWarningCard for freshly-generated accounts.

Why

New-account key backup is the highest-stakes moment in a Nostr client: the nsec is shown once and can never be reset. Current impl crams warning + npub + nsec + encrypted-copy + checkbox + continue into one NewKeyWarningCard. Inside the 480px add-account dialog it has no scroll, so it clips — labels above and the Continue button below are cut off → reads as "no label / no way forward". Even un-clipped, one dense card is confusing.

Decision (user): guided multi-step full-screen flow, soft checkbox gate.

What already exists (reuse — don't rebuild)

  • Split generation (just built): AccountManager.buildNewAccount() creates the keypair WITHOUT activating it; activateAccount(state) flips account state. This is what lets a backup step render before the account switch tears the screen down. Keep.
  • Entry points that generate a key:
    • Cold start: LoginScreen (onGenerateNew → buildNewAccount()).
    • Logged-in: AddAccountDialog (onGenerateNew → buildNewAccount()), confirmed via onNewAccountConfirmed on the App scope.
  • Pieces to lift out of NewKeyWarningCard: CopyKeyButton, EncryptedCopyRow (NIP-49 password → ncryptsec1), SelectableKeyText, the warning strings (new_key_*). The nsec→hex decode (decodePrivateKeyAsHexOrNull) + Nip49.
  • QR: QrCodeCanvas(data) — npub only, never nsec.
  • Settings backup card (BackupKeysCard) already covers "view my keys later".

Design — NewKeyOnboardingScreen (stepper, 3 steps)

A full-window composable (NOT a dialog). Rendered while buildNewAccount() result is held and before activateAccount. Fixed max content width (~560dp), centered, each step vertically scrollable so nothing clips.

Step 1 — Why this matters (education)

  • Headline "Save your keys" + plain-language explainer: npub = your public identity (shareable); nsec = a password that can never be reset or recovered — lose it and the account is gone. No inputs. [Next].

Step 2 — Your keys (the actual backup)

  • Public key (npub): monospace, Copy, optional Show QR.
  • Secret key (nsec): monospace (shown — this is the one moment we intentionally reveal it), Copy (primary, prominent) + best-effort clipboard auto-clear.
  • Copy encrypted (recommended): collapsible/secondary — password field → ncryptsec1. Keep out of the way so the primary Copy is obvious.
  • Strong "never share the nsec" inline warning. No nsec QR. [Back] [Next].

Step 3 — Confirm & continue (soft gate)

  • Recap one line ("Stored somewhere safe? A password manager is ideal.").
  • Soft checkbox "I have saved my keys somewhere safe" → enables Continue. (Honor-system, matches the Android nudge decision. No copy/verify enforcement.)
  • Continue → activateAccount(state) + persist + proceed into the app.

Progress indicator (e.g. "Step 2 of 3" or dots). [Back] on 2/3.

Integration

  • New file: desktopApp/.../ui/auth/NewKeyOnboardingScreen.kt (stepper + step composables; reuse the extracted Copy/Encrypt pieces).
  • Cold start: in LoginScreen, when generatedAccount != null render the onboarding screen full-bleed instead of the inline card. onFinish = activateAccount + onLoginSuccess.
  • Add-account: when a key is generated, DON'T keep it inside the small dialog. Either (a) dismiss the dialog and show the onboarding screen at the App level (preferred — full space, survives the later account switch), or (b) let the onboarding screen be the dialog's content at a larger, scrollable size. Pick (a) for consistency with the cold-start path: hoist a top-level pendingNewAccount: AccountState.LoggedIn? in the App composable; both entry points set it; one NewKeyOnboardingScreen renders when non-null; onFinish activates + persists on the App scope. This unifies both flows through one screen and removes the dialog-clipping problem entirely.
  • Retire NewKeyWarningCard (and its Preview) once both paths use the stepper.

Testing (add to the manual sheet)

  • Cold start: log out → Generate → 3-step screen, no clipping, Copy works, encrypted copy → ncryptsec1, checkbox gates Continue, Continue lands in app.
  • Add account (already logged in): Add → Generate → same screen at App level (not a cramped dialog), Continue switches to the new account + persists (survives restart).
  • Back/Next preserve state; window resize keeps everything reachable (scroll).
  • NIP-49 round trip (encrypted copy → login elsewhere → same npub).

Non-goals / follow-ups

  • Android first-run guided screen (Android already has AccountBackupScreen + post-signup nudge; a matching stepper is a separate follow-up).
  • Hard copy/verify enforcement (explicitly declined — soft gate).
  • Mnemonic (NIP-06) backup.

Unanswered questions

  • Add-account path: hoist to App-level full-screen (recommended) vs enlarge the dialog to host the stepper? (Plan assumes hoist.)
  • Does generating from the logged-in state and cancelling mid-onboarding need an explicit "discard this new key" confirm, or is silent discard fine?
  • Show the nsec revealed by default on Step 2 (it's first-run, user must save it) — or masked-with-reveal like the Settings card? (Plan assumes shown, since the whole point is to save it now.)
  • Progress UI: numbered "Step X of 3" vs dots vs a top wizard bar?
  • Keep the NewKeyWarningCard as a fallback anywhere, or fully delete?