Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Dr460nixed NixOS ❄️

built with nix built with garnix Sync Tailscale ACLs

This repository contains a framework for NixOS configuration, featuring opinionated and selected default settings. It also includes my personal NixOS setup, which may serve as inspiration for others.

While starting out with NixOS, reading other people’s flakes proved very helpful, so I’m sharing my own configuration. Those who like the “dr460nized” look of Garuda will definitely enjoy this one ☺️

Overview

This flake provides various NixOS configurations, modules, and helper utilities. Outputs are automatically built and cached via garnix. You can adapt snippets from the documentation to craft your own setup.

There is documentation available, which contains code snippets and other topics that might be useful for customization.

How does it look like?

desktop-kde

What kind of configuration is available via code snippets?

  • Multiple NixOS configurations such as cup-dragon, dev-container, dragons-ryzen, dragons-strix, dr460nixed-base, dr460nixed-desktop, and nixos-wsl.
  • Root-on-ZFS and secure-boot enabled systems via Lanzaboote
  • Opt-in persistence through impermanence + ZFS snapshots
  • Mesh networked hosts with Tailscale
  • Opt-in 2FA protection of ssh and password prompts with Duo Security
  • Secrets managed via nix-sops
  • Always up-to-date live images automatically built via Github actions
  • Custom devshells with fully declarative pre-commit-hooks
  • No password prompts when having a Yubikey connected to a device
  • Easily remote-controllable machines via KDEConnect and a self-hosted Rustdesk server
  • Custom bleeding-edge Mesa builds

Structure

├── compose/
│   └── cup-dragon/
├── docs/
├── garuda-nix-subsystem/
├── home-manager/
├── hosts/
├── lib/
├── maintenance/
├── nixos/
│   ├── modules/
│   └── flake-module.nix
├── overlays/
├── packages/
├── secrets/
└── users/

Credits

Inspiration for parts of this configuration came from these awesome people’s setups ❄️

  • https://github.com/PedroHLC/system-setup
  • https://github.com/Misterio77/nix-config
  • https://github.com/isabelroses/dotfiles

Quick start

Using the installer

An installer has been created to ease the installation process by bootstrapping a basic flake with a personal host and usernames. Information on how to build an ISO can be found on the here. The installer can also be used on any OS, that has nix available. Find more information about how to proceed here.

Creating your own configuration including sops-nix

This one is the harder way and is mostly suitable for people with a basic understanding of NixOS. Several preparations have to be made to bootstrap a working installation if not using the installer:

  • A working hardware-configuration.nix needs to be generated for the current machine to replace mine, this includes having already partitioned disks.
  • The hosts *.nix configuration should be adapted to suit the hardware’s needs, eg. needed kernel modules or services.xserver.videoDrivers should be fitting
  • Since sops-nix is used to handle secrets, my files need to be replaced with your own ones. Usage instructions can be found here, basically one needs to create an age public key from the host’s ed21559 SSH private key, which is then added to .sops.yaml to allow the host to decrypt secrets while booting up. A fitting age key should also be generated and placed in ~/.config/sops/age/keys.txt as described in the usage instructions - this allows decrypting the secrets file to edit it. It lives in secrets/global.yaml and contains the secrets and can be edited with sops secrets/global.yaml (opens a terminal text editor).
  • It might be easier to supply a static password in users.nix for bootstrapping since no login will be possible if the secrets management isn’t properly set up yet. I had a few issues with this in the past while setting things up, so I felt giving this advice might help. Usernames are of course also to be changed, as well as SSH public keys.

Then, the bootstrapping process can be started. Here, nix + nixos-install-tools is sufficient to set up your our configuration as follows:

export NIX_CONFIG="experimental-features = nix-command flakes" # if flakes are disabled
nixos-install --flake .#hostname

If the operation succeeds, you will be able to boot into your new installation.

How to proceed from here?

  • Adapt the configurations like enabled modules and home-manager configs to your needs
  • Set up CI to build your custom system configurations
  • Enable secure boot via Lanzaboote
  • Add your hosts to Tailscale, if you want to be using it. I can warmly recommend it for connecting with any kind of host!
  • Build an ISO to play around with nix run .#iso
  • … so much more. It never ends ❄️

Generating images

Images of this flake may easily be built by using nixos-generators. In our case, we use it as NixOS module to be able to build the system via garuda-nix.lib.garudaSystem. This is important, as this function exposes all the garuda-nix-subsystem modules for us to consume.

How to get going?

Let’s build a regular dr460nixed ISO!

nix build .#nixosConfigurations.dr460nixed-desktop.config.formats.install-iso

You may also just run nix run .#iso, which builds an image using the install-iso format.

This is pretty much everything needed! The result will be available at ./result, which links to the corresponding path in /nix/store.

Likewise, all other configurations may be built by simply exchanging installer-iso with the desired format. Depending on what kind of image is being built it is needed to use the dr460nixed-base system to build the image.

nix build .#nixosConfigurations.dr460nixed-base.config.formats.sdcard

For a list of other supported formats please have a look here.

Cross-compiling

It is also possible to build images for other architectures, eg. the Raspberry Pi. To allow cross-compilation, please have a look at how to set up the required options here.

TLDR: add the following to your configuration and apply it:

{
  # Enable binfmt emulation of aarch64-linux.
  boot.binfmt.emulatedSystems = [ "aarch64-linux" ];
}

Provided images

Prebuilt images used to be provided via GitHub actions, though lately builds are seemingly hitting the resource limits of the free GitHub actions VMs. Therefore, ISO builds are currently only provided with new releases. In order to work around the filze size limit of release attachments, ISO files need to need to be downloaded as segments. To join them to a full file, you can do the following:

cat dr460nixed-desktop.iso.part-* > dr460nixed-desktop.iso

The installer

This flake features an installer, which may be used to set up a basic dr460nixed NixOS installation. Both the dr460nixed ISO and regular NixOS live CDs are supported. The installer uses the files of the templates folder to run nix init -t from and proceeds to customize the installation with users’ choices. Multiple disk formats are available via pre-configured disko configurations. Choices during the execution of the script currently include:

  • the disk to install NixOS on
  • the partition layout to use during the process
  • hostname
  • username

The installer may only install by wiping the destination disk, custom partition layouts are currently unsupported. Both UEFI and legacy systems are supported. Nix command and flakes need to be enabled.

To begin, simply run the installer:

sudo installer # use this if booted into a dr460nixed ISO
sudo nix run github:dr460nf1r3/dr460nixed#installer # regular NixOS systems

Provide the needed input. After completion, a dr460nixed system is ready for you to use. You may customize it with configurations found in the main repository since the template has been kept as generic as possible.

Users are expected to continue building their own flake after the installation is finished. To do so, the dr460nixed repository has many exemplary configurations available. They may be inspected by browsing the “modules” section of this documentation.

Script

This is a small script which is serving as installer for this project. It basically:

  • Sets up an environment to install the system from
  • Asks for basic information such as the disk to be used or the username
  • Sets up the partition layout via disko and mounting the partitions to /mnt
  • Provides a basic, quite generic dr460nixed template and customizes it based on previous choices
  • Executes the installation
#!/usr/bin/env bash
set -eo pipefail

# Check for root rights
if [ "$EUID" -ne 0 ]; then
  echo "I can only run as root!"
  exit 3
fi

# Prepare our environment
prepare() {
  # Clone dr460nixed repo if it is not present, otherwise use current dir
  if [ ! "$(test -f flake.nix)" ]; then
    test -d /tmp/dr460nixed && sudo rm -rf /tmp/dr460nixed
    WORK_DIR=/tmp/dr460nixed
    git clone https://github.com/dr460nf1r3/dr460nixed.git "$WORK_DIR"
    cd "$WORK_DIR"
  else
    WORK_DIR=$(pwd)
  fi

  # Ensure needed "experimental" features are always enabled
  export NIX_CONFIG="experimental-features = nix-command flakes"
}

# Confirmation prompt
confirm_choices() {
  # Continue if the user confirms our choice
  read -rp "Are you sure you want to continue? $1 [y/n] " _ANSWER

  while [ -z "${KILLIT+x}" ]; do
    case "${_ANSWER}" in
    y | yes | Y | YES)
      KILLIT=1
      ;;
    n | no | N | NO)
      exit 1
      ;;
    *)
      read -rp "Invalid input. Only yes or no is valid: " _ANSWER
      ;;
    esac
  done
}

# Create a runner for disko, directly from git
disko_runner() {
  nix run github:nix-community/disko -- --mode disko "$1" --arg disks "[ \"$2\" ]"
}

