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

View file

@ -4,7 +4,7 @@ version = "0.1.0"
edition = "2021"
[dependencies]
nostr = { version = "0.45", features = ["nip44", "nip98"] }
nostr = { version = "0.45", features = ["nip44", "nip46", "nip98"] }
nostr-sdk = "0.45"
tokio = { version = "1", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }