nix/configuration.nix: host option to inject provisioning flake/inventory pointers into the operator environment #10

Closed
opened 2026-07-15 19:05:02 +01:00 by vnprc-agent · 0 comments

Add an option to the host framework module (nixosModules.host, nix/configuration.nix) that injects the provisioning flake/inventory pointers into the operator's shell environment, so an operator on a host whose pointers differ from the baked defaults runs rebuild-vm-from-host <machine> / provision-vm-from-host <machine> with no per-shell exports.

Context

rebuild-vm-from-host and provision-vm-from-host read their provisioning pointers from the operator's shell environment. As of the current master (after issue #5 and issue #6), the contract is: rebuild-vm-from-host reads only DEPLOY_FLAKE (default $HOME/work/allod/profiles) and derives the target IP, username, and host-key pin from deploy_flake_vm_facts; provision-vm-from-host reads DEPLOY_FLAKE plus INVENTORY_CHECKOUT (default $HOME/work/allod/inventory), where the inventory checkout still backs assert_inventory_worktree_ip_matches_pin and is exported as INVENTORY to the new-vm child (DHCP-lease eviction and scripts/vm-specs.json). SECRETS_CHECKOUT is gone (issue #6).

The provisioning scripts are installed unwrapped — nix/provisioning-scripts.nix copies them and runs patchShebangs — so they resolve both their tool dependencies and these pointers from the system environment. The host framework module (nixosModules.host, nix/configuration.nix) already sets one provisioning-relevant session variable this way, environment.sessionVariables.LIBVIRT_DEFAULT_URI, but has no option for the deploy-flake/inventory pointers. Consequently a host whose pointers differ from the baked $HOME/work/allod/{profiles,inventory} defaults must export them by hand in every shell before invoking the scripts.

That per-shell export is host configuration data living in operator memory. Principle 8 (one source of truth per fact; consumers derive): the deploy-flake pointer is a fact the host knows once, so it should be declared in host configuration and rendered into the environment the unwrapped scripts read, not retyped per session.

Direction

Add an option to the host framework module (nix/configuration.nix / nixosModules.host) that, when set, renders the provisioning pointers into the operator's environment. The natural mechanism is environment.sessionVariables, alongside the existing LIBVIRT_DEFAULT_URI; a wrapper around the scripts is an alternative the implementation may weigh, but plain environment injection matches the current unwrapped-script design. Grounded on the current script contract, the option surface is:

Variable Consumed by (current master) Include in the option?
DEPLOY_FLAKE rebuild-vm-from-host, provision-vm-from-host Yes — the intended steady-state single pointer
INVENTORY_CHECKOUT provision-vm-from-host only (inventory-IP pin cross-check plus the INVENTORY export to new-vm) Yes, but only while provision-vm-from-host still reads it; remove its option in the same change that drops that read
SECRETS_CHECKOUT none (removed by issue #6) No — do not add

The option should be unset by default and render nothing when unset, so the public template host (which relies on the baked $HOME/work/allod/{profiles,inventory} defaults) is unchanged and the scripts keep their own fallbacks. A malformed configured value should fail evaluation loudly rather than silently substituting a default (principle 11). Because issue #6 already collapsed rebuild to a single pointer, the steady-state surface is DEPLOY_FLAKE alone; INVENTORY_CHECKOUT is included only for the window while provision-vm-from-host still reads it.

Keep behavior in the public framework and data out of it (principle 3, principle 5): the option lives in the framework module, and a deployment supplies its real deploy-flake and inventory locations by composing nixosModules.host and setting the option. This issue is the public option only; wiring real host values is a separate, private concern.

Boundaries

Public framework module and its template validation only. Out of scope: the script env-var contract itself (owned by issue #5 / issue #6 and any successor), wrapping the provisioning scripts, weakening assert_inventory_worktree_ip_matches_pin or the host-key pin preflight, and any private host wiring or private deploy-flake data (tracked and performed privately). Host-side live rebuild/provision remains a human gate.

Implementation note: the module is currently a config-only function ({ pkgs, hostname, agenix, ... }: { ... }), so introducing an option means adding an options block (or a sibling module) while preserving the existing nixosModules.host import interface. The no-hardcoded-arch check forbids literal system strings in nix/ modules; an env-var option satisfies this trivially.

Validation

A NixOS module-eval check (new, in the flake checks, gated by nix flake check) that instantiates a minimal template host composing nixosModules.host: with the option set, assert the rendered environment.sessionVariables (or the chosen mechanism) carries the configured DEPLOY_FLAKE (and INVENTORY_CHECKOUT) value; with the option unset, assert no such variable is forced. The existing provisioning-contract and no-hardcoded-arch checks must stay green.

Classification

Single-repo, single-option change on the operator-convenience seam, with no cross-repo interface change. Residual risk R1-R2: a wrong rendered pointer aims host-side rebuild/provision at the wrong deploy data, recoverable by reverting the host config or unsetting the variable for a manual run. Small enough to land without a standalone dev plan; if a consumer bundles it with a private adoption, that planning happens on the private side.

Sequencing

Public-first and unblocked: builds on issue #5 and issue #6, both landed. Prove the option on a template host. Any private adoption composes this option afterward and is out of scope here.

Add an option to the host framework module (`nixosModules.host`, `nix/configuration.nix`) that injects the provisioning flake/inventory pointers into the operator's shell environment, so an operator on a host whose pointers differ from the baked defaults runs `rebuild-vm-from-host <machine>` / `provision-vm-from-host <machine>` with no per-shell exports. ## Context `rebuild-vm-from-host` and `provision-vm-from-host` read their provisioning pointers from the operator's shell environment. As of the current master (after issue #5 and issue #6), the contract is: `rebuild-vm-from-host` reads only `DEPLOY_FLAKE` (default `$HOME/work/allod/profiles`) and derives the target IP, username, and host-key pin from `deploy_flake_vm_facts`; `provision-vm-from-host` reads `DEPLOY_FLAKE` plus `INVENTORY_CHECKOUT` (default `$HOME/work/allod/inventory`), where the inventory checkout still backs `assert_inventory_worktree_ip_matches_pin` and is exported as `INVENTORY` to the `new-vm` child (DHCP-lease eviction and `scripts/vm-specs.json`). `SECRETS_CHECKOUT` is gone (issue #6). The provisioning scripts are installed unwrapped — `nix/provisioning-scripts.nix` copies them and runs `patchShebangs` — so they resolve both their tool dependencies and these pointers from the system environment. The host framework module (`nixosModules.host`, `nix/configuration.nix`) already sets one provisioning-relevant session variable this way, `environment.sessionVariables.LIBVIRT_DEFAULT_URI`, but has no option for the deploy-flake/inventory pointers. Consequently a host whose pointers differ from the baked `$HOME/work/allod/{profiles,inventory}` defaults must export them by hand in every shell before invoking the scripts. That per-shell export is host configuration data living in operator memory. Principle 8 (one source of truth per fact; consumers derive): the deploy-flake pointer is a fact the host knows once, so it should be declared in host configuration and rendered into the environment the unwrapped scripts read, not retyped per session. ## Direction Add an option to the host framework module (`nix/configuration.nix` / `nixosModules.host`) that, when set, renders the provisioning pointers into the operator's environment. The natural mechanism is `environment.sessionVariables`, alongside the existing `LIBVIRT_DEFAULT_URI`; a wrapper around the scripts is an alternative the implementation may weigh, but plain environment injection matches the current unwrapped-script design. Grounded on the current script contract, the option surface is: | Variable | Consumed by (current master) | Include in the option? | | --- | --- | --- | | `DEPLOY_FLAKE` | `rebuild-vm-from-host`, `provision-vm-from-host` | Yes — the intended steady-state single pointer | | `INVENTORY_CHECKOUT` | `provision-vm-from-host` only (inventory-IP pin cross-check plus the `INVENTORY` export to `new-vm`) | Yes, but only while `provision-vm-from-host` still reads it; remove its option in the same change that drops that read | | `SECRETS_CHECKOUT` | none (removed by issue #6) | No — do not add | The option should be unset by default and render nothing when unset, so the public template host (which relies on the baked `$HOME/work/allod/{profiles,inventory}` defaults) is unchanged and the scripts keep their own fallbacks. A malformed configured value should fail evaluation loudly rather than silently substituting a default (principle 11). Because issue #6 already collapsed `rebuild` to a single pointer, the steady-state surface is `DEPLOY_FLAKE` alone; `INVENTORY_CHECKOUT` is included only for the window while `provision-vm-from-host` still reads it. Keep behavior in the public framework and data out of it (principle 3, principle 5): the option lives in the framework module, and a deployment supplies its real deploy-flake and inventory locations by composing `nixosModules.host` and setting the option. This issue is the public option only; wiring real host values is a separate, private concern. ## Boundaries Public framework module and its template validation only. Out of scope: the script env-var contract itself (owned by issue #5 / issue #6 and any successor), wrapping the provisioning scripts, weakening `assert_inventory_worktree_ip_matches_pin` or the host-key pin preflight, and any private host wiring or private deploy-flake data (tracked and performed privately). Host-side live rebuild/provision remains a human gate. Implementation note: the module is currently a config-only function (`{ pkgs, hostname, agenix, ... }: { ... }`), so introducing an option means adding an `options` block (or a sibling module) while preserving the existing `nixosModules.host` import interface. The `no-hardcoded-arch` check forbids literal system strings in `nix/` modules; an env-var option satisfies this trivially. ## Validation A NixOS module-eval check (new, in the flake `checks`, gated by `nix flake check`) that instantiates a minimal template host composing `nixosModules.host`: with the option set, assert the rendered `environment.sessionVariables` (or the chosen mechanism) carries the configured `DEPLOY_FLAKE` (and `INVENTORY_CHECKOUT`) value; with the option unset, assert no such variable is forced. The existing `provisioning-contract` and `no-hardcoded-arch` checks must stay green. ## Classification Single-repo, single-option change on the operator-convenience seam, with no cross-repo interface change. Residual risk R1-R2: a wrong rendered pointer aims host-side rebuild/provision at the wrong deploy data, recoverable by reverting the host config or unsetting the variable for a manual run. Small enough to land without a standalone dev plan; if a consumer bundles it with a private adoption, that planning happens on the private side. ## Sequencing Public-first and unblocked: builds on issue #5 and issue #6, both landed. Prove the option on a template host. Any private adoption composes this option afterward and is out of scope here.
vnprc closed this issue 2026-07-15 21:15:41 +01:00
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
allod/nexus#10
No description provided.