feat: introduce guests as nested-container definitions #28
Reference in New Issue
Block a user
Delete Branch "task-0002-guest-walking-skeleton"
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?
Summary
Introduces the guest — a third auto-loaded kind beside the host and the module.
A guest is a reusable definition under
guests/that a host enables like a module and that realizes its interior as a systemd-nspawn nested container.guests/with the same recursion it uses formodules/andhosts/, so a guest wires itself in with no manualimportsedits.base.nix(the substrate both bases share — primary user, home-manager, theunstable/stableoverlays),system.nix(the host base, keeping bootloader, sops decryption, the password, and the maintenance timers), and a newguest.nix(the slim guest-base). The guest-base imports the fullmodules/tree, pins the interior release, and auto-enables thetoolkitbundle andmodules.ssh.modules.sshguest flavor. AhostKeys.restoreflag (default on) and nullablehostKeys.sopsFile/userKey.sopsFile. A host restores its committed host keys as before; a guest self-generates a host key and carries no age key.guesthelper inlib.nixdeclaresguests.<path>.{enable,backend}, guards the body onenable, and builds the nested container.backendis an enum defaulting tocontainer;microvmis reserved and trips a clear build-time assertion rather than silently building nothing.neogaiaenables it, so the guest interior builds through the existingnix flake checkseam.nix flake checkpasses and builds the nestednixos-system-sampleinterior in full.Verified by evaluation: the interior has the toolkit and all five members on, SSH in guest flavor with self-generated host keys and the operator's authorized key, and an empty
sops.secrets, whileneogaiastill restores its own host keys withopenssh.hostKeys = [ ].Deviations
base.nixholds the common substrate so "both bases include the primary user, home-manager, and the overlays" stays a single fact rather than a duplicated one.sops-nixandstylixalongside the module tree. This is load-bearing: the module system pushes anmkIfdown to its leaves, so an option path a module names must be declared even where itsenableis off, and the tree namessops.*andstylix.*.privateNetwork = true(asmkDefault) so its interior sshd never contends with the host's; the later networking foundation can attach it to a VLAN bridge.Review
Risk
Overall: HIGH
mkHost, and restructuresmodules/ssh.nix, so all hosts re-evaluate.git revertand rebuild fully restores the prior system.nix flake checkbuilds each toplevel plus the nested guest interior, but nothing exercises runtime SSH auth or container isolation/networking.wheel/root lockout, and the guest's network namespace.lib.guest(option-path metaprogramming) and themkMerge/mkIfSSH split.neogaia; a regression in therestore/sopsFilebranching could break host-key restore.Note on the SSH risk: verified that
neogaiais unchanged — its sops secrets still includessh-host-ed25519-key/ssh-host-rsa-keyandservices.openssh.hostKeys = [ ](restored, not generated).Standards — unaddressed
sops-nixandstylixare imported in bothguest.nixandmkHost. Left explicit — the two sites differ in intent (active on the host, declared-but-inert in the guest), and extracting a shared two-item list would be premature abstraction; theguest.nixcomment documents why the guest needs them.guest.nix's header describes what it imports and that it stands on the shared base. Kept — a one-line purpose statement is the permitted file-top form and aids navigation.Spec — unaddressed
guesthelper takesnameas a literal, so the option path matching the file's location is convention, not machinery. By design — this is exactly how a module declares its own namespace, and the loader change is the same recursion, so the index-node and file-or-folder rules a folder-shaped guest would use come for free (none exists yet to exercise them).— Claude