Dev VMs: render INVENTORY from an inventoryCheckout argument #120

Merged
vnprc merged 4 commits from agent/dev-vm-inventory-pointer into master 2026-09-29 01:59:34 +01:00
Member

A dev VM can now be told where the deployment's own inventory checkout is, so allod there finds repositories that only the deployment's registry lists. Nothing changes on any machine until a deployment sets the new value.

  • What changes for a running machine: nothing by merging this. A dev VM whose inventory entry sets inventoryCheckout gets INVENTORY in its session environment after its next rebuild, in login shells and in ssh <vm> <command> alike.
  • What stays identical: every machine that does not set it. All four machines of the public template evaluate to the same system derivation on this branch as on master.
  • What proves it: a new check builds a dev machine through the real builder and fails if the variable is not rendered, is rendered when unset, if a relative or empty path is accepted, or if ssh sessions stop receiving the session environment.
  • Not proven here: the two lines of the issue that need a rebuilt VM. They are yours to run, listed under Validation.
  • If it is wrong: remove the setting from the machine's inventory entry and rebuild, or revert this PR. Either is a straight revert with no state left behind.

Closes allod/archetypes#119

How a deployment sets it

In the machine's entry in the deployment inventory's scripts/vm-specs.json, under profile:

"profile": { "inventoryCheckout": "/home/<user>/work/<deployment>/inventory" }

machineConfigurations already merges an entry's profile settings, and profiles.lib.profileData.<machine>, into the builder's arguments, so no further plumbing was added and flake.nix is unchanged.

What changed

  • nix/builders/dev.nix: mkDevVm takes inventoryCheckout, default null. Non-null renders environment.sessionVariables.INVENTORY. Null renders nothing: the attribute is absent, not empty. A value that is not a non-empty absolute path fails evaluation with a message naming the machine and the argument, by the same rule (^/.+) the hypervisor's nexus.provisioning.inventoryCheckout applies.
  • checks/dev-inventory-pointer.nix, registered in checks/default.nix: the check described above, on a fixture machine of its own that reads no data input by name.
  • README.md, Builders: one paragraph on the argument.

Only INVENTORY is rendered. INVENTORY_CHECKOUT is a provisioning pointer with no reader on a dev VM.

Risk

R2. The change adds a session variable to a machine's generated configuration, and only where a deployment asks for it.

  • Worst credible failure after the checks pass: a deployment sets a path that is absolute and wrong. allod on that VM then reads a registry that is not there, finds no entries, and reports sites and patch ids as unknown. Nothing is written or deleted; correcting the value and rebuilding recovers it.
  • The variable is rendered through the NixOS session environment, which PAM applies, and not through home-manager, whose session variables reach only a shell that sources a login profile. The check pins the two settings that carry it into an ssh session (services.openssh.settings.UsePAM, security.pam.services.sshd.setEnvironment). That is a reading of the generated configuration, not a measurement on a running machine.
  • The new argument is also a key the profiles layer can set, which is the intent here.

Validation

Run on this branch at 05683bd:

  • allod check: All 32 steps passed (checks: 19 built)., the new check among them.
  • For each of the four machines, nix eval --raw .#nixosConfigurations.<machine>.config.system.build.toplevel.drvPath prints the same path on this branch and on master.
  • In the public allod/deploy checkout, fleet-diff --override archetypes=05683bd0aed64bafb1d472889d356294f550308d --expect-none: 0 of 4 machines change. and Expectation matches. That covers the fleet that checkout composes; a deployment's own composition root has its own.
  • The check was shown to fail three ways, each by a temporary edit that was reverted: with the render disabled (INVENTORY not rendered into the session environment), with the refusal disabled (a relative inventoryCheckout was accepted, an empty inventoryCheckout was accepted), and with UsePAM = false on the fixture (sshd does not use PAM).
  • With the argument injected for the template's dev machine, config.environment.sessionVariables held INVENTORY = "/srv/demo/inventory"; without it, no INVENTORY.

The last commit, edeed80, changes comments only: no line outside a comment differs from 05683bd. On it, the dev-inventory-pointer check was rebuilt and passed, and all four machines still evaluate to the same derivation as on master. The full allod check and fleet-diff were not repeated on it.

Not run, because they need a rebuilt VM and a deployment that sets the value:

  • echo $INVENTORY in a login shell on the dev VM, and ssh <vm> 'echo $INVENTORY' from the hypervisor.
  • allod site serve with no variable typed, in a site only the deployment's registry lists.

./fork-safety.sh was not run: it belongs to the lock-bump path, and the new check reads nothing from the data inputs.

