summary refs log tree commit diff stats
path: root/system
diff options
context:
space:
mode:
Diffstat (limited to 'system')
-rw-r--r--system/.gitignore6
-rwxr-xr-xsystem/autorandr/docked-close/block3
-rw-r--r--system/autorandr/docked-close/config23
-rw-r--r--system/autorandr/docked-close/setup3
-rwxr-xr-xsystem/autorandr/docked-open/block3
-rw-r--r--system/autorandr/docked-open/config25
-rw-r--r--system/autorandr/docked-open/setup3
-rw-r--r--system/autorandr/laptop/config19
-rw-r--r--system/autorandr/laptop/setup1
-rwxr-xr-xsystem/autorandr/postswitch2
-rw-r--r--system/autorandr/work-1/config21
-rw-r--r--system/autorandr/work-1/setup2
-rw-r--r--system/linde.nix806
-rw-r--r--system/mba.nix1
-rwxr-xr-xsystem/nanopi.nix319
-rw-r--r--system/prefect.nix58
-rw-r--r--system/settings/base.nix5
-rw-r--r--system/settings/configuration/networking.nix11
-rw-r--r--system/settings/configuration/nix-linux.nix1
-rw-r--r--system/settings/configuration/nix.nix13
-rw-r--r--system/settings/configuration/user.nix1
-rw-r--r--system/settings/darwin.nix10
-rw-r--r--system/settings/hardware/intel-gpu.nix18
-rw-r--r--system/settings/hardware/laptop.nix79
-rw-r--r--system/settings/hardware/mouse.nix2
-rw-r--r--system/settings/hardware/network-manager.nix16
-rw-r--r--system/settings/hardware/personal-computer.nix8
-rw-r--r--system/settings/hardware/thinkpad.nix23
-rw-r--r--system/settings/hardware/trackball.nix15
-rw-r--r--system/settings/hardware/trezor.nix6
-rw-r--r--system/settings/machines/t470s.nix57
-rw-r--r--system/settings/pin.nix12
-rw-r--r--system/settings/programs/barrier.nix10
-rw-r--r--system/settings/programs/base.nix17
-rw-r--r--system/settings/programs/gnome.nix26
-rw-r--r--system/settings/programs/gnupg.nix12
-rw-r--r--system/settings/programs/kde.nix7
-rw-r--r--system/settings/programs/shell.nix1
-rw-r--r--system/settings/programs/tor.nix27
-rw-r--r--system/settings/programs/window-manager.nix57
-rw-r--r--system/settings/programs/xfce.nix8
-rw-r--r--system/settings/services/git-server.nix278
-rw-r--r--system/settings/services/virtualisation.nix4
-rw-r--r--system/settings/services/xserver.nix15
-rw-r--r--system/settings/user-interface.nix13
45 files changed, 993 insertions, 1054 deletions
diff --git a/system/.gitignore b/system/.gitignore
deleted file mode 100644
index 9e7db1a3..00000000
--- a/system/.gitignore
+++ /dev/null
@@ -1,6 +0,0 @@
-/private/
-/hardware-configuration.nix
-/configuration.nix
-/darwin-configuration.nix
-/cachix
-/result
diff --git a/system/autorandr/docked-close/block b/system/autorandr/docked-close/block
deleted file mode 100755
index 782c3a74..00000000
--- a/system/autorandr/docked-close/block
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/sh
-
-exec grep -vq close /proc/acpi/button/lid/LID/state
\ No newline at end of file
diff --git a/system/autorandr/docked-close/config b/system/autorandr/docked-close/config
deleted file mode 100644
index d8ce5031..00000000
--- a/system/autorandr/docked-close/config
+++ /dev/null
@@ -1,23 +0,0 @@
-output DP1
-off
-output DP2
-off
-output DP2-3
-off
-output HDMI1
-off
-output HDMI2
-off
-output VIRTUAL1
-off
-output eDP1
-off
-output DP2-1
-mode 2560x1440
-pos 0x0
-primary
-rate 59.95
-output DP2-2
-mode 2560x1440
-pos 2560x0
-rate 59.95
diff --git a/system/autorandr/docked-close/setup b/system/autorandr/docked-close/setup
deleted file mode 100644
index 607eacbc..00000000
--- a/system/autorandr/docked-close/setup
+++ /dev/null
@@ -1,3 +0,0 @@
-DP2-1 00ffffffffffff0030aeaf61010101011c1c0104a53c22783e9325a9544d9e250c5054a1080081809500b300d1c0d100a9c001010101565e00a0a0a029503020350055502100001a023a801871382d40582c450055502100001e000000fd00324c1e7822000a202020202020000000fc004c454e20503237682d31300a20010d02031df14a01020304051413901f12230907078301000065030c001000011d007251d01e206e28550055502100001e8c0ad08a20e02d10103e96005550210000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bd
-DP2-2 00ffffffffffff0030aeaf61010101010a1c0104a53c22783e9325a9544d9e250c5054a1080081809500b300d1c0d100a9c001010101565e00a0a0a029503020350055502100001a023a801871382d40582c450055502100001e000000fd00324c1e7822000a202020202020000000fc004c454e20503237682d31300a20011f02031df14a01020304051413901f12230907078301000065030c001000011d007251d01e206e28550055502100001e8c0ad08a20e02d10103e96005550210000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bd
-eDP1 00ffffffffffff0006af3d2400000000001a0104951f117802a2b591575894281c505400000001010101010101010101010101010101843a8034713828403064310035ad10000018d02e8034713828403064310035ad10000018000000fe0041554f0a202020202020202020000000fe004231343048414e30322e34200a00e4
diff --git a/system/autorandr/docked-open/block b/system/autorandr/docked-open/block
deleted file mode 100755
index 4c82692a..00000000
--- a/system/autorandr/docked-open/block
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/sh
-
-exec grep -vq open /proc/acpi/button/lid/LID/state
\ No newline at end of file
diff --git a/system/autorandr/docked-open/config b/system/autorandr/docked-open/config
deleted file mode 100644
index 231a3942..00000000
--- a/system/autorandr/docked-open/config
+++ /dev/null
@@ -1,25 +0,0 @@
-output DP1
-off
-output DP2
-off
-output DP2-3
-off
-output HDMI1
-off
-output HDMI2
-off
-output VIRTUAL1
-off
-output eDP1
-mode 1920x1080
-pos 1600x1440
-rate 60.03
-output DP2-1
-mode 2560x1440
-pos 0x0
-primary
-rate 59.95
-output DP2-2
-mode 2560x1440
-pos 2560x0
-rate 59.95
diff --git a/system/autorandr/docked-open/setup b/system/autorandr/docked-open/setup
deleted file mode 100644
index 607eacbc..00000000
--- a/system/autorandr/docked-open/setup
+++ /dev/null
@@ -1,3 +0,0 @@
-DP2-1 00ffffffffffff0030aeaf61010101011c1c0104a53c22783e9325a9544d9e250c5054a1080081809500b300d1c0d100a9c001010101565e00a0a0a029503020350055502100001a023a801871382d40582c450055502100001e000000fd00324c1e7822000a202020202020000000fc004c454e20503237682d31300a20010d02031df14a01020304051413901f12230907078301000065030c001000011d007251d01e206e28550055502100001e8c0ad08a20e02d10103e96005550210000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bd
-DP2-2 00ffffffffffff0030aeaf61010101010a1c0104a53c22783e9325a9544d9e250c5054a1080081809500b300d1c0d100a9c001010101565e00a0a0a029503020350055502100001a023a801871382d40582c450055502100001e000000fd00324c1e7822000a202020202020000000fc004c454e20503237682d31300a20011f02031df14a01020304051413901f12230907078301000065030c001000011d007251d01e206e28550055502100001e8c0ad08a20e02d10103e96005550210000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bd
-eDP1 00ffffffffffff0006af3d2400000000001a0104951f117802a2b591575894281c505400000001010101010101010101010101010101843a8034713828403064310035ad10000018d02e8034713828403064310035ad10000018000000fe0041554f0a202020202020202020000000fe004231343048414e30322e34200a00e4
diff --git a/system/autorandr/laptop/config b/system/autorandr/laptop/config
deleted file mode 100644
index 94f4e43d..00000000
--- a/system/autorandr/laptop/config
+++ /dev/null
@@ -1,19 +0,0 @@
-output DP1
-off
-output HDMI1
-off
-output DP2
-off
-output HDMI2
-off
-output DP2-1
-off
-output DP2-2
-off
-output DP2-3
-off
-output eDP1
-mode 1920x1080
-pos 0x0
-primary
-rate 60.03
diff --git a/system/autorandr/laptop/setup b/system/autorandr/laptop/setup
deleted file mode 100644
index dc17ff13..00000000
--- a/system/autorandr/laptop/setup
+++ /dev/null
@@ -1 +0,0 @@
-eDP1 00ffffffffffff0006af3d2400000000001a0104951f117802a2b591575894281c505400000001010101010101010101010101010101843a8034713828403064310035ad10000018d02e8034713828403064310035ad10000018000000fe0041554f0a202020202020202020000000fe004231343048414e30322e34200a00e4
diff --git a/system/autorandr/postswitch b/system/autorandr/postswitch
deleted file mode 100755
index 25b068d2..00000000
--- a/system/autorandr/postswitch
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/sh
-notify-send --expire-time=5000 "Display profile: '$AUTORANDR_CURRENT_PROFILE'"
diff --git a/system/autorandr/work-1/config b/system/autorandr/work-1/config
deleted file mode 100644
index 34fda0db..00000000
--- a/system/autorandr/work-1/config
+++ /dev/null
@@ -1,21 +0,0 @@
-output DP1
-off
-output HDMI1
-off
-output DP2
-off
-output HDMI2
-mode 1920x1200
-pos 0x0
-primary
-rate 59.95
-output DP2-1
-off
-output DP2-2
-off
-output DP2-3
-off
-output eDP1
-mode 1920x1080
-pos 1920x0
-rate 60.03
diff --git a/system/autorandr/work-1/setup b/system/autorandr/work-1/setup
deleted file mode 100644
index 12a4c698..00000000
--- a/system/autorandr/work-1/setup
+++ /dev/null
@@ -1,2 +0,0 @@
-HDMI2 00ffffffffffff0015c33425b1a21403041b010380342178ea0495a9554d9d26105054a10800a9408180b300a9c081c0810001010101283c80a070b023403020360007442100001a023a801871382d40582c450007442100001e000000fd00313d0f4c11000a202020202020000000fc004556323435350a202020202020011e020325f14e901f051404130312021107160615230907078301000066030c00100080e2007b011d8018711c1620582c250007442100009e011d80d0721c1620102c258007442100009e8c0ad08a20e02d10103e96000744210000188c0ad090204031200c4055000744210000180000000000000000000000000000000000008e
-eDP1 00ffffffffffff0006af3d2400000000001a0104951f117802a2b591575894281c505400000001010101010101010101010101010101843a8034713828403064310035ad10000018d02e8034713828403064310035ad10000018000000fe0041554f0a202020202020202020000000fe004231343048414e30322e34200a00e4
diff --git a/system/linde.nix b/system/linde.nix
index 765307cc..f255bc30 100644
--- a/system/linde.nix
+++ b/system/linde.nix
@@ -15,12 +15,16 @@ let
   net-rdnsip = "2a01:4f8:c012:23a4::53";
   net-mask6 = "64";
   net-gw6 = "fe80::1";
