refactor(machine): drop VITE_LNBITS_HTTP_URL — lnurl now arrives populated from LNbits (#57 gap 2)

Closes gap 2 from coord log 2026-06-01T18:30Z. The LNbits withdraw
extension's nostr-transport RPC now populates `link.lnurl` from
`settings.lnbits_baseurl` (aiolabs/withdraw#1 / commit e9d911e), so the
ATM no longer needs a separate HTTP URL on the wire to compose the
LNURL-withdraw callback itself.

What goes:

- `VITE_LNBITS_HTTP_URL` env var (renderer + Electron main)
- `lnbitsHttpUrl` field on `LightningConfig`, `RuntimeConfig`, and the
  Window mirror in `src/types/electron.d.ts`
- The manual `${lnbitsHttpUrl}/withdraw/api/v1/lnurl/${unique_hash}`
  composition in `generateLnurlWithdraw`
- The `encodeLnurl` bech32 helper in `lightning.ts` (LNbits returns
  bech32-encoded; we just `.toUpperCase()` to match BOLT/LNURL convention)
- `@scure/base` dep from `apps/machine/package.json` (only used by the
  removed helper; clink still uses it directly)
- The `lnbitsHttpUrl` option + `LNBITS_HTTP_URL=…` env var + boot echo
  in `deploy/nixos/bitspire-atm.nix`
- Doc references in CLAUDE.md, README.md, deploy/nixos/README.md,
  docs/architecture-comparison.md, and the lightning-check skill

What stays:

- `link.lnurl` consumption, with an explicit error if LNbits returns
  null (which signals `LNBITS_BASEURL` is unset on the server side —
  better to fail clearly than silently)
- The receiver-side bech32 uppercasing (LNbits returns lowercase per
  the standard library)

Why this is a net win:

- Removes a config-drift surface — if LNbits's external URL moved
  (DNS, port, reverse-proxy rewrite), every ATM in the field would
  stop issuing redeemable LNURL-withdraw QRs until reconfigured.
  Now LNbits derives its own URL from `settings.lnbits_baseurl`,
  one source of truth.
- Removes an extra provisioning step. No more `LNBITS_HTTP_URL=…`
  before running `provision-atm.sh`; the relay + server pubkey suffice.
- Removes the misleading boot echo that triggered the §`18:30Z`
  smoke triage confusion ("LNbits HTTP: <url>" read like ATM-→-LNbits
  connectivity, when it was only ever a URL embedded in customer QRs).

Also adds a `# pragma: allowlist secret` marker above the
`VITE_ATM_PRIVATE_KEY` doc block in `.env.example` so the global
secret scanner stops false-positiving on the documentation prose.

Workspace typecheck + 24/24 apps/machine tests still green.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Padreug 2026-06-01 20:33:28 +02:00
commit 4f68ddc40b
14 changed files with 63 additions and 91 deletions

View file

@ -1,7 +1,13 @@
# bitSpire ATM Service Module
# Manages the ATM Electron application and related services
{ config, lib, pkgs, pkgs-unstable, ... }:
{
config,
lib,
pkgs,
pkgs-unstable,
...
}:
with lib;
@ -29,17 +35,6 @@ in
'';
};
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 {
type = types.path;
default = "/opt/bitspire";
@ -53,7 +48,12 @@ in
};
logLevel = mkOption {
type = types.enum [ "error" "warn" "info" "debug" ];
type = types.enum [
"error"
"warn"
"info"
"debug"
];
default = "info";
description = "Logging level for the ATM application";
};
@ -73,7 +73,11 @@ in
};
type = mkOption {
type = types.enum [ "id003" "mei" "ccnet" ];
type = types.enum [
"id003"
"mei"
"ccnet"
];
default = "id003";
description = "Bill validator protocol type";
};
@ -93,7 +97,10 @@ in
};
type = mkOption {
type = types.enum [ "puloon" "genmega" ];
type = types.enum [
"puloon"
"genmega"
];
default = "puloon";
description = "Bill dispenser type";
};
@ -128,7 +135,6 @@ in
# bitSpire ATM Configuration
RELAY_URL=${cfg.relayUrl}
LNBITS_SERVER_PUBKEY=${cfg.lnbitsServerPubkey}
LNBITS_HTTP_URL=${cfg.lnbitsHttpUrl}
LOG_LEVEL=${cfg.logLevel}
DATA_DIR=${cfg.dataDir}
@ -153,7 +159,10 @@ in
systemd.services.bitspire = {
description = "bitSpire ATM Application";
wantedBy = [ "graphical.target" ];
after = [ "graphical.target" "network-online.target" ];
after = [
"graphical.target"
"network-online.target"
];
wants = [ "network-online.target" ];
serviceConfig = {
@ -180,7 +189,10 @@ in
NoNewPrivileges = true;
ProtectSystem = "strict";
ProtectHome = true;
ReadWritePaths = [ cfg.dataDir "/tmp" ];
ReadWritePaths = [
cfg.dataDir
"/tmp"
];
PrivateTmp = true;
# Allow device access for hardware
@ -196,7 +208,6 @@ in
preStart = ''
echo "bitSpire starting..."
echo "Relay: ${cfg.relayUrl}"
echo "LNbits HTTP: ${cfg.lnbitsHttpUrl}"
# Check bill validator if enabled
if [ "${boolToString cfg.billValidator.enable}" = "true" ]; then