summaryrefslogtreecommitdiff
path: root/machines/kusanagi/default.nix
diff options
context:
space:
mode:
authorHenry J. Webster <hwebs@hwebs.info>2026-07-30 10:38:54 -0500
committerHenry J. Webster <hwebs@hwebs.info>2026-07-30 10:38:54 -0500
commit11ba75e40728e8b5735c9c7cefc7af1631c942af (patch)
treecaef581fd56be96ec9c5338a17d0332b51b08e64 /machines/kusanagi/default.nix
parentd47d76336a9680b661220d23c5d429d90a2f3f9e (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.nix317
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" ];
+ })
+ ];
+}