+  ts-domain = "hydra-pinecone.ts.net";
 in
 {
   imports =
     [
       # Include the results of the hardware scan.
       ./linde-hardware.nix
+
+      ./settings/pin.nix
+      ./settings/services/git-server.nix
     ];
   age.secrets = {
     paperless =
@@ -36,7 +40,16 @@ in
       };
     acme.file = ../secrets/acme.age;
     binarycache.file = ../secrets/binarycache.age;
+    dex.file = ../secrets/dex.age;
     powerdns.file = ../secrets/powerdns.age;
+    golink = let golink = config.services.golink; in {
+      # hope this doesn't collide...
+      path = "${golink.dataDir}/.config/tsnet-golink/auth.key";
+      owner = golink.user;
+      mode = "400";
+      symlink = false;
+      file = ../secrets/golink.age;
+    };
   };
 
   # Use the systemd-boot EFI boot loader.
@@ -63,51 +76,6 @@ in
     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 = {
@@ -125,13 +93,14 @@ in
   programs.mosh.enable = true;
 
   system.autoUpgrade = {
-    enable = true;
+    enable = false;
     dates = "02:10";
     randomizedDelaySec = "59 min";
     allowReboot = true;
     flake = "git+file://${config.services.gitolite.dataDir}/repositories/nixfiles.git";
     flags = [
       "--no-write-lock-file"
+      "--impure"
       "--update-input"
       "nixpkgs-small"
       "--update-input"
@@ -168,6 +137,7 @@ in
 
   services.nix-serve = {
     enable = true;
+    package = pkgs.nix-serve-ng;
     secretKeyFile = config.age.secrets.binarycache.path;
   };
 
@@ -209,51 +179,111 @@ in
       ];
       allowedUDPPorts = [
         53
+        443 # HTTP/3 (QUIC)
         3478
         6885 # DHT
         6922
       ];
+      trustedInterfaces = [ "tailscale0" ];
     };
     resolvconf = {
-      enable = true;
+      enable = false;
       useLocalResolver = false;
     };
   };
-  services.resolved.enable = false;
+  services.resolved = {
+    enable = true;
+    llmnr = "false";
+    dnssec = "true";
+  };
   systemd.network = {
     enable = true;
     networks.${netif} =
       {
         name = netif;
-        gateway = [ net-gw ];
-        routes = [{
-          routeConfig = {
+        routes = [
+          {
             Gateway = net-gw6;
             PreferredSource = net-ip6;
-          };
-        }];
+            QuickAck = true;
+            InitialCongestionWindow = 30;
+            InitialAdvertisedReceiveWindow = 30;
+          }
+          {
+            Gateway = net-gw;
+            QuickAck = true;
+            InitialCongestionWindow = 30;
+            InitialAdvertisedReceiveWindow = 30;
+          }
+        ];
         address = [
           "${net-ip6}/${net-mask6}"
           "${net-rdnsip}/${net-mask6}"
         ];
         addresses = [{
-          addressConfig = {
-            Address = "${net-ip4}/${net-mask4}";
-            Peer = "${net-gw}/32";
-          };
+          Address = "${net-ip4}/${net-mask4}";
+          Peer = "${net-gw}/32";
         }];
       };
+    wait-online = {
+      extraArgs = [ "--interface=${netif}" ];
+    };
+  };
+
+  services.tailscale = {
+    enable = true;
+    extraUpFlags = [ "--accept-routes" ];
+    useRoutingFeatures = "client";
+  };
+  services.golink = {
+    enable = true;
+    tailscaleAuthKeyFile = config.age.secrets.golink.path;
   };
 
   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";
-  };
+  zramSwap = {
+    enable = true;
+    algorithm = "zstd";
+  };
+
+  boot.kernel.sysctl =
+    let
+      buffer_size = 16 * 1024 * 1024;
+      server_count = 2;
+      max_clients = 100;
+      page_size = 4096;
+      # This server might have 100 clients simultaneously, so:
+      #   max(tcp_wmem) * 2 * 100 / 4096
+      mem = toString (buffer_size * server_count * max_clients / page_size);
+    in
+    {
+      "net.ipv4.tcp_allowed_congestion_control" = "bbr illinois reno";
+      "net.ipv4.tcp_congestion_control" = "bbr";
+      "net.core.default_qdisc" = "fq";
+
+      # Provide adequate buffer memory.
+      # rmem_max and wmem_max are TCP max buffer size
+      # settable with setsockopt(), in bytes
+      # tcp_rmem and tcp_wmem are per socket in bytes.
+      # tcp_mem is for all TCP streams, in 4096-byte pages.
+      # The following are suggested on IBM's
+      # High Performance Computing page
+      "net.core.rmem_max" = buffer_size;
+      "net.core.wmem_max" = buffer_size;
+      "net.core.rmem_default" = buffer_size;
+      "net.core.wmem_default" = buffer_size;
+      "net.ipv4.tcp_rmem" = "4096 87380 ${toString buffer_size}";
+      "net.ipv4.tcp_wmem" = "4096 87380 ${toString buffer_size}";
+      "net.ipv4.tcp_mem" = "${mem} ${mem} ${mem}";
+
+      "net.ipv4.tcp_sack" = false;
+      "net.ipv4.tcp_dsack" = false;
+
+      "net.ipv4.tcp_slow_start_after_idle" = false;
+    };
 
   security.sudo.execWheelOnly = true;
   security.sudo.extraConfig = ''
@@ -294,6 +324,7 @@ in
       "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBmDSZnUzIPQowLrKSa24eSb1WFQe7yPjTcDPPe3UY0Q nix@mba"
       "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE9of82WBHK8nr8L9RGeieLMfcAWaFCeCkmvYHM9LCuT nanopi"
       "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIy9jFioBvV0JA0lc+De2N+vDOABGHgCECW6vkD33CE4 sourcehut"
+      "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIII7sWEwsm8JZiJ0LUnjSt0Kg1RXypG6p5AzP/R2n5ca actions@github.com"
     ];
   };
 
@@ -305,40 +336,53 @@ in
   # (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
-    '';
-  };
+  services.powerdns =
+    let
+      inherit (lib.lists) flatten;
+      inherit (lib.strings) concatStringsSep;
+      he = rec {
+        notify = "216.218.130.2";
+        axfr = [
+          "216.218.133.2"
+          "2001:470:600::2"
+        ];
+      };
+      iplist = ips: concatStringsSep "," (flatten ips);
+    in
+    {
+      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=${iplist [ he.notify ]}
+        allow-axfr-ips=${iplist [ he.axfr ]}
+        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;
@@ -497,21 +541,6 @@ in
     };
   };
 