Review

One reviewer from another model family (gpt-5.6-terra) read the diff cold, followed by one fix round.

  • Acted on: the check did not show that ssh sessions receive the variable. It now pins the two settings above.
  • Declined: a check that inventory profile settings reach the builder. That merge is unchanged here and forwards every setting alike. checks/pi-integration.nix already witnesses it for memoryCheckouts when ./fork-safety.sh runs it against the fork fixture, whose inventory sets profile.memoryCheckouts; the public template sets no profile at all.
  • Acted on: comments in the check and the builder that restated the code were cut.
A dev VM can now be told where the deployment's own inventory checkout is, so `allod` there finds repositories that only the deployment's registry lists. Nothing changes on any machine until a deployment sets the new value. - **What changes for a running machine:** nothing by merging this. A dev VM whose inventory entry sets `inventoryCheckout` gets `INVENTORY` in its session environment after its next rebuild, in login shells and in `ssh <vm> <command>` alike. - **What stays identical:** every machine that does not set it. All four machines of the public template evaluate to the same system derivation on this branch as on master. - **What proves it:** a new check builds a dev machine through the real builder and fails if the variable is not rendered, is rendered when unset, if a relative or empty path is accepted, or if ssh sessions stop receiving the session environment. - **Not proven here:** the two lines of the issue that need a rebuilt VM. They are yours to run, listed under Validation. - **If it is wrong:** remove the setting from the machine's inventory entry and rebuild, or revert this PR. Either is a straight revert with no state left behind. Closes allod/archetypes#119 ## How a deployment sets it In the machine's entry in the deployment inventory's `scripts/vm-specs.json`, under `profile`: ```json "profile": { "inventoryCheckout": "/home/<user>/work/<deployment>/inventory" } ``` `machineConfigurations` already merges an entry's `profile` settings, and `profiles.lib.profileData.<machine>`, into the builder's arguments, so no further plumbing was added and `flake.nix` is unchanged. ## What changed - `nix/builders/dev.nix`: `mkDevVm` takes `inventoryCheckout`, default null. Non-null renders `environment.sessionVariables.INVENTORY`. Null renders nothing: the attribute is absent, not empty. A value that is not a non-empty absolute path fails evaluation with a message naming the machine and the argument, by the same rule (`^/.+`) the hypervisor's `nexus.provisioning.inventoryCheckout` applies. - `checks/dev-inventory-pointer.nix`, registered in `checks/default.nix`: the check described above, on a fixture machine of its own that reads no data input by name. - `README.md`, Builders: one paragraph on the argument. Only `INVENTORY` is rendered. `INVENTORY_CHECKOUT` is a provisioning pointer with no reader on a dev VM. ## Risk R2. The change adds a session variable to a machine's generated configuration, and only where a deployment asks for it. - Worst credible failure after the checks pass: a deployment sets a path that is absolute and wrong. `allod` on that VM then reads a registry that is not there, finds no entries, and reports sites and patch ids as unknown. Nothing is written or deleted; correcting the value and rebuilding recovers it. - The variable is rendered through the NixOS session environment, which PAM applies, and not through home-manager, whose session variables reach only a shell that sources a login profile. The check pins the two settings that carry it into an ssh session (`services.openssh.settings.UsePAM`, `security.pam.services.sshd.setEnvironment`). That is a reading of the generated configuration, not a measurement on a running machine. - The new argument is also a key the profiles layer can set, which is the intent here. ## Validation Run on this branch at `05683bd`: - `allod check`: `All 32 steps passed (checks: 19 built).`, the new check among them. - For each of the four machines, `nix eval --raw .#nixosConfigurations.<machine>.config.system.build.toplevel.drvPath` prints the same path on this branch and on master. - In the public `allod/deploy` checkout, `fleet-diff --override archetypes=05683bd0aed64bafb1d472889d356294f550308d --expect-none`: `0 of 4 machines change.` and `Expectation matches.` That covers the fleet that checkout composes; a deployment's own composition root has its own. - The check was shown to fail three ways, each by a temporary edit that was reverted: with the render disabled (`INVENTORY not rendered into the session environment`), with the refusal disabled (`a relative inventoryCheckout was accepted`, `an empty inventoryCheckout was accepted`), and with `UsePAM = false` on the fixture (`sshd does not use PAM`). - With the argument injected for the template's dev machine, `config.environment.sessionVariables` held `INVENTORY = "/srv/demo/inventory"`; without it, no `INVENTORY`. The last commit, `edeed80`, changes comments only: no line outside a comment differs from `05683bd`. On it, the `dev-inventory-pointer` check was rebuilt and passed, and all four machines still evaluate to the same derivation as on master. The full `allod check` and `fleet-diff` were not repeated on it. Not run, because they need a rebuilt VM and a deployment that sets the value: - `echo $INVENTORY` in a login shell on the dev VM, and `ssh <vm> 'echo $INVENTORY'` from the hypervisor. - `allod site serve` with no variable typed, in a site only the deployment's registry lists. `./fork-safety.sh` was not run: it belongs to the lock-bump path, and the new check reads nothing from the data inputs. ## Review One reviewer from another model family (`gpt-5.6-terra`) read the diff cold, followed by one fix round. - Acted on: the check did not show that ssh sessions receive the variable. It now pins the two settings above. - Declined: a check that inventory `profile` settings reach the builder. That merge is unchanged here and forwards every setting alike. `checks/pi-integration.nix` already witnesses it for `memoryCheckouts` when `./fork-safety.sh` runs it against the fork fixture, whose inventory sets `profile.memoryCheckouts`; the public template sets no `profile` at all. - Acted on: comments in the check and the builder that restated the code were cut.
`allod` reads the repository registry from `$INVENTORY/scripts/repositories.json`
when that variable is set. The hypervisor renders it from
`nexus.provisioning.inventoryCheckout`; nothing rendered it on a dev VM, so a
registry lookup for a repository only the deployment's inventory lists failed
there.

