Render a dev VM's own external SSH aliases beside its forge entry #85
No reviewers
Labels
No labels
blocked
bug
decision
duplicate
enhancement
help wanted
invalid
landed?
question
ready-to-merge
stale
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!85
Loading…
Reference in a new issue
No description provided.
Delete branch "agent/dev-vm-ssh-aliases"
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?
A dev VM can now carry SSH aliases for the few external hosts it reaches with its own key, declared in its own identity record and rendered beside its forge entry, without receiving the hypervisor's address book. On every running machine today nothing changes: no template or deployment record the public deploy lock pins declares an alias yet, so
allod-dev's andnexus's rendered SSH configs are byte-identical to the current lock, and the hypervisor path is untouched. Once a deploy lock carries this together with a secrets record that declares aliases (the template'sexample-build-cachein allod/secrets#34, or a deployment's own), those aliases appear in that VM's config at its next rebuild, keyed to the VM's own key. An alias that names the forge host is refused at evaluation here as well as in secrets, so nothing can replace the framework's forge block silently. If it is wrong, revert this PR; a secrets record carrying the field is inert against the previous framework. Closes allod/archetypes#83 and, with the companion template change, allod/strategy#57; the one deployment that raised the need has its data merged already.What changes
modules/host-ssh-hosts.nixis renamedmodules/ssh-hosts.nix: the same field-name translation, now imported by the hypervisor builder withidentity.sshHostsand by the dev VM builder with that VM'sdevIdentities.<vm>.sshHosts(empty when the secrets fork predates the field). It applies no defaults; the secrets projection has already filled inidentityFileandidentitiesOnly. Its diagnostics name the record the operator edits:identity.sshHosts.<name>on the hypervisor,devVMs.<machine>.sshHosts.<alias>on a dev VM.mkDevVmrefuses an alias whose name equals the forge host, compared case-insensitively, with an error naming the machine and the alias. This is the layer beneath the secrets projection's own refusal: a fork with a different projection still cannot redefine the blockdev-ssh-forge.nixrenders.ssh-settingscheck gains a dev fixture with one per-VM host, resolved withssh -G: the alias resolves to the VM's key,IdentitiesOnly yesand itsHostKeyAlias, the forge block beside it is unchanged, and the config holds exactly twoHostlines. A fixture drives the productionmkDevVmwith an upper-cased forge-named alias and asserts evaluation fails; deleting the builder assertion fails this fixture rather than passing it vacuously, because the two differently-cased names are distinct attributes to the module system. The wiring assertion that reads every inventory dev machine through the production builder now expects the forge line plus one line per alias its identity declares, so the composed check covers the new import as soon as the template carries an entry.Risk
R3: a new data path from secrets onto every dev VM's SSH client config, across two repositories. The worst credible failure after validation is a per-VM entry carrying a field the translation does not know, which is a loud evaluation error naming
devVMs.<machine>.sshHosts.<alias>at the next lock bump, not a silent config change. Runtime landing order with allod/secrets#34 is free: this framework reads the field with an empty default, and the field is inert against the previous framework. Merge order is not free: merge allod/secrets#34 first, then this branch takes a lock bump of its ownsecretsinput so the wiring assertion that reads the template through the production builder sees the template alias and pins the new import; until that bump, archetypes' own lock pins a template with no alias and that assertion is satisfied with or without the import (the fixtures cover it in isolation, and the composed run with the secrets branch overriding covers it end to end). Rollback is a revert.Validation
Run by the integrator at
55f2f30:nix build .#checks.x86_64-linux.ssh-settings --rebuildexits 0. The worker showed the per-VM fixture failing on a wrong expectedHostKeyAlias, the forge-alias fixture failing with the case-insensitive comparison removed and again with the assertion deleted, then reverted each.nix flake check --no-buildexits 0 (worker run, alone).--override-input archetypes <this branch>alone:allod-devandnexusrender byte-identical SSH configs to the current lock.nexusis byte-identical to the current lock;allod-devrenders the forge block unchanged plusHost example-build-cachewithHostName 192.0.2.40,User cache,IdentityFile ~/.ssh/allod_vm,IdentitiesOnly yes,HostKeyAlias example-build-cache, andssh -Gresolves both hosts to those values;allod-dev,nexusandprivacy-1evaluate to their toplevel derivations with no evaluation warnings.ssh-settingscheck built with--override-input secrets <allod/secrets#34>exits 0, so the composed wiring assertion holds with two expected lines on the template's dev machine.Cold cross-family review:
gpt-5.6-solviacodex exec, reasoning effortxhigh, read this diff at3d01544together with allod/secrets#34 (Claude-family workers authored both). Four findings, three marked blocker, all confirmed by tracing and fixed in55f2f30(this repo) and the companion's fix commit. Blockers: a flattenedextraOptions.headerbecame the block's literalHostline and captured the forge, and a newline in any string field injected a block the same way, now refused in the translation on both paths; two aliases differing only by case both matched one host, now refused; the builder's fallback compared lower-cased raw names, so a trailing space or a literalHostprefix slipped past while OpenSSH still matched the forge, now refused by requiring one literal token plus the case-insensitive forge comparison, with the negative fixture switched to the upper-cased, trailing-space form that only both rules together reject. Should-fix: the standalone wiring assertion cannot pin the new import while this repo's lock pins a template without an alias; addressed by the merge order above rather than by a heavier fixture. The reviewer traced and cleared:identityFileas a default an entry may override; missingsshKeyNamefailing loudly; the projection's refusals forced withdeepSeqand the builder negative tripping at weak-head evaluation; the shell assertions failing closed; no stale consumers of the renamed module or the check parameters; runtime landing order; RFC 5737 addresses only. No second reading follows the fix round; the fixes are settled by the rebuilt check, the by-hand capture witness before and after the refusal, and the renders re-run at55f2f30.Closes allod/archetypes#83
Closes allod/strategy#57
Both builders import modules/ssh-hosts.nix now, but every throw in it was prefixed with the literal identity.sshHosts — the hypervisor's address book. A malformed per-VM entry therefore sent the operator to the wrong record, and to the wrong repository's field. Add a `source` argument defaulting to "identity.sshHosts", so the hypervisor keeps its message, and have mkDevVm pass "devVMs.${name}.sshHosts": the record in secrets the operator edits, named by the machine's own inventory name, not the devIdentities projection the entry reaches the builder through. Refs allod/archetypes#83