refactor(modules): move base modules under modules
This commit was merged in pull request #70.
This commit is contained in:
@@ -65,6 +65,7 @@ One flake builds every machine I own.
|
||||
|
||||
- Guest modules are host-agnostic wiring, while host files own deployment-specific service configuration such as served volumes and host paths.
|
||||
For example, `guests/copyparty.nix` should expose options and translate them into guest mounts/service settings, while `hosts/pikachu/default.nix` chooses `/inbox`, `/media`, and their backing paths.
|
||||
Guest addresses and routes are host-level configuration because a guest module can be used on multiple hosts.
|
||||
- `self.lib.registry.collect { hosts = self.nixosConfigurations; }` reads only top-level host `config.registry` declarations.
|
||||
Registry declarations inside guest interiors live under `config.containers.<guest>.config.registry`.
|
||||
The collected registry does not include those guest-interior declarations.
|
||||
|
||||
91
base.nix
91
base.nix
@@ -1,91 +0,0 @@
|
||||
{
|
||||
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"
|
||||
"storage"
|
||||
];
|
||||
};
|
||||
|
||||
# The shared write group.
|
||||
# Its gid is fixed, so a host and every guest carry the same number.
|
||||
# An identity-mapped container write then lands on the pool as this group, sparing every service the permission juggling.
|
||||
# 10000 clears the system-group ids assigned automatically and leaves headroom above the primary user, so nothing else claims it.
|
||||
users.groups.storage.gid = 10000;
|
||||
|
||||
# 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";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
32
guest.nix
32
guest.nix
@@ -1,32 +0,0 @@
|
||||
{
|
||||
my,
|
||||
inputs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
# The guest-base: the slim foundation every nested guest's interior stands on.
|
||||
# It imports the full modules tree so any module is available to enable inside a
|
||||
# guest, and stands on the same shared base a host does.
|
||||
{
|
||||
imports = my.collectNixFiles (inputs.self + "/modules") ++ [
|
||||
(inputs.self + "/base.nix")
|
||||
|
||||
# The modules tree reaches for these option namespaces, so they must be
|
||||
# declared for the tree to evaluate even where a guest leaves them off.
|
||||
my.registry.module
|
||||
inputs.sops-nix.nixosModules.sops
|
||||
inputs.stylix.nixosModules.stylix
|
||||
];
|
||||
|
||||
# A nested container has no per-host `default.nix` to pin its release.
|
||||
system.stateVersion = "26.05";
|
||||
|
||||
# The baseline toolset and SSH access, so any guest shelled into is a workable
|
||||
# environment without per-guest wiring.
|
||||
modules.toolkit.enable = lib.mkDefault true;
|
||||
modules.ssh.enable = lib.mkDefault true;
|
||||
|
||||
# A guest carries no host identity, so it presents a self-generated host key
|
||||
# rather than restoring one from secrets.
|
||||
modules.ssh.hostKeys.restore = lib.mkDefault false;
|
||||
}
|
||||
@@ -40,8 +40,6 @@
|
||||
modules.ssh.hostKeys.sopsFile = ../../secrets/neogaia.yaml;
|
||||
modules.ssh.userKey.sopsFile = ../../secrets/neogaia.yaml;
|
||||
|
||||
modules.toolkit.enable = true;
|
||||
|
||||
# The walking-skeleton guest, enabled like any module: proves the guest path
|
||||
# end to end through this host's `nix flake check`.
|
||||
# Modest caps keep the skeleton guest from starving the laptop.
|
||||
|
||||
@@ -79,8 +79,6 @@
|
||||
modules.ssh.userKey.sopsFile = ../../secrets/pikachu.yaml;
|
||||
|
||||
modules.git.enable = true;
|
||||
modules.toolkit.enable = true;
|
||||
|
||||
guests.actualbudget = {
|
||||
enable = true;
|
||||
vlan = 20;
|
||||
|
||||
@@ -32,6 +32,9 @@ let
|
||||
) (builtins.readDir dir)
|
||||
);
|
||||
|
||||
moduleFiles = collectNixFiles (self + "/modules");
|
||||
guestFiles = collectNixFiles (self + "/guests");
|
||||
|
||||
specialArgs = {
|
||||
inherit inputs self;
|
||||
my = self.lib;
|
||||
@@ -42,10 +45,13 @@ let
|
||||
|
||||
guests = import ./guests.nix {
|
||||
inherit
|
||||
inputs
|
||||
lib
|
||||
self
|
||||
specialArgs
|
||||
bridgeName
|
||||
moduleFiles
|
||||
registry
|
||||
;
|
||||
};
|
||||
|
||||
@@ -57,17 +63,19 @@ let
|
||||
inputs.nixpkgs.lib.nixosSystem {
|
||||
inherit system specialArgs;
|
||||
modules =
|
||||
(collectNixFiles (self + "/modules"))
|
||||
++ (collectNixFiles (self + "/guests"))
|
||||
moduleFiles
|
||||
++ guestFiles
|
||||
++ [
|
||||
inputs.chaotic.nixosModules.default
|
||||
inputs.disko.nixosModules.disko
|
||||
inputs.sops-nix.nixosModules.sops
|
||||
inputs.stylix.nixosModules.stylix
|
||||
registry.module
|
||||
(self + "/system.nix")
|
||||
(self + "/hosts/${hostName}")
|
||||
{ networking.hostName = hostName; }
|
||||
{
|
||||
networking.hostName = hostName;
|
||||
modules.host.enable = true;
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
{
|
||||
inputs,
|
||||
lib,
|
||||
self,
|
||||
specialArgs,
|
||||
bridgeName,
|
||||
moduleFiles,
|
||||
registry,
|
||||
}:
|
||||
let
|
||||
inherit (lib) escapeShellArg mapAttrsToList;
|
||||
@@ -345,11 +348,18 @@ let
|
||||
inherit specialArgs;
|
||||
|
||||
config = {
|
||||
imports = [
|
||||
(self + "/guest.nix")
|
||||
guestNet
|
||||
interior
|
||||
];
|
||||
imports =
|
||||
moduleFiles
|
||||
++ [
|
||||
registry.module
|
||||
inputs.sops-nix.nixosModules.sops
|
||||
inputs.stylix.nixosModules.stylix
|
||||
{
|
||||
modules.guest.enable = true;
|
||||
}
|
||||
guestNet
|
||||
interior
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
126
modules/default.nix
Normal file
126
modules/default.nix
Normal file
@@ -0,0 +1,126 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
inputs,
|
||||
...
|
||||
}:
|
||||
# The shared foundation for every host and guest.
|
||||
let
|
||||
inherit (lib) mkOption types;
|
||||
user = config.user;
|
||||
|
||||
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 = ''
|
||||
Primary interactive user for this system.
|
||||
This drives the system account and Home Manager user together.
|
||||
'';
|
||||
};
|
||||
description = mkOption {
|
||||
type = types.str;
|
||||
default = "Alexion";
|
||||
description = "Human-readable description for the primary user.";
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
nixpkgs.overlays = [
|
||||
(_final: prev: {
|
||||
unstable = import inputs.nixpkgs-unstable (pinArgs prev);
|
||||
stable = import inputs.nixpkgs-stable (pinArgs prev);
|
||||
})
|
||||
];
|
||||
nixpkgs.config.allowUnfree = true;
|
||||
|
||||
nix.settings.experimental-features = [
|
||||
"nix-command"
|
||||
"flakes"
|
||||
];
|
||||
|
||||
users.users.${user.name} = {
|
||||
isNormalUser = true;
|
||||
description = user.description;
|
||||
extraGroups = [
|
||||
"wheel"
|
||||
"storage"
|
||||
];
|
||||
};
|
||||
|
||||
users.groups.storage.gid = 10000;
|
||||
|
||||
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";
|
||||
home.packages = with pkgs; [
|
||||
eza
|
||||
wget
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
registry.shell-shortcut.${config.networking.hostName} = {
|
||||
ls = {
|
||||
command = "eza -alg --color=always --group-directories-first --icons=always";
|
||||
abbreviation = false;
|
||||
};
|
||||
la = {
|
||||
command = "eza -a --color=always --group-directories-first --icons=always";
|
||||
abbreviation = false;
|
||||
};
|
||||
ll = {
|
||||
command = "eza -lg --color=always --group-directories-first --icons=always";
|
||||
abbreviation = false;
|
||||
};
|
||||
lt = {
|
||||
command = "eza -aT -I '.git' --color=always --group-directories-first --icons=always";
|
||||
abbreviation = false;
|
||||
};
|
||||
"l." = {
|
||||
command = "eza -a | grep -e '^\\.'";
|
||||
abbreviation = false;
|
||||
};
|
||||
|
||||
".." = "cd ..";
|
||||
"..." = "cd ../..";
|
||||
"...." = "cd ../../..";
|
||||
|
||||
cp = "cp -v";
|
||||
tarnow = "tar -acf ";
|
||||
untar = "tar -zxvf ";
|
||||
wget = "wget -c ";
|
||||
grep = "grep --color=auto";
|
||||
fgrep = "fgrep --color=auto";
|
||||
egrep = "egrep --color=auto";
|
||||
jctl = "journalctl -p 3 -xb";
|
||||
|
||||
update = "sudo nixos-rebuild switch --flake .#${config.networking.hostName}";
|
||||
cleanup = "sudo nix-collect-garbage -d";
|
||||
};
|
||||
|
||||
modules.fish.enable = lib.mkDefault true;
|
||||
modules.fish.defaultShell = lib.mkDefault true;
|
||||
modules.tmux.enable = lib.mkDefault true;
|
||||
modules.nvim.enable = lib.mkDefault true;
|
||||
modules.git.enable = lib.mkDefault true;
|
||||
modules.direnv.enable = lib.mkDefault true;
|
||||
};
|
||||
}
|
||||
14
modules/guest.nix
Normal file
14
modules/guest.nix
Normal file
@@ -0,0 +1,14 @@
|
||||
{ config, lib, ... }:
|
||||
let
|
||||
cfg = config.modules.guest;
|
||||
in
|
||||
{
|
||||
options.modules.guest.enable = lib.mkEnableOption "nested-guest base configuration";
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
system.stateVersion = "26.05";
|
||||
|
||||
modules.ssh.enable = lib.mkDefault true;
|
||||
modules.ssh.hostKeys.restore = lib.mkDefault false;
|
||||
};
|
||||
}
|
||||
42
modules/host.nix
Normal file
42
modules/host.nix
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.modules.host;
|
||||
user = config.user;
|
||||
passwordSecret = "${user.name}-password";
|
||||
in
|
||||
{
|
||||
options.modules.host.enable = lib.mkEnableOption "physical-host base configuration";
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
nix.settings.extra-substituters = [ "https://nyx-cache.chaotic.cx/" ];
|
||||
nix.settings.extra-trusted-public-keys = [
|
||||
"nyx-cache.chaotic.cx:dJxTrgMC3V3cFfyIiBQDQorG6k1LsqurH/srpMSq7qk="
|
||||
];
|
||||
|
||||
nix.gc.automatic = true;
|
||||
nix.gc.dates = "Mon 03:15";
|
||||
nix.gc.options = "--delete-older-than 30d";
|
||||
|
||||
nix.optimise.automatic = true;
|
||||
nix.optimise.dates = [ "Thu 03:45" ];
|
||||
|
||||
boot.loader.systemd-boot.configurationLimit = 15;
|
||||
|
||||
services.xserver.xkb.layout = "us";
|
||||
services.xserver.xkb.options = "caps:escape_shifted_capslock";
|
||||
console.useXkbConfig = true;
|
||||
|
||||
sops.defaultSopsFile = ../secrets/shared.yaml;
|
||||
sops.age.keyFile = "/var/lib/sops-nix/key.txt";
|
||||
sops.age.sshKeyPaths = [ ];
|
||||
sops.gnupg.sshKeyPaths = [ ];
|
||||
|
||||
users.mutableUsers = false;
|
||||
sops.secrets.${passwordSecret}.neededForUsers = true;
|
||||
users.users.${user.name}.hashedPasswordFile = config.sops.secrets.${passwordSecret}.path;
|
||||
};
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
# The baseline interactive toolset as one deliberate bundle.
|
||||
let
|
||||
cfg = config.modules.toolkit;
|
||||
user = config.user.name;
|
||||
in
|
||||
{
|
||||
options.modules.toolkit.enable =
|
||||
lib.mkEnableOption "the baseline interactive environment — fish, tmux, nvim, git, and direnv — as one unit";
|
||||
|
||||
# Each piece is turned on at default priority, so a host can still override any one of them.
|
||||
config = lib.mkIf cfg.enable {
|
||||
home-manager.users.${user}.home.packages = with pkgs; [
|
||||
eza
|
||||
wget
|
||||
];
|
||||
|
||||
registry.shell-shortcut.${config.networking.hostName} = {
|
||||
ls = {
|
||||
command = "eza -alg --color=always --group-directories-first --icons=always";
|
||||
abbreviation = false;
|
||||
};
|
||||
la = {
|
||||
command = "eza -a --color=always --group-directories-first --icons=always";
|
||||
abbreviation = false;
|
||||
};
|
||||
ll = {
|
||||
command = "eza -lg --color=always --group-directories-first --icons=always";
|
||||
abbreviation = false;
|
||||
};
|
||||
lt = {
|
||||
command = "eza -aT -I '.git' --color=always --group-directories-first --icons=always";
|
||||
abbreviation = false;
|
||||
};
|
||||
"l." = {
|
||||
command = "eza -a | grep -e '^\\.'";
|
||||
abbreviation = false;
|
||||
};
|
||||
|
||||
# Walk up the tree.
|
||||
".." = "cd ..";
|
||||
"..." = "cd ../..";
|
||||
"...." = "cd ../../..";
|
||||
|
||||
cp = "cp -v";
|
||||
tarnow = "tar -acf ";
|
||||
untar = "tar -zxvf ";
|
||||
wget = "wget -c ";
|
||||
grep = "grep --color=auto";
|
||||
fgrep = "fgrep --color=auto";
|
||||
egrep = "egrep --color=auto";
|
||||
jctl = "journalctl -p 3 -xb";
|
||||
|
||||
# Rebuild the system, and reclaim disk from old generations.
|
||||
update = "sudo nixos-rebuild switch --flake .#${config.networking.hostName}";
|
||||
cleanup = "sudo nix-collect-garbage -d";
|
||||
};
|
||||
|
||||
modules.fish.enable = lib.mkDefault true;
|
||||
# fish is also the login shell, so the bundled environment is what a shell drops into.
|
||||
modules.fish.defaultShell = lib.mkDefault true;
|
||||
modules.tmux.enable = lib.mkDefault true;
|
||||
modules.nvim.enable = lib.mkDefault true;
|
||||
modules.git.enable = lib.mkDefault true;
|
||||
modules.direnv.enable = lib.mkDefault true;
|
||||
};
|
||||
}
|
||||
75
system.nix
75
system.nix
@@ -1,75 +0,0 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
# The host base: the host-only machinery a physical machine needs on top of the
|
||||
# shared base — bootloader, secret decryption, and the maintenance timers.
|
||||
let
|
||||
user = config.user;
|
||||
|
||||
passwordSecret = "${user.name}-password";
|
||||
in
|
||||
{
|
||||
imports = [ ./base.nix ];
|
||||
|
||||
# chaotic's binary cache, so the CachyOS kernel is fetched rather than compiled.
|
||||
# The `extra-` prefix keeps cache.nixos.org alongside it.
|
||||
nix.settings.extra-substituters = [ "https://nyx-cache.chaotic.cx/" ];
|
||||
nix.settings.extra-trusted-public-keys = [
|
||||
"nyx-cache.chaotic.cx:dJxTrgMC3V3cFfyIiBQDQorG6k1LsqurH/srpMSq7qk="
|
||||
];
|
||||
|
||||
# A month of generations is kept, because on a rolling channel with a
|
||||
# third-party kernel an old generation is a known-good system to boot when
|
||||
# an update breaks something.
|
||||
nix.gc.automatic = true;
|
||||
nix.gc.dates = "Mon 03:15";
|
||||
nix.gc.options = "--delete-older-than 30d";
|
||||
|
||||
# Deduplication runs on a timer, off the rebuild path, so it never adds
|
||||
# latency to a `nixos-rebuild switch`.
|
||||
# It falls on a different day from collection, so the two never contend.
|
||||
nix.optimise.automatic = true;
|
||||
nix.optimise.dates = [ "Thu 03:45" ];
|
||||
|
||||
# The EFI system partition holds a kernel and an initrd per entry at roughly
|
||||
# 70 MiB apiece, and is fixed in size.
|
||||
# An exhausted one fails at bootloader installation, after the build has
|
||||
# already succeeded.
|
||||
boot.loader.systemd-boot.configurationLimit = 15;
|
||||
|
||||
environment.systemPackages = [ pkgs.git ];
|
||||
|
||||
# Caps Lock is a second Escape.
|
||||
# Shift+Caps Lock still toggles Caps Lock.
|
||||
services.xserver.xkb.layout = "us";
|
||||
services.xserver.xkb.options = "caps:escape_shifted_capslock";
|
||||
|
||||
# Compile the console keymap from the layout above, so the remap holds on a
|
||||
# bare TTY and not only under a graphical session.
|
||||
console.useXkbConfig = true;
|
||||
|
||||
# Decryption machinery every host depends on.
|
||||
# The identity sits on the encrypted root, which is mounted early enough to
|
||||
# satisfy the secret below.
|
||||
# Clearing both `sshKeyPaths` defaults keeps the SSH host keys out of the
|
||||
# decryption path.
|
||||
sops.defaultSopsFile = ./secrets/shared.yaml;
|
||||
sops.age.keyFile = "/var/lib/sops-nix/key.txt";
|
||||
sops.age.sshKeyPaths = [ ];
|
||||
sops.gnupg.sshKeyPaths = [ ];
|
||||
|
||||
# A password set by hand on a running machine otherwise takes precedence.
|
||||
# That leaves the declared `hashedPasswordFile` below silently inert.
|
||||
# Root has no declared password and is therefore locked.
|
||||
# `sudo` from the wheel group is the way in.
|
||||
users.mutableUsers = false;
|
||||
|
||||
# Decrypted in an earlier activation stage than ordinary secrets.
|
||||
# That is early enough to precede the account that reads it.
|
||||
sops.secrets.${passwordSecret}.neededForUsers = true;
|
||||
|
||||
users.users.${user.name}.hashedPasswordFile = config.sops.secrets.${passwordSecret}.path;
|
||||
}
|
||||
Reference in New Issue
Block a user