Module Library #
File Tree #
modules/
├── aggregators/ # role bundles + aspect aggregators (flake.aspectInclude keys)
│ ├── base.nix # bundle → core, shell, local, users, tailscale, helix
│ ├── workstation.nix # bundle → base, desktop, niri, ly, git, nix, zed, kitty
│ ├── server.nix # bundle → base, serverCore, sshd, git, network, nh, yazi
│ ├── shell.nix # → zsh, shellTools, starship, ssh
│ ├── apps.nix # → yazi, browser, gtk, nh, fastfetch
│ ├── desktop.nix # → services, apps, noctalia
│ └── services.nix # direct-import module (nixos+HM+finix), deliberately NOT aspectInclude
├── features/
│ ├── base/
│ │ ├── core.nix # nixos: boot, Nix settings/GC, substituters, nix-ld · HM: home-manager, XDG
│ │ ├── local.nix # timezone (Europe/Berlin), locale (en_US/de_DE), keymap (de)
│ │ ├── cachyosKernel.nix # CachyOS kernel overlay import
│ │ └── sops.nix # sops-nix helper (defaultSopsFile per host)
│ ├── desktop/
│ │ ├── apps/
│ │ │ ├── term/ # foot, ghostty, kitty
│ │ │ ├── browser.nix # brave + MIME associations
│ │ │ ├── fastfetch.nix # fastfetch with Nix logo
│ │ │ ├── fuzzel.nix # fuzzel launcher
│ │ │ ├── gtk.nix # GTK theme (adw-gtk3, Pop icons)
│ │ │ ├── nh.nix # nh helper + auto-cleanup
│ │ │ └── yazi.nix # yazi + termfilechooser portal
│ │ ├── bar/waybar.nix # Waybar status bar
│ │ ├── compositors/ # hyprland, mango, niri (each nixos + HM)
│ │ ├── noctalia/ # noctalia.nix (shell + lib fns) + noctaliaSettings.nix
│ │ ├── services/ # audio, bluetooth, fonts, graphics, greetd, ly, mako, music, portals, session, userServices
│ │ └── tools/ # screenshot, wallpaper (HM)
│ ├── dev/
│ │ ├── editors/ # helix, zed (HM)
│ │ ├── languages/ # bash c css go html java javascript json lua markdown nix puml python rust yaml zig (HM, import = enable)
│ │ └── tools/ # build cli database direnv git network opencode security (HM, import = enable)
│ ├── profiles/ # gaming, laptop, performance, virtualisation, mkVM (nixos, import = enable)
│ ├── rescue/rescue.nix # rescue/install toolkit (disko, cryptsetup, parted, …)
│ ├── server/ # nixos, import = enable
│ │ ├── serverCore.nix # firewall, fail2ban base, lid-switch ignore, plugdev/media groups, htop/tmux/rsync
│ │ ├── ssh.nix # OpenSSH (no root, key-only) + fail2ban sshd jail (module key: sshd)
│ │ ├── nginx.nix # nginx + gzip + fail2ban http jails
│ │ ├── monitoring.nix # netdata (withCloudUi dashboard) on tailscale0:19999
│ │ ├── media/ # jellyfin, navidrome, nextcloud, ownCloud, nixarr
│ │ ├── security/ # authentik, restic, vaultwarden
│ │ ├── share/ # croc, element, forgejo, matrix, ollama
│ │ └── vpn/ # airvpn, cloudflared, mullvad, tailscale
│ └── shell/ # zsh, shellTools, starship, ssh
├── hosts/ # per-host registry entries (+ hardware/disko/impermanence)
│ ├── nimeses/ hardware, disko # workstation
│ ├── prometheus/ hardware # workstation (NVIDIA)
│ ├── hephaistos/ hardware # server (tailnet-only)
│ ├── athena/ hardware # server (public, full stack)
│ ├── hermes/ hardware, disko, imperm. # base
│ └── icarus/ hardware, disko # finix (experimental)
├── lib/
│ ├── compositors.nix # shared features.compositors.* options (monitors, gaps, colors, …)
│ └── registry.nix # registry.hosts + aspects (enum + flake.lib.aspects) + aspectInclude closure
├── meta/
│ ├── flakeParts.nix # flake-parts setup
│ ├── generation.nix # NixOS generator: registry.hosts → nixosConfigurations (commonModule: hostName, domain)
│ ├── homeModules.nix # home-manager routing / homeConfigurations
│ ├── deploy.nix # deploy-rs deploy.nodes (generated from the registry)
│ ├── minimalHosts.nix # <host>Minimal install/rescue nixosConfigurations
│ ├── finixVm.nix # finix VM build (icarus)
│ ├── checks.nix # every host toplevel into nix flake check
│ ├── nixpkgs.nix # nixpkgs config + overlays
│ ├── overlays.nix # package overlays
│ └── users.nix # user account definitions
└── MODULES.md # this file
Aggregators & Role Bundles #
Aggregators live in modules/aggregators/ and are keyed entries of
flake.aspectInclude.<name> — a name in a host's aspects list that expands to a
list of other aspect names via transitive closure (resolveAspects). An aggregator
key needs no backing module; it contributes only its members. The role bundles
(base, workstation, server) are the top-level aggregators — a host lists a bundle
plus a few extras instead of repeating the common set.
| Aggregator |
Kind |
Expands to |
base |
role bundle |
core, shell, local, users, tailscale, helix |
workstation |
role bundle |
base, desktop, niri, ly, git, nix, zed, kitty |
server |
role bundle |
base, serverCore, sshd, git, network, nh, yazi |
shell |
aggregator |
zsh, shellTools, starship, ssh |
apps |
aggregator (HM) |
yazi, browser, gtk, nh, fastfetch |
desktop |
aggregator |
services, apps, noctalia |
services |
direct-import module |
see below |
services is deliberately not an aspectInclude — it stays a per-layer
direct-import module because it encodes cross-layer asymmetry that name-based routing
can't express: nixos imports graphics, fonts, portals, audio, bluetooth; HM imports
userServices; finix imports fonts, ly. Don't "simplify" it into an aggregator.
Desktop #
Compositors (niri, hyprland, mango) are not bundled by desktop — each is added
directly by the host (workstation adds niri).
Apps #
| Module |
Side |
Packages |
Options |
browser |
HM |
brave |
features.apps.browser.defaultBrowser (brave|firefox|chromium) |
fastfetch |
HM |
— |
— |
foot |
HM |
foot (apps/term/) |
— |
ghostty |
HM |
ghostty (apps/term/) |
— |
kitty |
HM |
kitty (apps/term/) |
— |
fuzzel |
HM |
fuzzel |
— |
gtk |
HM |
adw-gtk3, pop-icon-theme |
— |
nh |
HM |
— |
— |
yazi |
HM |
xdg-desktop-portal-termfilechooser, xdg-terminal-exec |
features.apps.yazi.terminalFilechooser.terminal (default: ghostty) |
Compositors #
| Module |
Side |
Options |
niri |
nixos + HM |
features.compositors.niri.enable, .extraBinds, .autoStart, .input.touchpad.enable |
hyprland |
nixos + HM |
features.compositors.hyprland.enable, .extraBinds, .autoStart, .input.touchpad.enable |
mango |
nixos + HM |
import = enable (no .enable), features.compositors.mango.extraBinds, .autoStart |
lib/compositors |
nixos + HM |
features.compositors.monitors.<name>.{resolution, refreshRate, scale, transform, position, vrr.enable, primary} |
Bar #
| Module |
Side |
Configures |
waybar |
HM |
Waybar status bar |
Services #
| Module |
Side |
Configures |
Options |
audio |
nixos |
PipeWire + WirePlumber + RTKit |
— |
bluetooth |
nixos |
Bluetooth + Blueman |
— |
fonts |
nixos (+ finix) |
ibm-plex, google-fonts, material-symbols, nerd-fonts |
— |
graphics |
nixos |
DRM, modesetting, XKB (de) |
— |
greetd |
nixos |
tuigreet, GNOME Keyring PAM |
— |
ly |
nixos (+ finix) |
ly display manager, GNOME Keyring PAM |
— |
mako |
HM |
Mako notification daemon |
— |
music |
nixos |
MPD |
— |
portals |
nixos |
XDG portal, dbus, udisks2 |
— |
session |
finix |
seatd, getty tty1, doas, greetd PAM (finix session) |
— |
userServices |
HM |
gnome-keyring, udiskie |
features.services.user.storage.udiskie.{notify, automount} |
| Module |
Side |
Configures |
Options |
screenshot |
HM |
screenshot tooling |
— |
wallpaper |
HM |
wallpaper configuration |
features.compositors.wallpaper.image |
Noctalia #
| Module |
Side |
Configures |
noctalia |
nixos + HM |
noctalia-shell shell layer, spawn-at-startup, layer rules |
noctaliaSettings |
HM |
Full noctalia config (bar, launcher, audio, notifications, wallpaper, etc.) |
noctalia.nix also exposes two shared lib functions:
| Function |
Usage |
Output |
config.flake.lib.mkNoctaliaNiri |
mkNoctaliaNiri "volume increase" |
["noctalia-shell" "ipc" "call" "volume" "increase"] |
config.flake.lib.mkNoctaliaHypr |
mkNoctaliaHypr "volume increase" |
"exec, noctalia-shell ipc call volume increase" |
Dev #
Editors #
| Module |
Side |
Packages |
Sets |
helix |
HM |
— |
EDITOR=hx, VISUAL=hx (mkDefault), mimeApps text/* → helix, clipboard-provider (mkDefault wayland) |
zed |
HM |
zed-editor-fhs |
VISUAL=zeditor --wait (overrides helix default) |
Servers set programs.helix.settings.editor.clipboard-provider = "termcode" in their
host homeModule so yank/paste works over SSH (OSC 52) where there's no Wayland clipboard.
All HM side. Import = enable.
| Module |
Packages |
build |
gnumake, cmake, pkg-config |
cli |
jq, yq, sd, just, hyperfine, tokei, watchexec, btop, gh |
database |
sqlite, postgresql |
direnv |
direnv + nix-direnv |
git |
git, delta, lazygit, pre-commit, commitizen, lefthook, tig, git-absorb |
network |
httpie, bandwhich |
opencode |
opencode (from inputs), MCP integration, custom agents |
security |
nmap, netcat, mtr, tcpdump, traceroute |
Languages #
All HM side. Import = enable.
| Module |
Packages |
Helix LSP |
bash |
bash-language-server, shellcheck, shfmt, bash |
bash-language-server |
c |
clang-tools, ccls, gcc |
clangd (C + C++) |
css |
vscode-langservers-extracted |
vscode-css-languageserver |
go |
gopls, gotools, golangci-lint, go |
gopls |
html |
vscode-langservers-extracted |
vscode-html-languageserver |
java |
jdk, jdt-language-server, gradle |
jdtls |
javascript |
typescript-language-server, prettier, eslint, bun |
typescript-language-server (JS + TS) |
json |
vscode-langservers-extracted |
vscode-json-languageserver |
lua |
lua-language-server, stylua, lua |
lua-language-server |
markdown |
marksman, markdownlint-cli |
marksman |
nix |
nixd, alejandra |
nixd + alejandra formatter |
puml |
plantuml |
— |
python |
pyright, ruff, python3, uv |
pyright |
rust |
rust-analyzer, rustfmt, clippy, rustc, cargo |
rust-analyzer |
yaml |
yaml-language-server, yamlfmt |
yaml-language-server |
zig |
zls, zig |
zls |
Server #
All nixos side. Import = enable. features.server.domain must be set when importing
nginx or any service with a reverse proxy.
Core #
| Module |
Configures |
Options |
serverCore |
Firewall, fail2ban base, lid-switch ignore, plugdev/media groups, no docs, htop/tmux/rsync |
— |
sshd |
OpenSSH (no root, key-only), fail2ban sshd jail |
features.server.sshPort (default 22) |
nginx |
Nginx + gzip + fail2ban http jails |
features.server.domain |
monitoring |
netdata with bundled dashboard (withCloudUi), tailscale0:19999 |
— |
VPN (vpn/) #
| Module |
Configures |
tailscale |
Tailscale, trusts tailscale0 interface |
cloudflared |
Cloudflare tunnel |
airvpn |
AirVPN system-wide (PostUp rules for Tailscale coexistence) |
mullvad |
Mullvad VPN |
| Module |
Configures |
jellyfin |
Jellyfin, nginx proxy (media.${domain}) |
navidrome |
Navidrome music server, nginx proxy, restic backup of /var/lib/navidrome |
nextcloud |
Nextcloud, nginx proxy |
ownCloud |
ownCloud Infinite Scale, nginx proxy |
nixarr |
nixarr arr stack |
Security (security/) #
| Module |
Configures |
authentik |
Authentik SSO |
restic |
services.restic.backups.system — daily, keep 7d/4w/6m, sops restic-password, repo /backup/<host> (default) |
vaultwarden |
Vaultwarden (port 8222), nginx proxy, sops env |
Service modules opt into backup by adding their state dir to
services.restic.backups.system.paths and pulling in the restic aspect.
Share (share/) #
| Module |
Configures |
Options |
forgejo |
Forgejo (sqlite, LFS, SSH :2222), nginx proxy |
— |
matrix |
Matrix (Synapse), nginx proxy |
— |
element |
Element web client, nginx proxy |
— |
croc |
Croc relay, firewall ports 9009-9013 on tailscale0 |
— |
ollama |
Ollama with configurable acceleration |
features.server.ollama.{host, port, acceleration} (rocm|cuda|null) |
Base / Core #
| Module |
Side |
Configures |
core |
nixos |
systemd-boot, Plymouth, Nix flakes/GC/auto-optimise, NetworkManager, nix-ld, all substituters/caches (cache.nixos.org, nix-community, niri-nix, noctalia, hyprland, lantian, kopuz) |
core |
HM |
home-manager, XDG base dirs |
local |
nixos |
Timezone (Europe/Berlin), locale (en_US / de_DE), keymap (de) |
cachyosKernel |
nixos |
CachyOS kernel overlay import |
sops |
nixos |
sops-nix helper — per-host defaultSopsFile, age key wiring |
base is the role bundle (see Aggregators); it pulls in core, shell, local,
users, tailscale, helix.
Profiles #
All nixos side. Import = enable.
| Module |
Configures |
gaming |
Steam, Proton, GameMode, Gamescope, Vulkan, DXVK |
laptop |
upower, thermald, power-profiles, libinput touchpad |
performance |
TCP tuning, zram (zstd 50%), Nix daemon scheduling |
virtualisation |
libvirtd, KVM, virt-manager, KSM, hugepages |
mkVM |
build-a-VM helper for a host config |
Rescue #
| Module |
Side |
Configures |
rescue |
nixos |
Install/repair toolkit: nixos-install-tools, disko, cryptsetup, parted, gptfdisk, ntfs3g, exfatprogs, smartmontools, nvme-cli, … |
Shell #
| Module |
Side |
Configures |
shell |
aggregator |
zsh, shellTools, starship, ssh |
zsh |
nixos + HM |
zsh, eza aliases, history, syntax highlighting, fastfetch on login |
shellTools |
HM |
zoxide, fzf, ripgrep, fd |
starship |
HM |
Starship prompt (git, nix-shell, python, OS symbol) |
ssh |
HM |
SSH config + YubiKey FIDO2 identities, ControlMaster multiplexing, host aliases |
Lib #
Infrastructure modules — not imported by hosts, wired in automatically.
| File |
Purpose |
compositors.nix |
HM module — defines all shared features.compositors.* options (monitors, gaps, colors, borders, opacity, cursor, keyboard, terminal) |
registry.nix |
Defines options.registry.hosts — host registry (username, system, stateVersion, domain, hostId, extraGroups, homeDirectory, aspects, nixosModule, homeModule). Exposes flake.lib.aspects (name→name map for the bare form), folds flake.aspectInclude keys into the valid-names enum, and resolves the transitive closure of aspects. |
Aspects (host module selection) #
Each host lists its modules once as registry.hosts.<host>.aspects. Names route to
whichever layer defines them (flake.modules.nixos.<name> → system,
flake.modules.homeManager.<name> → home, both → both; nixos-only names are skipped by
the standalone homeConfigurations build). Names can also be aggregators
(flake.aspectInclude.<name> keys) that expand to more names — see Aggregators & Role
Bundles. The option is enum-typed against all module names plus all aggregator keys,
so typos fail at eval with the full valid list. Written bare via
with config.flake.lib.aspects; [ … ].
registry.hosts.nimeses.aspects = with config.flake.lib.aspects; [
workstation # role bundle → base + desktop + niri + …
hardwareNimeses # host-only
gaming performance # extra profiles
];
Deployment (deploy-rs) #
modules/meta/deploy.nix generates flake.deploy.nodes from the registry — one node
per NixOS host (filtered to those with a nixosConfigurations entry, so *Minimal
variants and finix icarus are excluded).
deploy .#<host> # build locally, copy closure, activate remotely
deploy .#<host> --skip-checks # skip the slow flake-check pre-flight
Per node: sshUser = <host>, user = "root", interactiveSudo = true, and
sshOpts = ["-o" "ControlPath=none"]. Magic rollback and auto rollback are on
(deploy-rs defaults). The sshOpts line is load-bearing: without it the
confirmation reconnect can ride an SSH ControlMaster socket and falsely confirm a
deploy that has actually broken inbound SSH — forcing ControlPath=none makes the
confirm a genuine fresh login, so a broken sshd/firewall/network triggers the revert
instead of locking you out.
Secrets (sops-nix) #
Secrets are managed with sops-nix + age keys. The sops aspect (in base) wires the
per-host defaultSopsFile and age key; secret-consuming service modules declare
sops.secrets.<name> and read config.sops.secrets.<name>.path.
| Host |
Secret file |
Age key location |
nimeses |
secrets/nimeses.yaml |
/home/nimeses/.config/sops/age/keys.txt |
hephaistos |
secrets/hephaistos.yaml |
/root/.config/sops/age/keys.txt |
athena |
secrets/athena.yaml |
/root/.config/sops/age/keys.txt |
Age keys are derived from SSH host keys (YubiKey-backed on nimeses). Secrets are
root-readable only (never placed in environment.*, which is world-readable in the Nix
store). To add a new secret: encrypt with sops and reference via
config.sops.secrets.<name>.path.