# Create partitions using disko and mount them to /mnt
disko() {
  echo "The following partition layouts are available:
1) BTRFS with subvolumes
2) BTRFS on LUKS with subvolumes
3) Simple EFI
4) ZFS (recommended)
5) ZFS encrypted"

  read -rp "Enter the number of the partition layout you want to use: " _LAYOUT

  # https://stackoverflow.com/questions/3601515/how-to-check-if-a-variable-is-set-in-bash
  while [ -z "${DISKO_MODULE+x}" ]; do
    case "${_LAYOUT}" in
    1)
      DISKO_MODULE=btrfs-subvolumes
      ;;
    2)
      DISKO_MODULE=luks-btrfs-subvolumes
      ;;
    3)
      DISKO_MODULE=simple-efi
      ;;
    4)
      DISKO_MODULE=zfs-encrypted
      ;;
    5)
      DISKO_MODULE=zfs
      ;;
    *)
      read -rp "Invalid input. Enter the number of the partition layout you want to use: " _LAYOUT
      ;;
    esac
  done

  while [ -z "${_VALID_DISK+x}" ]; do
    read -rp 'Specify the disk you want to use, eg. "nvme0n1": ' DISK
    # Disk identifiers should at least be 3 characters long and be present in our system
    # this does not prevent all possible errors (eg. nvme0 would be valid) but good enough for now
    if [ ${#DISK} -gt 2 ] && lsblk | grep "$DISK"; then
      _VALID_DISK=1
    else
      echo "The disk you entered is invalid! Try again."
    fi
  done

  # Ask whether the hard drive should really be wiped
  echo "The disk you chose to format is $DISK."
  confirm_choices "This will start the wiping process!"

  # Create partitions and set up /mnt
  disko_runner ./nixos/modules/disko/"$DISKO_MODULE".nix /dev/"$DISK"
}

# Create initial configuration
create_config() {
  NIX_ROOT=/mnt/etc/nixos
  read -rp "Enter the hostname you want to use: " HOSTNAME
  read -rp "Enter your desired username: " USER
  [ -d /sys/firmware/efi ] && SYSTEM_TYPE=systemd-boot || SYSTEM_TYPE=grub

  # Create config without filesystems as disko provides those already
  # also apply our dr460nixed template
  nixos-generate-config --no-filesystems --root /mnt
  pushd "$NIX_ROOT" || exit 2
  nix flake init --template "$WORK_DIR"#dr460nixed

  mv ./hardware-configuration.nix ./nixos/example-host
  rm ./configuration.nix # we are using flakes, no need for that anymore
  mv ./nixos/example-host ./nixos/"$HOSTNAME"
  mv ./nixos/"$HOSTNAME"/example-host.nix ./nixos/"$HOSTNAME"/"$HOSTNAME".nix

  sed -i s/example-boot/"$SYSTEM_TYPE"/g ./nixos/"$HOSTNAME"/"$HOSTNAME".nix
  sed -i s/example-disk/"$DISK"/g .{/nixos/flake-module.nix,nixos/"$HOSTNAME"/"$HOSTNAME".nix}
  sed -i s/example-hostname/"$HOSTNAME"/g {./nixos/flake-module.nix,nixos/"$HOSTNAME"/"$HOSTNAME".nix}
  sed -i s/example-layout/"$DISKO_MODULE"/g ./nixos/flake-module.nix
  sed -i s/example-user/"$USER"/g ./nixos/modules/users.nix

  echo "Configuration successfully created.
You made the following choices:
hostname: $HOSTNAME
user: $USER"
  confirm_choices "This starts the installation process."

  popd || exit 2
}

# Install basic dr460nixed system
install_system() {
  nixos-install --flake "$NIX_ROOT#$HOSTNAME" --verbose
}

# Notices
finish() {
  echo "The installation finished successfully. You may now reboot into your new system."
  confirm_choices "This will remove the temporary directory an reboot the system."
  umount -Rf /mnt
  rm -rf "$WORK_DIR"
}

# Actually execute our functions
prepare
disko
create_config
install_system
finish

Apps

{{#include ../../../nixos/modules/apps/cli.nix}}

Auto-upgrade

{
  config,
  lib,
  ...
}:
let
  cfg = config.dr460nixed.auto-upgrade;
in
{
  options.dr460nixed.auto-upgrade = {
    enable = lib.mkEnableOption "automatic system upgrades";
  };

  config = lib.mkIf cfg.enable {
    system.autoUpgrade = {
      allowReboot = true;
      dates = "04:00";
      enable = true;
      flake = "github:dr460nf1r3/dr460nixed";
      randomizedDelaySec = "1h";
      rebootWindow = {
        lower = "00:00";
        upper = "06:00";
      };
    };
  };
}

Boot

{
  config,
  lib,
  pkgs,
  ...
}:
let
  cfgLanza = config.dr460nixed.lanzaboote;
in
{
  imports = [
    ./common.nix
    ./grub.nix
    ./lanzaboote.nix
    ./systemd-boot.nix
  ];

  options.dr460nixed = with lib; {
    grub = {
      enable = mkOption {
        default = false;
        type = types.bool;
        description = mdDoc "Configures the system to install GRUB to a particular device.";
      };
      enableCryptodisk = mkOption {
        default = false;
        type = types.bool;
        description = mdDoc "Whether to enable GRUB cryptodisk support.";
      };
      device = mkOption {
        default = "nodev";
        type = types.str;
        description = mdDoc "Defines which device to install GRUB to.";
      };
    };
    systemd-boot = {
      enable = mkOption {
        default = false;
        type = types.bool;
        description = mdDoc "Configures common options for a quiet systemd-boot.";
      };
    };
    lanzaboote = {
      enable = mkOption {
        default = false;
        type = types.bool;
        description = mdDoc "Configures common options using Lanzaboote as secure boot manager.";
      };
    };
  };

  config = {
    environment.systemPackages = lib.mkIf cfgLanza.enable [ pkgs.sbctl ];
  };
}

Chromium

{
  config,
  lib,
  ...
}:
let
  cfg = config.dr460nixed.chromium;
in
{
  options.dr460nixed.chromium = {
    enable = lib.mkEnableOption "Chromium with a set of default extensions and settings";
  };

  config = lib.mkIf cfg.enable {
    programs.chromium = {
      defaultSearchProviderEnabled = true;
      defaultSearchProviderSearchURL = "https://searx.garudalinux.org/search?q=%s";
      defaultSearchProviderSuggestURL = "https://searx.garudalinux.org/autocomplete?q=%s";
      enable = true;
      enablePlasmaBrowserIntegration = true;
      extensions = [
        "bkkmolkhemgaeaeggcmfbghljjjoofoh" # Catppuccin theme
        "clngdbkpkpeebahjckkjfobafhncgmne" # Stylus
        "doojmbjmlfjjnbmnoijecmcbfeoakpjm" # NoScript
        "hlepfoohegkhhmjieoechaddaejaokhf" # Github Refined
        "kbfnbcaeplbcioakkpcpgfkobkghlhen" # Grammarly
        "mdjildafknihdffpkfmmpnpoiajfjnjd" # Consent-O-Matic
        "mnjggcdmjocbbbhaepdhchncahnbgone" # SponsorBlock
        "nngceckbapebfimnlniiiahkandclblb" # Bitwarden
      ];
      extraOpts = {
        "QuicAllowed" = true;
        "RestoreOnStartup" = true;
        "ShowHomeButton" = true;
      };
    };

    security.chromiumSuidSandbox.enable = true;
  };
}

Common

{
  config,
  lib,
  pkgs,
  ...
}:
let
  cfg = config.dr460nixed;
in
{
  options.dr460nixed = with lib; {
    common = {
      enable = mkEnableOption "Whether to enable common system configurations." // {
        default = true;
      };
    };
    nodocs = mkOption {
      default = true;
      type = types.bool;
      description = mdDoc ''
        Whether to disable the documentation.
      '';
    };
  };

  config = lib.mkIf cfg.common.enable {
    nixpkgs.config.allowUnfree = true;

    # A few kernel tweaks
    boot.kernelParams = [ "noresume" ];

    # Disable unprivileged user namespaces, unless containers are enabled
    security = {
      # User namespaces are required for sandboxing
      allowUserNamespaces = true;
      # This is only required for containers
      unprivilegedUsernsClone = config.virtualisation.containers.enable;
      # Force-enable the Page Table Isolation (PTI) Linux kernel feature
      forcePageTableIsolation = true;
    };

    # Allow wheel group users to use sudo
    security.sudo.execWheelOnly = true;

    # This is the default sops file that will be used for all secrets
    sops = {
      age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
      defaultSopsFile = ../../../secrets/global.yaml;
    };

    # Theming
    garuda.catppuccin.enable = true;

    # Increase open file limit for sudoers
    security.pam.loginLimits = [
      {
        domain = "@wheel";
        item = "nofile";
        type = "soft";
        value = "524288";
      }
      {
        domain = "@wheel";
        item = "nofile";
        type = "hard";
        value = "1048576";
      }
    ];

    # Always needed applications
    programs = {
      git = {
        enable = true;
        lfs.enable = true;
      };
      # The GnuPG agent
      gnupg.agent = {
        enable = true;
        pinentryPackage = lib.mkForce pkgs.pinentry-curses;
      };
    };

    # https://gitlab.com/ananicy-cpp/ananicy-cpp/-/issues/40#note_1986279383
    systemd.services.ananicy-cpp = {
      serviceConfig = {
        Delegate-cpu = "cpuset io memory pids";
        ExecStartPre = "${pkgs.coreutils}/bin/sleep 30";
      };
    };

    # Who needs documentation when there is the internet? #bl04t3d
    documentation = lib.mkIf cfg.nodocs {
      dev.enable = false;
      doc.enable = false;
      enable = true;
      info.enable = false;
      man.enable = true;
      nixos.enable = false;
    };

    # Enable all hardware drivers
    hardware.enableRedistributableFirmware = true;

    # No need for that in real NixOS systems
    garuda.garuda-nix-manager.enable = false;

    # Custom label for boot menu entries (otherwise set to "garuda-nix-subsystem")
    system.nixos.label = lib.mkForce (
      builtins.concatStringsSep "-" [ "dr460nixed-" ] + config.system.nixos.version
    );
  };
}

Compose runner

{
  lib,
  pkgs,
  config,
  ...
}:
let
  cfg = config.dr460nixed.compose-runner;
in
{
  options.dr460nixed.compose-runner = lib.mkOption (
    with lib;
    {
      type = types.attrsOf (
        types.submodule {
          options = {
            source = mkOption {
              default = null;
              description = "Folder containing a compose file.";
              type = types.path;
            };
            envfile = mkOption {
              default = null;
              description = "Direct path to a valid .env file";
              type = types.nullOr types.path;
            };
          };
        }
      );
      default = { };
    }
  );

  config = {
    systemd.services = lib.mapAttrs' (
      name: value:
      lib.nameValuePair ("compose-runner-" + name) (
        let
          output = derivation {
            builder = pkgs.writeShellScript "build" ''
              PATH="${pkgs.rsync}/bin:${pkgs.coreutils}/bin:${pkgs.gnused}/bin"
              set -e
              mkdir "$out"
              sed -r 's/(^\s+restart:\s*)(unless-stopped|always)(\s*($|#))/\1on-failure\3/g' "$src/compose.yml" > "$out/compose.yml"
              rsync -a "$src/" "$out"
            '';
            name = "compose-runner-" + name;
            src = value.source;
            inherit (pkgs.hostPlatform) system;
          };
          statepath = "/var/compose-runner/${name}";
        in
        {
          description = "Compose runner for ${name}";
          path = with pkgs; [
            rsync
            docker-compose
            podman
            bash
          ];
          serviceConfig = {
            ExecStart = pkgs.writeShellScript ("execstart-compose-runner-" + name) ''
              set -e
              mkdir -p "${statepath}"
              rsync -a --no-owner --size-only "${output}/" "${statepath}"
              ${lib.optionalString (value.envfile != null) ''
                cp "${value.envfile}" "${statepath}/.env"
                chmod 600 "${statepath}/.env"
              ''}
              cd "${statepath}"
              docker-compose up
            '';
            ExecStopPost = pkgs.writeShellScript ("execstop-compose-runner-" + name) ''
              set -e
              cd "${statepath}"
              docker-compose down
            '';
          };
          unitConfig = {
            After = "docker.socket";
            Requisite = "docker.socket";
            StopPropagatedFrom = "docker.socket";
          };
          wantedBy = [ "multi-user.target" ];
        }
      )
    ) cfg;
    environment.systemPackages = lib.mkIf (cfg != { }) [ pkgs.docker-compose ];
    virtualisation.docker.enable = lib.mkIf (cfg != { }) true;
  };
}

Desktops

{
  config,
  lib,
  ...
}:
let
  cfg = config.dr460nixed.desktops;
in
{
  imports = [
    ./kde.nix
    ./spicetify.nix
    ./desktop-apps.nix
  ];

  options.dr460nixed.desktops = with lib; {
    enable = mkOption {
      default = false;
      type = types.bool;
      description = mdDoc "Whether to enable basic dr460nized desktop theming.";
    };
    kde = mkOption {
      default = false;
      type = types.bool;
      description = mdDoc "Enable KDE Plasma desktop";
    };
    spicetify = mkOption {
      default = false;
      type = types.bool;
      description = mdDoc "Enable Spicetify (Spotify theming)";
    };
  };

  config = lib.mkIf cfg.enable {
    dr460nixed.desktops = {
      kde = true;
      spicetify = true;
    };
  };
}

Dev Container

{
  config,
  lib,
  pkgs,
  ...
}:
let
  cfg = config.dr460nixed.dev-container;
in
{
  options.dr460nixed.dev-container = {
    enable = lib.mkEnableOption "development container configuration";
    user = lib.mkOption {
      type = lib.types.str;
      default = "nixos";
      description = lib.mdDoc "The user to create in the dev container.";
    };
  };

  config = lib.mkIf cfg.enable {
    environment.systemPackages = with pkgs; [
      chromium
      jetbrains.webstorm
      nodejs_latest
    ];

    services = {
      desktopManager.plasma6.enable = true;
      displayManager.plasma-login-manager.enable = true;
      xserver.enable = true;
    };

    nixpkgs.config.allowUnfree = true;

    users.users.${cfg.user} = {
      isNormalUser = true;
      extraGroups = [ "wheel" ];
      password = "password";
    };

    services.xrdp.enable = true;
    services.xrdp.defaultWindowManager = "startplasma-x11";
    services.xrdp.openFirewall = true;
    security.polkit.extraConfig = ''
      polkit.addRule(function(action, subject) {
        if (
          subject.isInGroup("users")
            && (
              action.id == "org.freedesktop.login1.reboot" ||
              action.id == "org.freedesktop.login1.reboot-multiple-sessions" ||
              action.id == "org.freedesktop.login1.power-off" ||
              action.id == "org.freedesktop.login1.power-off-multiple-sessions"
            )
          )
        {
          return polkit.Result.YES;
        }
      });
    '';

    programs.ssh.setXAuthLocation = lib.mkForce true;

    system.stateVersion = "26.05";
  };
}

Deterministic ids

# https://github.com/oddlama/nix-config/blob/main/modules/system/deteministic-ids.nix
{
  lib,
  config,
  ...
}:
let
  inherit (lib)
    concatLists
    flip
    mapAttrsToList
    mdDoc
    mkDefault
    mkIf
    mkOption
    types
    ;

  cfg = config.users.deterministicIds;
in
{
  options = {
    users.deterministicIds = mkOption {
      default = { };
      description = mdDoc ''
        Maps a user or group name to its expected uid/gid values. If a user/group is
        used on the system without specifying a uid/gid, this module will assign the
        corresponding ids defined here, or show an error if the definition is missing.
      '';
      type = types.attrsOf (
        types.submodule {
          options = {
            uid = mkOption {
              type = types.nullOr types.int;
              default = null;
              description = mdDoc "The uid to assign if it is missing in `users.users.<name>`.";
            };
            gid = mkOption {
              type = types.nullOr types.int;
              default = null;
              description = mdDoc "The gid to assign if it is missing in `users.groups.<name>`.";
            };
          };
        }
      );
    };

    users.users = mkOption {
      type = types.attrsOf (
        types.submodule (
          { name, ... }:
          {
            config.uid =
              let
                deterministicUid = cfg.${name}.uid or null;
              in
              mkIf (deterministicUid != null) (mkDefault deterministicUid);
          }
        )
      );
    };

    users.groups = mkOption {
      type = types.attrsOf (
        types.submodule (
          { name, ... }:
          {
            config.gid =
              let
                deterministicGid = cfg.${name}.gid or null;
              in
              mkIf (deterministicGid != null) (mkDefault deterministicGid);
          }
        )
      );
    };
  };

  config = {
    assertions =
      concatLists (
        flip mapAttrsToList config.users.users (
          name: user: [
            {
              assertion = user.uid != null;
              message = "non-deterministic uid detected for '${name}', please assign one via `users.deterministicIds`";
            }
            {
              assertion = !user.autoSubUidGidRange;
              message = "non-deterministic subUids/subGids detected for: ${name}";
            }
          ]
        )
      )
      ++ flip mapAttrsToList config.users.groups (
        name: group: {
          assertion = group.gid != null;
          message = "non-deterministic gid detected for '${name}', please assign one via `users.deterministicIds`";
        }
      );
  };
}

Development

{
  config,
  lib,
  ...
}:
let
  cfg = config.dr460nixed.development;
in
{
  imports = [
    ./docker.nix
    ./podman.nix
    ./vms.nix
    ./tools.nix
    ./jetbrains.nix
  ];

  options.dr460nixed.development = with lib; {
    enable = mkOption {
      default = false;
      type = types.bool;
      description = mdDoc ''
        Enables commonly used development tools.
      '';
    };
    docker = mkOption {
      default = false;
      type = types.bool;
      description = mdDoc "Enable Docker and containers";
    };
    podman = mkOption {
      default = false;
      type = types.bool;
      description = mdDoc "Enable Podman and Quadlet containers";
    };
    vms = mkOption {
      default = false;
      type = types.bool;
      description = mdDoc "Enable VM support (VirtualBox, KVM)";
    };
    tools = mkOption {
      default = false;
      type = types.bool;
      description = mdDoc "Enable development tools";
    };
    jetbrains = mkOption {
      default = false;
      type = types.bool;
      description = mdDoc "Enable JetBrains IDEs and Android Studio";
    };
    user = mkOption {
      type = types.nullOr types.str;
      default = null;
      description = mdDoc "The user to configure development tools for.";
    };
  };

  config = lib.mkIf cfg.enable {
    dr460nixed.development = {
      podman = true;
      tools = true;
      vms = true;
      jetbrains = true;
    };
  };
}

Disko

{
  lib,
  disks ? [ "/dev/nvme0n1" ],
  ...
}:
{
  disko.devices = {
    disk = {
      nvme1 = {
        device = builtins.elemAt disks 0;
        content = {
          partitions = {
            ESP = {
              content = {
                format = "vfat";
                mountpoint = lib.mkDefault "/boot";
                type = "filesystem";
              };
              size = "1024M";
              type = "EF00";
            };
            zfs = {
              size = "100%";
              content = {
                pool = "zroot";
                type = "zfs";
              };
            };
          };
          type = "gpt";
        };
        type = "disk";
      };
    };
    zpool = {
      zroot = {
        type = "zpool";
        rootFsOptions = {
          "com.sun:auto-snapshot" = "false";
          acltype = "posixacl";
          atime = "off";
          canmount = "off";
          compression = "zstd";
          dedup = "on";
          devices = "off";
          encryption = "aes-256-gcm";
          keyformat = "passphrase";
          keylocation = "prompt";
          mountpoint = "none";
          xattr = "sa";
        };
        postCreateHook = ''
          zfs set keylocation=prompt zroot;
        '';
        datasets = {
          "data" = {
            options.mountpoint = "none";
            type = "zfs_fs";
          };
          "ROOT" = {
            options.mountpoint = "none";
            type = "zfs_fs";
          };
          "ROOT/empty" = {
            mountpoint = "/";
            options.mountpoint = "legacy";
            postCreateHook = ''
              zfs snapshot zroot/ROOT/empty@start
            '';
            type = "zfs_fs";
          };
          "ROOT/nix" = {
            mountpoint = "/nix";
            options.mountpoint = "legacy";
            type = "zfs_fs";
          };
          "ROOT/residues" = {
            mountpoint = "/var/residues";
            options.mountpoint = "legacy";
            type = "zfs_fs";
          };
          "data/persistent" = {
            mountpoint = "/var/persistent";
            options.mountpoint = "legacy";
            type = "zfs_fs";
          };
          "reserved" = {
            options = {
              mountpoint = "none";
              reservation = "10G";
            };
            type = "zfs_fs";
          };
        };
      };
    };
  };

  # Needed for impermanence
  fileSystems."/var/persistent".neededForBoot = true;
  fileSystems."/var/residues".neededForBoot = true;
}

Gaming

{
  config,
  dragonLib,
  lib,
  pkgs,
  ...
}:
let
  cfg = config.dr460nixed.gaming;
in
{
  options.dr460nixed.gaming = with lib; {
    enable = mkOption {
      default = false;
      type = types.bool;
      description = mdDoc ''
        Whether this device is used for gaming.
      '';
    };
  };

  config = lib.mkIf cfg.enable {
    programs.gamemode.enable = true;

    programs.steam = {
      enable = true;
      extraCompatPackages = with pkgs; [
        proton-cachyos-x86_64_v4
      ];
    };

    environment.systemPackages = with pkgs; [
      lutris
      prismlauncher
      (dragonLib.GPUOffloadApp config pkgs prismlauncher "org.prismlauncher.PrismLauncher")
      (dragonLib.GPUOffloadApp config pkgs steam "steam")
    ];

    drivers.mesa-git = {
      enable = true;
      cacheCleanup = {
        enable = true;
        protonPackage = pkgs.proton-cachyos-x86_64_v4;
      };
      steamOrphanCleanup = {
        enable = true;
      };
    };
  };
}

Hardening

{
  config,
  # inputs,
  lib,
  pkgs,
  ...
}:
let
  cfg = config.dr460nixed.hardening;
  cfgServers = config.dr460nixed.servers.enable;
in
{
  options.dr460nixed.hardening = with lib; {
    enable = mkOption {
      default = true;
      example = false;
      type = types.bool;
      description = mdDoc ''
        Whether the operating system should be hardened.
      '';
    };
  };

  config = lib.mkIf cfg.enable {
    # Disable some of it
    #    nm-overrides = {
    #      compatibility = {
    #        binfmt-misc.enable = true;
    #        ip-forward.enable = true;
    #      };
    #      desktop = {
    #        allow-multilib.enable = true;
    #        allow-unprivileged-userns.enable = true;
    #        home-exec.enable = true;
    #        tmp-exec.enable = true;
    #        usbguard-allow-at-boot.enable = true;
    #      };
    #      performance = {
    #        allow-smt.enable = true;
    #      };
    #      security = {
    #        tcp-timestamp-disable.enable = true;
    #        disable-intelme-kmodules.enable = true;
    #      };
    #    };

    boot.blacklistedKernelModules = [
      # Obscure network protocols
      "ax25"
      "netrom"
      "rose"
      # Old or rare or insufficiently audited filesystems
      "adfs"
      "affs"
      "befs"
      "bfs"
      "btusb"
      "cifs"
      "cramfs"
      "cramfs"
      "efs"
      "erofs"
      "exofs"
      "f2fs"
      "freevxfs"
      "freevxfs"
      "gfs2"
      "hfs"
      "hfsplus"
      "hpfs"
      "jffs2"
      "jfs"
      "ksmbd"
      "minix"
      "nfs"
      "nfsv3"
      "nfsv4"
      "nilfs2"
      "omfs"
      "qnx4"
      "qnx6"
      "sysv"
      "udf"
      "vivid"
    ];

    # Disable root login & password authentication on sshd
    # also, apply recommendations of ssh-audit.com
    services.openssh = {
      extraConfig = ''
        AllowTcpForwarding no
        HostKeyAlgorithms ssh-ed25519,ssh-ed25519-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,sk-ssh-ed25519-cert-v01@openssh.com,rsa-sha2-256,rsa-sha2-512,rsa-sha2-256-cert-v01@openssh.com,rsa-sha2-512-cert-v01@openssh.com
        PermitTunnel no
      '';
      settings = {
        Ciphers = [
          "aes128-ctr"
          "aes128-gcm@openssh.com"
          "aes256-ctr,aes192-ctr"
          "aes256-gcm@openssh.com"
        ];
        KbdInteractiveAuthentication = false;
        KexAlgorithms = [
          "curve25519-sha256"
          "curve25519-sha256@libssh.org"
          "diffie-hellman-group16-sha512"
          "diffie-hellman-group18-sha512"
          "sntrup761x25519-sha512@openssh.com"
        ];
        Macs = [
          "hmac-sha2-256-etm@openssh.com"
          "hmac-sha2-512"
          "hmac-sha2-512-etm@openssh.com"
          "umac-128-etm@openssh.com"
        ];
        PasswordAuthentication = false;
        PermitRootLogin = "no";
        X11Forwarding = false;
      };
    };

    # Client side SSH configuration
    programs.ssh = {
      ciphers = [
        "aes256-gcm@openssh.com"
        "aes256-ctr,aes192-ctr"
        "aes128-ctr"
        "aes128-gcm@openssh.com"
        "chacha20-poly1305@openssh.com"
      ];
      hostKeyAlgorithms = [
        "ssh-ed25519"
        "ssh-ed25519-cert-v01@openssh.com"
        "sk-ssh-ed25519@openssh.com"
        "sk-ssh-ed25519-cert-v01@openssh.com"
        "rsa-sha2-512"
        "rsa-sha2-512-cert-v01@openssh.com"
        "rsa-sha2-256"
        "rsa-sha2-256-cert-v01@openssh.com"
      ];
      kexAlgorithms = [
        "curve25519-sha256"
        "curve25519-sha256@libssh.org"
        "diffie-hellman-group16-sha512"
        "diffie-hellman-group18-sha512"
        "sntrup761x25519-sha512@openssh.com"
      ];
      knownHosts = {
        aur-rsa = {
          hostNames = [ "aur.archlinux.org" ];
          publicKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDKF9vAFWdgm9Bi8uc+tYRBmXASBb5cB5iZsB7LOWWFeBrLp3r14w0/9S2vozjgqY5sJLDPONWoTTaVTbhe3vwO8CBKZTEt1AcWxuXNlRnk9FliR1/eNB9uz/7y1R0+c1Md+P98AJJSJWKN12nqIDIhjl2S1vOUvm7FNY43fU2knIhEbHybhwWeg+0wxpKwcAd/JeL5i92Uv03MYftOToUijd1pqyVFdJvQFhqD4v3M157jxS5FTOBrccAEjT+zYmFyD8WvKUa9vUclRddNllmBJdy4NyLB8SvVZULUPrP3QOlmzemeKracTlVOUG1wsDbxknF1BwSCU7CmU6UFP90kpWIyz66bP0bl67QAvlIc52Yix7pKJPbw85+zykvnfl2mdROsaT8p8R9nwCdFsBc9IiD0NhPEHcyHRwB8fokXTajk2QnGhL+zP5KnkmXnyQYOCUYo3EKMXIlVOVbPDgRYYT/XqvBuzq5S9rrU70KoI/S5lDnFfx/+lPLdtcnnEPk=";
        };
        aur-ed25519 = {
          hostNames = [ "aur.archlinux.org" ];
          publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEuBKrPzbawxA/k2g6NcyV5jmqwJ2s+zpgZGZ7tpLIcN";
        };
        github-rsa = {
          hostNames = [ "github.com" ];
          publicKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk=";
        };
        github-ed25519 = {
          hostNames = [ "github.com" ];
          publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl";
        };
        gitlab-rsa = {
          hostNames = [ "gitlab.com" ];
          publicKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsj2bNKTBSpIYDEGk9KxsGh3mySTRgMtXL583qmBpzeQ+jqCMRgBqB98u3z++J1sKlXHWfM9dyhSevkMwSbhoR8XIq/U0tCNyokEi/ueaBMCvbcTHhO7FcwzY92WK4Yt0aGROY5qX2UKSeOvuP4D6TPqKF1onrSzH9bx9XUf2lEdWT/ia1NEKjunUqu1xOB/StKDHMoX4/OKyIzuS0q/T1zOATthvasJFoPrAjkohTyaDUz2LN5JoH839hViyEG82yB+MjcFV5MU3N1l1QL3cVUCh93xSaua1N85qivl+siMkPGbO5xR/En4iEY6K2XPASUEMaieWVNTRCtJ4S8H+9";
        };
        gitlab-ed25519 = {
          hostNames = [ "gitlab.com" ];
          publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf";
        };
      };
      macs = [
        "hmac-sha2-512-etm@openssh.com"
        "hmac-sha2-256-etm@openssh.com"
        "umac-128-etm@openssh.com"
      ];
    };

    # Timeout TTY after 1 hour
    programs.bash.interactiveShellInit = "if [[ $(tty) =~ /dev\\/tty[1-6] ]]; then TMOUT=3600; fi";

    # Don't lock kernel modules, this is also enabled by the hardening profile by default
    security.lockKernelModules = false;

    # Run security analysis
    environment.systemPackages = with pkgs; [ lynis ];

    # Technically we don't need this as we use pubkey authentication
    services.fail2ban = lib.mkIf cfgServers {
      enable = true;
      ignoreIP = [
        "100.0.0.0/8"
        "127.0.0.1/8"
      ];
    };
  };
}

Impermanence

{
  lib,
  config,
  options,
  ...
}:
let
  # System-level persistent directories
  systemEtcDirs = [
    "asusd"
    "NetworkManager/system-connections"
    "nixos"
    "pacman.d/gnupg"
  ];

  systemVarCacheDirs = [ ];

  systemVarLibDirs = [
    "AccountsService/icons"
    "machines"
    "NetworkManager"
    "sbctl"
    "plasmalogin"
    "systemd"
    "nixos"
    "upower"
  ];

  systemVarLibDirsWithPerms = [
    {
      directory = "iwd";
      mode = "u=rwx,g=,o=";
    }
  ];

  # Root user persistence
  rootDirs = [
    {
      directory = ".gnupg";
      mode = "0700";
    }
    {
      directory = ".ssh";
      mode = "0700";
    }
  ];

  # User important data directories
  userDataDirs = [
    ".cargo"
    ".claude"
    ".config"
    ".java"
    ".local/share/direnv"
    ".local/share/fish"
    ".local/state"
    ".pki"
    ".tldrc"
    ".wakatime"
    "Documents"
    "Downloads"
    "Music"
    "Pictures"
    "Projects"
    "Sync"
    "Videos"
  ];

  # User cache directories to persist (important for IDE caches, etc.)
  userCacheDirs = [
    ".cache/bat"
    ".cache/.bun"
    ".cache/bookmarksrunner"
    ".cache/electron"
    ".cache/fastfetch"
    ".cache/github-copilot"
    ".cache/mesa_shader_cache"
    ".cache/nix"
    ".cache/pnpm"
    ".cache/systemsettings"
    ".cache/tldr"
  ];

  # User state directories
  userStateDirs = [
    ".local/share/icons/distrobox"
    ".local/state/syncthing"
    ".local/state/wireplumber"
  ];

  # User files to persist
  userFiles = [
    ".bash_history"
    ".wakatime.bdb"
    ".wakatime.cfg"
  ];

  # User directories with special permissions
  userDirsWithPerms = [
    {
      directory = ".gnupg";
      mode = "0700";
    }
    {
      directory = ".local/share/keyrings";
      mode = "0700";
    }
    {
      directory = ".ssh";
      mode = "0700";
    }
  ];

  # Optional service directories (gated by service config)
  optionalServiceDirs = [
    {
      condition = config.security.acme.acceptTerms;
      dirs = [
        {
          directory = "/var/lib/acme";
          user = "acme";
          group = "acme";
          mode = "0755";
        }
      ];
    }
    {
      condition = config.services.printing.enable;
      dirs = [
        {
          directory = "/var/lib/cups";
          user = "root";
          group = "root";
          mode = "0700";
        }
      ];
    }
    {
      condition = config.services.fail2ban.enable;
      dirs = [
        {
          directory = "/var/lib/fail2ban";
          user = "fail2ban";
          group = "fail2ban";
          mode = "0750";
        }
      ];
    }
    {
      condition = config.services.postgresql.enable;
      dirs = [
        {
          directory = "/var/lib/postgresql";
          user = "postgres";
          group = "postgres";
          mode = "0700";
        }
      ];
    }
    {
      condition = config.services.loki.enable;
      dirs = [
        {
          directory = "/var/lib/loki";
          user = "loki";
          group = "loki";
          mode = "0700";
        }
      ];
    }
    {
      condition = config.services.grafana.enable;
      dirs = [
        {
          directory = config.services.grafana.dataDir;
          user = "grafana";
          group = "grafana";
          mode = "0700";
        }
      ];
    }
    {
      condition = config.services.vaultwarden.enable;
      dirs = [
        {
          directory = "/var/lib/vaultwarden";
          user = "vaultwarden";
          group = "vaultwarden";
          mode = "0700";
        }
      ];
    }
    {
      condition = config.services.influxdb2.enable;
      dirs = [
        {
          directory = "/var/lib/influxdb2";
          user = "influxdb2";
          group = "influxdb2";
          mode = "0700";
        }
      ];
    }
    {
      condition = config.services.telegraf.enable;
      dirs = [
        {
          directory = "/var/lib/telegraf";
          user = "telegraf";
          group = "telegraf";
          mode = "0700";
        }
      ];
    }
    {
      condition = config.services.adguardhome.enable;
      dirs = [
        {
          directory = "/var/lib/private/AdGuardHome";
          user = "root";
          group = "root";
          mode = "0700";
        }
      ];
    }
    {
      condition = config.virtualisation.docker.enable;
      dirs = [
        {
          directory = "/var/lib/docker";
          user = "root";
          group = "root";
          mode = "0700";
        }
      ];
    }
    {
      condition = config.virtualisation.podman.enable;
      dirs = [ "/var/lib/containers" ];
    }
    {
      condition = config.services.flatpak.enable;
      dirs = [ "/var/lib/flatpak" ];
    }
    {
      condition = config.services.fwupd.enable;
      dirs = [ "/var/lib/fwupd" ];
    }
    {
      condition = config.virtualisation.libvirtd.enable;
      dirs = [ "/var/lib/libvirt" ];
    }
    {
      condition = config.services.tailscale.enable;
      dirs = [ "/var/lib/tailscale" ];
    }
    {
      condition = config.services.tailscale.enable;
      dirs = [ "/var/cache/tailscale" ];
    }
    {
      condition = config.hardware.bluetooth.enable;
      dirs = [ "/var/lib/bluetooth" ];
    }
    {
      condition = config.services.vnstat.enable;
      dirs = [ "/var/lib/vnstat" ];
    }
  ];

  # Optional user data directories (gated by config)
  optionalUserDataDirs = [
    {
      condition = config.virtualisation.podman.enable;
      dirs = [ ".local/share/containers" ];
    }
    {
      condition = config.dr460nixed.development.jetbrains or false;
      dirs = [
        ".local/share/DBeaverData"
        ".local/share/JetBrains"
        ".eclipse"
      ];
    }
    {
      condition = config.dr460nixed.gaming.enable or false;
      dirs = [
        ".local/share/PrismLauncher"
        ".local/share/Steam"
        ".local/share/lutris"
        ".steam"
        "Games"
      ];
    }
    {
      condition = config.dr460nixed.desktops.kde or false;
      dirs = [
        ".local/share/baloo"
        ".local/share/dolphin"
        ".local/share/kactivitymanagerd"
        ".local/share/klipper"
        ".local/share/knewstuff3"
        ".local/share/konsole"
        ".local/share/kpeoplevcard"
        ".local/share/kscreen"
        ".local/share/kwalletd"
        ".local/share/plasma"
        ".local/share/plasma-systemmonitor"
      ];
    }
    {
      condition = config.dr460nixed.development.tools or false;
      dirs = [
        ".ansible"
        ".gemini"
        ".local/share/heroku"
        ".local/share/opencode"
        ".local/share/pnpm"
        ".local/share/yarn"
        ".local/share/zed"
        ".vscode"
      ];
    }
    {
      condition = config.dr460nixed.development.vms or false;
      dirs = [ "VirtualBox VMs" ];
    }
    {
      condition = config.dr460nixed.desktops.enable or false;
      dirs = [
        ".firedragon"
        ".gitkraken"
        ".local/share/AyuGramDesktop"
        ".local/share/Vorta"
        ".local/share/krita"
        ".mozilla"
        ".thunderbird"
      ];
    }
    {
      condition = config.dr460nixed.sync.nextcloud or config.dr460nixed.misc.nextcloud or false;
      dirs = [
        ".local/share/Nextcloud"
        "Nextcloud"
      ];
    }
    {
      condition = config.dr460nixed.development.enable or false;
      dirs = [
        ".android"
      ];
    }
    {
      condition = config.dr460nixed.yubikey.enable or false;
      dirs = [
        ".yubico"
      ];
    }
  ];

  # Optional user cache directories (gated by config)
  optionalUserCacheDirs = [
    {
      condition = config.dr460nixed.gaming.enable or false;
      dirs = [ ".cache/lutris" ];
    }
    {
      condition = config.dr460nixed.desktops.kde or false;
      dirs = [ ".cache/konsole" ];
    }
    {
      condition = config.dr460nixed.development.tools or false;
      dirs = [ ".cache/distrobox" ];
    }
    {
      condition = config.dr460nixed.desktops.enable or false;
      dirs = [
        ".cache/BraveSoftware"
        ".cache/firedragon"
        ".cache/mozilla"
        ".cache/thunderbird"
      ];
    }
    {
      condition = config.dr460nixed.desktops.spicetify or false;
      dirs = [ ".cache/spotify" ];
    }
    {
      condition = config.dr460nixed.development.jetbrains or false;
      dirs = [ ".cache/JetBrains" ];
    }
  ];

  # Flatten optional dirs based on conditions
  optionalDirs = lib.concatMap (s: if s.condition then s.dirs else [ ]) optionalServiceDirs;
  optionalUserDirs = lib.concatMap (s: if s.condition then s.dirs else [ ]) optionalUserDataDirs;
  optionalUserCache = lib.concatMap (s: if s.condition then s.dirs else [ ]) optionalUserCacheDirs;

  cfg = config.dr460nixed.impermanence;
in
{
  # Persistent files
  config = lib.mkIf cfg.enable (
    lib.optionalAttrs (options.environment ? persistence) {
      environment.persistence."/persist" = {
        hideMounts = true;
        directories =
          map (dir: "/etc/${dir}") systemEtcDirs
          # System /var/cache directories
          ++ map (dir: "/var/cache/${dir}") systemVarCacheDirs
          # System /var/lib directories
          ++ map (dir: "/var/lib/${dir}") systemVarLibDirs
          # System /var/lib directories with special permissions
          ++ (map (entry: {
            directory = "/var/lib/${entry.directory}";
            user = entry.user or "root";
            group = entry.group or "root";
            mode = entry.mode or "0755";
          }) systemVarLibDirsWithPerms)
          # Optional service directories based on enabled services
          ++ optionalDirs
          # Catch-all for /var/cache
          ++ [ "/var/cache" ];
        users = {
          "root" = {
            directories = rootDirs;
          };
        }
        // (lib.genAttrs cfg.persistentUsers (_name: {
          directories =
            userDataDirs
            # Optional user data directories
            ++ optionalUserDirs
            # Cache directories to persist (IDE caches, etc.)
            ++ userCacheDirs
            # Optional user cache directories
            ++ optionalUserCache
            # State directories
            ++ userStateDirs
            # Directories with special permissions
            ++ userDirsWithPerms;
          files = userFiles;
        }));
      };
    }
  );
}

