diff --git a/flake.nix b/flake.nix index 158dfac..705fd4c 100644 --- a/flake.nix +++ b/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=#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"; }; }; }