Wire a microvm guest into the host that starts it, so declared credential names become real host paths #38
Labels
No labels
bug
duplicate
enhancement
help wanted
invalid
question
wontfix
bug
duplicate
enhancement
help wanted
invalid
question
wontfix
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
allod/archetypes#38
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Connect a microvm guest to the host that starts it, so the credential names the guest declares become the actual file paths QEMU reads at boot.
Everything on both sides of that join now exists and nothing joins them. A selected microvm guest declares which credentials it expects by name and deliberately supplies no values (allod/archetypes#29, contract 7);
allod/nexusowns the host root those values are derived from and asserts that the two sides agree (nix/microvm/host.nix:234-257). ButmkHypervisorcomposes onlynexus.nixosModules.host(flake.nix:748), nevernexus.nixosModules.microvmHost, and nothing anywhere setsmicrovm.vms.<name>. So no host can start a credentialed microvm guest at all, and the agreement nexus asserts is asserted about nothing.Primary goals:
extendModuleson the already-exported guestnixosConfiguration, soallod/nexusnever importsallod/archetypesand never restates the credential-name set.-fw_cfg file=arguments name nothing.nexus.microvm.hostPlaintextRootchanges everycredentialFilesvalue and the built runner's-fw_cfg file=arguments, proved by a second fixture at a non-default root rather than by a text scan for the default.nexus.inputs.vm.follows = "vm"so the host module and the guest module come from oneallod/vm, one microvm.nix, and one nixpkgs (contract 1a), checked against the lock graph rather than assumed.restartIfChanged = trueexplicitly, because supplyingevaluatedConfigflips upstream's default to false andswitch-to-configurationwould otherwise skip the unit on every rebuild.Current state
allod/archetypesafter allod/archetypes#29:config.allod.archetypes.microvm.credentialNames— a closed set derived from what the machine declares,readOnlyand internal (modules/microvm-credentials.nix:160). Itsmicrovm.credentialFilesis{}, and the module asserts that it is either empty or keyed exactly by that name set (modules/microvm-credentials.nix:278), which is what makes an extended result legal and a mismatched one an evaluation error.mkHypervisorcomposesnexus.nixosModules.hostand nothing microvm-related (flake.nix:748).nexusinput followsnixpkgs,home-managerandagenix, but notvm(flake.nix:19-24).allod/nexus:nixosModules.microvmHostcomposesallod/vm's re-exported upstream host module with the Allod host module (flake.nix:50-52), and is deliberately not part of the baseline host — a libvirt-only host does not acquire microvm.nix by importingnixosModules.host.nexus.microvm.hostPlaintextRootdefaults to/run/allod/microvmand is a realnoswaptmpfs mount (nix/microvm/host.nix:316). Per-VM credentials live under its derivedactive/<machine>child (nix/microvm/host.nix:43,85).nexus.microvm.machines.<name>.credentialSourcesmaps each credential name to an absolute host source path, supplied by deployment composition (nix/microvm/host.nix:353).microvm.credentialFileskeys and that each value is the exact<root>/active/<machine>/<credential>string (nix/microvm/host.nix:234-257). Both assertions are unreachable today because no host composes a guest.The join
For each machine whose
runtimefact ismicrovm, the hypervisor configuration:nexus.nixosModules.microvmHostalongsidenexus.nixosModules.host. A fleet with no microvm machine composes neither, so a libvirt-only host's generated output is unchanged.extendModuleson that machine's exported guestnixosConfiguration, adding one module that setsmicrovm.credentialFilestobuiltins.listToAttrsover the guest's ownconfig.allod.archetypes.microvm.credentialNames, each value the exact string<host's evaluated nexus.microvm.hostPlaintextRoot>/active/<machine>/<name>.microvm.vms.<name>.evaluatedConfig, and setsmicrovm.vms.<name>.restartIfChanged = true.The values are strings and never Nix paths: a path literal would copy the host's credential into the world-readable store, which is the failure contract 7 exists to name. The guest module already rejects a store-prefixed or non-string value, so the extension is checked by the configuration it extends.
Validation
credentialFileskeys, every value equals the host-option-derived path, and the runner the host installs is the extended guest's, not the standalone one. It compares evaluated values, not prose and not a text scan for a default path.hostPlaintextRoot, where everycredentialFilesvalue and the built runner's-fw_cfg file=arguments follow it. A fixture retaining a default-root path fails.microvm.vms.<name>.evaluatedConfigfails, and for the reason it names rather than for a missing attribute somewhere downstream.X-RestartIfChanged=true. Read off the renderedmicrovm@<name>drop-in, with a paired fixture that drops the explicit setting and shows the drop-in going false.vm, microvm.nix and nixpkgs revisions.Every assertion added here carries a paired sabotage that fails for the diagnostic it names, per the same rule the credential slice followed.
Scope
In scope, all in
allod/archetypes: thenexus.inputs.vm.followsredirect, composing the microvm host module, theextendModulesjoin,restartIfChanged, and the checks above.Out of scope and tracked elsewhere:
credentialSourcespaths, volume image locations, TAP attachment, addressing and cutover are deployment composition and belong to the private integration plan. Nothing here supplies a real path.runtimefact ismicrovm— anallod/inventorydata change, not this one. It needs its own issue and carries a constraint worth stating there: the machine that carriesruntime = "microvm"must be an example machine and nothing else, because per-machine encrypted secret filenames are keyed to the name and renaming afterwards is a human-only host action.Part of allod/strategy#20
Implemented, with two additions to absorb and one verification request
The join is implemented and under review. Two things go beyond what this issue's scope names, and neither is optional; recording them here rather than leaving them to be found in the diff.
1. The join sets the guest's
microvm.socket, because a composed host does not evaluate without itThis issue's "The join" section lists three steps and none of them is the QMP socket. But
allod/nexusrefuses upstream's default — a bare"<hostName>.sock", relative to the unit's working directory — and requires a normalized absolute path strictly below/run/allod, outside both the host plaintext root and the namespace roots (nix/microvm/host.nixcontract 8). Nothing inallod/archetypesorallod/vmsets it.So composing
nexus.nixosModules.microvmHostproduces a configuration that fails evaluation on nexus's own assertion, and the goal this issue states — a host that can start a credentialed guest — is unreachable until someone owns that path. The join owns it, for the same reason it owns the credential paths: the host is the only side that knows where its own runtime tree lives. The value is/run/allod/microvm-qmp/<machine>/control.sock, the layout nexus's host fixtures already assume, written as a framework literal rather than an option because no deployment has needed to move it.If the framework would rather this lived in
allod/vm's guest module, that is a defensible call and a separate change — but it cannot simply be dropped.2. Each wired VM sets
autostart = falseUpstream defaults
microvm.vms.<name>.autostartto true, andmicrovms.targetthen wants the unit, so wiring a guest without disarming it makes a host rebuild start it.allod/strategy#20's agent gates make enabling autostart for a real machine a human-only act, so the framework declares the VM without arming it and a deployment opts in. Measured: with the line removed,config.microvm.autostartcomes back naming the machine.One planned assertion was cut as unprovable
The join asserts exactly one rule — that every wired guest's
credentialFileskeys equal its declaredcredentialNames, which is the propertyallod/nexuscannot state because it must not learn the name set. Two neighbouring conditions were written and removed: nexus dereferencesvm.guest.microvm.credentialFileswhile building its own assertion list (nix/microvm/host.nix:259), so a VM wired in upstream's other two shapes, or wired to something that is not a microvm guest, raises a raw attribute error before any archetypes assertion can render. Both still fail loudly, just not with a message from this repo.Worth knowing for whoever writes the next slice: a check that reads
config.microvm.vmsor a rendered unit does not forceconfig.assertions— nixpkgs only enforces those throughsystem.build.toplevel. The first working version of this check passed while its own fixtures were configurations no host could build, and that is exactly how the socket problem above stayed hidden. Every positive fixture now forcestoplevel.drvPathand reports the failed assertion messages.Verification request
Everything is proved against the public example fleet, which is not the fleet that matters. Two requests, both pure evaluation, neither needing hardware, a boot, or real key material.
No machine moves. At the head of the archetypes branch for this issue, compare
nixosConfigurations.<each>.config.system.build.toplevel.drvPathagainst the parent commit and report which machines move. A pass is: none. Every change is gated on the fleet declaring a microvm machine and no machine declares that runtime, so any machine that moves is a leak in the gating. Measured on the public fleet: all four unchanged, both for thenexus.inputs.vm.follows = "vm"lock change alone and for the whole commit.The join against real machine data. For a scratch evaluation with one private machine switched to
runtime = "microvm", and the deployment's ownnexus.microvm.machines.<name>.credentialSourcessupplied, report whether the hypervisor evaluates and whatconfig.microvm.vms.<name>.evaluatedConfig.config.microvm.credentialFilescontains. A pass is: it evaluates, and every value is<the deployment's hostPlaintextRoot>/active/<machine>/<name>keyed exactly by that machine's declared names.Two things a deployment needs to know before it can evaluate a microvm machine at all:
credentialSourcesmust be supplied for it and must name exactly the credentials the machine declares, and the QMP socket now lands under/run/allod/microvm-qmp, which must not collide with that deployment'shostPlaintextRoot.Nothing here asks for a rebuild or a cutover, and no machine should select the microvm runtime on the strength of this change.
allod-agent referenced this issue from allod/strategy2026-08-20 16:42:26 +01:00
merged