diff --git a/docs/RUNNER_IMAGES.md b/docs/RUNNER_IMAGES.md new file mode 100644 index 0000000..fa96f5b --- /dev/null +++ b/docs/RUNNER_IMAGES.md @@ -0,0 +1,50 @@ +# Runner Images (NixOS) + +This repo exports reproducible NixOS runner configurations via flake outputs: + +- `nixosConfigurations.ec-runner-aarch64` +- `nixosConfigurations.ec-runner-x86_64` +- `nixosConfigurations.ec-runner-aarch64-netboot` +- `nixosConfigurations.ec-runner-x86_64-netboot` +- `nixosConfigurations.ec-runner-x86_64-iso` +- `nixosConfigurations.ec-runner-aarch64-sdimage` + +The runner OS exposes this repo's flake source inside the system at: + +- `/etc/every-channel/flake` + +This allows a runner to self-build and verify artifacts from the same flake definition. + +## Build (OrbStack / Linux) + +These commands should be run inside a Linux environment with Nix enabled (e.g. OrbStack VM). + +Build netboot artifacts (iPXE/PXE): + +```sh +nix build .#nixosConfigurations.ec-runner-aarch64-netboot.config.system.build.netboot +``` + +Build an installer ISO (x86_64): + +```sh +nix build .#nixosConfigurations.ec-runner-x86_64-iso.config.system.build.isoImage +``` + +Build an aarch64 SD image: + +```sh +nix build .#nixosConfigurations.ec-runner-aarch64-sdimage.config.system.build.sdImage +``` + +## Outputs + +After building, artifacts will be in `./result` (a symlink into the Nix store). + +Common netboot outputs include: + +- `kernel` +- `initrd` +- `netboot.ipxe` + +Exact filenames may vary across NixOS releases. diff --git a/evolution/proposals/ECP-0065-nixos-runner-images.md b/evolution/proposals/ECP-0065-nixos-runner-images.md new file mode 100644 index 0000000..dd6b4b9 --- /dev/null +++ b/evolution/proposals/ECP-0065-nixos-runner-images.md @@ -0,0 +1,45 @@ +# ECP-0065: NixOS Runner Images + Netboot Artifacts + +Status: Draft + +## Decision + +Publish a first-party, reproducible NixOS "runner" system definition from this repo, and expose build outputs suitable for: + +- local-disk installs (pave/reinstall), +- netboot (iPXE/PXE) bootstrap, and +- byte-identical runner OS images produced in CI. + +The runner system: + +- is defined in-repo as a `nixosConfiguration` in `flake.nix`, +- exports the repo source tree inside the OS at a stable path (read-only) so the node can self-build and verify from the same flake, +- uses `ec-node` as the primary long-running publisher binary, with orchestration via NixOS + systemd. + +Initial implementation targets `aarch64-linux` builds first (local builds via OrbStack). `x86_64-linux` is defined in the flake but may not be built until an x86 builder is available. + +## Motivation + +- "Bootstrap path == update path": the same flake definition and CI-built artifacts should be usable to (re)install and to update. +- Fleet operability: remove per-node hand configuration; treat nodes as cattle. +- Verifiability: runners can rebuild and compare their OS closure against the CI artifacts using the embedded flake source. + +## Scope + +In scope: + +- `nixosConfigurations.ec-runner-{aarch64,x86_64}` in `flake.nix`. +- `nixosConfigurations.ec-runner-*-netboot` and `nixosConfigurations.ec-runner-*-iso` for image artifacts. +- Minimal runner NixOS module for baseline host settings and stable in-OS flake source path. +- Docs/scripts for building netboot outputs locally in OrbStack. + +Out of scope (defer): + +- CI publishing pipeline (binary cache, attestation, release upload). +- Remote runtime provisioning (fetching per-node channel lists). +- Hardware-accelerated transcode changes (keep current CPU x264 baseline). + +## Rollout / Reversibility + +- Rollout begins with local builds and a single test machine. +- Reversible by removing the `nixosConfigurations` and runner module; existing nodes can continue to run via manual `tmux` or ad-hoc installs. diff --git a/flake.nix b/flake.nix index 7651f9c..36a7082 100644 --- a/flake.nix +++ b/flake.nix @@ -12,10 +12,89 @@ let nixosModules = rec { ec-node = import ./nix/modules/ec-node.nix; + ec-runner = import ./nix/modules/ec-runner.nix; default = ec-node; }; in - { inherit nixosModules; } + { + inherit nixosModules; + + nixosConfigurations = + let + mkRunner = system: extraModules: + nixpkgs.lib.nixosSystem { + inherit system; + specialArgs = { inherit self; }; + modules = [ + ./nix/nixos/ec-runner.nix + ] ++ extraModules; + }; + in + { + # Base runner system (for normal installs). + ec-runner-aarch64 = mkRunner "aarch64-linux" [ ]; + ec-runner-x86_64 = mkRunner "x86_64-linux" [ ]; + + # Netboot artifacts (iPXE/PXE). + ec-runner-aarch64-netboot = mkRunner "aarch64-linux" [ + ({ modulesPath, ... }: { + imports = [ (modulesPath + "/installer/netboot/netboot-minimal.nix") ]; + }) + ({ config, pkgs, ... }: { + # Convenience output dir: { kernel, initrd, netboot.ipxe }. + system.build.netboot = pkgs.linkFarm "ec-runner-netboot" [ + { + name = "kernel"; + path = "${config.system.build.kernel}/${config.system.boot.loader.kernelFile}"; + } + { + name = "initrd"; + path = "${config.system.build.netbootRamdisk}/initrd"; + } + { + name = "netboot.ipxe"; + path = "${config.system.build.netbootIpxeScript}/netboot.ipxe"; + } + ]; + }) + ]; + ec-runner-x86_64-netboot = mkRunner "x86_64-linux" [ + ({ modulesPath, ... }: { + imports = [ (modulesPath + "/installer/netboot/netboot-minimal.nix") ]; + }) + ({ config, pkgs, ... }: { + system.build.netboot = pkgs.linkFarm "ec-runner-netboot" [ + { + name = "kernel"; + path = "${config.system.build.kernel}/${config.system.boot.loader.kernelFile}"; + } + { + name = "initrd"; + path = "${config.system.build.netbootRamdisk}/initrd"; + } + { + name = "netboot.ipxe"; + path = "${config.system.build.netbootIpxeScript}/netboot.ipxe"; + } + ]; + }) + ]; + + # Installer ISO (primarily for x86_64 bring-up). + ec-runner-x86_64-iso = mkRunner "x86_64-linux" [ + ({ modulesPath, ... }: { + imports = [ (modulesPath + "/installer/cd-dvd/installation-cd-minimal.nix") ]; + }) + ]; + + # aarch64 SD image (useful for quick ARM bring-up and as a "real image" build target). + ec-runner-aarch64-sdimage = mkRunner "aarch64-linux" [ + ({ modulesPath, ... }: { + imports = [ (modulesPath + "/installer/sd-card/sd-image-aarch64.nix") ]; + }) + ]; + }; + } // flake-utils.lib.eachDefaultSystem (system: let pkgs = import nixpkgs { diff --git a/nix/modules/ec-runner.nix b/nix/modules/ec-runner.nix new file mode 100644 index 0000000..5b8ef35 --- /dev/null +++ b/nix/modules/ec-runner.nix @@ -0,0 +1,28 @@ +{ lib, config, pkgs, self, ... }: + +let + cfg = config.services.every-channel.runner; +in +{ + options.services.every-channel.runner = { + enable = lib.mkEnableOption "every.channel runner base system profile"; + }; + + config = lib.mkIf cfg.enable { + # Minimal, conservative baseline for headless runners. + networking.useDHCP = lib.mkDefault true; + services.openssh.enable = lib.mkDefault true; + + # Keep Nix flakes available on the runner itself. + nix.settings.experimental-features = lib.mkDefault [ "nix-command" "flakes" ]; + + # Provide the flake source tree at a stable path (symlink into /nix/store). + environment.etc."every-channel/flake".source = self; + + environment.systemPackages = with pkgs; [ + git + jq + curl + ]; + }; +} diff --git a/nix/nixos/ec-runner.nix b/nix/nixos/ec-runner.nix new file mode 100644 index 0000000..1e11bd2 --- /dev/null +++ b/nix/nixos/ec-runner.nix @@ -0,0 +1,29 @@ +{ lib, pkgs, ... }: + +{ + imports = [ + ../modules/ec-node.nix + ../modules/ec-runner.nix + ]; + + services.every-channel.runner.enable = true; + + # This is a role image; avoid baking per-host secrets/keys. SSH host keys will be + # generated at first boot by NixOS defaults. + networking.hostName = lib.mkDefault "ec-runner"; + + time.timeZone = lib.mkDefault "UTC"; + + # Basic hygiene for unattended boxes. + services.openssh.settings.PasswordAuthentication = false; + services.openssh.settings.KbdInteractiveAuthentication = false; + + # Enable serial console logs where possible (helps in headless bring-up). + boot.kernelParams = [ + "console=tty0" + ]; + + # Required by NixOS. + system.stateVersion = "24.11"; +} +