Commit Graph
2 Commits
Author SHA1 Message Date
npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67dandTyler Longwell dde37183e3 Address implementation-review blockers: import ordering, site-granular inventory, uppercase bech32
Blocker 1: import_identity persisted-before-cleanup ordering. New
commit_imported_identity helper runs durable persistence FIRST; a failed
different-key import now leaves both the old in-memory identity and its
valid canonical identity.ncryptsec intact. Stale-backup cleanup runs after
the durable commit and is deliberately best-effort (logged, not surfaced
as a half-applied-import error). Regression tests cover the failure path
and prove cleanup cannot precede persist.

Blocker 2: the /events inventory tripwire is now site-granular. Each
inventoried file pairs an expected non-comment /events occurrence count
with an expected egress-guard call count, so an unguarded ninth site in an
already-listed file (or a deleted guard call) fails the scan. The scan
core is pure over (path, content) pairs, with mutation-style tests
demonstrating all three drift classes.

Hardening: bech32 permits an all-uppercase encoding, so the egress guard
now rejects NCRYPTSEC1... too (mixed case stays unblocked - it cannot
decode), and both import classifiers (Rust recover_keys_from_input, TS
classifyKeyImportInput/isPlausibleNcryptsec) route uppercase-valid blobs
to the encrypted path consistently.

Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
2026-07-25 23:45:09 -04:00
npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67dandTyler Longwell 1c01889929 Encrypted-by-default key backup UI + ncryptsec import (frontend half)
Frontend half of PLANS/NIP49_LOCAL_BACKUP_PLAN.md Rev 3 (D3):

- BackupStep: encrypted by default. The default path never invokes
  get_nsec — the webview only ever sees the finished ncryptsec1 blob
  returned by create_ncryptsec_backup. The raw key path survives behind
  an explicit 'Show raw key instead' click with its previous
  loading/error/skip semantics.
- EncryptedBackupCreator (shared by onboarding + settings): generated
  6-word passphrase default with cannot-be-recovered copy, 'choose my
  own' = min 12 chars + confirmation, create -> masked display + copy +
  'Save a copy…' via the native dialog.
- encryptedBackup.ts: pure reducer/validation model, unit-tested without
  a DOM; keyImportInput.ts: HRP classification + submit gating.
- NsecMaskedDisplay: kind='nsec'|'ncryptsec' drives labels/aria/testids;
  existing nsec call sites unchanged.
- NostrKeyImportForm: ncryptsec1 paste switches to encrypted mode
  (passphrase field, no npub preview possible), decrypt errors surface;
  raw nsec flow untouched. import_identity plumbs the optional password
  through OnboardingFlow/MachineOnboardingFlow/KeyringLockedScreen.
- ProfileSettingsCard: 'Encrypted backup' row alongside the raw reveal.
- ncryptsecSourceScan.test.mjs: TS-side source-allowlist tripwire
  mirroring the Rust scan (defense-in-depth per plan D4).
- e2e: mock bridge learns the three backup commands + ncryptsec import;
  onboarding-backup.spec.ts covers the encrypted happy path (asserting
  get_nsec is never called), custom-passphrase validation, raw fallback,
  and error/retry; onboarding.spec.ts adds encrypted-import happy path
  including a wrong-passphrase rejection.

Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
2026-07-25 22:44:03 -04:00