The bitspire-env activation seeds .env only when ABSENT (never refreshes on
redeploy), and env WINS over the pairing seed — so any value written at first
boot is frozen for the disk's life and silently masks the seed's source. That's
how a dead relay.aiolabs.dev and a provisioned VITE_OPERATOR_PUBKEYS made stale
installs "work" while a fresh machine broke.
Seed ONLY image-baked, non-maskable values (model, fiat, ELECTRON_FORCE_PROD,
DISPLAY, empty VITE_SPIRE_SEED placeholder). Relay + server pubkey come from the
seed; operator pubkey + fee config come from LNbits over the transport — so those
keys are no longer pre-seeded at all. VITE_RELAY_URL / VITE_LNBITS_SERVER_PUBKEY
are emitted only when the operator deliberately pins them via the Nix options (an
explicit override). Also drops the inert RELAY_URL/LNBITS_SERVER_PUBKEY lines from
/etc/bitspire/config.env (never loaded — EnvironmentFile is forced to .env).
Verified: built sintra-installed .env template is 5 lines, 0 maskable vars.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The bitspire-env activation seeded VITE_RELAY_URL from the relayUrl option
(default wss://relay.aiolabs.dev). Because env wins over the pairing seed, every
fresh machine pinned itself to that relay — which is dead — so a scanned seed's
relay was ignored ("No connected relays"; hit live on the aio-demo USB). Default
relayUrl to "" so both relay and server pubkey come from the seed; a non-empty
option now pins a machine (an explicit override) rather than being the default.
Descriptions updated to match.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The fresh-boot `/var/lib/bitspire/.env` template at flake.nix's
`bitspire-env` activation script seeded `VITE_RELAY_URL=` empty, which
forced every operator to run `provision-atm.sh` (or hand-edit .env)
before the renderer could resolve a relay. Meanwhile the NixOS option
`services.bitspire.relayUrl` was wired only to the dead-code
`/etc/bitspire/config.env` (mkForce-shadowed by `/var/lib/bitspire/.env`).
Thread the NixOS option through: seed `VITE_RELAY_URL=${cfg.relayUrl}`
on first boot. The .env override path remains intact — provision-atm.sh
or a hand edit still take precedence at runtime (the file is the
EnvironmentFile, not the activation-time template). Existing ATMs
already have a populated `.env` and aren't affected (the activation
script's `if [ ! -f ... ]` guard skips the rewrite).
Renderer resolution order:
/var/lib/bitspire/.env → NixOS module default → renderer fallback
(`ws://localhost:7777` in lightning.ts:63 / main.ts:284)
Also expand the `services.bitspire.relayUrl` option description so
future readers see the wire-through + the override path documented
where the option lives.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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>
Read /var/lib/bitspire/branding/{logo.png,branding.json} on startup and
apply across the renderer. branding.json may set title, theme (one of
the 6 built-ins or "custom"), and a custom_colors map (with optional
.dark overlay) — unset CSS vars fall back to gruvbox.
Wiring:
- electron/main.ts:loadBranding() reads + validates the JSON and
base64-encodes logo.png; surfaced via the existing get-config IPC
- composables/useBranding.ts holds reactive logoUrl/title refs and a
single setBranding() setter — the seam where #48's Nostr-event
source will eventually overlay the local-file source
- composables/useTheme.ts:applyBrandingTheme() handles built-in theme
swap and injects a <style#branding-custom-theme> block for custom
- IdleView binds :src/title; App.vue calls setBranding() before the
maintenance screen renders so "Under Service" wears operator branding
Provisioning: new deploy/nixos/provision-branding.sh rsyncs a local dir
to /var/lib/bitspire/branding/ via sudo-on-the-far-side and restarts
bitspire.service. The existing provision-atm.sh stays focused on .env.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
System-level half of the lamassu → bitspire rebrand the rest of dev
already did at the path / service / package layers. Touches user/group
declarations, every systemd `User=` block, the udev rules filename, all
chown calls in flake.nix + live.nix, the displayManager autoLogin user,
the trusted-users nix entry, provision-atm.sh's ATM_USER, plus README +
CLAUDE.md doc references.
In-place migration for the Sintra dev unit (which auto-pulls dev at
04:00) lives in `system.activationScripts.bitspire-user-migration` and:
- copies `/home/lamassu/.ssh/authorized_keys` → `/home/bitspire/` once,
so SSH access survives the rename
- recursively chowns `/var/lib/bitspire` to the new bitspire UID on
every boot — cheap no-op once done, but covers the case where the
data dir was written by the now-removed lamassu UID
- leaves `/home/lamassu/` in place as evidence; operator can `rm -rf`
after confirming bitspire login works
Recovery path if the migration breaks SSH access: root key is still in
configuration.nix:142-144 (padreug@gizmo), so ssh root@<host> works.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
Five-file coordinated rename to give the dev-branch deploy a clean
`bitspire` namespace at the NixOS level:
deploy/nixos/lamassu-atm.nix → deploy/nixos/bitspire-atm.nix
- `services.lamassu-atm` → `services.bitspire`
- `systemd.services.lamassu-atm` → `systemd.services.bitspire`
- `/var/lib/lamassu-atm` → `/var/lib/bitspire`
- `/opt/lamassu-atm` → `/opt/bitspire`
- `/etc/lamassu-atm/config.env` → `/etc/bitspire/config.env`
flake.nix
- module import path updated
- `nixosModules.lamassu-atm` → `nixosModules.bitspire`
- `system.activationScripts.lamassu-env` → `bitspire-env`
- all activation-script paths point at /var/lib/bitspire
deploy/nixos/configuration.nix
- `networking.hostName = "lamassu-atm"` → `"bitspire"`
deploy/nixos/live.nix
- module import path updated
- ISO name template: `lamassu-atm-<model>-live.iso` → `bitspire-<model>-live.iso`
- activation-script name updated
deploy/nixos/provision-atm.sh
- data-dir paths: /var/lib/lamassu-atm → /var/lib/bitspire
- systemctl + journalctl unit names updated
DELIBERATELY kept as `lamassu` (for now):
- The `lamassu` UNIX user and group account. Renaming would
require file-ownership migration scripts; the Sintra is a
fresh flash so no existing data, but the internal user
namespace inconsistency is acceptable.
- LP-specific bits in provision-atm.sh (admin token, the
`docker logs lamassu-lightning-pub` extractor) — those
get ripped out in 3c when the script switches to LNbits.
NO migration activation script added — the Sintra flash is fresh,
production batm3/douro stay on `main` and never see this branch.
A future dev→main cutover will need a separate migration story
(rename UNIX user, move /var/lib/lamassu-atm → /var/lib/bitspire,
SSH key relocation, etc.).
Verified:
nix eval .#nixosConfigurations.batm3-installed.config.systemd.services.bitspire.enable
→ true
nix eval .#nixosConfigurations.bitSpire-live-sintra.config.networking.hostName
→ "bitspire"
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-01 19:08:03 +02:00
Renamed from deploy/nixos/lamassu-atm.nix (Browse further)