refactor: centralize host and user metadata

This commit is contained in:
2026-04-21 12:12:43 +02:00
parent 5cfd4d01c8
commit 6332c96d3e
33 changed files with 805 additions and 479 deletions
-83
View File
@@ -1,83 +0,0 @@
# Repository Guidelines
## Project Structure & Module Organization
This repository is a simplified `flake-parts` NixOS flake. `flake.nix` imports `./modules` through `import-tree`, so normal `.nix` files under `modules/` are loaded automatically unless their file or directory name starts with `_`.
- `modules/flake-parts.nix` defines the `flake-parts` setup, formatter, and the exported `nixosConfigurations`.
- `modules/hosts/<name>/default.nix` defines one top-level `flake.modules.nixos.<host>` module and assembles that machine by importing reusable features, user modules, and host-local helpers.
- `modules/hosts/<name>/_*.nix` are private host-local helper modules such as hardware and disk layout files.
- `modules/users/<name>.nix` defines one reusable NixOS user module and the baseline Home Manager imports for that account.
- `modules/features/*.nix` contains reusable NixOS and Home Manager feature modules.
- `modules/features/<feature>/default.nix` is used when a feature needs private helper files, for example `niri/_bindings.nix`.
- `modules/features/services/*.nix` contains reusable service-oriented NixOS modules.
- `modules/secrets/sops.nix` wires `sops-nix` for both NixOS and Home Manager.
- `modules/secrets/secrets.yaml` stores encrypted secrets, with `.sops.yaml` defining SOPS creation rules.
- `modules/_treefmt.nix` configures repository formatting.
Keep host files thin. Shared behavior belongs in `modules/features/` or `modules/users/`. Host files should mainly compose imports and hold host-only settings such as monitor layouts, hardware quirks, boot tweaks, and machine-local firewall or service choices.
## Mental Model
This repo is direct module composition around `flake.modules`, not the old inventory-driven dendritic design.
- Reusable building blocks are exposed as `flake.modules.nixos.<name>` and `flake.modules.homeManager.<name>`.
- Host modules are the composition root. They import the reusable NixOS modules they need, enable Home Manager, and add any host-specific Home Manager imports inline.
- User modules define the Unix user plus that accounts baseline Home Manager setup.
- There is no `config.repo`, inventory schema, profiles layer, or attachment builder anymore.
In practice:
- Prefer importing a feature module directly over inventing a repo-local option just to toggle it.
- Put cross-host reusable behavior in `modules/features/`.
- Put account-specific defaults in `modules/users/`.
- Keep private helper files `_`-prefixed so `import-tree` does not expose them as top-level modules.
- Match the existing split between NixOS composition in host modules and Home Manager composition in user or host modules.
## Current Host Composition
There are three exported hosts:
- `orion`: server-oriented host with `kiri`, SOPS, and service modules such as Caddy, Gitea, Vaultwarden, Radicale, Actual, and OpenSSH.
- `polaris`: graphical desktop host with `kiri` and `ergon`, hardware imports, Niri, Steam, local desktop features, and host-specific monitor layout.
- `zenith`: graphical laptop host with `kiri` and `ergon`, Niri, laptop hardware support, firmware updates, and host-specific monitor layout.
When adjusting user-facing software, check whether it belongs in:
- a user baseline in `modules/users/<name>.nix`
- a reusable Home Manager feature in `modules/features/*.nix`
- a host-local extension inside `modules/hosts/<name>/default.nix`
Be careful not to move host-specific Home Manager imports into a user baseline unless that behavior should apply on every host that imports that user module.
## Validation And Development Commands
Run commands from the repository root.
- `nix build --no-link --show-trace .#nixosConfigurations.<host>.config.system.build.toplevel`: baseline validation for one host.
- `nix build --no-link --show-trace .#nixosConfigurations.orion.config.system.build.toplevel .#nixosConfigurations.polaris.config.system.build.toplevel .#nixosConfigurations.zenith.config.system.build.toplevel`: validate all defined hosts in one invocation.
- `nixos-rebuild build --flake .#<host>`: use when you specifically want `nixos-rebuild` semantics without activation.
- `nix eval --json .#nixosConfigurations.<host>.config.<option>`: inspect a single evaluated option while iterating.
- `nix fmt`: format the repository using the flake-provided formatter from `modules/_treefmt.nix`.
- `nix store diff-closures <old> <new>`: compare built system closures when reviewing refactors for parity or regressions.
This repo does not define a first-party `checks` output. Validation is primarily host builds plus targeted `nix eval` checks.
## Coding Style & Naming Conventions
Use two-space indentation and standard Nix attrset formatting. Prefer small `let` bindings, lowerCamelCase local names, and lowercase file names.
- Define reusable modules as `flake.modules.nixos.<name>` or `flake.modules.homeManager.<name>`.
- Keep one obvious feature per file or directory.
- Use `default.nix` only when the feature needs private helper files alongside it.
- Match surrounding style instead of reformatting unrelated code.
- Prefer explicit host imports over hidden indirection.
## Commit
Follow the existing history style: short imperative subjects, optionally with a conventional prefix, for example `refactor: simplify host composition`.
## Security & Configuration Tips
Never commit plaintext secrets. Add or update secrets through `modules/secrets/secrets.yaml` and reference them via `config.sops.secrets.<name>.path`.
Be explicit and cautious with changes to:
- firewall and OpenSSH settings
- disk layout and boot configuration
- SOPS key handling and admin user access
- firmware, hardware, and authentication settings
- host-vs-user module boundaries, because it is easy to accidentally broaden behavior to the wrong machines
+11 -1
View File
@@ -1,12 +1,22 @@
{ ... }: { ... }:
{ {
flake.modules.homeManager.bitwarden = flake.modules.homeManager.bitwarden =
{ pkgs, ... }: {
config,
lib,
pkgs,
...
}:
let
user = config.meta.user;
primaryEmail = builtins.head (lib.filter (email: email.primary) (builtins.attrValues user.emails));
in
{ {
programs.rbw = { programs.rbw = {
enable = true; enable = true;
settings = { settings = {
base_url = "https://vault.jelles.net"; base_url = "https://vault.jelles.net";
email = primaryEmail.address;
pinentry = pkgs.pinentry-gnome3; pinentry = pkgs.pinentry-gnome3;
}; };
}; };
+30 -15
View File
@@ -1,15 +1,29 @@
{ inputs, config, ... }: { config, ... }:
let let
nixosModules = config.flake.modules.nixos; nixosModules = config.flake.modules.nixos;
in in
{ {
flake.modules.nixos.desktopBase = { flake.modules.nixos."core-base" = {
imports = [ imports = [
inputs.home-manager.nixosModules.home-manager nixosModules."meta-host"
nixosModules."home-manager-base"
nixosModules.nix nixosModules.nix
nixosModules.systemBase nixosModules."region-nl"
nixosModules.standardBoot nixosModules."sops-host"
nixosModules.regionNl ];
};
flake.modules.nixos."server-base" = {
imports = [
nixosModules."core-base"
nixosModules.openssh
];
};
flake.modules.nixos."workstation-base" = {
imports = [
nixosModules."core-base"
nixosModules."standard-boot"
nixosModules.sddm nixosModules.sddm
nixosModules.niri nixosModules.niri
nixosModules.audio nixosModules.audio
@@ -18,18 +32,19 @@ in
nixosModules.fonts nixosModules.fonts
nixosModules.networking nixosModules.networking
nixosModules.printing nixosModules.printing
nixosModules.qbittorrentClient nixosModules."qbittorrent-client"
nixosModules.sopsHost
]; ];
home-manager = { users.mutableUsers = false;
useGlobalPkgs = true;
backupFileExtension = "bak"; services.dbus.implementation = "broker";
extraSpecialArgs = { inherit inputs; };
programs.nix-ld.enable = true;
environment.localBinInPath = true;
}; };
security.sudo.extraConfig = '' flake.modules.nixos."portable-host" = {
Defaults env_keep+=SSH_AUTH_SOCK hardware.enableRedistributableFirmware = true;
''; services.fwupd.enable = true;
}; };
} }
+1 -1
View File
@@ -1,5 +1,5 @@
{ {
flake.modules.homeManager.devTools = flake.modules.homeManager."dev-tools" =
{ config, ... }: { config, ... }:
{ {
home.sessionVariables.CARGO_HOME = "${config.xdg.dataHome}/cargo"; home.sessionVariables.CARGO_HOME = "${config.xdg.dataHome}/cargo";
+21 -34
View File
@@ -1,17 +1,23 @@
{ ... }: { ... }:
{ {
flake.modules.homeManager.email = flake.modules.homeManager.email =
{ config, ... }: {
config,
lib,
...
}:
let let
realName = "Jelle Spreeuwenberg"; user = config.meta.user;
mkOffice365Account = mkOffice365Account =
{ {
address, address,
primary, primary,
...
}: }:
{ {
enable = true; enable = true;
inherit address primary realName; inherit address primary;
realName = user.realName;
userName = address; userName = address;
thunderbird = { thunderbird = {
enable = true; enable = true;
@@ -26,10 +32,12 @@
{ {
address, address,
primary, primary,
...
}: }:
{ {
enable = true; enable = true;
inherit address primary realName; inherit address primary;
realName = user.realName;
userName = address; userName = address;
thunderbird.enable = true; thunderbird.enable = true;
imap = { imap = {
@@ -45,6 +53,14 @@
tls.enable = true; tls.enable = true;
}; };
}; };
mkEmailAccount =
email:
if email.type == "office365" then
mkOffice365Account email
else if email.type == "mxrouting" then
mkMxrouteAccount email
else
throw "Unsupported email type `${email.type}` for ${config.home.username}";
in in
{ {
programs.thunderbird = { programs.thunderbird = {
@@ -65,35 +81,6 @@
}; };
}; };
accounts.email.accounts = accounts.email.accounts = lib.mapAttrs (_: mkEmailAccount) user.emails;
if config.home.username == "ergon" then
{
work = mkOffice365Account {
address = "jelle.spreeuwenberg@yookr.org";
primary = true;
};
}
else
{
main = mkMxrouteAccount {
address = "mail@jelles.net";
primary = true;
};
old = mkMxrouteAccount {
address = "mail@jellespreeuwenberg.nl";
primary = false;
};
uni = mkOffice365Account {
address = "j.spreeuwenberg@student.tue.nl";
primary = false;
};
work = mkOffice365Account {
address = "jelle.spreeuwenberg@yookr.org";
primary = false;
};
};
}; };
} }
+9
View File
@@ -0,0 +1,9 @@
{ config, ... }:
let
homeModules = config.flake.modules.homeManager;
in
{
flake.modules.homeManager."ergon-workstation" = {
imports = [ homeModules.nix ];
};
}
+13 -1
View File
@@ -1,7 +1,15 @@
{ ... }: { ... }:
{ {
flake.modules.homeManager.git = flake.modules.homeManager.git =
{ ... }: {
config,
lib,
...
}:
let
user = config.meta.user;
primaryEmail = builtins.head (lib.filter (email: email.primary) (builtins.attrValues user.emails));
in
{ {
programs.git = { programs.git = {
enable = true; enable = true;
@@ -12,6 +20,10 @@
]; ];
settings = { settings = {
init.defaultBranch = "main"; init.defaultBranch = "main";
user = {
name = user.realName;
email = primaryEmail.address;
};
}; };
}; };
}; };
+26
View File
@@ -0,0 +1,26 @@
{
inputs,
config,
...
}:
let
homeModules = config.flake.modules.homeManager;
in
{
flake.modules.nixos."home-manager-base" =
{ ... }:
{
imports = [ inputs.home-manager.nixosModules.home-manager ];
home-manager = {
useGlobalPkgs = true;
backupFileExtension = "bak";
extraSpecialArgs = { inherit inputs; };
sharedModules = [ homeModules."meta-context" ];
};
security.sudo.extraConfig = ''
Defaults env_keep+=SSH_AUTH_SOCK
'';
};
}
+23
View File
@@ -0,0 +1,23 @@
{ config, ... }:
let
homeModules = config.flake.modules.homeManager;
in
{
flake.modules.homeManager."kiri-workstation" = {
imports = [
homeModules.nix
homeModules.bitwarden
homeModules.email
homeModules.pim
homeModules.mpv
homeModules.niri
homeModules.clipboard
homeModules."local-apps"
homeModules."qbittorrent-client"
homeModules.vicinae
homeModules.xdg
homeModules.theme
homeModules.noctalia
];
};
}
+1 -1
View File
@@ -1,5 +1,5 @@
{ {
flake.modules.homeManager.localApps = flake.modules.homeManager."local-apps" =
{ pkgs, ... }: { pkgs, ... }:
{ {
home.sessionVariables.BROWSER = "vivaldi"; home.sessionVariables.BROWSER = "vivaldi";
+147
View File
@@ -0,0 +1,147 @@
{ lib, ... }:
let
emailType = lib.types.submodule (
{ ... }:
{
options = {
address = lib.mkOption {
type = lib.types.str;
};
primary = lib.mkOption {
type = lib.types.bool;
};
type = lib.mkOption {
type = lib.types.str;
};
};
}
);
userType = lib.types.submodule (
{ ... }:
{
options = {
name = lib.mkOption {
type = lib.types.str;
};
realName = lib.mkOption {
type = lib.types.str;
};
homeDirectory = lib.mkOption {
type = lib.types.str;
};
emails = lib.mkOption {
type = lib.types.attrsOf emailType;
};
};
}
);
displayModeType = lib.types.submodule (
{ ... }:
{
options = {
width = lib.mkOption {
type = lib.types.int;
};
height = lib.mkOption {
type = lib.types.int;
};
refresh = lib.mkOption {
type = lib.types.float;
};
};
}
);
displayType = lib.types.submodule (
{ ... }:
{
options = {
primary = lib.mkOption {
type = lib.types.bool;
default = false;
};
x = lib.mkOption {
type = lib.types.int;
};
y = lib.mkOption {
type = lib.types.int;
};
scale = lib.mkOption {
type = lib.types.nullOr lib.types.float;
default = null;
};
mode = lib.mkOption {
type = lib.types.nullOr displayModeType;
default = null;
};
};
}
);
hostType = lib.types.submodule (
{ ... }:
{
options = {
name = lib.mkOption {
type = lib.types.str;
};
kind = lib.mkOption {
type = lib.types.enum [
"server"
"workstation"
];
};
traits = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
};
displays = lib.mkOption {
type = lib.types.attrsOf displayType;
default = { };
};
users = lib.mkOption {
type = lib.types.attrsOf userType;
default = { };
};
};
}
);
in
{
flake.modules.nixos."meta-host" = {
options.meta.host = lib.mkOption {
type = hostType;
};
};
flake.modules.homeManager."meta-context" = {
options.meta = {
host = lib.mkOption {
type = lib.types.nullOr hostType;
default = null;
};
user = lib.mkOption {
type = lib.types.nullOr userType;
default = null;
};
};
};
}
+2 -4
View File
@@ -2,10 +2,8 @@
flake.modules.homeManager.neovim = flake.modules.homeManager.neovim =
{ {
pkgs, pkgs,
lib,
config, config,
inputs, inputs,
osConfig,
... ...
}: }:
{ {
@@ -119,8 +117,8 @@
# Hostname/ConfigDir needed for nixd # Hostname/ConfigDir needed for nixd
nixdExtras = { nixdExtras = {
nixpkgs = "import ${pkgs.path} {}"; nixpkgs = "import ${pkgs.path} {}";
nixos_options = ''(builtins.getFlake "path://${config.home.homeDirectory}/.config/nixos").nixosConfigurations.${osConfig.networking.hostName}.options''; nixos_options = ''(builtins.getFlake "path://${config.home.homeDirectory}/.config/nixos").nixosConfigurations.${config.meta.host.name}.options'';
home_manager_options = ''(builtins.getFlake "path://${config.home.homeDirectory}/.config/nixos").nixosConfigurations.${osConfig.networking.hostName}.options.home-manager.users.type.getSubOptions []''; home_manager_options = ''(builtins.getFlake "path://${config.home.homeDirectory}/.config/nixos").nixosConfigurations.${config.meta.host.name}.options.home-manager.users.type.getSubOptions []'';
}; };
# TODO: Put in separate theme file # TODO: Put in separate theme file
+8
View File
@@ -1,4 +1,12 @@
{ {
flake.modules.nixos."server-firewall" = {
networking = {
firewall.enable = true;
firewall.allowPing = false;
nftables.enable = true;
};
};
flake.modules.nixos.networking = { flake.modules.nixos.networking = {
networking = { networking = {
nftables.enable = true; nftables.enable = true;
+27 -1
View File
@@ -16,7 +16,32 @@
}; };
flake.modules.homeManager.niri = flake.modules.homeManager.niri =
{ config, pkgs, ... }: {
config,
lib,
pkgs,
...
}:
let
outputs = lib.mapAttrs (
_: display:
{
position = {
x = display.x;
y = display.y;
};
}
// lib.optionalAttrs (display.primary or false) {
"focus-at-startup" = true;
}
// lib.optionalAttrs (display ? scale) {
inherit (display) scale;
}
// lib.optionalAttrs (display ? mode) {
inherit (display) mode;
}
) config.meta.host.displays;
in
{ {
home.sessionVariables.NIXOS_OZONE_WL = "1"; home.sessionVariables.NIXOS_OZONE_WL = "1";
@@ -34,6 +59,7 @@
]; ];
programs.niri.settings = { programs.niri.settings = {
inherit outputs;
environment.DISPLAY = ":0"; environment.DISPLAY = ":0";
spawn-at-startup = [ spawn-at-startup = [
{ command = [ "xwayland-satellite" ]; } { command = [ "xwayland-satellite" ]; }
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
flake.modules.nixos.qbittorrentClient = { flake.modules.nixos."qbittorrent-client" = {
networking.firewall = { networking.firewall = {
allowedTCPPorts = [ 43864 ]; allowedTCPPorts = [ 43864 ];
allowedUDPPorts = [ 43864 ]; allowedUDPPorts = [ 43864 ];
}; };
}; };
flake.modules.homeManager.qbittorrentClient = flake.modules.homeManager."qbittorrent-client" =
{ pkgs, ... }: { pkgs, ... }:
{ {
home.packages = [ pkgs.qbittorrent ]; home.packages = [ pkgs.qbittorrent ];
+1 -1
View File
@@ -1,6 +1,6 @@
{ ... }: { ... }:
{ {
flake.modules.nixos.regionNl = { flake.modules.nixos."region-nl" = {
time.timeZone = "Europe/Amsterdam"; time.timeZone = "Europe/Amsterdam";
i18n.defaultLocale = "en_US.UTF-8"; i18n.defaultLocale = "en_US.UTF-8";
+12 -4
View File
@@ -1,6 +1,11 @@
{ config, ... }:
let
metaLib = config.meta.lib;
in
{ {
flake.modules.nixos.actual = flake.modules.nixos.actual =
{ config, ... }: { lib, ... }:
lib.mkMerge [
{ {
services.actual = { services.actual = {
enable = true; enable = true;
@@ -10,8 +15,11 @@
hostname = "127.0.0.1"; hostname = "127.0.0.1";
}; };
}; };
}
services.caddy.virtualHosts."finance.jelles.net".extraConfig = (metaLib.mkCaddyReverseProxy {
"reverse_proxy :${toString config.services.actual.settings.port}"; domain = "finance.jelles.net";
}; port = 3000;
})
];
} }
+2 -2
View File
@@ -1,5 +1,5 @@
{ {
flake.modules.nixos.delugeService = flake.modules.nixos."deluge-service" =
{ ... }: { ... }:
{ {
sops.secrets.deluge-auth-file = { }; sops.secrets.deluge-auth-file = { };
@@ -10,7 +10,7 @@
}; };
}; };
flake.modules.homeManager.delugeClient = flake.modules.homeManager."deluge-client" =
{ pkgs, ... }: { pkgs, ... }:
{ {
home.packages = [ pkgs.deluge ]; home.packages = [ pkgs.deluge ];
+14 -4
View File
@@ -1,6 +1,11 @@
{ config, ... }:
let
metaLib = config.meta.lib;
in
{ {
flake.modules.nixos.gitea = flake.modules.nixos.gitea =
{ config, ... }: { lib, ... }:
lib.mkMerge [
{ {
services.gitea = { services.gitea = {
enable = true; enable = true;
@@ -19,10 +24,15 @@
service.DISABLE_REGISTRATION = true; service.DISABLE_REGISTRATION = true;
}; };
}; };
}
{
services.openssh.settings.AllowUsers = [ "gitea" ]; services.openssh.settings.AllowUsers = [ "gitea" ];
}
services.caddy.virtualHosts."git.jelles.net".extraConfig = (metaLib.mkCaddyReverseProxy {
"reverse_proxy :${toString config.services.gitea.settings.server.HTTP_PORT}"; domain = "git.jelles.net";
}; port = 3001;
})
];
} }
+9 -6
View File
@@ -1,17 +1,20 @@
{ ... }: { ... }:
{ {
flake.modules.nixos."ssh-agent-auth" = {
security.pam = {
sshAgentAuth.enable = true;
services.sudo.sshAgentAuth = true;
};
};
flake.modules.nixos.openssh = flake.modules.nixos.openssh =
{ {
config, config,
hostType ? "desktop",
lib,
... ...
}: }:
let let
isServer = hostType == "server"; isServer = config.meta.host.kind == "server";
hostUserNames = builtins.attrNames ( hostUserNames = builtins.attrNames config.meta.host.users;
lib.filterAttrs (_: user: user.isNormalUser or false) config.users.users
);
in in
{ {
services.openssh = { services.openssh = {
+26 -8
View File
@@ -1,6 +1,11 @@
{ config, ... }:
let
metaLib = config.meta.lib;
in
{ {
flake.modules.nixos.radicale = flake.modules.nixos.radicale =
{ ... }: { lib, ... }:
lib.mkMerge [
{ {
services.radicale = { services.radicale = {
enable = true; enable = true;
@@ -16,12 +21,25 @@
storage.filesystem_folder = "/var/lib/radicale/collections"; storage.filesystem_folder = "/var/lib/radicale/collections";
}; };
}; };
}
services.caddy.virtualHosts."radicale.jelles.net".extraConfig = '' (metaLib.mkCaddyReverseProxy {
reverse_proxy :5232 { domain = "radicale.jelles.net";
header_up X-Script-Name / port = 5232;
header_up X-Forwarded-For {remote} extraHeaders = [
header_up X-Remote-User {http.auth.user.id} {
}''; name = "X-Script-Name";
}; value = "/";
}
{
name = "X-Forwarded-For";
value = "{remote}";
}
{
name = "X-Remote-User";
value = "{http.auth.user.id}";
}
];
})
];
} }
+12 -4
View File
@@ -1,6 +1,11 @@
{ config, ... }:
let
metaLib = config.meta.lib;
in
{ {
flake.modules.nixos.vaultwarden = flake.modules.nixos.vaultwarden =
{ config, ... }: { lib, ... }:
lib.mkMerge [
{ {
services.vaultwarden = { services.vaultwarden = {
enable = true; enable = true;
@@ -12,8 +17,11 @@
ROCKET_LOG = "critical"; ROCKET_LOG = "critical";
}; };
}; };
}
services.caddy.virtualHosts."vault.jelles.net".extraConfig = (metaLib.mkCaddyReverseProxy {
"reverse_proxy :${toString config.services.vaultwarden.config.ROCKET_PORT}"; domain = "vault.jelles.net";
}; port = 8100;
})
];
} }
+5 -1
View File
@@ -1,4 +1,5 @@
{ {
flake.modules.homeManager.shell = flake.modules.homeManager.shell =
{ lib, config, ... }: { lib, config, ... }:
{ {
@@ -145,7 +146,10 @@
}; };
}; };
programs.eza.enable = true; programs.eza = {
enable = true;
extraOptions = [ "--group-directories-first" ];
};
programs.fzf = { programs.fzf = {
enable = true; enable = true;
enableZshIntegration = true; enableZshIntegration = true;
+1 -1
View File
@@ -1,5 +1,5 @@
{ {
flake.modules.homeManager.sshClient = flake.modules.homeManager."ssh-client" =
{ config, ... }: { config, ... }:
{ {
programs.ssh = { programs.ssh = {
+17 -2
View File
@@ -1,7 +1,7 @@
{ ... }: { ... }:
{ {
flake.modules.nixos.standardBoot = flake.modules.nixos."standard-boot" =
{ pkgs, ... }: { config, pkgs, ... }:
{ {
boot = { boot = {
loader = { loader = {
@@ -10,6 +10,21 @@
enable = true; enable = true;
consoleMode = "auto"; consoleMode = "auto";
configurationLimit = 5; configurationLimit = 5;
extraInstallCommands = ''
ENTRIES="${config.boot.loader.efi.efiSysMountPoint}/loader/entries"
PROFILES="/nix/var/nix/profiles"
for file in "$ENTRIES"/nixos-generation-*.conf; do
generation=$(${pkgs.coreutils}/bin/basename "$file" | ${pkgs.gnugrep}/bin/grep -o -E '[0-9]+')
timestamp=$(${pkgs.coreutils}/bin/stat -c %y "$PROFILES/system-$generation-link" 2>/dev/null | ${pkgs.coreutils}/bin/cut -d. -f1)
if [ -z "$timestamp" ]; then
timestamp="Unknown Date"
fi
${pkgs.gnused}/bin/sed -i "s/^version .*/version Generation $generation - $timestamp/" "$file"
done
'';
}; };
}; };
-10
View File
@@ -1,10 +0,0 @@
{
flake.modules.nixos.systemBase = {
users.mutableUsers = false;
services.dbus.implementation = "broker";
programs.nix-ld.enable = true;
environment.localBinInPath = true;
};
}
+11 -11
View File
@@ -3,17 +3,17 @@ let
homeModules = config.flake.modules.homeManager; homeModules = config.flake.modules.homeManager;
in in
{ {
flake.modules.homeManager.userBase = { flake.modules.homeManager."user-base" = {
imports = with homeModules; [ imports = [
terminal homeModules.terminal
shell homeModules.shell
neovim homeModules.neovim
sshClient homeModules."ssh-client"
sopsAdmin homeModules."sops-admin"
git homeModules.git
devTools homeModules."dev-tools"
podman homeModules.podman
gemini homeModules.gemini
]; ];
}; };
} }
+23 -44
View File
@@ -1,45 +1,12 @@
{ { config, ... }:
inputs,
config,
...
}:
let let
nixosModules = config.flake.modules.nixos; nixosModules = config.flake.modules.nixos;
metaLib = config.meta.lib;
in in
{ {
flake.modules.nixos.orion = flake.modules.nixos."orion-admin" =
{ pkgs, ... }: { pkgs, ... }:
{ {
_module.args.hostType = "server";
imports = [
inputs.home-manager.nixosModules.home-manager
nixosModules.sopsHost
nixosModules.caddy
nixosModules.openssh
nixosModules.vaultwarden
nixosModules.radicale
nixosModules.actual
nixosModules.gitea
nixosModules.kiri
./_hardware.nix
./_disk.nix
];
system.stateVersion = "24.05";
home-manager = {
useGlobalPkgs = true;
backupFileExtension = "bak";
extraSpecialArgs = { inherit inputs; };
};
networking.hostName = "orion";
security.sudo.extraConfig = ''
Defaults env_keep+=SSH_AUTH_SOCK
'';
users.users.kiri = { users.users.kiri = {
linger = true; linger = true;
openssh.authorizedKeys.keys = [ openssh.authorizedKeys.keys = [
@@ -50,16 +17,28 @@ in
environment.systemPackages = [ environment.systemPackages = [
pkgs.kitty pkgs.kitty
]; ];
networking = {
firewall.enable = true;
firewall.allowPing = false;
nftables.enable = true;
}; };
security.pam = { flake.modules.nixos.orion = metaLib.mkHost {
sshAgentAuth.enable = true; name = "orion";
services.sudo.sshAgentAuth = true; kind = "server";
users = {
inherit (metaLib.users) kiri;
}; };
imports = [
nixosModules."server-base"
nixosModules.caddy
nixosModules."server-firewall"
nixosModules."ssh-agent-auth"
nixosModules."orion-admin"
nixosModules.vaultwarden
nixosModules.radicale
nixosModules.actual
nixosModules.gitea
nixosModules."user-kiri"
./_hardware.nix
./_disk.nix
];
}; };
} }
+27 -65
View File
@@ -5,23 +5,38 @@
}: }:
let let
nixosModules = config.flake.modules.nixos; nixosModules = config.flake.modules.nixos;
homeModules = config.flake.modules.homeManager; metaLib = config.meta.lib;
in in
{ {
flake.modules.nixos.polaris = flake.modules.nixos.polaris = metaLib.mkHost {
{ name = "polaris";
config, kind = "workstation";
pkgs,
... displays = {
}: "LG Electronics LG ULTRAGEAR 103NTYT8R290" = {
{ primary = true;
_module.args.hostType = "desktop"; x = 0;
y = 0;
};
"LG Electronics LG ULTRAGEAR 103NTJJ8R332" = {
x = 2560;
y = 0;
};
};
users = {
inherit (metaLib.users)
ergon
kiri
;
};
imports = [ imports = [
nixosModules.desktopBase nixosModules."workstation-base"
nixosModules.steam nixosModules.steam
nixosModules.kiri nixosModules."user-kiri"
nixosModules.ergon nixosModules."user-ergon"
./_hardware.nix ./_hardware.nix
] ]
++ (with inputs.nixos-hardware.nixosModules; [ ++ (with inputs.nixos-hardware.nixosModules; [
@@ -30,58 +45,5 @@ in
common-cpu-amd common-cpu-amd
common-gpu-amd common-gpu-amd
]); ]);
system.stateVersion = "24.05";
networking.hostName = "polaris";
home-manager.users.kiri.imports = with homeModules; [
nix
bitwarden
email
pim
mpv
niri
clipboard
localApps
qbittorrentClient
vicinae
xdg
theme
noctalia
];
home-manager.users.kiri.programs.niri.settings.outputs = {
"LG Electronics LG ULTRAGEAR 103NTYT8R290" = {
"focus-at-startup" = true;
position = {
x = 0;
y = 0;
};
};
"LG Electronics LG ULTRAGEAR 103NTJJ8R332" = {
position = {
x = 2560;
y = 0;
};
};
};
boot.loader.systemd-boot.extraInstallCommands = ''
ENTRIES="${config.boot.loader.efi.efiSysMountPoint}/loader/entries"
PROFILES="/nix/var/nix/profiles"
for file in "$ENTRIES"/nixos-generation-*.conf; do
generation=$(${pkgs.coreutils}/bin/basename "$file" | ${pkgs.gnugrep}/bin/grep -o -E '[0-9]+')
timestamp=$(${pkgs.coreutils}/bin/stat -c %y "$PROFILES/system-$generation-link" 2>/dev/null | ${pkgs.coreutils}/bin/cut -d. -f1)
if [ -z "$timestamp" ]; then
timestamp="Unknown Date"
fi
${pkgs.gnused}/bin/sed -i "s/^version .*/version Generation $generation - $timestamp/" "$file"
done
'';
}; };
} }
+22 -39
View File
@@ -5,49 +5,19 @@
}: }:
let let
nixosModules = config.flake.modules.nixos; nixosModules = config.flake.modules.nixos;
homeModules = config.flake.modules.homeManager; metaLib = config.meta.lib;
in in
{ {
flake.modules.nixos.zenith = flake.modules.nixos.zenith = metaLib.mkHost {
{ ... }: name = "zenith";
{ kind = "workstation";
_module.args.hostType = "laptop"; traits = [ "portable" ];
imports = [ displays = {
nixosModules.desktopBase
nixosModules.kiri
nixosModules.ergon
./_hardware.nix
inputs.nixos-hardware.nixosModules.lenovo-yoga-7-14ARH7-amdgpu
];
system.stateVersion = "24.05";
networking.hostName = "zenith";
home-manager.users.kiri.imports = with homeModules; [
nix
bitwarden
email
pim
mpv
niri
clipboard
localApps
qbittorrentClient
vicinae
xdg
theme
noctalia
];
home-manager.users.kiri.programs.niri.settings.outputs = {
"California Institute of Technology 0x1410 Unknown" = { "California Institute of Technology 0x1410 Unknown" = {
"focus-at-startup" = true; primary = true;
position = {
x = 0; x = 0;
y = 0; y = 0;
};
scale = 1.5; scale = 1.5;
mode = { mode = {
width = 3072; width = 3072;
@@ -57,7 +27,20 @@ in
}; };
}; };
hardware.enableRedistributableFirmware = true; users = {
services.fwupd.enable = true; inherit (metaLib.users)
ergon
kiri
;
};
imports = [
nixosModules."workstation-base"
nixosModules."portable-host"
nixosModules."user-kiri"
nixosModules."user-ergon"
./_hardware.nix
inputs.nixos-hardware.nixosModules.lenovo-yoga-7-14ARH7-amdgpu
];
}; };
} }
+89
View File
@@ -0,0 +1,89 @@
{
config,
lib,
...
}:
let
mkHost =
{
name,
kind,
traits ? [ ],
displays ? { },
users ? { },
imports ? [ ],
stateVersion ? "24.05",
}:
{
meta.host = {
inherit
displays
kind
name
traits
users
;
};
inherit imports;
networking.hostName = name;
system.stateVersion = stateVersion;
};
mkCaddyReverseProxy =
{
domain,
port,
extraHeaders ? [ ],
extraConfigText ? "",
}:
let
headerLines = map (header: " header_up ${header.name} ${header.value}") extraHeaders;
extraConfigLines = map (line: " ${line}") (
lib.filter (line: line != "") (lib.splitString "\n" extraConfigText)
);
bodyLines = headerLines ++ extraConfigLines;
body = lib.concatStringsSep "\n" bodyLines;
in
{
services.caddy.virtualHosts.${domain}.extraConfig =
if body == "" then
"reverse_proxy :${toString port}"
else
''
reverse_proxy :${toString port} {
${body}
}
'';
};
in
{
options.meta.lib.mkHost = lib.mkOption {
type = lib.types.raw;
description = "Internal host constructor shared between flake-parts modules.";
internal = true;
readOnly = true;
};
options.meta.lib.mkCaddyReverseProxy = lib.mkOption {
type = lib.types.raw;
description = "Internal Caddy reverse proxy helper shared between flake-parts modules.";
internal = true;
readOnly = true;
};
options.meta.lib.users = lib.mkOption {
type = lib.types.attrs;
description = "Canonical user attrsets shared by host definitions.";
internal = true;
readOnly = true;
};
config.meta.lib = {
inherit
mkCaddyReverseProxy
mkHost
;
};
}
+5 -5
View File
@@ -6,15 +6,15 @@ let
sopsAdminKeyPath = "/var/lib/sops/keys.txt"; sopsAdminKeyPath = "/var/lib/sops/keys.txt";
in in
{ {
flake.modules.nixos.sopsHost = flake.modules.nixos."sops-host" =
{ {
hostType ? "desktop", config,
lib, lib,
... ...
}: }:
let let
useHostSshKey = hostType == "server"; useHostSshKey = config.meta.host.kind == "server";
useAdminKeyFile = hostType != "server"; useAdminKeyFile = config.meta.host.kind != "server";
adminKeyDir = builtins.dirOf sopsAdminKeyPath; adminKeyDir = builtins.dirOf sopsAdminKeyPath;
in in
{ {
@@ -37,7 +37,7 @@ in
]; ];
}; };
flake.modules.homeManager.sopsAdmin = flake.modules.homeManager."sops-admin" =
{ {
pkgs, pkgs,
... ...
+111 -32
View File
@@ -1,47 +1,90 @@
{ config, ... }: {
config,
lib,
...
}:
let let
homeModules = config.flake.modules.homeManager; homeModules = config.flake.modules.homeManager;
realName = "Jelle Spreeuwenberg";
kiri = { kiri = {
name = "kiri"; name = "kiri";
realName = "Jelle Spreeuwenberg";
homeDirectory = "/home/kiri"; homeDirectory = "/home/kiri";
gitEmail = "mail@jelles.net"; emails = {
vaultEmail = "mail@jelles.net"; main = {
extraHomeImports = with homeModules; [ syncthing ]; address = "mail@jelles.net";
primary = true;
type = "mxrouting";
};
old = {
address = "mail@jellespreeuwenberg.nl";
primary = false;
type = "mxrouting";
};
uni = {
address = "j.spreeuwenberg@student.tue.nl";
primary = false;
type = "office365";
};
work = {
address = "jelle.spreeuwenberg@yookr.org";
primary = false;
type = "office365";
};
};
}; };
ergon = { ergon = {
name = "ergon"; name = "ergon";
realName = "Jelle Spreeuwenberg";
homeDirectory = "/home/ergon"; homeDirectory = "/home/ergon";
gitEmail = "jelle.spreeuwenberg@yookr.org"; emails = {
vaultEmail = "jelle.spreeuwenberg@yookr.org"; work = {
extraHomeImports = with homeModules; [ nix ]; address = "jelle.spreeuwenberg@yookr.org";
primary = true;
type = "office365";
};
};
}; };
mkUser = mkUserModules =
account: {
name,
extraHomeImports ? [ ],
}:
let
userModuleName = "user-${name}";
workstationModuleName = "${name}-workstation";
in
{
nixos =
{ {
config, config,
hostType ? "desktop",
lib,
pkgs, pkgs,
... ...
}: }:
let let
username = account.name; account = config.meta.host.users.${name};
isServer = hostType == "server"; primaryEmails = lib.filter (email: email.primary) (builtins.attrValues account.emails);
isWorkstation = config.meta.host.kind == "workstation";
hasWorkstationModule = builtins.hasAttr workstationModuleName homeModules;
in in
{ {
sops.secrets = lib.optionalAttrs (!isServer) { assertions = [
"hashed-password-${username}".neededForUsers = true; {
}; assertion = builtins.length primaryEmails == 1;
message = "User ${name} must define exactly one primary email entry.";
}
];
programs.zsh.enable = true; programs.zsh.enable = true;
users.users.${username} = { sops.secrets = lib.optionalAttrs isWorkstation {
name = username; "hashed-password-${name}".neededForUsers = true;
};
users.users.${name} = {
name = account.name;
home = account.homeDirectory; home = account.homeDirectory;
isNormalUser = true; isNormalUser = true;
shell = pkgs.zsh; shell = pkgs.zsh;
@@ -50,29 +93,65 @@ let
"networkmanager" "networkmanager"
]; ];
} }
// lib.optionalAttrs (!isServer) { // lib.optionalAttrs isWorkstation {
hashedPasswordFile = config.sops.secrets."hashed-password-${username}".path; hashedPasswordFile = config.sops.secrets."hashed-password-${name}".path;
}; };
home-manager.users.${username} = { home-manager.users.${name} = {
imports = [
homeModules.${userModuleName}
]
++ lib.optionals (isWorkstation && hasWorkstationModule) [
homeModules.${workstationModuleName}
];
meta = {
host = config.meta.host;
user = account;
};
};
};
homeManager =
{ config, ... }:
let
account = config.meta.user;
in
{
home = { home = {
inherit username; username = account.name;
homeDirectory = account.homeDirectory; homeDirectory = account.homeDirectory;
stateVersion = "24.05"; stateVersion = "24.05";
}; };
imports = [ homeModules.userBase ] ++ account.extraHomeImports; imports = [
homeModules."user-base"
programs.git.settings.user = { ]
name = realName; ++ extraHomeImports;
email = account.gitEmail; };
}; };
programs.rbw.settings.email = account.vaultEmail; kiriModules = mkUserModules {
name = "kiri";
extraHomeImports = [
homeModules.syncthing
];
}; };
ergonModules = mkUserModules {
name = "ergon";
}; };
in in
{ {
flake.modules.nixos.kiri = mkUser kiri; meta.lib.users = {
flake.modules.nixos.ergon = mkUser ergon; inherit
ergon
kiri
;
};
flake.modules.nixos."user-kiri" = kiriModules.nixos;
flake.modules.nixos."user-ergon" = ergonModules.nixos;
flake.modules.homeManager."user-kiri" = kiriModules.homeManager;
flake.modules.homeManager."user-ergon" = ergonModules.homeManager;
} }