-  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; [
@@ -554,7 +583,7 @@ in
     };
     acceptTerms = true;
     certs."alanpearce.eu" = {
-      extraDomainNames = [ "*.alanpearce.eu" ];
+      extraDomainNames = [ "*.alanpearce.eu" "*.linde.alanpearce.eu" ];
     };
     certs."dns.alanpearce.eu" = {
       reloadServices = map (x: "kresd@${toString x}") (range 1 config.services.kresd.instances);
@@ -572,259 +601,284 @@ in
       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 = ''
-          encode zstd gzip
-          root * /srv/http/files
-          file_server browse
-        '';
-      };
-      "git.alanpearce.eu" =
-        let
-          fcgi = config.services.fcgiwrap;
-          fcgisocket = "${fcgi.socketType}/${fcgi.socketAddress}";
-        in
-        {
+    virtualHosts =
+      let
+        inherit (import ../lib/caddy.nix { inherit lib; }) security-headers;
+      in
+      {
+        "http://" = {
+          # Needed for HTTP->HTTPS servers
+        };
+        "alanpearce.eu" = {
+          serverAliases = [ "www.alanpearce.eu" "test.alanpearce.eu" ];
           useACMEHost = "alanpearce.eu";
           extraConfig = ''
-            root * ${pkgs.cgit-pink}/cgit/
             encode zstd gzip
-            handle_path /custom/* {
-              file_server {
-                root /srv/http/cgit/
-              }
+            root * /srv/http/website/public
+            file_server
+            ${security-headers {}}
+            handle_errors {
+              rewrite * /404.html
+              file_server
             }
-            rewrite /robots.txt /assets/robots.txt
-            handle_path /assets/* {
-              file_server  {
-                hide cgit.cgi
-              }
+          '';
+        };
+        "${hostname}.alanpearce.eu" = {
+          serverAliases = [ "https://" ];
+          useACMEHost = "alanpearce.eu";
+          extraConfig = ''
+            respond * 204
+            ${security-headers {}}
+          '';
+        };
+        "pdns.alanpearce.eu" = {
+          useACMEHost = "alanpearce.eu";
+          extraConfig = ''
+            log {
+              output discard
             }
-            @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
-                }
-              }
+            reverse_proxy 127.0.0.1:8081
+          '';
+        };
+        "id.alanpearce.eu" = {
+          useACMEHost = "alanpearce.eu";
+          extraConfig = ''
+            encode zstd gzip
+            ${security-headers {}}
+            reverse_proxy http://${config.services.dex.settings.web.http}
+          '';
+        };
+        "dns.alanpearce.eu" = {
+          useACMEHost = "alanpearce.eu";
+          extraConfig = ''
+            log {
+              output discard
             }
-            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/
-                  ''}
-                  }
-                }
+            encode zstd gzip
+            reverse_proxy localhost:443 {
+              transport http {
+                tls_server_name dns.alanpearce.eu
+              }
             }
           '';
         };
-      "ntfy.alanpearce.eu" = {
-        useACMEHost = "alanpearce.eu";
-        extraConfig = ''
-          encode zstd gzip
-          reverse_proxy localhost${config.services.ntfy-sh.settings.listen-http}
-        '';
-      };
-      "searchix.alanpearce.eu" = {
-        useACMEHost = "alanpearce.eu";
-        extraConfig = ''
-          reverse_proxy localhost:${toString config.services.searchix.settings.web.port} {
-            health_uri /health
-            health_status 2xx
-          }
-          encode zstd gzip {
-            match {
-              header Content-Type text/*
-              header Content-Type application/json*
-              header Content-Type application/javascript*
-              header Content-Type application/opensearchdescription+xml
-              header Content-Type application/atom+xml*
-              header Content-Type application/rss+xml*
-              header Content-Type image/svg+xml*
-            }
-          }
-        '';
-      };
-      "legit.alanpearce.eu" =
-        let
-          server = config.services.legit.settings.server;
-        in
-        {
+        "files.alanpearce.eu" = {
           useACMEHost = "alanpearce.eu";
           extraConfig = ''
             encode zstd gzip
-            handle_path /static/* {
-              root * /srv/http/legit/src/static
-              file_server
-            }
-            reverse_proxy ${server.host}:${toString server.port}
+            ${security-headers {}}
+            root * /srv/http/files
+            file_server browse
           '';
         };
-      "papers.alanpearce.eu" = {
-        extraConfig = ''
-          encode zstd gzip
-          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
-        {
+        "ntfy.alanpearce.eu" = {
+          useACMEHost = "alanpearce.eu";
           extraConfig = ''
-            reverse_proxy ${ns.bindAddress}:${toString ns.port}
+            encode zstd gzip
+            ${security-headers {}}
+            reverse_proxy localhost${config.services.ntfy-sh.settings.listen-http}
           '';
         };
-    };
+        "searchix.alanpearce.eu" = {
+          useACMEHost = "alanpearce.eu";
+          serverAliases = [ "searchix.linde.alanpearce.eu" ];
+          extraConfig = ''
+            reverse_proxy localhost:${toString config.services.searchix.settings.web.port} {
+              health_uri /health
+              health_status 2xx
+            }
+            encode zstd gzip {
+              match {
+                header Content-Type text/*
+                header Content-Type application/json*
+                header Content-Type application/javascript*
+                header Content-Type application/opensearchdescription+xml
+                header Content-Type application/atom+xml*
+                header Content-Type application/rss+xml*
+                header Content-Type image/svg+xml*
+              }
+            }
+          '';
+        };
+        "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 = {
+  networking.nat = {
     enable = true;
-    group = "gitolite";
-    preforkProcesses = 2;
-    socketType = "tcp6";
-    socketAddress = "[::1]:9000";
+    internalInterfaces = [ "ve-+" ];
+    externalInterface = netif;
+    enableIPv6 = true;
   };
-  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' );
-    '';
+
+  users.users.paperless = {
+    group = "paperless";
+    uid = config.ids.uids.paperless;
+    home = "/srv/paperless";
   };
-  services.legit = {
-    enable = true;
-    group = "gitolite";
-    settings = {
-      server.name = "legit.alanpearce.eu";
-      dirs = {
-        templates = "/srv/http/legit/src/templates";
+  users.groups.paperless.members = [ "alan" "syncthing" ];
+  containers.papers =
+    let
+      hostDataDir = config.users.users.paperless.home;
+      localAddress6 = "fc00::2";
+      tsHostname = "papers.${ts-domain}";
+      tsPort = 41642;
+    in
+    {
+      # or maybe socket activated?
+      autoStart = true;
+      # does TS need this?
+      enableTun = true;
+      privateNetwork = true;
+      hostAddress6 = "fc00::1";
+      inherit localAddress6;
+      forwardPorts = [{
+        hostPort = tsPort;
+      }];
+      bindMounts = {
+        ${config.services.paperless.dataDir} = {
+          hostPath = hostDataDir;
+          isReadOnly = false;
+        };
       };
-      repo = {
-        scanPath = "/srv/http/legit/repos";
-        readme = [
-          "readme"
-          "readme.md"
-          "README.md"
+      config = {
+        environment.systemPackages = with pkgs; [
+          lsof
         ];
+        networking = {
+          useHostResolvConf = false;
+          resolvconf.enable = false;
+          firewall.trustedInterfaces = [ "tailscale0" ];
+          firewall.rejectPackets = true;
+          nameservers = config.networking.nameservers;
+        };
+        services.resolved = {
+          enable = true;
+          llmnr = "false";
+        };
+        services.tailscale = {
+          enable = true;
+          openFirewall = true;
+          permitCertUid = "caddy";
+          port = tsPort;
+        };
+        services.caddy = {
+          enable = true;
+          email = "caddy@alanpearce.eu";
+          virtualHosts = {
+            "http://" = {
+              # avoid logging to an awkward file name based on the attribute name i.e. http://
+              hostName = "papers";
+              extraConfig = ''
+                redir ${tsHostname}{uri}
+              '';
+            };
+            ${tsHostname} = {
+              extraConfig = ''
+                encode zstd gzip
+                tls {
+                  get_certificate tailscale
+                }
+                handle_path /static/* {
+                  root * ${config.services.paperless.package}/lib/paperless-ngx/static
+                  file_server
+                }
+                reverse_proxy [::1]:${toString config.services.paperless.port}
+              '';
+            };
+          };
+        };
+        services.paperless = {
+          enable = true;
+          address = "[::1]";
+          settings = {
+            PAPERLESS_DBENGINE = "sqlite";
+            PAPERLESS_TIME_ZONE = "Europe/Berlin";
+
+            PAPERLESS_URL = "https://${tsHostname}";
+            PAPERLESS_TRUSTED_PROXIES = "[::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;
+          };
+        };
+        system.stateVersion = "24.11";
       };
     };
-  };
-  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 = {
+  services.etcd = {
     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/*" ];
+    initialClusterState = "existing";
+    dataDir = "/var/lib/etcd"; # TODO backup
+  };
 
-      PAPERLESS_FILENAME_FORMAT = "{correspondent}/{created} {title} {asn}";
-      PAPERLESS_FILENAME_FORMAT_REMOVE_NONE = true;
+  services.dex =
+    let
+      issuer = "https://id.alanpearce.eu/";
+    in
+    {
+      enable = true;
+      environmentFile = config.age.secrets.dex.path;
+      settings = {
+        inherit issuer;
+        storage = {
+          type = "etcd";
+          config = {
+            endpoints = config.services.etcd.listenClientUrls;
+            namespace = "dex/";
+          };
+        };
+        web.http = "127.0.0.1:5556";
+        connectors = [{
+          type = "github";
+          id = "github";
+          name = "GitHub";
+          config = {
+            clientID = "$GITHUB_CLIENT_ID";
+            clientSecret = "$GITHUB_CLIENT_SECRET";
+            redirectURI = "${issuer}callback";
+            orgs = [{
+              name = "alan-pearce";
+            }];
+            teamNameField = "slug";
+            useLoginAsID = true;
+          };
+        }];
+        staticClients = [
+          {
+            name = "Tailscale";
+            id = "oCaiv7aije1thaep0eib";
+            secretEnv = "TAILSCALE_CLIENT_SECRET";
+            redirectURIs = [ "https://login.tailscale.com/a/oauth_response" ];
+          }
+        ];
+      };
     };
-  };
 
   services.syncthing = {
     enable = true;
@@ -838,45 +892,69 @@ in
   services.searchix = {
     enable = true;
     settings = {
-      web = {
-        baseURL = "https://searchix.alanpearce.eu";
-        sentryDSN = "https://26d4cd8d20157ae2f6b4726ceae1a563@o4507187730120704.ingest.de.sentry.io/4507187734970448";
-        contentSecurityPolicy = {
-          script-src = [
-            "'self'"
-            "https://gc.zgo.at"
-            "https://js-de.sentry-cdn.com"
-            "https://browser.sentry-cdn.com"
-          ];
-          img-src = [
-            "'self'"
-            "https://gc.zgo.at"
-          ];
-          connect-src = [
-            "'self'"
-            "https://searchix.goatcounter.com/count"
-            "*.sentry.io"
-          ];
-          worker-src = [
-            "blob:"
-          ];
+      web =
+        let
+          baseURL = "https://searchix.alanpearce.eu";
+        in
+        {
+          inherit baseURL;
+          sentryDSN = "https://26d4cd8d20157ae2f6b4726ceae1a563@o4507187730120704.ingest.de.sentry.io/4507187734970448";
+          contentSecurityPolicy =
+            let
+              self = "'self'";
+            in
+            {
+              script-src = [
+                (baseURL + "/static/")
+                "https://gc.zgo.at"
+                "https://js-de.sentry-cdn.com"
+                "https://browser.sentry-cdn.com"
+              ];
+              img-src = [
+                self
+                "https://gc.zgo.at"
+              ];
+              connect-src = [
+                self
+                "https://searchix.goatcounter.com/count"
+                "*.sentry.io"
+              ];
+              worker-src = [
+                "blob:"
+              ];
+            };
+          extraHeadHTML = ''
+            <script async
+              src="https://js-de.sentry-cdn.com/d735e99613a86e1625fb85d0e8e762de.min.js"
+              crossorigin="anonymous"></script>
+            <script data-goatcounter="https://searchix.goatcounter.com/count"
+                    async src="//gc.zgo.at/count.v4.js"
+                    crossorigin="anonymous"
+                    integrity="sha384-nRw6qfbWyJha9LhsOtSb2YJDyZdKvvCFh0fJYlkquSFjUxp9FVNugbfy8q1jdxI+"></script>
+          '';
         };
-        extraHeadHTML = ''
-          <script async
-            src="https://js-de.sentry-cdn.com/d735e99613a86e1625fb85d0e8e762de.min.js"
-            crossorigin="anonymous"></script>
-          <script data-goatcounter="https://searchix.goatcounter.com/count"
-                async src="//gc.zgo.at/count.js"></script>
-        '';
-      };
 
       importer.sources = {
         darwin = {
           enable = true;
           fetcher = "download";
-          url = "https://alanpearce.github.io/nix-darwin-options";
+          url = "https://alanpearce.github.io/nix-options/darwin";
+        };
+        home-manager = {
+          enable = true;
+          fetcher = "download";
+          url = "https://alanpearce.github.io/nix-options/home-manager";
+        };
+        nixpkgs = {
+          enable = true;
+          fetcher = "channel-nixpkgs";
+          channel = "nixos-unstable";
+        };
+        nixos = {
+          enable = true;
+          fetcher = "channel-nixpkgs";
+          channel = "nixos-unstable";
         };
-        home-manager.enable = true;
       };
     };
   };
diff --git a/system/mba.nix b/system/mba.nix
index c39862cf..abed520b 100644
--- a/system/mba.nix
+++ b/system/mba.nix
@@ -8,6 +8,7 @@
   networking = {
     hostName = "mba";
   };
+  services.tailscale.enable = true;
 
   services.activate-system.enable = true;
 
diff --git a/system/nanopi.nix b/system/nanopi.nix
index 45eae872..6ee61e69 100755
--- a/system/nanopi.nix
+++ b/system/nanopi.nix
@@ -1,16 +1,19 @@
 { config
 , pkgs
 , lib
-, inputs
 , ...
 }:
 let
   fsTypes = [ "f2fs" "ext" "exfat" "vfat" ];
+  domain = "home.arpa";
+  ts_domain = "hydra-pinecone.ts.net";
 in
 {
   imports = [
     ./nanopi-hardware.nix
-    (inputs.nixos-hardware + "/friendlyarm/nanopi-r5s")
+    <agenix/modules/age.nix>
+    <nixos-hardware/friendlyarm/nanopi-r5s>
+    <home-manager/nixos>
   ];
 
   age.secrets = {
@@ -68,6 +71,26 @@ in
     };
   };
 
+  systemd.services.backup-golink = {
+    enable = true;
+    startAt = "daily";
+    description = "Export short links from golink";
+    path = with pkgs; [ curl gitMinimal ];
+    script = ''
+      [ -d golink ] || git init --quiet golink --initial-branch=main --shared=world
+      git config --global user.email linde@alanpearce.eu
+      cd golink
+      curl https://go.${ts_domain}/.export > links.json
+      git add links.json
+      git commit -m $(date +%F)
+    '';
+    serviceConfig = {
+      Type = "oneshot";
+      User = "linde";
+      WorkingDirectory = config.users.users.linde.home;
+    };
+  };
+
   services.journald.extraConfig = ''
     MaxRetentionSec=1 month
   '';
@@ -85,61 +108,33 @@ in
   systemd.network.config.networkConfig = {
     SpeedMeter = true;
   };
+
   networking = {
     hostName = "nanopi";
-    domain = "lan";
+    domain = domain;
+    search = [ domain ];
+    hosts = {
+      "fd7a:115c:a1e0::53" = [ "tailscale" "ts" ];
+      "192.168.100.1" = [ "modem" "pyur" ];
+      "192.168.4.1" = [ "lte" ];
+    };
     useDHCP = false;
     useNetworkd = true;
-    nameservers = [
-      "176.9.93.198"
-      "176.9.1.117"
-      "2a01:4f8:151:34aa::198"
-      "2a01:4f8:141:316d::117"
-    ];
+    nat = {
+      enable = true;
+      internalInterfaces = [ "bridge0" "lan1" "lan2" ];
+      externalInterface = "wan0";
+    };
     firewall = {
       enable = true;
       rejectPackets = true;
       logRefusedConnections = false;
       pingLimit = "5/second";
       filterForward = true; # we are a router
-      allowedUDPPorts = [
-        53
-        123
-      ];
-      allowedTCPPorts = [
-        53
-        123
-        80
-        443
+      trustedInterfaces = [
+        "bridge0"
+        "tailscale0"
       ];
-      interfaces.bridge0 = {
-        allowedTCPPorts = [
-          53
-          67
-          139
-          445
-          1883
-          3000
-          3689
-          5357
-          5533 # SmartDNS
-          8096
-          9091 # Transmission
-        ];
-        allowedUDPPorts = [
-          53
-          67
-          69
-          137
-          4011 # PXE
-          5533 # SmartDNS
-          5353
-          5355 # LLMNR
-          3702 # Samba WSDD
-          41641
-          51827
-        ];
-      };
       interfaces.wan0 = {
         allowedTCPPorts = [
           6980 # aria2c
@@ -151,8 +146,10 @@ in
         ];
       };
       extraForwardRules = ''
-        iifname { "wan0", "wlan0", "wwan0" } oifname { "lan1", "lan2", "bridge0" } icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, echo-request, mld-listener-query, nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } accept
-        iifname { "lan1", "lan2", "bridge0" } oifname { "wan0", "wlan0", "wwan0" } accept
+        iifname { "wlan0", "lte0" } oifname { "lan1", "lan2", "bridge0" } icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, echo-request, mld-listener-query, nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } accept
+        iifname { "lan1", "lan2", "bridge0" } oifname { "wlan0", "lte0" } accept
+        iifname "tailscale0" oifname "bridge0" accept
+        iifname "bridge0" oifname "tailscale0" accept
       '';
     };
     nftables = {
@@ -163,7 +160,7 @@ in
           content = ''
             chain postrouting {
               type nat hook postrouting priority srcnat; policy accept;
-              oifname { "wan0", "wlan0", "wwan0" } masquerade
+              oifname { "wlan0", "lte0" } masquerade
             }
             chain prerouting {
               type nat hook prerouting priority dstnat;
@@ -185,7 +182,57 @@ in
       # };
     };
   };
-  services.resolved.enable = false;
+
+  networking = {
+    resolvconf = {
+      # having this enabled (the default) is pointless
+      # a) this device has fixed upstream nameservers
+      enable = false;
+      # b) it makes tailscale think it should change the search domains for MagicDNS
+      # ... due to this:
+      # useLocalResolver = false;
+      # which is set by kresd?!
+      # https://github.com/NixOS/nixpkgs/blob/7780e5160e011b39019797a4c4b1a4babc80d1bf/nixos/modules/services/networking/kresd.nix#L113
+    };
+    nameservers = lib.optionals config.services.dnsmasq.enable [
+      "::1"
+      "127.0.0.1"
+    ];
+  };
+  services.resolved = {
+    # this allows link-specific DNS configuration, which is useful.
+    enable = true;
+    # why use simple boolean when string do trick?
+    llmnr = "false";
+    dnssec = "true";
+    fallbackDns = [
+      "9.9.9.9"
+      "149.112.112.112"
+      "2620::fe:fe"
+      "2620::fe:9"
+      "116.203.248.56"
+      "2a01:4f8:c012:23a4::1"
+    ];
+  };
+
+  # leaving this here just in case I ever think about disabling both `resolvconf` and `resolved`
+  # I thought that there would have been a fallback that does this anyway, but apparently not.
+  environment.etc."resolv.conf".text = lib.mkDefault (lib.optionalString
+    (
+      !config.networking.resolvconf.enable
+      &&
+      !config.services.resolved.enable
+    ) ''
+    search ${domain} ${ts_domain}
+    nameserver ::1
+    nameserver 127.0.0.1
+    options edns0
+  '');
+
+  services.tailscale = {
+    enable = true;
+    extraUpFlags = [ "--accept-dns=false" "--advertise-routes=10.0.0.0/20,fd12:d04f:65d:42::/56" ];
+  };
 
   programs.command-not-found.enable = false;
 
@@ -193,6 +240,10 @@ in
     enable = true;
     openFirewall = true;
     startWhenNeeded = false;
+    settings = {
+      PasswordAuthentication = false;
+      KbdInteractiveAuthentication = false;
+    };
   };
   programs.mosh.enable = true;
   services.sshguard = {
@@ -203,7 +254,7 @@ in
   systemd.network = {
     enable = true;
     wait-online = {
-      ignoredInterfaces = [ "wan0" "wlan0" "wwan0" ];
+      extraArgs = [ "--interface" "bridge0" ];
     };
     links = {
       "10-name-lan1" = {
@@ -233,10 +284,10 @@ in
           Name = "wlan0";
         };
       };
-      "10-name-wwan0" = {
+      "10-name-lte0" = {
         matchConfig.MACAddress = "34:4b:50:00:00:00";
         linkConfig = {
-          Name = "wwan0";
+          Name = "lte0";
         };
       };
     };
@@ -254,7 +305,6 @@ in
         bridge = [ "bridge0" ];
         linkConfig = {
           MACAddress = "82:E0:06:9C:8E:7C";
-          RequiredForOnline = "no";
         };
         networkConfig.LinkLocalAddressing = "no";
       };
@@ -265,11 +315,20 @@ in
           "10.0.0.1/20"
           "fd12:d04f:65d:42::1/56"
         ];
+        addresses = [
+          {
+            Address = "fe80::1/64";
+            Scope = "link";
+          }
+        ];
         networkConfig = {
           IPv6AcceptRA = false;
-          IPv6SendRA = true;
+          IPv6SendRA = false;
           DHCPPrefixDelegation = true;
           ConfigureWithoutCarrier = true;
+          MulticastDNS = true;
+          BindCarrier = [ "lan0" "lan1" ];
+          Domains = [ domain ];
         };
         dhcpPrefixDelegationConfig = {
           UplinkInterface = "wan0";
@@ -277,17 +336,9 @@ in
           Assign = true;
           Token = "::1";
         };
-        ipv6SendRAConfig = {
-          RouterLifetimeSec = 1800;
-          EmitDNS = true;
-          DNS = "fd12:d04f:65d:42::1";
-          EmitDomains = true;
-          Domains = [ config.networking.domain ];
-        };
       };
-      "50-wwan0" = {
-        matchConfig.Name = "wwan0";
-        linkConfig.RequiredForOnline = false;
+      "50-lte0" = {
+        matchConfig.Name = "lte0";
         networkConfig = {
           DHCP = "yes";
           IPv6AcceptRA = true;
@@ -296,17 +347,16 @@ in
         dhcpV4Config = {
           UseDNS = false;
           SendHostname = false;
-          RouteMetric = 2048;
+          UseRoutes = false;
         };
         ipv6AcceptRAConfig.UseDNS = false;
         routes = [
           {
-            routeConfig = {
-              Gateway = "_dhcp4";
-              QuickAck = true;
-              InitialCongestionWindow = 30;
-              InitialAdvertisedReceiveWindow = 30;
-            };
+            Gateway = "_dhcp4";
+            Metric = 2048;
+            QuickAck = true;
+            InitialCongestionWindow = 30;
+            InitialAdvertisedReceiveWindow = 30;
           }
         ];
         cakeConfig = {
@@ -320,7 +370,6 @@ in
       };
       "50-wan" = {
         matchConfig.Name = "wan0";
-        linkConfig.RequiredForOnline = "no";
         networkConfig = {
           DHCP = "yes";
           IPv6AcceptRA = true;
@@ -328,6 +377,7 @@ in
         };
         dhcpV4Config = {
           UseDNS = false;
+          UseRoutes = false;
           SendHostname = false;
           SendRelease = false;
           UseHostname = false;
@@ -343,15 +393,28 @@ in
         };
         ipv6AcceptRAConfig = {
           UseDNS = false;
+          UseGateway = false;
         };
         addresses = [
           {
-            addressConfig = {
-              Address = "192.168.100.10/24";
-              # Peer = "192.168.100.1/32";
-              Label = "wan0:0";
-              # Scope = "link";
-            };
+            Address = "192.168.100.10/24";
+            # Peer = "192.168.100.1/32";
+            Label = "wan0:0";
+            # Scope = "link";
+          }
+        ];
+        routes = [
+          {
+            Gateway = "_dhcp4";
+            QuickAck = true;
+            InitialCongestionWindow = 30;
+            InitialAdvertisedReceiveWindow = 30;
+          }
+          {
+            Gateway = "_ipv6ra";
+            QuickAck = true;
+            InitialCongestionWindow = 30;
+            InitialAdvertisedReceiveWindow = 30;
           }
         ];
         cakeConfig = {
@@ -365,7 +428,6 @@ in
       };
       "60-wlan" = {
         matchConfig.MACAddress = "9c:53:22:33:bf:e9";
-        linkConfig.RequiredForOnline = "no";
         networkConfig = {
           DHCP = "yes";
           IPForward = "yes";
@@ -380,13 +442,11 @@ in
         };
         routes = [
           {
-            routeConfig = {
-              Metric = 2048;
-              Gateway = "_dhcp4";
-              QuickAck = true;
-              InitialCongestionWindow = 30;
-              InitialAdvertisedReceiveWindow = 30;
-            };
+            Metric = 2048;
+            Gateway = "_dhcp4";
+            QuickAck = true;
+            InitialCongestionWindow = 30;
+            InitialAdvertisedReceiveWindow = 30;
           }
         ];
         cakeConfig = {
@@ -415,12 +475,13 @@ in
 
   services.dnsmasq = {
     enable = true;
-    resolveLocalQueries = true;
+    # let systemd-resolved.do this
+    resolveLocalQueries = false;
     alwaysKeepRunning = true;
     settings = {
       local-ttl = 60;
-      domain = "lan";
-      dhcp-fqdn = false;
+      domain = domain;
+      dhcp-fqdn = true;
       domain-needed = true;
       bogus-priv = true;
       no-resolv = true;
@@ -434,34 +495,42 @@ in
         "2620::fe:9"
         "116.203.248.56"
         "2a01:4f8:c012:23a4::1"
-        # "127.0.0.1#5553"
-        # "::1#5553"
-        "127.0.0.1#5533"
-        "::1#5533"
+        # kresd
+        "127.0.0.1#5553"
+        "::1#5553"
+        # smartdns
+        # "127.0.0.1#5533"
+        # "::1#5533"
+        "/ts.net/tailscale"
       ];
       localise-queries = true;
       cname = [
-        "homeassistant,ha"
+        "ha,home-assistant"
       ];
       interface-name = [
-        "home.alanpearce.eu,wan0"
-        "nanopi.alanpearce.eu,wan0"
-        "nanopi.lan.alanpearce.eu,bridge0"
-        "syncthing.lan.alanpearce.eu,bridge0"
-        "wan,wan0"
-        "wlan,wlan0"
-        "wwan,wwan0"
+        "nanopi.${domain},bridge0"
+        "wan.${domain},wan0"
+        "wlan.${domain},wlan0"
       ];
       interface = [
+        "lo"
         "bridge0"
       ];
+      no-dhcp-interface = [
+        "tailscale0"
+      ];
       # auth-zone = "lan,wan0";
       # auth-server = [
       #   "nanopi.alanpearce.eu,wan0"
       # ];
-      bind-interfaces = false;
+      bind-interfaces = true;
 
-      no-hosts = true;
+      # if this is false, a remote query for nanopi returns 127.0.0.2, because that's in /etc/hosts
+      no-hosts = false;
+      expand-hosts = true;
+
+      dnssec = true;
+      trust-anchor = ".,20326,8,2,E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D";
 
       enable-ra = true;
       dhcp-lease-max = 240;
@@ -469,19 +538,17 @@ in
       dhcp-rapid-commit = true;
       dhcp-range = [
         "10.0.1.0,10.0.1.250,12h"
-        "::, constructor:bridge0, ra-stateless, 48h"
-        "fd12:d04f:65d::, ra-stateless, ra-names, 48h"
+        "::,constructor:bridge0,ra-stateless,ra-names,48h"
       ];
       dhcp-host = [
         "00:a0:de:b3:0c:01,10.0.0.50,wxa-50"
         "10:f0:68:12:b1:e0,10.0.0.11,Ruckus"
         "9c:93:4e:ad:05:c8,10.0.0.210,xerox-b210"
         "00:08:9b:f5:b8:25,10.0.0.42,dontpanic"
-        "d8:3a:dd:34:85:cc,d8:3a:dd:34:85:cd,10.0.0.81,ha"
+        "d8:3a:dd:34:85:cc,d8:3a:dd:34:85:cd,10.0.0.81,home-assistant"
       ];
       dhcp-option = [
         "option:ntp-server,0.0.0.0"
-        "option:dns-server,0.0.0.0,10.0.0.81"
         "option:tftp-server,0.0.0.0"
         "option:ip-forward-enable,0" # ip-forwarding
         "252,\"\\n\""
@@ -512,7 +579,7 @@ in
 
   services.networkd-dispatcher = {
     # broken?
-    enable = false;
+    enable = true;
     rules = {
       update-home-address = {
         onState = [ "configured" "configuring" ];
@@ -527,6 +594,18 @@ in
           exit 0
         '';
       };
+      tailscale-subnet-router-optimisation = {
+        onState = [ "routable" ];
+        script = ''
+          #!${pkgs.runtimeShell}
+          set -eu
+
+          if [[ $IFACE == "wan0" && $OperationalState == "routable" ]]
+          then
+            ${pkgs.ethtool}/bin/ethtool -K $IFACE rx-udp-gro-forwarding on rx-gro-list off
+          fi
+        '';
+      };
     };
   };
 
@@ -555,6 +634,7 @@ in
       "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMvcW4Z9VxOQgEJjsRC1uSMwEJ4vru9BwjT+Z50nawp4 lan"
     ];
   };
+  home-manager.users.alan = import ../user/nanopi.nix;
 
   users.groups = {
     linde.members = [ ];
@@ -566,6 +646,7 @@ in
       isSystemUser = true;
       shell = "/bin/sh";
       home = "/srv/backup/linde";
+      homeMode = "755";
       createHome = true;
       packages = with pkgs; [ rdiff-backup ];
       openssh.authorizedKeys.keys = [
@@ -614,7 +695,7 @@ in
   };
   nixpkgs.config.allowUnfree = true;
   system.autoUpgrade = {
-    enable = false;
+    enable = true;
     dates = "04:15";
     randomizedDelaySec = "59 min";
     flake = "git+https://git.alanpearce.eu/nixfiles";
@@ -700,22 +781,6 @@ in
     '';
   };
 
-  services.avahi = {
-    enable = true;
-    nssmdns4 = true;
-    denyInterfaces = [ "wan0" "wwan0" "wlan0" ];
-    browseDomains = [
-      "alanpearce.eu"
-    ];
-    publish = {
-      enable = true;
-      hinfo = true;
-      addresses = true;
-      userServices = true;
-      workstation = true;
-    };
-  };
-
   services.samba = {
     enable = true;
     enableNmbd = false;
@@ -786,7 +851,7 @@ in
         "10.0.0.1:53 -group lan -exclude-default-group"
       ];
       nameserver = [
-        "/lan/lan"
+        "/${domain}/${domain}"
       ];
       dualstack-ip-selection = true;
       dualstack-ip-selection-threshold = 10;
diff --git a/system/prefect.nix b/system/prefect.nix
index 12f0dd56..0fc80eb9 100644
--- a/system/prefect.nix
+++ b/system/prefect.nix
@@ -10,21 +10,25 @@
     ./settings/configuration/user.nix
     ./settings/hardware/audio.nix
     ./settings/hardware/bare-metal.nix
-    ./settings/hardware/personal-computer.nix
     ./settings/hardware/mouse.nix
     ./settings/hardware/systemd-boot.nix
     ./settings/hardware/nvidia-gpu.nix
     ./settings/hardware/keyboard.nix
     ./settings/hardware/keyboard-lofree.nix
+    ./settings/hardware/trezor.nix
     ./settings/services/syncthing.nix
-    ./settings/services/zeroconf.nix
+    ./settings/services/virtualisation.nix
     ./settings/user-interface.nix
     ./settings/programs/base.nix
-    ./settings/programs/gnupg.nix
     ./settings/programs/kde.nix
     ./settings/programs/shell.nix
     ./settings/programs/docker.nix
     ./settings/gaming.nix
+    <nixos-hardware/common/cpu/amd>
+    <nixos-hardware/common/cpu/amd/pstate.nix>
+    <nixos-hardware/common/pc/ssd>
+    <nixos-hardware/common/pc>
+    <nixos-hardware/common/gpu/nvidia>
   ];
 
   nixpkgs.hostPlatform = "x86_64-linux";
@@ -40,9 +44,6 @@
     user = "alan";
     enable = true;
   };
-  services.displayManager.sddm = {
-    enableHidpi = false;
-  };
 
   boot.kernelPackages = pkgs.linuxPackages_xanmod;
   boot.extraModulePackages = with config.boot.kernelPackages; [
@@ -102,38 +103,53 @@
   };
 
   systemd.network = {
-    enable = true;
     networks."40-enp7s0" = {
+      matchConfig = {
+        Name = "enp7s0";
+      };
       dhcpV4Config = {
-        UseDNS = true;
+        UseDomains = true;
+      };
+      dhcpV6Config = {
+        UseDomains = true;
       };
       ipv6AcceptRAConfig = {
-        UseDNS = true;
+        UseDomains = true;
+      };
+      networkConfig = {
+        MulticastDNS = true;
       };
     };
   };
   networking = {
+    hostName = "prefect";
     useDHCP = false;
     useNetworkd = true;
     interfaces.enp7s0 = {
       useDHCP = true;
     };
-  };
-  networking.nftables = {
-    enable = true;
-  };
-  networking.firewall = {
-    allowedTCPPorts = [ 80 443 139 445 1024 ];
-    extraInputRules = ''
-      ip saddr 10.0.0.0/8 accept
-      ip6 saddr { fd00::/8, fe80::/10 } accept
-    '';
+    hosts = {
+      "fd7a:115c:a1e0::53" = [ "tailscale" "ts" ];
+    };
+
+    nftables = {
+      enable = true;
+    };
+    firewall = {
+      extraInputRules = ''
+        ip saddr 10.0.0.0/8 accept
+        ip6 saddr { fd00::/8, fe80::/10 } accept
+      '';
+    };
   };
 
-  networking = {
-    hostName = "prefect";
+  services.resolved = {
+    llmnr = "false";
+    dnssec = "true";
   };
 
+  services.tailscale.enable = true;
+
   system.stateVersion = "23.05";
 
   boot.binfmt.emulatedSystems = [ "aarch64-linux" ];
diff --git a/system/settings/base.nix b/system/settings/base.nix
index 5eee9088..81dab9a1 100644
--- a/system/settings/base.nix
+++ b/system/settings/base.nix
@@ -1,14 +1,9 @@
 { config
 , pkgs
 , lib
-, inputs
 , ...
 }:
-let
-  inherit (inputs) self;
-in
 {
   boot.loader.timeout = lib.mkDefault 1;
   services.irqbalance.enable = true;
-  system.configurationRevision = toString (self.rev or self.dirtyRev or self.lastModified or "unknown");
 }
diff --git a/system/settings/configuration/networking.nix b/system/settings/configuration/networking.nix
deleted file mode 100644
index ad4200b1..00000000
--- a/system/settings/configuration/networking.nix
+++ /dev/null
@@ -1,11 +0,0 @@
-{ config
-, pkgs
-, ...
-}: {
-  environment.systemPackages = with pkgs; [ lxqt.lxqt-policykit ]; # provides a default authentification client for policykit
-  services.gvfs.enable = true; # enables gvfs
-
-  imports = [
-    ../services/zeroconf.nix
-  ];
-}
diff --git a/system/settings/configuration/nix-linux.nix b/system/settings/configuration/nix-linux.nix
index 3ee9f420..e11b0389 100644
--- a/system/settings/configuration/nix-linux.nix
+++ b/system/settings/configuration/nix-linux.nix
@@ -20,7 +20,6 @@
   system.autoUpgrade = {
     enable = true;
     flags = [ "--max-jobs" "2" ];
-    flake = "/home/alan/projects/alanpearce/nixfiles";
   };
   systemd.services.nixos-upgrade = {
     script = pkgs.lib.mkForce ''
diff --git a/system/settings/configuration/nix.nix b/system/settings/configuration/nix.nix
index c8db7836..b28fde18 100644
--- a/system/settings/configuration/nix.nix
+++ b/system/settings/configuration/nix.nix
@@ -3,9 +3,7 @@
 , pkgs
 , ...
 }: {
-  imports = [
-    ../../../pin.nix
-  ];
+  imports = [ ../pin.nix ];
   nix = {
     settings = {
       cores = lib.mkDefault 0;
@@ -14,6 +12,15 @@
       keep-derivations = true;
       experimental-features = "nix-command flakes";
       warn-dirty = false;
+      substituters = [
+        "https://nix-community.cachix.org"
+        "https://binarycache.alanpearce.eu"
+      ];
+
+      trusted-public-keys = [
+        "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
+        "binarycache.alanpearce.eu:ZwqO3XMuajPictjwih8OY2+RXnOKpjZEZFHJjGSxAI4="
+      ];
     };
 
     gc = {
diff --git a/system/settings/configuration/user.nix b/system/settings/configuration/user.nix
index 3a7f9620..9d6fed69 100644
--- a/system/settings/configuration/user.nix
+++ b/system/settings/configuration/user.nix
@@ -15,6 +15,7 @@
       "dialout"
       "pipewire"
       "networkmanager"
+      "libvirtd"
       "video"
     ];
     initialPassword = "password";
diff --git a/system/settings/darwin.nix b/system/settings/darwin.nix
index aa55cce5..6d5b357d 100644
--- a/system/settings/darwin.nix
+++ b/system/settings/darwin.nix
@@ -16,6 +16,7 @@
     [ "/run/current-system/sw" "/nix/var/nix/profiles/default" ]
   ];
 
+  environment.darwinConfig = "$HOME/.config/nixpkgs/darwin-configuration.nix";
   nix = {
     daemonIOLowPriority = true;
     gc = {
@@ -24,7 +25,6 @@
     };
     settings.extra-platforms = "aarch64-darwin x86_64-darwin";
 
-    linux-builder.enable = true;
     settings.trusted-users = [ "@admin" ];
   };
 
@@ -32,7 +32,13 @@
     allowUnfree = true;
   };
 
-  launchd.user.agents.lorri = lib.mkIf config.services.lorri.enable {
+  # needed so that nix-darwin can activate the system as root
+  security.sudo.extraConfig = ''
+    Defaults	env_keep += "NIX_PATH"
+  '';
+
+  services.lorri.enable = true;
+  launchd.user.agents.lorri = {
     serviceConfig = {
       RunAtLoad = lib.mkForce false;
       Sockets = {
diff --git a/system/settings/hardware/intel-gpu.nix b/system/settings/hardware/intel-gpu.nix
deleted file mode 100644
index 494cb86e..00000000
--- a/system/settings/hardware/intel-gpu.nix
+++ /dev/null
@@ -1,18 +0,0 @@
-{ config
-, pkgs
-, ...
-}: {
-  # https://wiki.gentoo.org/wiki/Intel#Feature_support
-  services.xserver = {
-    useGlamor = true;
-    deviceSection = ''
-      Option      "DRI"            "3"
-    '';
-    videoDrivers = [ "intel" ];
-  };
-
-  boot.kernelParams = [
-    "i915.enable_guc=2"
-    "i915.fastboot=1"
-  ];
-}
diff --git a/system/settings/hardware/laptop.nix b/system/settings/hardware/laptop.nix
deleted file mode 100644
index bd66fb8f..00000000
--- a/system/settings/hardware/laptop.nix
+++ /dev/null
@@ -1,79 +0,0 @@
-{ config
-, pkgs
-, lib
-, ...
-}: {
-  imports = [
-    ./bluetooth.nix
-    ./bluetooth-audio.nix
-    ./connman.nix
-    ./iwd.nix
-    ./personal-computer.nix
-    ../user-interface.nix
-  ];
-
-  boot.kernelModules = [ "coretemp" ];
-
-  environment.systemPackages = with pkgs; [
-    powerstat
-    powertop
-
-    arandr
-    autorandr
-  ];
-
-  programs.light.enable = true;
-
-  services.autorandr = {
-    enable = true;
-    defaultTarget = "common";
-  };
-  systemd.services.autorandr.wantedBy = [ "graphical.target" ];
-
-  environment.etc.autorandr = {
-    enable = true;
-    source = ../../autorandr;
-    target = "xdg/autorandr";
-  };
-
-  services.logind = {
-    lidSwitch = "suspend";
-    lidSwitchExternalPower = "ignore";
-    extraConfig = ''
-      IdleAction=suspend
-      IdleActionSec=600
-    '';
-  };
-
-  services.acpid = {
-    enable = true;
-    lidEventCommands = ''
-      ${pkgs.autorandr}/bin/autorandr --batch --change
-    '';
-  };
-
-  services.tlp = {
-    extraConfig = ''
-      CPU_SCALING_GOVERNOR_ON_BAT=powersave
-      ENERGY_PERF_POLICY_ON_BAT="balance_power"
-
-      SOUND_POWER_SAVE_ON_AC=60
-      DEVICES_TO_DISABLE_ON_BAT_NOT_IN_USE="bluetooth wwan"
-    '';
-  };
-
-  services.xserver = {
-    libinput = {
-      enable = lib.mkDefault true;
-      naturalScrolling = true;
-      disableWhileTyping = true;
-    };
-    displayManager.sessionCommands = ''
-      ${pkgs.autorandr}/bin/autorandr --change --force
-    '';
-  };
-
-  systemd.services.nixos-upgrade.unitConfig.ConditionACPower = true;
-  systemd.services.nix-gc.unitConfig.ConditionACPower = true;
-  systemd.services.docker-prune.unitConfig.ConditionACPower = true;
-}
diff --git a/system/settings/hardware/mouse.nix b/system/settings/hardware/mouse.nix
index b74d17aa..d4a232af 100644
--- a/system/settings/hardware/mouse.nix
+++ b/system/settings/hardware/mouse.nix
@@ -2,7 +2,7 @@
 , pkgs
 , ...
 }: {
-  services.xserver.libinput = {
+  services.libinput = {
     enable = true;
     mouse = {
       accelProfile = "flat";
diff --git a/system/settings/hardware/network-manager.nix b/system/settings/hardware/network-manager.nix
deleted file mode 100644
index a27ca892..00000000
--- a/system/settings/hardware/network-manager.nix
+++ /dev/null
@@ -1,16 +0,0 @@
-{ config
-, lib
-, pkgs
-, ...
-}: {
-  networking = {
-    networkmanager = {
-      enable = true;
-    };
-  };
-
-  environment.systemPackages = with pkgs; [
-    networkmanagerapplet
-    networkmanager_dmenu
-  ];
-}
diff --git a/system/settings/hardware/personal-computer.nix b/system/settings/hardware/personal-computer.nix
deleted file mode 100644
index 35824136..00000000
--- a/system/settings/hardware/personal-computer.nix
+++ /dev/null
@@ -1,8 +0,0 @@
-{ config
-, pkgs
-, lib
-, ...
-}: {
-  boot.kernelPackages = lib.mkDefault pkgs.linuxPackages_zen;
-  powerManagement.cpuFreqGovernor = "schedutil";
-}
diff --git a/system/settings/hardware/thinkpad.nix b/system/settings/hardware/thinkpad.nix
deleted file mode 100644
index 649f626a..00000000
--- a/system/settings/hardware/thinkpad.nix
+++ /dev/null
@@ -1,23 +0,0 @@
-{ config
-, pkgs
-, ...
-}: {
-  boot.kernelModules = [ ];
-  boot.blacklistedKernelModules = [ "thinkpad_ec" ];
-  boot.extraModulePackages = with config.boot.kernelPackages; [
-    acpi_call
-  ];
-
-  services.fwupd = {
-    enable = true;
-  };
-
-  services.thinkfan = {
-    enable = true;
-  };
-
-  imports = [
-    ./bare-metal.nix
-    ./laptop.nix
-  ];
-}
diff --git a/system/settings/hardware/trackball.nix b/system/settings/hardware/trackball.nix
deleted file mode 100644
index c2f7e68c..00000000
--- a/system/settings/hardware/trackball.nix
+++ /dev/null
@@ -1,15 +0,0 @@
-{ config
-, pkgs
-, ...
-}: {
-  services.xserver.config = ''
-    Section "InputClass"
-        Identifier "Trackball (No Acceleration)"
-        MatchIsPointer "yes"
-        MatchIsTouchpad "no"
-        MatchProduct "Trackball"
-        Option "AccelerationProfile" "-1"
-        Option "AccelerationScheme" "none"
-    EndSection
-  '';
-}
diff --git a/system/settings/hardware/trezor.nix b/system/settings/hardware/trezor.nix
index 1004833a..3883d76f 100644
--- a/system/settings/hardware/trezor.nix
+++ b/system/settings/hardware/trezor.nix
@@ -5,13 +5,7 @@
 }: {
   services.trezord.enable = true;
   environment.systemPackages = with pkgs; [
-    gnupg
-    pinentry
     (python3.withPackages (ps: with ps; [ trezor_agent wheel ]))
     trezor-suite
   ];
-  programs.gnupg.agent = {
-    enable = lib.mkForce false;
-    enableSSHSupport = lib.mkForce false;
-  };
 }
diff --git a/system/settings/machines/t470s.nix b/system/settings/machines/t470s.nix
deleted file mode 100644
index 5f1f4a1c..00000000
--- a/system/settings/machines/t470s.nix
+++ /dev/null
@@ -1,57 +0,0 @@
-{ config
-, pkgs
-, ...
-}: {
-  hardware.usbWwan.enable = false; # unused
-  systemd.services.ModemManager.enable = false;
-
-  hardware.enableRedistributableFirmware = true;
-
-  boot.extraModprobeConfig = ''
-    options thinkpad_acpi fan_control=1
-  '';
-
-  services.thinkfan.sensors = ''
-    hwmon /sys/devices/platform/coretemp.0/hwmon/hwmon0/temp3_input
-    hwmon /sys/devices/platform/coretemp.0/hwmon/hwmon0/temp1_input
-    hwmon /sys/devices/platform/coretemp.0/hwmon/hwmon0/temp2_input
-  '';
-  services.thinkfan.levels = ''
-    (0,     0,      48)
-    (1,     45,     52)
-    (2,     50,     57)
-    (3,     55,     63)
-    (6,     60,     65)
-    (7,     60,     85)
-    (127,   80,     32767)
-  '';
-
-  boot.postBootCommands = ''
-    echo bfq > /sys/block/nvme0n1/queue/scheduler
-  '';
-
-  hardware.pulseaudio.extraConfig = ''
-    load-module module-alsa-sink device=hw:0,7
-  '';
-
-  services.tlp.extraConfig = ''
-    DISK_DEVICES="nvme0n1"
-    DISK_IOSCHED="keep"
-  '';
-
-  services.xserver = {
-    dpi = 109;
-    monitorSection = ''
-      DisplaySize 310 176
-    '';
-  };
-
-  environment.systemPackages = with pkgs; [
-    nvme-cli
-  ];
-
-  imports = [
-    ../hardware/intel-gpu.nix
-    ../hardware/thinkpad.nix
-  ];
-}
diff --git a/system/settings/pin.nix b/system/settings/pin.nix
new file mode 100644
index 00000000..533149fe
--- /dev/null
+++ b/system/settings/pin.nix
@@ -0,0 +1,12 @@
+let
+  inherit (import ../../sources.nix) nixPath sources;
+in
+{
+  nix = {
+    inherit nixPath;
+    registry.nixpkgs.to = {
+      type = "path";
+      path = sources.nixpkgs;
+    };
+  };
+}
diff --git a/system/settings/programs/barrier.nix b/system/settings/programs/barrier.nix
deleted file mode 100644
index 76e1b06b..00000000
--- a/system/settings/programs/barrier.nix
+++ /dev/null
@@ -1,10 +0,0 @@
-{ config
-, pkgs
-, ...
-}: {
-  environment.systemPackages = with pkgs; [
-    barrier
-  ];
-
-  networking.firewall.allowedTCPPorts = [ 24800 ];
-}
diff --git a/system/settings/programs/base.nix b/system/settings/programs/base.nix
index bfc81312..47ed4c07 100644
--- a/system/settings/programs/base.nix
+++ b/system/settings/programs/base.nix
@@ -1,26 +1,11 @@
 { pkgs, ... }: {
-  services.lorri.enable = true;
   environment.systemPackages = with pkgs; [
     home-manager
+    brotli
     lzma
     lzop
     zstd
   ] ++ (lib.optionals (stdenv.isLinux) [
     psmisc
   ]);
-  nix.settings = {
-    substituters = [
-      "https://nix-community.cachix.org"
-      "https://deploy-rs.cachix.org"
-      "https://binarycache.alanpearce.eu"
-      "https://deploy-rs.cachix.org"
-    ];
-
-    trusted-public-keys = [
-      "deploy-rs.cachix.org-1:xfNobmiwF/vzvK1gpfediPwpdIP0rpDV2rYqx40zdSI="
-      "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
-      "deploy-rs.cachix.org-1:xfNobmiwF/vzvK1gpfediPwpdIP0rpDV2rYqx40zdSI="
-      "binarycache.alanpearce.eu:ZwqO3XMuajPictjwih8OY2+RXnOKpjZEZFHJjGSxAI4="
-    ];
-  };
 }
diff --git a/system/settings/programs/gnome.nix b/system/settings/programs/gnome.nix
deleted file mode 100644
index f9618009..00000000
--- a/system/settings/programs/gnome.nix
+++ /dev/null
@@ -1,26 +0,0 @@
-{ config
-, lib
-, pkgs
-, ...
-}:
-with lib; {
-  services = {
-    gnome3 = {
-      gnome-documents.enable = false;
-      gnome-user-share.enable = false;
-      gnome-online-accounts.enable = false;
-      tracker.enable = false;
-    };
-    telepathy.enable = false;
-
-    xserver = {
-      desktopManager.gnome3 = {
-        enable = true;
-        extraGSettingsOverrides = ''
-          [org.gnome.desktop.input-sources]
-          sources=[('xkb','${config.services.xserver.layout + (optionalString (config.services.xserver.xkbVariant != "") ("+" + config.services.xserver.xkbVariant))}')]
-        '';
-      };
-    };
-  };
-}
diff --git a/system/settings/programs/gnupg.nix b/system/settings/programs/gnupg.nix
deleted file mode 100644
index f17263c9..00000000
--- a/system/settings/programs/gnupg.nix
+++ /dev/null
@@ -1,12 +0,0 @@
-{ config
-, pkgs
-, lib
-, ...
-}: {
-  environment.systemPackages = with pkgs; [
-    gnupg
-    pinentry
-    (python3.withPackages (ps: with ps; [ trezor_agent wheel ]))
-  ];
-  environment.variables.GNUPGHOME = "$HOME/.gnupg/trezor/";
-}
diff --git a/system/settings/programs/kde.nix b/system/settings/programs/kde.nix
index 1a753cf2..1cf3c917 100644
--- a/system/settings/programs/kde.nix
+++ b/system/settings/programs/kde.nix
@@ -3,13 +3,16 @@
 , pkgs
 , ...
 }:
-with lib; {
+{
   services = {
     desktopManager = {
       plasma6.enable = true;
     };
     displayManager = {
-      sddm.enable = true;
+      sddm = {
+        enable = true;
+        enableHidpi = lib.mkDefault false;
+      };
     };
 
     physlock.enable = lib.mkForce false;
diff --git a/system/settings/programs/shell.nix b/system/settings/programs/shell.nix
index 87372033..680985cd 100644
--- a/system/settings/programs/shell.nix
+++ b/system/settings/programs/shell.nix
@@ -4,6 +4,7 @@
 }: {
   programs.fish = {
     enable = true;
+    useBabelfish = true;
   };
   users.users.alan.shell = pkgs.fish;
 }
diff --git a/system/settings/programs/tor.nix b/system/settings/programs/tor.nix
deleted file mode 100644
index 31521857..00000000
--- a/system/settings/programs/tor.nix
+++ /dev/null
@@ -1,27 +0,0 @@
-{ config
-, pkgs
-, lib
-, ...
-}: {
-  services.tor = {
-    enable = true;
-    client = {
-      enable = true;
-      socksListenAddress = {
-        IPv6Traffic = true;
-        port = 9050;
-      };
-    };
-    torsocks = {
-      enable = true;
-    };
-  };
-  systemd.services.tor.wantedBy = lib.mkForce [ ];
-  systemd.timers.tor = {
-    description = "Delayed startup of Tor";
-    wantedBy = [ "timers.target" ];
-    timerConfig = {
-      OnActiveSec = "1 min";
-    };
-  };
-}
diff --git a/system/settings/programs/window-manager.nix b/system/settings/programs/window-manager.nix
deleted file mode 100644
index bbe4c638..00000000
--- a/system/settings/programs/window-manager.nix
+++ /dev/null
@@ -1,57 +0,0 @@
-{ config
-, pkgs
-, lib
-, ...
-}: {
-  services.xserver = {
-    desktopManager.xterm.enable = false;
-
-    displayManager = {
-      autoLogin = {
-        user = "alan";
-        enable = false;
-      };
-      lightdm = {
-        enable = true;
-        greeter.enable = true;
-        greeters.mini = {
-          enable = false;
-          user = "alan";
-        };
-      };
-      sessionCommands = ''
-        ${pkgs.xorg.xrdb}/bin/xrdb -merge $HOME/.xresources/main
-        ${pkgs.xorg.xsetroot}/bin/xsetroot -cursor_name left_ptr -solid '#4d4d4c'
-      '' ++ (lib.optionalString config.networking.networkmanager.enable ''
-        ${pkgs.networkmanagerapplet}/bin/nm-applet &
-      '');
-    };
-    xautolock = {
-      enable = true;
-      locker = "${pkgs.i3lock}/bin/i3lock -n";
-      enableNotifier = true;
-      notifier = "${pkgs.libnotify}/bin/notify-send \"Locking in 10 seconds\"";
-      time = 5;
-    };
-  };
-
-  services.xserver.displayManager.setupCommands = ''
-    ${pkgs.redshift}/bin/redshift \
-    -l ${toString config.location.latitude}:${toString config.location.longitude} \
-    -t ${toString config.services.redshift.temperature.day}:${toString config.services.redshift.temperature.night} \
-    -b 1:1 \
-    -o \
-    -r
-  '';
-
-  environment.systemPackages = with pkgs; [
-    dmenu
-    libnotify # for notify-send
-    xterm
-    rofi
-    sxhkd
-    maim
-
-    perlPackages.FileMimeInfo # xdg-utils uses this when no DE
-  ];
-}
diff --git a/system/settings/programs/xfce.nix b/system/settings/programs/xfce.nix
deleted file mode 100644
index a896810a..00000000
--- a/system/settings/programs/xfce.nix
+++ /dev/null
@@ -1,8 +0,0 @@
-{ pkgs, ... }: {
-  services.xserver.desktopManager.xfce = {
-    enable = true;
-  };
-  environment.systemPackages = with pkgs; [
-    xfce.xfce4-panel-profiles
-  ];
-}
diff --git a/system/settings/services/git-server.nix b/system/settings/services/git-server.nix
new file mode 100644
index 00000000..0ef40ccc
--- /dev/null
+++ b/system/settings/services/git-server.nix
@@ -0,0 +1,278 @@
+{ config
+, lib
+, pkgs
+, ...
+}:
+let
+  inherit (builtins) mapAttrs attrValues;
+  inherit (lib) pipe flatten mergeAttrsList mapAttrsToList;
+  inherit (import ../../../lib/caddy.nix { inherit lib; }) security-headers;
+  repos = "${config.services.gitolite.dataDir}/repositories";
+
+  mirrors = {
+    sourcehut = {
+      hostname = "git.sr.ht";
+      username = "~alanpearce";
+    };
+    codeberg = {
+      hostname = "codeberg.org";
+      username = "alanpearce";
+    };
+    github = {
+      hostname = "github.com";
+      username = "alanpearce";
+    };
+  };
+
+  repoMirrors = {
+    nixfiles = [ "sourcehut" ];
+    searchix = [ "sourcehut" ];
+    website = [ "sourcehut" ];
+    nix-packages = [ "sourcehut" "github" ];
+    zola-bearblog = [ "sourcehut" "codeberg" ];
+  };
+
+  createMirrorService =
+    name: { hostname, username }:
+    {
+      services."mirror-to-${name}@" = {
+        path = with pkgs; [ gitMinimal openssh ];
+        serviceConfig = {
+          Type = "oneshot";
+          User = "gitolite";
+          WorkingDirectory = "${repos}/%i.git";
+          ExecStart = "${pkgs.gitMinimal}/bin/git push --mirror git@${hostname}:${username}/%i";
+        };
+        unitConfig = {
+          # only mirror public repositories
+          ConditionPathExists = "${repos}/%i.git/git-daemon-export-ok";
+        };
+      };
+      paths."mirror-to-${name}@" = {
+        pathConfig = {
+          PathChanged = "${repos}/%i.git/refs/heads";
+          StartLimitIntervalSec = "1h";
+          StartLimitBurst = 5;
+        };
+      };
+    };
+
+  mkMirrorWants = repo: map (target: "mirror-to-${target}@${repo}.path");
+in
+{
+  services.fcgiwrap = {
+    enable = true;
+    user = "gitolite";
+    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' );
+    '';
+  };
+  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"
+        ];
+      };
+    };
+  };
+  services.gitDaemon = {
+    enable = true;
+    user = "gitolite";
+    group = "gitolite";
+    basePath = repos;
+  };
+
+  services.caddy.virtualHosts = {
+    "git.alanpearce.eu" =
+      let
+        fcgi = config.services.fcgiwrap;
+        fcgisocket = "${fcgi.socketType}/${fcgi.socketAddress}";
+      in
+      {
+        useACMEHost = "alanpearce.eu";
+        extraConfig = ''
+          root * ${pkgs.cgit-pink}/cgit/
+          encode zstd gzip
+          ${security-headers {
+            overrides.content-security-policy = {
+              default-src = [ "none" ];
+              base-uri = [ "none" ];
+              style-src = [ "self" "unsafe-inline" ];
+              script-src = [ "self" "unsafe-inline" ];
+              form-action = [ "self" ];
+              connect-src = [ "self" ];
+              img-src = [ "https" ];
+              object-src = [ "none" ];
+            };
+          }}
+          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 "^.*/(HEAD|info/refs|objects/info/[^/]+|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 ${repos}
+              }
+            }
+          }
+          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=${repos}
+                ''}
+                }
+              }
+          }
+        '';
+      };
+
+    "legit.alanpearce.eu" =
+      let
+        server = config.services.legit.settings.server;
+      in
+      {
+        useACMEHost = "alanpearce.eu";
+        extraConfig = ''
+          encode zstd gzip
+          handle_path /static/* {
+            root * /srv/http/legit/src/static
+            file_server
+          }
+          ${security-headers {
+            overrides.content-security-policy = {
+              default-src = [ "none" ];
+              base-uri = [ "none" ];
+              style-src = [ "self" ];
+              script-src = [ "none" ];
+              form-action = [ "self" ];
+              connect-src = [ "self" ];
+              img-src = [ "https" ];
+              object-src = [ "none" ];
+            };
+          }}
+          reverse_proxy ${server.host}:${toString server.port}
+        '';
+      };
+  };
+
+  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
+      '')
+    ];
+  };
+
+  systemd = (pipe
+    mirrors [
+    (mapAttrsToList createMirrorService)
+    mergeAttrsList
+  ]) // {
+    targets.git-mirroring = {
+      wantedBy = [ "multi-user.target" ];
+      wants = pipe
+        repoMirrors [
+        (mapAttrsToList mkMirrorWants)
+        flatten
+      ];
+    };
+  };
+}
diff --git a/system/settings/services/virtualisation.nix b/system/settings/services/virtualisation.nix
index dbe041c7..172dfcec 100644
--- a/system/settings/services/virtualisation.nix
+++ b/system/settings/services/virtualisation.nix
@@ -12,9 +12,11 @@
       runAsRoot = false;
     };
   };
+  programs.virt-manager = {
+    enable = true;
+  };
 
   environment.systemPackages = with pkgs; [
-    virt-manager
     OVMF
   ];
 }
diff --git a/system/settings/services/xserver.nix b/system/settings/services/xserver.nix
index c5a82d48..29f181ee 100644
--- a/system/settings/services/xserver.nix
+++ b/system/settings/services/xserver.nix
@@ -20,12 +20,6 @@ with lib; {
     xorg.xdpyinfo
     xclip
     xfontsel
-
-    arc-theme
-    arc-icon-theme
-
-    gtk-engine-murrine
-    gtk_engines
   ];
 
   fonts = {
@@ -57,24 +51,15 @@ with lib; {
     };
     packages = with pkgs;
       [
-        gohufont
-        dina-font
-        terminus_font
-
         corefonts
-
         xorg.fontmiscmisc
         xorg.fontcursormisc
       ]
       ++ lib.optionals config.fonts.fontconfig.antialias [
         cantarell-fonts
 
-        fira
-        fira-code
-        fira-mono
         ibm-plex
 
-        oxygenfonts
         noto-fonts-color-emoji
 
         office-code-pro
diff --git a/system/settings/user-interface.nix b/system/settings/user-interface.nix
index 20cac135..d9d3297f 100644
--- a/system/settings/user-interface.nix
+++ b/system/settings/user-interface.nix
@@ -4,23 +4,14 @@
 , ...
 }: {
   documentation.info.enable = true;
-  nixpkgs.config.firefox.enableOfficialBranding = true;
 
   environment.systemPackages = with pkgs; [
-    aria2
-    pcmanfm
-
     epdfview
-    geeqie
 
     lxappearance
     lxrandr
     lxtask
 
-    mpv
-
-    cifs-utils
-
     trash-cli
   ];
 
@@ -59,11 +50,9 @@
     };
   };
 
-  programs.dconf.enable = true;
-
   programs.nh = {
     enable = true;
-    flake = "/home/alan/projects/alanpearce.eu/nixfiles";
+    flake = builtins.toString ../..;
     clean = {
       enable = true;
       extraArgs = "--keep-since 14d";