`mkDevVm` takes `inventoryCheckout`, defaulting to null. Non-null renders
`INVENTORY` into `environment.sessionVariables` — the NixOS session environment
PAM applies, so a non-interactive `ssh <vm> <command>` sees it too. Null renders
nothing, so the public template composes exactly as before. A value that is not
a non-empty absolute path fails evaluation, by the rule the hypervisor's own
pointer applies.

A deployment sets it through `machineConfigurations`, which already merges
`profileData` and the inventory entry's `profile` settings into the builder's
arguments.

The `dev-inventory-pointer` check builds its own fixture machine through the
production builder and pins all three: the value rendered, the absence under the
default, and the refusal of a relative and an empty path beside a control that
evaluates.

Refs allod/archetypes#119
Builders is where the builders' arguments are described, so the new one
joins them: what it points at, that null renders nothing, and that a
deployment sets it through the data machineConfigurations already merges.

Refs allod/archetypes#119
The pointer is only reachable from `ssh <vm> <command>` because sshd uses
PAM and its PAM service sets the environment; a machine that stopped doing
either would still render INVENTORY and still pass the check. Read both
facts off the fixture, one failure line each. The sshd PAM service is read
fail-closed, since NixOS declares it only while sshd uses PAM.

The comments in the check and in mkDevVm shrink to the traps they were
carrying: the platform literal, the preferences default, the shallow WHNF
force, the control, and why the variable is a NixOS session variable rather
than a home-manager one. The rest restated the code or the README.

Refs allod/archetypes#119
Author
Member

Owner request: cut down the verbose comments in this PR. Follow-up commit to come on this branch; no behaviour change.

Owner request: cut down the verbose comments in this PR. Follow-up commit to come on this branch; no behaviour change.
Each comment keeps the trap or the pointer it carried and loses the
explanation around it. No code changes.

Refs allod/archetypes#119
vnprc approved these changes 2026-09-29 01:59:28 +01:00
vnprc merged commit edeed80fbd into master 2026-09-29 01:59:34 +01:00
vnprc deleted branch agent/dev-vm-inventory-pointer 2026-09-29 01:59:35 +01:00
Author
Member

Correction to this pull request's description, section "How a deployment sets it". It names the wrong file.

scripts/vm-specs.json is generated and is not what the framework reads. The setting belongs in the machine's entry in the inventory's flake.nix, beside repos:

profile.inventoryCheckout = "/home/<user>/work/<deployment>/inventory";

Then regenerate scripts/vm-specs.json as the inventory README describes, and rebuild the VM. The README paragraph this pull request added is correct as merged; only the description above was wrong.

Correction to this pull request's description, section "How a deployment sets it". It names the wrong file. `scripts/vm-specs.json` is generated and is not what the framework reads. The setting belongs in the machine's entry in the inventory's `flake.nix`, beside `repos`: ```nix profile.inventoryCheckout = "/home/<user>/work/<deployment>/inventory"; ``` Then regenerate `scripts/vm-specs.json` as the inventory README describes, and rebuild the VM. The README paragraph this pull request added is correct as merged; only the description above was wrong.
Sign in to join this conversation.
No description provided.