Pass inventory platform into the VM builders and check the built system matches #14

Open
opened 2026-07-28 15:27:44 +01:00 by allod-agent · 0 comments
Member

Pass each machine's inventory platform into the VM builders' specialArgs, and add a check that the built system matches the inventory fact, so a platform mismatch fails loudly instead of silently building the wrong architecture.

Primary goals:

  • Deliver the fact to the module that needs itmkDevVm and mkPrivacyVm both bind platform and then drop it, so the guest module in allod/vm has no way to derive it.
  • Make a mismatch loud — no check in any repo compares the built system against machines.<name>.platform; today the two disagree with zero diagnostics.
  • Validate the validator — pair the coherence check with a mutation case proving it fails on sabotaged input, in the style of the existing vm-facts-negative check.
  • Sequence the arc — this must land before issue allod/vm#1 makes the argument required, because archetypes pins vm as a flake input.

Current state

mkDevVm binds platform = machines.${name}.platform (flake.nix:168) and honors it for nixpkgs-unstable (:169) and for nixosSystem { system = platform; } (:173), but its specialArgs (:174) passes only hostPublicKeys, identity, tokenFile, secrets, and username. mkPrivacyVm has the same shape: platform bound at :214, specialArgs at :220 passing only hostPublicKeys and username.

The system = platform argument does not survive contact with the guest module. nixpkgs.hostPlatform takes precedence whenever it is defined at any priority, and allod/vm's modules/qemu-guest.nix:18 defines it as lib.mkDefault "x86_64-linux", which every dev and privacy VM imports through sharedModules (flake.nix:60). Evaluated against the pinned nixpkgs b6018f87da91d19d0ab4cf979885689b469cdd41, with inventory declaring aarch64-linux:

configuration resulting pkgs.stdenv.hostPlatform.system
current code, mkDefault "x86_64-linux" present x86_64-linux
guest module line deleted aarch64-linux
platform passed explicitly aarch64-linux

The wrong case emits no warnings and no failed assertions. nixpkgs' own platform-conflict assertion (nixos/modules/misc/nixpkgs.nix:395) is vacuous here: it keys off nixpkgs.localSystem's priority, and nixos/lib/eval-config.nix:55 defines nixpkgs.system, never localSystem.

mkHypervisor (flake.nix:233) does not include sharedModules and never imports the guest module, so it already derives its platform correctly. Only the dev and privacy builders are affected.

Because flake.nix:169 does honor the fact for nixpkgs-unstable, a non-x86_64 machine would also get an arch-mismatched home closure — claude-code, codex, and pi-coding-agent built for the inventory platform inside a system built for x86_64-linux.

No check catches it

checks (flake.nix:370) is keyed by genAttrs supportedPlatforms but never compares the inventory fact against the built system. None of credential-profiles (:371), vm-facts-coherence (:671), vm-facts-negative (:727), netrc-activation (:784), profile-definition-contracts (:804), composed-layer-smoke (:857), pi-integration (:862), or agent-vm-status (:891) reads a platform. platform is absent from vmFacts (nix/vm-facts.nix:82-87) and from vmSpecsJson in allod/inventory (flake.nix:94-97), so neither the facts layer nor the provisioning scripts can see it.

The nearest loud thing is the installer single-platform assertion (flake.nix:51-52), which fires only for a heterogeneous fleet, only when nixosConfigurations.installer is forced — nix flake check forces it, nixos-rebuild --flake .#<machine> and nixos-anywhere do not — and reports an unrelated message. A homogeneous non-x86_64 fleet gets no diagnostic at all.

Scope

In scope: adding platform to specialArgs at flake.nix:174 and :220; a per-machine coherence check asserting that the built pkgs.stdenv.hostPlatform.system equals machines.<name>.platform, with a mutation case proving the check fails on sabotaged input; and the vm input bump once allod/vm#1 lands.

Handled by issue allod/vm#1: requiring the argument in modules/qemu-guest.nix, removing boot.kernelModules = [ "kvm-intel" ], the apps.x86_64-linux.agenix key, and a no-hardcoded-arch guard in that repo.

Out of scope, each its own follow-up if wanted: relaxing the single-platform installer assertion for heterogeneous fleets; carrying platform into vm-specs.json and new-vm, which passes no --arch and so cannot provision a non-native guest; and the binfmt or remote-builder support a genuinely non-native machine needs, since provision-vm-from-host and rebuild-vm-from-host build the closure on nexus. This issue makes the platform correct and loud; it does not make aarch64 provisionable.

Sequencing

archetypes pins vm by flake input (flake.nix:5), so the argument must be passed before it is required: (1) the specialArgs change here, inert on its own since unused specialArgs are harmless; (2) issue allod/vm#1; (3) the vm input bump plus the coherence check and its mutation case. Reversing (1) and (2) breaks evaluation at the input bump.

Part of the same arc as issue allod/vm#1.

