Commit graph

39 commits

Author SHA1 Message Date
e98fd67653 feat(deploy): set the timezone per machine, sintra to Europe/Paris
Every machine inherited America/Guatemala from the shared base config,
which is right for douro and tejo and wrong for sintra in France. It read
six hours behind, so its journal timestamps had to be converted by hand
against anything on the relay.

The clock is not cosmetic here. system.autoUpgrade's `dates = "04:00"` is
local time, so the zone decides when the nightly rebuild restarts the app
and the Fujitsu dispenser runs its audible init routine. On sintra that
was firing at 10:17 in the morning, in the room, rather than at 4am.

timeZoneForModel mirrors fiatCodeForModel and lists only the exceptions.
The base config keeps America/Guatemala as the fleet default, now under
mkDefault so a per-model value wins without mkForce. Verified by eval:
sintra resolves to Europe/Paris, douro, tejo and batm3 are unchanged.

batm3 is deliberately left alone. It is USD and in the field, and I do
not know where.
2026-09-24 12:26:55 +02:00
46e52f6598 chore: scrub "Lamassu" from shipped labels
The kiosk's <title> still read "Lamassu ATM" — visible as the browser tab
on the public demo, and inherited by the Electron window. The product has
been bitSpire since the rename; Lamassu belongs in the provenance credits
(README, the c0b69d1 boundary note), not on the artifact.

Rename the user-facing labels that ship: the page title, the flake
description (surfaces in `nix flake metadata`), the ISO build banner, the
header comments on the live-USB config / udev rules / app derivation that
land on the machine image, and the workspace packages' descriptions.

