diff --git a/deploy/nixos/configuration.nix b/deploy/nixos/configuration.nix index 2306f5a..652de98 100644 --- a/deploy/nixos/configuration.nix +++ b/deploy/nixos/configuration.nix @@ -159,6 +159,18 @@ # Auto-updates (optional - disabled by default for stability) # system.autoUpgrade.enable = false; + # Trust the Forgejo host key up front. system.autoUpgrade fetches the flake + # over ssh AS ROOT, and a machine whose root has never connected by hand has + # no known_hosts entry, so every nightly run dies at + # "Host key verification failed" before it reaches authentication. batm3 did + # exactly that, silently, from its 2026-08-06 install until 09-22 (#98): it + # sat on its install generation for six weeks while reporting a failed unit + # nobody was watching. sintra only ever worked because a human had ssh'd as + # root once and accepted the key. Declaring it means a freshly flashed ATM + # can update from first boot with no manual step. + programs.ssh.knownHosts."git.atitlan.io".publicKey = + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMlo3f05o4+bk0+8x2VG91o9GubshOb46HmBPvND9pJx"; + # pragma: allowlist secret # Ensure WireGuard private key directory exists with correct permissions system.activationScripts.wireguard-key = '' @@ -169,6 +181,20 @@ fi ''; + # The tunnel is operator-provisioned: wg0.key is written per machine after + # flashing, and until it is, `wg set … private-key` exits 1 with + # "fopen: No such file or directory". One failed unit makes + # switch-to-configuration exit 4, which marks the entire nightly + # system.autoUpgrade run as failed — so an ATM that simply never had its + # tunnel provisioned reports a broken updater for the life of the machine + # (sintra, #98). Skip the unit when there is no key instead of failing + # activation over an interface that was never set up; a provisioned machine + # is unaffected. Guarded on wg0 still being declared so the live image, + # which mkForce's the interfaces away, doesn't get a unit with no ExecStart. + systemd.services = lib.mkIf (config.networking.wireguard.interfaces ? wg0) { + wireguard-wg0.unitConfig.ConditionPathExists = "/var/lib/wireguard/wg0.key"; + }; + # In-place rename migration: lamassu user → bitspire user. # Runs after `users` activation so the bitspire user exists with its UID. # Idempotent: re-running on an already-migrated system is a chown no-op.