From 832e1144f03d0199b033ff17a9dcffddac61fbc2 Mon Sep 17 00:00:00 2001 From: Avi Date: Thu, 27 Aug 2026 12:09:53 -0500 Subject: [PATCH] checkpoint: fix delete/undo + themed auto-dismiss bar --- CHECKPOINT-encryption.md | 1454 +------------------------------------- 1 file changed, 17 insertions(+), 1437 deletions(-) diff --git a/CHECKPOINT-encryption.md b/CHECKPOINT-encryption.md index 86dda29..da13251 100644 --- a/CHECKPOINT-encryption.md +++ b/CHECKPOINT-encryption.md @@ -1,1447 +1,27 @@ -# Checkpoint — nostr stack 0.45 security migration (2026-08-25) - -A stopping point you can return to if this session is closed. Everything below was -verified green at the moment this file was written. +# Checkpoint — fix profile delete/undo + themed auto-dismiss bar (2026-08-27) ## Where things are - - Project: `/home/avi/Projects/Nostr_Keynctr` -- Git repo: `master` @ `8130c3e` ("frontend: click a profile row on Home to - select it"). Before it: `98f4520` (active-profile picture on Compose + - sidebar), `f534165` (remove Profiles sidebar tab), `835b0a0` (list all - profiles on Home, pin dev host, widen card), `5635f79` (checkpoint), - `71e580f` (Trim white margins), `f29d4f9` (Checkpoint 0.45 migration), - `fa5ba08` (Security: upgrade nostr stack 0.40->0.45), `bf10ae3`, `e210f17`, - `7098e75`, `e6a1efc`, `6d5063d`, `1c428a9`. -- Working tree: still DIRTY with the long-standing user changes — `concept3.svg` - deleted, `KeynectrAppIconPossibility02.jpeg` untracked. These are NOT part - of the frontend commit above and remain uncommitted. +- Git repo: `master` @ `9e635e7` ("fix: restore profile delete/undo and themed auto-dismiss undo bar"). Before it: `56c2a6d` (checkpoint: click profile row), `8130c3e` (click profile row on Home), `a5838ea`, `98f4520` etc. +- Working tree: clean except `KeynectrAppIconPossibility02.jpeg` untracked (long-standing, not part of commits). ## What was completed +1. **Fixed broken profile delete/undo.** `src/ipc.rs` was missing `DeleteProfile`/`UndoDelete` request variants and handlers, so `delete_profile` from renderer returned `Unexpected signer request`. Added `DeleteProfile {npub}`/`UndoDelete` to `Request`, handlers that call `profiles::delete_profile`/`app.undo_delete()`, save vault, push/pop `undo_history`, return `state_view()`. Exposed `profiles::delete_profile` outside `#[cfg(test)]` (was only inside tests). Updated `src/profiles.rs` accordingly. CLI `delete_profile_direct` kept for `main.rs`. +2. **Wired frontend to backend state.** `frontend/src/lib/api.ts`: `deleteProfile`/`undoDelete` now `call` (was `ProfileSummary`). `frontend/src/state/AppProvider.tsx`: both now use `applyState(...)` and type `Promise`; added `clearLastDeleted` that locally slices `undo_history` for UI dismissal. +3. **Themed auto-dismiss undo bar.** `frontend/src/screens/ProfilesScreen.tsx`: replaced permanent white bar (`var(--token-item-bg, #f0f0f0)` + "Profile deleted. You can restore it until you delete another or quit.") with themed bar (`var(--primary-soft)` bg, `var(--border)` border) showing `Profile "X" deleted. — Undo and restore profile` where the link uses `var(--primary)` (adapts to light/dark/neon/glass). Bar appears in both empty and populated states, auto-dismisses after 5s via `useEffect` watching `lastDeleted` (previous version fired unconditionally on mount). Fake backend updated to simulate delete/undo for tests. -1. **Security migration of the Nostr stack (`fa5ba08`).** `cargo audit` was - installed on this machine (`cargo install cargo-audit`, v0.22.2) and its - first scan found **11 RustSec vulnerabilities** in nostr 0.40 / - relay-pool 0.40.1 — including forged-event signature-bypass (0224), - NIP-46 credential Debug leak (0225), and auth-challenge memory - exhaustion (0231). Upgraded to `nostr 0.45.3` + `nostr-sdk 0.45.2` - (secp256k1 0.30), clearing every vulnerability and all 4 warnings. - Code adaptations: `Client::builder().authenticator(SignerAuthenticator)` - replaces implicit signer (NIP-42 auto-auth stays working); signing moved - from `EventBuilder::sign` to `finalize_async`; nip44 encrypt now takes an - explicit random nonce (getrandom); feed + signer receive loops moved to - `stream_events` (signer's notification stream still opens BEFORE the - announce, preserving the ordering fix); contact p-tags parsed via - `single_letter_tag()`; `relay()` returns Option; `try_connect().timeout()`. - Dropped unused `nip46` feature (signer is hand-rolled). Dry-run first: - bump-only build proved zero dependency-crate failures / no MSRV issues; - all 24 errors were exactly the planned API edits. -2. **New regression tests (+2, suite now 115).** Malformed NIP-44 payloads - (not-base64, empty, truncated, bad version byte) reject cleanly with no - panic; error strings never contain secret-key material (0225 class). -3. Earlier today: R3 modal-save dedup (`7098e75`) completing the DRY audit, - NIP-42 clarification docs (`bf10ae3`). - -## Commits added most recently - -- `8130c3e` frontend: click a profile row on Home to select it -- `98f4520` frontend: show active-profile picture on Compose and sidebar -- `f534165` frontend: remove redundant Profiles sidebar tab -- `835b0a0` frontend: list all profiles on Home, pin dev host, widen profiles card -- `fa5ba08` Security: upgrade nostr stack 0.40->0.45 clearing 11 RustSec advisories +## Commits added in this session (newest first) +- `9e635e7` fix: restore profile delete/undo and themed auto-dismiss undo bar ## Verification commands run (all green) +- Rust: `cargo fmt --check` clean; `cargo clippy --all-targets` 2 pre-existing warnings; `cargo test` 115 passed; `cargo build --release` success +- Frontend (`frontend/`): `npm run typecheck` clean; `npm run lint` clean; `npm run format:check` clean (after `npm run format`); `npm test` 15 files / 99 tests passed; `npm run build` success; `npm run electron:build` success +- The `--ozone-platform=wayland` Vulkan and `has no handler with id` messages seen on `electron:build` are harmless Electron/Wayland warnings, not profile errors. -Rust (repo root): cargo fmt --check clean; cargo clippy --all-targets only -the two pre-existing profiles.rs warnings; cargo test 115 passed (release -mode re-run too); cargo build --release success. -Frontend (`frontend/`, untouched): typecheck clean; format:check clean; -npm test 15 files / 99 tests; vite build success; electron:build success. -Live: probe built on 0.45 stack published kind-1 notes accepted by -wss://soloco.nl and wss://relay.primal.net within the 6 s watchdog. -Pre-commit audit per user protocol: staged diff = Cargo.toml/Cargo.lock/src -only; git diff --check clean. +## How to resume / reproduce +GUI: `cargo build --release && cd frontend && npm run build && npm run electron:build && npm start` (or `npm run dev`). Create 2 profiles, delete one via Profiles → Delete → confirm, see themed undo bar for 5s with "Undo and restore profile" in primary color, click it to restore, or wait for auto-dismiss. +CLI: `cargo run -- delete-profile ` then `cargo run -- undo-delete` (in-memory undo only within same `serve` session; CLI undo across processes is not persisted). -## How to use / reproduce - -No user-facing change. Rebuild + restart to pick up the new backend: - -```bash -cd ~/Projects/Nostr_Keynctr && cargo build --release -cd frontend && npm run build && npm start -``` - -Re-check Updates card: Rust advisory section should now list no -vulnerabilities (cargo-audit is installed machine-wide). - -## Notes & next steps - -- wss://nostr.l484.com had a DNS-resolution blip at the very end of the - session (gaierror for ~a minute); it worked earlier today including a - stored test event. If it stays down, check the relay host — not app code. -- Probe harnesses live in /tmp/opencode (nip42-probe on 0.45; python raw - readers). Copy into scripts/ if they should survive reboots. -- Pre-existing clippy warnings in src/profiles.rs remain untouched by request. -- Relay health today: nos.lol 502 intermittent, nostr.wine 403, - l484.com transient DNS failure at session end, nostr.band unreachable; - primal/mom/soloco healthy. - ---- - -# Older checkpoint — DRY modal save lifecycle / R3 complete (2026-08-25) - -A stopping point you can return to if this session is closed. Everything below was -verified green at the moment this file was written. - -## Where things are - -- Project: `/home/avi/Projects/Nostr_Keynctr` -- Git repo: `master` @ `7098e75` ("refactor: share modal save lifecycle"). - Before it: `e6a1efc` (refactor: share hex-id shortening in lib/format), - `ec237bf` (Checkpoint: relay pool bootstrap), `6d5063d` - (refactor: share relay pool bootstrap), `1c428a9` (DRY fan-out R1), - `b21678f` (signer-fix checkpoint), `64ad94d` (NIP-46 handshake fix). -- Working tree after this checkpoint commit: only the long-standing user - changes remain — `concept3.svg` deleted and - `KeynectrAppIconPossibility02.jpeg` untracked, exactly as the user had them. - -## What was completed - -1. **R3 modal save-lifecycle dedup (`7098e75`).** The three profile modals in - `frontend/src/screens/ProfilesScreen.tsx` (Picture, Rename, NIP-05) each - carried an identical ~20-line async save lifecycle. Now a single - module-local `runModalSave` helper owns it: clear error → set saving → - claim publishing slot (`onSavingChange(npub)`) → run API → build success - message from the publish report → report via `onSaved`, or on failure - surface the error via `onError` → always release saving state and the slot - in `finally`. Each modal keeps only what is genuinely its own: the API call, - its success-message logic inline (including NIP-05's set/removed branch), - and RenameModal's `canSave` guard outside the helper. No user-facing change: - callback order, messages, buttons, props, and tests untouched. - With this, all four DRY-audit items (R1–R4) are complete. -2. Earlier: R4 hex-id shortening (`e6a1efc`), R2 relay-pool bootstrap - (`6d5063d`), R1 fan-out dedup (`1c428a9`) — details in the older checkpoints - below. - -## Commits added most recently - -- `7098e75` refactor: share modal save lifecycle - -## Verification commands run (all green) - -Rust (repo root): cargo test 113 passed; cargo clippy --all-targets finished -(pre-existing profiles.rs warnings only); cargo fmt --check clean; -cargo build --release success. -Frontend (`frontend/`): npm test 15 files / 99 tests passed; typecheck clean; -lint clean; format:check clean; npm run build success; electron:build success. -Pre-commit audit per user protocol: staged diff contained only -ProfilesScreen.tsx; git diff --check clean; nothing else staged. - -## How to use / reproduce - -No user-facing change. Profiles → Picture / Edit name / NIP-05 modals behave -exactly as before; any future change to the shared save policy happens in one -place: `runModalSave` in `frontend/src/screens/ProfilesScreen.tsx` -(just above `Nip05Modal`). - -## Notes & next steps - -- **NIP-42 status clarified (2026-08-25 addendum).** Earlier notes saying - "l484.com needs NIP-42 auth" described *raw probes* that don't answer - challenges — not a Keynectr gap. Verified: nostr-sdk 0.40 attaches the - signer in `Client::new` and enables NIP-42 auto-authentication by default - (`nip42_auto_authentication: true`), so AUTH challenges on read AND write - are answered automatically on every Keynectr path. Live test against - `wss://nostr.l484.com` replicating Keynectr's exact client setup: event - accepted in 0.3 s (inside the 6 s `RELAY_SEND_TIMEOUT`) and retrievable - afterwards. Known edge, left as-is by choice: the SDK's worst-case auth - dance (~7 s wait + resend) can exceed the 6 s watchdog on a very slow - authed relay → "did not respond in time"; healthy relays finish in <1 s. - Probe harness: `/tmp/opencode/nip42-probe` (throwaway key, same crates). -- DRY audit items R1–R4 are all complete; nothing left on hold from that list. -- Pre-existing clippy warnings in src/profiles.rs remain untouched by request. -- Relay health today: nos.lol 502 (intermittent), nostr.wine 403, - l484.com sends NIP-42 AUTH challenges but works fine with Keynectr - (auto-answered — see addendum above), nostr.band unreachable; - primal/mom/soloco healthy. -- Client visibility rule of thumb confirmed today: a note appears only on - clients whose read relays overlap the relays it was published to; if a - note "is missing" somewhere, first compare relay lists (Primal indexes - broadly; Iris/Yakihonne read narrower sets). - ---- - -# Older checkpoint — DRY hex-id shortening (2026-08-24) - -A stopping point you can return to if this session is closed. Everything below was -verified green at the moment this file was written. - -## Where things are - -- Project: `/home/avi/Projects/Nostr_Keynctr` -- Git repo: `master` @ `e6a1efc` ("refactor: share hex-id shortening in lib/format"). - Before it: `ec237bf` (Checkpoint: relay pool bootstrap), `6d5063d` - (refactor: share relay pool bootstrap), `1c428a9` (DRY fan-out R1), - `b21678f` (signer-fix checkpoint), `64ad94d` (NIP-46 handshake fix), - `dd50b24` (Aurora rename). -- Working tree: clean apart from this checkpoint update. `concept3.svg` deleted - and `KeynectrAppIconPossibility02.jpeg` untracked remain as the user had them. - -## What was completed - -1. **R4 hex-id shortening (`e6a1efc`).** Exported `shortHexId` from - `frontend/src/lib/format.ts` with verbatim logic from the former local - `shortHex` in `SignerScreen.tsx`; SignerScreen now imports and calls - `shortHexId`; added `format.test.ts` with 7 focused tests (empty, len 16, - len 17, 64-hex, shortenNpub disabled, enabled truncation). Output behavior - is byte-identical: >16 uses first 8 chars + Unicode ellipsis U+2026 + last 8, - ≤16 passes through unchanged. Does not reuse `shortenNpub`; preserves - existing visual output exactly per audit constraint. -2. **R2 DRY refactor (`6d5063d`).** New `relays::open_pool(keys, urls, - wait: Option) -> Result` is the single pool-construction - path (add each relay with strict error propagation, connect, optional - wait). Adopted by feed's two fetchers (`Keys::generate()` + 10 s wait) and - note publishing (`keys.clone()`, no wait). `profiles.rs` intentionally NOT - adopted — its unique ignore-add-errors policy stays local; it regained an - explicit `connect()` when `send_to_all_relays` dropped its internal one - (avoids double-connect on the note path). `signer.rs` deliberately - untouched: ordering-critical notification setup + fail-to-status error model. - Net −20 lines; behavior, error strings, timing unchanged. -3. Earlier this session: R1 fan-out dedup (`1c428a9`), NIP-46 handshake fix - (`64ad94d`), Aurora rename + dropdown fix (`dd50b24`), frosted-glass look - (`ecb666b`), glass build fix (`bfe14f0`). - -## Commits added most recently - -- `e6a1efc` refactor: share hex-id shortening in lib/format -- `6d5063d` refactor: share relay pool bootstrap - -## Verification commands run (all green) - -Rust: cargo test 113 passed; clippy only pre-existing profiles.rs warnings; -fmt clean; release binary rebuilt. -Frontend (untouched): npm test 99; typecheck, lint, format, electron:build, -vite build all clean. -Pre-commit audit per user protocol: git diff --check clean; signer.rs empty -diff; 3 test files added (format.test.ts with 7 new tests). - -## How to use / reproduce - -No user-facing change. Future connection-policy work (e.g., NIP-42 auth for -nostr.l484.com, retries) now starts in `relays::open_pool`. - -## Notes & next steps - -- Remaining DRY items by user request on hold: R3 (modal save-lifecycle hook). -- R4 (shortHex vs shortenNpub) is complete: shared `shortHexId` in lib/format, - output preserved exactly, tests green. -- Relay health today: nos.lol 502, nostr.wine 403, l484.com needs NIP-42 auth, - nostr.band unreachable; primal/mom/soloco healthy. ---- - -# Older checkpoint — DRY relay fan-out (2026-08-24) - -A stopping point you can return to if this session is closed. Everything below was -verified green at the moment this file was written. - -## Where things are - -- Project: `/home/avi/Projects/Nostr_Keynctr` -- Git repo: `master` @ `1c428a9` ("DRY: single parallel relay fan-out for notes - and metadata"). Before it: `64ad94d` (NIP-46 handshake fix), `b21678f`/ - `ae84526` (checkpoints), `dd50b24` (Aurora rename). -- Working tree: clean apart from this checkpoint update. `concept3.svg` deleted - and `KeynectrAppIconPossibility02.jpeg` untracked remain as the user had them. - -## What was completed - -1. **R1 DRY refactor (`1c428a9`).** The parallel relay send block (~55 lines: - JoinSet per-relay sends, 6 s timeout, indexed outcome collection, - disconnect, succeeded/failed split) existed twice — in - `publish.rs::publish_with_keys` and `profiles.rs::publish_metadata_async`. - Now one `pub(crate) publish::send_to_all_relays(client, urls, event, noun)` - serves both; callers keep their distinct add-relay policies (note path - aborts on add errors, metadata ignores them) so behavior, error strings, - and public APIs are unchanged. Removed duplicate constant - `METADATA_SEND_TIMEOUT`, dead helper `failure_for`. Net −49 lines. -2. Deliberately NOT done (pending approval): R2 shared client-bootstrap - helper, R3 modal save-lifecycle hook (frontend), R4 shortHex/shortenNpub. - -## Commits added most recently - -- `1c428a9` DRY: single parallel relay fan-out for notes and metadata - -## Verification commands run (all green) - -Rust: cargo fmt --check clean; clippy only pre-existing profiles.rs warnings; -cargo test 113 passed; release binary rebuilt. -Frontend (untouched, suite rerun): npm test 92 passed; typecheck, lint, -format:check, electron:build, vite build all clean. - -## How to use / reproduce - -No user-facing change; note and metadata publishing behave exactly as before. -Any future change to relay-send policy now happens in one place: -`send_to_all_relays` in `src/publish.rs`. - -## Notes & next steps - -- R2–R4 from the DRY audit remain unimplemented by request. -- Relay health today: nos.lol 502, nostr.wine 403, nostr.l484.com needs - NIP-42 auth, nostr.band unreachable; primal/mom/soloco healthy. - ---- - -# Older checkpoint — NIP-46 signer handshake fix (2026-08-24) - -A stopping point you can return to if this session is closed. Everything below was -verified green at the moment this file was written. - -## Where things are - -- Project: `/home/avi/Projects/Nostr_Keynctr` -- Git repo: `master` @ `64ad94d` ("Signer: listen before announcing so the - handshake reply is not lost"). Before it: `dd50b24` (Aurora rename + - dropdown fix), `ecb666b` (frosted-glass look), `bfe14f0` (glass build fix). -- Working tree: clean apart from this checkpoint update. `concept3.svg` deleted - and `KeynectrAppIconPossibility02.jpeg` untracked remain as the user had them. - -## What was completed - -1. **Root-caused the "Yakihonne never leaves the QR screen" bug (`64ad94d`).** - Reproduced with a reference NIP-46 client harness over live relays - (`/tmp/opencode/nc-client`): the app's announcement arrived, the client's - `ack` + `get_public_key` were answered to nobody — the signer task created - its notification receiver *after* publishing the announce, so the client's - millisecond-fast handshake reply landed in the broadcast channel before any - receiver existed and was dropped. Client waits forever → stuck QR. - Fix: open `client.notifications()` immediately after adding relays, before - connect/subscribe/announce. Post-fix round-trip PASSES end-to-end - (announce → ack → get_public_key → pubkey returned). -2. Relay observations from testing: nos.lol 502 today; nostr.wine 403 - (auth/paid); nostr.l484.com sends NIP-42 AUTH challenges; healthy: - relay.primal.net, nostr.mom, soloco.nl. - -## Commits added most recently - -- `64ad94d` Signer: listen before announcing so the handshake reply is not lost - -## Verification commands run (all green) - -Rust (repo root): cargo test 113 passed; clippy (pre-existing profiles.rs -warnings only); fmt clean; release binary rebuilt with the fix. -Frontend (`frontend/`): npm test 92 passed; typecheck, lint, format clean. -End-to-end: reference NIP-46 client + `keynectr signer connect ` over -wss://nostr.mom, relay.primal.net, soloco.nl — full handshake PASS. - -## How to use / reproduce - -GUI: `cd ~/Projects/Nostr_Keynctr/frontend && npm start` → Signer → paste a -client's nostrconnect:// link → approve requests. With Yakihonne: choose its -remote-signer/connect option, copy its link into Keynectr within its timeout; -Yakihonne should now switch from the QR screen to the logged-in state. - -Harness (if ever needed again): `/tmp/opencode/nc-client` — `nc-client` -prints a nostrconnect URI to /tmp/opencode/uri.txt and PASSes on round-trip; -run `target/release/keynectr signer connect $(cat /tmp/opencode/uri.txt)`. - -## Notes & next steps - -- If a client is still slow, remaining edge: relays that reject kind 24133 or - need auth can starve the handshake — consider surfacing per-relay connect - status in the Signer UI later. -- Pre-existing clippy warnings in src/profiles.rs untouched by request. - ---- - -# Older checkpoint — Aurora rename + dropdown fix (2026-08-24) - -A stopping point you can return to if this session is closed. Everything below was -verified green at the moment this file was written. - -## Where things are - -- Project: `/home/avi/Projects/Nostr_Keynctr` -- Git repo: `master` @ `dd50b24` ("Rename glass theme display to Aurora; fix - unreadable select options"). Before it: `ecb666b` (frosted-glass look), - `0cab883`/`1aae81c` (checkpoints), `bfe14f0` (glass fix). -- Working tree: clean apart from this checkpoint update. `concept3.svg` deleted - and `KeynectrAppIconPossibility02.jpeg` untracked remain as the user had them. - -## What was completed - -1. **Theme renamed to Aurora (`dd50b24`).** User picked "Aurora" from options - (Aurora/Frost/Glacier/Ice/keep). Display label only — internal id stays - `glass`, so existing settings.json values need no migration. -2. **Fixed unreadable theme dropdown (`dd50b24`).** Under the glass theme the - native `