{ config, inputs, outputs, pkgs, ... }: {
  boot = {
    initrd.kernelModules = [ "zfs" ];
    kernel.sysctl = {
      "kernel.hostname" = "arrakis.bitgnome.net";
      "net.ipv4.ip_forward" = 1;
      "net.netfilter.nf_log_all_netns" = 1;
      #"net.ipv4.conf.all.proxy_arp" = 1;
    };
    kernelPackages = pkgs.linuxPackages_6_18;
    loader = {
      efi = {
        canTouchEfiVariables = true;
        efiSysMountPoint = "/efiboot/efi1";
      };
      systemd-boot = {
        enable = true;
        extraInstallCommands = ''
          ${pkgs.rsync}/bin/rsync -av --delete /efiboot/efi1/ /efiboot/efi2
        '';
        memtest86.enable = true;
      };
      timeout = 3;
    };
    supportedFilesystems = [ "zfs" ];
    zfs = {
      forceImportRoot = false;
      package = pkgs.zfs_2_4;
    };
  };

  environment.etc = {
    "netns/vpn/resolv.conf".text = ''
      nameserver 10.64.0.1
      options edns0
    '';

    "nftables-vpn.conf".text = ''
      # VPN firewall
  
      flush ruleset
  
      table inet filter {
      	chain input {
      		type filter hook input priority filter; policy drop;
  
      		# established/related connections
      		ct state established,related accept
  
      		# invalid connections
      		ct state invalid drop
  
      		# loopback interface
      		iif lo accept
  
      		# ICMP (routers may also want: mld-listener-query, nd-router-solicit)
      		#ip6 nexthdr icmpv6 icmpv6 type { destination-unreachable, echo-reply, echo-request, nd-neighbor-advert, nd-neighbor-solicit, nd-router-advert, packet-too-big, parameter-problem, time-exceeded } accept
      		ip protocol icmp icmp type { destination-unreachable, echo-reply, echo-request, parameter-problem, router-advertisement, source-quench, time-exceeded } accept
   
      		# services
      		iif veth.vpn tcp dport 8080 accept # qBittorrent
      		iif veth.vpn tcp dport 9696 accept # Prowlarr
      		iifname wg1 tcp dport { 49152-65535 } accept # Transmission
  
      		# drop everything else
      		counter drop
      	}
      
      	chain output {
      		type filter hook output priority filter; policy drop;
  
      		# explicitly allow my DNS traffic without VPN
      		skuid nipsy ip daddr 192.168.1.1 tcp dport domain accept
      		skuid nipsy ip daddr 192.168.1.1 udp dport domain accept
  
      		# explicitly allow my traffic without VPN
      		oifname veth.vpn skuid nipsy tcp sport 8080 accept # qBittorrent
      		oifname veth.vpn skuid nipsy tcp sport 9696 accept # Prowlarr
      		oifname veth.vpn skuid nipsy ip daddr 192.168.1.2 tcp dport { 7878, 8686, 8787, 8989 } accept # Prowlarr to { Radarr, Lidarr, Readarr, Sonarr }
      		oif lo skuid nipsy ip daddr 192.168.1.3 tcp dport 8080 accept # Prowlarr to qBittorrent
  
      		# allow any traffic out through VPN
      		oifname wg1 accept
  
      		# drop everything else
      		counter drop
      	}
  
      	chain forward {
      		type filter hook forward priority filter; policy drop;
      	}
      }
    '';
  };

  environment.systemPackages = [
    #pkgs.bitcoind
    #pkgs.igir
    pkgs.lidarr
    pkgs.mailutils
    pkgs.megacmd
    pkgs.prowlarr
    pkgs.qbittorrent-nox
    pkgs.radarr
    pkgs.rdiff-backup
    pkgs.readarr
    pkgs.sonarr
    pkgs.wpa_supplicant
  ];

  imports = [
    ./disks.nix
    ./hardware-configuration.nix
    ./services.nix
    ../common/core
    #../common/optional/db.nix
    ../common/optional/dev.nix
    #../common/optional/ebooks.nix
    ../common/optional/games.nix
    ../common/optional/google-authenticator.nix
    ../common/optional/misc.nix
    ../common/optional/multimedia.nix
    #../common/optional/pipewire.nix
    #../common/optional/sdr.nix
    ../common/optional/services/chrony.nix
    ../common/optional/services/openssh.nix
    #../common/optional/services/xorg.nix
    #../common/optional/sound.nix
    ../common/optional/wdt.nix
    ../common/optional/zfs.nix
    ../common/users/lin
    ../common/users/nipsy
    ../common/users/root
  ];

  networking = {
    defaultGateway = {
      address = "192.168.1.1";
      interface = "enp6s0";
    };
    hostId = "2ae4c89f";
    hostName = "arrakis";
    interfaces = {
      enp6s0 = {
        ipv4.addresses = [
          { address = "192.168.1.2"; prefixLength = 24; }
        ];
      };
    };
    nameservers = [ "192.168.1.1" ];
    nftables.enable = true;
    search = [
      "bitgnome.net"
    ];
    useDHCP = false;
    wg-quick.interfaces = {
      wg0 = {
        address = [
          "10.4.20.1/24"
        ];
        listenPort = 51820;
        peers = [
          { # black-sheep
            allowedIPs = [ "10.4.20.2/32" ];
            presharedKeyFile = "${config.sops.secrets."wireguard/black-sheep_psk".path}";
            publicKey = "wQsGWsfXI2+GmKHdCH2V2xIeTyV2YlH/IFp6gerxcW8=";
          }
          { # lilnasx
            allowedIPs = [ "10.4.20.3/32" ];
            presharedKeyFile = "${config.sops.secrets."wireguard/lilnasx_psk".path}";
            publicKey = "87cANtuPf28vGrB0uL69/tXgsD/30FUYt/XevQjpz3o=";
          }
          { # ramped
            allowedIPs = [ "10.4.20.4/32" ];
            presharedKeyFile = "${config.sops.secrets."wireguard/ramped_psk".path}";
            publicKey = "TvE3f0QKJXUn8pxmKMaztFvRIdi9z9dxNDN2KNdtRXQ=";
          }
          { # homer
            allowedIPs = [ "10.4.20.5/32" ];
            presharedKeyFile = "${config.sops.secrets."wireguard/homer_psk".path}";
            publicKey = "FBc2ZnypwHgjLQcZrwzPR35gax8JsXLa1ZcZy6iAc3Q=";
          }
          { # treebeard
            allowedIPs = [ "10.4.20.6/32" ];
            presharedKeyFile = "${config.sops.secrets."wireguard/treebeard_psk".path}";
            publicKey = "aYh7q1QNmz6TLYx5OsZcyHQe45Dv0SyIOCtRp1NHDU8=";
          }
          { # lolli
            allowedIPs = [ "10.4.20.7/32" ];
            presharedKeyFile = "${config.sops.secrets."wireguard/lolli_psk".path}";
            publicKey = "npt86Lt/f/9J3qS6u6C0X1MFHIONaA5PKE6lzwwUxTc=";
          }
          { # timetrad
            allowedIPs = [ "10.4.20.8/32" ];
            presharedKeyFile = "${config.sops.secrets."wireguard/timetrad_psk".path}";
            publicKey = "/lWCEMGRIr3Gl/3GQYuweAKylhH5H2KqamiXeocYFVM=";
          }
          { # fangorn
            allowedIPs = [ "10.4.20.9/32" ];
            presharedKeyFile = "${config.sops.secrets."wireguard/fangorn_psk".path}";
            publicKey = "G4oahOfaCR+ecXLGM2ilPYzqX6x8v/6z8VIo2vP2RC4=";
          }
          { # ginaz
            allowedIPs = [ "10.4.20.254/32" ];
            presharedKeyFile = "${config.sops.secrets."wireguard/ginaz_psk".path}";
            publicKey = "GuE9PVeS0IDTcaBxOSJmKlvEx2xflxwVGOU+uM0HhBk=";
          }
        ];
        privateKeyFile = "${config.sops.secrets."wireguard/arrakis_key".path}";
      };
    };
    wireless = {
      enable = true;
      networks = {
        "Crystal Palace" = {
          pskRaw = "ext:psk_crystal_palace";
        };
      };
      secretsFile = "${config.sops.secrets."wpa_supplicant".path}";
    };
  };

  nixpkgs = {
    config = {
      allowUnfree = true;
    };
    hostPlatform = "x86_64-linux";
    overlays = [
      inputs.nvidia-patch.overlays.default
      outputs.overlays.additions
      outputs.overlays.modifications
      outputs.overlays.master-packages
      outputs.overlays.stable-packages
      #outputs.overlays.wine9_22-packages
    ];
  };

  sops = {
    age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
    defaultSopsFile = ../secrets/arrakis.yaml;

    secrets = {
      "nftables/ssh" = {};
      "nix-access-token-github" = {};
      "ssh_config".path = "/root/.ssh/config";
      "wireguard/arrakis_key" = {};
      "wireguard/black-sheep_psk" = {};
      "wireguard/fangorn_psk" = {};
      "wireguard/ginaz_psk" = {};
      "wireguard/homer_psk" = {};
      "wireguard/lilnasx_psk" = {};
      "wireguard/lolli_psk" = {};
      "wireguard/ramped_psk" = {};
      "wireguard/timetrad_psk" = {};
      "wireguard/treebeard_psk" = {};
      "wireguard/wg1_conf" = {};
      "wpa_supplicant" = {};
    };
  };

  system.activationScripts = {
    fix-acl-cgit-home-nipsy = ''
      ${pkgs.acl}/bin/setfacl -m u:cgit:--x /home/nipsy
    '';
  };

  system.stateVersion = "23.11";

  systemd.services = {

    "lidarr" = {
      after = [ "network.target" ];
      description = "Lidarr Daemon";
      serviceConfig = {
        User = "nipsy";
        Group = "nipsy";
        Restart = "always";
        RestartSec= 5;
        Type = "simple";
        ExecStart = "${pkgs.lidarr}/bin/Lidarr -nobrowser";
        TimeoutStopSec = 20;
        PIDFile = "/home/nipsy/.config/Lidarr/lidarr.pid";
      };
      wantedBy = [ "multi-user.target" ];
    };

    "nfs-bind-mount" = {
      after = [ "zfs-import-data.service" ];
      description = "Bind NFS exports to ZFS paths";
      script = ''
        ${pkgs.util-linux}/bin/mount --onlyonce /srv/caladan/downloads || ${pkgs.coreutils}/bin/true
        ${pkgs.util-linux}/bin/mount --onlyonce /srv/caladan/www || ${pkgs.coreutils}/bin/true
        ${pkgs.util-linux}/bin/mount --onlyonce /srv/nfs/keepers || ${pkgs.coreutils}/bin/true
        ${pkgs.util-linux}/bin/mount --onlyonce /srv/nfs/movies || ${pkgs.coreutils}/bin/true
        ${pkgs.util-linux}/bin/mount --onlyonce /srv/nfs/tv || ${pkgs.coreutils}/bin/true
      '';
      wantedBy = [ "local-fs.target" ];
    };

    "nftables-extra" = let rules_script = ''
        ${pkgs.nftables}/bin/nft insert rule inet nixos-fw input 'iifname "enp6s0" tcp dport { http, https } counter accept # 80, 443'
        ${pkgs.nftables}/bin/nft insert rule inet nixos-fw input 'iifname "enp6s0" udp dport { netbios-ns, netbios-dgm } counter accept # 137, 138'
        ${pkgs.nftables}/bin/nft insert rule inet nixos-fw input 'iifname "enp6s0" tcp dport { netbios-ssn, microsoft-ds } counter accept # 139, 445'
        ${pkgs.nftables}/bin/nft insert rule inet nixos-fw input 'iifname "enp6s0" tcp dport 2049 counter accept'
        ${pkgs.nftables}/bin/nft insert rule inet nixos-fw input 'iifname "enp6s0" udp dport { 2456, 2457 } counter accept # Valheim dedicated server'
        ${pkgs.nftables}/bin/nft insert rule inet nixos-fw input 'iifname "enp6s0" udp dport 5121 counter accept # Neverwinter Nights Server'
        ${pkgs.nftables}/bin/nft insert rule inet nixos-fw input 'iifname "veth.host" tcp dport { 7878, 8080, 8686, 8787, 8989 } counter accept # Radarr, Sabnzb, Lidarr, Sonarr, Readarr'
        ${pkgs.nftables}/bin/nft insert rule inet nixos-fw input 'iifname "enp6s0" tcp dport { 7878, 8080, 8686, 8787, 8989 } counter accept # Radarr, Sabnzb, Lidarr, Sonarr, Readarr'
        ${pkgs.nftables}/bin/nft insert rule inet nixos-fw input 'iifname "enp6s0" udp dport 15637 counter accept # Enshrouded'
        ${pkgs.nftables}/bin/nft insert rule inet nixos-fw input 'iifname "enp6s0" ip saddr 192.168.1.0/24 udp dport { 27031, 27036 } counter accept # Steam Remote Play'
        ${pkgs.nftables}/bin/nft insert rule inet nixos-fw input 'iifname "enp6s0" ip saddr 192.168.1.0/24 tcp dport { 27036, 27037 } counter accept # Steam Remote Play'
        ${pkgs.nftables}/bin/nft insert rule inet nixos-fw input 'iifname "enp6s0" udp dport 51820 counter accept # WireGuard'
        ${pkgs.nftables}/bin/nft -f ${config.sops.secrets."nftables/ssh".path}
      ''; in {
        description = "nftables extra firewall rules";
        reload = rules_script;
        script = rules_script;
        serviceConfig = {
          RemainAfterExit = true;
          Type = "oneshot";
        };
        unitConfig = {
          ConditionPathExists = [
            config.sops.secrets."nftables/ssh".path
          ];
          ReloadPropagatedFrom = "nftables.service";
        };
        wantedBy = [ "multi-user.target" ];
        after = [ "nftables.service" ];
        partOf = [ "nftables.service" ];
    };

    "prowlarr" = {
      after = [ "vpn.service" ];
      description = "Prowlarr Daemon";
      serviceConfig = {
        User = "nipsy";
        Group = "nipsy";
        Restart = "always";
        RestartSec= 5;
        Type = "simple";
        ExecStart = "${pkgs.prowlarr}/bin/Prowlarr -nobrowser";
        TimeoutStopSec = 20;
        PIDFile = "/home/nipsy/.config/Prowlarr/prowlarr.pid";
        NetworkNamespacePath = "/run/netns/vpn";
      };
      wantedBy = [ "multi-user.target" ];
    };

    "qbittorrent" = {
      after = [ "vpn.service" ];
      description = "qBittorrent-nox service";
      serviceConfig = {
        User = "nipsy";
        Group = "nipsy";
        Type = "simple";
        ExecStart = "${pkgs.qbittorrent-nox}/bin/qbittorrent-nox";
        TimeoutStopSec = 1800;
        NetworkNamespacePath = "/run/netns/vpn";
      };
      wantedBy = [ "multi-user.target" ];
    };

    "radarr" = {
      after = [ "network.target" ];
      description = "Radarr Daemon";
      serviceConfig = {
        User = "nipsy";
        Group = "nipsy";
        Restart = "always";
        RestartSec= 5;
        Type = "simple";
        ExecStart = "${pkgs.radarr}/bin/Radarr -nobrowser";
        TimeoutStopSec = 20;
        PIDFile = "/home/nipsy/.config/Radarr/radarr.pid";
      };
      wantedBy = [ "multi-user.target" ];
    };

    "readarr" = {
      after = [ "network.target" ];
      description = "Readarr Daemon";
      serviceConfig = {
        User = "nipsy";
        Group = "nipsy";
        Restart = "always";
        RestartSec= 5;
        Type = "simple";
        ExecStart = "${pkgs.readarr}/bin/Readarr -nobrowser";
        TimeoutStopSec = 20;
        PIDFile = "/home/nipsy/.config/Readarr/readarr.pid";
      };
      wantedBy = [ "multi-user.target" ];
    };

    "sonarr" = {
      after = [ "network.target" ];
      description = "Sonarr Daemon";
      serviceConfig = {
        User = "nipsy";
        Group = "nipsy";
        Restart = "always";
        RestartSec= 5;
        Type = "simple";
        ExecStart = "${pkgs.sonarr}/bin/Sonarr -nobrowser";
        TimeoutStopSec = 20;
        PIDFile = "/home/nipsy/.config/Sonarr/sonarr.pid";
      };
      wantedBy = [ "multi-user.target" ];
    };

    "vpn" = {
      after = [ "network.target" ];
      description = "VPN service";
      serviceConfig = {
        RemainAfterExit = true;
        Type = "oneshot";
        ExecStart = "/root/bin/vpnctl start";
      };
      unitConfig = {
        ConditionPathExists = [
          "/root/bin/vpnctl"
        ];
      };
      wantedBy = [ "multi-user.target" ];
    };

  };

  systemd.paths."nftables-extra" = {
    pathConfig = {
      PathExists = [
        config.sops.secrets."nftables/ssh".path
      ];
    };
    wantedBy = [ "multi-user.target" ];
  };
}
