# Config for framework16 { config, pkgs, lib, nixos-hardware, impermanence, talon-nix, plover-flake, #beads-flake, awww, niri, ... }: { nix.settings = { download-buffer-size = 524288000; # 500 MiB }; imports = [ nixos-hardware.nixosModules.framework-16-7040-amd ./hardware-configuration.nix impermanence.nixosModules.impermanence talon-nix.nixosModules.talon ]; boot = { loader.systemd-boot.enable = true; loader.systemd-boot.configurationLimit = 20; loader.efi.canTouchEfiVariables = true; #initrd.luks.devices."cryptroot".device = "/dev/disk/by-partlabel/disk-nvme0n1-cryptroot"; initrd.luks.devices."cryptswap".device = "/dev/disk/by-partlabel/disk-nvme0n1-swap"; resumeDevice = "/dev/mapper/cryptswap"; kernelParams = [ "mem_sleep_default=deep" ]; extraModprobeConfig = '' options amdgpu cwsr_enable=0 # For ROCm to not crash ''; }; swapDevices = [ { device = "/dev/mapper/cryptswap"; } ]; # Allow hibernate image to be larger (50 GiB) to accommodate 56 GiB system RAM # Default is ~2/5 of RAM which is too small when memory is heavily used systemd.tmpfiles.rules = [ "w /sys/power/image_size - - - - 53687091200" ]; services.fwupd.enable = true; hardware.framework.enableKmod = true; fileSystems = { "/" = { device = "none"; fsType = "tmpfs"; options = [ "defaults" "size=17G" "mode=755" ]; }; "/persist" = { neededForBoot = true; }; }; hardware.amdgpu = { opencl.enable = true; }; hardware.graphics.enable = true; services.xserver.videoDrivers = [ "amdgpu" ]; services.nextjs-ollama-llm-ui = { enable = true; port = 4666; }; services.ollama = { enable = true; package = pkgs.unstable.ollama-vulkan; loadModels = [ "qwen3.5" "gpt-oss" ]; #acceleration = "vulkan"; #acceleration = "rocm"; #rocmOverrideGfx = "11.0.2"; }; services.llama-swap = { enable = false; port = 11434; settings = let llamaServer = lib.getExe' pkgs.unstable.llama-cpp-vulkan "llama-server"; in { hooks.on_startup.preload = [ "qwen3.5:4b" ]; models."qwen3.5:4b" = { cmd = "${llamaServer} --port \${PORT} -m /var/lib/llama-swap/models/Qwen_Qwen3.5-4B-Q5_K_L.gguf -c 65536"; aliases = [ "qwen" ]; }; models."qwen3.5:35b" = { cmd = "${llamaServer} --port \${PORT} -m /var/lib/llama-swap/models/Qwen_Qwen3.5-35B-A3B-Q5_K_L.gguf -c 65536 --mmap --gpu-layers 35"; aliases = [ "qwen3.5" ]; }; }; }; systemd.services.llama-swap = { environment = { HOME = "/var/lib/llama-swap"; XDG_CACHE_HOME = "/var/cache/llama-swap"; }; serviceConfig = { StateDirectory = "llama-swap"; CacheDirectory = "llama-swap"; }; }; networking.hostId = "3294c9a2"; # Required for ZFS networking.hostName = "liminal"; networking.networkmanager.enable = true; # networking.wireless.enable = true; # Enables wireless support via wpa_supplicant. networking.extraHosts = '' 127.0.0.1 keycloak 127.0.0.1 k3d-cmvm 172.17.0.1 docker.host.internal ''; environment.persistence."/persist" = { hideMounts = true; directories = [ "/root" "/etc/nixos" "/etc/ssh" "/etc/NetworkManager/system-connections" ]; files = [ "/etc/machine-id" #"/etc/nix/id_rsa" # Needed? ]; }; # Files are not copied to /persist during install, so need to do so manually #rsync -azPH /mnt/root/ /mnt/persist/root #rsync -azPH /mnt/etc/nixos/ /mnt/persist/etc/nixos #rsync -azPH /mnt/etc/ssh/ /mnt/persist/etc/ssh #cp /mnt/etc/machine-id /mnt/persist/etc/machine-id security.sudo.extraConfig = '' # Don't lecture after reboot Defaults lecture = never # Let hunner run commands as the agents user without a password. hunner ALL=(agents) NOPASSWD: ALL ''; # Add local development CA security.pki.certificates = [ '' -----BEGIN CERTIFICATE----- MIIDWjCCAkKgAwIBAgIUGRnCFfA+iZqheSfHGXSccBYVFsUwDQYJKoZIhvcNAQEL BQAwGzEZMBcGA1UEAwwQVmFsaWRNaW5kIERldiBDQTAeFw0yNTA3MTcyMjEyMTJa Fw0zNTA3MTUyMjEyMTJaMBsxGTAXBgNVBAMMEFZhbGlkTWluZCBEZXYgQ0EwggEi MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCdGG304TanKBR3z3u0Snc6+kBN piAK4DXhBTFEsCyxzdmH225LOiv16pgZBMq940KdUslExg9/XMy15ozStP/hpTms eInyV77s6T2k9fe593fDLvTxrbCzjYJm0JJ1dBgrRUZe2FwiYS+qeTSOFnrAe7y9 SYLNZ1RJG+bQCJcJr89OvVoCyHkWA2CytgYNupDanuexGQxkBtGZ81s5AtX/dNtL prlsuGoNOUGXZ/sPaL452ZVwu1Ha+41zHTSipo/WzwSCmw8xFaEIY5sySbATz9YD IqbtaZrj1600sHE804jRDnnCfU5AGTC3fhzPodnWLf4LUdrTxh9fEZMedE3/AgMB AAGjgZUwgZIwDAYDVR0TBAUwAwEB/zAdBgNVHQ4EFgQUUcRKwhFGel3xrDZEBofl hk0duQAwVgYDVR0jBE8wTYAUUcRKwhFGel3xrDZEBoflhk0duQChH6QdMBsxGTAX BgNVBAMMEFZhbGlkTWluZCBEZXYgQ0GCFBkZwhXwPomaoXknxxl0nHAWFRbFMAsG A1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEARD1cMX3e+1/5X3qjDPX3sVNe b/02sWhD0RtAAlaSEAlnM4bYx7JLrq45hEgdwBE6aH3tN+HseC2TnLCfp6TeWi5F C/lvuJE3dbNlxv+Js4ksBhuqlqFWJm8SEVwUCnRX70M08Pxmr3Lz76NCGjV9xMEL LKAmzE4i8Nk53lNJnCEIaH4vOuGymIyFqHgoFLGZ4fW+tdHVUbDKB+8BTtba7xbs zCU+LkT1YdvVZzfa6K6wrTY8HWTqSa9fPMyqYvalQJmKJVTI45RqbwfqXzBl4bfe ndPc43JRBIoQAj3rbtQvLKB9HUTqD5fvohqA/v9MmdkUnwAWfXe4usRXnh4+BA== -----END CERTIFICATE----- '' ]; # Set your time zone. time.timeZone = "America/Los_Angeles"; # Select internationalisation properties. i18n.defaultLocale = "en_US.UTF-8"; i18n.extraLocaleSettings = { LC_ADDRESS = "en_US.UTF-8"; LC_IDENTIFICATION = "en_US.UTF-8"; LC_MEASUREMENT = "en_US.UTF-8"; LC_MONETARY = "en_US.UTF-8"; LC_NAME = "en_US.UTF-8"; LC_NUMERIC = "en_US.UTF-8"; LC_PAPER = "en_US.UTF-8"; LC_TELEPHONE = "en_US.UTF-8"; LC_TIME = "en_US.UTF-8"; }; virtualisation.docker = { enable = true; extraOptions = "--storage-driver=overlay2"; extraPackages = [ pkgs.docker-buildx pkgs.docker-compose ]; }; programs.niri.enable = true; programs.niri.package = niri.packages.${pkgs.stdenv.hostPlatform.system}.default; programs.hyprland.enable = true; programs.hyprland.xwayland.enable = false; programs.hyprland.withUWSM = true; programs.hyprlock.enable = true; services.hypridle.enable = true; programs.waybar.enable = false; environment.sessionVariables.NIXOS_OZONE_WL = "1"; # hint electron apps to use wayland programs.zsh.enable = true; services.openssh.enable = true; services.openssh.settings.PermitRootLogin = "yes"; services.tailscale = { enable = true; useRoutingFeatures = "client"; extraUpFlags = [ "--accept-dns" "--accept-routes" ]; }; hardware.brillo.enable = true; # Enable the X11 windowing system. services.xserver.enable = true; services.xserver.displayManager.sessionCommands = '' ( sleep 2 ${pkgs.xrandr}/bin/xrandr --output eDP --mode 2560x1600 --rate 120 --primary --auto || true ) & ''; # Enable the GNOME Desktop Environment. services.displayManager.gdm.enable = true; #services.displayManager.gdm.wayland = true; services.displayManager.gdm.autoSuspend = true; services.desktopManager.gnome.enable = true; services.xserver.windowManager.xmonad = { enable = true; enableContribAndExtras = true; }; # Configure keymap in X11 services.xserver.xkb = { layout = "us"; variant = ""; }; xdg.portal = { enable = true; xdgOpenUsePortal = true; extraPortals = [ pkgs.xdg-desktop-portal-wlr pkgs.xdg-desktop-portal-gtk pkgs.xdg-desktop-portal-gnome #pkgs.xdg-desktop-portal-hyprland ]; config = { hyprland = { default = [ "hyprland" "gtk" ]; "org.freedesktop.impl.portal.ScreenCast" = [ "hyprland" ]; "org.freedesktop.impl.portal.Screenshot" = [ "wlr" ]; "org.freedesktop.impl.portal.FileChooser" = [ "gtk" ]; }; }; }; # Enable CUPS to print documents. services.printing.enable = true; # Enable bluetooth hardware.bluetooth.enable = true; services.blueman.enable = true; services.logind.settings.Login = { HandlePowerKey = "suspend"; HandleLidSwitch = "suspend"; }; services.upower = { enable = true; percentageLow = 15; percentageCritical = 5; percentageAction = 3; criticalPowerAction = "Hibernate"; }; # Enable sound with pipewire. services.pulseaudio.enable = false; security.rtkit.enable = true; services.pipewire = { enable = true; alsa.enable = true; alsa.support32Bit = true; pulse.enable = true; # If you want to use JACK applications, uncomment this #jack.enable = true; # use the example session manager (no others are packaged yet so this is enabled by default, # no need to redefine it in your config for now) #media-session.enable = true; }; # Enable touchpad support (enabled default in most desktopManager). services.libinput.enable = true; services.touchegg.enable = true; # sops-nix secrets sops.defaultSopsFile = ./secrets/config.yaml; sops.age.sshKeyPaths = [ "/persist/etc/ssh/ssh_host_ed25519_key" ]; sops.secrets.hashedPassword.neededForUsers = true; # Define user accounts users.groups.ai = {}; users.users.agents = { isSystemUser = true; description = "Account for AI agent sandboxing"; group = "ai"; home = "/home/agents"; createHome = true; homeMode = "0770"; extraGroups = [ "docker" "audio" "video" "dialout" "input" ]; shell = pkgs.zsh; packages = with pkgs; [ nodejs_24 docker-credential-helpers pass pkgs.unstable.claude-code pkgs.unstable.codex just socat #pkgs.beads gh pkgs.unstable.opencode pkgs.pi-coding-agent pkgs.xai-grok python3 clang clangStdenv cmake just dtach sox # claude voice #lean-ctx ]; }; users.users.hunner = { isNormalUser = true; description = "Hunter Haugen"; extraGroups = [ "docker" "networkmanager" "wheel" "audio" "video" "dialout" "ai" ]; hashedPasswordFile = config.sops.secrets.hashedPassword.path; openssh.authorizedKeys.keys = [ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAqd6VkCyGOaFVfh61+hVKOvYaCZsCChQq3c6rNH/ifG me@hunner.dev" ]; shell = pkgs.zsh; packages = with pkgs; [ alacritty rofi wofi xss-lock xlockmore dzen2 arandr xclip scrot xrandr xsetroot xset xev hsetroot redshift pkgs.unstable.code-cursor pkgs.unstable.cursor-cli pwvucontrol pamixer #helvum #hp15c #nonpareil kitty # for Hyprland kdePackages.dolphin # file browser in hyprland cliphist xscreensaver scarlett2 alsa-scarlett-gui #pkgs.unstable.ndi-6 #(pkgs.unstable.flameshot.override { enableWlrSupport = true; }) grim slurp hyprshot hyprpicker flameshot chromium lmstudio fzf neovim asdf-vm pinentry-gtk2 pinentry-gnome3 gnupg pkgs.unstable.firefox-devedition nodejs_24 slack mplayer ffmpeg-full docker-credential-helpers jetbrains-toolbox pass dua diff-so-fancy zed-editor pkgs.unstable.package-version-server amdgpu_top nixd # for zed rust-analyzer # for zed nil # for zed rustc # for zed rustup # for zed gcc # for zed #ruff # for zed #goose-cli pkgs.unstable.claude-code pkgs.unstable.codex #neofetch eww quickshell fuzzel xwayland-satellite hyprpaper # for hyprland hyprcursor # for hyprland nordzy-icon-theme nordzy-cursor-theme wl-clipboard onlyoffice-desktopeditors calibre clipse plex-desktop signal-desktop telegram-desktop flyctl dtach gromit-mpx urbanterror ghostty talon-nix.packages.${pkgs.stdenv.hostPlatform.system}.default pyright just yt-dlp ledger-live-desktop socat #plover-flake.packages.${pkgs.stdenv.hostPlatform.system}.plover-full pkgs.unstable.zoom-us #pkgs.beads awww.packages.${pkgs.stdenv.hostPlatform.system}.awww nix-index # for nix-locate sops #bitwarden-desktop #bitwarden-cli itch dtach whois plus42 sox # claude voice pkgs.pi-coding-agent pkgs.xai-grok xrandr-invert-colors xcalib #lean-ctx kanata ]; }; systemd.user.services = { polkit-agent = { description = "PolKit Authentication Agent"; wantedBy = [ "graphical-session.target" ]; wants = [ "graphical-session.target" ]; partOf = [ "graphical-session.target" ]; after = [ "graphical-session.target" ]; unitConfig.StartLimitIntervalSec = 0; serviceConfig = { Type = "simple"; ExecStart = "${pkgs.polkit_gnome}/libexec/polkit-gnome-authentication-agent-1"; Environment = "GDK_BACKEND=wayland,x11"; Restart = "on-failure"; RestartSec = 5; TimeoutStopSec = 10; }; }; }; # Enable automatic login for the user. services.displayManager.autoLogin.enable = false; services.displayManager.autoLogin.user = "hunner"; # Workaround for GNOME autologin: https://github.com/NixOS/nixpkgs/issues/103746#issuecomment-945091229 systemd.services."getty@tty1".enable = false; systemd.services."autovt@tty1".enable = false; # Install firefox. programs.firefox.enable = true; # Allow unfree packages nixpkgs.config.allowUnfree = true; # Allow dynamic linked binaries programs.nix-ld = { enable = true; libraries = with pkgs; [ stdenv.cc.cc.lib ]; }; programs.steam = { enable = true; remotePlay.openFirewall = true; # Open ports in the firewall for Steam Remote Play dedicatedServer.openFirewall = true; # Open ports in the firewall for Source Dedicated Server localNetworkGameTransfers.openFirewall = true; # Open ports in the firewall for Steam Local Network Game Transfers }; # List packages installed in system profile. To search, run: # $ nix search wget environment.systemPackages = with pkgs; [ jq yq bat git vim emacs-pgtk wget curl htop tmux file ripgrep powertop fortune shellcheck fd framework-tool restic unzip zip xdg-utils btrbk devenv lsof sg3_utils libimobiledevice ifuse ]; services.usbmuxd.enable = true; services.clipmenu.enable = true; services.picom = { enable = true; settings = { inactive-opacity = 1.0; inactive-dim = 0.0; inactive-opacity-override = false; frame-opacity = 1.0; inactive-dim-fixed = false; fading = false; # Optional, if you want to disable fading as well }; }; programs.direnv = { enable = true; nix-direnv.enable = true; #nix-direnv.package = unstable.nix-direnv; }; programs._1password = { enable = true; }; programs._1password-gui = { enable = true; polkitPolicyOwners = [ "hunner" "agents" ]; }; programs.obs-studio = { enable = true; package = pkgs.unstable.obs-studio; enableVirtualCamera = true; plugins = with pkgs.unstable.obs-studio-plugins; [ wlrobs obs-backgroundremoval obs-pipewire-audio-capture #obs-ndi distroav ]; }; fonts.packages = with pkgs; [ nerd-fonts.droid-sans-mono nerd-fonts.liberation nerd-fonts.jetbrains-mono nerd-fonts.sauce-code-pro nerd-fonts.symbols-only liberation_ttf font-awesome ]; # Some programs need SUID wrappers, can be configured further or are # started in user sessions. # programs.mtr.enable = true; # programs.gnupg.agent = { # enable = true; # enableSSHSupport = true; # }; services.pcscd.enable = true; # List services that you want to enable: # Enable the OpenSSH daemon. # services.openssh.enable = true; # Open ports in the firewall. networking.firewall.allowedTCPPorts = [ 8080 8081 8082 1234 4096 ]; networking.firewall.allowedUDPPorts = [ 8080 8081 8082 1234 4096 ]; # Or disable the firewall altogether. # networking.firewall.enable = false; systemd.services.upower.enable = true; services.avahi = { enable = true; nssmdns4 = true; # Enable NSS lookup for .local domains openFirewall = true; publish = { enable = true; addresses = true; domain = true; workstation = true; }; }; services.dbus = { enable = true; packages = [ pkgs.polkit ]; }; environment.etc = { "1password/custom_allowed_browsers" = { text = '' firefox-devedition ''; mode = "0755"; }; }; programs.dconf.enable = true; security.polkit.enable = true; systemd.services."user@".serviceConfig.Delegate = "cpu io memory pids cpuset"; services.fprintd.enable = true; #security.pam.services = { # login.fprintAuth = true; # xscreensaver.fprintAuth = true; # sudo.fprintAuth = true; # #gdm.fprintAuth = true; # gdm-password.fprintAuth = true; #}; nix.settings.experimental-features = [ "nix-command" "flakes" ]; # 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. # Before changing this value read the documentation for this option # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html). system.stateVersion = "24.11"; # Did you read the comment? } # vim: ft=nix ai