From 1719d54ee05681f245c6dc68dba2ac0b63a3eeae Mon Sep 17 00:00:00 2001 From: primary Date: Sat, 18 Apr 2026 00:58:55 -0400 Subject: [PATCH] phase 3: new flake.nix + extract common-{nix,doas,shell-fish}; rewire imports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - New unified flake with two nixpkgs channels (unstable for desktops, 25.11 for muffin) - modules/common-{doas,shell-fish,nix}.nix extracted from duplicated blocks - modules/desktop-common.nix: renamed from system/common.nix; secret paths point to secrets/desktop/ - hosts/{mreow,yarn}/default.nix import desktop-common; yarn imports modules/no-rgb.nix - hosts/muffin/default.nix imports common-* + server-prefixed modules + services/; duplicate doas/fish/nix blocks removed; gc retention preserved as mkForce override - modules/age-secrets.nix: file paths → ../secrets/server/*.age - services/{minecraft,matrix/livekit}: secret paths → ../secrets/server/ - home/profiles/*.nix: ./progs/ → ../progs/ - hosts/{mreow,yarn}/home.nix: imports rewired to ../../home/profiles/ and ../../home/progs/ - home/progs/pi.nix and hosts/yarn/home.nix: secret reads → ../../secrets/home/ - tests/*.nix: ../modules/security.nix → ../modules/server-security.nix; ../modules/overlays.nix → ../lib/overlays.nix - lib/default.nix: takes explicit lib param (defaults to nixpkgs-stable.lib) --- flake.nix | 370 ++++++++++++++++++ home/profiles/desktop.nix | 8 +- home/profiles/gui.nix | 12 +- home/profiles/no-gui.nix | 6 +- home/progs/pi.nix | 4 +- hosts/mreow/default.nix | 4 +- hosts/mreow/home.nix | 8 +- hosts/muffin/default.nix | 136 +++---- hosts/yarn/default.nix | 6 +- hosts/yarn/home.nix | 6 +- lib/default.nix | 3 +- modules/age-secrets.nix | 48 +-- modules/common-doas.nix | 15 + modules/common-nix.nix | 22 ++ modules/common-shell-fish.nix | 16 + .../common.nix => modules/desktop-common.nix | 77 +--- services/matrix/livekit.nix | 2 +- services/minecraft.nix | 2 +- tests/fail2ban-caddy.nix | 2 +- tests/fail2ban-gitea.nix | 2 +- tests/fail2ban-immich.nix | 2 +- tests/fail2ban-jellyfin.nix | 2 +- tests/fail2ban-ssh.nix | 2 +- tests/fail2ban-vaultwarden.nix | 2 +- tests/file-perms.nix | 2 +- tests/minecraft.nix | 2 +- tests/ntfy-alerts.nix | 2 +- tests/zfs.nix | 2 +- 28 files changed, 562 insertions(+), 203 deletions(-) create mode 100644 flake.nix create mode 100644 modules/common-doas.nix create mode 100644 modules/common-nix.nix create mode 100644 modules/common-shell-fish.nix rename legacy/dotfiles/system/common.nix => modules/desktop-common.nix (88%) diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..16d0eed --- /dev/null +++ b/flake.nix @@ -0,0 +1,370 @@ +{ + description = "Unified NixOS flake for mreow (laptop), yarn (desktop), muffin (server)"; + + inputs = { + # Two channels: unstable for desktops, 25.11 for server. + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + nixpkgs-stable.url = "github:NixOS/nixpkgs/nixos-25.11"; + + home-manager = { + url = "github:nix-community/home-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + home-manager-stable = { + url = "github:nix-community/home-manager/release-25.11"; + inputs.nixpkgs.follows = "nixpkgs-stable"; + }; + + # Shared across all hosts + lanzaboote = { + url = "github:nix-community/lanzaboote"; + inputs.nixpkgs.follows = "nixpkgs"; + inputs.rust-overlay.follows = "rust-overlay"; + }; + nixos-hardware.url = "github:NixOS/nixos-hardware/master"; + disko = { + url = "github:nix-community/disko/latest"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + impermanence = { + url = "github:nix-community/impermanence"; + inputs.nixpkgs.follows = "nixpkgs"; + inputs.home-manager.follows = "home-manager"; + }; + + # Desktop (mreow + yarn) + rust-overlay = { + url = "github:oxalica/rust-overlay"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + zen-browser = { + url = "github:0xc000022070/zen-browser-flake"; + inputs.nixpkgs.follows = "nixpkgs"; + inputs.home-manager.follows = "home-manager"; + }; + firefox-addons = { + url = "gitlab:rycee/nur-expressions?dir=pkgs/firefox-addons"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + niri = { + url = "github:sodiboo/niri-flake"; + inputs.nixpkgs.follows = "nixpkgs"; + inputs.nixpkgs-stable.follows = "nixpkgs"; + }; + emacs-overlay = { + url = "github:nix-community/emacs-overlay"; + inputs.nixpkgs.follows = "nixpkgs"; + inputs.nixpkgs-stable.follows = "nixpkgs"; + }; + nix-flatpak.url = "github:gmodena/nix-flatpak/"; + nix-doom-emacs-unstraightened = { + url = "github:marienz/nix-doom-emacs-unstraightened"; + inputs.nixpkgs.follows = "nixpkgs"; + inputs.emacs-overlay.follows = "emacs-overlay"; + }; + jovian-nixos = { + url = "github:Jovian-Experiments/Jovian-NixOS"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + noctalia = { + url = "github:noctalia-dev/noctalia-shell"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + nix-cachyos-kernel = { + url = "github:xddxdd/nix-cachyos-kernel/release"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + llm-agents = { + url = "github:numtide/llm-agents.nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + json2steamshortcut = { + url = "github:ChrisOboe/json2steamshortcut"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + # Server (muffin) — follows nixpkgs-stable + nix-minecraft = { + url = "github:Infinidoge/nix-minecraft"; + inputs.nixpkgs.follows = "nixpkgs-stable"; + }; + vpn-confinement.url = "github:Maroka-chan/VPN-Confinement"; + llamacpp = { + url = "github:TheTom/llama-cpp-turboquant/feature/turboquant-kv-cache"; + inputs.nixpkgs.follows = "nixpkgs-stable"; + }; + srvos = { + url = "github:nix-community/srvos"; + inputs.nixpkgs.follows = "nixpkgs-stable"; + }; + deploy-rs = { + url = "github:serokell/deploy-rs"; + inputs.nixpkgs.follows = "nixpkgs-stable"; + }; + agenix = { + url = "github:ryantm/agenix"; + inputs.nixpkgs.follows = "nixpkgs-stable"; + inputs.home-manager.follows = "home-manager-stable"; + inputs.darwin.follows = ""; + }; + senior_project-website = { + url = "github:Titaniumtown/senior-project-website"; + flake = false; + }; + website = { + url = "git+https://git.sigkill.computer/titaniumtown/website"; + flake = false; + }; + trackerlist = { + url = "github:ngosang/trackerslist"; + flake = false; + }; + ytbn-graphing-software = { + url = "git+https://git.sigkill.computer/titaniumtown/YTBN-Graphing-Software"; + }; + arr-init = { + url = "git+ssh://gitea@git.gardling.com/titaniumtown/arr-init"; + inputs.nixpkgs.follows = "nixpkgs-stable"; + }; + nixpkgs-p2pool-module = { + url = "github:JacoMalan1/nixpkgs/create-p2pool-service"; + flake = false; + }; + qbittorrent-metrics-exporter = { + url = "git+https://codeberg.org/anriha/qbittorrent-metrics-exporter"; + inputs.nixpkgs.follows = "nixpkgs-stable"; + }; + }; + + outputs = + inputs@{ + self, + nixpkgs, + nixpkgs-stable, + home-manager, + home-manager-stable, + lanzaboote, + nixos-hardware, + disko, + impermanence, + nix-minecraft, + vpn-confinement, + srvos, + deploy-rs, + agenix, + arr-init, + nixpkgs-p2pool-module, + jovian-nixos, + ... + }: + let + username = "primary"; + system = "x86_64-linux"; + + niriPackage = inputs.niri.packages.${system}.niri-unstable; + + # --- Server (muffin) plumbing --- + bootstrapPkgs = import nixpkgs-stable { inherit system; }; + patchedStableSrc = bootstrapPkgs.applyPatches { + name = "nixpkgs-stable-patched"; + src = nixpkgs-stable; + patches = [ + ./patches/nixpkgs/0001-firefox-syncserver-add-postgresql-backend-support.patch + ]; + }; + serverPkgs = import patchedStableSrc { + inherit system; + targetPlatform = system; + buildPlatform = builtins.currentSystem; + }; + serviceConfigs = import ./hosts/muffin/service-configs.nix; + serverLib = import ./lib { + inherit inputs; + lib = nixpkgs-stable.lib; + pkgs = serverPkgs; + service_configs = serviceConfigs; + }; + testSuite = import ./tests/tests.nix { + pkgs = serverPkgs; + lib = serverLib; + inherit inputs; + config = self.nixosConfigurations.muffin.config; + }; + + # --- Host builders --- + + # Desktop: unstable + home-manager-unstable + niri-unstable + mkDesktopHost = + hostname: + nixpkgs.lib.nixosSystem { + specialArgs = { + inherit inputs username hostname; + niri-package = niriPackage; + }; + modules = [ + home-manager.nixosModules.home-manager + ( + { config, ... }: + { + home-manager.useUserPackages = true; + home-manager.sharedModules = [ + inputs.zen-browser.homeModules.twilight + ]; + home-manager.extraSpecialArgs = { + inherit + inputs + hostname + username + ; + niri-package = niriPackage; + homeDirectory = "/home/${username}"; + stateVersion = config.system.stateVersion; + }; + home-manager.users.${username} = import ./hosts/${hostname}/home.nix; + } + ) + ./hosts/${hostname}/default.nix + ]; + }; + + # Server: stable + home-manager-stable + srvos + agenix + patched pkgs + muffinHost = serverLib.nixosSystem { + inherit system; + specialArgs = { + inherit + username + inputs + ; + hostname = "muffin"; + eth_interface = "enp4s0"; + service_configs = serviceConfigs; + lib = serverLib; + }; + modules = [ + # SAFETY! port sanity checks + ( + { config, lib, ... }: + let + publicPorts = lib.attrValues serviceConfigs.ports.public; + privatePorts = lib.attrValues serviceConfigs.ports.private; + allPortNumbers = map (p: p.port) (publicPorts ++ privatePorts); + uniquePortNumbers = lib.unique allPortNumbers; + + publicTcp = map (p: p.port) (lib.filter (p: p.proto == "tcp" || p.proto == "both") publicPorts); + publicUdp = map (p: p.port) (lib.filter (p: p.proto == "udp" || p.proto == "both") publicPorts); + privatePortNumbers = map (p: p.port) privatePorts; + + fwTcp = config.networking.firewall.allowedTCPPorts; + fwUdp = config.networking.firewall.allowedUDPPorts; + + missingTcp = lib.filter (p: !(builtins.elem p fwTcp)) publicTcp; + missingUdp = lib.filter (p: !(builtins.elem p fwUdp)) publicUdp; + leakedTcp = lib.filter (p: builtins.elem p fwTcp) privatePortNumbers; + leakedUdp = lib.filter (p: builtins.elem p fwUdp) privatePortNumbers; + in + { + config.assertions = [ + { + assertion = (lib.length allPortNumbers) == (lib.length uniquePortNumbers); + message = "Duplicate port numbers detected in ports.public / ports.private"; + } + { + assertion = missingTcp == [ ]; + message = "Public ports missing from allowedTCPPorts: ${builtins.toString missingTcp}"; + } + { + assertion = missingUdp == [ ]; + message = "Public ports missing from allowedUDPPorts: ${builtins.toString missingUdp}"; + } + { + assertion = leakedTcp == [ ] && leakedUdp == [ ]; + message = "Private ports leaked into firewall allow-lists — TCP: ${builtins.toString leakedTcp}, UDP: ${builtins.toString leakedUdp}"; + } + ]; + } + ) + + srvos.nixosModules.server + srvos.nixosModules.mixins-terminfo + + ./hosts/muffin/disk.nix + ./hosts/muffin/default.nix + + # Firefox-syncserver: swap upstream module + package for patched versions. + { + disabledModules = [ "services/networking/firefox-syncserver.nix" ]; + imports = [ + "${patchedStableSrc}/nixos/modules/services/networking/firefox-syncserver.nix" + ]; + nixpkgs.overlays = [ + nix-minecraft.overlay + (import ./lib/overlays.nix) + (_final: prev: { + syncstorage-rs = + prev.callPackage "${patchedStableSrc}/pkgs/by-name/sy/syncstorage-rs/package.nix" + { }; + }) + ]; + nixpkgs.config.allowUnfreePredicate = + pkg: + builtins.elem (nixpkgs-stable.lib.getName pkg) [ + "minecraft-server" + ]; + } + + lanzaboote.nixosModules.lanzaboote + arr-init.nixosModules.default + (import "${nixpkgs-p2pool-module}/nixos/modules/services/networking/p2pool.nix") + + home-manager-stable.nixosModules.home-manager + ( + { ... }: + { + home-manager.users.${username} = import ./hosts/muffin/home.nix; + } + ) + ] + ++ (with nixos-hardware.nixosModules; [ + common-cpu-amd-pstate + common-cpu-amd-zenpower + common-pc-ssd + common-gpu-intel + ]); + }; + in + { + formatter.${system} = nixpkgs.legacyPackages.${system}.nixfmt-tree; + + nixosConfigurations = { + mreow = mkDesktopHost "mreow"; + yarn = mkDesktopHost "yarn"; + muffin = muffinHost; + }; + + deploy.nodes.muffin = { + hostname = "server-public"; + profiles.system = { + sshUser = "root"; + user = "root"; + path = deploy-rs.lib.${system}.activate.nixos self.nixosConfigurations.muffin; + }; + }; + + checks.${system} = testSuite; + + packages.${system} = { + tests = serverPkgs.linkFarm "all-tests" ( + serverPkgs.lib.mapAttrsToList (name: test: { + name = name; + path = test; + }) testSuite + ); + } + // (serverPkgs.lib.mapAttrs' (name: test: { + name = "test-${name}"; + value = test; + }) testSuite); + + lib = serverLib; + }; +} diff --git a/home/profiles/desktop.nix b/home/profiles/desktop.nix index ab2b5a1..e38955e 100644 --- a/home/profiles/desktop.nix +++ b/home/profiles/desktop.nix @@ -7,16 +7,16 @@ { imports = [ # niri wayland compositor - ./progs/niri.nix + ../progs/niri.nix # lockscreen - ./progs/swaylock.nix + ../progs/swaylock.nix # notification daemon - ./progs/dunst.nix + ../progs/dunst.nix # noctalia desktop shell - ./progs/noctalia.nix + ../progs/noctalia.nix ]; home.packages = with pkgs; [ diff --git a/home/profiles/gui.nix b/home/profiles/gui.nix index 4f067be..85aed92 100644 --- a/home/profiles/gui.nix +++ b/home/profiles/gui.nix @@ -8,12 +8,12 @@ { imports = [ ./no-gui.nix - # ./progs/ghostty.nix - ./progs/alacritty.nix - ./progs/emacs.nix - # ./progs/trezor.nix # - broken - ./progs/flatpak.nix - ./progs/zen + # ../progs/ghostty.nix + ../progs/alacritty.nix + ../progs/emacs.nix + # ../progs/trezor.nix # - broken + ../progs/flatpak.nix + ../progs/zen ]; nixpkgs.config.allowUnfreePredicate = diff --git a/home/profiles/no-gui.nix b/home/profiles/no-gui.nix index 5c16a98..d210c4c 100644 --- a/home/profiles/no-gui.nix +++ b/home/profiles/no-gui.nix @@ -117,9 +117,9 @@ let in { imports = [ - ./progs/fish.nix - ./progs/helix.nix - ./progs/pi.nix + ../progs/fish.nix + ../progs/helix.nix + ../progs/pi.nix ( { ... }: { diff --git a/home/progs/pi.nix b/home/progs/pi.nix index da89c87..e6ca7d3 100644 --- a/home/progs/pi.nix +++ b/home/progs/pi.nix @@ -23,11 +23,11 @@ let ompModels = { providers = { openrouter = { - apiKey = lib.strings.trim (builtins.readFile ../secrets/openrouter_api_key); + apiKey = lib.strings.trim (builtins.readFile ../../secrets/home/openrouter_api_key); }; "llama.cpp" = { baseUrl = "https://llm.sigkill.computer"; - apiKey = lib.strings.trim (builtins.readFile ../secrets/llama_cpp_api_key); + apiKey = lib.strings.trim (builtins.readFile ../../secrets/home/llama_cpp_api_key); api = "openai-responses"; authHeader = true; discovery.type = "llama.cpp"; diff --git a/hosts/mreow/default.nix b/hosts/mreow/default.nix index 16d7902..7222e09 100644 --- a/hosts/mreow/default.nix +++ b/hosts/mreow/default.nix @@ -8,8 +8,8 @@ }: { imports = [ - ./common.nix - ./disk_mreow.nix + ../../modules/desktop-common.nix + ./disk.nix inputs.nixos-hardware.nixosModules.framework-amd-ai-300-series ]; diff --git a/hosts/mreow/home.nix b/hosts/mreow/home.nix index 27df1a1..3f7cc72 100644 --- a/hosts/mreow/home.nix +++ b/hosts/mreow/home.nix @@ -6,12 +6,12 @@ }: { imports = [ - ./gui.nix - ./desktop.nix - ./progs/borg.nix + ../../home/profiles/gui.nix + ../../home/profiles/desktop.nix + ../../home/progs/borg.nix # effects headphones too - # ./progs/framework-13-easyeffects.nix + # ../../home/progs/framework-13-easyeffects.nix ]; # media controls diff --git a/hosts/muffin/default.nix b/hosts/muffin/default.nix index 0be5282..43a333b 100644 --- a/hosts/muffin/default.nix +++ b/hosts/muffin/default.nix @@ -11,68 +11,74 @@ }: { imports = [ - ./modules/hardware.nix - ./modules/zfs.nix - ./modules/impermanence.nix - ./modules/usb-secrets.nix - ./modules/age-secrets.nix - ./modules/secureboot.nix - ./modules/no-rgb.nix - ./modules/security.nix - ./modules/ntfy-alerts.nix - ./modules/power.nix + # common across all hosts + ../../modules/common-doas.nix + ../../modules/common-shell-fish.nix + ../../modules/common-nix.nix - ./services/postgresql.nix - ./services/jellyfin - ./services/caddy - ./services/immich.nix - ./services/gitea.nix - ./services/gitea-actions-runner.nix - ./services/minecraft.nix + # muffin-only system modules + ./hardware.nix + ../../modules/zfs.nix + ../../modules/server-impermanence.nix + ../../modules/usb-secrets.nix + ../../modules/age-secrets.nix + ../../modules/server-lanzaboote-agenix.nix + ../../modules/no-rgb.nix + ../../modules/server-security.nix + ../../modules/ntfy-alerts.nix + ../../modules/server-power.nix - ./services/wg.nix - ./services/qbittorrent.nix - ./services/bitmagnet.nix + ../../services/postgresql.nix + ../../services/jellyfin + ../../services/caddy + ../../services/immich.nix + ../../services/gitea.nix + ../../services/gitea-actions-runner.nix + ../../services/minecraft.nix - ./services/arr/prowlarr.nix - ./services/arr/sonarr.nix - ./services/arr/radarr.nix - ./services/arr/bazarr.nix - ./services/arr/jellyseerr.nix - ./services/arr/recyclarr.nix - ./services/arr/arr-search.nix - ./services/arr/torrent-audit.nix - ./services/arr/init.nix + ../../services/wg.nix + ../../services/qbittorrent.nix + ../../services/bitmagnet.nix - ./services/soulseek.nix + ../../services/arr/prowlarr.nix + ../../services/arr/sonarr.nix + ../../services/arr/radarr.nix + ../../services/arr/bazarr.nix + ../../services/arr/jellyseerr.nix + ../../services/arr/recyclarr.nix + ../../services/arr/arr-search.nix + ../../services/arr/torrent-audit.nix + ../../services/arr/init.nix - # ./services/llama-cpp.nix - ./services/trilium.nix + ../../services/soulseek.nix - ./services/ups.nix + # ../../services/llama-cpp.nix + ../../services/trilium.nix - ./services/grafana + ../../services/ups.nix - ./services/bitwarden.nix - ./services/firefox-syncserver.nix + ../../services/grafana - ./services/matrix + ../../services/bitwarden.nix + ../../services/firefox-syncserver.nix - ./services/monero + ../../services/matrix - ./services/graphing-calculator.nix + ../../services/monero - ./services/ssh.nix + ../../services/graphing-calculator.nix - ./services/syncthing.nix + ../../services/ssh.nix - ./services/ntfy + ../../services/syncthing.nix - ./services/mollysocket.nix + ../../services/ntfy - ./services/harmonia.nix + ../../services/mollysocket.nix - ./services/ddns-updater.nix + ../../services/harmonia.nix + + ../../services/ddns-updater.nix ]; # Hosts entries for CI/CD deploy targets @@ -110,17 +116,8 @@ } ]; - nix = { - # optimize the store - optimise.automatic = true; - - # garbage collection - gc = { - automatic = true; - dates = "weekly"; - options = "--delete-older-than 7d"; - }; - }; + # muffin overrides default gc retention (30d in common-nix.nix) + nix.gc.options = lib.mkForce "--delete-older-than 7d"; # Intel Arc A380 (DG2, 56a5) uses the i915 driver on kernel 6.12. # The xe driver's iHD media driver integration has buffer mapping @@ -276,31 +273,8 @@ hashedPasswordFile = config.age.secrets.hashedPass.path; }; - # https://nixos.wiki/wiki/Fish#Setting_fish_as_your_shell - programs.fish.enable = true; - programs.bash = { - interactiveShellInit = '' - if [[ $(${pkgs.procps}/bin/ps --no-header --pid=$PPID --format=comm) != "fish" && -z ''${BASH_EXECUTION_STRING} ]] - then - shopt -q login_shell && LOGIN_OPTION='--login' || LOGIN_OPTION="" - exec ${pkgs.fish}/bin/fish $LOGIN_OPTION - fi - ''; - }; - - security = { - #lets use doas and not sudo! - doas.enable = true; - sudo.enable = false; - # Configure doas - doas.extraRules = [ - { - users = [ username ]; - keepEnv = true; - persist = true; - } - ]; - }; + # programs.fish + bash→fish redirect and security.doas block are in + # modules/common-shell-fish.nix and modules/common-doas.nix. services.murmur = { enable = true; diff --git a/hosts/yarn/default.nix b/hosts/yarn/default.nix index 3698c01..5371032 100644 --- a/hosts/yarn/default.nix +++ b/hosts/yarn/default.nix @@ -8,10 +8,10 @@ }: { imports = [ - ./disk_yarn.nix - ./common.nix + ../../modules/desktop-common.nix + ../../modules/no-rgb.nix + ./disk.nix ./impermanence.nix - ./no-rgb.nix ./vr.nix inputs.impermanence.nixosModules.impermanence diff --git a/hosts/yarn/home.nix b/hosts/yarn/home.nix index 1d145a3..6fa8552 100644 --- a/hosts/yarn/home.nix +++ b/hosts/yarn/home.nix @@ -7,8 +7,8 @@ }: { imports = [ - ./gui.nix - ./desktop.nix + ../../home/profiles/gui.nix + ../../home/profiles/desktop.nix inputs.json2steamshortcut.homeModules.default ]; @@ -31,7 +31,7 @@ services.steam-shortcuts = { enable = true; overwriteExisting = true; - steamUserId = lib.strings.toInt (lib.strings.trim (builtins.readFile ./secrets/steam-user-id)); + steamUserId = lib.strings.toInt (lib.strings.trim (builtins.readFile ../../secrets/home/steam-user-id)); shortcuts = [ { AppName = "Prism Launcher"; diff --git a/lib/default.nix b/lib/default.nix index 2d85360..413c9c3 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -2,9 +2,10 @@ inputs, pkgs, service_configs, + lib ? inputs.nixpkgs-stable.lib, ... }: -inputs.nixpkgs.lib.extend ( +lib.extend ( final: prev: let lib = prev; diff --git a/modules/age-secrets.nix b/modules/age-secrets.nix index b38ba82..8980363 100644 --- a/modules/age-secrets.nix +++ b/modules/age-secrets.nix @@ -15,7 +15,7 @@ # ZFS encryption key # path is set to /etc/zfs-key to match the ZFS dataset keylocation property zfs-key = { - file = ../secrets/zfs-key.age; + file = ../secrets/server/zfs-key.age; mode = "0400"; owner = "root"; group = "root"; @@ -24,7 +24,7 @@ # Secureboot keys archive secureboot-tar = { - file = ../secrets/secureboot.tar.age; + file = ../secrets/server/secureboot.tar.age; mode = "0400"; owner = "root"; group = "root"; @@ -32,7 +32,7 @@ # System passwords hashedPass = { - file = ../secrets/hashedPass.age; + file = ../secrets/server/hashedPass.age; mode = "0400"; owner = "root"; group = "root"; @@ -40,7 +40,7 @@ # Service authentication caddy_auth = { - file = ../secrets/caddy_auth.age; + file = ../secrets/server/caddy_auth.age; mode = "0400"; owner = "caddy"; group = "caddy"; @@ -48,7 +48,7 @@ # Njalla API token (NJALLA_API_TOKEN=...) for Caddy DNS-01 challenge njalla-api-token-env = { - file = ../secrets/njalla-api-token-env.age; + file = ../secrets/server/njalla-api-token-env.age; mode = "0400"; owner = "caddy"; group = "caddy"; @@ -56,21 +56,21 @@ # ddns-updater config.json with Njalla provider credentials ddns-updater-config = { - file = ../secrets/ddns-updater-config.age; + file = ../secrets/server/ddns-updater-config.age; mode = "0400"; owner = "ddns-updater"; group = "ddns-updater"; }; jellyfin-api-key = { - file = ../secrets/jellyfin-api-key.age; + file = ../secrets/server/jellyfin-api-key.age; mode = "0400"; owner = "root"; group = "root"; }; slskd_env = { - file = ../secrets/slskd_env.age; + file = ../secrets/server/slskd_env.age; mode = "0500"; owner = config.services.slskd.user; group = config.services.slskd.group; @@ -78,7 +78,7 @@ # Network configuration wg0-conf = { - file = ../secrets/wg0.conf.age; + file = ../secrets/server/wg0.conf.age; mode = "0400"; owner = "root"; group = "root"; @@ -86,14 +86,14 @@ # ntfy-alerts secrets (group-readable for CI runner notifications) ntfy-alerts-topic = { - file = ../secrets/ntfy-alerts-topic.age; + file = ../secrets/server/ntfy-alerts-topic.age; mode = "0440"; owner = "root"; group = "gitea-runner"; }; ntfy-alerts-token = { - file = ../secrets/ntfy-alerts-token.age; + file = ../secrets/server/ntfy-alerts-token.age; mode = "0440"; owner = "root"; group = "gitea-runner"; @@ -101,19 +101,19 @@ # Firefox Sync server secrets (SYNC_MASTER_SECRET) firefox-syncserver-env = { - file = ../secrets/firefox-syncserver-env.age; + file = ../secrets/server/firefox-syncserver-env.age; mode = "0400"; }; # MollySocket env (MOLLY_VAPID_PRIVKEY + MOLLY_ALLOWED_UUIDS) mollysocket-env = { - file = ../secrets/mollysocket-env.age; + file = ../secrets/server/mollysocket-env.age; mode = "0400"; }; # Murmur (Mumble) server password murmur-password-env = { - file = ../secrets/murmur-password-env.age; + file = ../secrets/server/murmur-password-env.age; mode = "0400"; owner = "murmur"; group = "murmur"; @@ -121,7 +121,7 @@ # Coturn static auth secret coturn-auth-secret = { - file = ../secrets/coturn-auth-secret.age; + file = ../secrets/server/coturn-auth-secret.age; mode = "0400"; owner = "turnserver"; group = "turnserver"; @@ -129,7 +129,7 @@ # Matrix (continuwuity) registration token matrix-reg-token = { - file = ../secrets/matrix-reg-token.age; + file = ../secrets/server/matrix-reg-token.age; mode = "0400"; owner = "continuwuity"; group = "continuwuity"; @@ -138,7 +138,7 @@ # Matrix (continuwuity) TURN secret — same secret as coturn-auth-secret, # decrypted separately so continuwuity can read it with its own ownership matrix-turn-secret = { - file = ../secrets/coturn-auth-secret.age; + file = ../secrets/server/coturn-auth-secret.age; mode = "0400"; owner = "continuwuity"; group = "continuwuity"; @@ -146,7 +146,7 @@ # CI deploy SSH key ci-deploy-key = { - file = ../secrets/ci-deploy-key.age; + file = ../secrets/server/ci-deploy-key.age; mode = "0400"; owner = "gitea-runner"; group = "gitea-runner"; @@ -154,7 +154,7 @@ # Git-crypt symmetric key for dotfiles repo git-crypt-key-dotfiles = { - file = ../secrets/git-crypt-key-dotfiles.age; + file = ../secrets/server/git-crypt-key-dotfiles.age; mode = "0400"; owner = "gitea-runner"; group = "gitea-runner"; @@ -162,7 +162,7 @@ # Git-crypt symmetric key for server-config repo git-crypt-key-server-config = { - file = ../secrets/git-crypt-key-server-config.age; + file = ../secrets/server/git-crypt-key-server-config.age; mode = "0400"; owner = "gitea-runner"; group = "gitea-runner"; @@ -170,7 +170,7 @@ # Gitea Actions runner registration token gitea-runner-token = { - file = ../secrets/gitea-runner-token.age; + file = ../secrets/server/gitea-runner-token.age; mode = "0400"; owner = "gitea-runner"; group = "gitea-runner"; @@ -178,7 +178,7 @@ # llama-cpp API key for bearer token auth llama-cpp-api-key = { - file = ../secrets/llama-cpp-api-key.age; + file = ../secrets/server/llama-cpp-api-key.age; mode = "0400"; owner = "root"; group = "root"; @@ -186,7 +186,7 @@ # Harmonia binary cache signing key harmonia-sign-key = { - file = ../secrets/harmonia-sign-key.age; + file = ../secrets/server/harmonia-sign-key.age; mode = "0400"; owner = "harmonia"; group = "harmonia"; @@ -194,7 +194,7 @@ # Caddy basic auth for nix binary cache (separate from main caddy_auth) nix-cache-auth = { - file = ../secrets/nix-cache-auth.age; + file = ../secrets/server/nix-cache-auth.age; mode = "0400"; owner = "caddy"; group = "caddy"; diff --git a/modules/common-doas.nix b/modules/common-doas.nix new file mode 100644 index 0000000..2e0875a --- /dev/null +++ b/modules/common-doas.nix @@ -0,0 +1,15 @@ +{ username, ... }: +{ + # doas replaces sudo on every host + security = { + doas.enable = true; + sudo.enable = false; + doas.extraRules = [ + { + users = [ username ]; + keepEnv = true; + persist = true; + } + ]; + }; +} diff --git a/modules/common-nix.nix b/modules/common-nix.nix new file mode 100644 index 0000000..458a947 --- /dev/null +++ b/modules/common-nix.nix @@ -0,0 +1,22 @@ +{ lib, ... }: +{ + # Common Nix daemon settings. Host-specific overrides (binary cache substituters, + # gc retention) live in the host's default.nix. + nix = { + optimise.automatic = true; + + gc = { + automatic = true; + dates = "weekly"; + # Default retention: override per-host via lib.mkForce if different. + options = lib.mkDefault "--delete-older-than 30d"; + }; + + settings = { + experimental-features = [ + "nix-command" + "flakes" + ]; + }; + }; +} diff --git a/modules/common-shell-fish.nix b/modules/common-shell-fish.nix new file mode 100644 index 0000000..13d0505 --- /dev/null +++ b/modules/common-shell-fish.nix @@ -0,0 +1,16 @@ +{ pkgs, lib, ... }: +{ + # https://nixos.wiki/wiki/Fish#Setting_fish_as_your_shell + # Login shells stay bash but immediately `exec fish` so fish is the effective shell + # without breaking scripts that hardcode #!/bin/bash. + programs.fish.enable = true; + programs.bash = { + interactiveShellInit = '' + if [[ $(${pkgs.procps}/bin/ps --no-header --pid=$PPID --format=comm) != "fish" && -z ''${BASH_EXECUTION_STRING} ]] + then + shopt -q login_shell && LOGIN_OPTION='--login' || LOGIN_OPTION="" + exec ${lib.getExe pkgs.fish} $LOGIN_OPTION + fi + ''; + }; +} diff --git a/legacy/dotfiles/system/common.nix b/modules/desktop-common.nix similarity index 88% rename from legacy/dotfiles/system/common.nix rename to modules/desktop-common.nix index a056908..1016441 100644 --- a/legacy/dotfiles/system/common.nix +++ b/modules/desktop-common.nix @@ -4,17 +4,21 @@ pkgs, lib, username, - system, - hostname, inputs, niri-package, ... }: { imports = [ - ./vm.nix - ./steam.nix - ./networking.nix + # shared across all hosts + ./common-doas.nix + ./common-shell-fish.nix + ./common-nix.nix + + # desktop-only modules + ./desktop-vm.nix + ./desktop-steam.nix + ./desktop-networkmanager.nix inputs.disko.nixosModules.disko inputs.lanzaboote.nixosModules.lanzaboote @@ -59,7 +63,7 @@ #!/usr/bin/env sh rm -fr ${config.boot.lanzaboote.pkiBundle} || true mkdir -p ${config.boot.lanzaboote.pkiBundle} - ${lib.getExe pkgs.gnutar} xf ${./secrets/secureboot.tar} -C ${config.boot.lanzaboote.pkiBundle} + ${lib.getExe pkgs.gnutar} xf ${../secrets/desktop/secureboot.tar} -C ${config.boot.lanzaboote.pkiBundle} chown -R root:wheel ${config.boot.lanzaboote.pkiBundle} chmod -R 500 ${config.boot.lanzaboote.pkiBundle} ''; @@ -67,31 +71,14 @@ swapDevices = [ ]; - nix = { - # optimize the store - optimise.automatic = true; - - # auto garbage collect old generations - gc = { - automatic = true; - dates = "weekly"; - options = "--delete-older-than 30d"; - }; - - settings = { - # enable flakes! - experimental-features = [ - "nix-command" - "flakes" - ]; - - # Use muffin server as a binary cache - substituters = [ "https://nix-cache.sigkill.computer" ]; - trusted-public-keys = [ - "nix-cache.sigkill.computer-1:ONtQC9gUjL+2yNgMWB68NudPySXhyzJ7I3ra56/NPgk=" - ]; - netrc-file = "${./secrets/nix-cache-netrc}"; - }; + # Desktop-specific Nix cache — muffin serves it, desktops consume. + # Base nix settings (optimise, gc, experimental-features) come from common-nix.nix. + nix.settings = { + substituters = [ "https://nix-cache.sigkill.computer" ]; + trusted-public-keys = [ + "nix-cache.sigkill.computer-1:ONtQC9gUjL+2yNgMWB68NudPySXhyzJ7I3ra56/NPgk=" + ]; + netrc-file = "${../secrets/desktop/nix-cache-netrc}"; }; # cachyos kernel overlay @@ -380,20 +367,6 @@ # EST time.timeZone = "America/New_York"; - security = { - # lets use doas and not sudo! - doas.enable = true; - sudo.enable = false; - # Configure doas - doas.extraRules = [ - { - users = [ username ]; - keepEnv = true; - persist = true; - } - ]; - }; - # Select internationalisation properties. i18n.defaultLocale = "en_US.UTF-8"; @@ -430,7 +403,7 @@ "adbusers" ]; # TODO! this is really bad :( I should really figure out how to do proper secrets management - hashedPasswordFile = "${./secrets/password-hash}"; + hashedPasswordFile = "${../secrets/desktop/password-hash}"; }; services.gvfs.enable = true; @@ -473,18 +446,6 @@ # wayland with electron/chromium applications environment.sessionVariables.NIXOS_OZONE_WL = "1"; - # https://nixos.wiki/wiki/Fish#Setting_fish_as_your_shell - programs.fish.enable = true; - programs.bash = { - interactiveShellInit = '' - if [[ $(${pkgs.procps}/bin/ps --no-header --pid=$PPID --format=comm) != "fish" && -z ''${BASH_EXECUTION_STRING} ]] - then - shopt -q login_shell && LOGIN_OPTION='--login' || LOGIN_OPTION="" - exec ${lib.getExe pkgs.fish} $LOGIN_OPTION - fi - ''; - }; - # port 53317 for localsend networking.firewall.allowedUDPPorts = [ 53317 ]; networking.firewall.allowedTCPPorts = [ 53317 ]; diff --git a/services/matrix/livekit.nix b/services/matrix/livekit.nix index 99addec..99a5208 100644 --- a/services/matrix/livekit.nix +++ b/services/matrix/livekit.nix @@ -3,7 +3,7 @@ ... }: let - keyFile = ../../secrets/livekit_keys; + keyFile = ../../secrets/server/livekit_keys; in { services.livekit = { diff --git a/services/minecraft.nix b/services/minecraft.nix index ea201cf..895d9c5 100644 --- a/services/minecraft.nix +++ b/services/minecraft.nix @@ -92,7 +92,7 @@ spawn-protection = 0; }; - whitelist = import ../secrets/minecraft-whitelist.nix; + whitelist = import ../secrets/server/minecraft-whitelist.nix; symlinks = { "mods" = pkgs.linkFarmFromDrvs "mods" ( diff --git a/tests/fail2ban-caddy.nix b/tests/fail2ban-caddy.nix index 54b11a9..3eb5296 100644 --- a/tests/fail2ban-caddy.nix +++ b/tests/fail2ban-caddy.nix @@ -17,7 +17,7 @@ pkgs.testers.runNixOSTest { }: { imports = [ - ../modules/security.nix + ../modules/server-security.nix ]; # Set up Caddy with basic auth (minimal config, no production stuff) diff --git a/tests/fail2ban-gitea.nix b/tests/fail2ban-gitea.nix index b9fe355..65d6119 100644 --- a/tests/fail2ban-gitea.nix +++ b/tests/fail2ban-gitea.nix @@ -53,7 +53,7 @@ pkgs.testers.runNixOSTest { }: { imports = [ - ../modules/security.nix + ../modules/server-security.nix giteaModule ]; diff --git a/tests/fail2ban-immich.nix b/tests/fail2ban-immich.nix index 04a8530..424b291 100644 --- a/tests/fail2ban-immich.nix +++ b/tests/fail2ban-immich.nix @@ -51,7 +51,7 @@ pkgs.testers.runNixOSTest { }: { imports = [ - ../modules/security.nix + ../modules/server-security.nix immichModule ]; diff --git a/tests/fail2ban-jellyfin.nix b/tests/fail2ban-jellyfin.nix index 69e06e2..18317c9 100644 --- a/tests/fail2ban-jellyfin.nix +++ b/tests/fail2ban-jellyfin.nix @@ -51,7 +51,7 @@ pkgs.testers.runNixOSTest { }: { imports = [ - ../modules/security.nix + ../modules/server-security.nix jellyfinModule ]; diff --git a/tests/fail2ban-ssh.nix b/tests/fail2ban-ssh.nix index 758820f..417e3c2 100644 --- a/tests/fail2ban-ssh.nix +++ b/tests/fail2ban-ssh.nix @@ -5,7 +5,7 @@ ... }: let - securityModule = import ../modules/security.nix; + securityModule = import ../modules/server-security.nix; sshModule = { diff --git a/tests/fail2ban-vaultwarden.nix b/tests/fail2ban-vaultwarden.nix index 5869a71..6345594 100644 --- a/tests/fail2ban-vaultwarden.nix +++ b/tests/fail2ban-vaultwarden.nix @@ -46,7 +46,7 @@ pkgs.testers.runNixOSTest { }: { imports = [ - ../modules/security.nix + ../modules/server-security.nix vaultwardenModule ]; diff --git a/tests/file-perms.nix b/tests/file-perms.nix index dd6b3b7..d31367c 100644 --- a/tests/file-perms.nix +++ b/tests/file-perms.nix @@ -5,7 +5,7 @@ ... }: let - testPkgs = pkgs.appendOverlays [ (import ../modules/overlays.nix) ]; + testPkgs = pkgs.appendOverlays [ (import ../lib/overlays.nix) ]; in testPkgs.testers.runNixOSTest { name = "file-perms test"; diff --git a/tests/minecraft.nix b/tests/minecraft.nix index 40dbfa9..8f8f061 100644 --- a/tests/minecraft.nix +++ b/tests/minecraft.nix @@ -22,7 +22,7 @@ let config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [ "minecraft-server" ]; overlays = [ inputs.nix-minecraft.overlay - (import ../modules/overlays.nix) + (import ../lib/overlays.nix) ]; }; in diff --git a/tests/ntfy-alerts.nix b/tests/ntfy-alerts.nix index c9b72cf..3104483 100644 --- a/tests/ntfy-alerts.nix +++ b/tests/ntfy-alerts.nix @@ -5,7 +5,7 @@ ... }: let - testPkgs = pkgs.appendOverlays [ (import ../modules/overlays.nix) ]; + testPkgs = pkgs.appendOverlays [ (import ../lib/overlays.nix) ]; in testPkgs.testers.runNixOSTest { name = "ntfy-alerts"; diff --git a/tests/zfs.nix b/tests/zfs.nix index a712ccc..107409b 100644 --- a/tests/zfs.nix +++ b/tests/zfs.nix @@ -7,7 +7,7 @@ }: let # Create pkgs with ensureZfsMounts overlay - testPkgs = pkgs.appendOverlays [ (import ../modules/overlays.nix) ]; + testPkgs = pkgs.appendOverlays [ (import ../lib/overlays.nix) ]; in testPkgs.testers.runNixOSTest { name = "zfs test";