From e97d8a75f085c6ecaeb7519d98e70ff7a02e698c Mon Sep 17 00:00:00 2001 From: Avi Date: Wed, 16 Sep 2026 21:12:13 -0500 Subject: [PATCH] =?UTF-8?q?checkpoint:=20sync=20to=20188b2eb=20=E2=80=94?= =?UTF-8?q?=20lenient=20NIP-46=20payload=20parse,=20real=20decrypt-error?= =?UTF-8?q?=20logging=20(2026-09-16)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHECKPOINT-encryption.md | 70 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/CHECKPOINT-encryption.md b/CHECKPOINT-encryption.md index 2c1ff9e..2fd813d 100644 --- a/CHECKPOINT-encryption.md +++ b/CHECKPOINT-encryption.md @@ -1,3 +1,73 @@ +# Checkpoint — Amber pairing: lenient NIP-46 payload parse + real decrypt-error logging (2026-09-16) + +## Where things are +- Project: `/home/avi/Projects/Keynctr` +- Branch: `master` @ **`188b2eb`** ("fix(pairing): never reject a decrypted + NIP-46 payload on shape"). Previous feature HEADs: `aedde8f`, `759b5dd`, + `5aa122d`, `f59c2b1`. +- Working tree: clean for tracked files. Untracked intentionally NOT + committed: `COSMIC_THEME.md`, `KeynectrAppIconPossibility02.jpeg`, + `deferred/` (stays deferred). +- Release binary rebuilt at `188b2eb` (2026-09-16 ~21:06) — the binary the + Sep 15/16 Amber scans ran against was the Sep 12 build; the id-coercion + + payload-dump diagnostics are live NOW. +- Verification (all green at `188b2eb`): `cargo test` **208 unit + 2 e2e + passed / 0 failed**, `cargo clippy --all-targets` 0 warnings, + `cargo fmt --check` clean, `cargo build --release` green. Frontend: + `npm test` **116 passed (16 files)**, `npm run typecheck` clean, + `npm run lint` clean, `npm run format:check` clean, + `npm run electron:build` and `npm run build` green. + +## What was completed since the last checkpoint +- **Universal-lenient RawRequest parse (`188b2eb`)**: the derived + `Deserialize` (even after `aedde8f`'s params coercion) still rejected real + signer payloads. Replaced with a hand-written coercion deserializer: any + *valid JSON* deserializes — numeric/missing ids become text, object-shaped + `params` become a single param, a double-encoded JSON-string request is + unwrapped. A parse failure is now only possible for non-JSON plaintext, + which the log dumps verbatim. Regression tests for all five shapes. +- **Real decrypt-error logging (`188b2eb`)**: pairing decrypt failures now + log the actual NIP-44 error (invalid HMAC vs invalid padding vs wrong + conversation key) instead of a generic "wrong conversation key", and the + not-a-request log prints the exact decrypted payload. +- **Offline forensics (no commit)**: all four captured pairing frames in + `~/Tools/keynctr-debug/pairing-capture.jsonl` (latest: Sep 16 20:11) are + 163-byte NIP-44 v2 payloads = 1 ver + 32 nonce + 98 buffer + 32 MAC. + 98 is a VALID final-spec padding bucket (plaintext 65–96 bytes; the + observed 89 fits). So the frames are spec-conformant, decryption + succeeds, and the failure was purely the JSON-shape parse — confirming + the fix targets the right layer. Note `/tmp/keynctr-el*.log` no longer + exists (tmp-cleaner); backend stderr now only reaches the Electron + console — the next failed scan's payload dump needs + `npx electron .` launched from a terminal or with stderr redirected. + +## Commits added (newest first) +- `188b2eb` fix(pairing): never reject a decrypted NIP-46 payload on shape + +## How to reproduce / exercise +- Dev loop (unchanged): `npx vite --port 5173` in `frontend/` FIRST, then + `NOSTR_GUI_DEV_URL=http://localhost:5173 KEYNCTR_ENABLE_GPU=1 npx electron .`. + To capture pairing diagnostics: run electron with stderr kept, e.g. + `... npx electron . 2>&1 | tee ~/Tools/keynctr-debug/el.log`. +- E2E: `cargo test --test nip46_e2e` (no network) — green. +- GUI: Signer mode -> "Show QR" -> scan in Amber -> approve. Raw pairing + events keep appending to `~/Tools/keynctr-debug/pairing-capture.jsonl`. + +## Outstanding / next steps +1. **Live Amber re-scan required** (cannot be done from an unattended run): + if pairing still fails, the backend log now contains the exact decrypted + 89-byte payload and/or the exact NIP-44 error — that text names the last + possible cause. +2. If the payload turns out to be valid JSON but not `method:"connect"` + (e.g. Amber's deferred-approval `get_public_key` first), the log will + show it and the handshake gate can be relaxed accordingly. +3. `publish_profile_metadata` (kind 0) still signs locally — reroute + through `Signing` for external profiles (P2). +4. Step 5 (KDF upgrade), Step 6 (undo preserves ProfileSummary), Step 7 + (rename pass incl. `homepage` URL). + +--- + # Checkpoint — QR pairing, identity adoption, always-allow grants + pairing-relay widening (2026-09-12) ## Where things are