refactor(nostr-client): slim the spire-seed to carry the pubkey once, add lnbits_npub

The v1 seed spelled the spire pubkey three times — spire_npub, spire_pubkey
(hex), and again inside a full bunker_url — which bloats a QR that's already
hard to scan off the machine's camera. Carry it once, as an npub, and derive
the rest:

- spire_pubkey (hex) ← decode(spire_npub). npub is ~the same length as hex but
  carries a bech32 checksum, so a mis-scanned character is caught instead of
  yielding a wrong-but-valid-looking key.
- bunker_url ← reconstructed from spire_pubkey + bunker_secret + bunker_relay.
- bunker_relay is OPTIONAL, defaulting to relays[0] (option 3): minimal in the
  common case where the bunker shares the event relay, explicit when it differs.
- lnbits_npub is NEW — gives a paired machine its LNbits transport server pubkey
  from the seed itself, so nothing else needs provisioning (bitspire-#70 part 2).

Kept as v: 1 (redefined in place, no compat shim): the seed is a one-shot
pairing token, no bitspire machine has shipped, and a paired machine resumes
from its stored binding, not by re-parsing the seed. Roughly a third smaller
encoded — ~180-200 fewer chars in the QR.

Lockstep: aiolabs/spirekeeper pairing.py must emit the new shape (spire_npub +
lnbits_npub + bunker_secret, drop spire_pubkey/bunker_url) before a new seed can
be minted. Consumer wiring (relays + lnbitsServerPubkey into LightningConfig)
and a resolver-resilience guard for machines holding an old-shape seed land
separately.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Padreug 2026-07-01 13:14:59 +02:00 • committed by padreug
commit 98bdd92044
3 changed files with 107 additions and 40 deletions

View file

@ -1,6 +1,7 @@
import { describe, it, expect, vi, afterEach } from 'vitest'
import { ingestScannedSeed } from '../ingest'
import { SPIRE_SEED_SCHEME } from '@bitSpire/nostr-client'
import { npubEncode } from 'nostr-tools/nip19'
/** Mirror of spirekeeper pairing.py: urlsafe base64, padding stripped. */
function makeSeed(json: unknown): string {
@ -15,9 +16,9 @@ function makeSeed(json: unknown): string {
const SPIRE_PUBKEY = 'a'.repeat(64)
const VALID_SEED = makeSeed({
v: 1,
spire_npub: 'npub1example',
spire_pubkey: SPIRE_PUBKEY,
bunker_url: `bunker://${SPIRE_PUBKEY}?relay=wss%3A%2F%2Fbunker.relay%2F&secret=deadbeef`,
spire_npub: npubEncode(SPIRE_PUBKEY),
lnbits_npub: npubEncode('b'.repeat(64)),
bunker_secret: 'deadbeef',
relays: ['wss://events.relay/'],
})