# Edit this configuration file to define what should be installed on
# your system.  Help is available in the configuration.nix(5) man page
# and in the NixOS manual (accessible by running `nixos-help`).

{ config, lib, pkgs, ... }:

with lib;
let
  netif = "enp1s0";
  hostname = "linde";
  net-ip4 = "116.203.248.56";
  net-mask4 = "32";
  net-gw = "172.31.1.1";
  net-ip6 = "2a01:4f8:c012:23a4::1";
  net-rdnsip = "2a01:4f8:c012:23a4::53";
  net-mask6 = "64";
  net-gw6 = "fe80::1";
in
{
  imports =
    [
      # Include the results of the hardware scan.
      ./linde-hardware.nix
    ];
  age.secrets = {
    paperless =
      let
        cfg = config.services.paperless;
      in
      {
        file = ../secrets/paperless.age;
        path = "${cfg.dataDir}/nixos-paperless-secret-key";
        owner = cfg.user;
        mode = "400";
        symlink = false;
      };
    acme.file = ../secrets/acme.age;
    binarycache.file = ../secrets/binarycache.age;
    powerdns.file = ../secrets/powerdns.age;
  };

  # Use the systemd-boot EFI boot loader.
  boot.loader.systemd-boot.enable = true;
  boot.loader.efi.canTouchEfiVariables = true;
  boot.loader.efi.efiSysMountPoint = "/boot/efi";

  time.timeZone = "Europe/Berlin";

  i18n.defaultLocale = "en_GB.UTF-8";

  environment.homeBinInPath = true;
  environment.localBinInPath = true;
  environment.systemPackages = with pkgs; [
    kitty.terminfo
    htop
    lsof
    gitMinimal
    powerdns
    sqlite-interactive
    knot-dns

    nixpkgs-review
    nix-output-monitor
  ];

  programs.ssh = with pkgs; {
    knownHostsFiles = [
      (writeText "github.keys" ''
        # github.com:22 SSH-2.0-babeld-05989c77
        # github.com:22 SSH-2.0-babeld-05989c77
        # github.com:22 SSH-2.0-babeld-05989c77
        # github.com:22 SSH-2.0-babeld-05989c77
        # github.com:22 SSH-2.0-babeld-05989c77
        github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk=
        github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=
        github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl
      '')
      (writeText "gitlab.keys" ''
        # gitlab.com:22 SSH-2.0-GitLab-SSHD
        # gitlab.com:22 SSH-2.0-GitLab-SSHD
        # gitlab.com:22 SSH-2.0-GitLab-SSHD
        # gitlab.com:22 SSH-2.0-GitLab-SSHD
        # gitlab.com:22 SSH-2.0-GitLab-SSHD
        gitlab.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsj2bNKTBSpIYDEGk9KxsGh3mySTRgMtXL583qmBpzeQ+jqCMRgBqB98u3z++J1sKlXHWfM9dyhSevkMwSbhoR8XIq/U0tCNyokEi/ueaBMCvbcTHhO7FcwzY92WK4Yt0aGROY5qX2UKSeOvuP4D6TPqKF1onrSzH9bx9XUf2lEdWT/ia1NEKjunUqu1xOB/StKDHMoX4/OKyIzuS0q/T1zOATthvasJFoPrAjkohTyaDUz2LN5JoH839hViyEG82yB+MjcFV5MU3N1l1QL3cVUCh93xSaua1N85qivl+siMkPGbO5xR/En4iEY6K2XPASUEMaieWVNTRCtJ4S8H+9
        gitlab.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFSMqzJeV9rUzU4kWitGjeR4PWSa29SPqJ1fVkhtj3Hw9xjLVXVYrU9QlYWrOLXBpQ6KWjbjTDTdDkoohFzgbEY=
        gitlab.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf
      '')
      (writeText "codeberg.keys" ''
        # codeberg.org:22 SSH-2.0-OpenSSH_9.2p1 Debian-2+deb12u2
        # codeberg.org:22 SSH-2.0-OpenSSH_9.2p1 Debian-2+deb12u2
        # codeberg.org:22 SSH-2.0-OpenSSH_9.2p1 Debian-2+deb12u2
        # codeberg.org:22 SSH-2.0-OpenSSH_9.2p1 Debian-2+deb12u2
        # codeberg.org:22 SSH-2.0-OpenSSH_9.2p1 Debian-2+deb12u2
        codeberg.org ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC8hZi7K1/2E2uBX8gwPRJAHvRAob+3Sn+y2hxiEhN0buv1igjYFTgFO2qQD8vLfU/HT/P/rqvEeTvaDfY1y/vcvQ8+YuUYyTwE2UaVU5aJv89y6PEZBYycaJCPdGIfZlLMmjilh/Sk8IWSEK6dQr+g686lu5cSWrFW60ixWpHpEVB26eRWin3lKYWSQGMwwKv4LwmW3ouqqs4Z4vsqRFqXJ/eCi3yhpT+nOjljXvZKiYTpYajqUC48IHAxTWugrKe1vXWOPxVXXMQEPsaIRc2hpK+v1LmfB7GnEGvF1UAKnEZbUuiD9PBEeD5a1MZQIzcoPWCrTxipEpuXQ5Tni4mN
        codeberg.org ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBL2pDxWr18SoiDJCGZ5LmxPygTlPu+cCKSkpqkvCyQzl5xmIMeKNdfdBpfbCGDPoZQghePzFZkKJNR/v9Win3Sc=
        codeberg.org ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIVIC02vnjFyL+I4RHfvIGNtOgJMe769VTF1VR4EB3ZB
      '')
      (writeText "sr.ht.keys" ''
        # git.sr.ht:22 SSH-2.0-OpenSSH_9.6
        # git.sr.ht:22 SSH-2.0-OpenSSH_9.6
        # git.sr.ht:22 SSH-2.0-OpenSSH_9.6
        # git.sr.ht:22 SSH-2.0-OpenSSH_9.6
        # git.sr.ht:22 SSH-2.0-OpenSSH_9.6
        git.sr.ht ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDZ+l/lvYmaeOAPeijHL8d4794Am0MOvmXPyvHTtrqvgmvCJB8pen/qkQX2S1fgl9VkMGSNxbp7NF7HmKgs5ajTGV9mB5A5zq+161lcp5+f1qmn3Dp1MWKp/AzejWXKW+dwPBd3kkudDBA1fa3uK6g1gK5nLw3qcuv/V4emX9zv3P2ZNlq9XRvBxGY2KzaCyCXVkL48RVTTJJnYbVdRuq8/jQkDRA8lHvGvKI+jqnljmZi2aIrK9OGT2gkCtfyTw2GvNDV6aZ0bEza7nDLU/I+xmByAOO79R1Uk4EYCvSc1WXDZqhiuO2sZRmVxa0pQSBDn1DB3rpvqPYW+UvKB3SOz
        git.sr.ht ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCj6y+cJlqK3BHZRLZuM+KP2zGPrh4H66DacfliU1E2DHAd1GGwF4g1jwu3L8gOZUTIvUptqWTkmglpYhFp4Iy4=
        git.sr.ht ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMZvRd4EtM7R+IHVMWmDkVU3VLQTSwQDSAvW0t2Tkj60
      '')
    ];
  };

  # Initial empty root password for easy login:
  users.users.root.initialHashedPassword = "";
  services.openssh = {
    enable = true;
    settings = {
      PermitRootLogin = "no";
      PasswordAuthentication = false;
      KbdInteractiveAuthentication = false;
    };
  };
  services.sshguard = {
    enable = true;
    services = [ "sshd" ];
  };
  programs.mosh.enable = true;

  system.autoUpgrade = {
    enable = true;
    dates = "05:10";
    allowReboot = true;
    flake = "git+file://${config.services.gitolite.dataDir}/repositories/nixfiles.git";
    flags = [
      "--no-write-lock-file"
      "--update-input"
      "nixpkgs"
    ];
  };

  nix = {
    daemonCPUSchedPolicy = "batch";
    daemonIOSchedPriority = 6;
    settings = {
      max-jobs = 2;
      auto-optimise-store = true;
      trusted-users = [ "root" "nixremote" ];
      experimental-features = [ "nix-command" "flakes" ];
    };
    gc = {
      automatic = true;
      dates = "08:15";
      options = "--delete-older-than 14d";
    };
    optimise = {
      automatic = true;
      dates = [ "02:30" ];
    };
  };

  services.nix-serve = {
    enable = true;
    secretKeyFile = config.age.secrets.binarycache.path;
  };

  programs.neovim = {
    enable = true;
    defaultEditor = true;
    viAlias = true;
    vimAlias = true;
  };

  networking = {
    hostName = hostname;
    useDHCP = false;
    dhcpcd.enable = false;
    nameservers = [
      "2606:4700:4700::1111"
      "2606:4700:4700::1001"
      "1.1.1.1"
      "1.0.0.1"
    ];
    hosts = lib.mkForce {
      ${net-ip4} = [ "${hostname}.alanpearce.eu" hostname ];
      ${net-ip6} = [ "${hostname}.alanpearce.eu" hostname ];
      ${net-rdnsip} = [ "dns" ];
    };
    firewall = {
      enable = true;
      allowPing = true;
      pingLimit = "--limit 60/minute --limit-burst 30";
      logRefusedConnections = false;
      allowedTCPPorts = [
        22
        80
        443
        53
        853
        9418
        6922
      ];
      allowedUDPPorts = [
        53
        3478
        6885 # DHT
        6922
      ];
    };
    resolvconf = {
      enable = true;
      useLocalResolver = false;
    };
  };
  services.resolved.enable = false;
  systemd.network = {
    enable = true;
    networks.${netif} =
      {
        name = netif;
        gateway = [ net-gw ];
        routes = [{
          routeConfig = {
            Gateway = net-gw6;
            PreferredSource = net-ip6;
          };
        }];
        address = [
          "${net-ip6}/${net-mask6}"
          "${net-rdnsip}/${net-mask6}"
        ];
        addresses = [{
          addressConfig = {
            Address = "${net-ip4}/${net-mask4}";
            Peer = "${net-gw}/32";
          };
        }];
      };
  };

  services.journald.extraConfig = ''
    MaxRetentionSec=1 month
  '';

  boot.kernel.sysctl = {
    "net.ipv4.tcp_allowed_congestion_control" = "bbr illinois reno";
    "net.ipv4.tcp_congestion_control" = "bbr";
    "net.core.default_qdisc" = "fq";
  };

  security.sudo.execWheelOnly = true;
  security.sudo.extraConfig = ''
    Defaults:root,%wheel env_keep+=EDITOR
  '';

  nixpkgs = {
    config.allowUnfree = true;
  };

  programs.fish = {
    enable = true;
    interactiveShellInit = ''
      set --universal fish_greeting ""
    '';
  };
  programs.zsh.enable = true;
  users.users.root.shell = "${pkgs.fish}/bin/fish";
  users.users.alan = {
    shell = "${pkgs.fish}/bin/fish";
    extraGroups = [ "wheel" "caddy" "docker" ];
    isNormalUser = true;
    home = "/home/alan";
    createHome = true;

    openssh.authorizedKeys.keys = [
      "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII8VIII+598QOBxi/52O1Kb19RdUdX0aZmS1/dNoyqc5 alan@hetzner.strongbox"
    ];
  };

  users.users.nixremote = {
    shell = "/bin/sh";
    isNormalUser = true;
    home = "/var/lib/nixremote/";
    createHome = true;
    openssh.authorizedKeys.keys = [
      "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBxa7lxDu0M4chats/VvpFzjT3ruexKa3J9UC6ASo3bN root@NanoPi.lan"
      "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE9of82WBHK8nr8L9RGeieLMfcAWaFCeCkmvYHM9LCuT nanopi"
      "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIy9jFioBvV0JA0lc+De2N+vDOABGHgCECW6vkD33CE4 sourcehut"
    ];
  };

  # This value determines the NixOS release from which the default
  # settings for stateful data, like file locations and database versions
  # on your system were taken. It's perfectly fine and recommended to leave
  # this value at the release version of the first install of this system.
  # Before changing this value read the documentation for this option
  # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
  system.stateVersion = "23.05"; # Did you read the comment?

  services.powerdns = {
    enable = true;
    secretFile = config.age.secrets.powerdns.path;
    extraConfig = ''
      launch=gsqlite3
      dnsupdate=yes
      allow-dnsupdate-from=0.0.0.0/0,::/0
      only-notify=
      also-notify=216.218.130.2
      allow-axfr-ips=216.218.133.2,2001:470:600::2
      outgoing-axfr-expand-alias=yes
      expand-alias=yes
      resolver=1.1.1.1
      local-address=${net-ip4} ${net-ip6}
      reuseport=yes
      log-dns-details=no
      log-dns-queries=no
      loglevel=5
      primary=yes
      secondary=yes
      send-signed-notify=no
      prevent-self-notification=no

      default-soa-edit=inception-increment

      api=yes
      # replaced by secretFile/envsubst
      api-key=$API_KEY

      gsqlite3-database=/var/db/pdns/zones.db
      gsqlite3-pragma-foreign-keys=yes
      gsqlite3-dnssec=yes
    '';
  };

  systemd.services.hagezi-blocklist-update = {
    enable = true;
    startAt = "daily";
    serviceConfig = {
      CacheDirectory = "blocklist";
      UMask = "0077";
      DynamicUser = "yes";
      ProtectSystem = "strict";
      ProtectHome = true;
      PrivateTmp = true;
      PrivateDevices = true;
      PrivateUsers = true;
      ProtectClock = true;
      ProtectKernelTunables = true;
      ProtectKernelModules = true;
      ProtectKernelLogs = true;
      ProtectControlGroups = true;
      ProtectProc = "invisible";
      RestrictAddressFamilies = "AF_INET AF_INET6";
      RestrictNamespaces = true;
      RestrictRealtime = true;
      LockPersonality = true;
      MemoryDenyWriteExecute = "true";
      SystemCallFilter = [
        "~@clock"
        "~@cpu-emulation"
        "~@debug"
        "~@module"
        "~@mount"
        "~@obsolete"
        "~@privileged"
        "~@raw-io"
        "~@reboot"
        "~@resources"
        "~@swap"
      ];
      SystemCallArchitectures = "native";
      CapabilityBoundingSet = "";
      DevicePolicy = "closed";
      ProcSubset = "pid";
      NoNewPrivileges = true;
      ExecStart = "${pkgs.curl}/bin/curl --no-progress-meter --output %C/blocklist/hagezi.rpz https://raw.githubusercontent.com/hagezi/dns-blocklists/main/rpz/pro.plus.txt";
      #  https://raw.githubusercontent.com/hagezi/dns-blocklists/main/rpz/pro.plus.txt"
      ExecStartPost = [
        "+/bin/sh -c 'exec install --compare --mode=644 %C/blocklist/hagezi.rpz /etc/knot-resolver/blocklist.rpz'"
        "-/bin/sh -c 'exec rm -f %C/blocklist/hagezi.rpz'"
      ];
      Environment = [
        "HOME=%C/blocklist"
      ];
    };
  };

  services.kresd = {
    enable = true;
    # package = pkgs.knot-resolver.override { extraFeatures = true; };
    listenPlain = [
      "[${net-rdnsip}]:53"
    ];
    listenTLS = [
      "127.0.0.1:853"
      "[::1]:853"
      "${net-ip4}:853"
      "[${net-ip6}]:853"
    ];
    listenDoH = [
      "[::1]:443"
      "127.0.0.1:443"
    ];
    instances = 2;
    extraConfig = ''
      modules = {
        'rebinding < iterate',
        'hints > iterate',
        'serve_stale < cache',
        'stats',
        predict = {
          window = 30,
          period = 24 * (60/30),
        },
        'nsid',
      }

      local systemd_instance = os.getenv("SYSTEMD_INSTANCE")
      nsid.name(systemd_instance)

      log_groups({ 'policy' })

      cache.size = 500 * MB

      net.tls(
        '/var/lib/acme/dns.alanpearce.eu/cert.pem',
        '/var/lib/acme/dns.alanpearce.eu/key.pem'
      )

      -- override blocklist
      policy.add(policy.suffix(policy.PASS, policy.todnames({
      })))

      policy.add(policy.rpz(
        policy.DENY_MSG('domain blocked by hagezi'),
        '/etc/knot-resolver/blocklist.rpz',
        false -- needs wrapped kresd
        -- true -- will watch the file for updates
      ))

      policy.add(policy.domains(policy.REFUSE, policy.todnames({
        'use-application-dns.net',
        'telemetry.astro.build',
      })))

      -- disable DNSSEC when using Quad9 since they do it
      -- trust_anchors.remove('.')
      -- policy.add(policy.all(policy.TLS_FORWARD({
      --   {'2620:fe::fe', hostname='dns.quad9.net'},
      --   {'2620:fe::9', hostname='dns.quad9.net'},
      --   {'9.9.9.9', hostname='dns.quad9.net'},
      --   {'149.112.122.122', hostname='dns.quad9.net'},
      -- })))
    '';
  };

  users.groups.ntfy = { };
  users.users.ntfy = {
    isSystemUser = true;
    group = "ntfy";
  };
  services.ntfy-sh = {
    enable = true;
    user = "ntfy";
    group = "ntfy";
    settings = {
      base-url = "https://ntfy.alanpearce.eu";
      listen-http = ":2586";
      behind-proxy = true;
      manager-interval = "1h";
      cache-startup-queries = ''
        PRAGMA journal_mode = WAL;
        PRAGMA busy_timeout = 5000;
        PRAGMA synchronous = NORMAL;
      '';
      cache-file = "/var/cache/ntfy/cache.db";
      attachment-cache-dir = "/var/cache/ntfy/attachments";
      auth-default-access = "deny-all";
      auth-file = "/var/lib/ntfy/user.db";
      upstream-base-url = "https://ntfy.sh";
    };
  };
  systemd.services.ntfy-sh = {
    serviceConfig = {
      DynamicUser = lib.mkForce false;
      StateDirectory = lib.mkForce "ntfy";
      RuntimeDirectory = "ntfy";
      CacheDirectory = "ntfy";
    };
  };

  systemd.services.backup-etc-nixos = {
    startAt = "04:30";
    path = with pkgs; [
      rdiff-backup
      openssh
    ];
    script = ''
      rdiff-backup --api-version 201 backup /etc/nixos ${hostname}@home.alanpearce.eu::nixos
      rdiff-backup --api-version 201 remove increments --older-than 3M ${hostname}@home.alanpearce.eu::nixos
    '';
    serviceConfig = {
      Type = "oneshot";
    };
  };

  systemd.services.backup-gitolite = {
    startAt = "daily";
    path = with pkgs; [
      rdiff-backup
      openssh
    ];
    script = ''
      rdiff-backup --api-version 201 backup ${config.services.gitolite.dataDir} ${hostname}@home.alanpearce.eu::gitolite
      rdiff-backup --api-version 201 remove increments --older-than 3M ${hostname}@home.alanpearce.eu::gitolite
    '';
    serviceConfig.Type = "oneshot";
  };

  systemd.services.backup-paperless = {
    startAt = "daily";
    path = with pkgs; [
      sudo
      rdiff-backup
      openssh
    ];
    script = ''
      sudo -u paperless ./paperless-manage document_exporter --delete --use-filename-format --no-archive --no-thumbnail --no-progress-bar ./export
      rdiff-backup --api-version 201 backup ./export ${hostname}@home.alanpearce.eu::paperless
      rdiff-backup --api-version 201 remove increments --older-than 3M ${hostname}@home.alanpearce.eu::paperless
    '';
    serviceConfig = {
      Type = "oneshot";
      WorkingDirectory = config.services.paperless.dataDir;
    };
  };

  security.acme = {
    defaults = {
      email = "alan@alanpearce.eu";
      dnsProvider = "pdns";
      dnsResolver = "1.1.1.1:53";
      credentialsFile = config.age.secrets.acme.path;
      reloadServices = [ "caddy" ];
      validMinDays = 32;
    };
    acceptTerms = true;
    certs."alanpearce.eu" = {
      extraDomainNames = [ "*.alanpearce.eu" ];
    };
    certs."dns.alanpearce.eu" = {
      reloadServices = map (x: "kresd@${toString x}") (range 1 config.services.kresd.instances);
      group = "knot-resolver";
    };
  };
  users.groups.acme.members = [
    "caddy"
  ];

  services.caddy = {
    enable = true;
    group = "caddy";
    globalConfig = ''
      auto_https disable_certs
      default_bind ${net-ip6} ${net-ip4}
    '';
    virtualHosts = {
      "http://" = {
        # Needed for HTTP->HTTPS servers
      };
      "${hostname}.alanpearce.eu" = {
        serverAliases = [ "https://" ];
        useACMEHost = "alanpearce.eu";
        extraConfig = ''
          respond * 204
        '';
      };
      "pdns.alanpearce.eu" = {
        useACMEHost = "alanpearce.eu";
        extraConfig = ''
          log {
            output discard
          }
          reverse_proxy 127.0.0.1:8081
        '';
      };
      "dns.alanpearce.eu" = {
        useACMEHost = "alanpearce.eu";
        extraConfig = ''
          log {
            output discard
          }
          reverse_proxy localhost:443 {
            transport http {
              tls_server_name dns.alanpearce.eu
            }
          }
        '';
      };
      "files.alanpearce.eu" = {
        useACMEHost = "alanpearce.eu";
        extraConfig = ''
          root * /srv/http/files
          encode gzip zstd
          file_server browse
        '';
      };
      "git.alanpearce.eu" =
        let
          fcgi = config.services.fcgiwrap;
          fcgisocket = "${fcgi.socketType}/${fcgi.socketAddress}";
        in
        {
          useACMEHost = "alanpearce.eu";
          extraConfig = ''
            root * ${pkgs.cgit-pink}/cgit/
            encode gzip zstd
            handle_path /custom/* {
              file_server {
                root /srv/http/cgit/
              }
            }
            rewrite /robots.txt /assets/robots.txt
            handle_path /assets/* {
              file_server  {
                hide cgit.cgi
              }
            }
            @git_http_backend path_regexp "^/.+/(info/refs|git-upload-pack)$"
            handle @git_http_backend {
              reverse_proxy ${fcgisocket} {
                transport fastcgi {
                  env SCRIPT_FILENAME ${pkgs.git}/libexec/git-core/git-http-backend
                  env GIT_PROJECT_ROOT ${config.services.gitolite.dataDir}/repositories
                }
              }
            }
            handle {
              reverse_proxy ${fcgisocket} {
                transport fastcgi {
                  env       SCRIPT_FILENAME  {http.vars.root}/cgit.cgi
                  env       CGIT_CONFIG      ${pkgs.writeText "cgitrc" ''
                    head-include=/srv/http/cgit/responsive-cgit-css-master/head.html
                    css=/custom/responsive-cgit-css-master/cgit.css
                    virtual-root=/
                    logo=
                    readme=:README.md
                    source-filter=${pkgs.cgit-pink}/lib/cgit/filters/syntax-highlighting.py
                    about-filter=${pkgs.cgit-pink}/lib/cgit/filters/about-formatting.sh
                    enable-git-config=1
                    enable-index-owner=0
                    enable-index-links=1
                    enable-follow-links=0
                    enable-log-linecount=1
                    max-stats=year
                    snapshots=tar.lz tar.zst zip
                    cache-size=10240
                    enable-http-clone=1
                    enable-commit-graph=1
                    mimetype-file=${pkgs.nginx}/conf/mime.types
                    section-from-path=1
                    noplainemail=1
                    repository-sort=age
                    root-title=my personal projects
                    clone-url=git://git.alanpearce.eu/$CGIT_REPO_URL https://git.alanpearce.eu/$CGIT_REPO_URL
                    remove-suffix=1
                    strict-export=git-daemon-export-ok
                    scan-path=${config.services.gitolite.dataDir}/repositories/
                  ''}
                  }
                }
            }
          '';
        };
      "ntfy.alanpearce.eu" = {
        useACMEHost = "alanpearce.eu";
        extraConfig = ''
          reverse_proxy localhost${config.services.ntfy-sh.settings.listen-http}
        '';
      };
      "legit.alanpearce.eu" =
        let
          server = config.services.legit.settings.server;
        in
        {
          useACMEHost = "alanpearce.eu";
          extraConfig = ''
            handle_path /static/* {
              root * /srv/http/legit/src/static
              file_server
            }
            reverse_proxy ${server.host}:${toString server.port}
          '';
        };
      "papers.alanpearce.eu" = {
        extraConfig = ''
          handle_path /static/* {
            root * ${config.services.paperless.package}/lib/paperless-ngx/static
            file_server
          }
          reverse_proxy localhost:${toString config.services.paperless.port}

        '';
      };
      "binarycache.alanpearce.eu" =
        let
          ns = config.services.nix-serve;
        in
        {
          extraConfig = ''
            reverse_proxy ${ns.bindAddress}:${toString ns.port}
          '';
        };
    };
  };
  systemd.services.caddy.serviceConfig = {
    UMask = "007";
  };

  services.fcgiwrap = {
    enable = true;
    group = "gitolite";
    preforkProcesses = 2;
    socketType = "tcp6";
    socketAddress = "[::1]:9000";
  };
  services.gitolite = {
    enable = true;
    adminPubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII8VIII+598QOBxi/52O1Kb19RdUdX0aZmS1/dNoyqc5 alan@hetzner.strongbox";
    extraGitoliteRc = ''
      $RC{UMASK} = 0027;
      $RC{LOG_EXTRA} = 0;
      $RC{HOSTNAME} = "${config.networking.hostName}";
      $RC{LOCAL_CODE} = "$rc{GL_ADMIN_BASE}/local";
      push( @{$RC{ENABLE}}, 'D' );
      push( @{$RC{ENABLE}}, 'Shell alan' );
      push( @{$RC{ENABLE}}, 'cgit' );
      push( @{$RC{ENABLE}}, 'repo-specific-hooks' );
    '';
  };
  services.legit = {
    enable = true;
    group = "gitolite";
    settings = {
      server.name = "legit.alanpearce.eu";
      dirs = {
        templates = "/srv/http/legit/src/templates";
      };
      repo = {
        scanPath = "/srv/http/legit/repos";
        readme = [
          "readme"
          "readme.md"
          "README.md"
        ];
      };
    };
  };
  users.groups.git.gid = config.ids.gids.git;
  services.gitDaemon = {
    enable = true;
    user = "git";
    group = "gitolite";
    basePath = "${config.services.gitolite.dataDir}/repositories/";
  };

  users.groups.paperless.members = [ "alan" "syncthing" ];
  services.paperless = {
    enable = true;
    package = pkgs.paperless-ngx;
    dataDir = "/srv/paperless";
    settings = {
      PAPERLESS_DBENGINE = "sqlite";
      PAPERLESS_TIME_ZONE = "Europe/Berlin";

      PAPERLESS_URL = "https://papers.alanpearce.eu";
      PAPERLESS_TRUSTED_PROXIES = "127.0.0.1";
      PAPERLESS_USE_X_FORWARD_HOST = true;
      PAPERLESS_USE_X_FORWARD_PORT = true;
      PAPERLESS_PROXY_SSL_HEADER = [ "HTTP_X_FORWARDED_PROTO" "https" ];
      PAPERLESS_ENABLE_COMPRESSION = false; # let caddy do it

      PAPERLESS_OCR_SKIP_ARCHIVE_FILE = "with_text";
      PAPERLESS_OCR_LANGUAGE = "deu+eng";
      PAPERLESS_IGNORE_DATES = "09.08.90";

      PAPERLESS_TASK_WORKERS = 2;
      PAPERLESS_THREADS_PER_WORKER = 1;
      PAPERLESS_NUMBER_OF_SUGGESTED_DATES = 4;

      PAPERLESS_CONSUMER_IGNORE_PATTERN = [ ".DS_STORE/*" "desktop.ini" ".stfolder/*" ".stversions/*" ];

      PAPERLESS_FILENAME_FORMAT = "{correspondent}/{created} {title} {asn}";
      PAPERLESS_FILENAME_FORMAT_REMOVE_NONE = true;
    };
  };

  services.syncthing = {
    enable = true;
    dataDir = "/srv/syncthing";
    configDir = "/var/lib/syncthing";
    overrideDevices = false;
    overrideFolders = false;
  };
}