Live CD

{
  config,
  lib,
  ...
}:
let
  cfg = config.dr460nixed.live-cd;
in
{
  options.dr460nixed.live-cd = {
    enable = lib.mkEnableOption "live CD applications and configurations";
  };

  config = lib.mkIf cfg.enable {
    # No specific config here from misc.nix other than the option definition
    # but it's used elsewhere.
  };
}

Locales

{
  config,
  lib,
  ...
}:
let
  cfg = config.dr460nixed.locales;
  de = "de_DE.UTF-8";
  defaultLocale = "en_GB.UTF-8";
in
{
  options.dr460nixed.locales = with lib; {
    enable = mkEnableOption "Whether the operating system be having a default set of locales set." // {
      default = true;
    };
  };

  config = lib.mkIf cfg.enable {
    time = {
      hardwareClockInLocalTime = true;
      timeZone = "Europe/Berlin";
    };

    i18n = {
      inherit defaultLocale;

      extraLocaleSettings = {
        LANG = defaultLocale;
        LC_COLLATE = defaultLocale;
        LC_CTYPE = defaultLocale;
        LC_MESSAGES = defaultLocale;

        LC_ADDRESS = de;
        LC_IDENTIFICATION = de;
        LC_MEASUREMENT = de;
        LC_MONETARY = de;
        LC_NAME = de;
        LC_NUMERIC = de;
        LC_PAPER = de;
        LC_TELEPHONE = de;
        LC_TIME = de;
      };

      supportedLocales = [
        "C.UTF-8/UTF-8"
        "de_DE.UTF-8/UTF-8"
        "en_GB.UTF-8/UTF-8"
        "en_US.UTF-8/UTF-8"
      ];
    };

    # Console font
    console.keyMap = "de";
  };
}

