diff --git a/CHECKPOINT-encryption.md b/CHECKPOINT-encryption.md index 8b5f28d..bd6ea73 100644 --- a/CHECKPOINT-encryption.md +++ b/CHECKPOINT-encryption.md @@ -1,3 +1,98 @@ +# Checkpoint — NIP-46 handshake debugged: connect-params spec fix + full [NIP46] trace + visible errors (2026-09-23) + +## Where things are +- Project: `/home/avi/Projects/Keynctr` +- Branch: `master` @ **`8f055f7`** ("fix(nip46): spec-correct connect params, + full-handshake [NIP46] trace, visible handshake errors"). Previous: + `2bc6321` (identity-RPC retry), `9cfc1cf` (relay-accept trace). +- Working tree: clean for tracked files. Untracked intentionally NOT + committed: `COSMIC_THEME.md`, `KeynectrAppIconPossibility02.jpeg`, + `deferred/` (stays deferred). +- Release binary: **rebuilt at `8f055f7`** — Electron spawns this one. + +## What was completed this session +1. **Spec-verified the whole signer flow against NIP-46** (20-point checklist + in the session brief). URI generation, keypair handling, subscribe-before- + publish, relay URL encoding, kind-24133 listen, NIP-44 decrypt, secret + validation, identity-via-`get_public_key`, account-state update and UI + polling were all traced file-by-file (`nip46_client.rs`, `ipc.rs`, + `SignerModeScreen.tsx`, `relays.rs`, `nip46_e2e.rs`). +2. **Found + fixed the definitive paste-flow (`bunker://`) bug (`8f055f7`)**: + `run_handshake` sent `connect` params as `[client_pubkey, secret]`, but + NIP-46 (and rust-nostr's own `NostrConnectRequest::Connect` codec) require + `[, ]`. Strict signers answer a + malformed connect with silence, stalling the handshake with zero feedback. + New pure helper `connect_params(peer, secret)` + 2 unit tests (one + round-trips through the library codec). +3. **Client keypair is now always ephemeral** in `connect()` (was: reused the + vault's local secret key when unlocked). Per NIP-46 the client keypair is + disposable and must never be confused with the user's identity or a vault + key. QR flow already did this; both flows now agree. +4. **Full `[NIP46]` diagnostic trace** across both flows (console stderr, in + addition to the existing `pairing-trace.log` forensics): keypair gen, + client pubkey, secret presence (NEVER values), relay connecting/connected, + subscription created/registered (filter + id), URI generated, every inbound + 24133 (author, tags, decrypt OK/real-error, method, secret PASS/FAIL, + remote pubkey), every RPC publish/timeout/response-routing decision + (including stale/duplicate ids), relay-health pre-check before each + `get_public_key` retry (fails fast with a useful error when no relay is + connected), user pubkey, account-state update, UI-state update. +5. **Failures are visible now, not silent**: `fail()` keeps the FIRST + (specific) error instead of letting the demux exit overwrite it with + generic "Connect handshake failed"; `SignerModeScreen` renders + `status.error` as an alert in both the pairing and the idle/paste branches + and shows a "Connection request sent — approve it in Amber" hint while a + paste-URI connect is in flight. No fake Connected state anywhere: + `Connected` still requires `get_public_key` to resolve the identity. +6. **Tests**: new strict-Amber e2e + (`nip46_bunker_connect_params_match_spec_against_strict_amber`) — fake + signer rejects `connect` unless params[0] is its own pubkey, then serves + identity + sign like Amber; asserts the REAL user pubkey lands in the + vault as a secretless row AND becomes the active profile. Verified RED on + the old param order, GREEN on the fix. New `SignerModeScreen.test.tsx` + (3 tests: QR waiting hint, connecting hint, failed-handshake error + visible) + `nip46_*` dispatch in `fakeBackend.ts`. Also widened the e2e + `VAULT_ENV_LOCK` to whole-test bodies (data_dir() re-reads process-global + XDG_DATA_HOME on every save — one cross-write flake seen under full-suite + parallelism) with targeted `await_holding_lock` allows. +- Verification (all green at `8f055f7`): `cargo test` **211 unit + 4 e2e + passed / 0 failed**, `cargo clippy --all-targets` 0 warnings, + `cargo fmt --check` clean, `cargo build --release` green; frontend + `npm test` **119 passed (17 files)**, `typecheck`, `lint`, `format:check`, + `electron:build`, `build` clean. + +## Commits added (newest first) +- `8f055f7` fix(nip46): spec-correct connect params, full-handshake [NIP46] + trace, visible handshake errors + +## How to resume / reproduce +- Dev loop: `cd frontend && npx vite --port 5173` then + `NOSTR_GUI_DEV_URL=http://localhost:5173 KEYNCTR_ENABLE_GPU=1 npx electron .` + (release binary already rebuilt at `8f055f7`). +- **Live Amber re-scan (still the decisive test)**: Signer Mode → Show QR → + scan in Amber → approve, **keep Amber open in the foreground**. Watch + terminal/backend console for the `[NIP46]` lines in order (see "Expected + logs" in the session report) and `~/Tools/keynctr-debug/pairing-trace.log`. +- E2E: `cargo test --test nip46_e2e` (4 tests, no network). +- Frontend: `cd frontend && npm test -- SignerModeScreen`. + +## Outstanding / next steps +1. **Live Amber re-scan against `8f055f7`** — the QR flow's `get_public_key` + silence (5 scans × 4 attempts, publishes accepted by primal+nos.lol, zero + responses) is downstream of our publish: either Amber never receives our + request (e.g. its subscription fails, NIP-42 AUTH on those relays from + mobile, or the app was backgrounded) or it receives and never answers. + The new demux logs distinguish these live: zero inbound lines during the + retries ⇒ Amber-side; inbound-but-dropped lines ⇒ our filter/decrypt. +2. If the live trace shows zero inbound during retries, next step is a relay + experiment (add a no-auth REQ relay to the pairing set) and/or confirming + Amber stays foregrounded with network. +3. `publish_profile_metadata` (kind 0) still signs locally — reroute through + `Signing` for external profiles (P2, pre-existing). +4. Step 5 (KDF upgrade), Step 7 (rename pass incl. `homepage` URL). + +--- + # Checkpoint — first live Amber pairing succeeded; subscription race fixed (2026-09-23) ## Where things are