feat: externalize Lightning.Pub configuration to environment variables

- Replace hardcoded DEV_CONFIG with environment variable loading
- Add VITE_RELAY_URL for Nostr relay WebSocket URL
- Add VITE_LIGHTNING_PUB_PUBKEY for Lightning.Pub's Nostr pubkey (required)
- Add VITE_LIGHTNING_PUB_API_URL for HTTP API
- Add VITE_ATM_PRIVATE_KEY for persistent ATM identity
- Generate ephemeral identity if no private key configured
- Update machine installation docs with correct env var names

Environment variables:
- VITE_RELAY_URL (default: ws://localhost:7777)
- VITE_LIGHTNING_PUB_PUBKEY (required)
- VITE_LIGHTNING_PUB_API_URL (default: http://localhost:1776)
- VITE_ATM_PRIVATE_KEY (optional, generates ephemeral if not set)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Patrick Mulligan 2026-01-30 10:45:49 -05:00
commit c604aa501c
2 changed files with 75 additions and 37 deletions

View file

@ -33,25 +33,37 @@ import type { ATMServices, ATMContext } from '@lamassu/state-machine'
// Check if we're in a browser environment
const isBrowser = typeof window !== 'undefined'
// Development infrastructure configuration
// In production, these would come from environment or secure config
// Use local network IP for testing from other devices (e.g., Shock Wallet on phone)
const LOCAL_IP = '192.168.1.122' // Change this to your machine's local IP
/**
* Lightning configuration from environment variables
*
* Environment variables (prefix with VITE_ for Vite):
* - VITE_RELAY_URL: Nostr relay WebSocket URL
* - VITE_LIGHTNING_PUB_PUBKEY: Lightning.Pub's Nostr pubkey (hex or npub)
* - VITE_LIGHTNING_PUB_API_URL: Lightning.Pub HTTP API URL
* - VITE_ATM_PRIVATE_KEY: ATM's Nostr private key (hex or nsec)
* - VITE_ADMIN_TOKEN: Lightning.Pub admin token (dev only)
*/
function loadLightningConfig() {
// Development defaults (local Docker infrastructure)
const defaults = {
relayUrl: 'ws://localhost:7777',
lightningPubPubkey: '',
lightningPubApiUrl: 'http://localhost:1776',
adminToken: 'lamassu-dev-admin-token',
atmPrivateKey: '',
}
const DEV_CONFIG = {
// Lightning.Pub Nostr pubkey (from docker logs - check with: docker logs lamassu-lightning-pub | grep pubkey)
lightningPubPubkey: '4be8e203a3341bb2b74a4dcbf8774e061437f63ec21af7ec3144c8d0a68e2f39',
// Local strfry relay - use local IP for cross-device testing
relayUrl: `ws://${LOCAL_IP}:7777`,
// Admin token for HTTP API (development only)
adminToken: 'lamassu-dev-admin-token',
// Lightning.Pub HTTP API
lightningPubApiUrl: `http://${LOCAL_IP}:1776`,
// Fixed dev identity for testing (fund this pubkey in Lightning.Pub)
// In production, identity is loaded from secure storage
devPrivateKey: '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef',
return {
relayUrl: import.meta.env.VITE_RELAY_URL || defaults.relayUrl,
lightningPubPubkey: import.meta.env.VITE_LIGHTNING_PUB_PUBKEY || defaults.lightningPubPubkey,
lightningPubApiUrl: import.meta.env.VITE_LIGHTNING_PUB_API_URL || defaults.lightningPubApiUrl,
adminToken: import.meta.env.VITE_ADMIN_TOKEN || defaults.adminToken,
atmPrivateKey: import.meta.env.VITE_ATM_PRIVATE_KEY || defaults.atmPrivateKey,
}
}
const CONFIG = loadLightningConfig()
interface LightningServices {
nostrClient: NostrClient
lightningPub: LightningPubClient
@ -75,27 +87,45 @@ type PaymentReceivedCallback = (preimage: string) => void
*/
export async function initializeLightningServices(): Promise<LightningServices> {
console.log('[Lightning] Initializing services...')
console.log('[Lightning] Relay URL:', CONFIG.relayUrl)
console.log('[Lightning] Lightning.Pub pubkey:', CONFIG.lightningPubPubkey || '(not configured)')
// Use fixed dev identity for testing (in production, load from secure storage)
// This allows us to fund the account once and reuse across page reloads
const identity = isBrowser ? loadIdentityFromHex(DEV_CONFIG.devPrivateKey) : generateIdentity()
console.log('[Lightning] Machine identity:', identity.publicKey)
console.log('[Lightning] Fund this pubkey in Lightning.Pub for testing')
// Validate required configuration
if (!CONFIG.lightningPubPubkey) {
throw new Error(
'[Lightning] VITE_LIGHTNING_PUB_PUBKEY is required. ' +
'Get it from: docker logs lamassu-lightning-pub | grep pubkey'
)
}
// Load or generate ATM identity
let identity: MachineIdentity
if (CONFIG.atmPrivateKey) {
// Use configured private key
identity = loadIdentityFromHex(CONFIG.atmPrivateKey)
console.log('[Lightning] Loaded ATM identity from config')
} else {
// Generate new identity (for development/testing)
identity = generateIdentity()
console.warn('[Lightning] No VITE_ATM_PRIVATE_KEY configured - generated ephemeral identity')
console.warn('[Lightning] Set VITE_ATM_PRIVATE_KEY for persistent identity across restarts')
}
console.log('[Lightning] ATM pubkey:', identity.publicKey)
// Create Nostr client
const nostrClient = new NostrClient({
relays: [{ url: DEV_CONFIG.relayUrl }],
relays: [{ url: CONFIG.relayUrl }],
identity,
})
// Connect to relay
await nostrClient.connect()
console.log('[Lightning] Connected to relay:', DEV_CONFIG.relayUrl)
console.log('[Lightning] Connected to relay:', CONFIG.relayUrl)
// Create Lightning.Pub client
const lightningPub = new LightningPubClient({
accountPubkey: DEV_CONFIG.lightningPubPubkey,
relays: [DEV_CONFIG.relayUrl],
accountPubkey: CONFIG.lightningPubPubkey,
relays: [CONFIG.relayUrl],
})
lightningPub.initialize(nostrClient, identity)
@ -105,8 +135,8 @@ export async function initializeLightningServices(): Promise<LightningServices>
const clink = new CLINKClient({
nostrClient,
identity,
operatorPubkey: DEV_CONFIG.lightningPubPubkey, // Use Lightning.Pub as operator for dev
relays: [DEV_CONFIG.relayUrl],
operatorPubkey: CONFIG.lightningPubPubkey, // Use Lightning.Pub as operator for dev
relays: [CONFIG.relayUrl],
})
// Callbacks for events
@ -208,8 +238,8 @@ function createATMServices(
// so it must be Lightning.Pub's pubkey for it to receive and pay.
// The pointer identifies which Lightning.Pub user account pays (e.g., "atm")
const ndebit = encodeNdebit({
pubkey: DEV_CONFIG.lightningPubPubkey,
relay: DEV_CONFIG.relayUrl,
pubkey: CONFIG.lightningPubPubkey,
relay: CONFIG.relayUrl,
pointer: 'atm', // Lightning.Pub user identifier for the ATM account
})
@ -514,4 +544,4 @@ export function watchInvoice(
})
}
export { DEV_CONFIG }
export { CONFIG }

View file

@ -119,16 +119,24 @@ See [Device Configuration](./device-configuration.md) for detailed configuration
The ATM needs to connect to a Lightning.Pub instance. Configure via environment:
```bash
# Nostr relay URL
VITE_NOSTR_RELAY_URL=wss://your-relay.example.com
# Nostr relay WebSocket URL (required)
VITE_RELAY_URL=wss://your-relay.example.com
# Lightning.Pub public key
VITE_LIGHTNING_PUB_PUBKEY=npub1...
# Lightning.Pub's Nostr public key (required)
# Get from: docker logs lamassu-lightning-pub | grep pubkey
VITE_LIGHTNING_PUB_PUBKEY=4be8e203a3341bb2b74a4dcbf8774e061437f63ec21af7ec3144c8d0a68e2f39
# ATM keypair (generate with: npx @lamassu/nostr-client generate-keypair)
VITE_ATM_NSEC=nsec1...
# Lightning.Pub HTTP API URL (optional, for admin operations)
VITE_LIGHTNING_PUB_API_URL=https://lp.operator.com
# ATM's Nostr private key (recommended for persistent identity)
# Generate with: npx @lamassu/nostr-client generate-keypair
# If not set, a new ephemeral identity is generated on each restart
VITE_ATM_PRIVATE_KEY=0123456789abcdef...
```
**Important:** The `VITE_LIGHTNING_PUB_PUBKEY` is required. Without it, the ATM cannot communicate with Lightning.Pub.
## Auto-Start on Boot
### Systemd Service