Files
nixos-4screen/hosts/sam-4screen-desktop/configuration.nix
2026-03-05 17:06:32 +11:00

459 lines
11 KiB
Nix

{ config, pkgs, lib, ... }:
{
# Stop nouveau from binding the NVIDIA GPU
boot.blacklistedKernelModules = [ "nouveau" ];
boot.kernelParams = [
"nvidia.NVreg_PreserveVideoMemoryAllocations=1"
"intel_iommu=off"
"dev_mem_signed_off=1"
"modprobe.blacklist=nouveau"
"nouveau.modeset=0"
];
boot.extraModprobeConfig = ''
options nvidia_modeset vblank_sem_control=0
'';
imports = [
./hardware-configuration.nix
];
home-manager.backupFileExtension = "hm-bak";
# ---
# Nix (enable flakes on the installed system)
# ---
nix.settings.experimental-features = [ "nix-command" "flakes" ];
# ---
# Bootloader / kernel
# ---
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
# Known-good baseline from niri-4screen.md
# ---
# Identity
# ---
networking.hostName = "sam-4screen-desktop";
time.timeZone = "Australia/Sydney";
# ---
# Networking
# ---
networking.networkmanager.enable = true;
networking.interfaces.enp0s31f6.wakeOnLan.enable = true;
# Static IP policy:
# - Static IP must be on Wi-Fi SSID "Aussie Broadband 8729"
# - Do NOT store Wi-Fi secrets (PSK) in git-tracked Nix files
#
# Approach:
# - Connect to Wi-Fi normally via nmtui/nmcli/GUI (credentials stored locally by NM)
# - On activation, this dispatcher script enforces the static IP/DNS/gateway
# only for that SSID on interface wlp4s2.
environment.etc."NetworkManager/dispatcher.d/10-wlp4s2-static-ip-aussie-broadband-8729".source =
pkgs.writeShellScript "10-wlp4s2-static-ip-aussie-broadband-8729" ''
set -euo pipefail
IFACE="$1"
STATUS="$2"
TARGET_ID="Aussie Broadband 8729"
NMCLI="${pkgs.networkmanager}/bin/nmcli"
# Only touch the Wi-Fi interface you specified.
if [[ "$IFACE" != "wlp4s2" ]]; then
exit 0
fi
# Apply on pre-up/up so settings are in place as early as possible.
case "$STATUS" in
pre-up|up) ;;
*) exit 0 ;;
esac
# NetworkManager dispatcher provides these env vars.
# If they are missing, we can't safely target the right connection.
if [[ -z "''${CONNECTION_UUID:-}" ]]; then
exit 0
fi
# Ensure it is actually a Wi-Fi connection.
TYPE="$("$NMCLI" -g connection.type connection show "$CONNECTION_UUID" 2>/dev/null || true)"
if [[ "$TYPE" != "802-11-wireless" ]]; then
exit 0
fi
# Determine the connection "id" (name). This is typically the SSID, but not always.
CONN_ID="$("$NMCLI" -g connection.id connection show "$CONNECTION_UUID" 2>/dev/null || true)"
if [[ "$CONN_ID" != "$TARGET_ID" ]]; then
exit 0
fi
# Enforce your confirmed static IPv4 configuration.
"$NMCLI" connection modify "$CONNECTION_UUID" \
ipv4.method manual \
ipv4.addresses "192.168.20.27/24" \
ipv4.gateway "192.168.20.1" \
ipv4.dns "192.168.20.35 192.168.20.13" \
ipv4.ignore-auto-dns yes \
ipv6.method auto
# NOTE:
# This modifies the connection profile. If the connection is already "up",
# you may need to reconnect once for all settings to apply immediately:
# nmcli connection down "$CONNECTION_UUID"
# nmcli connection up "$CONNECTION_UUID"
exit 0
'';
environment.etc."NetworkManager/dispatcher.d/10-wlp4s2-static-ip-aussie-broadband-8729".mode = "0755";
# ---
# Users
# ---
users.users.sam.openssh.authorizedKeys.keys = [
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC3yxLtvBY6ClGliUfKA6eJLwUoCmagT5aZTrMsJnguUIch5lmdIGqz8gbGoy9ADEBaUpSA9nQsZZaXQE34t4xEqAzqra7hYogHR2uT6bIoML0ZWPiiMvSHOtknKcnfK/MBBPKjGYJSMhy2nzP02ORZs9D+DlfV8nVknsJa/G5jkgJXpA4za0bhxIZWJE7DPFF/utfSjxs6AB2wOzjfLPeuqSyUVq8nze+b1zf8baMBYVNxxpbHCyEXCvpuoxDw3JVk5Ha+hYyWb7563cCSeakrGM3f5048pFdPRGdbi4qcNsQV1KCBEd939VsOSNcY7Yr69zjnWzZNDCRBWxzlu+3DNY8I7ukHzxR78FPAQ6eCDR4druwIiGqlo5CE6S4P1h0uqKH2RW75qXoLb/orlwHKnAi3YOxDvELTQnxnLVgp6ahRpRu7ZqtLfJfwMLVcjTaExm8Yo4fLTJMgNv7U4bY/yRgenRzzRKHwL+kR/+SfY6KOH+8X0Jf0J653d9Z3VcVrXgo8r4vv6wx8CE605AfgQAsK9RN0cmtaDgd5GaXs3BOWMz+IeeiauSF2EXvPK5BsnvnZbBfATgpqsfi+iShoIDJ0+6mbwFqbczc17Ss9Z0mkSZyg3fsQHpWo9UFBNq5Jic1UGcjUKcvvZaB6/5bPvc6dVL/JKGcj0xqogr1paQ== root@core-ssh"
];
services.ollama.enable = true;
services.udisks2.enable = true;
services.dbus.enable = true;
programs.kdeconnect.enable = true;
services.gvfs.enable = true;
services.tumbler.enable = true;
programs.zsh.enable = true;
users.users.sam = {
isNormalUser = true;
description = "Sam";
extraGroups = [ "wheel" "networkmanager" "video" "render" "docker"];
shell = pkgs.zsh;
};
# greetd runs the greeter session as this user; it must exist.
users.groups.greeter = { };
users.users.greeter = {
isSystemUser = true;
group = "greeter";
home = "/var/lib/greeter";
createHome = true;
};
# ---
# SSH
# ---
services.openssh = {
enable = true;
openFirewall = true;
settings = {
PasswordAuthentication = true;
KbdInteractiveAuthentication = true;
PermitRootLogin = "no";
};
};
#services.openssh.enable = true;
# services.openssh.openFirewall = true;
# Defaulting to keys-only for safety. If you explicitly want password auth for the migration,
# flip this to true.
#services.openssh.settings.PasswordAuthentication = false;
# Explicitly enable firewall (keep SSH as the only opened port via openFirewall above).
networking.firewall.enable = true;
# ---
# dconf (helps portals/GTK apps)
# ---
programs.dconf.enable = true;
# Polkit is commonly required for a smooth experience with portals and desktop actions,
# especially in minimal Wayland sessions.
security.polkit.enable = true;
security.polkit.extraConfig = ''
polkit.addRule(function(action, subject) {
if (action.id == "org.freedesktop.login1.suspend" &&
subject.user == "sam") {
return polkit.Result.YES;
}
});
'';
# ---
# Firmware / microcode (stability)
# ---
hardware.enableRedistributableFirmware = true;
hardware.cpu.intel.updateMicrocode = true;
# ---
# OpenGL (important for NVIDIA Wayland apps)
# ---
hardware.graphics = {
enable = true;
enable32Bit = true;
};
# ---
# Audio (PipeWire)
# ---
security.rtkit.enable = true;
services.pipewire = {
enable = true;
pulse.enable = true;
alsa.enable = true;
alsa.support32Bit = true;
wireplumber.enable = true;
};
# ---
# Swap (zram; no hibernation)
# ---
zramSwap.enable = true;
# ---
# Docker (DEFER for now)
# ---
virtualisation.docker.enable = true;
# ---
# Mounts
# ---
# fileSystems."/data" = {
# device = "/dev/disk/by-uuid/27febd74-20aa-4a3a-92c1-6fdd1ad7e88e";
# fsType = "ext4";
# options = [ "nofail" "x-systemd.device-timeout=1s" ];
# };
systemd.services.wake-on-lan-resume = {
description = "Re-enable Wake-on-LAN after resume";
wantedBy = [ "post-resume.target" ];
after = [ "post-resume.target" ];
serviceConfig = {
Type = "oneshot";
ExecStart = "${pkgs.ethtool}/bin/ethtool -s enp0s31f6 wol g";
};
};
# Allow sam to suspend without password (for Home Assistant WOL)
security.sudo.extraRules = [
{
users = [ "sam" ];
commands = [
{
command = "${pkgs.systemd}/bin/systemctl suspend";
options = [ "NOPASSWD" ];
}
];
}
];
# This was the integral drive that died.
# fileSystems."/mnt/integral300" = {
# device = "/dev/disk/by-uuid/27febd74-20aa-4a3a-92c1-6fdd1ad7e88e";
# fsType = "ext4";
# options = [
# "nofail"
# "noauto"
# "x-systemd.automount"
# "x-systemd.idle-timeout=60"
# "x-systemd.device-timeout=30s"
# ];
# };
fileSystems."/mnt/backup" = {
device = "/dev/disk/by-uuid/0806B92006B90FA4";
fsType = "ntfs3";
options = [
"ro"
"nofail"
"noauto"
"x-systemd.automount"
"x-systemd.idle-timeout=60"
"x-systemd.device-timeout=30s"
"uid=1000"
"gid=100"
"umask=022"
];
};
fileSystems."/mnt/tempbackup" = {
device = "/dev/disk/by-uuid/4f9c4bd5-fea5-408f-9370-731fc095da3f";
fsType = "ext4";
options = [
"nofail"
"noauto"
"x-systemd.automount"
"x-systemd.idle-timeout=60"
"x-systemd.device-timeout=30s"
];
};
fileSystems."/mnt/xpsystemdrive" = {
device = "/dev/disk/by-uuid/82c994f1-9adb-49e4-ba1e-5b6e5ccbd49b";
fsType = "ext4";
options = [
"nofail"
"noauto"
"x-systemd.automount"
"x-systemd.idle-timeout=60"
"x-systemd.device-timeout=30s"
];
};
fileSystems."/mnt/smartdrive" = {
device = "/dev/disk/by-uuid/819c3228-c1af-4d8e-b507-2ad11b20cbef";
fsType = "ext4";
options = [
"nofail"
"noauto"
"x-systemd.automount"
"x-systemd.idle-timeout=60"
"x-systemd.device-timeout=30s"
];
};
systemd.tmpfiles.rules = lib.mkAfter [
"d /mnt/integral300 0755 root root -"
"d /mnt/backup 0755 root root -"
"d /mnt/tempbackup 0755 root root -"
"d /mnt/xpsystemdrive 0755 root root -"
"d /mnt/smartdrive 0755 root root -"
];
# ---
# Niri + login (greetd)
# ---
services.greetd = {
enable = true;
settings = {
default_session = {
user = "greeter";
command =
"${pkgs.greetd.tuigreet}/bin/tuigreet --time --remember --cmd ${
lib.escapeShellArg
"${pkgs.bash}/bin/bash -lc 'mkdir -p ~/.local/state; exec ${pkgs.niri}/bin/niri --session 2>~/.local/state/niri.log'"
}";
};
};
};
# Wayland portals (refine later if screencast needs a different backend)
xdg.portal = {
enable = true;
extraPortals = [
pkgs.xdg-desktop-portal-gtk
pkgs.xdg-desktop-portal-wlr
pkgs.kdePackages.xdg-desktop-portal-kde
];
};
fonts.packages = with pkgs; [
nerd-fonts.symbols-only
font-awesome
nerd-fonts.jetbrains-mono
];
# Minimal system packages needed for the session and core usability
environment.systemPackages = with pkgs; [
ethtool
swww
uv
(pkgs.callPackage ../../pkgs/agentpipe.nix { })
(pkgs.callPackage ../../pkgs/sidecar-bin.nix { })
xfce.thunar
xfce.tumbler
ffmpegthumbnailer
# Optional: ensure Qt apps look okay in Niri
kdePackages.qtwayland
kdePackages.qtsvg
poppler-utils
kdePackages.kdeconnect-kde
tmux
brave
vorta
borgbackup
libreoffice-fresh
filezilla
nushell
zed-editor
brave
swappy
nerd-fonts.symbols-only
font-awesome
nerd-fonts.jetbrains-mono
spotify
vlc
telegram-desktop
waybar
firefox
google-chrome
kitty
fuzzel
niri
greetd.tuigreet
xwayland
wl-clipboard
grim
slurp
];
services.tailscale.enable = true;
# ---
# NVIDIA (simple, first-boot stable config; PRIME tuning later)
# ---
services.xserver.videoDrivers = [ "nvidia" ];
hardware.nvidia = {
modesetting.enable = true;
nvidiaSettings = true;
nvidiaPersistenced = true;
open = false;
# Helps resume by preserving VRAM allocations:
powerManagement.enable = true;
};
# ---
# NixOS release compatibility
# ---
system.stateVersion = "24.05";
}