Monitoring

{
  config,
  lib,
  ...
}:
let
  cfg = config.dr460nixed;
in
{
  options.dr460nixed = with lib; {
    grafanaStack = {
      enable = mkEnableOption "Enables the Grafana stack (Grafana, Prometheus and Loki).";
      address = mkOption {
        default = "";
        type = types.str;
        description = mdDoc ''
          The address of the Grafana frontend.
        '';
      };
    };
    prometheus = {
      adguardExporter = {
        enable = mkEnableOption "Enables Prometheus' AdGuard home exporter.";
        configfile = mkOption {
          default = "";
          type = types.str;
          description = mdDoc ''
            The path to the AdGuard home exporter config file.
          '';
        };
      };
      blackboxExporter = lib.mkEnableOption "Enables Prometheus' blackbox exporter.";
      enable = mkEnableOption "Enables Prometheus' node_exporter.";
      nginxExporter = lib.mkEnableOption "Enables Prometheus' Nginx exporter.";
    };
    promtail = {
      enable = mkEnableOption "Enables shipping systemd journal logs to Loki.";
      lokiAddress = mkOption {
        default = "";
        type = types.str;
        description = mdDoc ''
          The address of the Loki frontend.
        '';
      };
    };
  };

  config = {
    services.prometheus = {
      port = 3020;
      enable = lib.mkIf cfg.grafanaStack.enable true;

      exporters = lib.mkIf cfg.prometheus.enable {
        blackbox = lib.mkIf cfg.prometheus.blackboxExporter {
          enable = true;
          port = 9115;
          configFile = "/var/lib/prometheus/blackbox.yml";
        };
        node = {
          port = 3021;
          enabledCollectors = [ "systemd" ];
          enable = true;
        };
        nginx = lib.mkIf cfg.prometheus.nginxExporter {
          enable = true;
        };
      };

      # ingest the published nodes
      scrapeConfigs = [
        {
          job_name = "nodes";
          static_configs = [
            {
              targets = [
                "127.0.0.1:${toString config.services.prometheus.exporters.node.port}"
                "100.97.58.140:${toString config.services.prometheus.exporters.node.port}"
              ];
            }
          ];
        }
        {
          job_name = "adguard";
          static_configs = [
            {
              targets = [
                "127.0.0.1:9617"
              ];
            }
          ];
        }
        {
          job_name = "loki";
          static_configs = [
            {
              targets = [
                "${cfg.grafanaStack.address}:8030"
              ];
            }
          ];
        }
        {
          job_name = "prometheus";
          static_configs = [
            {
              targets = [
                "127.0.0.1:9113"
              ];
            }
          ];
        }
      ];
    };

    # Loki for system logs
    services.loki = lib.mkIf cfg.grafanaStack.enable {
      enable = true;
      configuration = {
        auth_enabled = false;
        ingester = {
          chunk_idle_period = "1h";
          chunk_retain_period = "30s";
          chunk_target_size = 999999;
          lifecycler = {
            address = "${cfg.grafanaStack.address}";
            ring = {
              kvstore = {
                store = "inmemory";
              };
              replication_factor = 1;
            };
          };
          max_chunk_age = "24h";
        };
        schema_config = {
          configs = [
            {
              from = "2022-06-06";
              store = "boltdb-shipper";
              object_store = "filesystem";
              schema = "v11";
              index = {
                prefix = "index_";
                period = "24h";
              };
            }
          ];
        };
        server.http_listen_port = 3030;
        storage_config = {
          boltdb_shipper = {
            active_index_directory = "/var/lib/loki/boltdb-shipper-active";
            cache_location = "/var/lib/loki/boltdb-shipper-cache";
            cache_ttl = "24h";
            shared_store = "filesystem";
          };
          filesystem = {
            directory = "/var/lib/loki/chunks";
          };
        };
        limits_config = {
          ingestion_burst_size_mb = 512;
          ingestion_rate_mb = 1024;
          reject_old_samples = true;
          reject_old_samples_max_age = "168h";
        };
        chunk_store_config = {
          max_look_back_period = "0s";
        };
        table_manager = {
          retention_deletes_enabled = false;
          retention_period = "0s";
        };
        compactor = {
          working_directory = "/var/lib/loki";
          shared_store = "filesystem";
          compactor_ring = {
            kvstore = {
              store = "inmemory";
            };
          };
        };
      };
    };

    # promtail: port 3031 (8031)
    #
    services.promtail = lib.mkIf cfg.promtail.enable {
      enable = true;
      configuration = {
        server = {
          http_listen_port = 3031;
          grpc_listen_port = 0;
        };
        clients = [
          {
            url = "http://${config.dr460nixed.promtail.lokiAddress}:3030/loki/api/v1/push";
          }
        ];
        scrape_configs = [
          {
            job_name = "journal";
            journal = {
              path = "/var/log/journal";
              max_age = "24h";
              labels = {
                job = "systemd-journal";
                host = "${config.networking.hostName}";
              };
            };
            relabel_configs = [
              {
                source_labels = [ "__journal__hostname" ];
                target_label = "host";
              }
              {
                source_labels = [ "__journal_priority" ];
                target_label = "priority";
              }
              {
                source_labels = [ "__journal_priority_keyword" ];
                target_label = "level";
              }
              {
                source_labels = [ "__journal__systemd_unit" ];
                target_label = "unit";
              }
              {
                source_labels = [ "__journal__systemd_user_unit" ];
                target_label = "user_unit";
              }
              {
                source_labels = [ "__journal__boot_id" ];
                target_label = "boot_id";
              }
              {
                source_labels = [ "__journal__comm" ];
                target_label = "command";
              }
            ];
          }
        ];
        pipeline_stages = [
          {
            json.expressions = {
              transport = "_TRANSPORT";
              unit = "_SYSTEMD_UNIT";
              msg = "MESSAGE";
              coredump_cgroup = "COREDUMP_CGROUP";
              coredump_exe = "COREDUMP_EXE";
              coredump_cmdline = "COREDUMP_CMDLINE";
              coredump_uid = "COREDUMP_UID";
              coredump_gid = "COREDUMP_GID";
            };
          }
        ];
      };
    };
    systemd.services.promtail.serviceConfig.RestartSec = "600"; # Retry every 10 minutes

    # Grafana on port 3010 (8010)
    services.grafana = lib.mkIf cfg.grafanaStack.enable {
      enable = true;
      provision = {
        enable = true;
        datasources.settings = {
          apiVersion = 1;
          datasources = [
            {
              access = "proxy";
              name = "Prometheus";
              type = "prometheus";
              url = "http://127.0.0.1:${toString config.services.prometheus.port}";
            }
            {
              access = "proxy";
              name = "Loki";
              type = "loki";
              url = "http://127.0.0.1:${toString config.services.loki.configuration.server.http_listen_port}";
            }
          ];
        };
      };
      settings = {
        analytics.reporting_enabled = false;
        live = {
          allowed_origins = [ "http://${config.dr460nixed.grafanaStack.address}:8010" ]; # Needed to get WS to work
        };
        security.admin_email = "root@dr460nf1r3.org";
        server = {
          http_addr = "127.0.0.1";
          http_port = 3010;
          protocol = "http";
          rootUrl = "http://${config.dr460nixed.grafanaStack.address}:8010";
        };
      };
    };

    # Also enable promtail on the Grafana host
    dr460nixed.promtail.enable = lib.mkIf cfg.grafanaStack.enable true;

    # Nginx reverse proxy
    services.nginx = lib.mkIf cfg.grafanaStack.enable {
      enable = true;
      upstreams = {
        "grafana" = {
          servers = {
            "127.0.0.1:${toString config.services.grafana.settings.server.http_port}" = { };
          };
        };
        "prometheus" = {
          servers = {
            "${config.dr460nixed.grafanaStack.address}:${toString config.services.prometheus.port}" = { };
          };
        };
        "loki" = {
          servers = {
            "127.0.0.1:${toString config.services.loki.configuration.server.http_listen_port}" = { };
          };
        };
        "promtail" = {
          servers = {
            "127.0.0.1:${toString config.services.promtail.configuration.server.http_listen_port}" = { };
          };
        };
      };
      virtualHosts.grafana = {
        locations."/" = {
          proxyPass = "http://grafana";
          extraConfig = ''
            proxy_set_header Host $host;
          '';
        };
        locations."/api/live/" = {
          proxyPass = "http://grafana";
          proxyWebsockets = true;
          extraConfig = ''
            proxy_set_header Host $host;
          '';
        };
        listen = [
          {
            addr = "${config.dr460nixed.grafanaStack.address}";
            port = 8010;
          }
        ];
      };
      virtualHosts.prometheus = {
        locations."/".proxyPass = "http://prometheus";
        listen = [
          {
            addr = "${config.dr460nixed.grafanaStack.address}";
            port = 8020;
          }
        ];
      };
      virtualHosts.loki = {
        locations."/".proxyPass = "http://loki";
        listen = [
          {
            addr = "${config.dr460nixed.grafanaStack.address}";
            port = 8030;
          }
        ];
      };
      virtualHosts.promtail = {
        locations."/".proxyPass = "http://promtail";
        listen = [
          {
            addr = "${config.dr460nixed.grafanaStack.address}";
            port = 8031;
          }
        ];
      };
    };
  };
}

