feat(signer): QR pairing — client-initiated nostrconnect:// flow for Amber

Keynctr is the NIP-46 client; Amber is the scanner. Amber hands out no
link — it scans one — so the signer screen now mints a pairing token:

- start_pairing(): ephemeral key + secret, nostrconnect:// token via
  NostrConnectUri::client_with_secret, status().pairing_uri for the GUI
- run_pairing_task(): listens for the signer's connect request, echoes
  the secret (anti-spoofing), persists the connection row + secret,
  then adopts identity via get_public_key and hands to the demux loop
- pairing subscription is closed at handoff so the demux loop owns the
  conversation (relay could otherwise deliver signer replies under the
  stale pairing sub id where nobody routes them)
- IPC: nip46_pair_start; status carries pairing_uri
- SignerModeScreen: 'Show QR' button, QR render (qrcode) of the token,
  copy-link fallback, cancel; paste-link flow unchanged
- e2e: fake QR scanner consumes the real pairing token end-to-end
  (scan -> secret echo -> identity -> sign -> vault persistence)
This commit is contained in:
Avi 2026-09-12 04:47:20 -05:00
commit 38499d4506
12 changed files with 1041 additions and 49 deletions

1
Cargo.lock generated
View file

@ -1283,6 +1283,7 @@ version = "0.45.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0ba32ce43631188586469ba1a4c40bcfa63641f1ad5de89ef77f74d801cc17a"
dependencies = [
"aes 0.8.4",
"base64",
"bech32",
"bip39",