Deliberately NOT touched, because they are identifiers rather than labels
and renaming them has deployed-machine consequences:
- VITE_LAMASSU_MACHINE_MODEL / VITE_LAMASSU_FIAT_CODE (provisioned .env)
- LamassuEventKind (exported enum)
- localStorage keys lamassu-theme / lamassu-color-mode (would reset
  every machine's stored theme)
- docker container names + devenv scripts (dev-only)
- the packages/hal Cargo crate name
Hardware names in HAL driver comments ("Lamassu Sintra", "Douro", "Tejo")
stay: those are the physical machines' real names — that IS the credit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013A6683cCHnQxFUosx1krY4
2026-09-19 09:58:42 +02:00
Patrick Mulligan
f8f2037100 refactor(deploy): expose batm3-usb as a named nixosConfiguration
Lift the USB-variant config (distinct fs labels, nofail /boot, no
growPartition, autoUpgrade off) out of the inline disk-image-batm3-usb
`let` into `nixosConfigurations.batm3-usb`, and build the disk-image from
that same config. Enables in-place app deploys to a running stick via
`nix copy` + `switch-to-configuration` (build the toplevel, copy the
closure, activate) — no reflash, preserving pairing + /var/lib state.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-04 18:49:59 +02:00
Patrick Mulligan
6edcb8b96d feat(deploy): USB-bootable batm3 test image (disk-image-batm3-usb)
Add a USB-bootable BATM3 disk-image target plus the batm3 hardware
changes that make a dd'd USB stick boot reliably on the Dell 9030 AIO.

flake.nix — new `disk-image-batm3-usb` target:
- Distinct partition labels (nixos-usb / ESP-USB) so stage-1 by-label
  resolution can't latch onto an internal SATA drive that already holds
  a generic nixos/ESP-labelled install. Post-build mlabel relabels the
  ESP FAT volume to ESP-USB (bootloader files untouched; UEFI still
  loads /EFI/BOOT/BOOTX64.EFI).
- /boot mounted nofail + short device-timeout: the firmware already
  loaded the bootloader before Linux; without nofail a slow/late ESP-USB
  enumeration drops to emergency mode with root locked — a dead end.
- NO growPartition/autoResize on the USB image: sfdisk rewriting the
  partition table on first boot is the single most bus-stressing write,
  and flaky USB bridges drop off the bus mid-rewrite (sfdisk wedges in
  uninterruptible D-state and ESP-USB vanishes with the device, so /boot
  times out too). Persistent state is a few MB and the image already
  ships ~2GB free in root. The internal-SATA disk-image-batm3 keeps
  growPartition — a real AHCI SSD won't drop the bus.
- autoUpgrade off (test image, not a managed fleet member).

batm3.nix — USB-boot reliability:
- Add usb_storage to initrd.availableKernelModules so stage-1 binds the
  stick and /dev/disk/by-label/* appears.
- Blacklist uas + usbcore.autosuspend=-1: force the slower-but-reliable
  Bulk-Only Transport path and stop the boot medium being power-suspended
  mid-I/O — both were causing "device offline error" bus drops.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-29 23:56:41 +00:00
f70105e8ff feat(deploy): add self-growing disk-image-batm3 target
batm3-installed existed as a nixosConfiguration but had no dd-able disk
image (only the live ISO, which is tmpfs — no persistent state.db/.env).
Mirrors the douro/sintra make-disk-image blocks, with one improvement:
boot.growPartition + fileSystems."/".autoResize so the root partition
and ext4 expand to fill the target drive on first boot. Flashing is
dd-and-done — no manual parted/resize2fs — and the full drive is
available to the nix store from day one (the #55 headroom lesson).

Image-only override via extendModules: the running system's
batm3-installed config (what auto-upgrade rebuilds against) is
unchanged.

Build: nix build .#disk-image-batm3  → result/nixos.img

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 17:58:22 +02:00
42c0d3e9ca chore(deploy): seed a minimal .env — stop pre-seeding maskable vars (#70)
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>
2026-07-02 21:53:51 +00:00
7896c122da fix(deploy): relay + LNbits pubkey are seed-provided, not env-pinned (#70)
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>
2026-07-02 21:53:51 +00:00
c8745addbc fix(deploy): make disk-image-sintra-usb BIOS+UEFI bootable (GRUB)
The USB disk image was systemd-boot (UEFI-only) with make-disk-image's "efi"
table (pure GPT + ESP, protective MBR). The Sintra's Aaeon UP Board firmware
USB-boots in Legacy/BIOS mode — it boots the live ISO via that ISO's isolinux
(BIOS) El Torito image, not the UEFI ESP — so a dd'd systemd-boot image has no
BIOS boot code to execute and the firmware won't list it (a hand-added hybrid
MBR didn't help: nothing to run).

Switch the USB target to GRUB with BIOS + UEFI on make-disk-image's "hybrid"
table: it adds a bios_grub partition, GRUB writes its BIOS stage to the MBR AND
a removable /EFI/BOOT/BOOTX64.EFI — mirroring the live ISO's dual boot. The
Aaeon now lists it (as two "ia android" entries, BIOS + UEFI) and boots it.
Scoped to disk-image-sintra-usb only; the eMMC install keeps systemd-boot.
ESP stays partition 1 so the ESP-USB relabel step is unchanged.

Verified on hardware: booted from USB into the wizard with the full upboard.nix
hardware config.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 21:31:36 +02:00
ce649947f6 feat(deploy): add disk-image-sintra-usb with distinct partition labels
A USB-bootable Sintra image variant for booting on a machine whose eMMC
already holds a nixos/ESP-labelled install. make-disk-image hardcodes the
root/ESP labels (nixos/ESP); booting the standard image from USB next to
the eMMC races stage-1's by-label/nixos between the two roots and likely
mounts the eMMC. This variant labels root nixos-usb (via make-disk-image
-L) and relabels the ESP to ESP-USB in a post-step (mtools), with
fileSystems pointed at the new labels. Auto-upgrade is disabled — it's a
portable test / hand-off image, and that also removes scheduled bootloader
writes that could land on the eMMC's ESP.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 23:47:18 +02:00
962214ec15 fix(deploy): point Sintra autoUpgrade at aiolabs/bitspire, not lamassu-next
The dev autoUpgrade flake URL still referenced the pre-migration repo
(aiolabs/lamassu-next), so the Sintra test unit would auto-pull
lamassu-next/dev at 04:00 — which lacks all the bitspire work (the
QR-pairing wizard, etc.) and would revert the box to the old no-seed
build. Repoint it at aiolabs/bitspire?ref=dev, the post-migration home
of this code. lamassu-next still feeds the not-yet-converted production
ATMs (batm3, douro) until they migrate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 11:40:46 +02:00
8a02d72bd1 feat(deploy): provision VITE_SPIRE_SEED for bunker pairing (Phase E)
provision-atm.sh now writes VITE_SPIRE_SEED (the spire-seed:v1: pairing seed
from spirekeeper) as the production identity, validating the scheme prefix;
the generated nsec path is kept only as a dev fallback when SPIRE_SEED is
unset. Relay default moved to the LNbits bundled nostrrelay
(ws://$HOST_IP:5001/nostrrelay/test). .env templates (live.nix + the flake's
installed-default) swap VITE_ATM_PRIVATE_KEY → VITE_SPIRE_SEED and drop the
dead LP-era vars. README notes state.db now also holds the bunker binding
(keep it or re-pair).

Part of Phase E, aiolabs/bitspire#52. Unblocks the Sintra live-pairing smoke.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 12:49:30 +02:00
055afba894 chore(nix): thread cfg.relayUrl into fresh-boot .env stub (#57 follow-up)
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>
2026-06-01 20:42:05 +02:00
4f68ddc40b 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>
2026-06-01 20:33:28 +02:00
a8e984e12b fix(nix): ATM_DB_PATH was reverted to /var/lib/lamassu-atm by rebase
Main's commit e5d7a86 ("chore: set ATM_DB_PATH env var for atm-tui")
added two ATM_DB_PATH lines pointing at /var/lib/lamassu-atm/state.db
AFTER dev had forked. Dev's path-rename commit (10d2869) couldn't
touch those lines because they didn't exist on dev's base; the
rebase brought them in unchanged, undoing the lamassu-atm → bitspire
data-dir migration for the ATM_DB_PATH consumers.

Re-point both occurrences at /var/lib/bitspire/state.db so atm-tui
and other ATM_DB_PATH consumers reach the actual on-disk location.
2026-06-01 19:35:01 +02:00
3128975224 chore(nix): bump nixpkgs 25.05 → 25.11
isoImage.isoName was renamed to image.fileName in 25.11 (unified
image module). Split the rename out — the rest of isoImage.* stays
put (makeEfiBootable, makeBiosBootable, squashfsCompression).

All 8 nixosConfigurations evaluate clean on 25.11 with zero
deprecation warnings.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-01 19:12:11 +02:00
2594f35abf chore(nix): bump nixpkgs 24.11 → 25.05
Clean step — all 8 nixosConfigurations evaluate without deprecation
warnings on 25.05.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-01 19:12:11 +02:00
7c1011d382 chore(nix): bump nixpkgs 24.05 → 24.11
Three breakages handled:

- hardware.opengl → hardware.graphics (renamed in 24.11). Touches
  upboard.nix, douro.nix, batm3.nix, live.nix.
- vaapiIntel dropped — legacy pre-Broadwell driver, removed in
  nixpkgs. UP Board (Cherry Trail) and OptiPlex 9030 (Haswell) both
  use intel-media-driver, which stays.
- vaapiVdpau renamed → libva-vdpau-driver.

system.stateVersion stays 24.05 — convention is to never bump after
install. Existing Sintra and fresh flashes keep the 24.05 state
semantics; that's correct.

All 8 nixosConfigurations (4 models × {live,installed}) evaluate
clean with zero deprecation warnings on 24.11.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-01 19:12:11 +02:00
264cc47e0c refactor(deploy): rename system user lamassu → bitspire
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>
2026-06-01 19:11:32 +02:00
5a04346c18 feat(flake): sintra-installed nixos config + disk-image-sintra (4a)
- nixosConfigurations.sintra-installed reuses the existing UP Board
  hardware module (deploy/nixos/hardware/upboard.nix). Sintra has
  identical peripheral layout to tejo — Aaeon UP Board with iVIZION
  validator on ttyJ5 and Fujitsu F56 dispenser on ttyJ7 — so no new
  hardware nix module is needed.
- packages.x86_64-linux.disk-image-sintra is a raw GPT disk image
  consumable via \`dd if=result/*.img of=/dev/<eMMC>\`. Mirrors the
  existing disk-image-douro shape.
- bitspire-env activation script (the template that lands at
  /var/lib/bitspire/.env on first boot) now emits LNbits fields
  (VITE_LNBITS_SERVER_PUBKEY, VITE_LNBITS_HTTP_URL) instead of the
  retired LP fields. Empty values mean the ATM boots into a
  "needs provisioning" state, ready for provision-atm.sh to fill in.

Evaluations confirmed: nix eval .#nixosConfigurations.sintra-installed
and .#packages.x86_64-linux.disk-image-sintra both resolve.

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>
2026-06-01 19:08:03 +02:00
28a35ca479 refactor(rename): NixOS module + service + paths → bitspire
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
1bb35220e9 refactor(rename): root + flake output names → bitSpire/bitspire
Three root-level identifier renames:

  - Root package.json `name`:  lamassu-next  → bitSpire
  - flake.nix nixosConfigurations.lamassu-live-*: kept (legacy
    aliases) and ADDED bitSpire-live-* alongside. Both work.
    Drop the lamassu-* aliases at the final cutover once nothing
    references them.
  - nix/mkAtmApp.nix `pname`:  lamassu-atm-app → bitspire-atm-app
    (lowercase to match nix package naming conventions; the
    Electron app's externally-facing names get their own commit).

What's NOT renamed here (per the plan):

  - Hardware-named outputs (douro, tejo, sintra, batm3) — those
    are physical product names.
  - nixosConfigurations.{douro,tejo,sintra,batm3} ergonomic
    aliases — same reason.
  - nixosModules.lamassu-atm + its imported file — that's the
    systemd service, deferred to 2d.
  - apps/machine/package.json appId/productName — Electron
    identity, deferred to 2c.

Verified:
  pnpm typecheck         clean (12/12 cached)
  nix eval .#nixosConfigurations.bitSpire-live-sintra ✓
  nix eval .#packages.x86_64-linux.atm-app-sintra.pname ✓
  → "bitspire-atm-app"

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-01 19:08:03 +02:00
adbde0eade chore(deploy): pin auto-upgrade to ?ref=dev on this branch
The system.autoUpgrade flake URL had no explicit ref, so the auto-pull
at 04:00 resolves to the repo's default branch (main). That's correct
for production ATMs flashed from main, but it would silently regress
a Sintra flashed from dev back to main code overnight.

Pin to ?ref=dev on the dev branch so any ATM deployed from dev stays
on dev. The main branch's flake.nix stays unchanged — production ATMs
keep pulling main HEAD as before.

First commit on the new dev branch. Tagged pre-bitspire-cutover on
main beforehand as a rollback target.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-01 19:08:03 +02:00
9f5dfd7dd0 chore(nix): time-bound local builds (timeout = 60) — backstop for max-jobs = 1
Follow-up to 9430c9b. `max-jobs = 1` reopened the door for ANY uncached
derivation to build locally, including derivations whose outputs SHOULD
be substituted but happen to miss the cache (network blip, hash drift,
operator forgot to push). On ATM hardware a kernel/electron/rustc build
would take literal hours and silently wedge the kiosk while it churns.

`timeout = 60` caps every local build's wall-clock at 60s. Activation-
time stitches (boot.json, system-units, X-Restart-Triggers, etc.) finish
in well under a second; anything that doesn't return by 60s is by
definition a heavy compile that has no business running on an ATM.
Killing it fast makes the upgrade fail loudly so the operator can fix
the cache miss, rather than the box silently chewing CPU all night.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 12:51:40 +02:00
d2bb11f642 fix(nix): allow tiny local builds (max-jobs = 0 → 1) — auto-upgrade was bricked fleet-wide
NixOS generates several trivial activation-time derivations that are
hardcoded with `allowSubstitutes = false` + `preferLocalBuild = true`
— most visibly `boot.json` (the bootspec) and `nixos-rebuild`. These
will NEVER appear in any binary cache (cachix follows the
non-substitutable flag) and they CAN'T be substituted (allowSubstitutes
= false). They're only realized via local build.

With `max-jobs = 0`, that's structurally impossible, so every nightly
`nixos-upgrade` across the fleet has been failing for at least a week:

  May 19 04:00:36 lamassu-atm: Cannot build '/nix/store/...-boot.json.drv'
  May 20-24 04:00:xx: Cannot build '/nix/store/...-nixos-rebuild.drv'
  May 25 04:11:17 lamassu-atm: Cannot build '/nix/store/...-atm-transactions.drv'

The kiosk kept running so nobody noticed — the systemd unit fails but
the old generation continues. Caught when re-provisioning the Sintra
dev unit to the demo LNbits today and the migration commits wouldn't
land.

`max-jobs = 1` allows one concurrent local build slot. Heavy compiles
(kernel, rustc, electron) DON'T have `preferLocalBuild`, so they still
go through normal substitution and effectively never build locally
because they're cached upstream. The slot exists strictly to unblock
the trivially-cheap activation-time stitch derivations.

Refs: lamassu-next#47 (caught during demo-server provisioning)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 12:51:07 +02:00
58f09fad5e flake: bump determinate past 3.16.3 regression
3.16.3 ships a regressed nix-functional-tests test
(local-overlay-store / stale-file-handle FAILs at exit 1) that triggers
when the determinate-nix-3.16.3 derivation has to be built from source
locally — no aiolabs.cachix nor cache.flakehub.com substituter has the
output cached for our exact nixos-24.05 / x86_64-linux combo, so the
build evaluates the test phase and fails.

Bumping the semver pin from `?3` (≥3.0.0 → resolves to 3.16.3) to
`?3.15` (≥3.15.0 → resolves to 3.20.0) skips past the regression.
3.20.0 builds cleanly; nix bumps from 2.33 → 2.34.6 across the BATM3
fleet once it auto-pulls.

Mirrors the same fix already applied on dev in 6d8c217.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 22:55:44 +02:00
Patrick Mulligan
e5d7a86bc2 chore: set ATM_DB_PATH env var for atm-tui
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-26 13:49:38 -04:00
Patrick Mulligan
366869a8a4 fix(deploy): add --refresh flag to auto-upgrade
Without --refresh, nix caches the flake evaluation and
nixos-upgrade may not pull the latest commits. The --refresh
flag forces re-fetching the git repo on every upgrade.

Only affects flake evaluation cache — /var/lib data is untouched.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 18:31:08 -04:00
Patrick Mulligan
d3befc11c1 feat: add fund-atm CLI for generating ATM funding invoices
Standalone Node.js script that generates a Lightning invoice for
the ATM's Lightning.Pub account. Reads config from .env, connects
to the relay, creates an invoice via Nostr RPC, displays a QR code
in the terminal, and prints the BOLT11.

Bundled as self-contained CJS with esbuild (all dependencies inlined)
so it works from the nix store without separate node_modules.

Usage: fund-atm <amount_sats>
  e.g. fund-atm 100000
       fund-atm 100000 sats

Added to NixOS systemPackages for both live and installed configs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 00:45:45 -04:00
Patrick Mulligan
88a52cf769 feat(deploy): add batm3-installed NixOS config
HP ProDesk 600 G3 DM hardware module (temporary board replacement for
BATM3). Includes batm3-installed nixosConfiguration with auto-upgrade,
cachix, and systemd-boot.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 17:13:02 -04:00
Patrick Mulligan
dc5f4f8aa6 feat(deploy): add batm3 ISO build target
Add batm3 to flake.nix NixOS configs, live.nix (CDC ACM kernel modules,
MEI udev rules), and build-iso.sh. Default fiat: USD.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 03:20:46 -04:00
Patrick Mulligan
2cc0acfdef feat: include atm-tui in NixOS ATM deployments
Add atm-tui as a flake input and include it in environment.systemPackages
for both live and installed NixOS configs. The TUI will be available as
'atm-tui' on PATH after deployment — no more manual SCP.

The binary is pushed to cachix alongside the ATM app, so douro pulls
it from the binary cache on upgrade (no local compilation needed).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 03:15:54 -04:00
Patrick Mulligan
143ae9ff0b feat(nix): add tejo hardware support and per-model auto-upgrade
- Add tejo-specific udev rules for both UP Board and UP4000 variants
  (serial symlinks ttyJ4/J5/J7, camera, LED SPI, I2C, USB autosuspend)
- Add USB serial kernel modules (usbserial, ftdi_sio, cp210x) for tejo
- Add tejo serial console kernel params (ttyS4 debug UART)
- Fix upboard.nix: hardware.graphics → hardware.opengl (NixOS 24.05)
- Add tejo-installed nixosConfiguration with model-specific auto-upgrade
- Parameterize auto-upgrade flake URL per machine model

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 08:54:50 -05:00
Patrick Mulligan
7f9e1d2da0 fix(nix): prevent local builds on ATM hardware (max-jobs = 0)
The ATM should never compile from source — only download pre-built
binaries from cachix/cache.nixos.org. If a derivation isn't cached,
the upgrade fails cleanly instead of trying to build on the mSATA.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 14:58:26 -05:00
Patrick Mulligan
1861f24187 fix: add Determinate Nix to live ISO config
Without this, freshly flashed ATMs have mismatched nix store hashes and
fall back to building from source instead of pulling cachix binaries.
Matches the installed config which already had this.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 13:25:31 -05:00
Patrick Mulligan
fe5a3d661f fix: add stability fixes to installed ATM config
Same fixes as live.nix: disable SwiftShader, enforce MemoryMax=1G,
add 1GB swap file, and clean /tmp on boot. Applies to douro-installed
and tejo-installed configs used by nixos-rebuild on disk-installed ATMs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 13:08:28 -05:00
Patrick Mulligan
31c5376317 feat(nix): add Determinate Nix to douro-installed config
Resolves the cachix binary cache hash mismatch between local dev machines
(Determinate Nix 2.33) and douro (stock Nix 2.18). With both sides using
Determinate Nix, `cachix push` from local builds produces store paths that
douro's 4am auto-upgrade can download directly instead of building from source.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 14:45:50 -05:00
Patrick Mulligan
34179a4e34 feat(deploy): add auto-upgrade, cachix, and passwordless sudo for douro-installed
- Passwordless sudo for lamassu user (nixos-rebuild without TTY)
- Cachix binary cache (aiolabs) as substituter
- Daily auto-upgrade timer pulling latest flake from Forgejo
- trusted-users includes lamassu for nix commands

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 14:42:49 -05:00
Patrick Mulligan
625cebed25 refactor(nix): consolidate deploy flake into root flake with pure ISO builds
Move deploy/nixos/flake.nix into the root flake.nix, adding mkAtmApp
for pure Nix builds of the Electron app (no local pnpm needed). Simplify
build-iso.sh to a thin wrapper around `nix build .#iso-<model>`. Add
douro hardware configuration. Streamline live.nix to consume the
Nix-built app package.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 10:58:43 -05:00
Patrick Mulligan
c98f126ba7 feat(docker): add dev.sh with auto-funding and ATM app setup
- Add dev.sh script for managing regtest development environment
- Implement cmd_fund to fund ATM app owner via Lightning.Pub API
- Add --fund flag to cmd_up for automatic funding on startup
- Update setup_atm_app to write VITE_APP_ID to machine .env
- Fix Electron IPC to pass appId and extensionApiUrl to renderer
- Restructure repo from nested lamassu-next/ to root

The dev.sh script now supports:
- ./dev.sh up --fund  # Start regtest and auto-fund ATM
- ./dev.sh fund       # Fund existing ATM app
- ./dev.sh status     # Show environment status
- ./dev.sh reset      # Clean restart

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-15 14:19:16 -05:00
Renamed from lamassu-next/flake.nix (Browse further)