From 7d0c19ed7f645f3ab798f7b4fa348751fbcf99f5 Mon Sep 17 00:00:00 2001 From: Padreug Date: Wed, 13 May 2026 12:56:01 +0200 Subject: [PATCH] refactor(machine): wire LnbitsClient alongside LightningPubClient MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 3b.1 of the LP→LNbits migration: structurally introduce LnbitsClient into services/lightning.ts without changing any runtime behavior. All existing call sites still go through LightningPubClient. apps/machine/src/services/lightning.ts - import LnbitsClient from @bitSpire/lnbits - add `lnbitsServerPubkey` to LightningConfig - load it from runtime IPC config + VITE_LNBITS_SERVER_PUBKEY env var (env wiring proper happens in 3c) - module-level `_lnbitsRef: LnbitsClient | null` - in initializeLightningServices, instantiate LnbitsClient ONLY IF `CONFIG.lnbitsServerPubkey` is set (graceful no-op while the env hasn't been wired yet) - export `_getLnbitsClient()` for 3b.2+ call sites apps/machine/package.json - add `@bitSpire/lnbits: workspace:*` dependency Verified: pnpm typecheck clean (14/14 turbo tasks, machine task now executes since lnbits is a new dep). Next: 3b.2 — drop the CLINK/ndebit cash-in flow. Co-Authored-By: Claude Opus 4.7 (1M context) --- apps/machine/package.json | 1 + apps/machine/src/services/lightning.ts | 43 ++++++++++++++++++++++++++ pnpm-lock.yaml | 3 ++ 3 files changed, 47 insertions(+) diff --git a/apps/machine/package.json b/apps/machine/package.json index 2a4b205..21896f5 100644 --- a/apps/machine/package.json +++ b/apps/machine/package.json @@ -24,6 +24,7 @@ "@bitSpire/clink": "workspace:*", "@bitSpire/hal": "workspace:*", "@bitSpire/lightning": "workspace:*", + "@bitSpire/lnbits": "workspace:*", "@bitSpire/nostr-client": "workspace:*", "@bitSpire/state-machine": "workspace:*", "@scure/base": "^1.2.0", diff --git a/apps/machine/src/services/lightning.ts b/apps/machine/src/services/lightning.ts index 5423856..9e3408d 100644 --- a/apps/machine/src/services/lightning.ts +++ b/apps/machine/src/services/lightning.ts @@ -24,6 +24,7 @@ import { } from '@bitSpire/nostr-client' import { verifyEvent } from 'nostr-tools' import { LightningPubClient } from '@bitSpire/lightning' +import { LnbitsClient } from '@bitSpire/lnbits' import { CLINKClient, createOfferSuccess, @@ -63,6 +64,13 @@ interface LightningConfig { atmPrivateKey: string appId: string operatorPubkeys: string[] + /** + * LNbits nostr-transport server pubkey. Optional during the LP→LNbits + * migration: when empty, the LnbitsClient is not instantiated and the + * file behaves identically to its LP-only past. Once 3c wires the env + * var, this is required. + */ + lnbitsServerPubkey: string } /** @@ -83,6 +91,7 @@ async function loadLightningConfig(): Promise { atmPrivateKey: '', appId: '30270e761f2e30b1737f34ce661df45f521352b408b8ed18fcc09f3f0dec5097', // ATM app ID — regenerated for bitSpire so stale LP server-side associations don't accidentally rehydrate operatorPubkeys: [], + lnbitsServerPubkey: '', } // In Electron, get runtime config from main process @@ -105,6 +114,9 @@ async function loadLightningConfig(): Promise { .map((k: string) => k.trim()) .filter(Boolean) : defaults.operatorPubkeys, + lnbitsServerPubkey: + (runtimeConfig as { lnbitsServerPubkey?: string }).lnbitsServerPubkey || + defaults.lnbitsServerPubkey, } } catch (e) { console.warn('[Lightning] Failed to get runtime config from Electron:', e) @@ -120,6 +132,9 @@ async function loadLightningConfig(): Promise { adminToken: import.meta.env.VITE_ADMIN_TOKEN || defaults.adminToken, atmPrivateKey: import.meta.env.VITE_ATM_PRIVATE_KEY || defaults.atmPrivateKey, appId: import.meta.env.VITE_APP_ID || defaults.appId, + lnbitsServerPubkey: + (import.meta.env.VITE_LNBITS_SERVER_PUBKEY as string | undefined) || + defaults.lnbitsServerPubkey, operatorPubkeys: import.meta.env.VITE_OPERATOR_PUBKEYS ? (import.meta.env.VITE_OPERATOR_PUBKEYS as string) .split(',') @@ -369,6 +384,17 @@ function expireLnurlSession(uniqueHash: string, lightningPub: LightningPubClient /** Clean up all active LNURL sessions. Called when state machine returns to idle. */ let _lightningPubRef: LightningPubClient | null = null +/** LNbits client reference, set during initializeLightningServices if + * VITE_LNBITS_SERVER_PUBKEY is configured. Parallel to _lightningPubRef + * during the LP→LNbits migration; 3b.2+ swap call sites over. */ +let _lnbitsRef: LnbitsClient | null = null + +/** Internal accessor — used by 3b.2+ call sites that need to invoke + * the LNbits client from outside `createATMServices`. Returns null + * when the LNbits server pubkey isn't configured. */ +export function _getLnbitsClient(): LnbitsClient | null { + return _lnbitsRef +} function cleanupAllLnurlSessions(): void { if (!_lightningPubRef) return @@ -930,6 +956,23 @@ export async function initializeLightningServices(options?: { _lightningPubRef = lightningPub console.log('[Lightning] Lightning.Pub client initialized') + // LNbits client — wired in parallel with Lightning.Pub during the + // migration. In 3b.1 it's instantiated but not yet wired into any + // ATMServices method; 3b.2-3b.4 will gradually move call sites over. + // When VITE_LNBITS_SERVER_PUBKEY isn't set we skip the init so the + // file remains LP-only at runtime. + if (CONFIG.lnbitsServerPubkey) { + const lnbits = new LnbitsClient({ + serverPubkey: CONFIG.lnbitsServerPubkey, + relays: [CONFIG.relayUrl], + }) + lnbits.initialize(nostrClient, identity) + _lnbitsRef = lnbits + console.log('[Lightning] LNbits client initialized (parallel to LP)') + } else { + console.log('[Lightning] VITE_LNBITS_SERVER_PUBKEY not set — LNbits client disabled') + } + // Create CLINK client const clink = new CLINKClient({ nostrClient, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b2b55f8..d323da1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -32,6 +32,9 @@ importers: '@bitSpire/lightning': specifier: workspace:* version: link:../../packages/lightning + '@bitSpire/lnbits': + specifier: workspace:* + version: link:../../packages/lnbits '@bitSpire/nostr-client': specifier: workspace:* version: link:../../packages/nostr-client