MSMTP

{
  config,
  lib,
  ...
}:
let
  cfg = config.dr460nixed.smtp;
in
{
  options.dr460nixed.smtp = with lib; {
    enable = mkEnableOption "Enable sending mails via CMD using msmtp.";
    user = mkOption {
      type = types.str;
      description = mdDoc "The SMTP user.";
    };
    host = mkOption {
      type = types.str;
      default = "mail.garudalinux.net";
      description = mdDoc "The SMTP host.";
    };
    from = mkOption {
      type = types.str;
      description = mdDoc "The default from address.";
    };
    passwordeval = mkOption {
      type = types.str;
      description = mdDoc "The command to evaluate to get the password.";
    };
  };

  config = lib.mkIf cfg.enable {
    programs.msmtp = {
      enable = true;
      setSendmail = true;
      defaults = {
        aliases = "/etc/aliases";
        auth = "login";
        port = 465;
        tls = "on";
        tls_starttls = "off";
        tls_trust_file = "/etc/ssl/certs/ca-certificates.crt";
      };
      accounts = {
        default = {
          inherit (cfg)
            from
            host
            user
            passwordeval
            ;
        };
      };
    };
    environment.etc = {
      "aliases".text = ''
        root: ${cfg.from}
      '';
    };
  };
}

Networking

