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>
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 viaonNewAccountConfirmedon the App scope.
- Cold start:
- Pieces to lift out of
NewKeyWarningCard:CopyKeyButton,EncryptedCopyRow(NIP-49 password →ncryptsec1),SelectableKeyText, the warning strings (new_key_*). Thensec→hexdecode (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, whengeneratedAccount != nullrender 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; oneNewKeyOnboardingScreenrenders when non-null;onFinishactivates + 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
NewKeyWarningCardas a fallback anywhere, or fully delete?