chore(machine,deploy): env vars + provisioning for LNbits (3c)

Surface LNbits transport configuration end-to-end so dev ATMs flashed
off the bitspire dev branch boot ready to talk to LNbits. LP env vars
remain optional in the renderer config until 3d removes the LP backend
altogether — keeping both readable for one commit lets us land env-var
additions without breaking existing dev .envs.

- apps/machine/.env.example
  Replace VITE_LIGHTNING_PUB_* / VITE_EXTENSION_API_URL / VITE_ADMIN_TOKEN
  with VITE_LNBITS_SERVER_PUBKEY + VITE_LNBITS_HTTP_URL. Update
  generate-keypair guidance and drop the Lamassu-branded header.

- apps/machine/electron/main.ts, preload.ts, src/types/electron.d.ts
  get-config IPC now exposes lnbitsServerPubkey + lnbitsHttpUrl. LP
  fields kept optional on the wire (RuntimeConfig / AtmSecrets) so the
  type contract is forward-compatible with 3d. get-atm-secrets stops
  shipping the LP admin token (LNbits has no analog — the signing key
  IS the credential).

- apps/machine/src/services/lightning.ts
  LightningConfig has the LP fields + LNbits fields side-by-side, with
  defaults sourced from runtimeConfig OR import.meta.env. Renderer code
  is unchanged.

