feat(machine): source LNbits transport from the pairing seed, not just env
Completes the consumer half of bitspire-#70: a paired machine gets its LNbits
transport relay(s) + server pubkey from the pairing, so a blank-.env unit reaches
the backend after scanning a seed — no VITE_RELAY_URL / VITE_LNBITS_SERVER_PUBKEY
provisioning.
- resolveSigner now returns { signer, transport }. transport (relays +
lnbitsServerPubkey) comes from the seed on a fresh pair / seeded resume, and
from the binding on a seedless resume. It's threaded out of resolveSigner
rather than re-parsed in loadLightningConfig because the seed arrives over the
one-shot get-atm-secrets IPC — a second consumer would break that contract.
- bunker_binding persists relays + lnbits_server_pubkey (state.db v11→v12,
nullable so pre-#70 bindings resume and fall back to env). Mirrored into
BunkerBindingRecord (preload + electron.d.ts).
- initializeLightningServices resolves effective transport with env-wins
precedence (explicit env override for dev, else pairing, else a dev-only
localhost relay), mutating CONFIG to a single source of truth and building the
Nostr/LNbits/CLINK clients from the full relay list. Strict + required-config
validation now run on the resolved values.
state.db round-trip test covers the new columns + their absence on a pre-#70
binding. Renderer + electron typechecks and all 38 machine tests pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
786789f517
commit
883c599835
7 changed files with 224 additions and 33 deletions
69
apps/machine/electron/__tests__/state-store-bunker.test.ts
Normal file
69
apps/machine/electron/__tests__/state-store-bunker.test.ts
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
/**
|
||||
* Tests for bunker-binding persistence in state-store (aiolabs/bitspire#52,
|
||||
* transport config added in #70).
|
||||
*
|
||||
* Validates the round-trip of the binding singleton, including the v11→v12
|
||||
* transport columns (relays JSON + lnbits_server_pubkey) and their absence on
|
||||
* a pre-#70 binding.
|
||||
*
|
||||
* Uses an in-memory SQLite database — fresh per test, no on-disk artifacts.
|
||||
*/
|
||||
|
||||
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
|
||||
import {
|
||||
clearBunkerBinding,
|
||||
closeDatabase,
|
||||
getBunkerBinding,
|
||||
initDatabase,
|
||||
saveBunkerBinding,
|
||||
type StoredBunkerBinding,
|
||||
} from '../state-store.js'
|
||||
|
||||
const BASE: StoredBunkerBinding = {
|
||||
clientSecretHex: 'aa'.repeat(32),
|
||||
spirePubkey: 'bb'.repeat(32),
|
||||
bunkerUrl: 'bunker://bb?relay=wss%3A%2F%2Fr%2F&secret=deadbeef',
|
||||
seedFingerprint: 'cc'.repeat(32),
|
||||
pairedAt: 1_780_000_000,
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
initDatabase(':memory:')
|
||||
})
|
||||
afterEach(() => {
|
||||
closeDatabase()
|
||||
})
|
||||
|
||||
describe('bunker binding persistence', () => {
|
||||
it('round-trips a binding carrying transport config (#70)', () => {
|
||||
const binding: StoredBunkerBinding = {
|
||||
...BASE,
|
||||
relays: ['wss://one.relay/', 'wss://two.relay/'],
|
||||
lnbitsServerPubkey: 'dd'.repeat(32),
|
||||
}
|
||||
saveBunkerBinding(binding)
|
||||
expect(getBunkerBinding()).toEqual(binding)
|
||||
})
|
||||
|
||||
it('round-trips a pre-#70 binding (no transport config) as undefined fields', () => {
|
||||
saveBunkerBinding(BASE)
|
||||
const got = getBunkerBinding()
|
||||
expect(got).toEqual(BASE)
|
||||
expect(got?.relays).toBeUndefined()
|
||||
expect(got?.lnbitsServerPubkey).toBeUndefined()
|
||||
})
|
||||
|
||||
it('upserts transport config in place (re-pair overwrites)', () => {
|
||||
saveBunkerBinding({ ...BASE, relays: ['wss://old/'], lnbitsServerPubkey: 'ee'.repeat(32) })
|
||||
saveBunkerBinding({ ...BASE, relays: ['wss://new/'], lnbitsServerPubkey: 'ff'.repeat(32) })
|
||||
const got = getBunkerBinding()
|
||||
expect(got?.relays).toEqual(['wss://new/'])
|
||||
expect(got?.lnbitsServerPubkey).toBe('ff'.repeat(32))
|
||||
})
|
||||
|
||||
it('returns null after clear', () => {
|
||||
saveBunkerBinding(BASE)
|
||||
clearBunkerBinding()
|
||||
expect(getBunkerBinding()).toBeNull()
|
||||
})
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue