diff options
| author | Henry J. Webster <hwebs@hwebs.info> | 2026-07-30 10:38:54 -0500 |
|---|---|---|
| committer | Henry J. Webster <hwebs@hwebs.info> | 2026-07-30 10:38:54 -0500 |
| commit | 11ba75e40728e8b5735c9c7cefc7af1631c942af (patch) | |
| tree | caef581fd56be96ec9c5338a17d0332b51b08e64 /machines/kusanagi/default.nix | |
| parent | d47d76336a9680b661220d23c5d429d90a2f3f9e (diff) | |
Restructure into a multi-machine flake and add the enzo laptop
Split the single-host config into a shared minimal base plus opt-in
profiles, and add a second machine (enzo laptop) alongside kusanagi.
- flake: add mkMachine helper; declare nixosConfigurations.kusanagi and
.enzo; add the disko input; move overlays per-machine.
- common/: minimal base only (boot, networking, tailscale, locale, nix
settings, neovim editor). Old configuration.nix/programs.nix removed.
- profiles/: coarse opt-in bundles (desktop, dev, apps). kusanagi imports
all three and stays byte-for-byte identical to the running system.
- machines/kusanagi/: all workstation-only config (ROCm, Scarlett audio,
optical-disc archiving, NAS mounts, kavita/immich/ollama); hardware
config moved here unchanged. User stays 'henz'.
- machines/enzo/: new laptop. Declarative LUKS+btrfs via disko
(LVM-on-LUKS, TPM2+PIN unlock, zram + encrypted swap for
suspend-then-hibernate); its own minimal niri desktop with
playback-only audio (no rtkit/jack); user 'hwebs'.
Assisted-by: Claude:claude-opus-4-8
Diffstat (limited to 'machines/kusanagi/default.nix')
| -rw-r--r-- | machines/kusanagi/default.nix | 317 |
1 files changed, 317 insertions, 0 deletions
diff --git a/machines/kusanagi/default.nix b/machines/kusanagi/default.nix new file mode 100644 index 0000000..10531c2 --- /dev/null +++ b/machines/kusanagi/default.nix @@ -0,0 +1,317 @@ +# kusanagi — AMD workstation. Everything here is specific to this machine: +# ROCm/GPU, the Focusrite Scarlett audio interface, optical-disc archiving, the +# NAS mounts, and the Kavita/Immich/Ollama servers. +{ pkgs, ... }: + +{ + imports = [ + ../../profiles/desktop.nix + ../../profiles/dev.nix + ../../profiles/apps.nix + ./hardware-configuration.nix + ]; + + networking.hostName = "kusanagi"; + + # This value determines the NixOS release from which the default + # settings for stateful data, like file locations and database versions + # on your system were taken. It‘s perfectly fine and recommended to leave + # this value at the release version of the first install of this system. + system.stateVersion = "24.11"; # Did you read the comment? + + powerManagement.cpuFreqGovernor = "performance"; + + boot.binfmt.emulatedSystems = [ "aarch64-linux" ]; + + # --- GPU (ROCm) --- + nixpkgs.config.rocmSupport = true; + hardware.graphics.extraPackages = with pkgs; [ + rocmPackages.clr.icd + ]; + + # --- Optical disc drives / archiving --- + boot.kernelModules = [ "sg" ]; # for disc drive + + # For disc drives + services.udev.extraRules = '' + KERNEL=="sr[0-9]*", GROUP="cdrom", MODE="0660" + KERNEL=="sg[0-9]*", GROUP="sg", MODE="0660" + ''; + + services.pcscd.enable = true; + + # abcde: patch its musicbrainz tool to accept discs with 4 args. + nixpkgs.overlays = [ + (self: super: { + abcde = super.abcde.overrideAttrs (oldAttrs: { + # We use postInstall to modify the script after it has been copied to the output directory + postInstall = (oldAttrs.postInstall or "") + '' + # Patch 1: Allow GetOptions to accept 4 arguments (instead of requiring 5+) + sed -i 's/discinfo=i{5,}/discinfo=i{4,}/g' $out/bin/abcde-musicbrainz-tool + + # Patch 2: Lower the logic check from 5 to 4 + sed -i 's/$#discinfo < 5/$#discinfo < 4/g' $out/bin/abcde-musicbrainz-tool + ''; + }); + }) + ]; + + # --- Scanner --- + hardware.sane.enable = true; + + # --- Audio interface (Focusrite Scarlett 18i20) --- + # Add this for better pro audio support + services.pipewire.extraConfig.pipewire."10-low-latency" = { + "context.properties" = { + "default.clock.rate" = 48000; + "default.clock.quantum" = 256; + "default.clock.min-quantum" = 256; + "default.clock.max-quantum" = 2048; + }; + }; + + # Pin the Scarlett 18i20 to the raw "Pro Audio" profile. + # The 26.05 pipewire/wireplumber update began preferring the UCM "HiFi" + # profile, which splits the interface into Direct1/Line5/SPDIF1/... sinks and + # broke monitoring. This priority rule forces the multichannel Pro Audio + # profile statelessly on every boot (and survives future UCM renames). + services.pipewire.wireplumber.extraConfig."51-scarlett-pro-audio" = { + "device.profile.priority.rules" = [ + { + matches = [ + { + "device.name" = "alsa_card.usb-Focusrite_Scarlett_18i20_USB_P9DCR6Y378D1E6-00"; + } + ]; + actions = { + update-props = { + priorities = [ "pro-audio" ]; + }; + }; + } + ]; + }; + + # Real-time audio optimizations + security.pam.loginLimits = [ + { + domain = "@audio"; + item = "memlock"; + type = "-"; + value = "unlimited"; + } + { + domain = "@audio"; + item = "rtprio"; + type = "-"; + value = "99"; + } + { + domain = "@audio"; + item = "nofile"; + type = "soft"; + value = "99999"; + } + { + domain = "@audio"; + item = "nofile"; + type = "hard"; + value = "99999"; + } + ]; + + # Kernel optimizations for audio + boot.kernelParams = [ + "threadirqs" + "preempt=full" # if using a PREEMPT kernel + # Add USB audio optimizations + "usbcore.usbfs_memory_mb=1024" + ]; + + # --- MPD (music) --- + services.mpd.enable = false; + + systemd.user.services.mpd = { + description = "Music Player Daemon"; + after = [ + "network.target" + "sound.target" + ]; + wantedBy = [ "default.target" ]; + serviceConfig = { + ExecStart = "${pkgs.mpd}/bin/mpd --no-daemon"; + Type = "notify"; + LimitRTPRIO = 50; + LimitRTTIME = "infinity"; + }; + }; + + # --- NAS (NFS) --- + boot.supportedFilesystems = [ "nfs" ]; + + services.nfs.server.enable = false; + services.rpcbind.enable = true; + + services.nfs.idmapd.settings = { + General = { + Domain = "kusanagi"; # or your actual domain name + }; + }; + + # NAS shares. Use the mDNS-resolvable name (bare "milgrim" doesn't resolve) + # and automount on first access so a boot-time DNS/network race can't leave + # them unmounted (which silently breaks MPD, whose DB points at these paths). + fileSystems."/mnt/milgrim-share" = { + device = "milgrim.local:/volume1/share"; + fsType = "nfs"; + options = [ + "nfsvers=4" + "rsize=8192" + "wsize=8192" + "_netdev" + "noauto" + "x-systemd.automount" + "x-systemd.idle-timeout=600" + "x-systemd.mount-timeout=10s" + ]; + }; + + fileSystems."/mnt/milgrim-public" = { + device = "milgrim.local:/volume1/public"; + fsType = "nfs"; + options = [ + "nfsvers=4" + "rsize=8192" + "wsize=8192" + "_netdev" + "noauto" + "x-systemd.automount" + "x-systemd.idle-timeout=600" + "x-systemd.mount-timeout=10s" + ]; + }; + + # --- Servers --- + services.kavita = { + enable = true; + tokenKeyFile = "/var/lib/kavita/token-key"; + settings.Port = 5000; + }; + + systemd.tmpfiles.rules = [ + "d /srv/library 2750 henz kavita - -" + ]; + + services.ollama = { + enable = true; + package = pkgs.ollama-rocm; + environmentVariables = { + HIP_VISIBLE_DEVICES = "0"; + OLLAMA_CONTEXT_LENGTH = "32768"; + OLLAMA_KV_CACHE_TYPE = "q8_0"; + }; + }; + + services.immich = { + enable = true; + machine-learning.enable = false; + }; + + # --- discidurl / haunt tooling (needs the per-machine overlays in flake.nix) --- + environment.systemPackages = + let + # open the MusicBrainz submission URL for the disc in the drive + discid = pkgs.writeShellApplication { + name = "discid"; + runtimeInputs = [ + pkgs.discidurl + pkgs.xdg-utils + ]; + text = "discidurl | xargs -r xdg-open"; + }; + in + with pkgs; + [ + discidurl + discid + haunt + ]; + + # --- User account (henz) --- + # (Desktop groups audio/lp/input come from profiles/desktop.nix.) + users.users.henz.isNormalUser = true; + users.users.henz.description = "Henry Webster"; + users.users.henz.extraGroups = [ + # base + "networkmanager" + "wheel" + # kusanagi hardware + "cdrom" + "optical" + "sg" + "scanner" + ]; + + nix.settings.trusted-users = [ + "root" + "henz" + ]; + + users.users.henz.packages = with pkgs; [ + # base CLI + git + neovim + tmux + btop + ripgrep + unzip + xclip + bc + psmisc + usbutils + stow + nushell + + # kusanagi + blender + rocmPackages.rpp + rocmPackages.hipcc + rocmPackages.rocminfo + ardour + davinci-resolve-studio + calf + alsa-utils + alsa-tools + pipewire.jack + nfs-utils + + # music + mpd + mpc + ncmpcpp + + # for archiving + #makemkv + abcde + libaacs + libbdplus + libdvdcss + mkvtoolnix + dvdbackup + chromaprint + picard + flac + + # scanners + simple-scan + + (pkgs.makeDesktopItem { + name = "PICO-8"; + desktopName = "PICO-8"; + exec = "pico8"; + icon = "pico8"; + comment = "Fantasy console for making, sharing and playing tiny games"; + categories = [ "Game" ]; + }) + ]; +} |
