bitspire/docs/machine-installation.md
Padreug 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

96 lines
8.1 KiB
Markdown

# Deploying bitSpire to a Sintra (or other ATM)
This document gives the high-level shape of an ATM deployment. **For the step-by-step walkthrough — every command from `nix build` to a kiosk on a real Sintra — see [deploy/nixos/README.md](../deploy/nixos/README.md).** This file is the orientation doc that explains *why* the pipeline looks the way it does.
> The pre-NixOS workflow described in earlier versions of this file (manual AppImage scp, hand-written systemd unit, ad-hoc env files) has been retired. NixOS is the supported deployment path on `dev`. The historical AppImage build still exists for one-off testing on non-NixOS dev boxes, but it is not how production ATMs are installed.
## The pipeline at a glance
```
┌─────────────────┐ nix build .# ┌─────────────────────────┐
│ dev box │ ─disk-image-sintra──▶ │ result/nixos.img │
│ (Nix + flake) │ │ (8.5 GB sparse, GPT) │
└─────────────────┘ └────────────┬────────────┘
│ dd
▼
┌──────────────────────┐
│ USB stick │
└──────────┬───────────┘
│ carry to ATM
▼
┌─────────────────┐ ┌─────────────────────┐
│ Alpine live USB │ boot Sintra │ Sintra (UP Board) │
│ (toolchain) │ ─────────────────────▶ │ eMMC: empty │
└────────┬────────┘ └──────────┬──────────┘
│ apk add + dd USB → eMMC + parted resize + reboot
▼
┌────────────────────────────────────────────────────────────────┐
│ Sintra booted from eMMC, bitspire.service in needs-prov state │
└──────────────────────────────┬─────────────────────────────────┘
│ provision-atm.sh from dev box
▼
┌────────────────────────────────────────────────────────────────┐
│ /var/lib/bitspire/.env populated, service restarted, kiosk │
│ connects to LNbits over nostr-transport, ready for transactions │
└────────────────────────────────────────────────────────────────┘
```
## Why this pipeline
Three properties matter for ATM software running unattended in retail locations:
1. **Reproducible boot media.** Two ATMs flashed from the same `nixos.img` boot identical environments — same kernel, same systemd units, same Electron build, same nix-store closure. No "works on my machine" drift between fleet units.
2. **Auditable provenance.** Every file on the ATM traces back to a derivation in `/nix/store/`, and every derivation traces back to a commit in this repo. There's no `pip install` reaching out to PyPI, no `npm install` pulling un-pinned packages, no `apt update` mutating the system underfoot.
3. **Safe in-place updates.** `nixos-rebuild switch` against the `dev` branch atomically installs a new generation; if the new generation fails to boot or activate, the previous one stays bootable. Auto-upgrade at 04:00 daily means an operator can patch the fleet by pushing to `dev`.
The disk-image approach (versus `nixos-install` from a live USB) is specifically to avoid the human-in-the-loop installation step. Every Sintra gets the same image; the only per-machine variation is the `.env` written at provisioning time.
## What's in the image
Conceptually:
| Layer | Source | Purpose |
|---|---|---|
| **Kernel + initrd** | `nixpkgs` 24.11 + `upboard.nix` initrd modules | Boot the Sintra hardware (eMMC via `sdhci-acpi`, validator/dispenser at `ttyJ5`/`ttyJ7`) |
| **NixOS base** | `nixpkgs` 24.11 | systemd, Xorg, openbox, the `lamassu` user, sshd for provisioning |
| **bitspire.service** | `deploy/nixos/bitspire-atm.nix` | systemd unit that launches the Electron kiosk |
| **The Electron app** | `apps/machine` built into a nix derivation | The actual ATM UI + state machine + Lightning client |
| **Hardware-specific config** | `deploy/nixos/hardware/upboard.nix` (or `douro.nix`, `batm3.nix`) | udev rules, kernel modules, panel calibration |
## What's NOT in the image
The image is **identity-free** by design. After flashing, the ATM has no concept of:
- Which LNbits server to talk to
- Which nostr relay to use
- Its own nostr identity (signing key)
- Which fiat currency to display (defaulted from build args, but overridable)
All of these come from `/var/lib/bitspire/.env`, which is written by `provision-atm.sh` after first boot. This separation means a single image flavor can serve dev, staging, and prod just by changing the provisioning data.
## Pre-deployment checklist
Before flashing a Sintra you'll want:
- [ ] **An Alpine live USB** for use as the installer-OS on the Sintra (Alpine is small, has a working busybox toolchain, and apk is fast). The bitSpire image itself is *not* a live system — it expects to live on the eMMC.
- [ ] **A second USB stick** to flash the bitSpire image onto (this is what you'll dd between on the Sintra).
- [ ] **A running LNbits instance** with the nostr-native-transport branch built in. The dev compose at `~/dev/local/docker/regtest` provides one; production deployments point at `lnbits.aiolabs.dev` or your operator's instance.
- [ ] **Network reachability between the Sintra and the LNbits host.** The ATM connects to the relay endpoint at `ws://<lnbits-host>:5001/nostrrelay/test` (no separate strfry container — LNbits ships its own `nostrrelay` extension).
- [ ] **The LNbits server pubkey** (`docker logs <lnbits-container> | grep 'Public key (share this)'`).
- [ ] **A freshly generated nostr private key** for the ATM (`openssl rand -hex 32`). Each ATM should have its own; never share keys between machines.
## After deployment
Once the kiosk is up, useful things to know:
- **Service status:** `ssh lamassu@<atm> 'sudo systemctl status bitspire'`
- **Live log tail:** `ssh lamassu@<atm> 'sudo journalctl -u bitspire -f'`
- **Re-provision (e.g., wrong relay URL):** rerun `provision-atm.sh` from the dev box with the new env vars
- **Push a code change without reflashing:** `nixos-rebuild switch --flake .#sintra-installed --target-host lamassu@<atm> --use-remote-sudo`
- **Inspect transaction history:** `ssh lamassu@<atm> 'sudo bash /etc/nixos/atm-transactions.sh'` (queries `/var/lib/bitspire/state.db`)
## Related documentation
- [deploy/nixos/README.md](../deploy/nixos/README.md) — the full step-by-step walkthrough and NixOS module reference
- [device-configuration.md](./device-configuration.md) — hardware-specific configuration (validator types, cassette layouts, fiat currency)
- [adr/001-hal-architecture.md](./adr/001-hal-architecture.md) — why HAL is TypeScript-in-Node rather than Rust-via-Tauri
- [CLAUDE.md](../CLAUDE.md) — the dev-facing overview, including the hardware-specific gotchas we hard-learned on the first real Sintra flash