Pass each machine's inventory `platform` into the VM builders' `specialArgs`, and add a check that the built system matches the inventory fact, so a platform mismatch fails loudly instead of silently building the wrong architecture. Primary goals: - **Deliver the fact to the module that needs it** — `mkDevVm` and `mkPrivacyVm` both bind `platform` and then drop it, so the guest module in `allod/vm` has no way to derive it. - **Make a mismatch loud** — no check in any repo compares the built system against `machines.<name>.platform`; today the two disagree with zero diagnostics. - **Validate the validator** — pair the coherence check with a mutation case proving it fails on sabotaged input, in the style of the existing `vm-facts-negative` check. - **Sequence the arc** — this must land before issue `allod/vm#1` makes the argument required, because `archetypes` pins `vm` as a flake input. ### Current state `mkDevVm` binds `platform = machines.${name}.platform` (`flake.nix:168`) and honors it for `nixpkgs-unstable` (`:169`) and for `nixosSystem { system = platform; }` (`:173`), but its `specialArgs` (`:174`) passes only `hostPublicKeys`, `identity`, `tokenFile`, `secrets`, and `username`. `mkPrivacyVm` has the same shape: `platform` bound at `:214`, `specialArgs` at `:220` passing only `hostPublicKeys` and `username`. The `system = platform` argument does not survive contact with the guest module. `nixpkgs.hostPlatform` takes precedence whenever it is defined at any priority, and `allod/vm`'s `modules/qemu-guest.nix:18` defines it as `lib.mkDefault "x86_64-linux"`, which every dev and privacy VM imports through `sharedModules` (`flake.nix:60`). Evaluated against the pinned nixpkgs `b6018f87da91d19d0ab4cf979885689b469cdd41`, with inventory declaring `aarch64-linux`: | configuration | resulting `pkgs.stdenv.hostPlatform.system` | |---|---| | current code, `mkDefault "x86_64-linux"` present | `x86_64-linux` | | guest module line deleted | `aarch64-linux` | | `platform` passed explicitly | `aarch64-linux` | The wrong case emits no warnings and no failed assertions. nixpkgs' own platform-conflict assertion (`nixos/modules/misc/nixpkgs.nix:395`) is vacuous here: it keys off `nixpkgs.localSystem`'s priority, and `nixos/lib/eval-config.nix:55` defines `nixpkgs.system`, never `localSystem`. `mkHypervisor` (`flake.nix:233`) does not include `sharedModules` and never imports the guest module, so it already derives its platform correctly. Only the dev and privacy builders are affected. Because `flake.nix:169` does honor the fact for `nixpkgs-unstable`, a non-x86_64 machine would also get an arch-mismatched home closure — `claude-code`, `codex`, and `pi-coding-agent` built for the inventory platform inside a system built for `x86_64-linux`. ### No check catches it `checks` (`flake.nix:370`) is keyed by `genAttrs supportedPlatforms` but never compares the inventory fact against the built system. None of `credential-profiles` (`:371`), `vm-facts-coherence` (`:671`), `vm-facts-negative` (`:727`), `netrc-activation` (`:784`), `profile-definition-contracts` (`:804`), `composed-layer-smoke` (`:857`), `pi-integration` (`:862`), or `agent-vm-status` (`:891`) reads a platform. `platform` is absent from `vmFacts` (`nix/vm-facts.nix:82-87`) and from `vmSpecsJson` in `allod/inventory` (`flake.nix:94-97`), so neither the facts layer nor the provisioning scripts can see it. The nearest loud thing is the installer single-platform assertion (`flake.nix:51-52`), which fires only for a heterogeneous fleet, only when `nixosConfigurations.installer` is forced — `nix flake check` forces it, `nixos-rebuild --flake .#<machine>` and `nixos-anywhere` do not — and reports an unrelated message. A homogeneous non-x86_64 fleet gets no diagnostic at all. ### Scope In scope: adding `platform` to `specialArgs` at `flake.nix:174` and `:220`; a per-machine coherence check asserting that the built `pkgs.stdenv.hostPlatform.system` equals `machines.<name>.platform`, with a mutation case proving the check fails on sabotaged input; and the `vm` input bump once `allod/vm#1` lands. Handled by issue `allod/vm#1`: requiring the argument in `modules/qemu-guest.nix`, removing `boot.kernelModules = [ "kvm-intel" ]`, the `apps.x86_64-linux.agenix` key, and a `no-hardcoded-arch` guard in that repo. Out of scope, each its own follow-up if wanted: relaxing the single-platform installer assertion for heterogeneous fleets; carrying `platform` into `vm-specs.json` and `new-vm`, which passes no `--arch` and so cannot provision a non-native guest; and the binfmt or remote-builder support a genuinely non-native machine needs, since `provision-vm-from-host` and `rebuild-vm-from-host` build the closure on nexus. This issue makes the platform correct and loud; it does not make `aarch64` provisionable. ### Sequencing `archetypes` pins `vm` by flake input (`flake.nix:5`), so the argument must be passed before it is required: (1) the `specialArgs` change here, inert on its own since unused `specialArgs` are harmless; (2) issue `allod/vm#1`; (3) the `vm` input bump plus the coherence check and its mutation case. Reversing (1) and (2) breaks evaluation at the input bump. Part of the same arc as issue `allod/vm#1`.
Sign in to join this conversation.
No description provided.