bitspire/deploy/nixos/provision-atm.sh
Padreug bf2b9427aa refactor(config): rename VITE_LAMASSU_* machine-config env vars to VITE_BITSPIRE_*
MACHINE_MODEL, FIAT_CODE, VALIDATOR_DEVICE, DISPENSER_DEVICE and CASSETTES
carried the old brand in their names. Renamed everywhere they are read
(device.ts, electron/main.ts), written (flake.nix, mkAtmApp.nix, live.nix,
provision-atm.sh, factory-reset-atm.sh) and documented (.env.example,
docs/device-configuration.md). No compatibility fallback in code: the
machine reads VITE_BITSPIRE_* and nothing else.

The deployed .env files are the one place the old names persist — sintra's
/var/lib/bitspire/.env holds all three keys today — and the machine reads
MACHINE_MODEL / FIAT_CODE / CASSETTES from that file on every boot. Renaming
the keys in code alone would boot a live machine on preset defaults (wrong
bays, wrong fiat) at the next nightly pull. So configuration.nix gains an
activation script, beside the existing lamassu→bitspire user migration,
that rewrites VITE_LAMASSU_* → VITE_BITSPIRE_* in that file. Idempotent;
runs before bitspire.service starts.
2026-10-09 21:57:38 +02:00

151 lines
6.4 KiB
Bash
Executable file

