feat(deploy): USB-bootable douro image (disk-image-douro-usb)

The douro cutover to bitspire is being done remotely with a USB stick
and the machine's internal drive is not NixOS, so the stick has to be
the system rather than an installer medium. Give douro the same
run-from-USB shape batm3 already has.

flake.nix
- Lift the batm3-usb module and image post-processing into shared
  `usbBootModule` / `mkUsbDiskImage` helpers (distinct nixos-usb/ESP-USB
  labels, nofail /boot, no growPartition, autoUpgrade off, ESP relabel).
  batm3-usb evaluates to the same fileSystems/upgrade config as before.
- Add `nixosConfigurations.douro-usb` and
  `packages.disk-image-douro-usb` on top of douro-installed.

douro.nix
- Blacklist uas and set usbcore.autosuspend=-1, the same bus-drop
  hardening batm3.nix carries, so a stick is a reliable boot medium on
  the Bay Trail box.

README
- Document the -usb outputs and the flash-with-Etcher, no-installer flow.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Padreug 2026-09-16 19:31:45 +02:00
commit 23fe4a59f1
3 changed files with 105 additions and 75 deletions

View file

@ -33,9 +33,19 @@ Each ATM model has two flake outputs:
| `nixosConfigurations.<model>-installed` | installed | full GPT + systemd-boot install, ext4 root, supports `nixos-rebuild switch` |
| `packages.x86_64-linux.iso-<model>` | ISO | ISO image of the live variant |
| `packages.x86_64-linux.disk-image-<model>` | raw image | dd-able full disk image of the installed variant |
| `nixosConfigurations.<model>-usb` | installed, on a stick | `<model>-installed` hardened to run from a USB stick: `nixos-usb`/`ESP-USB` labels, `nofail` `/boot`, no partition growing, auto-upgrade off (`batm3`, `douro` only) |
| `packages.x86_64-linux.disk-image-<model>-usb` | raw image | dd-able image of the `-usb` variant — flash, plug in, boot; no installer step |
Models: `douro`, `tejo`, `sintra`, `batm3`.
**Run-from-USB deployments** (`batm3` today, `douro` since the LNbits cutover)
skip the Alpine + dd-to-internal-disk procedure below entirely: the stick *is*
the system. Flash `disk-image-<model>-usb` with balenaEtcher (it verifies the
write — a truncated or bad copy fails stage-1 fsck on first boot) onto a stick
of 16 GB or more, plug it in, power on. Updates go in-place against the
`<model>-usb` config (`nix copy` the toplevel + `switch-to-configuration`),
which keeps pairing and `/var/lib/bitspire`.
```bash
# Build a Sintra disk image
nix build .#disk-image-sintra

View file

@ -20,12 +20,24 @@
initrd.availableKernelModules = [
"xhci_pci"
"ahci"
# USB mass-storage: required to boot the dd'd image from a USB stick
# (stage-1 must bind the flash drive as a SCSI disk so
# /dev/disk/by-label/* appears). Harmless on the internal install.
#
# NOTE: deliberately NO "uas" here. Many USB sticks/bridges advertise
# UAS but drop off the bus ("device offline error, dev sdb") under
# sustained write load. Blacklisting uas below forces the slower-but-
# reliable usb-storage (Bulk-Only Transport) path. SATA/mSATA installs
# don't use uas anyway. (Same hardening as batm3.nix.)
"usb_storage"
"sd_mod"
"sdhci_pci"
"i915"
];
# Keep the USB flash drive off the flaky UAS driver (see note above).
blacklistedKernelModules = [ "uas" ];
kernelModules = [
"kvm-intel"
"i2c-dev"
@ -37,6 +49,9 @@
"vt.handoff=7" # Bay Trail: preserve BIOS display init
"quiet"
"splash"
# Disable USB autosuspend so the boot medium (and kiosk peripherals)
# aren't power-suspended mid-I/O — another cause of "device offline".
"usbcore.autosuspend=-1"
];
};