feat: Raspberry Pi 5 (aarch64) target + Pyramid Apex RS-232 bill validator #87
1 changed files with 144 additions and 77 deletions
feat(deploy): split rpi5 target into installed + image variants
`rpi5-installed` previously bundled the sd-image-aarch64 module, so it
was only good for building a flashable image — a `nixos-rebuild switch`
against it (local or the git+ssh remote form) would drag in the image
builder and its image-specific fs wiring. Split it, mirroring the x86
fleet's mkLiveConfig/mkInstalledConfig separation:
- rpi5-installed → in-place rebuild target. Declares the flashed media's
own root fs (NIXOS_SD / FIRMWARE labels), nothing image-specific. This
is what
sudo nixos-rebuild switch --flake \
"git+ssh://forgejo@git.atitlan.io/aiolabs/bitspire.git?ref=<branch>#rpi5-installed"
targets, the aarch64 equivalent of the sintra/douro deploy ritual.
- rpi5-image → same shared runtime + the sd-image builder. Its
system.build.sdImage is the flashable artifact; packages.aarch64-linux
.sd-image-rpi5 now points here.
Shared runtime extracted into piBaseModules/mkPiRuntime; folded the
aiolabs cachix substituter + trusted key into the Pi's nix.settings so a
remote rebuild substitutes the heavy aarch64 closure instead of building
it on the Pi (no max-jobs/timeout watchdog — the Pi 5 can build locally
if it must).
Verified: rpi5-installed evaluates to a valid system toplevel (root fs
present), rpi5-image/sd-image-rpi5 to the .img.zst builder, and x86
sintra-installed is byte-identically unaffected.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SGUJJjBDuYwRaWSkFdK3bm
commit
d8680f67ae
225
flake.nix
225
flake.nix
|
|
@ -283,89 +283,151 @@
|
|||
];
|
||||
};
|
||||
|
||||
# Raspberry Pi 5 (aarch64) installed config — the DIY Pi build. Mirrors
|
||||
# mkInstalledConfig's runtime (bitspire service, env activation, electron
|
||||
# service override, swap) on aarch64 + Pi hardware, but deliberately drops
|
||||
# the x86 fleet machinery for a first bring-up: no determinate/autoUpgrade
|
||||
# (not yet a managed fleet member) and no atm-tui (add once it publishes an
|
||||
# aarch64 package). Builds an SD image; needs an aarch64 builder (native
|
||||
# Pi / arm box / binfmt emulation) — the app closure won't build on x86.
|
||||
mkPiConfig = machineModel:
|
||||
let
|
||||
atm-app = mkAtmAppAarch64 {
|
||||
model = machineModel;
|
||||
fiatCode = fiatCodeForModel.${machineModel} or "USD";
|
||||
};
|
||||
# Raspberry Pi 5 (aarch64) DIY build. Two products from one shared runtime:
|
||||
# - mkPiInstalled: the in-place rebuild target. `nixos-rebuild switch
|
||||
# --flake .#rpi5-installed` (or the git+ssh remote form) targets this.
|
||||
# Declares the flashed media's own root fs (NIXOS_SD / FIRMWARE) and
|
||||
# NOTHING image-specific, so a switch on a running Pi never trips over
|
||||
# the sd-image builder.
|
||||
# - mkPiImage: the same runtime + the aarch64 sd-image module, whose
|
||||
# system.build.sdImage is the flashable artifact. The module supplies
|
||||
# its OWN NIXOS_SD/FIRMWARE fileSystems + u-boot firmware, so we must
|
||||
# not re-declare the root fs here (double definition = eval conflict).
|
||||
#
|
||||
# The runtime mirrors mkInstalledConfig (bitspire service, env activation,
|
||||
# electron override, cache substituters) on aarch64 + Pi hardware, but
|
||||
# deliberately drops the x86 fleet machinery for first bring-up: no
|
||||
# determinate, no atm-tui (add once it ships an aarch64 package). Any Pi
|
||||
# build needs an aarch64 builder (native Pi / arm box / binfmt emulation) —
|
||||
# the app closure won't build on x86.
|
||||
mkPiRuntime = machineModel: {
|
||||
atm-app = mkAtmAppAarch64 {
|
||||
model = machineModel;
|
||||
fiatCode = fiatCodeForModel.${machineModel} or "USD";
|
||||
in
|
||||
};
|
||||
fiatCode = fiatCodeForModel.${machineModel} or "USD";
|
||||
};
|
||||
|
||||
# Shared module list (everything EXCEPT the root fs and the sd-image
|
||||
# builder). atm-app/fiatCode are threaded in so both products share one
|
||||
# evaluated app closure.
|
||||
piBaseModules = { machineModel, atm-app, fiatCode }: [
|
||||
nixos-hardware.nixosModules.raspberry-pi-5
|
||||
./deploy/nixos/configuration.nix
|
||||
./deploy/nixos/bitspire-atm.nix
|
||||
./deploy/nixos/hardware/raspberry-pi-5.nix
|
||||
({ config, lib, pkgs, pkgs-unstable, ... }: {
|
||||
services.bitspire = {
|
||||
enable = true;
|
||||
appDir = "${atm-app}";
|
||||
};
|
||||
|
||||
environment.systemPackages = [
|
||||
(pkgs.writeShellScriptBin "fund-atm" ''
|
||||
exec ${pkgs-unstable.nodejs}/bin/node ${atm-app}/dist-electron/fund-atm.bundle.cjs "$@"
|
||||
'')
|
||||
];
|
||||
environment.variables.ATM_DB_PATH = "/var/lib/bitspire/state.db";
|
||||
|
||||
boot.kernel.sysctl."kernel.unprivileged_userns_clone" = 1;
|
||||
security.sudo.wheelNeedsPassword = false;
|
||||
|
||||
# Pull from the aiolabs binary cache so a `nixos-rebuild switch`
|
||||
# (local or the git+ssh remote form) substitutes the heavy aarch64
|
||||
# closure instead of compiling on the Pi. Mirrors the x86 fleet's
|
||||
# nix.settings, minus max-jobs/timeout — the Pi 5 can actually build
|
||||
# locally if it must, so we don't want the 60s watchdog killing a
|
||||
# legitimate first build.
|
||||
nix.settings = {
|
||||
trusted-users = [ "root" "bitspire" ];
|
||||
substituters = [ "https://cache.nixos.org" "https://aiolabs.cachix.org" ];
|
||||
trusted-public-keys = [
|
||||
"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="
|
||||
"aiolabs.cachix.org-1:PrAjsGU9PE77tFKP2+iO+mgR88c4xv3utM9JmpTblUQ="
|
||||
];
|
||||
};
|
||||
|
||||
# Same first-boot env seed as the x86 installed configs.
|
||||
system.activationScripts.bitspire-env = ''
|
||||
mkdir -p /var/lib/bitspire
|
||||
if [ ! -f /var/lib/bitspire/.env ]; then
|
||||
cp ${pkgs.writeText "bitspire-env-default" (''
|
||||
VITE_LAMASSU_MACHINE_MODEL=${machineModel}
|
||||
VITE_LAMASSU_FIAT_CODE=${fiatCode}
|
||||
VITE_SPIRE_SEED=
|
||||
ELECTRON_FORCE_PROD=1
|
||||
DISPLAY=:0
|
||||
'' + pkgs.lib.optionalString (config.services.bitspire.relayUrl != "") ''
|
||||
VITE_RELAY_URL=${config.services.bitspire.relayUrl}
|
||||
'' + pkgs.lib.optionalString (config.services.bitspire.lnbitsServerPubkey != "") ''
|
||||
VITE_LNBITS_SERVER_PUBKEY=${config.services.bitspire.lnbitsServerPubkey}
|
||||
'')} /var/lib/bitspire/.env
|
||||
chmod 600 /var/lib/bitspire/.env
|
||||
chown bitspire:bitspire /var/lib/bitspire/.env
|
||||
fi
|
||||
'';
|
||||
|
||||
# Electron runtime override (same flags as the x86 fleet, aarch64
|
||||
# electron). No eDP display-reset here — that's UP-Board-specific;
|
||||
# the Pi drives HDMI/DSI directly.
|
||||
systemd.services.bitspire.serviceConfig = {
|
||||
EnvironmentFile = lib.mkForce "/var/lib/bitspire/.env";
|
||||
Environment = "LD_LIBRARY_PATH=${pkgs.stdenv.cc.cc.lib}/lib";
|
||||
ExecStart = lib.mkForce "${pkgs-unstable.electron}/bin/electron --no-sandbox --disable-gpu-sandbox --disable-software-rasterizer --enable-logging ${atm-app}";
|
||||
MemoryMax = lib.mkForce "2G";
|
||||
NoNewPrivileges = lib.mkForce false;
|
||||
ProtectSystem = lib.mkForce false;
|
||||
ProtectHome = lib.mkForce false;
|
||||
PrivateTmp = lib.mkForce false;
|
||||
DevicePolicy = lib.mkForce "auto";
|
||||
DeviceAllow = lib.mkForce [ "char-* rw" ];
|
||||
};
|
||||
|
||||
swapDevices = [{ device = "/var/swapfile"; size = 2048; }];
|
||||
boot.tmp.cleanOnBoot = true;
|
||||
services.openssh.settings.PasswordAuthentication = lib.mkForce true;
|
||||
})
|
||||
];
|
||||
|
||||
# In-place rebuild target — declares the flashed media's own filesystems
|
||||
# (the labels mkPiImage's sd-image module writes), no image builder.
|
||||
mkPiInstalled = machineModel:
|
||||
let rt = mkPiRuntime machineModel; in
|
||||
nixpkgs.lib.nixosSystem {
|
||||
system = "aarch64-linux";
|
||||
specialArgs = {
|
||||
pkgs-unstable = pkgsUnstableAarch64;
|
||||
inherit atm-app;
|
||||
inherit (rt) atm-app;
|
||||
};
|
||||
modules = [
|
||||
nixos-hardware.nixosModules.raspberry-pi-5
|
||||
modules = (piBaseModules { inherit machineModel; inherit (rt) atm-app fiatCode; }) ++ [
|
||||
{
|
||||
fileSystems."/" = {
|
||||
device = "/dev/disk/by-label/NIXOS_SD";
|
||||
fsType = "ext4";
|
||||
};
|
||||
fileSystems."/boot/firmware" = {
|
||||
device = "/dev/disk/by-label/FIRMWARE";
|
||||
fsType = "vfat";
|
||||
options = [ "nofail" "noauto" ];
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
# Flashable SD/USB image — same runtime + the aarch64 sd-image builder,
|
||||
# which brings its own NIXOS_SD/FIRMWARE fileSystems and the u-boot
|
||||
# firmware. Its system.build.sdImage is exposed as
|
||||
# packages.aarch64-linux.sd-image-rpi5.
|
||||
mkPiImage = machineModel:
|
||||
let rt = mkPiRuntime machineModel; in
|
||||
nixpkgs.lib.nixosSystem {
|
||||
system = "aarch64-linux";
|
||||
specialArgs = {
|
||||
pkgs-unstable = pkgsUnstableAarch64;
|
||||
inherit (rt) atm-app;
|
||||
};
|
||||
modules = (piBaseModules { inherit machineModel; inherit (rt) atm-app fiatCode; }) ++ [
|
||||
(nixpkgs + "/nixos/modules/installer/sd-card/sd-image-aarch64.nix")
|
||||
./deploy/nixos/configuration.nix
|
||||
./deploy/nixos/bitspire-atm.nix
|
||||
./deploy/nixos/hardware/raspberry-pi-5.nix
|
||||
({ config, lib, pkgs, pkgs-unstable, ... }: {
|
||||
services.bitspire = {
|
||||
enable = true;
|
||||
appDir = "${atm-app}";
|
||||
};
|
||||
|
||||
environment.systemPackages = [
|
||||
(pkgs.writeShellScriptBin "fund-atm" ''
|
||||
exec ${pkgs-unstable.nodejs}/bin/node ${atm-app}/dist-electron/fund-atm.bundle.cjs "$@"
|
||||
'')
|
||||
];
|
||||
environment.variables.ATM_DB_PATH = "/var/lib/bitspire/state.db";
|
||||
|
||||
boot.kernel.sysctl."kernel.unprivileged_userns_clone" = 1;
|
||||
security.sudo.wheelNeedsPassword = false;
|
||||
|
||||
# Same first-boot env seed as the x86 installed configs.
|
||||
system.activationScripts.bitspire-env = ''
|
||||
mkdir -p /var/lib/bitspire
|
||||
if [ ! -f /var/lib/bitspire/.env ]; then
|
||||
cp ${pkgs.writeText "bitspire-env-default" (''
|
||||
VITE_LAMASSU_MACHINE_MODEL=${machineModel}
|
||||
VITE_LAMASSU_FIAT_CODE=${fiatCode}
|
||||
VITE_SPIRE_SEED=
|
||||
ELECTRON_FORCE_PROD=1
|
||||
DISPLAY=:0
|
||||
'' + pkgs.lib.optionalString (config.services.bitspire.relayUrl != "") ''
|
||||
VITE_RELAY_URL=${config.services.bitspire.relayUrl}
|
||||
'' + pkgs.lib.optionalString (config.services.bitspire.lnbitsServerPubkey != "") ''
|
||||
VITE_LNBITS_SERVER_PUBKEY=${config.services.bitspire.lnbitsServerPubkey}
|
||||
'')} /var/lib/bitspire/.env
|
||||
chmod 600 /var/lib/bitspire/.env
|
||||
chown bitspire:bitspire /var/lib/bitspire/.env
|
||||
fi
|
||||
'';
|
||||
|
||||
# Electron runtime override (same flags as the x86 fleet, aarch64
|
||||
# electron). No eDP display-reset here — that's UP-Board-specific;
|
||||
# the Pi drives HDMI/DSI directly.
|
||||
systemd.services.bitspire.serviceConfig = {
|
||||
EnvironmentFile = lib.mkForce "/var/lib/bitspire/.env";
|
||||
Environment = "LD_LIBRARY_PATH=${pkgs.stdenv.cc.cc.lib}/lib";
|
||||
ExecStart = lib.mkForce "${pkgs-unstable.electron}/bin/electron --no-sandbox --disable-gpu-sandbox --disable-software-rasterizer --enable-logging ${atm-app}";
|
||||
MemoryMax = lib.mkForce "2G";
|
||||
NoNewPrivileges = lib.mkForce false;
|
||||
ProtectSystem = lib.mkForce false;
|
||||
ProtectHome = lib.mkForce false;
|
||||
PrivateTmp = lib.mkForce false;
|
||||
DevicePolicy = lib.mkForce "auto";
|
||||
DeviceAllow = lib.mkForce [ "char-* rw" ];
|
||||
};
|
||||
|
||||
swapDevices = [{ device = "/var/swapfile"; size = 2048; }];
|
||||
boot.tmp.cleanOnBoot = true;
|
||||
services.openssh.settings.PasswordAuthentication = lib.mkForce true;
|
||||
})
|
||||
];
|
||||
};
|
||||
in
|
||||
|
|
@ -401,8 +463,13 @@
|
|||
batm3-installed = mkInstalledConfig "batm3" ./deploy/nixos/hardware/batm3.nix;
|
||||
|
||||
# Raspberry Pi 5 (aarch64) DIY build — Apex 7600 / NV10 over USB-serial.
|
||||
# Build the SD image via packages.aarch64-linux.sd-image-rpi5.
|
||||
rpi5-installed = mkPiConfig "rpi5";
|
||||
# rpi5-installed → in-place rebuild target:
|
||||
# sudo nixos-rebuild switch --flake \
|
||||
# "git+ssh://forgejo@git.atitlan.io/aiolabs/bitspire.git?ref=<branch>#rpi5-installed"
|
||||
# rpi5-image → source of the flashable image
|
||||
# (packages.aarch64-linux.sd-image-rpi5).
|
||||
rpi5-installed = mkPiInstalled "rpi5";
|
||||
rpi5-image = mkPiImage "rpi5";
|
||||
|
||||
# USB-bootable variant of batm3-installed. This is the config the
|
||||
# flashed USB stick actually runs — distinct fs labels so stage-1 can't
|
||||
|
|
@ -623,7 +690,7 @@
|
|||
# / arm box / `boot.binfmt` emulation on this x86 host):
|
||||
# nix build .#packages.aarch64-linux.sd-image-rpi5
|
||||
packages.aarch64-linux = {
|
||||
sd-image-rpi5 = self.nixosConfigurations.rpi5-installed.config.system.build.sdImage;
|
||||
sd-image-rpi5 = self.nixosConfigurations.rpi5-image.config.system.build.sdImage;
|
||||
atm-app-rpi5 = mkAtmAppAarch64 { model = "rpi5"; fiatCode = "USD"; };
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue