settings/theming.nix renders a full config set per wallpaper (GTK, ghostty, btop, hyprland, vesktop, zen, folder icons) from its own base16 palette; theme-apply swaps them in and reloads what can reload. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
258 lines
11 KiB
Nix
258 lines
11 KiB
Nix
# Common.nix
|
|
{ config, pkgs, lib, inputs, ... }:
|
|
|
|
let
|
|
isMacbook = config.networking.hostName == "FredOS-Macbook";
|
|
|
|
# nom's Haskell renderer measured 91% CPU during a rebuild — a full core of
|
|
# the Macbook's four, spent drawing a progress tree, while nix itself wanted
|
|
# 146%. Plain `-L` output there; the other hosts can afford the pretty one.
|
|
buildLog = lib.optionalString (!isMacbook)
|
|
" --log-format internal-json 2>&1 | nom --json";
|
|
|
|
flake = "git+https://forg.gregersen.it/rope/nixos";
|
|
|
|
# Now that the clients only copy, the copy is the whole wait. nix's own
|
|
# --log-format bar prints counters but no gauge, so render one from the json
|
|
# log: activity type 103 is the top-level copy-paths, and its type-105
|
|
# progress results carry [done, expected, running, failed] path counts.
|
|
# Messages at warning level and above still go through to stderr — otherwise
|
|
# folding nix's stderr into the filter would swallow the errors too.
|
|
copyBar = pkgs.writeShellScriptBin "nix-copy-bar" ''
|
|
set -o pipefail
|
|
${pkgs.jq}/bin/jq -Rr --unbuffered '
|
|
ltrimstr("@nix ") | fromjson? // empty
|
|
| if .action == "start" and .type == 103 then "A \(.id)"
|
|
elif .action == "result" and .type == 105 then "P \(.id) \(.fields[0]) \(.fields[1])"
|
|
elif .action == "msg" and .level <= 2 then "M \(.msg)"
|
|
else empty end' |
|
|
{
|
|
blocks='########################################'
|
|
drawn=
|
|
total=0
|
|
while read -r tag rest; do
|
|
case "$tag" in
|
|
A) copy_id=$rest ;;
|
|
M) [ -n "$drawn" ] && printf '\n' && drawn=
|
|
printf '%s\n' "$rest" >&2 ;;
|
|
P) set -- $rest
|
|
[ "$1" = "''${copy_id:-}" ] && [ "''${3:-0}" -gt 0 ] || continue
|
|
total=$3
|
|
pct=$(( $2 * 100 / $3 ))
|
|
printf '\r[%-40s] %3d%% %d/%d paths' \
|
|
"''${blocks:0:$(( pct * 40 / 100 ))}" "$pct" "$2" "$3"
|
|
drawn=1 ;;
|
|
esac
|
|
done
|
|
# `if`, not `[ … ] && …`: with nothing to copy the bar never draws, and a
|
|
# bare test as the last statement would exit 1 and break the caller's
|
|
# && chain. A real copy failure still surfaces via the caller's pipefail.
|
|
#
|
|
# nix stops emitting type-105 results once the last path lands, so the
|
|
# bar's last live frame is always one path short. Redraw it full on EOF.
|
|
# An error would have cleared $drawn via the M branch first, so this only
|
|
# fires on a copy that actually finished.
|
|
if [ -n "$drawn" ]; then
|
|
printf '\r[%-40s] 100%% %d/%d paths\n' "$blocks" "$total" "$total"
|
|
fi
|
|
}
|
|
'';
|
|
|
|
post = "nvd diff $OLD_SYSTEM /run/current-system && (command -v record-update &>/dev/null && record-update $OLD_SYSTEM /run/current-system || true)";
|
|
|
|
# The server evaluates and builds for itself.
|
|
localUpdate = "bash -c 'set -o pipefail && OLD_SYSTEM=$(readlink /run/current-system) && sudo nixos-rebuild switch $@ --refresh --flake ${flake} -L${buildLog} && ${post}' --";
|
|
|
|
# The clients offload the *evaluation* too, not just the build. nixos-rebuild
|
|
# always evaluates on the machine it runs on (--build-host ships the
|
|
# derivation, not the expression), so the only way to move eval off a client
|
|
# is to run nix on the server over SSH and hand the resulting closure to
|
|
# --store-path, which skips eval and build entirely. Same lantian substituter
|
|
# as services/prebuild.nix, and --out-link keeps a gc root on the server so
|
|
# `clean` there can't reap the closure mid-copy. SSH and the copy run as
|
|
# fred, not root: root has no key on the server, and require-sigs = false
|
|
# below is a daemon setting, so the daemon takes the unsigned closure from an
|
|
# untrusted user just fine. Only the activation needs sudo.
|
|
remoteUpdate = "bash -c 'set -o pipefail && OLD_SYSTEM=$(readlink /run/current-system) && NEW=$(ssh fred@nordhammer.it \"nix build --refresh --print-out-paths --out-link prebuild-${config.networking.hostName} --option extra-substituters https://attic.xuyh0120.win/lantian --option extra-trusted-public-keys lantian:EeAUQ+W+6r7EtwnmYjeVwx5kOGEBpjlBfPlzGlTNvHc= ${flake}#nixosConfigurations.${config.networking.hostName}.config.system.build.toplevel\") && nix copy --from ssh://fred@nordhammer.it $NEW --log-format internal-json 2>&1 | ${copyBar}/bin/nix-copy-bar && sudo nixos-rebuild switch --no-reexec --store-path $NEW && ${post}'";
|
|
in
|
|
{
|
|
imports = [
|
|
# Host modules are imported per-host by mkHost in flake.nix.
|
|
|
|
# Generic settings #
|
|
./settings/desktop.nix
|
|
./settings/hyprland.nix
|
|
./settings/quickshell.nix
|
|
./settings/theming.nix
|
|
./settings/locale.nix
|
|
./settings/audio.nix
|
|
./settings/users.nix
|
|
./settings/shell.nix
|
|
./apps/zen.nix
|
|
|
|
# Services #
|
|
./services/server-permissions.nix
|
|
./services/game-servers.nix
|
|
./services/pelican.nix
|
|
# ./services/dr-server.nix
|
|
./services/qbittorrent-nox.nix
|
|
./services/nginx.nix
|
|
./services/go2rtc.nix
|
|
# ./services/frigate.nix
|
|
./services/sonarr.nix
|
|
./services/radarr.nix
|
|
./services/prowlarr.nix
|
|
./services/jellyfin.nix
|
|
./services/seerr.nix
|
|
./services/bazarr.nix
|
|
./services/bazarr-sync.nix
|
|
./services/cloudflare-ddns.nix
|
|
./services/authelia.nix
|
|
./services/homepage.nix
|
|
./services/arr-interconnect.nix
|
|
./services/profilarr.nix
|
|
./services/shelfarr.nix
|
|
./services/adguard.nix
|
|
./services/router.nix
|
|
./services/router-ui.nix
|
|
./services/crowdsec.nix
|
|
./services/service-health.nix
|
|
./services/sabnzbd.nix
|
|
./services/forgejo-runner.nix
|
|
./services/code-server.nix
|
|
./services/memos.nix
|
|
./services/hardware-health.nix
|
|
./services/wan-watchdog.nix
|
|
./services/prebuild.nix
|
|
];
|
|
|
|
### Make build time quicker
|
|
documentation.nixos.enable = false;
|
|
|
|
# Home Manager #
|
|
home-manager.useGlobalPkgs = true;
|
|
home-manager.useUserPackages = true;
|
|
home-manager.backupFileExtension = "hm-bak";
|
|
home-manager.extraSpecialArgs = { inherit inputs; };
|
|
home-manager.users.fred = import ./home-manager/fred.nix;
|
|
|
|
#############################################################################
|
|
# Kill all user processes on logout so systemd user services don't linger
|
|
# in broken states across sessions (e.g. waybar failing to start on re-login).
|
|
services.logind.settings.Login.KillUserProcesses = true;
|
|
|
|
# Shorten shutdown stop timeout to avoid long "stop job" waits
|
|
systemd.settings.Manager.DefaultTimeoutStopSec = "10s";
|
|
|
|
# Make boot time quicker
|
|
boot.loader.timeout = lib.mkDefault 5;
|
|
systemd.services.NetworkManager-wait-online.enable = false;
|
|
systemd.services.systemd-udev-settle.enable = false;
|
|
systemd.services.firewall = {
|
|
wantedBy = lib.mkForce [ ];
|
|
after = [ "multi-user.target" ];
|
|
};
|
|
|
|
boot.initrd.verbose = false;
|
|
#############################################################################
|
|
|
|
# Compressed in-memory swap as a safety net during local build storms.
|
|
# Cheap when idle; without it a transient OOM during an uncached build
|
|
# can stall AdGuard/Jellyfin to the point of freezing the box.
|
|
zramSwap = {
|
|
enable = true;
|
|
memoryPercent = 50;
|
|
};
|
|
|
|
# Use latest kernel
|
|
boot.kernelPackages = pkgs.linuxPackages_latest;
|
|
|
|
# Allow unfree packages
|
|
nixpkgs.config.allowUnfree = true;
|
|
|
|
# vesktop (multiple hosts) builds with pnpm via fetchPnpmDeps, which nixpkgs
|
|
# marks insecure (build-time only, hash-pinned FOD — not in PATH). broadcom-sta
|
|
# is Macbook-only Wi-Fi but allowing it everywhere is harmless (absent on others).
|
|
nixpkgs.config.allowInsecurePredicate = pkg:
|
|
lib.any (p: lib.hasPrefix p (lib.getName pkg)) [ "broadcom-sta" "pnpm" ];
|
|
|
|
# Flakes — nixos-rebuild self-enables these, but plain `nix eval` /
|
|
# `nix flake check` on the hosts need them too.
|
|
nix.settings.experimental-features = [ "nix-command" "flakes" ];
|
|
|
|
# Enable network-manager
|
|
networking.networkmanager.enable = true;
|
|
|
|
# Fish shell
|
|
programs.fish.enable = true;
|
|
users.defaultUserShell = pkgs.fish;
|
|
|
|
# Shell aliases (work in both bash and fish)
|
|
environment.shellAliases = {
|
|
update =
|
|
if config.networking.hostName == "FredOS-Mediaserver"
|
|
then localUpdate
|
|
else remoteUpdate;
|
|
# Fallback for when the server is down or the alias itself changed and the
|
|
# 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'";
|
|
};
|
|
|
|
# Add packages
|
|
environment.systemPackages = with pkgs; [
|
|
git
|
|
localsend
|
|
nvd
|
|
nix-output-monitor
|
|
jq
|
|
dnsutils
|
|
busybox
|
|
];
|
|
|
|
# Hard-link identical files in the store as new paths are added, so the
|
|
# store de-duplicates itself on every build instead of drifting.
|
|
# Off on the Macbook: it hashes and relinks every file the daemon writes,
|
|
# which lands on top of the substitution writes on a slow SATA blade —
|
|
# measured io pressure full avg60=57% during a rebuild. Run `nix store
|
|
# optimise` by hand there if the store ever gets fat.
|
|
nix.settings.auto-optimise-store = !isMacbook;
|
|
|
|
# Offload builds to the media server. Excluded on the server itself to
|
|
# avoid a pointless SSH round-trip to localhost.
|
|
nix.distributedBuilds =
|
|
lib.mkIf (config.networking.hostName != "FredOS-Mediaserver") true;
|
|
|
|
nix.buildMachines =
|
|
lib.mkIf (config.networking.hostName != "FredOS-Mediaserver") [{
|
|
hostName = "nordhammer.it";
|
|
systems = [ "x86_64-linux" "i686-linux" ];
|
|
sshUser = "fred";
|
|
sshKey = "/root/.ssh/id_ed25519";
|
|
maxJobs = 4;
|
|
supportedFeatures = [ "nixos-test" "benchmark" "big-parallel" "kvm" ];
|
|
}];
|
|
|
|
# nix.buildMachines runs from the daemon, i.e. as root, so it needs root's own
|
|
# key — fred's doesn't count, and ssh refuses a key file owned by another user
|
|
# outright. Copying fred's (root can read it anyway, so this leaks nothing new)
|
|
# beats committing a private key to a repo the hosts fetch over plain HTTPS.
|
|
# C+ re-copies on every activation, so rotating fred's key rotates root's too.
|
|
systemd.tmpfiles.rules = lib.mkIf
|
|
(lib.elem config.networking.hostName [ "FredOS-Gaming" "FredOS-Macbook" ]) [
|
|
"d /root/.ssh 0700 root root -"
|
|
"C+ /root/.ssh/id_ed25519 0600 root root - /home/fred/.ssh/id_ed25519"
|
|
];
|
|
|
|
# Accept unsigned paths copied back from the remote builder.
|
|
nix.extraOptions =
|
|
lib.mkIf (config.networking.hostName != "FredOS-Mediaserver") ''
|
|
require-sigs = false
|
|
'';
|
|
}
|