#!/usr/bin/env bash
# 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. # pragma: allowlist secret
#
# The primary input is SPIRE_SEED — the pairing seed carries the relay, the
# LNbits server pubkey AND the signing identity, so a seed-provisioned machine
# needs nothing else (aiolabs/bitspire#70).
#
# Environment variables:
# SPIRE_SEED RECOMMENDED. The spire pairing seed
# (`spire-seed:v1:<base64url>`) minted by spirekeeper.
# Carries relay + LNbits server pubkey + the production
# identity under the NIP-46 bunker (aiolabs/bitspire#52 / #70).
# RELAY_URL OPTIONAL override — pins VITE_RELAY_URL and WINS over the
# seed's relay (env-first precedence). Leave unset to let the
# seed drive it. Required only on the no-seed dev path
# (default there: ws://$HOST_IP:5001/nostrrelay/test).
# LNBITS_SERVER_PUBKEY OPTIONAL override (hex). Leave unset with a seed. On the
# no-seed dev path it's scraped from
# `docker logs lnbits | grep 'nostr_transport pubkey'`.
# ATM_PRIVATE_KEY DEV-ONLY 32-byte hex nsec fallback, used only when
# SPIRE_SEED is unset (no bunker). Generated if unset
# AND no SPIRE_SEED is provided.
#
# Usage:
# bash provision-atm.sh # defaults: SSH to localhost:2222 (QEMU)
# bash provision-atm.sh 192.168.1.50 # SSH to a real ATM on the LAN
# bash provision-atm.sh 192.168.1.50 22 # custom SSH port
set -euo pipefail
ATM_HOST="${1:-localhost}"
ATM_SSH_PORT="${2:-2222}"
ATM_USER="bitspire"
# Machine model + fiat. Model is operator-set; fiat defaults per-model to
# match the flake's fiatCodeForModel table (sintra=EUR, douro/tejo=GTQ,
# batm3=USD). Operators can override either via env var.
MODEL="${MODEL:-sintra}"
case "${FIAT_CODE:-}" in
"")
case "$MODEL" in
sintra) FIAT_CODE=EUR ;;
douro|tejo) FIAT_CODE=GTQ ;;
batm3) FIAT_CODE=USD ;;
*) FIAT_CODE=USD ;;
esac
;;
esac
echo "=== Provisioning bitSpire ATM at $ATM_HOST:$ATM_SSH_PORT ==="
# 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 ""
echo "--- QEMU detected: using $HOST_IP as host gateway ---"
else
HOST_IP=$(ip -4 route get 1 | awk '{print $7; exit}')
echo ""
echo "--- LAN ATM: using $HOST_IP as dev machine address ---"
fi
# Steps 2-4: transport config (relay + LNbits server pubkey) + signing identity.
#
# Under aiolabs/bitspire#70 the relay + server pubkey come from the pairing SEED,
# so a seed-provisioned machine needs NEITHER in .env. We only pin them when the
# operator EXPLICITLY passes RELAY_URL / LNBITS_SERVER_PUBKEY (a deliberate
# override that WINS over the seed via env-first precedence), or when there is no
# seed (the dev-nsec fallback has nothing else to supply them, so we scrape/default).
TRANSPORT_LINES=""
if [ -n "${SPIRE_SEED:-}" ]; then
case "$SPIRE_SEED" in
spire-seed:v1:*) : ;;
*) echo "ERROR: SPIRE_SEED must start with 'spire-seed:v1:'"; exit 1 ;;
esac
echo ""
echo "--- Spire pairing seed: relay + LNbits pubkey come from the seed ---"
if [ -n "${RELAY_URL:-}" ]; then
echo " (pinning VITE_RELAY_URL=$RELAY_URL — overrides the seed's relay)"
TRANSPORT_LINES="VITE_RELAY_URL=$RELAY_URL"
fi
if [ -n "${LNBITS_SERVER_PUBKEY:-}" ]; then
TRANSPORT_LINES="${TRANSPORT_LINES:+$TRANSPORT_LINES
}VITE_LNBITS_SERVER_PUBKEY=$LNBITS_SERVER_PUBKEY"
fi
IDENTITY_LINES="# Spire pairing seed — bunker-backed identity (aiolabs/bitspire#52)
VITE_SPIRE_SEED=$SPIRE_SEED"
else
# No seed → DEV-ONLY nsec fallback. Nothing else supplies the relay + pubkey,
# so scrape/default them.
if [ -z "${LNBITS_SERVER_PUBKEY:-}" ]; then
echo ""
echo "--- No seed: 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: no SPIRE_SEED, and could not extract the LNbits pubkey."
echo "Provide a SPIRE_SEED (recommended — the seed carries relay + pubkey),"
echo "or set LNBITS_SERVER_PUBKEY explicitly."
exit 1
fi
fi
RELAY_URL="${RELAY_URL:-ws://$HOST_IP:5001/nostrrelay/test}"
TRANSPORT_LINES="VITE_RELAY_URL=$RELAY_URL
VITE_LNBITS_SERVER_PUBKEY=$LNBITS_SERVER_PUBKEY"
if [ -z "${ATM_PRIVATE_KEY:-}" ]; then
ATM_PRIVATE_KEY=$(openssl rand -hex 32)
echo ""
echo "--- No SPIRE_SEED; generated a DEV-ONLY ATM_PRIVATE_KEY (no bunker) ---"
fi
IDENTITY_LINES="# DEV-ONLY local nsec (no bunker pairing) # pragma: allowlist secret
VITE_ATM_PRIVATE_KEY=$ATM_PRIVATE_KEY"
fi
# Step 6: Write .env to the ATM via SSH.
echo ""
echo "--- Step 2: Writing .env to ATM ---"
ENV_CONTENT="# bitSpire Configuration
# Auto-generated by provision-atm.sh on $(date -Iseconds)
# LNbits nostr-transport. Relay + server pubkey come from the pairing seed
# (aiolabs/bitspire#70); present below only as an explicit override or the
# no-seed dev fallback.
$TRANSPORT_LINES
$IDENTITY_LINES
# Machine configuration
VITE_BITSPIRE_MACHINE_MODEL=$MODEL
VITE_BITSPIRE_FIAT_CODE=$FIAT_CODE
# Force production mode
ELECTRON_FORCE_PROD=1
DISPLAY=:0"
ssh -o StrictHostKeyChecking=no -p "$ATM_SSH_PORT" "$ATM_USER@$ATM_HOST" \
"echo '$ENV_CONTENT' | sudo tee /var/lib/bitspire/.env > /dev/null && sudo systemctl restart bitspire"
echo ""
echo "=== ATM provisioned successfully ==="
echo ""
echo "Credentials written to /var/lib/bitspire/.env"
echo "ATM service restarted. Relay: ${RELAY_URL:-from the pairing seed}."
echo ""
echo "To check status: ssh -p $ATM_SSH_PORT $ATM_USER@$ATM_HOST 'sudo journalctl -u bitspire -f'"