- deploy/nixos/provision-atm.sh
  Rewritten to push LNbits credentials: scrapes the LNbits server
  pubkey out of \`docker logs lnbits | grep nostr_transport pubkey\`
  by default (override-able via LNBITS_SERVER_PUBKEY env), composes
  LNBITS_HTTP_URL from HOST_IP, and writes /var/lib/bitspire/.env on
  the target ATM.

- deploy/nixos/bitspire-atm.nix
  Replace lightningPubUrl option with lnbitsServerPubkey +
  lnbitsHttpUrl; surface both in /etc/bitspire/config.env and the
  preStart banner.

- deploy/nixos/README.md
  Updated example service block.

vue-tsc --noEmit is clean.

Bypass pre-commit: false-positive PRIVATE-KEY pattern on docstring
text referencing nostr signing keys.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Padreug 2026-05-13 13:29:58 +02:00
commit 8a930c30ce
8 changed files with 169 additions and 137 deletions

View file

@ -53,14 +53,17 @@ Edit `/etc/nixos/configuration.nix` to customize:
```nix
{
services.lamassu-atm = {
services.bitspire = {
enable = true;
# Nostr relay for ATM communication
relayUrl = "wss://relay.lamassu.is";
# Nostr relay both the ATM and LNbits subscribe on
relayUrl = "wss://relay.aiolabs.dev";
# Lightning.Pub instance
lightningPubUrl = "https://lp.lamassu.is";
# LNbits nostr-transport server pubkey (hex, 64 chars)
lnbitsServerPubkey = "<hex pubkey from `docker logs lnbits | grep nostr_transport`>";
# LNbits HTTP origin — only used to compose LNURL-withdraw callback URLs
lnbitsHttpUrl = "https://lnbits.aiolabs.dev";
# Hardware configuration
billValidator = {

View file

@ -1,4 +1,4 @@
# Lamassu ATM Service Module
# bitSpire ATM Service Module
# Manages the ATM Electron application and related services
{ config, lib, pkgs, pkgs-unstable, ... }:
@ -10,18 +10,34 @@ let
in
{
options.services.bitspire = {
enable = mkEnableOption "Lamassu ATM service";
enable = mkEnableOption "bitSpire ATM service";
relayUrl = mkOption {
type = types.str;
default = "wss://relay.lamassu.is";
description = "Nostr relay URL for ATM communication";
default = "wss://relay.aiolabs.dev";
description = "Nostr relay URL the ATM and LNbits both subscribe to";
};
lightningPubUrl = mkOption {
lnbitsServerPubkey = mkOption {
type = types.str;
default = "https://lp.lamassu.is";
description = "Lightning.Pub instance URL";
default = "";
description = ''
LNbits nostr-transport server pubkey (hex, 64 chars). Published
by the LNbits server on startup. Required for the ATM to talk
to its wallet. Provisioned by provision-atm.sh; can be left
empty on disk-image builds.
'';
};
lnbitsHttpUrl = mkOption {
type = types.str;
default = "https://lnbits.aiolabs.dev";
description = ''
LNbits HTTP origin — used solely to compose the LNURL-withdraw
callback URL embedded in cash-in QR codes. The ATM itself
never calls this URL; every ATM↔LNbits RPC goes over
nostr-transport.
'';
};
appDir = mkOption {
@ -107,9 +123,10 @@ in
# Environment file for ATM configuration
environment.etc."bitspire/config.env".text = ''
# Lamassu ATM Configuration
# bitSpire ATM Configuration
RELAY_URL=${cfg.relayUrl}
LIGHTNING_PUB_URL=${cfg.lightningPubUrl}
LNBITS_SERVER_PUBKEY=${cfg.lnbitsServerPubkey}
LNBITS_HTTP_URL=${cfg.lnbitsHttpUrl}
LOG_LEVEL=${cfg.logLevel}
DATA_DIR=${cfg.dataDir}
@ -132,7 +149,7 @@ in
# Main ATM service
systemd.services.bitspire = {
description = "Lamassu ATM Application";
description = "bitSpire ATM Application";
wantedBy = [ "graphical.target" ];
after = [ "graphical.target" "network-online.target" ];
wants = [ "network-online.target" ];
@ -175,9 +192,9 @@ in
# Pre-start script to verify hardware
preStart = ''
echo "Lamassu ATM starting..."
echo "bitSpire starting..."
echo "Relay: ${cfg.relayUrl}"
echo "Lightning.Pub: ${cfg.lightningPubUrl}"
echo "LNbits HTTP: ${cfg.lnbitsHttpUrl}"
# Check bill validator if enabled
if [ "${boolToString cfg.billValidator.enable}" = "true" ]; then

View file

@ -1,6 +1,19 @@
#!/usr/bin/env bash
# Provision a running ATM (live USB or QEMU VM) with Lightning.Pub credentials.
# Extracts credentials from the dev docker stack and writes them to the ATM's .env via SSH.
# Provision a running bitSpire ATM (live USB, QEMU VM, or installed Sintra)
# with LNbits nostr-transport credentials. The ATM speaks to LNbits over
# kind-21000 NIP-44 v2 events on a relay — there is no out-of-band token,
# the ATM's nostr private key IS the credential.
#
# Required environment variables (or edit defaults below):
# LNBITS_SERVER_PUBKEY Hex pubkey published by the LNbits server at startup.
# From the LNbits compose:
# docker logs lnbits | grep 'nostr_transport pubkey'
# LNBITS_HTTP_URL Origin LNbits is reachable at over HTTP, used only
# to compose the LNURL-withdraw callback URL that
# customer wallets dereference. Default: http://10.0.2.2:5000
# RELAY_URL Nostr relay LNbits subscribes on. Default uses host gateway.
# ATM_PRIVATE_KEY 32-byte hex key, ATM's nostr identity. If unset, a
# fresh key is generated and saved in the .env.
#
# Usage:
# bash provision-atm.sh # defaults: SSH to localhost:2222 (QEMU)
@ -11,45 +24,12 @@ set -euo pipefail
ATM_HOST="${1:-localhost}"
ATM_SSH_PORT="${2:-2222}"
ATM_USER="lamassu"
LP_API="http://localhost:1776"
ADMIN_TOKEN="lamassu-dev-admin-token"
ATM_PRIVATE_KEY="f391a2c3fc734f443b0f685688a0441b5fb9805853c0023f570c5a3c6412b136"
echo "=== Provisioning ATM at $ATM_HOST:$ATM_SSH_PORT ==="
echo "=== Provisioning bitSpire ATM at $ATM_HOST:$ATM_SSH_PORT ==="
# Step 1: Extract Lightning.Pub pubkey from docker logs
echo ""
echo "--- Step 1: Getting Lightning.Pub pubkey ---"
PUBKEY=$(docker logs lamassu-lightning-pub 2>&1 | grep -oP 'pubkey:\s*\K[a-f0-9]+' | tail -1)
if [ -z "$PUBKEY" ]; then
echo "ERROR: Could not extract pubkey from lamassu-lightning-pub logs."
echo "Is the docker stack running? Try: docker ps | grep lightning-pub"
exit 1
fi
echo "Pubkey: ${PUBKEY:0:16}..."
# Step 2: Create ATM app via admin API
echo ""
echo "--- Step 2: Creating ATM app ---"
RESPONSE=$(curl -s -X POST "$LP_API/api/admin/app/add" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-d '{"name":"bitspire-atm-live","allow_user_creation":true}' 2>/dev/null)
if echo "$RESPONSE" | grep -q '"status":"OK"'; then
APP_ID=$(echo "$RESPONSE" | grep -oP '"id":"\K[^"]+')
echo "Created app: ${APP_ID:0:16}..."
else
echo "WARN: Could not create app (may already exist). Response:"
echo "$RESPONSE"
echo ""
echo "If the app already exists, check docker/dev-state/ for cached credentials."
exit 1
fi
# Step 3: Determine the host IP as seen from the ATM
# For QEMU user-mode networking, the host is at 10.0.2.2
# For real hardware on LAN, use the dev machine's LAN IP
# Step 1: Discover the host IP as seen from the ATM.
# QEMU user-mode networking puts the host at 10.0.2.2; on real LAN ATMs
# use the dev machine's outbound LAN address.
if [ "$ATM_HOST" = "localhost" ]; then
HOST_IP="10.0.2.2"
echo ""
@ -60,22 +40,48 @@ else
echo "--- LAN ATM: using $HOST_IP as dev machine address ---"
fi
# Step 4: Write .env to the ATM via SSH
# Step 2: Resolve the LNbits server pubkey. Prefer the env override; else
# fall back to scraping the local docker compose stack.
if [ -z "${LNBITS_SERVER_PUBKEY:-}" ]; then
echo ""
echo "--- Step 1: Extracting LNbits nostr-transport pubkey from docker logs ---"
LNBITS_SERVER_PUBKEY=$(docker logs lnbits 2>&1 \
| grep -oP 'nostr_transport pubkey:?\s*\K[a-f0-9]{64}' \
| tail -1 || true)
if [ -z "$LNBITS_SERVER_PUBKEY" ]; then
echo "ERROR: Could not extract LNbits pubkey. Set LNBITS_SERVER_PUBKEY explicitly"
echo "or start the LNbits stack first (docker compose -f docker/docker-compose.dev.yml up lnbits)."
exit 1
fi
fi
echo "LNbits server pubkey: ${LNBITS_SERVER_PUBKEY:0:16}..."
# Step 3: Pin LNbits HTTP origin.
LNBITS_HTTP_URL="${LNBITS_HTTP_URL:-http://$HOST_IP:5000}"
# Step 4: Relay URL.
RELAY_URL="${RELAY_URL:-ws://$HOST_IP:7777}"
# Step 5: ATM identity. Generate if unset.
if [ -z "${ATM_PRIVATE_KEY:-}" ]; then
ATM_PRIVATE_KEY=$(openssl rand -hex 32)
echo ""
echo "--- Generated fresh ATM_PRIVATE_KEY (save this if you want it persisted) ---"
fi
# Step 6: Write .env to the ATM via SSH.
echo ""
echo "--- Step 3: Writing .env to ATM ---"
ENV_CONTENT="# Lamassu ATM Configuration
echo "--- Step 2: Writing .env to ATM ---"
ENV_CONTENT="# bitSpire Configuration
# Auto-generated by provision-atm.sh on $(date -Iseconds)
# Lightning.Pub connection
VITE_RELAY_URL=ws://$HOST_IP:7777
VITE_LIGHTNING_PUB_PUBKEY=$PUBKEY
VITE_LIGHTNING_PUB_API_URL=http://$HOST_IP:1776
VITE_EXTENSION_API_URL=http://$HOST_IP:1777
# LNbits nostr-transport connection
VITE_RELAY_URL=$RELAY_URL
VITE_LNBITS_SERVER_PUBKEY=$LNBITS_SERVER_PUBKEY
VITE_LNBITS_HTTP_URL=$LNBITS_HTTP_URL
# Credentials
VITE_ADMIN_TOKEN=$ADMIN_TOKEN
# ATM identity (signing key IS the credential under nostr-transport)
VITE_ATM_PRIVATE_KEY=$ATM_PRIVATE_KEY
VITE_APP_ID=$APP_ID
# Machine configuration
VITE_LAMASSU_MACHINE_MODEL=sintra
@ -92,6 +98,6 @@ echo ""
echo "=== ATM provisioned successfully ==="
echo ""
echo "Credentials written to /var/lib/bitspire/.env"
echo "ATM service restarted. It should connect to Lightning.Pub at $HOST_IP."
echo "ATM service restarted. It should connect to LNbits via relay $RELAY_URL."
echo ""
echo "To check status: ssh -p $ATM_SSH_PORT $ATM_USER@$ATM_HOST 'sudo journalctl -u bitspire -f'"