{
  config,
  lib,
  pkgs,
  ...
}:
let
  cfg = config.dr460nixed;
in
{
  networking = {
    nameservers = [
      "1.1.1.1"
      "2606:4700:4700::1111"
      "1.0.0.1"
      "2606:4700:4700::1001"
    ];
    networkmanager = lib.mkIf cfg.desktops.enable {
      # This is required to workaround Tailscale not recovering from net change
      # https://github.com/tailscale/tailscale/issues/8223
      dispatcherScripts = [
        {
          source = pkgs.writeScript "restartTailscaled" ''
            #!/usr/bin/env ${pkgs.bash}/bin/bash
            if [[ "$1" != "wlan0" ]]; then
              exit 0
            fi
            if [[ "$2" == "up" ]]; then
              if [[ $(${pkgs.iputils}/bin/ping -W 1 -c 1 garudalinux.org) != 0 ]]; then
                logger "Wlan0 up, restarting tailscaled"
                ${pkgs.systemd}/bin/systemctl restart tailscaled
              fi
            fi
          '';
          type = "basic";
        }
      ];
      dns = lib.mkForce "none";
      enable = true;
    };

    nftables.enable = true;
  };

  services = {
    openssh.enable = true;
    vnstat.enable = true;
  };

  programs.mosh.enable = true;
}

Nix

{
  inputs,
  pkgs,
  self,
  lib,
  ...
}:
let
  corePkgs = import ../apps/core-packages.nix { inherit pkgs lib; };
in
{
  config = {
    nix = {
      extraOptions = ''
        http-connections = 0
        warn-dirty = false
      '';

      nixPath = [ "nixpkgs=${inputs.nixpkgs}" ];

      settings = {
        experimental-features = [
          "nix-command"
          "flakes"
        ];
        accept-flake-config = true;
        keep-derivations = true;
        keep-outputs = true;
        keep-going = true;
        log-lines = 20;
        max-jobs = "auto";
        sandbox = pkgs.stdenv.isLinux;
        flake-registry = "/etc/nix/registry.json";

        inherit (inputs.self.dragonLib.binaryCaches) substituters;
        inherit (inputs.self.dragonLib.binaryCaches) trusted-public-keys;
      };
    };

    environment = {
      etc = with inputs; {
        "nix/flake-channels/home-manager".source = home-manager;
        "nix/flake-channels/nixpkgs".source = nixpkgs;
        "nix/flake-channels/system".source = self;
        "nixos/flake".source = self;
      };
      systemPackages = corePkgs;
    };
  };
}

NVIDIA

{
  config,
  lib,
  pkgs,
  ...
}:
let
  cfg = config.dr460nixed.nvidia;
in
{
  options.dr460nixed.nvidia = with lib; {
    enable = mkEnableOption "Whether to enable NVIDIA GPU support with proprietary drivers.";
    open = lib.mkEnableOption "Whether to use the open-source NVIDIA kernel module (Turing+)." // {
      default = true;
    };
    prime = {
      enable = mkEnableOption "Whether to enable PRIME offload for hybrid graphics.";
      nvidiaBusId = mkOption {
        default = "";
        type = types.str;
        description = mdDoc ''
          Bus ID of the NVIDIA GPU, e.g. "PCI:1:0:0".
        '';
      };
      amdgpuBusId = mkOption {
        default = null;
        type = types.nullOr types.str;
        description = mdDoc ''
          Bus ID of the AMD iGPU for AMD+NVIDIA hybrid setups.
        '';
      };
      intelBusId = mkOption {
        default = null;
        type = types.nullOr types.str;
        description = mdDoc ''
          Bus ID of the Intel iGPU for Intel+NVIDIA hybrid setups.
        '';
      };
    };
  };

  config = lib.mkIf cfg.enable (
    lib.mkMerge [
      {
        boot = {
          initrd.kernelModules = [
            "nvidia"
            "nvidia_drm"
            "nvidia_modeset"
            "nvidia_uvm"
          ];
          blacklistedKernelModules = [ "nouveau" ];
        };

        services.xserver.videoDrivers = [ "nvidia" ];

        hardware.nvidia = {
          modesetting.enable = true;
          inherit (cfg) open;
          nvidiaSettings = true;
          package = config.boot.kernelPackages.nvidiaPackages.beta;
          powerManagement.enable = true;
        };
        services.lact.enable = true;

        hardware.graphics = {
          extraPackages = with pkgs; [
            nvidia-vaapi-driver
          ];
        };
      }

      (lib.mkIf cfg.prime.enable {
        hardware.nvidia.prime = {
          offload = {
            enable = true;
            enableOffloadCmd = true;
          };
          inherit (cfg.prime) nvidiaBusId;
          amdgpuBusId = lib.mkIf (cfg.prime.amdgpuBusId != null) cfg.prime.amdgpuBusId;
          intelBusId = lib.mkIf (cfg.prime.intelBusId != null) cfg.prime.intelBusId;
        };

        hardware.nvidia.powerManagement.finegrained = true;
      })
    ]
  );
}

OCI

{
  config,
  lib,
  ...
}:
let
  cfg = config.dr460nixed.oci;
in
{
  options.dr460nixed.oci = with lib; {
    enable = mkEnableOption "Enable common options for Oracle cloud instances.";
  };

  config = lib.mkIf cfg.enable {
    boot = {
      kernelParams = [
        "nvme.shutdown_timeout=10"
        "nvme_core.shutdown_timeout=10"
        "libiscsi.debug_libiscsi_eh=1"
        "crash_kexec_post_notifiers"
        "console=tty1"
        "console=ttyS0"
        "console=ttyAMA0,115200"
      ];
      loader.grub = {
        device = "nodev";
        efiInstallAsRemovable = true;
        efiSupport = true;
        enable = lib.mkForce true;
        extraConfig = ''
          serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1
          terminal_input --append serial
          terminal_output --append serial
        '';
        splashImage = null;
      };
    };

    # https://docs.oracle.com/en-us/iaas/Content/Compute/Tasks/configuringntpservice.htm#Configuring_the_Oracle_Cloud_Infrastructure_NTP_Service_for_an_Instance
    networking.timeServers = [ "169.254.169.254" ];

    nix.settings.auto-optimise-store = lib.mkForce false;

    hardware.cpu = {
      amd.updateMicrocode = lib.mkForce false;
      intel.updateMicrocode = lib.mkForce false;
    };
  };
}

