feat: introduce guests as nested-container definitions

Add a third auto-loaded kind beside the host and the module: the guest, a
reusable definition under guests/ that a host enables like a module and that
realizes its interior as a systemd-nspawn nested container.

Split the shared base config so a guest can stand on it.
base.nix now holds the substrate both bases share — the primary user,
home-manager, and the unstable/stable overlays.
system.nix keeps the host-only machinery, and a new guest.nix is the slim
guest-base: it imports the full modules tree, pins the interior release, and
auto-enables the toolkit bundle and SSH so any guest is workable on sight.

Give modules.ssh a guest flavor.
A host restores its host keys from secrets as before, while a guest sets
hostKeys.restore = false, names no sops files, and self-generates a host key,
so it holds no age key of its own.

The lib grows a guest helper that declares the guests.<path> namespace with an
enable and a backend field.
Only the container backend is built; microvm is a reserved value that trips a
clear build-time assertion rather than silently building nothing.

A sample guest exercises the whole path, and neogaia enables it, so the guest
interior builds through the existing nix flake check seam.
This commit was merged in pull request #28.
This commit is contained in:
2026-07-25 13:19:07 -04:00
parent 8cd59cb292
commit e7d7eb14e1
8 changed files with 378 additions and 166 deletions

82
base.nix Normal file
View File

@@ -0,0 +1,82 @@
{
config,
lib,
inputs,
...
}:
# The shared foundation both the host base and the guest-base build on: the
# primary user, home-manager, and the fresher/pinned package overlays.
let
inherit (lib) mkOption types;
user = config.user;
# Args to instantiate an extra nixpkgs source on the base platform.
pinArgs = prev: {
inherit (prev.stdenv.hostPlatform) system;
config.allowUnfree = true;
};
in
{
imports = [ inputs.home-manager.nixosModules.home-manager ];
options.user = {
name = mkOption {
type = types.str;
default = "alexion";
description = ''
The primary interactive user this system is built for. Drives both the
system account and the home-manager user in lockstep.
'';
};
description = mkOption {
type = types.str;
default = "Alexion";
description = "Human-readable description (GECOS field) for the primary user.";
};
};
config = {
# Reach fresher packages with `unstable.<name>` or pin with `stable.<name>`.
nixpkgs.overlays = [
(_final: prev: {
unstable = import inputs.nixpkgs-unstable (pinArgs prev);
stable = import inputs.nixpkgs-stable (pinArgs prev);
})
];
nixpkgs.config.allowUnfree = true;
# Flakes, so `nixos-rebuild switch` works from the console and a direnv
# `use flake` resolves inside a guest.
nix.settings.experimental-features = [
"nix-command"
"flakes"
];
# Primary user.
# The wheel group is the way in, since root is locked.
# No password is set here, since that is host-only.
# A guest therefore has none and is reached by SSH key or `machinectl`.
users.users.${user.name} = {
isNormalUser = true;
description = user.description;
extraGroups = [ "wheel" ];
};
# home-manager as a NixOS module: one build produces the system and user
# environment together, sharing the system's pkgs and installing user
# packages into the system profile.
home-manager = {
useGlobalPkgs = true;
useUserPackages = true;
extraSpecialArgs = {
inherit inputs;
my = inputs.self.lib;
};
users.${user.name} = {
home.username = user.name;
home.homeDirectory = "/home/${user.name}";
home.stateVersion = "26.05";
};
};
};
}