diff --git a/.gitignore b/.gitignore index 7b5bb2c..ade4808 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ __pycache__/ result result-* -*.qcow2 diff --git a/common.nix b/common.nix index f8a72d5..4ad577c 100644 --- a/common.nix +++ b/common.nix @@ -123,7 +123,6 @@ in ./services/sabnzbd.nix ./services/forgejo-runner.nix ./services/code-server.nix - ./services/memos.nix ./services/redlib.nix ./services/hardware-health.nix ./services/wan-watchdog.nix @@ -202,9 +201,6 @@ in # live generation still has the old one: eval and build right here. update2 = localUpdate; clean = "sudo nix-collect-garbage -d"; - # Throw away the VM's disk first so every run is a real first boot — - # greeter, Plasma first-run, the lot. Builds into ./result in $PWD. - kaylavm = "rm -f FredOS-Kayla.qcow2 && nixos-rebuild build-vm --refresh --flake git+https://forg.gregersen.it/rope/nixos#kayla-vm && ./result/bin/run-FredOS-Kayla-vm"; ll = "ls -alh"; clear = "command clear"; reboot = "bash -c 'if [ \"$(hostname)\" = \"FredOS-Mediaserver\" ]; then read -r -p \"Reboot $(hostname)? [y/N] \" confirm; case \"$confirm\" in [Yy]) ;; *) exit 0 ;; esac; fi; sudo systemctl reboot'"; @@ -229,6 +225,16 @@ in # optimise` by hand there if the store ever gets fat. nix.settings.auto-optimise-store = !isMacbook; + # Weekly GC keeps two weeks of rollbacks; min-free/max-free also collect + # mid-build if the disk is about to fill. + nix.gc = { + automatic = true; + dates = "weekly"; + options = "--delete-older-than 14d"; + }; + nix.settings.min-free = 5 * 1024 * 1024 * 1024; + nix.settings.max-free = 20 * 1024 * 1024 * 1024; + # Offload builds to the media server. Excluded on the server itself to # avoid a pointless SSH round-trip to localhost. nix.distributedBuilds = diff --git a/docs/deployment.md b/docs/deployment.md index d9ce23f..9766c29 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -68,4 +68,4 @@ When ready to activate that checkout, invoke the built command explicitly with ` `python3 scripts/deploy-check.py` exercises failure after partial runtime activation, health failure, partial profile/boot commit, failed boot restoration, failed recovery health, timer setup/cancellation failures, stale timers, boot recovery, a full state filesystem, and missing candidate recovery support. It also tests actual process-level lock exclusion, independent recovery stopping a hung fixture worker before taking its lock, and subprocess timeout cleanup of descendants. Health-probe failures use mocked services, networking and sockets. -The deployment wrapper is built with Nix's shell checks, and physical host toplevels are evaluated. The existing `mediaserver-vm` root-filesystem/bootloader issue still prevents a clean whole-flake check. These tests use temporary filesystem/system fixtures; they do not create live deployment timers, reboot machines, or activate a real generation. A controlled server rollout remains the final operational validation. +The deployment wrapper is built with Nix's shell checks, and physical host toplevels are evaluated. These tests use temporary filesystem/system fixtures; they do not create live deployment timers, reboot machines, or activate a real generation. A controlled server rollout remains the final operational validation. diff --git a/docs/failure-path-fixes.md b/docs/failure-path-fixes.md index 34fde89..132f2a1 100644 --- a/docs/failure-path-fixes.md +++ b/docs/failure-path-fixes.md @@ -17,7 +17,7 @@ All four physical hosts were evaluated. The authentication bundle and media-read - The actual lock QML on a private headless Sway compositor: coverage is confirmed; the helper fails when IPC disappears or reports that coverage never became secure. - The generated media-readiness script in a private mount namespace: missing pool, missing branch and subsequently lost branch all fail; a complete mounted fixture receives its directories; the underlying root directory stays empty. -The broader `nix flake check --no-build` still fails on the existing `mediaserver-vm` output's root-filesystem/bootloader assertions. The configured nixos MCP server did not complete initialization; options were checked against the pinned module sources and host evaluations instead. +The broader `nix flake check --no-build` originally failed on the since-removed `mediaserver-vm` output. The configured nixos MCP server did not complete initialization; options were checked against the pinned module sources and host evaluations instead. These checks do not substitute for a physical login, keyring unlock, lid-close/resume or disk-disconnection test. No configuration has been activated, no production service has been restarted, and no production firewall or mount has been changed. diff --git a/flake.lock b/flake.lock index ebb679a..f679bab 100644 --- a/flake.lock +++ b/flake.lock @@ -298,22 +298,6 @@ } }, "nixpkgs_4": { - "locked": { - "lastModified": 1785386831, - "narHash": "sha256-sPS3CaXH8RAT3FZRuy4VcV47iuYIWMMfa0GbyJKC3o4=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "21ea275a7c46aef9d4d6ddc962e6d562e9d94183", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixos-26.05", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs_5": { "locked": { "lastModified": 1789286504, "narHash": "sha256-eiEK7cKZORNEvX0GeF3RtNEF/JXhgf2RqSp3230q13E=", @@ -409,7 +393,9 @@ "firefox-gnome-theme": "firefox-gnome-theme", "flake-parts": "flake-parts_2", "gnome-shell": "gnome-shell", - "nixpkgs": "nixpkgs_4", + "nixpkgs": [ + "nixpkgs" + ], "nur": "nur", "systems": "systems", "tinted-kitty": "tinted-kitty", @@ -516,7 +502,7 @@ "home-manager": [ "home-manager" ], - "nixpkgs": "nixpkgs_5" + "nixpkgs": "nixpkgs_4" }, "locked": { "lastModified": 1789702344, diff --git a/flake.nix b/flake.nix index e6178d6..089b91f 100644 --- a/flake.nix +++ b/flake.nix @@ -30,7 +30,10 @@ flake = false; }; - stylix.url = "github:nix-community/stylix/release-26.05"; + stylix = { + url = "github:nix-community/stylix/release-26.05"; + inputs.nixpkgs.follows = "nixpkgs"; + }; }; outputs = { self @@ -87,16 +90,6 @@ FredOS-Mediaserver = mkHost "FredOS-Mediaserver" [ ./hosts/hardware/FredOS-Mediaserver.nix ]; FredOS-Macbook = mkHost "FredOS-Macbook" [ ./hosts/hardware/FredOS-Macbook.nix ]; FredOS-Kayla = mkHost "FredOS-Kayla" [ ./hosts/hardware/FredOS-Kayla.nix ]; - - # Mediaserver's services on fake hardware, for testing from-scratch - # bring-up (arr-interconnect especially) in a throwaway VM: - # nixos-rebuild build-vm --flake .#mediaserver-vm - mediaserver-vm = mkHost "FredOS-Mediaserver" [ ./hosts/hardware/vm.nix ]; - - # Kayla's desktop in a window, for trying the Plasma session out before - # her machine exists. - # nixos-rebuild build-vm --flake .#kayla-vm - kayla-vm = mkHost "FredOS-Kayla" [ ./hosts/hardware/kayla-vm.nix ]; }; }; } diff --git a/hosts/hardware/kayla-vm.nix b/hosts/hardware/kayla-vm.nix deleted file mode 100644 index 4bd167e..0000000 --- a/hosts/hardware/kayla-vm.nix +++ /dev/null @@ -1,24 +0,0 @@ -#./hosts/hardware/kayla-vm.nix -# Stand-in "hardware" for `nixos-rebuild build-vm --flake .#kayla-vm`. -# -# Keeps hostName = FredOS-Kayla so hosts/FredOS-Kayla.nix switches on and you -# get the real Plasma session, stylix dark colours and app set — just to see -# what it feels like before committing real hardware to it. qemu-vm.nix -# supplies "/" and the bootloader, so no fileSystems here. -{ lib, ... }: -{ - networking.hostName = "FredOS-Kayla"; - system.stateVersion = "25.11"; - nixpkgs.hostPlatform = "x86_64-linux"; - - # The real user has no password until `passwd kayla` on first boot, which - # would leave the VM stuck at SDDM. VM-only, hence not in settings/users.nix. - users.users.kayla.initialPassword = "kayla"; - - virtualisation.vmVariant.virtualisation = { - memorySize = 8192; - cores = 4; - diskSize = 8192; # host store is mounted in, this is just /home + state - graphics = true; - }; -} diff --git a/hosts/hardware/vm.nix b/hosts/hardware/vm.nix deleted file mode 100644 index 4835766..0000000 --- a/hosts/hardware/vm.nix +++ /dev/null @@ -1,50 +0,0 @@ -#./hosts/hardware/vm.nix -# Stand-in "hardware" for `nixos-rebuild build-vm --flake .#mediaserver-vm`. -# -# Keeps hostName = FredOS-Mediaserver so every hostname-gated module under -# services/ switches on, but throws away the real disks, NICs and GPU. The -# point is watching arr-interconnect wire a *fresh* stack from empty state — -# something the live host can't demonstrate anymore, since its /var/lib is -# already populated and the script is a reconciler, not an installer. -{ lib, ... }: -{ - networking.hostName = "FredOS-Mediaserver"; - system.stateVersion = "25.11"; - nixpkgs.hostPlatform = "x86_64-linux"; - - # qemu-vm.nix supplies "/" and the bootloader. Only the media pool has to - # exist by hand, so the *arr root folders resolve — contents are throwaway. - fileSystems."/mnt/storage" = { - device = "tmpfs"; - fsType = "tmpfs"; - options = [ "mode=0777" ]; - }; - - # --- Undo router.nix. This VM is not a router. --- - # Its networkd units match NIC names pinned to the real MACs, so the VM's - # NIC matches nothing and comes up unconfigured; its nft input chain is - # policy-drop, which would eat the forwarded ports below. Scripted DHCP on - # QEMU's user-mode NIC replaces both. - networking.useNetworkd = lib.mkForce false; - networking.nftables.tables = lib.mkForce { }; - services.dnsmasq.enable = lib.mkForce false; - - virtualisation.vmVariant.virtualisation = { - memorySize = 8192; - cores = 4; - diskSize = 32768; # docker images for shelfarr/profilarr are not small - graphics = false; # serial console; ctrl-a x to quit - # Host side is offset by 10000 so the VM can run on the mediaserver - # itself without fighting the live services for those ports. - forwardPorts = [ - { from = "host"; host.port = 10022; guest.port = 22; } # ssh - { from = "host"; host.port = 18989; guest.port = 8989; } # sonarr - { from = "host"; host.port = 17878; guest.port = 7878; } # radarr - { from = "host"; host.port = 19696; guest.port = 9696; } # prowlarr - { from = "host"; host.port = 16767; guest.port = 6767; } # bazarr - { from = "host"; host.port = 18080; guest.port = 8080; } # qbittorrent - { from = "host"; host.port = 18096; guest.port = 8096; } # jellyfin - { from = "host"; host.port = 15055; guest.port = 5055; } # seerr - ]; - }; -} diff --git a/readme.md b/readme.md index 1066b49..6a58116 100644 --- a/readme.md +++ b/readme.md @@ -46,7 +46,6 @@ Flake-based NixOS configuration for three machines, built and deployed directly │ ├── game-servers.nix # Dockerised 7 Days to Die servers │ ├── go2rtc.nix # Camera/RTSP streaming │ ├── jellyfin.nix # Media server -│ ├── memos.nix # Flatnotes notes app (container) │ ├── nginx.nix # Reverse proxy + ACME wildcard cert via Cloudflare DNS-01 │ ├── profilarr.nix # Quality profile manager for *arr apps (container) │ ├── prowlarr.nix # Indexer manager diff --git a/services/authelia.nix b/services/authelia.nix index 8ecaa3f..0b411be 100644 --- a/services/authelia.nix +++ b/services/authelia.nix @@ -41,7 +41,6 @@ { domain = "shelfarr.nordhammer.it"; policy = "one_factor"; } { domain = "sabnzbd.nordhammer.it"; policy = "one_factor"; } { domain = "code.nordhammer.it"; policy = "one_factor"; } - { domain = "notes.nordhammer.it"; policy = "one_factor"; } { domain = "reddit.nordhammer.it"; policy = "one_factor"; } { domain = "panel.nordhammer.it"; policy = "one_factor"; } ]; diff --git a/services/game-servers.nix b/services/game-servers.nix index 0df3685..0950331 100644 --- a/services/game-servers.nix +++ b/services/game-servers.nix @@ -1,7 +1,7 @@ { config, pkgs, lib, ... }: { config = lib.mkIf (config.networking.hostName == "FredOS-Mediaserver") { - # Kept even with 7DTD commented out: memos/profilarr containers rely on these. + # Kept even with 7DTD commented out: profilarr/shelfarr/pelican containers rely on these. virtualisation.docker.enable = true; virtualisation.oci-containers.backend = "docker"; diff --git a/services/memos.nix b/services/memos.nix deleted file mode 100644 index 6bae24b..0000000 --- a/services/memos.nix +++ /dev/null @@ -1,20 +0,0 @@ -# services/memos.nix — Lightweight self-hosted notes at notes.nordhammer.it -# Uses Flatnotes with auth disabled — Authelia is the only gate. -{ config, lib, ... }: -{ - config = lib.mkIf (config.networking.hostName == "FredOS-Mediaserver") { - - virtualisation.oci-containers.containers.flatnotes = { - image = "dullage/flatnotes:latest"; - ports = [ "127.0.0.1:5230:8080" ]; - volumes = [ "/var/lib/flatnotes:/app/data" ]; - environment = { - FLATNOTES_AUTH_TYPE = "none"; - }; - }; - - systemd.tmpfiles.rules = [ - "d /var/lib/flatnotes 0750 root root -" - ]; - }; -} diff --git a/services/nginx.nix b/services/nginx.nix index f09c03f..70c4df3 100644 --- a/services/nginx.nix +++ b/services/nginx.nix @@ -182,7 +182,6 @@ in add_header Cache-Control "no-store" always; ''; }; - "notes.nordhammer.it" = protectedProxy 5230; "reddit.nordhammer.it" = protectedProxy 8087; # Pelican game panel. Can't turn off its own login (no such option