Performance

{
  config,
  lib,
  ...
}:
let
  cfg = config.dr460nixed.performance;
in
{
  options.dr460nixed.performance = {
    enable = lib.mkEnableOption "performance optimizations";
  };

  config = lib.mkIf cfg.enable {
    garuda.performance-tweaks.enable = true;
  };
}

Servers

{
  config,
  lib,
  ...
}:
let
  cfg = config.dr460nixed.servers;
in
{
  options.dr460nixed.servers = with lib; {
    enable = mkEnableOption "Whether this device is a server.";
  };

  config = lib.mkIf cfg.enable {
    # The common used config is not available
    programs.fish.shellInit = lib.mkForce ''
      set fish_greeting
      fastfetch -l nixos
    '';

    # Automatic server upgrades
    dr460nixed.auto-upgrade.enable = lib.mkDefault true;

    # No custom aliases
    dr460nixed.shells.enable = lib.mkDefault false;

    # These aren't needed on servers, but default on GNS
    garuda = {
      audio.pipewire.enable = false;
      hardware.enable = false;
      networking.enable = false;
    };
    boot.plymouth.enable = false;
  };
}

Shells

{
  config,
  lib,
  pkgs,
  ...
}:
let
  cfg = config.dr460nixed.shells;
in
{
  options.dr460nixed.shells.enable =
    lib.mkEnableOption "Whether the shell should receive our aliases and themes."
    // {
      default = true;
    };

  config = lib.mkIf cfg.enable {
    programs = {
      bash = {
        shellAliases = {
          "gpl" = "${pkgs.curl}/bin/curl https://www.gnu.org/licenses/gpl-3.0.txt -o LICENSE";
          "grep" = "${pkgs.ugrep}/bin/ugrep";
        };
      };

      fish = {
        shellAbbrs = {
          "gpl" = "${pkgs.curl}/bin/curl https://www.gnu.org/licenses/gpl-3.0.txt -o LICENSE";
        };
        shellAliases = {
          "grep" = "${pkgs.ugrep}/bin/ugrep";
        };
        useBabelfish = true;
      };
    };
  };
}

Syncthing

{
  config,
  dragonLib,
  lib,
  pkgs,
  ...
}:
let
  cfg = config.dr460nixed.syncthing;
  settingsFormat = pkgs.formats.json { };
in
{
  options.dr460nixed.syncthing = {
    enable = lib.mkEnableOption "Enable common file synchronisation between devices.";
    key = lib.mkOption {
      default = null;
      type = lib.types.nullOr lib.types.str;
      description = lib.mdDoc ''
        The key to use for Syncthing.
      '';
    };
    cert = lib.mkOption {
      default = null;
      type = lib.types.nullOr lib.types.str;
      description = lib.mdDoc ''
        The cert to use for Syncthing.
      '';
    };
    devices = lib.mkOption {
      default = dragonLib.syncthing.getDevicesFor config.networking.hostName;
      type = lib.types.attrsOf (
        lib.types.submodule (
          { name, ... }:
          {
            freeformType = settingsFormat.type;
            options = {
              name = lib.mkOption {
                type = lib.types.str;
                default = name;
                description = lib.mdDoc ''
                  The name of the device.
                '';
              };
              id = lib.mkOption {
                type = lib.types.str;
                description = lib.mdDoc ''
                  The device ID. See <https://docs.syncthing.net/dev/device-ids.html>.
                '';
              };
              autoAcceptFolders = lib.mkOption {
                type = lib.types.bool;
                default = false;
                description = lib.mdDoc ''
                  Automatically create or share folders that this device advertises at the default path.
                  See <https://docs.syncthing.net/users/config.html?highlight=autoaccept#config-file-format>.
                '';
              };
            };
          }
        )
      );
      description = lib.mdDoc ''
        The devices to sync with.
      '';
    };
    devicesNames = lib.mkOption {
      default = lib.attrNames cfg.devices;
      type = lib.types.listOf lib.types.str;
      description = lib.mdDoc ''
        The names of the devices to sync with.
      '';
    };
    user = lib.mkOption {
      default = null;
      type = lib.types.nullOr lib.types.str;
      description = lib.mdDoc ''
        The user to run syncthing as.
      '';
    };
    folders = lib.mkOption {
      default = { };
      type = lib.types.attrsOf (
        lib.types.submodule {
          options = {
            id = lib.mkOption { type = lib.types.str; };
            path = lib.mkOption { type = lib.types.str; };
            devices = lib.mkOption {
              type = lib.types.listOf lib.types.str;
              default = [ ];
            };
            ignorePatterns = lib.mkOption {
              type = lib.types.listOf lib.types.str;
              default = [
                ".directory"
                ".thumbnails"
              ];
            };
          };
        }
      );
      description = lib.mdDoc "Folders to sync.";
    };
  };

  config = lib.mkIf cfg.enable {
    services.syncthing = {
      inherit (cfg) cert;
      dataDir = lib.mkIf (cfg.user != null) "/home/${cfg.user}";
      enable = true;
      inherit (cfg) key;
      settings = {
        inherit (cfg) devices;
        folders = lib.mapAttrs (_name: folder: {
          inherit (folder)
            id
            path
            devices
            ignorePatterns
            ;
        }) cfg.folders;
        options = {
          localAnnounceEnabled = true;
          urAccepted = -1;
        };
      };
      user = lib.mkIf (cfg.user != null) cfg.user;
    };
  };
}

Tailscale TLS

{
  config,
  lib,
  pkgs,
  ...
}:
let
  cfg = config.dr460nixed.tailscale-tls;
  domainExpression =
    if cfg.domain-override != null then
      cfg.domain-override
    else
      "$(${pkgs.tailscale}/bin/tailscale cert 2>&1 | grep use | cut -d '\"' -f2)";
in
{
  options.dr460nixed.tailscale-tls = with lib; {
    enable = mkEnableOption "Automatic Tailscale certificates renewal";

    target = mkOption {
      type = types.str;
      description = "Where to put certificates";
      default = "/var/lib/tailscale-tls";
    };

    mode = mkOption {
      type = types.str;
      description = "File mode for certificates";
      default = "0640";
    };

    domain-override = mkOption {
      type = types.nullOr types.str;
      description = "Override domain. Defaults to suggested one by tailscale";
      default = null;
    };
  };

  config = lib.mkIf cfg.enable {
    users.users.tailscale-tls = {
      group = "tailscale-tls";
      home = "/var/lib/tailscale-tls";
      isSystemUser = true;
    };

    users.groups.tailscale-tls = { };

    systemd.services.tailscale-tls = {
      description = "Automatic Tailscale certificates";

      after = [
        "network-pre.target"
        "tailscale.service"
      ];
      wants = [
        "network-pre.target"
        "tailscale.service"
      ];
      wantedBy = [ "multi-user.target" ];

      serviceConfig.Type = "oneshot";
      script = ''
        status="Starting"

        until [ $status = "Running" ]; do
          sleep 2
          status=$(${pkgs.tailscale}/bin/tailscale status -json | ${pkgs.jq}/bin/jq -r .BackendState)
        done

        mkdir -p "${cfg.target}"

        DOMAIN=${domainExpression}

        ${pkgs.tailscale}/bin/tailscale cert \
          --cert-file "${cfg.target}/cert.crt" \
          --key-file "${cfg.target}/key.key" \
          "$DOMAIN"

        chown -R tailscale-tls:tailscale-tls "${cfg.target}"

        chmod ${cfg.mode} "${cfg.target}/cert.crt" "${cfg.target}/key.key"
      '';
    };

    systemd.timers.tailscale-tls = {
      description = "Automatic Tailscale certificates renewal";

      after = [
        "network-pre.target"
        "tailscale.service"
      ];
      wants = [
        "network-pre.target"
        "tailscale.service"
      ];
      wantedBy = [ "multi-user.target" ];

      timerConfig = {
        OnCalendar = "weekly";
        Persistent = "true";
        Unit = "tailscale-tls.service";
      };
    };
  };
}

Tailscale

{
  config,
  lib,
  ...
}:
let
  cfg = config.dr460nixed.tailscale;
in
{
  options.dr460nixed.tailscale = with lib; {
    enable = mkEnableOption "Tailscale client daemon";
  };

  config = lib.mkIf cfg.enable {
    services.tailscale.enable = true;

    networking.firewall.trustedInterfaces = [ "tailscale0" ];
  };
}

Tor

{
  config,
  lib,
  ...
}:
let
  cfg = config.dr460nixed.tor;
in
{
  options.dr460nixed.tor = {
    enable = lib.mkEnableOption "the Tor network";
  };

  config = lib.mkIf cfg.enable {
    services.tor = {
      client.dns.enable = true;
      client.enable = true;
      enable = true;
      torsocks.enable = true;
    };
  };
}

Users

{
  config,
  lib,
  ...
}:
let
  inherit (lib) mkOption types mdDoc;
  cfg = config.dr460nixed.users;

  ifTheyExist = groups: builtins.filter (group: builtins.hasAttr group config.users.groups) groups;
in
{
  options.dr460nixed.users = mkOption {
    description = mdDoc "User accounts to configure.";
    default = { };
    type = types.attrsOf (
      types.submodule (_: {
        options = {
          isNormalUser = mkOption {
            type = types.bool;
            default = true;
            description = mdDoc "Whether the user is a normal user.";
          };
          uid = mkOption {
            type = types.nullOr types.int;
            default = null;
            description = mdDoc "UID for the user.";
          };
          gid = mkOption {
            type = types.nullOr types.int;
            default = null;
            description = mdDoc "GID for the user.";
          };
          extraGroups = mkOption {
            type = types.listOf types.str;
            default = [
              "audio"
              "video"
              "wheel"
            ];
            description = mdDoc "Extra groups for the user.";
          };
          shellGroups = mkOption {
            type = types.listOf types.str;
            default = [ ];
            description = mdDoc "Groups to add only if they exist on the system.";
          };
          authorizedKeyFiles = mkOption {
            type = types.listOf types.path;
            default = [ ];
            description = mdDoc "SSH authorized key files.";
          };
          homeManager = mkOption {
            type = types.submodule (_: {
              options = {
                enable = lib.mkEnableOption "Enable home-manager for this user.";
                shellAliases = mkOption {
                  type = types.attrsOf types.str;
                  default = { };
                  description = mdDoc "User-specific shell aliases.";
                };
                fishAbbreviations = mkOption {
                  type = types.attrsOf types.str;
                  default = { };
                  description = mdDoc "User-specific fish abbreviations.";
                };
                git = {
                  userName = mkOption {
                    type = types.str;
                    default = "";
                    description = mdDoc "Git user name.";
                  };
                  userEmail = mkOption {
                    type = types.str;
                    default = "";
                    description = mdDoc "Git user email.";
                  };
                  signingKey = mkOption {
                    type = types.nullOr types.str;
                    default = null;
                    description = mdDoc "GPG signing key for git.";
                  };
                };
                stateVersion = mkOption {
                  type = types.str;
                  default = "26.05";
                  description = mdDoc "Home-manager state version.";
                };
              };
            });
            default = { };
            description = mdDoc "Home-manager configuration for this user.";
          };
        };
      })
    );
  };

  config = {
    users.deterministicIds =
      (lib.mapAttrs (_name: u: {
        inherit (u) uid gid;
      }) (lib.filterAttrs (_name: u: u.uid != null || u.gid != null) cfg))
      // {
        acme.uid = 999;
        acme.gid = 999;
        adbusers.uid = 998;
        adbusers.gid = 998;
        adguard.uid = 977;
        adguard.gid = 977;
        anubis.uid = 961;
        anubis.gid = 961;
        avahi.uid = 997;
        avahi.gid = 997;
        chaotic_op.uid = 996;
        chaotic_op.gid = 996;
        cloudflared.uid = 972;
        cloudflared.gid = 972;
        code-server.uid = 967;
        code-server.gid = 967;
        dhcpcd.uid = 976;
        dhcpcd.gid = 976;
        fwupd-refresh.uid = 975;
        fwupd-refresh.gid = 975;
        flatpak.uid = 974;
        flatpak.gid = 974;
        forgejo.uid = 963;
        forgejo.gid = 963;
        gamemode.uid = 969;
        gamemode.gid = 969;
        geoclue.uid = 959;
        geoclue.gid = 959;
        git.uid = 960;
        git.gid = 960;
        grafana.uid = 995;
        grafana.gid = 995;
        incus-admin.uid = 665;
        incus-admin.gid = 665;
        influxdb2.uid = 994;
        influxdb2.gid = 994;
        jellyfin.uid = 970;
        jellyfin.gid = 970;
        loki.uid = 993;
        loki.gid = 993;
        mandb.uid = 954;
        mandb.gid = 954;
        minecraft.uid = 973;
        minecraft.gid = 973;
        netdata.uid = 979;
        netdata.gid = 979;
        nixos.uid = 1001;
        nixos.gid = 1001;
        nm-iodine.uid = 992;
        nm-iodine.gid = 992;
        node-exporter.uid = 991;
        node-exporter.gid = 991;
        nscd.uid = 990;
        nscd.gid = 990;
        plocate.uid = 989;
        plocate.gid = 989;
        polkituser.uid = 988;
        polkituser.gid = 988;
        paperless.uid = 966;
        paperless.gid = 966;
        plasmalogin.uid = 958;
        plasmalogin.gid = 958;
        pcscd.uid = 953;
        pcscd.gid = 953;
        podman.uid = 968;
        podman.gid = 968;
        proc.uid = 971;
        proc.gid = 971;
        promtail.uid = 987;
        promtail.gid = 987;
        redis-paperless.uid = 965;
        redis-paperless.gid = 965;
        resolvconf.uid = 964;
        resolvconf.gid = 964;
        rtkit.uid = 986;
        rtkit.gid = 986;
        sshd.uid = 985;
        sshd.gid = 985;
        systemd-coredump.uid = 984;
        systemd-coredump.gid = 984;
        systemd-oom.uid = 983;
        systemd-oom.gid = 983;
        tailscale-tls.uid = 978;
        tailscale-tls.gid = 978;
        telegraf.uid = 982;
        telegraf.gid = 982;
        vnstatd.uid = 981;
        vnstatd.gid = 981;
        wakapi.uid = 962;
        wakapi.gid = 962;
        wireshark.uid = 957;
        wireshark.gid = 957;
        wpa_supplicant.uid = 956;
        wpa_supplicant.gid = 956;
        msr.gid = 955;
      };

    sops.secrets = {
      "passwords/root".neededForUsers = true;
    };

    users = {
      mutableUsers = false;
      users = lib.mkMerge [
        (lib.mapAttrs (name: u: {
          inherit (u) isNormalUser;
          extraGroups = u.extraGroups ++ ifTheyExist u.shellGroups;
          hashedPasswordFile = config.sops.secrets."passwords/${name}".path;
          home = "/home/${name}";
          openssh.authorizedKeys.keyFiles = u.authorizedKeyFiles;
          autoSubUidGidRange = false;
          subGidRanges = lib.mkIf config.virtualisation.podman.enable [
            {
              count = 65536;
              startGid = (if u.uid != null then u.uid else 1000) * 1000;
            }
          ];
          subUidRanges = [
            {
              count = 65536;
              startUid = (if u.uid != null then u.uid else 1000) * 1000;
            }
          ];
        }) cfg)
        {
          root = {
            hashedPassword = null;
            hashedPasswordFile = lib.mkForce config.sops.secrets."passwords/root".path;
          };
        }
      ];
    };

    security.sudo.extraRules = [
      {
        groups = [ "wheel" ];
        commands =
          let
            currentSystem = "/run/current-system/";
            storePath = "/nix/store/";
          in
          [
            {
              command = "${storePath}/*/bin/switch-to-configuration";
              options = [
                "SETENV"
                "NOPASSWD"
              ];
            }
            {
              command = "${currentSystem}/sw/bin/nix-store";
              options = [
                "SETENV"
                "NOPASSWD"
              ];
            }
            {
              command = "${currentSystem}/sw/bin/nixos-rebuild";
              options = [ "NOPASSWD" ];
            }
            {
              command = "${currentSystem}/sw/bin/nix-collect-garbage";
              options = [
                "SETENV"
                "NOPASSWD"
              ];
            }
            {
              command = "${currentSystem}/sw/bin/systemctl";
              options = [ "NOPASSWD" ];
            }
          ];
      }
    ];
  };
}

Wireguard

{
  config,
  lib,
  ...
}:
(
  let
    cfg = config.dr460nixed.wireguard;
    inherit (lib)
      mkOption
      mkEnableOption
      mkIf
      types
      mapAttrs'
      nameValuePair
      ;
  in
  {
    options.dr460nixed.wireguard = {
      enable = mkEnableOption "WireGuard connections via systemd-networkd";

      interfaces = mkOption {
        default = { };
        type = types.attrsOf (
          types.submodule {
            options = {
              address = mkOption {
                type = types.listOf types.str;
                description = "List of addresses to assign to the interface.";
              };
              listenPort = mkOption {
                type = types.int;
                default = 51820;
                description = "Port to listen on for incoming connections.";
              };
              privateKeySecretName = mkOption {
                type = types.str;
                description = "The name of the sops secret containing the private key.";
              };
              firewallMark = mkOption {
                type = types.nullOr types.int;
                default = null;
                description = "Firewall mark to set on packets.";
              };
              routeTable = mkOption {
                type = types.nullOr types.str;
                default = "main";
                description = "Routing table for the WireGuard interface.";
              };
              peers = mkOption {
                type = types.listOf (
                  types.submodule {
                    options = {
                      publicKey = mkOption {
                        type = types.str;
                        description = "Public key of the peer.";
                      };
                      allowedIPs = mkOption {
                        type = types.listOf types.str;
                        description = "List of IP ranges allowed from this peer.";
                      };
                      endpoint = mkOption {
                        type = types.nullOr types.str;
                        default = null;
                        description = "Endpoint address and port of the peer.";
                      };
                      persistentKeepalive = mkOption {
                        type = types.nullOr types.int;
                        default = null;
                        description = "Persistent keepalive interval in seconds.";
                      };
                    };
                  }
                );
                default = [ ];
                description = "List of WireGuard peers.";
              };
            };
          }
        );
        description = "WireGuard interface configurations.";
      };
    };

    config = mkIf cfg.enable {
      # Ensure systemd-networkd is used
      networking.useNetworkd = true;
      systemd.network.enable = true;

      # Open firewall ports
      networking.firewall.allowedUDPPorts = lib.mapAttrsToList (
        _name: value: value.listenPort
      ) cfg.interfaces;

      # systemd-networkd configuration
      systemd.network = {
        networks = mapAttrs' (
          name: value:
          (nameValuePair "50-${name}" {
            matchConfig.Name = name;
            inherit (value) address;
            networkConfig.IPv6PrivacyExtensions = "no";
          })
        ) cfg.interfaces;

        netdevs = mapAttrs' (
          name: value:
          (nameValuePair "50-${name}" {
            netdevConfig = {
              Kind = "wireguard";
              Name = name;
            };
            wireguardConfig = lib.filterAttrs (_n: v: v != null) {
              ListenPort = value.listenPort;
              PrivateKeyFile = config.sops.secrets."${value.privateKeySecretName}".path;
              RouteTable = value.routeTable;
              FirewallMark = value.firewallMark;
            };
            wireguardPeers = map (
              peer:
              lib.filterAttrs (_n: v: v != null) {
                PublicKey = peer.publicKey;
                AllowedIPs = peer.allowedIPs;
                Endpoint = peer.endpoint;
                PersistentKeepalive = peer.persistentKeepalive;
              }
            ) value.peers;
          })
        ) cfg.interfaces;
      };
    };
  }
)

Yubikey

{
  config,
  lib,
  pkgs,
  ...
}:
let
  cfg = config.dr460nixed.yubikey;
in
{
  options.dr460nixed.yubikey = {
    enable = lib.mkEnableOption "Yubikey support";
  };

  config = lib.mkIf cfg.enable {
    hardware.gpgSmartcards.enable = true;
    services.pcscd = {
      enable = true;
      plugins = [ pkgs.ccid ];
    };
    services.udev.packages = [ pkgs.yubikey-personalization ];

    environment.systemPackages = [ pkgs.yubioath-flutter ];

    security.pam.yubico = {
      debug = false;
      enable = true;
      mode = "challenge-response";
    };
  };
}

ZFS

{
  config,
  lib,
  ...
}:
let
  cfg = config.dr460nixed.zfs;
in
{
  options.dr460nixed.zfs = with lib; {
    enable = mkEnableOption "Configures common options for using ZFS on NixOS.";
    sendMails = mkOption {
      default = false;
      type = types.bool;
      description = mdDoc ''
        Enables sending status reports about ZFS maintenance via email.
      '';
    };
  };

  config = lib.mkIf cfg.enable {
    boot.supportedFilesystems = [ "zfs" ];

    boot.zfs.requestEncryptionCredentials = true;

    services.zfs = {
      autoScrub = {
        enable = true;
        interval = "weekly";
      };
      trim = {
        enable = true;
        interval = "weekly";
      };
    };

    dr460nixed.smtp.enable = lib.mkIf cfg.sendMails true;

    services.zfs.zed.enableMail = lib.mkIf cfg.sendMails false;

    services.telegraf.extraConfig.inputs = lib.mkIf config.services.telegraf.enable {
      zfs.poolMetrics = true;
    };
  };
}

Credits

Main sources

A special thanks to PedroHLC, who always gives great advice and who is also the reason I’m using NixOS today. Also, I studied Mysterio77’s and NotAShelf’s Nix configurations while building this one.

Further helpful resources (sorted alphabetically)