diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
deleted file mode 100644
index 40e5d59..0000000
--- a/.git-blame-ignore-revs
+++ /dev/null
@@ -1,14 +0,0 @@
-# Run this command to always ignore formatting commits in `git blame`
-# git config blame.ignoreRevsFile .git-blame-ignore-revs
-
-# Switch to nixfmt formatting
-04f7a7ef1d38906163afc9cddfa8ce2b0ebf3b45
-
-# Switch to nixpkgs-fmt formatting
-fd138d45e6a2cad89fead6e9f246ba282070d6b7
-
-# Switch to alejandra formatting
-046a88905ddfa7f9edc3291c310dbb985dee34f9
-
-# Apply wide linting
-63b3cbe00be80ccb4b221aad64eb657ae5c96d70
diff --git a/.gitignore b/.gitignore
index 7512d76..dbee5b9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,2 @@
 /result
 *.qcow2
-/gcroots/
diff --git a/.sops.yaml b/.sops.yaml
index bed2365..7444d2c 100644
--- a/.sops.yaml
+++ b/.sops.yaml
@@ -1,5 +1,5 @@
 keys:
-  - &tlater B9C7AE554602D82B1AC2CFD0BC7BB2DB17C78E42!
+  - &tlater 535B61015823443941C744DD12264F6BBDFABA89
   - &server_tlaternet 8a3737d48f1035fe6c3a0a8fd6a1976ca74c7f3b
   - &server_hetzner1 0af7641adb8aa843136cf6d047f71da3e5ad79f9
   - &server_staging 2f5caa73e7ceea4fcc8d2881fde587e6737d2dbc
diff --git a/checks/default.nix b/checks/default.nix
deleted file mode 100644
index f3db4ce..0000000
--- a/checks/default.nix
+++ /dev/null
@@ -1,61 +0,0 @@
-{
-  self,
-  nixpkgs,
-  deploy-rs,
-  system,
-  ...
-}:
-let
-  pkgs = nixpkgs.legacyPackages.${system};
-
-  statix' = pkgs.statix.overrideAttrs (old: {
-    patches = old.patches ++ [
-      (pkgs.fetchpatch {
-        url = "https://github.com/oppiliappan/statix/commit/925dec39bb705acbbe77178b4d658fe1b752abbb.patch";
-        hash = "sha256-0wacO6wuYJ4ufN9PGucRVJucFdFFNF+NoHYIrLXsCWs=";
-      })
-    ];
-  });
-
-  runNuCheck =
-    {
-      name,
-      packages,
-      check,
-    }:
-    pkgs.stdenvNoCC.mkDerivation {
-      inherit name;
-
-      src = nixpkgs.lib.cleanSourceWith {
-        src = self;
-        filter = nixpkgs.lib.cleanSourceFilter;
-      };
-
-      dontPatch = true;
-      dontConfigure = true;
-      dontBuild = true;
-      dontInstall = true;
-      dontFixup = true;
-      doCheck = true;
-
-      checkInputs = nixpkgs.lib.singleton pkgs.nushell ++ packages;
-
-      checkPhase = ''
-        nu ${check}
-      '';
-    };
-in
-nixpkgs.lib.recursiveUpdate {
-  lints = runNuCheck {
-    name = "lints";
-
-    packages = [
-      pkgs.deadnix
-      pkgs.nixfmt-rfc-style
-      pkgs.shellcheck
-      statix'
-    ];
-
-    check = ./lints.nu;
-  };
-} (deploy-rs.lib.${system}.deployChecks self.deploy)
diff --git a/checks/lints.nu b/checks/lints.nu
deleted file mode 100644
index ffc2047..0000000
--- a/checks/lints.nu
+++ /dev/null
@@ -1,39 +0,0 @@
-#!/usr/bin/env nu
-
-let shell_files = ls **/*.sh | get name
-let nix_files = ls **/*.nix | where name !~ "hardware-configuration.nix|_sources" | get name
-
-let linters = [
-  ([shellcheck] ++ $shell_files)
-  ([nixfmt --check --strict] ++ $nix_files)
-  ([deadnix --fail] ++ $nix_files)
-  ([statix check] ++ $nix_files)
-]
-
-mkdir $env.out
-
-def run-linter [linterArgs: list<string>] {
-  print $'Running ($linterArgs.0)...'
-
-  let exit_code = try {
-    ^$linterArgs.0 ...($linterArgs | skip 1)
-    $env.LAST_EXIT_CODE
-  } catch {|e| $e.exit_code}
-
-  [$linterArgs.0, $exit_code]
-}
-
-let results = $linters | each {|linter| run-linter $linter}
-
-print 'Linter results:'
-
-let success = $results | each {|result|
-  match $result.1 {
-    0 => {print $'(ansi green)($result.0)(ansi reset)'}
-    _ => {print $'(ansi red)($result.0)(ansi reset)'}
-  }
-
-  $result.1
-} | math sum
-
-exit $success
diff --git a/configuration/default.nix b/configuration/default.nix
index 0377e9c..556d81e 100644
--- a/configuration/default.nix
+++ b/configuration/default.nix
@@ -1,10 +1,11 @@
 {
   config,
+  pkgs,
+  lib,
   modulesPath,
   flake-inputs,
   ...
-}:
-{
+}: {
   imports = [
     flake-inputs.disko.nixosModules.disko
     flake-inputs.sops-nix.nixosModules.sops
@@ -13,40 +14,49 @@
     "${modulesPath}/profiles/minimal.nix"
     (import ../modules)
 
+    ./services/afvalcalendar.nix
+    ./services/auth.nix
     ./services/backups.nix
     ./services/battery-manager.nix
-    ./services/conduit
-    ./services/crowdsec.nix
+    ./services/conduit.nix
+    ./services/fail2ban.nix
     ./services/foundryvtt.nix
     ./services/gitea.nix
-    ./services/immich.nix
     ./services/metrics
     ./services/nextcloud.nix
     ./services/webserver.nix
     ./services/wireguard.nix
-    # ./services/starbound.nix -- Not currently used
+    ./services/starbound.nix
     ./services/postgres.nix
     ./nginx.nix
     ./sops.nix
   ];
 
-  nixpkgs.overlays = [ (_: prev: { local = import ../pkgs { pkgs = prev; }; }) ];
+  nixpkgs.overlays = [
+    (final: prev: {
+      local = import ../pkgs {
+        pkgs = prev;
+        lib = prev.lib;
+      };
+    })
+  ];
 
   nix = {
+    package = pkgs.nixFlakes;
     extraOptions = ''
       experimental-features = nix-command flakes
     '';
 
     # Enable remote builds from tlater
-    settings.trusted-users = [ "@wheel" ];
+    settings.trusted-users = ["@wheel"];
   };
 
+  nixpkgs.config.allowUnfreePredicate = pkg:
+    builtins.elem (lib.getName pkg) ["steam-original" "steam-runtime" "steam-run" "steamcmd"];
+
   # Optimization for minecraft servers, see:
   # https://bugs.mojang.com/browse/MC-183518
-  boot.kernelParams = [
-    "highres=off"
-    "nohz=off"
-  ];
+  boot.kernelParams = ["highres=off" "nohz=off"];
 
   networking = {
     usePredictableInterfaceNames = false;
@@ -97,15 +107,15 @@
 
   users.users.tlater = {
     isNormalUser = true;
-    extraGroups = [ "wheel" ];
-    openssh.authorizedKeys.keyFiles = [ ../keys/tlater.pub ];
+    extraGroups = ["wheel"];
+    openssh.authorizedKeys.keyFiles = [../keys/tlater.pub];
   };
 
   services = {
     openssh = {
       enable = true;
       allowSFTP = false;
-      ports = [ 2222 ];
+      ports = [2222];
       startWhenNeeded = true;
 
       settings = {
@@ -124,14 +134,14 @@
     pam = {
       sshAgentAuth = {
         enable = true;
-        authorizedKeysFiles = [ "/etc/ssh/authorized_keys.d/%u" ];
+        authorizedKeysFiles = ["/etc/ssh/authorized_keys.d/%u"];
       };
       services.sudo.sshAgentAuth = true;
     };
   };
 
   # Remove some unneeded packages
-  environment.defaultPackages = [ ];
+  environment.defaultPackages = [];
 
   system.stateVersion = "20.09";
 }
diff --git a/configuration/hardware-specific/hetzner/default.nix b/configuration/hardware-specific/hetzner/default.nix
index 3e35eb0..5ecf63a 100644
--- a/configuration/hardware-specific/hetzner/default.nix
+++ b/configuration/hardware-specific/hetzner/default.nix
@@ -8,7 +8,7 @@
   # disables it by default.
   #
   # TODO(tlater): See if would be useful for anything?
-  boot.kernelParams = [ "nosgx" ];
+  boot.kernelParams = ["nosgx"];
 
   networking.hostName = "hetzner-1";
   services.nginx.domain = "tlater.net";
@@ -19,11 +19,15 @@
     addresses = [
       # IPv4
       {
-        Address = "116.202.158.55/32";
-        Peer = "116.202.158.1/32"; # Gateway
+        addressConfig = {
+          Address = "116.202.158.55/32";
+          Peer = "116.202.158.1/32"; # Gateway
+        };
       }
       # IPv6
-      { Address = "2a01:4f8:10b:3c85::2/64"; }
+      {
+        addressConfig.Address = "2a01:4f8:10b:3c85::2/64";
+      }
     ];
 
     networkConfig = {
diff --git a/configuration/hardware-specific/hetzner/disko.nix b/configuration/hardware-specific/hetzner/disko.nix
index 7e1acd7..e404688 100644
--- a/configuration/hardware-specific/hetzner/disko.nix
+++ b/configuration/hardware-specific/hetzner/disko.nix
@@ -1,106 +1,82 @@
 {
-  disko.devices.disk =
-    let
-      bootPartition = {
-        size = "1M";
-        type = "EF02";
+  disko.devices.disk = let
+    bootPartition = {
+      size = "1M";
+      type = "EF02";
+    };
+
+    swapPartition = {
+      # 8G is apparently recommended for this much RAM, but we set up
+      # 4G on both disks for mirroring purposes.
+      #
+      # That'll still be 8G during normal operation, and it's probably
+      # not too bad to have slightly less swap if a disk dies.
+      size = "4G";
+      content = {
+        type = "swap";
+        randomEncryption = true;
       };
+    };
 
-      swapPartition = {
-        # 8G is apparently recommended for this much RAM, but we set up
-        # 4G on both disks for mirroring purposes.
-        #
-        # That'll still be 8G during normal operation, and it's probably
-        # not too bad to have slightly less swap if a disk dies.
-        size = "4G";
-        content = {
-          type = "swap";
-          randomEncryption = true;
-        };
-      };
+    mountOptions = ["compress=zstd" "noatime"];
+  in {
+    sda = {
+      type = "disk";
+      device = "/dev/sda";
+      content = {
+        type = "gpt";
+        partitions = {
+          boot = bootPartition;
+          swap = swapPartition;
 
-      mountOptions = [
-        "compress=zstd"
-        "noatime"
-      ];
-    in
-    {
-      sda = {
-        type = "disk";
-        device = "/dev/sda";
-        content = {
-          type = "gpt";
-          partitions = {
-            boot = bootPartition;
-            swap = swapPartition;
-
-            disk1 = {
-              size = "100%";
-              # Empty partition to combine in RAID0 with the other disk
-            };
+          disk1 = {
+            size = "100%";
+            # Empty partition to combine in RAID0 with the other disk
           };
         };
       };
+    };
 
-      sdb = {
-        type = "disk";
-        device = "/dev/sdb";
-        content = {
-          type = "gpt";
-          partitions = {
-            boot = bootPartition;
-            swap = swapPartition;
+    sdb = {
+      type = "disk";
+      device = "/dev/sdb";
+      content = {
+        type = "gpt";
+        partitions = {
+          boot = bootPartition;
+          swap = swapPartition;
 
-            disk2 = {
-              size = "100%";
-              content = {
-                type = "btrfs";
-                # Hack to get multi-device btrfs going
-                # See https://github.com/nix-community/disko/issues/99
-                extraArgs = [
-                  "-d"
-                  "raid1"
-                  "-m"
-                  "raid1"
-                  "--runtime-features"
-                  "quota"
-                  "/dev/sda3"
-                ];
-                subvolumes = {
-                  "/volume" = { };
-                  "/volume/root" = {
-                    inherit mountOptions;
-                    mountpoint = "/";
-                  };
-                  "/volume/home" = {
-                    inherit mountOptions;
-                    mountpoint = "/home";
-                  };
-                  "/volume/var" = {
-                    inherit mountOptions;
-                    mountpoint = "/var";
-                  };
-                  "/volume/var/lib/private/matrix-conduit" = {
-                    mountOptions = [
-                      # Explicitly don't compress here, since
-                      # conduwuit's database does compression by
-                      # itself, and relies on being able to read the
-                      # raw file data from disk (which is impossible
-                      # if btrfs compresses it)
-                      "noatime"
-                    ];
-                    mountpoint = "/var/lib/private/matrix-conduit";
-                  };
-                  "/volume/nix-store" = {
-                    inherit mountOptions;
-                    mountpoint = "/nix";
-                  };
-                  "/snapshots" = { };
+          disk2 = {
+            size = "100%";
+            content = {
+              type = "btrfs";
+              # Hack to get multi-device btrfs going
+              # See https://github.com/nix-community/disko/issues/99
+              extraArgs = ["-d" "raid1" "-m" "raid1" "--runtime-features" "quota" "/dev/sda3"];
+              subvolumes = {
+                "/volume" = {};
+                "/volume/root" = {
+                  inherit mountOptions;
+                  mountpoint = "/";
                 };
+                "/volume/home" = {
+                  inherit mountOptions;
+                  mountpoint = "/home";
+                };
+                "/volume/var" = {
+                  inherit mountOptions;
+                  mountpoint = "/var";
+                };
+                "/volume/nix-store" = {
+                  inherit mountOptions;
+                  mountpoint = "/nix";
+                };
+                "/snapshots" = {};
               };
             };
           };
         };
       };
     };
+  };
 }
diff --git a/configuration/hardware-specific/vm.nix b/configuration/hardware-specific/vm.nix
index 71870fb..7f1fcf1 100644
--- a/configuration/hardware-specific/vm.nix
+++ b/configuration/hardware-specific/vm.nix
@@ -1,35 +1,19 @@
-{ lib, ... }:
-{
+{lib, ...}: {
   users.users.tlater.password = "insecure";
 
   # Disable graphical tty so -curses works
-  boot.kernelParams = [ "nomodeset" ];
+  boot.kernelParams = ["nomodeset"];
 
   networking.hostName = "testvm";
-
-  services = {
-    # Sets the base domain for nginx to a local domain so that we can
-    # easily test locally with the VM.
-    nginx.domain = "dev.local";
-
-    # Don't run this
-    batteryManager.enable = lib.mkForce false;
-
-    openssh.hostKeys = lib.mkForce [
-      {
-        type = "rsa";
-        bits = 4096;
-        path = "/etc/staging.key";
-      }
-    ];
-  };
+  # Sets the base domain for nginx to a local domain so that we can
+  # easily test locally with the VM.
+  services.nginx.domain = "dev.local";
 
   # Use the staging secrets
   sops.defaultSopsFile = lib.mkOverride 99 ../../keys/staging.yaml;
 
   systemd.network.networks."10-eth0" = {
     matchConfig.Name = "eth0";
-    gateway = [ "192.168.9.1" ];
     networkConfig = {
       Address = "192.168.9.2/24";
     };
@@ -43,6 +27,14 @@
     source = ../../keys/hosts/staging.key;
   };
 
+  services.openssh.hostKeys = lib.mkForce [
+    {
+      type = "rsa";
+      bits = 4096;
+      path = "/etc/staging.key";
+    }
+  ];
+
   virtualisation.vmVariant = {
     virtualisation = {
       memorySize = 3941;
diff --git a/configuration/nginx.nix b/configuration/nginx.nix
index 3abef7f..82baab0 100644
--- a/configuration/nginx.nix
+++ b/configuration/nginx.nix
@@ -1,78 +1,67 @@
-{ config, lib, ... }:
 {
-  services = {
-    nginx = {
-      enable = true;
-      recommendedTlsSettings = true;
-      recommendedOptimisation = true;
-      recommendedGzipSettings = true;
-      recommendedProxySettings = true;
-      clientMaxBodySize = "10G";
+  config,
+  lib,
+  ...
+}: {
+  services.nginx = {
+    enable = true;
+    recommendedTlsSettings = true;
+    recommendedOptimisation = true;
+    recommendedGzipSettings = true;
+    recommendedProxySettings = true;
+    clientMaxBodySize = "10G";
 
-      statusPage = true; # For metrics, should be accessible only from localhost
+    statusPage = true; # For metrics, should be accessible only from localhost
 
-      commonHttpConfig = ''
-        log_format upstream_time '$remote_addr - $remote_user [$time_local] '
-                           '"$request" $status $body_bytes_sent '
-                           '"$http_referer" "$http_user_agent" '
-                           'rt=$request_time uct="$upstream_connect_time" '
-                           'uht="$upstream_header_time" urt="$upstream_response_time"';
-      '';
-    };
-
-    logrotate.settings =
-      {
-        # Override the default, just keep fewer logs
-        nginx.rotate = 6;
-      }
-      // lib.mapAttrs' (
-        virtualHost: _:
-        lib.nameValuePair "/var/log/nginx/${virtualHost}/access.log" {
-          frequency = "daily";
-          rotate = 2;
-          compress = true;
-          delaycompress = true;
-          su = "${config.services.nginx.user} ${config.services.nginx.group}";
-          postrotate = "[ ! -f /var/run/nginx/nginx.pid ] || kill -USR1 `cat /var/run/nginx/nginx.pid`";
-        }
-      ) config.services.nginx.virtualHosts;
-
-    backups.acme = {
-      user = "acme";
-      paths = lib.mapAttrsToList (
-        virtualHost: _: "/var/lib/acme/${virtualHost}"
-      ) config.services.nginx.virtualHosts;
-    };
+    commonHttpConfig = ''
+      log_format upstream_time '$remote_addr - $remote_user [$time_local] '
+                         '"$request" $status $body_bytes_sent '
+                         '"$http_referer" "$http_user_agent" '
+                         'rt=$request_time uct="$upstream_connect_time" '
+                         'uht="$upstream_header_time" urt="$upstream_response_time"';
+    '';
   };
 
-  systemd.tmpfiles.rules = lib.mapAttrsToList (
-    virtualHost: _:
-    #
-    "d /var/log/nginx/${virtualHost} 0750 ${config.services.nginx.user} ${config.services.nginx.group}"
-  ) config.services.nginx.virtualHosts;
+  services.logrotate.settings =
+    {
+      # Override the default, just keep fewer logs
+      nginx.rotate = 6;
+    }
+    // lib.mapAttrs' (virtualHost: _:
+      lib.nameValuePair "/var/log/nginx/${virtualHost}/access.log" {
+        frequency = "daily";
+        rotate = 2;
+        compress = true;
+        delaycompress = true;
+        su = "${config.services.nginx.user} ${config.services.nginx.group}";
+        postrotate = "[ ! -f /var/run/nginx/nginx.pid ] || kill -USR1 `cat /var/run/nginx/nginx.pid`";
+      })
+    config.services.nginx.virtualHosts;
+
+  systemd.tmpfiles.rules =
+    lib.mapAttrsToList (
+      virtualHost: _:
+      #
+      "d /var/log/nginx/${virtualHost} 0750 ${config.services.nginx.user} ${config.services.nginx.group}"
+    )
+    config.services.nginx.virtualHosts;
 
   security.acme = {
     defaults.email = "tm@tlater.net";
     acceptTerms = true;
 
     certs."tlater.net" = {
-      extraDomainNames = [
-        "*.tlater.net"
-        "tlater.com"
-        "*.tlater.com"
-      ];
-      dnsProvider = "porkbun";
-      group = "ssl-cert";
-      credentialFiles = {
-        PORKBUN_API_KEY_FILE = config.sops.secrets."porkbun/api-key".path;
-        PORKBUN_SECRET_API_KEY_FILE = config.sops.secrets."porkbun/secret-api-key".path;
-      };
+      extraDomainNames = ["*.tlater.net"];
+      dnsProvider = "hetzner";
+      group = "nginx";
+      credentialFiles."HETZNER_API_KEY_FILE" = config.sops.secrets."hetzner-api".path;
     };
   };
 
-  users.groups.ssl-cert = { };
-
-  systemd.services.nginx.serviceConfig.SupplementaryGroups = [
-    config.security.acme.certs."tlater.net".group
-  ];
+  services.backups.acme = {
+    user = "acme";
+    paths =
+      lib.mapAttrsToList (virtualHost: _: "/var/lib/acme/${virtualHost}")
+      config.services.nginx.virtualHosts;
+  };
 }
diff --git a/configuration/services/afvalcalendar.nix b/configuration/services/afvalcalendar.nix
new file mode 100644
index 0000000..e27ba62
--- /dev/null
+++ b/configuration/services/afvalcalendar.nix
@@ -0,0 +1,67 @@
+{
+  pkgs,
+  config,
+  ...
+}: {
+  systemd.services.afvalcalendar = {
+    description = "Enschede afvalcalendar -> ical converter";
+    wantedBy = ["multi-user.target"];
+    after = ["network.target"];
+
+    script = ''
+      ${pkgs.local.afvalcalendar}/bin/afvalcalendar > /srv/afvalcalendar/afvalcalendar.ical
+    '';
+
+    startAt = "daily";
+
+    serviceConfig = {
+      DynamicUser = true;
+      ProtectHome = true; # Override the default (read-only)
+      PrivateDevices = true;
+      PrivateIPC = true;
+      PrivateUsers = true;
+      ProtectHostname = true;
+      ProtectClock = true;
+      ProtectKernelTunables = true;
+      ProtectKernelModules = true;
+      ProtectKernelLogs = true;
+      ProtectControlGroups = true;
+      RestrictAddressFamilies = ["AF_UNIX" "AF_INET" "AF_INET6"];
+      RestrictNamespaces = true;
+      LockPersonality = true;
+      MemoryDenyWriteExecute = true;
+      RestrictRealtime = true;
+      RestrictSUIDSGID = true;
+      SystemCallArchitectures = "native";
+      SystemCallFilter = ["@system-service" "~@privileged @resources @setuid @keyring"];
+
+      Umask = 0002;
+      SupplementaryGroups = "afvalcalendar-hosting";
+
+      ReadWritePaths = "/srv/afvalcalendar";
+    };
+  };
+
+  services.nginx.virtualHosts."afvalcalendar.${config.services.nginx.domain}" = {
+    forceSSL = true;
+    useACMEHost = "tlater.net";
+    enableHSTS = true;
+
+    root = "/srv/afvalcalendar";
+  };
+
+  users.groups.afvalcalendar-hosting = {};
+  systemd.tmpfiles.settings."10-afvalcalendar" = {
+    "/srv/afvalcalendar".d = {
+      user = "nginx";
+      group = "afvalcalendar-hosting";
+      mode = "0775";
+    };
+
+    "/srv/afvalcalendar/afvalcalendar.ical".f = {
+      user = "nginx";
+      group = "afvalcalendar-hosting";
+      mode = "0775";
+    };
+  };
+}
diff --git a/configuration/services/auth.nix b/configuration/services/auth.nix
new file mode 100644
index 0000000..cd78111
--- /dev/null
+++ b/configuration/services/auth.nix
@@ -0,0 +1,95 @@
+{
+  pkgs,
+  config,
+  ...
+}: let
+  user = config.services.authelia.instances.main.user;
+  domain = "auth.${config.services.nginx.domain}";
+in {
+  services.authelia.instances.main = {
+    enable = true;
+    settings = {
+      theme = "auto";
+
+      access_control.default_policy = "one_factor";
+
+      authentication_backend = {
+        password_reset.disable = true;
+        file.path = "/var/lib/authelia-main/users.yml";
+      };
+
+      notifier.filesystem.filename = "/var/lib/authelia-main/notification.txt";
+
+      session = {
+        domain = config.services.nginx.domain;
+        redis.host = config.services.redis.servers.authelia.unixSocket;
+      };
+
+      storage.postgres = {
+        host = "/run/postgresql";
+        port = 5432;
+        database = user;
+        username = user;
+
+        password = "unnecessary";
+      };
+    };
+
+    secrets = {
+      storageEncryptionKeyFile = config.sops.secrets."authelia/storageEncryptionKey".path; # Database
+      sessionSecretFile = config.sops.secrets."authelia/sessionSecret".path; # Redis
+      jwtSecretFile = config.sops.secrets."authelia/jwtSecret".path;
+    };
+  };
+
+  systemd.services.authelia-main.after = ["postgresql.service"];
+
+  services.nginx = {
+    # TODO(tlater): Possibly remove on next authelia release
+    additionalModules = with pkgs.nginxModules; [
+      develkit
+      set-misc
+    ];
+
+    virtualHosts."${domain}" = {
+      forceSSL = true;
+      enableACME = true;
+      enableHSTS = true;
+
+      locations = {
+        "/" = {
+          proxyPass = "http://127.0.0.1:9091";
+          recommendedProxySettings = false;
+          enableAutheliaProxy = true;
+        };
+
+        "/api/verify" = {
+          proxyPass = "http://127.0.0.1:9091";
+          recommendedProxySettings = false;
+        };
+      };
+    };
+  };
+
+  services.redis.servers.authelia = {
+    inherit user;
+    enable = true;
+  };
+
+  sops.secrets = {
+    "authelia/storageEncryptionKey" = {
+      owner = user;
+      group = user;
+    };
+
+    "authelia/sessionSecret" = {
+      owner = user;
+      group = user;
+    };
+
+    "authelia/jwtSecret" = {
+      owner = user;
+      group = user;
+    };
+  };
+}
diff --git a/configuration/services/backups.nix b/configuration/services/backups.nix
index baa61e3..98aa473 100644
--- a/configuration/services/backups.nix
+++ b/configuration/services/backups.nix
@@ -3,33 +3,27 @@
   pkgs,
   lib,
   ...
-}:
-let
+}: let
   inherit (lib) types optional singleton;
-  mkShutdownScript =
-    service:
+  mkShutdownScript = service:
     pkgs.writeShellScript "backup-${service}-shutdown" ''
       if systemctl is-active --quiet '${service}'; then
         touch '/tmp/${service}-was-active'
         systemctl stop '${service}'
       fi
     '';
-  mkRestartScript =
-    service:
+  mkRestartScript = service:
     pkgs.writeShellScript "backup-${service}-restart" ''
       if [ -f '/tmp/${service}-was-active' ]; then
         rm '/tmp/${service}-was-active'
         systemctl start '${service}'
       fi
     '';
-  writeScript =
-    name: packages: text:
-    lib.getExe (
-      pkgs.writeShellApplication {
-        inherit name text;
-        runtimeInputs = packages;
-      }
-    );
+  writeScript = name: packages: text:
+    lib.getExe (pkgs.writeShellApplication {
+      inherit name text;
+      runtimeInputs = packages;
+    });
 
   # *NOT* a TOML file, for some reason quotes are interpreted
   # *literally
@@ -48,98 +42,96 @@ let
     RESTIC_REPOSITORY = "rclone:storagebox:backups";
     RCLONE_CONFIG = rcloneConfig;
   };
-in
-{
+in {
   options = {
     services.backups = lib.mkOption {
       description = lib.mdDoc ''
         Configure restic backups with a specific tag.
       '';
-      type = types.attrsOf (
-        types.submodule (
-          { name, ... }:
-          {
-            options = {
-              user = lib.mkOption {
-                type = types.str;
-                description = ''
-                  The user as which to run the backup.
-                '';
-              };
-              paths = lib.mkOption {
-                type = types.listOf types.str;
-                description = ''
-                  The paths to back up.
-                '';
-              };
-              tag = lib.mkOption {
-                type = types.str;
-                description = ''
-                  The restic tag to mark the backup with.
-                '';
-                default = name;
-              };
-              preparation = {
-                packages = lib.mkOption {
-                  type = types.listOf types.package;
-                  default = [ ];
-                  description = ''
-                    The list of packages to make available in the
-                    preparation script.
-                  '';
-                };
-                text = lib.mkOption {
-                  type = types.nullOr types.str;
-                  default = null;
-                  description = ''
-                    The preparation script to run before the backup.
-
-                    This should include things like database dumps and
-                    enabling maintenance modes. If a service needs to be
-                    shut down for backups, use `pauseServices` instead.
-                  '';
-                };
-              };
-              cleanup = {
-                packages = lib.mkOption {
-                  type = types.listOf types.package;
-                  default = [ ];
-                  description = ''
-                    The list of packages to make available in the
-                    cleanup script.
-                  '';
-                };
-                text = lib.mkOption {
-                  type = types.nullOr types.str;
-                  default = null;
-                  description = ''
-                    The cleanup script to run after the backup.
-
-                    This should do things like cleaning up database dumps
-                    and disabling maintenance modes.
-                  '';
-                };
-              };
-              pauseServices = lib.mkOption {
-                type = types.listOf types.str;
-                default = [ ];
-                description = ''
-                  The systemd services that need to be shut down before
-                  the backup can run. Services will be restarted after the
-                  backup is complete.
-
-                  This is intended to be used for services that do not
-                  support hot backups.
-                '';
-              };
+      type = types.attrsOf (types.submodule ({
+        config,
+        name,
+        ...
+      }: {
+        options = {
+          user = lib.mkOption {
+            type = types.str;
+            description = ''
+              The user as which to run the backup.
+            '';
+          };
+          paths = lib.mkOption {
+            type = types.listOf types.str;
+            description = ''
+              The paths to back up.
+            '';
+          };
+          tag = lib.mkOption {
+            type = types.str;
+            description = ''
+              The restic tag to mark the backup with.
+            '';
+            default = name;
+          };
+          preparation = {
+            packages = lib.mkOption {
+              type = types.listOf types.package;
+              default = [];
+              description = ''
+                The list of packages to make available in the
+                preparation script.
+              '';
             };
-          }
-        )
-      );
+            text = lib.mkOption {
+              type = types.nullOr types.str;
+              default = null;
+              description = ''
+                The preparation script to run before the backup.
+
+                This should include things like database dumps and
+                enabling maintenance modes. If a service needs to be
+                shut down for backups, use `pauseServices` instead.
+              '';
+            };
+          };
+          cleanup = {
+            packages = lib.mkOption {
+              type = types.listOf types.package;
+              default = [];
+              description = ''
+                The list of packages to make available in the
+                cleanup script.
+              '';
+            };
+            text = lib.mkOption {
+              type = types.nullOr types.str;
+              default = null;
+              description = ''
+                The cleanup script to run after the backup.
+
+                This should do things like cleaning up database dumps
+                and disabling maintenance modes.
+              '';
+            };
+          };
+          pauseServices = lib.mkOption {
+            type = types.listOf types.str;
+            default = [];
+            description = ''
+              The systemd services that need to be shut down before
+              the backup can run. Services will be restarted after the
+              backup is complete.
+
+              This is intended to be used for services that do not
+              support hot backups.
+            '';
+          };
+        };
+      }));
     };
   };
 
-  config = lib.mkIf (config.services.backups != { }) {
+  config = lib.mkIf (config.services.backups != {}) {
     systemd.services =
       {
         restic-prune = {
@@ -172,15 +164,16 @@ in
           };
         };
       }
-      // lib.mapAttrs' (
-        name: backup:
+      // lib.mapAttrs' (name: backup:
         lib.nameValuePair "backup-${name}" {
           # Don't want to restart mid-backup
           restartIfChanged = false;
 
-          environment = resticEnv // {
-            RESTIC_CACHE_DIR = "%C/backup-${name}";
-          };
+          environment =
+            resticEnv
+            // {
+              RESTIC_CACHE_DIR = "%C/backup-${name}";
+            };
 
           path = with pkgs; [
             coreutils
@@ -203,60 +196,47 @@ in
             PrivateTmp = true;
 
             ExecStart = [
-              (lib.concatStringsSep " " (
-                [
-                  "${pkgs.restic}/bin/restic"
-                  "backup"
-                  "--tag"
-                  name
-                ]
-                ++ backup.paths
-              ))
+              (lib.concatStringsSep " " (["${pkgs.restic}/bin/restic" "backup" "--tag" name] ++ backup.paths))
             ];
 
             ExecStartPre =
               map (service: "+${mkShutdownScript service}") backup.pauseServices
-              ++ singleton (
-                writeScript "backup-${name}-repo-init" [ ] ''
-                  restic snapshots || restic init
-                ''
-              )
-              ++ optional (backup.preparation.text != null) (
-                writeScript "backup-${name}-prepare" backup.preparation.packages backup.preparation.text
-              );
+              ++ singleton (writeScript "backup-${name}-repo-init" [] ''
+                restic snapshots || restic init
+              '')
+              ++ optional (backup.preparation.text != null)
+              (writeScript "backup-${name}-prepare" backup.preparation.packages backup.preparation.text);
 
             # TODO(tlater): Add repo pruning/checking
             ExecStopPost =
               map (service: "+${mkRestartScript service}") backup.pauseServices
-              ++ optional (backup.cleanup.text != null) (
-                writeScript "backup-${name}-cleanup" backup.cleanup.packages backup.cleanup.text
-              );
+              ++ optional (backup.cleanup.text != null)
+              (writeScript "backup-${name}-cleanup" backup.cleanup.packages backup.cleanup.text);
           };
-        }
-      ) config.services.backups;
+        })
+      config.services.backups;
 
     systemd.timers =
       {
         restic-prune = {
-          wantedBy = [ "timers.target" ];
+          wantedBy = ["timers.target"];
           timerConfig.OnCalendar = "Thursday 03:00:00 UTC";
           # Don't make this persistent, in case the server was offline
           # for a while. This job cannot run at the same time as any
           # of the backup jobs.
         };
       }
-      // lib.mapAttrs' (
-        name: _:
+      // lib.mapAttrs' (name: backup:
         lib.nameValuePair "backup-${name}" {
-          wantedBy = [ "timers.target" ];
+          wantedBy = ["timers.target"];
           timerConfig = {
             OnCalendar = "Wednesday 02:30:00 UTC";
             RandomizedDelaySec = "1h";
             FixedRandomDelay = true;
             Persistent = true;
           };
-        }
-      ) config.services.backups;
+        })
+      config.services.backups;
 
     users = {
       # This user is only used to own the ssh key, because apparently
@@ -265,7 +245,7 @@ in
         group = "backup";
         isSystemUser = true;
       };
-      groups.backup = { };
+      groups.backup = {};
     };
   };
 }
diff --git a/configuration/services/battery-manager.nix b/configuration/services/battery-manager.nix
index 9da7e32..7f27931 100644
--- a/configuration/services/battery-manager.nix
+++ b/configuration/services/battery-manager.nix
@@ -1,16 +1,17 @@
-{ config, flake-inputs, ... }:
 {
-  imports = [ flake-inputs.sonnenshift.nixosModules.default ];
+  config,
+  flake-inputs,
+  ...
+}: {
+  imports = [
+    flake-inputs.sonnenshift.nixosModules.default
+  ];
 
   services.batteryManager = {
     enable = true;
+    battery = "3ca39300-c523-4315-b9a3-d030f85a9373";
 
     emailFile = "${config.sops.secrets."battery-manager/email".path}";
     passwordFile = "${config.sops.secrets."battery-manager/password".path}";
-
-    settings = {
-      battery_id = "3ca39300-c523-4315-b9a3-d030f85a9373";
-      log_level = "DEBUG";
-    };
   };
 }
diff --git a/configuration/services/conduit.nix b/configuration/services/conduit.nix
new file mode 100644
index 0000000..2462d9b
--- /dev/null
+++ b/configuration/services/conduit.nix
@@ -0,0 +1,254 @@
+{
+  pkgs,
+  config,
+  lib,
+  ...
+}: let
+  inherit (lib.strings) concatMapStringsSep;
+
+  cfg = config.services.matrix-conduit;
+  domain = "matrix.${config.services.nginx.domain}";
+  turn-realm = "turn.${config.services.nginx.domain}";
+in {
+  services.matrix-conduit = {
+    enable = true;
+    settings.global = {
+      address = "127.0.0.1";
+      server_name = domain;
+      database_backend = "rocksdb";
+
+      turn_uris = let
+        address = "${config.services.coturn.realm}:${toString config.services.coturn.listening-port}";
+        tls-address = "${config.services.coturn.realm}:${toString config.services.coturn.tls-listening-port}";
+      in [
+        "turn:${address}?transport=udp"
+        "turn:${address}?transport=tcp"
+        "turns:${tls-address}?transport=udp"
+        "turns:${tls-address}?transport=tcp"
+      ];
+    };
+  };
+
+  systemd.services.heisenbridge = let
+    replaceSecretBin = "${pkgs.replace-secret}/bin/replace-secret";
+    registrationFile = builtins.toFile "heisenbridge-registration.yaml" (builtins.toJSON {
+      id = "heisenbridge";
+      url = "http://127.0.0.1:9898";
+      as_token = "@AS_TOKEN@";
+      hs_token = "@HS_TOKEN@";
+      rate_limited = false;
+      sender_localpart = "heisenbridge";
+      namespaces = {
+        users = [
+          {
+            regex = "@irc_.*";
+            exclusive = true;
+          }
+          {
+            regex = "@heisenbridge:.*";
+            exclusive = true;
+          }
+        ];
+        aliases = [];
+        rooms = [];
+      };
+    });
+
+    # TODO(tlater): Starting with systemd 253 it will become possible
+    # to do the credential setup as part of ExecStartPre/preStart
+    # instead.
+    #
+    # This will also make it possible to actually set caps on the
+    # heisenbridge process using systemd, so that we can run the
+    # identd process.
+    execScript = pkgs.writeShellScript "heisenbridge" ''
+      cp ${registrationFile} "$RUNTIME_DIRECTORY/heisenbridge-registration.yaml"
+      chmod 600 $RUNTIME_DIRECTORY/heisenbridge-registration.yaml
+      ${replaceSecretBin} '@AS_TOKEN@' "$CREDENTIALS_DIRECTORY/heisenbridge_as-token" "$RUNTIME_DIRECTORY/heisenbridge-registration.yaml"
+      ${replaceSecretBin} '@HS_TOKEN@' "$CREDENTIALS_DIRECTORY/heisenbridge_hs-token" "$RUNTIME_DIRECTORY/heisenbridge-registration.yaml"
+      chmod 400 $RUNTIME_DIRECTORY/heisenbridge-registration.yaml
+
+      ${pkgs.heisenbridge}/bin/heisenbridge \
+          --config $RUNTIME_DIRECTORY/heisenbridge-registration.yaml \
+          --owner @tlater:matrix.tlater.net \
+          'http://localhost:${toString cfg.settings.global.port}'
+    '';
+  in {
+    description = "Matrix<->IRC bridge";
+    wantedBy = ["multi-user.target"];
+    after = ["conduit.service"];
+
+    serviceConfig = {
+      Type = "simple";
+
+      LoadCredential = "heisenbridge:/run/secrets/heisenbridge";
+
+      ExecStart = execScript;
+
+      DynamicUser = true;
+      RuntimeDirectory = "heisenbridge";
+      RuntimeDirectoryMode = "0700";
+
+      RestrictNamespaces = true;
+      PrivateUsers = true;
+      ProtectHostname = true;
+      ProtectClock = true;
+      ProtectKernelTunables = true;
+      ProtectKernelModules = true;
+      ProtectKernelLogs = true;
+      ProtectControlGroups = true;
+      RestrictAddressFamilies = ["AF_INET AF_INET6"];
+      LockPersonality = true;
+      RestrictRealtime = true;
+      ProtectProc = "invisible";
+      ProcSubset = "pid";
+      UMask = 0077;
+
+      # For the identd port
+      # CapabilityBoundingSet = ["CAP_NET_BIND_SERVICE"];
+      # AmbientCapabilities = ["CAP_NET_BIND_SERVICE"];
+    };
+  };
+
+  # Pass in the TURN secret via EnvironmentFile, not supported by
+  # upstream module currently.
+  #
+  # See also https://gitlab.com/famedly/conduit/-/issues/314
+  systemd.services.conduit.serviceConfig.EnvironmentFile = config.sops.secrets."turn/env".path;
+
+  services.coturn = {
+    enable = true;
+    no-cli = true;
+    use-auth-secret = true;
+    static-auth-secret-file = config.sops.secrets."turn/secret".path;
+    realm = turn-realm;
+    relay-ips = [
+      "116.202.158.55"
+    ];
+
+    # SSL config
+    #
+    # TODO(tlater): Switch to letsencrypt once google fix:
+    #  https://github.com/vector-im/element-android/issues/1533
+    pkey = config.sops.secrets."turn/ssl-key".path;
+    cert = config.sops.secrets."turn/ssl-cert".path;
+
+    # Based on suggestions from
+    # https://github.com/matrix-org/synapse/blob/develop/docs/turn-howto.md
+    # and
+    # https://www.foxypossibilities.com/2018/05/19/setting-up-a-turn-sever-for-matrix-on-nixos/
+    no-tcp-relay = true;
+    secure-stun = true;
+    extraConfig = ''
+      # Deny various local IP ranges, see
+      # https://www.rtcsec.com/article/cve-2020-26262-bypass-of-coturns-access-control-protection/
+      no-multicast-peers
+      denied-peer-ip=0.0.0.0-0.255.255.255
+      denied-peer-ip=10.0.0.0-10.255.255.255
+      denied-peer-ip=100.64.0.0-100.127.255.255
+      denied-peer-ip=127.0.0.0-127.255.255.255
+      denied-peer-ip=169.254.0.0-169.254.255.255
+      denied-peer-ip=172.16.0.0-172.31.255.255
+      denied-peer-ip=192.0.0.0-192.0.0.255
+      denied-peer-ip=192.0.2.0-192.0.2.255
+      denied-peer-ip=192.88.99.0-192.88.99.255
+      denied-peer-ip=192.168.0.0-192.168.255.255
+      denied-peer-ip=198.18.0.0-198.19.255.255
+      denied-peer-ip=198.51.100.0-198.51.100.255
+      denied-peer-ip=203.0.113.0-203.0.113.255
+      denied-peer-ip=240.0.0.0-255.255.255.255 denied-peer-ip=::1
+      denied-peer-ip=64:ff9b::-64:ff9b::ffff:ffff
+      denied-peer-ip=::ffff:0.0.0.0-::ffff:255.255.255.255
+      denied-peer-ip=100::-100::ffff:ffff:ffff:ffff
+      denied-peer-ip=2001::-2001:1ff:ffff:ffff:ffff:ffff:ffff:ffff
+      denied-peer-ip=2002::-2002:ffff:ffff:ffff:ffff:ffff:ffff:ffff
+      denied-peer-ip=fc00::-fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
+      denied-peer-ip=fe80::-febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff
+
+      # *Allow* any IP addresses that we explicitly set as relay IPs
+      ${concatMapStringsSep "\n" (ip: "allowed-peer-ip=${ip}") config.services.coturn.relay-ips}
+
+      # Various other security settings
+      no-tlsv1
+      no-tlsv1_1
+
+      # Monitoring
+      prometheus
+    '';
+  };
+
+  services.nginx.virtualHosts."${domain}" = {
+    useACMEHost = "tlater.net";
+
+    listen = [
+      {
+        addr = "0.0.0.0";
+        port = 80;
+      }
+      {
+        addr = "[::0]";
+        port = 80;
+      }
+      {
+        addr = "0.0.0.0";
+        port = 443;
+        ssl = true;
+      }
+      {
+        addr = "[::0]";
+        port = 443;
+        ssl = true;
+      }
+      {
+        addr = "0.0.0.0";
+        port = 8448;
+        ssl = true;
+      }
+      {
+        addr = "[::0]";
+        port = 8448;
+        ssl = true;
+      }
+    ];
+
+    forceSSL = true;
+    enableHSTS = true;
+    extraConfig = ''
+      merge_slashes off;
+    '';
+
+    locations = {
+      "/_matrix" = {
+        proxyPass = "http://${cfg.settings.global.address}:${toString cfg.settings.global.port}";
+        # Recommended by conduit
+        extraConfig = ''
+          proxy_buffering off;
+        '';
+      };
+
+      # Add Element X support
+      # TODO(tlater): Remove when no longer required: https://github.com/vector-im/element-x-android/issues/1085
+      "=/.well-known/matrix/client" = {
+        alias = pkgs.writeText "well-known-matrix-client" (builtins.toJSON {
+          "m.homeserver".base_url = "https://${domain}";
+          "org.matrix.msc3575.proxy".url = "https://${domain}";
+        });
+
+        extraConfig = ''
+          default_type application/json;
+          add_header Access-Control-Allow-Origin "*";
+        '';
+      };
+    };
+  };
+
+  services.backups.conduit = {
+    user = "root";
+    paths = [
+      "/var/lib/private/matrix-conduit/"
+    ];
+    # Other services store their data in conduit, so no other services
+    # need to be shut down currently.
+    pauseServices = ["conduit.service"];
+  };
+}
diff --git a/configuration/services/conduit/default.nix b/configuration/services/conduit/default.nix
deleted file mode 100644
index 4ba5271..0000000
--- a/configuration/services/conduit/default.nix
+++ /dev/null
@@ -1,182 +0,0 @@
-{
-  pkgs,
-  config,
-  lib,
-  ...
-}:
-let
-  inherit (lib.strings) concatMapStringsSep;
-
-  cfg = config.services.matrix-conduit;
-  domain = "matrix.${config.services.nginx.domain}";
-  turn-realm = "turn.${config.services.nginx.domain}";
-in
-{
-  imports = [
-    ./heisenbridge.nix
-    ./matrix-hookshot.nix
-  ];
-
-  services = {
-    matrix-conduit = {
-      enable = true;
-      package = pkgs.matrix-continuwuity;
-      settings.global = {
-        address = "127.0.0.1";
-        server_name = domain;
-        new_user_displayname_suffix = "🦆";
-        allow_check_for_updates = true;
-
-        # Set up delegation: https://docs.conduit.rs/delegation.html#automatic-recommended
-        # This is primarily to make sliding sync work
-        well_known = {
-          client = "https://${domain}";
-          server = "${domain}:443";
-        };
-
-        turn_uris =
-          let
-            address = "${config.services.coturn.realm}:${toString config.services.coturn.listening-port}";
-            tls-address = "${config.services.coturn.realm}:${toString config.services.coturn.tls-listening-port}";
-          in
-          [
-            "turn:${address}?transport=udp"
-            "turn:${address}?transport=tcp"
-            "turns:${tls-address}?transport=udp"
-            "turns:${tls-address}?transport=tcp"
-          ];
-      };
-    };
-
-    coturn = {
-      enable = true;
-      no-cli = true;
-      use-auth-secret = true;
-      static-auth-secret-file = config.sops.secrets."turn/secret".path;
-      realm = turn-realm;
-      relay-ips = [ "116.202.158.55" ];
-
-      # SSL config
-      pkey = "${config.security.acme.certs."tlater.net".directory}/key.pem";
-      cert = "${config.security.acme.certs."tlater.net".directory}/fullchain.pem";
-
-      # Based on suggestions from
-      # https://github.com/matrix-org/synapse/blob/develop/docs/turn-howto.md
-      # and
-      # https://www.foxypossibilities.com/2018/05/19/setting-up-a-turn-sever-for-matrix-on-nixos/
-      no-tcp-relay = true;
-      secure-stun = true;
-      extraConfig = ''
-        # Deny various local IP ranges, see
-        # https://www.rtcsec.com/article/cve-2020-26262-bypass-of-coturns-access-control-protection/
-        no-multicast-peers
-        denied-peer-ip=0.0.0.0-0.255.255.255
-        denied-peer-ip=10.0.0.0-10.255.255.255
-        denied-peer-ip=100.64.0.0-100.127.255.255
-        denied-peer-ip=127.0.0.0-127.255.255.255
-        denied-peer-ip=169.254.0.0-169.254.255.255
-        denied-peer-ip=172.16.0.0-172.31.255.255
-        denied-peer-ip=192.0.0.0-192.0.0.255
-        denied-peer-ip=192.0.2.0-192.0.2.255
-        denied-peer-ip=192.88.99.0-192.88.99.255
-        denied-peer-ip=192.168.0.0-192.168.255.255
-        denied-peer-ip=198.18.0.0-198.19.255.255
-        denied-peer-ip=198.51.100.0-198.51.100.255
-        denied-peer-ip=203.0.113.0-203.0.113.255
-        denied-peer-ip=240.0.0.0-255.255.255.255 denied-peer-ip=::1
-        denied-peer-ip=64:ff9b::-64:ff9b::ffff:ffff
-        denied-peer-ip=::ffff:0.0.0.0-::ffff:255.255.255.255
-        denied-peer-ip=100::-100::ffff:ffff:ffff:ffff
-        denied-peer-ip=2001::-2001:1ff:ffff:ffff:ffff:ffff:ffff:ffff
-        denied-peer-ip=2002::-2002:ffff:ffff:ffff:ffff:ffff:ffff:ffff
-        denied-peer-ip=fc00::-fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
-        denied-peer-ip=fe80::-febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff
-
-        # *Allow* any IP addresses that we explicitly set as relay IPs
-        ${concatMapStringsSep "\n" (ip: "allowed-peer-ip=${ip}") config.services.coturn.relay-ips}
-
-        # Various other security settings
-        no-tlsv1
-        no-tlsv1_1
-
-        # Monitoring
-        prometheus
-      '';
-    };
-
-    nginx.virtualHosts."${domain}" = {
-      useACMEHost = "tlater.net";
-
-      listen = [
-        {
-          addr = "0.0.0.0";
-          port = 80;
-        }
-        {
-          addr = "[::0]";
-          port = 80;
-        }
-        {
-          addr = "0.0.0.0";
-          port = 443;
-          ssl = true;
-        }
-        {
-          addr = "[::0]";
-          port = 443;
-          ssl = true;
-        }
-        {
-          addr = "0.0.0.0";
-          port = 8448;
-          ssl = true;
-        }
-        {
-          addr = "[::0]";
-          port = 8448;
-          ssl = true;
-        }
-      ];
-
-      forceSSL = true;
-      enableHSTS = true;
-      extraConfig = ''
-        merge_slashes off;
-      '';
-
-      locations = {
-        "/_matrix" = {
-          proxyPass = "http://${cfg.settings.global.address}:${toString cfg.settings.global.port}";
-          # Recommended by conduit
-          extraConfig = ''
-            proxy_buffering off;
-          '';
-        };
-        "/.well-known/matrix" = {
-          proxyPass = "http://${cfg.settings.global.address}:${toString cfg.settings.global.port}";
-        };
-      };
-    };
-
-    backups.conduit = {
-      user = "root";
-      paths = [ "/var/lib/private/matrix-conduit/" ];
-      # Other services store their data in conduit, so no other services
-      # need to be shut down currently.
-      pauseServices = [ "conduit.service" ];
-    };
-  };
-
-  systemd.services.conduit.serviceConfig = {
-    ExecStart = lib.mkForce "${config.services.matrix-conduit.package}/bin/conduwuit";
-    # Pass in the TURN secret via EnvironmentFile, not supported by
-    # upstream module currently.
-    #
-    # See also https://gitlab.com/famedly/conduit/-/issues/314
-    EnvironmentFile = config.sops.secrets."turn/env".path;
-  };
-
-  systemd.services.coturn.serviceConfig.SupplementaryGroups = [
-    config.security.acme.certs."tlater.net".group
-  ];
-}
diff --git a/configuration/services/conduit/heisenbridge.nix b/configuration/services/conduit/heisenbridge.nix
deleted file mode 100644
index f0f7e49..0000000
--- a/configuration/services/conduit/heisenbridge.nix
+++ /dev/null
@@ -1,78 +0,0 @@
-{
-  pkgs,
-  lib,
-  config,
-  ...
-}:
-let
-  conduitCfg = config.services.matrix-conduit;
-  matrixLib = pkgs.callPackage ./lib.nix { };
-in
-{
-  systemd.services.heisenbridge =
-    let
-      registration = matrixLib.writeRegistrationScript {
-        id = "heisenbridge";
-        url = "http://127.0.0.1:9898";
-        sender_localpart = "heisenbridge";
-
-        namespaces = {
-          users = [
-            {
-              regex = "@irc_.*";
-              exclusive = true;
-            }
-            {
-              regex = "@heisenbridge:.*";
-              exclusive = true;
-            }
-          ];
-
-          aliases = [ ];
-          rooms = [ ];
-        };
-      };
-    in
-    {
-      description = "Matrix<->IRC bridge";
-      wantedBy = [ "multi-user.target" ];
-      after = [ "conduit.service" ];
-
-      serviceConfig = {
-        Type = "exec";
-
-        LoadCredential = "heisenbridge:/run/secrets/heisenbridge";
-
-        inherit (registration) ExecStartPre;
-        ExecStart = lib.concatStringsSep " " [
-          "${lib.getExe pkgs.heisenbridge}"
-          "--config \${RUNTIME_DIRECTORY}/heisenbridge-registration.yaml"
-          "--owner @tlater:matrix.tlater.net"
-          "http://localhost:${toString conduitCfg.settings.global.port}"
-        ];
-
-        DynamicUser = true;
-        RuntimeDirectory = "heisenbridge";
-        RuntimeDirectoryMode = "0700";
-
-        RestrictNamespaces = true;
-        PrivateUsers = true;
-        ProtectHostname = true;
-        ProtectClock = true;
-        ProtectKernelTunables = true;
-        ProtectKernelModules = true;
-        ProtectKernelLogs = true;
-        ProtectControlGroups = true;
-        RestrictAddressFamilies = [ "AF_INET AF_INET6" ];
-        LockPersonality = true;
-        RestrictRealtime = true;
-        ProtectProc = "invisible";
-        ProcSubset = "pid";
-        UMask = 77;
-
-        # For the identd port
-        # CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ];
-        # AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
-      };
-    };
-}
diff --git a/configuration/services/conduit/lib.nix b/configuration/services/conduit/lib.nix
deleted file mode 100644
index ef407cf..0000000
--- a/configuration/services/conduit/lib.nix
+++ /dev/null
@@ -1,67 +0,0 @@
-{
-  lib,
-  writeShellScript,
-  formats,
-  replace-secret,
-}:
-let
-  replaceSecretBin = "${lib.getExe replace-secret}";
-in
-{
-  # Write a script that will set up the service's registration.yaml
-  # with secrets from systemd credentials.
-  #
-  # The credentials should be named `${id}_as-token` and
-  # `${id}_hs-token`.
-  #
-  # This registration file needs to be manually added to conduit by
-  # messaging the admin with the yaml file.
-  #
-  # TODO(tlater): Conduwuit seems to support a CLI interface for this,
-  # may want to migrate to that sometime.
-  writeRegistrationScript =
-    {
-      id, # Must be unique among all registered appservices/bots
-      url, # The URL on which the service listens
-      sender_localpart,
-      rate_limited ? false,
-      namespaces ? {
-        aliases = [ ];
-        rooms = [ ];
-        users = [ ];
-      },
-      extraSettings ? { },
-      # The location to place the file; assumes systemd runtime dir
-      runtimeRegistration ? "$RUNTIME_DIRECTORY/${id}-registration.yaml",
-    }:
-    let
-      registrationFile = (formats.yaml { }).generate "${id}-registration.yaml" (
-        {
-          inherit
-            id
-            url
-            sender_localpart
-            rate_limited
-            namespaces
-            ;
-
-          as_token = "@AS_TOKEN@";
-          hs_token = "@HS_TOKEN@";
-        }
-        // extraSettings
-      );
-    in
-    {
-      inherit runtimeRegistration;
-      ExecStartPre = writeShellScript "${id}-registration-setup.sh" ''
-        cp -f ${registrationFile} "${runtimeRegistration}"
-        chmod 600 "${runtimeRegistration}"
-
-        # Write actual secrets into config
-        ${replaceSecretBin} '@AS_TOKEN@' "$CREDENTIALS_DIRECTORY/${id}_as-token" "${runtimeRegistration}"
-        ${replaceSecretBin} '@HS_TOKEN@' "$CREDENTIALS_DIRECTORY/${id}_hs-token" "${runtimeRegistration}"
-
-        chmod 400 "${runtimeRegistration}"
-      '';
-    };
-}
diff --git a/configuration/services/conduit/matrix-hookshot.nix b/configuration/services/conduit/matrix-hookshot.nix
deleted file mode 100644
index 6b788b2..0000000
--- a/configuration/services/conduit/matrix-hookshot.nix
+++ /dev/null
@@ -1,166 +0,0 @@
-{
-  pkgs,
-  lib,
-  config,
-  ...
-}:
-let
-  matrixLib = pkgs.callPackage ./lib.nix { };
-
-  cfg = config.services.matrix-hookshot;
-  conduitCfg = config.services.matrix-conduit;
-
-  domain = conduitCfg.settings.global.server_name;
-
-  registration = matrixLib.writeRegistrationScript {
-    id = "matrix-hookshot";
-    url = "http://127.0.0.1:9993";
-    sender_localpart = "hookshot";
-
-    namespaces = {
-      aliases = [ ];
-      rooms = [ ];
-      users = [
-        {
-          regex = "@${cfg.settings.generic.userIdPrefix}.*:${domain}";
-          exclusive = true;
-        }
-      ];
-    };
-
-    # Encryption support
-    # TODO(tlater): Enable when
-    # https://github.com/matrix-org/matrix-hookshot/issues/1060 is
-    # fixed
-    # extraSettings = {
-    #   "de.sorunome.msc2409.push_ephemeral" = true;
-    #   push_ephemeral = true;
-    #   "org.matrix.msc3202" = true;
-    # };
-
-    runtimeRegistration = "${cfg.registrationFile}";
-  };
-in
-{
-  # users = {
-  #   users.matrix-hookshot = {
-  #     home = "/run/matrix-hookshot";
-  #     group = "matrix-hookshot";
-  #     isSystemUser = true;
-  #   };
-
-  #   groups.matrix-hookshot = { };
-  # };
-
-  systemd.services.matrix-hookshot = {
-    serviceConfig = {
-      Type = lib.mkForce "exec";
-
-      LoadCredential = "matrix-hookshot:/run/secrets/matrix-hookshot";
-      inherit (registration) ExecStartPre;
-
-      # Some library in matrix-hookshot wants a home directory
-      Environment = [ "HOME=/run/matrix-hookshot" ];
-
-      # User = "matrix-hookshot";
-      DynamicUser = true;
-      StateDirectory = "matrix-hookshot";
-      RuntimeDirectory = "matrix-hookshot";
-      RuntimeDirectoryMode = "0700";
-
-      RestrictNamespaces = true;
-      PrivateUsers = true;
-      ProtectHostname = true;
-      ProtectClock = true;
-      ProtectKernelTunables = true;
-      ProtectKernelModules = true;
-      ProtectKernelLogs = true;
-      ProtectControlGroups = true;
-      RestrictAddressFamilies = [
-        # "AF_UNIX"
-        "AF_INET"
-        "AF_INET6"
-      ];
-      LockPersonality = true;
-      RestrictRealtime = true;
-      ProtectProc = "invisible";
-      ProcSubset = "pid";
-      UMask = 77;
-    };
-  };
-
-  # services.redis.servers.matrix-hookshot = {
-  #   enable = true;
-  #   user = "matrix-hookshot";
-  # };
-
-  services.matrix-hookshot = {
-    enable = true;
-
-    serviceDependencies = [ "conduit.service" ];
-
-    registrationFile = "/run/matrix-hookshot/registration.yaml";
-
-    settings = {
-      bridge = {
-        inherit domain;
-        url = "http://localhost:${toString conduitCfg.settings.global.port}";
-        mediaUrl = conduitCfg.settings.global.well_known.client;
-        port = 9993;
-        bindAddress = "127.0.0.1";
-      };
-
-      bot.displayname = "Hookshot";
-
-      # cache.redisUri = "redis://${config.services.redis.servers.matrix-hookshot.unixSocket}";
-
-      generic = {
-        enabled = true;
-        outbound = false;
-        # Only allow webhooks from localhost for the moment
-        urlPrefix = "http://127.0.0.1:9000/webhook";
-        userIdPrefix = "_webhooks_";
-        allowJsTransformationFunctions = true;
-      };
-
-      # TODO(tlater): Enable when
-      # https://github.com/matrix-org/matrix-hookshot/issues/1060 is
-      # fixed
-      # encryption.storagePath = "/var/lib/matrix-hookshot/cryptostore";
-
-      permissions = [
-        {
-          actor = "matrix.tlater.net";
-          services = [
-            {
-              service = "*";
-              level = "notifications";
-            }
-          ];
-        }
-        {
-          actor = "@tlater:matrix.tlater.net";
-          services = [
-            {
-              service = "*";
-              level = "admin";
-            }
-          ];
-        }
-      ];
-
-      listeners = [
-        {
-          port = 9000;
-          resources = [ "webhooks" ];
-        }
-        {
-          port = 9001;
-          resources = [ "metrics" ];
-        }
-      ];
-
-      metrics.enabled = true;
-    };
-  };
-}
diff --git a/configuration/services/crowdsec.nix b/configuration/services/crowdsec.nix
deleted file mode 100644
index b736047..0000000
--- a/configuration/services/crowdsec.nix
+++ /dev/null
@@ -1,82 +0,0 @@
-{
-  pkgs,
-  config,
-  lib,
-  ...
-}:
-{
-  security.crowdsec = {
-    enable = true;
-
-    parserWhitelist = [ "10.45.249.2" ];
-
-    extraGroups = [
-      "systemd-journal"
-      "nginx"
-    ];
-
-    acquisitions = [
-      {
-        source = "journalctl";
-        labels.type = "syslog";
-        journalctl_filter = [ "SYSLOG_IDENTIFIER=Nextcloud" ];
-      }
-
-      {
-        source = "journalctl";
-        labels.type = "syslog";
-        journalctl_filter = [ "SYSLOG_IDENTIFIER=sshd-session" ];
-      }
-
-      {
-        labels.type = "nginx";
-        filenames =
-          [ "/var/log/nginx/*.log" ]
-          ++ lib.mapAttrsToList (
-            vHost: _: "/var/log/nginx/${vHost}/access.log"
-          ) config.services.nginx.virtualHosts;
-      }
-    ];
-
-    remediationComponents.firewallBouncer = {
-      enable = true;
-      settings.prometheus = {
-        enabled = true;
-        listen_addr = "127.0.0.1";
-        listen_port = "60601";
-      };
-    };
-  };
-
-  # Add whitelists for matrix
-  systemd.tmpfiles.settings."10-matrix" =
-    let
-      stateDir = config.security.crowdsec.stateDirectory;
-    in
-    {
-      "${stateDir}/config/postoverflows".d = {
-        user = "crowdsec";
-        group = "crowdsec";
-        mode = "0700";
-      };
-
-      "${stateDir}/config/postoverflows/s01-whitelist".d = {
-        user = "crowdsec";
-        group = "crowdsec";
-        mode = "0700";
-      };
-
-      "${stateDir}/config/postoverflows/s01-whitelist/matrix-whitelist.yaml"."L+".argument =
-        ((pkgs.formats.yaml { }).generate "crowdsec-matrix-whitelist.yaml" {
-          name = "tetsumaki/matrix";
-          description = "custom matrix whitelist";
-          whitelist = {
-            reason = "whitelist false positive for matrix";
-            expression = [
-              "evt.Overflow.Alert.Events[0].GetMeta('target_fqdn') == '${config.services.matrix-conduit.settings.global.server_name}'"
-              "evt.Overflow.Alert.GetScenario() in ['crowdsecurity/http-probing', 'crowdsecurity/http-crawl-non_statics']"
-            ];
-          };
-        }).outPath;
-    };
-}
diff --git a/configuration/services/fail2ban.nix b/configuration/services/fail2ban.nix
new file mode 100644
index 0000000..ace3219
--- /dev/null
+++ b/configuration/services/fail2ban.nix
@@ -0,0 +1,42 @@
+{pkgs, ...}: {
+  services.fail2ban = {
+    enable = true;
+    extraPackages = [pkgs.ipset];
+    banaction = "iptables-ipset-proto6-allports";
+    bantime-increment.enable = true;
+
+    jails = {
+      nginx-botsearch = ''
+        enabled = true
+        logpath = /var/log/nginx/access.log
+      '';
+    };
+
+    ignoreIP = [
+      "127.0.0.0/8"
+      "10.0.0.0/8"
+      "172.16.0.0/12"
+      "192.168.0.0/16"
+    ];
+  };
+
+  # Allow metrics services to connect to the socket as well
+  users.groups.fail2ban = {};
+  systemd.services.fail2ban.serviceConfig = {
+    ExecStartPost =
+      "+"
+      + (pkgs.writeShellScript "fail2ban-post-start" ''
+        while ! [ -S /var/run/fail2ban/fail2ban.sock ]; do
+            sleep 1
+        done
+
+        while ! ${pkgs.netcat}/bin/nc -zU /var/run/fail2ban/fail2ban.sock; do
+            sleep 1
+        done
+
+        ${pkgs.coreutils}/bin/chown root:fail2ban /var/run/fail2ban /var/run/fail2ban/fail2ban.sock
+        ${pkgs.coreutils}/bin/chmod 660 /var/run/fail2ban/fail2ban.sock
+        ${pkgs.coreutils}/bin/chmod 710 /var/run/fail2ban
+      '');
+  };
+}
diff --git a/configuration/services/foundryvtt.nix b/configuration/services/foundryvtt.nix
index 6c475a3..ac206fc 100644
--- a/configuration/services/foundryvtt.nix
+++ b/configuration/services/foundryvtt.nix
@@ -2,48 +2,42 @@
   lib,
   config,
   flake-inputs,
-  pkgs,
   ...
-}:
-let
+}: let
   domain = "foundryvtt.${config.services.nginx.domain}";
-in
-{
-  imports = [ flake-inputs.foundryvtt.nixosModules.foundryvtt ];
+in {
+  imports = [flake-inputs.foundryvtt.nixosModules.foundryvtt];
 
-  services = {
-    foundryvtt = {
-      enable = true;
-      hostName = domain;
-      minifyStaticFiles = true;
-      proxySSL = true;
-      proxyPort = 443;
-      package = flake-inputs.foundryvtt.packages.${pkgs.system}.foundryvtt_13;
-    };
-
-    nginx.virtualHosts."${domain}" =
-      let
-        inherit (config.services.foundryvtt) port;
-      in
-      {
-        forceSSL = true;
-        useACMEHost = "tlater.net";
-        enableHSTS = true;
-
-        locations."/" = {
-          proxyWebsockets = true;
-          proxyPass = "http://localhost:${toString port}";
-        };
-      };
-
-    backups.foundryvtt = {
-      user = "foundryvtt";
-      paths = [ config.services.foundryvtt.dataDir ];
-      pauseServices = [ "foundryvtt.service" ];
-    };
+  services.foundryvtt = {
+    enable = true;
+    hostName = domain;
+    minifyStaticFiles = true;
+    proxySSL = true;
+    proxyPort = 443;
   };
 
   # Want to start it manually when I need it, not have it constantly
   # running
-  systemd.services.foundryvtt.wantedBy = lib.mkForce [ ];
+  systemd.services.foundryvtt.wantedBy = lib.mkForce [];
+
+  services.nginx.virtualHosts."${domain}" = let
+    inherit (config.services.foundryvtt) port;
+  in {
+    forceSSL = true;
+    useACMEHost = "tlater.net";
+    enableHSTS = true;
+
+    locations."/" = {
+      proxyWebsockets = true;
+      proxyPass = "http://localhost:${toString port}";
+    };
+  };
+
+  services.backups.foundryvtt = {
+    user = "foundryvtt";
+    paths = [
+      config.services.foundryvtt.dataDir
+    ];
+    pauseServices = ["foundryvtt.service"];
+  };
 }
diff --git a/configuration/services/gitea.nix b/configuration/services/gitea.nix
index 613d30c..26fe2f8 100644
--- a/configuration/services/gitea.nix
+++ b/configuration/services/gitea.nix
@@ -3,81 +3,93 @@
   config,
   lib,
   ...
-}:
-let
+}: let
   domain = "gitea.${config.services.nginx.domain}";
-in
-{
-  services = {
-    forgejo = {
-      enable = true;
-      database.type = "postgres";
+in {
+  services.forgejo = {
+    enable = true;
+    database.type = "postgres";
 
-      settings = {
-        server = {
-          DOMAIN = domain;
-          HTTP_ADDR = "127.0.0.1";
-          ROOT_URL = "https://${domain}/";
-          SSH_PORT = 2222;
-        };
-
-        metrics = {
-          ENABLED = true;
-          TOKEN = "#metricstoken#";
-        };
-        service.DISABLE_REGISTRATION = true;
-        session.COOKIE_SECURE = true;
-      };
-    };
-
-    # Set up SSL
-    nginx.virtualHosts."${domain}" =
-      let
-        httpAddress = config.services.forgejo.settings.server.HTTP_ADDR;
-        httpPort = config.services.forgejo.settings.server.HTTP_PORT;
-      in
-      {
-        forceSSL = true;
-        useACMEHost = "tlater.net";
-        enableHSTS = true;
-
-        locations."/".proxyPass = "http://${httpAddress}:${toString httpPort}";
-        locations."/metrics" = {
-          extraConfig = ''
-            access_log off;
-            allow 127.0.0.1;
-            ${lib.optionalString config.networking.enableIPv6 "allow ::1;"}
-            deny all;
-          '';
-        };
+    settings = {
+      server = {
+        DOMAIN = domain;
+        HTTP_ADDR = "127.0.0.1";
+        ROOT_URL = "https://${domain}/";
+        SSH_PORT = 2222;
       };
 
-    backups.forgejo = {
-      user = "forgejo";
-      paths = [
-        "/var/lib/forgejo/forgejo-db.sql"
-        "/var/lib/forgejo/repositories/"
-        "/var/lib/forgejo/data/"
-        "/var/lib/forgejo/custom/"
-        # Conf is backed up via nix
-      ];
-      preparation = {
-        packages = [ config.services.postgresql.package ];
-        text = "pg_dump ${config.services.forgejo.database.name} --file=/var/lib/forgejo/forgejo-db.sql";
+      metrics = {
+        ENABLED = true;
+        TOKEN = "#metricstoken#";
       };
-      cleanup = {
-        packages = [ pkgs.coreutils ];
-        text = "rm /var/lib/forgejo/forgejo-db.sql";
-      };
-      pauseServices = [ "forgejo.service" ];
+      service.DISABLE_REGISTRATION = true;
+      session.COOKIE_SECURE = true;
     };
   };
 
-  systemd.services.forgejo.serviceConfig.ExecStartPre =
-    let
-      replaceSecretBin = "${pkgs.replace-secret}/bin/replace-secret";
-      secretPath = config.sops.secrets."forgejo/metrics-token".path;
-      runConfig = "${config.services.forgejo.customDir}/conf/app.ini";
-    in
-    [ "+${replaceSecretBin} '#metricstoken#' '${secretPath}' '${runConfig}'" ];
+  systemd.services.forgejo.serviceConfig.ExecStartPre = let
+    replaceSecretBin = "${pkgs.replace-secret}/bin/replace-secret";
+    secretPath = config.sops.secrets."forgejo/metrics-token".path;
+    runConfig = "${config.services.forgejo.customDir}/conf/app.ini";
+  in [
+    "+${replaceSecretBin} '#metricstoken#' '${secretPath}' '${runConfig}'"
+  ];
+
+  # Set up SSL
+  services.nginx.virtualHosts."${domain}" = let
+    httpAddress = config.services.forgejo.settings.server.HTTP_ADDR;
+    httpPort = config.services.forgejo.settings.server.HTTP_PORT;
+  in {
+    forceSSL = true;
+    useACMEHost = "tlater.net";
+    enableHSTS = true;
+
+    locations."/".proxyPass = "http://${httpAddress}:${toString httpPort}";
+    locations."/metrics" = {
+      extraConfig = ''
+        access_log off;
+        allow 127.0.0.1;
+        ${lib.optionalString config.networking.enableIPv6 "allow ::1;"}
+        deny all;
+      '';
+    };
+  };
+
+  # Block repeated failed login attempts
+  #
+  # TODO(tlater): Update this - we switched to forgejo, who knows what
+  # the new matches are.
+  # environment.etc = {
+  #   "fail2ban/filter.d/gitea.conf".text = ''
+  #     [Definition]
+  #     failregex = .*(Failed authentication attempt|invalid credentials|Attempted access of unknown user).* from <HOST>
+  #     journalmatch = _SYSTEMD_UNIT=forgejo.service + _COMM=forgejo + SYSLOG_IDENTIFIER=forgejo
+  #   '';
+  # };
+
+  # services.fail2ban.jails = {
+  #   gitea = ''
+  #     enabled = true
+  #   '';
+  # };
+
+  services.backups.forgejo = {
+    user = "forgejo";
+    paths = [
+      "/var/lib/forgejo/forgejo-db.sql"
+      "/var/lib/forgejo/repositories/"
+      "/var/lib/forgejo/data/"
+      "/var/lib/forgejo/custom/"
+      # Conf is backed up via nix
+    ];
+    preparation = {
+      packages = [config.services.postgresql.package];
+      text = "pg_dump ${config.services.forgejo.database.name} --file=/var/lib/forgejo/forgejo-db.sql";
+    };
+    cleanup = {
+      packages = [pkgs.coreutils];
+      text = "rm /var/lib/forgejo/forgejo-db.sql";
+    };
+    pauseServices = ["forgejo.service"];
+  };
 }
diff --git a/configuration/services/immich.nix b/configuration/services/immich.nix
deleted file mode 100644
index 516ea3e..0000000
--- a/configuration/services/immich.nix
+++ /dev/null
@@ -1,67 +0,0 @@
-{
-  pkgs,
-  config,
-  lib,
-  ...
-}:
-let
-  hostName = "immich.${config.services.nginx.domain}";
-in
-{
-  services = {
-    immich = {
-      enable = true;
-      settings.server.externalDomain = "https://${hostName}";
-
-      environment.IMMICH_TELEMETRY_INCLUDE = "all";
-    };
-
-    nginx.virtualHosts.${hostName} =
-      let
-        local = "http://${config.services.immich.host}:${toString config.services.immich.port}";
-      in
-      {
-        forceSSL = true;
-        useACMEHost = "tlater.net";
-        enableHSTS = true;
-
-        locations."/" = {
-          proxyPass = local;
-          proxyWebsockets = true;
-        };
-        locations."/metrics" = {
-          extraConfig = ''
-            access_log off;
-            allow 127.0.0.1;
-            ${lib.optionalString config.networking.enableIPv6 "allow ::1;"}
-            deny all;
-          '';
-        };
-      };
-
-    backups.immich =
-      let
-        db-dump = "${config.services.immich.mediaLocation}/immich-db.sql";
-      in
-      {
-        user = "immich";
-        paths = [ config.services.immich.mediaLocation ];
-
-        preparation = {
-          packages = [ config.services.postgresql.package ];
-          text = ''
-            pg_dump ${config.services.immich.database.name} --clean --if-exists --file=${db-dump}
-          '';
-        };
-
-        cleanup = {
-          packages = [ pkgs.coreutils ];
-          text = "rm ${db-dump}";
-        };
-        pauseServices = [
-          "immich-server.service"
-          "immich-machine-learning.service"
-        ];
-      };
-  };
-}
diff --git a/configuration/services/metrics/default.nix b/configuration/services/metrics/default.nix
index fe250fe..84e126a 100644
--- a/configuration/services/metrics/default.nix
+++ b/configuration/services/metrics/default.nix
@@ -5,6 +5,5 @@
     ./exporters.nix
     ./grafana.nix
     ./victoriametrics.nix
-    ./victorialogs.nix
   ];
 }
diff --git a/configuration/services/metrics/exporters.nix b/configuration/services/metrics/exporters.nix
index 52c2a46..f3054db 100644
--- a/configuration/services/metrics/exporters.nix
+++ b/configuration/services/metrics/exporters.nix
@@ -3,49 +3,23 @@
   pkgs,
   lib,
   ...
-}:
-let
-  yaml = pkgs.formats.yaml { };
-in
-{
+}: let
+  yaml = pkgs.formats.yaml {};
+in {
   services.prometheus = {
     exporters = {
-      blackbox = {
-        enable = true;
-        listenAddress = "127.0.0.1";
-        configFile = yaml.generate "blackbox.yaml" {
-          modules = {
-            http_2xx = {
-              prober = "http";
-              timeout = "5s";
-              http.preferred_ip_protocol = "ip4";
-            };
-
-            turn_server = {
-              prober = "tcp";
-              timeout = "5s";
-              tcp = {
-                preferred_ip_protocol = "ip4";
-                source_ip_address = "116.202.158.55";
-                tls = true;
-              };
-            };
-          };
-        };
-      };
-
       # Periodically check domain registration status
       domain = {
         enable = true;
         listenAddress = "127.0.0.1";
-        extraFlags =
-          let
-            conf.domains = [
-              "tlater.net"
-              "tlater.com"
-            ];
-          in
-          [ "--config=${yaml.generate "domains.yml" conf}" ];
+        extraFlags = let
+          conf.domains = [
+            "tlater.net"
+            "tlater.com"
+          ];
+        in [
+          "--config=${yaml.generate "domains.yml" conf}"
+        ];
       };
 
       # System statistics
@@ -74,29 +48,54 @@ in
         listenAddress = "127.0.0.1";
         group = "nginx";
 
-        settings.namespaces = lib.mapAttrsToList (name: _: {
-          inherit name;
-          metrics_override.prefix = "nginxlog";
-          namespace_label = "vhost";
+        settings.namespaces =
+          lib.mapAttrsToList (name: virtualHost: {
+            inherit name;
+            metrics_override.prefix = "nginxlog";
+            namespace_label = "vhost";
 
-          format = lib.concatStringsSep " " [
-            "$remote_addr - $remote_user [$time_local]"
-            ''"$request" $status $body_bytes_sent''
-            ''"$http_referer" "$http_user_agent"''
-            ''rt=$request_time uct="$upstream_connect_time"''
-            ''uht="$upstream_header_time" urt="$upstream_response_time"''
-          ];
+            format = lib.concatStringsSep " " [
+              "$remote_addr - $remote_user [$time_local]"
+              ''"$request" $status $body_bytes_sent''
+              ''"$http_referer" "$http_user_agent"''
+              ''rt=$request_time uct="$upstream_connect_time"''
+              ''uht="$upstream_header_time" urt="$upstream_response_time"''
+            ];
 
-          source.files = [ "/var/log/nginx/${name}/access.log" ];
-        }) config.services.nginx.virtualHosts;
+            source.files = [
+              "/var/log/nginx/${name}/access.log"
+            ];
+          })
+          config.services.nginx.virtualHosts;
+      };
+    };
+
+    extraExporters = {
+      fail2ban = let
+        cfg = config.services.prometheus.extraExporters.fail2ban;
+      in {
+        port = 9191;
+        serviceOpts = {
+          after = ["fail2ban.service"];
+          requires = ["fail2ban.service"];
+          serviceConfig = {
+            Group = "fail2ban";
+            RestrictAddressFamilies = ["AF_UNIX" "AF_INET" "AF_INET6"];
+            ExecStart = lib.concatStringsSep " " [
+              "${pkgs.local.prometheus-fail2ban-exporter}/bin/fail2ban-prometheus-exporter"
+              "--collector.f2b.socket=/var/run/fail2ban/fail2ban.sock"
+              "--web.listen-address='${cfg.listenAddress}:${toString cfg.port}'"
+              "--collector.f2b.exit-on-socket-connection-error=true"
+            ];
+          };
+        };
       };
     };
 
     # TODO(tlater):
     #   - wireguard (?)
     #   - postgres (?)
+    #   - blackbox (?) (curl to see if http and similar is up)
     #   - ssl_exporter (?)
   };
-
-  services.dbus.implementation = "broker";
 }
diff --git a/configuration/services/metrics/grafana.nix b/configuration/services/metrics/grafana.nix
index b30806c..1136a17 100644
--- a/configuration/services/metrics/grafana.nix
+++ b/configuration/services/metrics/grafana.nix
@@ -1,15 +1,10 @@
-{ pkgs, config, ... }:
-let
+{config, ...}: let
   domain = "metrics.${config.services.nginx.domain}";
-in
-{
+in {
   services.grafana = {
     enable = true;
     settings = {
-      server = {
-        http_port = 3001; # Default overlaps with gitea
-        root_url = "https://metrics.tlater.net";
-      };
+      server.http_port = 3001; # Default overlaps with gitea
 
       security = {
         admin_user = "tlater";
@@ -28,11 +23,6 @@ in
       };
     };
 
-    declarativePlugins = [
-      pkgs.grafanaPlugins.victoriametrics-metrics-datasource
-      pkgs.grafanaPlugins.victoriametrics-logs-datasource
-    ];
-
     provision = {
       enable = true;
 
@@ -40,16 +30,7 @@ in
         {
           name = "Victoriametrics - tlater.net";
           url = "http://localhost:8428";
-          type = "victoriametrics-metrics-datasource";
-          access = "proxy";
-          isDefault = true;
-        }
-
-        {
-          name = "Victorialogs - tlater.net";
-          url = "http://${config.services.victorialogs.bindAddress}";
-          type = "victoriametrics-logs-datasource";
-          access = "proxy";
+          type = "prometheus";
         }
       ];
     };
@@ -59,12 +40,7 @@ in
     forceSSL = true;
     useACMEHost = "tlater.net";
     enableHSTS = true;
-    locations = {
-      "/".proxyPass = "http://localhost:${toString config.services.grafana.settings.server.http_port}";
-      "/api/live" = {
-        proxyWebsockets = true;
-        proxyPass = "http://localhost:${toString config.services.grafana.settings.server.http_port}";
-      };
-    };
+    enableAuthorization = true;
+    locations."/".proxyPass = "http://localhost:${toString config.services.grafana.settings.server.http_port}";
   };
 }
diff --git a/configuration/services/metrics/options.nix b/configuration/services/metrics/options.nix
index e1b0761..81f0865 100644
--- a/configuration/services/metrics/options.nix
+++ b/configuration/services/metrics/options.nix
@@ -3,235 +3,202 @@
   config,
   lib,
   ...
-}:
-let
+}: let
   inherit (lib) types mkOption mkDefault;
-  yaml = pkgs.formats.yaml { };
-in
-{
+  yaml = pkgs.formats.yaml {};
+in {
   options = {
     services.prometheus = {
       extraExporters = mkOption {
-        default = { };
-        type = types.attrsOf (
-          types.submodule {
-            options = {
-              port = mkOption {
-                type = types.int;
-                description = "The port on which this exporter listens.";
-              };
-              listenAddress = mkOption {
-                type = types.str;
-                default = "127.0.0.1";
-                description = "Address to listen on.";
-              };
-              serviceOpts = mkOption {
-                type = types.attrs;
-                description = "An attrset to be merged with the exporter's systemd service.";
-              };
+        type = types.attrsOf (types.submodule {
+          options = {
+            port = mkOption {
+              type = types.int;
+              description = "The port on which this exporter listens.";
             };
-          }
-        );
+            listenAddress = mkOption {
+              type = types.str;
+              default = "127.0.0.1";
+              description = "Address to listen on.";
+            };
+            serviceOpts = mkOption {
+              type = types.attrs;
+              description = "An attrset to be merged with the exporter's systemd service.";
+            };
+          };
+        });
       };
     };
 
     services.victoriametrics.scrapeConfigs = mkOption {
-      type = types.attrsOf (
-        types.submodule (
-          { name, ... }:
-          {
-            options = {
-              job_name = mkOption {
-                type = types.str;
-                default = name;
+      type = types.attrsOf (types.submodule ({
+        name,
+        self,
+        ...
+      }: {
+        options = {
+          job_name = mkOption {
+            type = types.str;
+            default = name;
+          };
+
+          extraSettings = mkOption {
+            type = types.anything;
+            description = ''
+              Other settings to set for this scrape config.
+            '';
+            default = {};
+          };
+
+          targets = mkOption {
+            type = types.listOf types.str;
+            description = lib.mdDoc ''
+              Addresses scrape targets for this config listen on.
+
+              Shortcut for `static_configs = lib.singleton {targets = [<targets>];}`
+            '';
+            default = [];
+          };
+
+          static_configs = mkOption {
+            default = [];
+            type = types.listOf (types.submodule {
+              options = {
+                targets = mkOption {
+                  type = types.listOf types.str;
+                  description = lib.mdDoc ''
+                    The addresses scrape targets for this config listen on.
+
+                    Must in `listenAddress:port` format.
+                  '';
+                };
+                labels = mkOption {
+                  type = types.attrsOf types.str;
+                  description = lib.mdDoc ''
+                    Labels to apply to all targets defined for this static config.
+                  '';
+                  default = {};
+                };
               };
-
-              extraSettings = mkOption {
-                inherit (pkgs.formats.yaml { }) type;
-                description = ''
-                  Other settings to set for this scrape config.
-                '';
-                default = { };
-              };
-
-              targets = mkOption {
-                type = types.listOf types.str;
-                description = lib.mdDoc ''
-                  Addresses scrape targets for this config listen on.
-
-                  Shortcut for `static_configs = lib.singleton {targets = [<targets>];}`
-                '';
-                default = [ ];
-              };
-
-              static_configs = mkOption {
-                default = [ ];
-                type = types.listOf (
-                  types.submodule {
-                    options = {
-                      targets = mkOption {
-                        type = types.listOf types.str;
-                        description = lib.mdDoc ''
-                          The addresses scrape targets for this config listen on.
-
-                          Must in `listenAddress:port` format.
-                        '';
-                      };
-                      labels = mkOption {
-                        type = types.attrsOf types.str;
-                        description = lib.mdDoc ''
-                          Labels to apply to all targets defined for this static config.
-                        '';
-                        default = { };
-                      };
-                    };
-                  }
-                );
-              };
-            };
-          }
-        )
-      );
+            });
+          };
+        };
+      }));
     };
   };
 
   config = {
     systemd.services = lib.mkMerge [
-      (lib.mapAttrs' (
-        name: exporter:
-        lib.nameValuePair "prometheus-${name}-exporter" (
-          lib.mkMerge [
-            {
-              # Shamelessly copied from upstream because the upstream
-              # module is an intractable mess
-              wantedBy = [ "multi-user.target" ];
-              after = [ "network.target" ];
-              serviceConfig = {
-                Restart = mkDefault "always";
-                PrivateTmp = mkDefault true;
-                WorkingDirectory = mkDefault /tmp;
-                DynamicUser = mkDefault true;
-                # Hardening
-                CapabilityBoundingSet = mkDefault [ "" ];
-                DeviceAllow = [ "" ];
-                LockPersonality = true;
-                MemoryDenyWriteExecute = true;
-                NoNewPrivileges = true;
-                PrivateDevices = mkDefault true;
-                ProtectClock = mkDefault true;
-                ProtectControlGroups = true;
-                ProtectHome = true;
-                ProtectHostname = true;
-                ProtectKernelLogs = true;
-                ProtectKernelModules = true;
-                ProtectKernelTunables = true;
-                ProtectSystem = mkDefault "strict";
-                RemoveIPC = true;
-                RestrictAddressFamilies = [
-                  "AF_INET"
-                  "AF_INET6"
-                ];
-                RestrictNamespaces = true;
-                RestrictRealtime = true;
-                RestrictSUIDSGID = true;
-                SystemCallArchitectures = "native";
-                UMask = "0077";
-              };
-            }
-            exporter.serviceOpts
-          ]
-        )
-      ) config.services.prometheus.extraExporters)
+      (lib.mapAttrs' (name: exporter:
+        lib.nameValuePair "prometheus-${name}-exporter" (lib.mkMerge [
+          {
+            # Shamelessly copied from upstream because the upstream
+            # module is an intractable mess
+            wantedBy = ["multi-user.target"];
+            after = ["network.target"];
+            serviceConfig.Restart = mkDefault "always";
+            serviceConfig.PrivateTmp = mkDefault true;
+            serviceConfig.WorkingDirectory = mkDefault /tmp;
+            serviceConfig.DynamicUser = mkDefault true;
+            # Hardening
+            serviceConfig.CapabilityBoundingSet = mkDefault [""];
+            serviceConfig.DeviceAllow = [""];
+            serviceConfig.LockPersonality = true;
+            serviceConfig.MemoryDenyWriteExecute = true;
+            serviceConfig.NoNewPrivileges = true;
+            serviceConfig.PrivateDevices = mkDefault true;
+            serviceConfig.ProtectClock = mkDefault true;
+            serviceConfig.ProtectControlGroups = true;
+            serviceConfig.ProtectHome = true;
+            serviceConfig.ProtectHostname = true;
+            serviceConfig.ProtectKernelLogs = true;
+            serviceConfig.ProtectKernelModules = true;
+            serviceConfig.ProtectKernelTunables = true;
+            serviceConfig.ProtectSystem = mkDefault "strict";
+            serviceConfig.RemoveIPC = true;
+            serviceConfig.RestrictAddressFamilies = ["AF_INET" "AF_INET6"];
+            serviceConfig.RestrictNamespaces = true;
+            serviceConfig.RestrictRealtime = true;
+            serviceConfig.RestrictSUIDSGID = true;
+            serviceConfig.SystemCallArchitectures = "native";
+            serviceConfig.UMask = "0077";
+          }
+          exporter.serviceOpts
+        ]))
+      config.services.prometheus.extraExporters)
 
       {
-        vmagent-scrape-exporters =
-          let
-            inherit (config.services.victoriametrics) listenAddress;
-            vmAddr = (lib.optionalString (lib.hasPrefix ":" listenAddress) "127.0.0.1") + listenAddress;
-            promscrape = yaml.generate "prometheus.yml" {
-              scrape_configs = lib.mapAttrsToList (
-                _: scrape:
-                lib.recursiveUpdate {
-                  inherit (scrape) job_name;
-                  static_configs =
-                    scrape.static_configs
-                    ++ lib.optional (scrape.targets != [ ]) { inherit (scrape) targets; };
-                } scrape.extraSettings
-              ) config.services.victoriametrics.scrapeConfigs;
-            };
-          in
-          {
-            enable = true;
-            path = [ pkgs.victoriametrics ];
-            wantedBy = [ "multi-user.target" ];
-            after = [
-              "network.target"
-              "victoriametrics.service"
-            ];
-            serviceConfig = {
-              ExecStart = [
-                (lib.concatStringsSep " " [
-                  "${pkgs.victoriametrics}/bin/vmagent"
-                  "-promscrape.config=${promscrape}"
-                  "-remoteWrite.url=http://${vmAddr}/api/v1/write"
-                  "-remoteWrite.tmpDataPath=%t/vmagent"
-                ])
-              ];
-              SupplementaryGroups = "metrics";
-
-              DynamicUser = true;
-              RuntimeDirectory = "vmagent";
-              CapabilityBoundingSet = [ "" ];
-              DeviceAllow = [ "" ];
-              LockPersonality = true;
-              MemoryDenyWriteExecute = true;
-              NoNewPrivileges = true;
-              PrivateDevices = true;
-              ProtectClock = true;
-              ProtectControlGroups = true;
-              ProtectHome = true;
-              ProtectHostname = true;
-              ProtectKernelLogs = true;
-              ProtectKernelModules = true;
-              ProtectKernelTunables = true;
-              ProtectSystem = "strict";
-              RemoveIPC = true;
-              RestrictAddressFamilies = [
-                "AF_INET"
-                "AF_INET6"
-              ];
-              RestrictNamespaces = true;
-              RestrictRealtime = true;
-              RestrictSUIDSGID = true;
-              SystemCallArchitectures = "native";
-              UMask = "0077";
-            };
+        vmagent-scrape-exporters = let
+          listenAddress = config.services.victoriametrics.listenAddress;
+          vmAddr = (lib.optionalString (lib.hasPrefix ":" listenAddress) "127.0.0.1") + listenAddress;
+          promscrape = yaml.generate "prometheus.yml" {
+            scrape_configs = lib.mapAttrsToList (_: scrape:
+              lib.recursiveUpdate {
+                inherit (scrape) job_name;
+                static_configs =
+                  scrape.static_configs
+                  ++ lib.optional (scrape.targets != []) {targets = scrape.targets;};
+              }
+              scrape.extraSettings)
+            config.services.victoriametrics.scrapeConfigs;
           };
+        in {
+          enable = true;
+          path = [pkgs.victoriametrics];
+          wantedBy = ["multi-user.target"];
+          after = ["network.target" "victoriametrics.service"];
+          serviceConfig = {
+            ExecStart = [
+              (lib.concatStringsSep " " [
+                "${pkgs.victoriametrics}/bin/vmagent"
+                "-promscrape.config=${promscrape}"
+                "-remoteWrite.url=http://${vmAddr}/api/v1/write"
+                "-remoteWrite.tmpDataPath=%t/vmagent"
+              ])
+            ];
+            SupplementaryGroups = "metrics";
+
+            DynamicUser = true;
+            RuntimeDirectory = "vmagent";
+            CapabilityBoundingSet = [""];
+            DeviceAllow = [""];
+            LockPersonality = true;
+            MemoryDenyWriteExecute = true;
+            NoNewPrivileges = true;
+            PrivateDevices = true;
+            ProtectClock = true;
+            ProtectControlGroups = true;
+            ProtectHome = true;
+            ProtectHostname = true;
+            ProtectKernelLogs = true;
+            ProtectKernelModules = true;
+            ProtectKernelTunables = true;
+            ProtectSystem = "strict";
+            RemoveIPC = true;
+            RestrictAddressFamilies = ["AF_INET" "AF_INET6"];
+            RestrictNamespaces = true;
+            RestrictRealtime = true;
+            RestrictSUIDSGID = true;
+            SystemCallArchitectures = "native";
+            UMask = "0077";
+          };
+        };
       }
     ];
 
-    users.groups.metrics = { };
+    users.groups.metrics = {};
 
-    services.victoriametrics.scrapeConfigs =
-      let
-        allExporters = lib.mapAttrs (_: exporter: { inherit (exporter) listenAddress port; }) (
-          (lib.filterAttrs (
-            name: exporter:
-            # A bunch of deprecated exporters that need to be ignored
-            !(builtins.elem name [
-              "blackbox"
-              "minio"
-              "tor"
-              "unifi-poller"
-            ])
-            && builtins.isAttrs exporter
-            && exporter.enable
-          ) config.services.prometheus.exporters)
-          // config.services.prometheus.extraExporters
-        );
-      in
+    services.victoriametrics.scrapeConfigs = let
+      allExporters =
+        lib.mapAttrs (name: exporter: {
+          inherit (exporter) listenAddress port;
+        }) ((lib.filterAttrs (_: exporter: builtins.isAttrs exporter && exporter.enable)
+          config.services.prometheus.exporters)
+        // config.services.prometheus.extraExporters);
+    in
       lib.mapAttrs (_: exporter: {
-        targets = [ "${exporter.listenAddress}:${toString exporter.port}" ];
-      }) allExporters;
+        targets = ["${exporter.listenAddress}:${toString exporter.port}"];
+      })
+      allExporters;
   };
 }
diff --git a/configuration/services/metrics/victorialogs.nix b/configuration/services/metrics/victorialogs.nix
deleted file mode 100644
index 413659a..0000000
--- a/configuration/services/metrics/victorialogs.nix
+++ /dev/null
@@ -1,30 +0,0 @@
-{ config, lib, ... }:
-let
-  cfg = config.services.victorialogs;
-in
-{
-  options.services.victorialogs.bindAddress = lib.mkOption {
-    readOnly = true;
-    type = lib.types.str;
-    description = ''
-      Final address on which victorialogs listens.
-    '';
-  };
-
-  config = {
-    services.victorialogs = {
-      enable = true;
-      bindAddress =
-        (lib.optionalString (lib.hasPrefix ":" cfg.listenAddress) "127.0.0.1") + cfg.listenAddress;
-    };
-
-    services.journald.upload = {
-      enable = true;
-      settings.Upload = {
-        URL = "http://${cfg.bindAddress}/insert/journald";
-        NetworkTimeoutSec = "20s";
-      };
-    };
-    systemd.services."systemd-journal-upload".after = [ "victorialogs.service" ];
-  };
-}
diff --git a/configuration/services/metrics/victoriametrics.nix b/configuration/services/metrics/victoriametrics.nix
index f37b8b0..daf3f94 100644
--- a/configuration/services/metrics/victoriametrics.nix
+++ b/configuration/services/metrics/victoriametrics.nix
@@ -1,99 +1,16 @@
-{ config, lib, ... }:
-let
-  blackbox_host = config.services.prometheus.exporters.blackbox.listenAddress;
-  blackbox_port = config.services.prometheus.exporters.blackbox.port;
-in
-{
+{config, ...}: {
   config.services.victoriametrics = {
     enable = true;
-    extraOptions = [ "-storage.minFreeDiskSpaceBytes=5GB" ];
+    extraOptions = [
+      "-storage.minFreeDiskSpaceBytes=5GB"
+    ];
 
     scrapeConfigs = {
       forgejo = {
-        targets = [ "127.0.0.1:${toString config.services.forgejo.settings.server.HTTP_PORT}" ];
+        targets = ["127.0.0.1:${toString config.services.forgejo.settings.server.HTTP_PORT}"];
         extraSettings.authorization.credentials_file = config.sops.secrets."forgejo/metrics-token".path;
       };
-
-      blackbox = {
-        static_configs = lib.singleton {
-          targets = lib.mapAttrsToList (vHost: _: "https://${vHost}") config.services.nginx.virtualHosts;
-        };
-
-        extraSettings = {
-          metrics_path = "/probe";
-          params.module = [ "http_2xx" ];
-
-          relabel_configs = [
-            {
-              source_labels = [ "__address__" ];
-              target_label = "__param_target";
-            }
-            {
-              source_labels = [ "__param_target" ];
-              target_label = "instance";
-            }
-            {
-              target_label = "__address__";
-              replacement = "${blackbox_host}:${toString blackbox_port}";
-            }
-          ];
-        };
-      };
-
-      blackbox_turn = {
-        targets = [ "turn.tlater.net:${toString config.services.coturn.tls-listening-port}" ];
-
-        extraSettings = {
-          metrics_path = "/probe";
-          params.module = [ "turn_server" ];
-
-          relabel_configs = [
-            {
-              source_labels = [ "__address__" ];
-              target_label = "__param_target";
-            }
-            {
-              source_labels = [ "__param_target" ];
-              target_label = "instance";
-            }
-            {
-              target_label = "__address__";
-              replacement = "${blackbox_host}:${toString blackbox_port}";
-            }
-          ];
-        };
-      };
-
-      blackbox_exporter.targets = [ "${blackbox_host}:${toString blackbox_port}" ];
-
-      coturn.targets = [ "127.0.0.1:9641" ];
-
-      crowdsec.targets =
-        let
-          address = config.security.crowdsec.settings.prometheus.listen_addr;
-          port = config.security.crowdsec.settings.prometheus.listen_port;
-        in
-        [ "${address}:${toString port}" ];
-
-      csFirewallBouncer.targets =
-        let
-          address =
-            config.security.crowdsec.remediationComponents.firewallBouncer.settings.prometheus.listen_addr;
-          port =
-            config.security.crowdsec.remediationComponents.firewallBouncer.settings.prometheus.listen_port;
-        in
-        [ "${address}:${toString port}" ];
-
-      immich.targets = [
-        "127.0.0.1:8081"
-        "127.0.0.1:8082"
-      ];
-
-      # Configured in the hookshot listeners, but it's hard to filter
-      # the correct values out of that config.
-      matrixHookshot.targets = [ "127.0.0.1:9001" ];
-
-      victorialogs.targets = [ config.services.victorialogs.bindAddress ];
+      coturn.targets = ["127.0.0.1:9641"];
     };
   };
 }
diff --git a/configuration/services/nextcloud.nix b/configuration/services/nextcloud.nix
index 4af77a9..2c7fe4f 100644
--- a/configuration/services/nextcloud.nix
+++ b/configuration/services/nextcloud.nix
@@ -1,103 +1,105 @@
 {
   pkgs,
   config,
-  lib,
   ...
-}:
-let
-  nextcloud = pkgs.nextcloud31;
+}: let
+  # Update pending on rewrite of nextcloud news, though there is an
+  # alpha to switch to if it becomes necessary:
+  # https://github.com/nextcloud/news/issues/2610
+  nextcloud = pkgs.nextcloud27;
   hostName = "nextcloud.${config.services.nginx.domain}";
-in
-{
-  services = {
-    nextcloud = {
-      inherit hostName;
+in {
+  services.nextcloud = {
+    inherit hostName;
 
-      package = nextcloud;
-      phpPackage = lib.mkForce (
-        pkgs.php.override {
-          packageOverrides = _: prev: {
-            extensions = prev.extensions // {
-              pgsql = prev.extensions.pgsql.overrideAttrs (_: {
-                configureFlags = [ "--with-pgsql=${lib.getDev config.services.postgresql.package.pg_config}" ];
-              });
-              pdo_pgsql = prev.extensions.pdo_pgsql.overrideAttrs (_: {
-                configureFlags = [ "--with-pdo-pgsql=${lib.getDev config.services.postgresql.package.pg_config}" ];
-              });
-            };
-          };
-        }
-      );
-      enable = true;
-      maxUploadSize = "2G";
-      https = true;
+    package = nextcloud;
+    enable = true;
+    maxUploadSize = "2G";
+    https = true;
 
-      configureRedis = true;
+    configureRedis = true;
 
-      config = {
-        dbtype = "pgsql";
-        dbhost = "/run/postgresql";
+    config = {
+      dbtype = "pgsql";
+      dbhost = "/run/postgresql";
 
-        adminuser = "tlater";
-        adminpassFile = config.sops.secrets."nextcloud/tlater".path;
-      };
-
-      settings = {
-        default_phone_region = "AT";
-        overwriteprotocol = "https";
-      };
-
-      phpOptions = {
-        "opcache.interned_strings_buffer" = "16";
-      };
-
-      extraApps = {
-        inherit (config.services.nextcloud.package.packages.apps)
-          calendar
-          contacts
-          cookbook
-          news
-          ;
-      };
+      adminuser = "tlater";
+      adminpassFile = config.sops.secrets."nextcloud/tlater".path;
     };
 
-    # Set up SSL
-    nginx.virtualHosts."${hostName}" = {
-      forceSSL = true;
-      useACMEHost = "tlater.net";
-      # The upstream module already adds HSTS
+    settings = {
+      default_phone_region = "AT";
+      overwriteprotocol = "https";
     };
 
-    backups.nextcloud = {
-      user = "nextcloud";
-      paths = [
-        "/var/lib/nextcloud/nextcloud-db.sql"
-        "/var/lib/nextcloud/data/"
-        "/var/lib/nextcloud/config/config.php"
-      ];
-      preparation = {
-        packages = [
-          config.services.postgresql.package
-          config.services.nextcloud.occ
-        ];
-        text = ''
-          nextcloud-occ maintenance:mode --on
-          pg_dump ${config.services.nextcloud.config.dbname} --file=/var/lib/nextcloud/nextcloud-db.sql
-        '';
-      };
-      cleanup = {
-        packages = [
-          pkgs.coreutils
-          config.services.nextcloud.occ
-        ];
-        text = ''
-          rm /var/lib/nextcloud/nextcloud-db.sql
-          nextcloud-occ maintenance:mode --off
-        '';
-      };
+    phpOptions = {
+      "opcache.interned_strings_buffer" = "16";
+    };
+
+    extraApps = {
+      inherit (pkgs.local) bookmarks calendar contacts cookbook news notes;
     };
   };
 
   # Ensure that this service doesn't start before postgres is ready
-  systemd.services.nextcloud-setup.after = [ "postgresql.service" ];
+  systemd.services.nextcloud-setup.after = ["postgresql.service"];
+
+  # Set up SSL
+  services.nginx.virtualHosts."${hostName}" = {
+    forceSSL = true;
+    useACMEHost = "tlater.net";
+    # The upstream module already adds HSTS
+  };
+
+  # Block repeated failed login attempts
+  environment.etc = {
+    "fail2ban/filter.d/nextcloud.conf".text = ''
+      [Definition]
+      _groupsre = (?:(?:,?\s*"\w+":(?:"[^"]+"|\w+))*)
+      failregex = \{%(_groupsre)s,?\s*"remoteAddr":"<HOST>"%(_groupsre)s,?\s*"message":"Login failed:
+                  \{%(_groupsre)s,?\s*"remoteAddr":"<HOST>"%(_groupsre)s,?\s*"message":"Trusted domain error.
+      datepattern = ,?\s*"time"\s*:\s*"%%Y-%%m-%%d[T ]%%H:%%M:%%S(%%z)?"
+      journalmatch = SYSLOG_IDENTIFIER=Nextcloud
+    '';
+  };
+
+  services.fail2ban.jails = {
+    nextcloud = ''
+      enabled = true
+
+      # Nextcloud does some throttling already, so we need to set
+      # these to something bigger.
+      findtime = 43200
+      bantime = 86400
+    '';
+  };
+
+  services.backups.nextcloud = {
+    user = "nextcloud";
+    paths = [
+      "/var/lib/nextcloud/nextcloud-db.sql"
+      "/var/lib/nextcloud/data/"
+      "/var/lib/nextcloud/config/config.php"
+    ];
+    preparation = {
+      packages = [
+        config.services.postgresql.package
+        config.services.nextcloud.occ
+      ];
+      text = ''
+        nextcloud-occ maintenance:mode --on
+        pg_dump ${config.services.nextcloud.config.dbname} --file=/var/lib/nextcloud/nextcloud-db.sql
+      '';
+    };
+    cleanup = {
+      packages = [
+        pkgs.coreutils
+        config.services.nextcloud.occ
+      ];
+      text = ''
+        rm /var/lib/nextcloud/nextcloud-db.sql
+        nextcloud-occ maintenance:mode --off
+      '';
+    };
+  };
 }
diff --git a/configuration/services/postgres.nix b/configuration/services/postgres.nix
index 85a6843..4ec1e83 100644
--- a/configuration/services/postgres.nix
+++ b/configuration/services/postgres.nix
@@ -1,5 +1,8 @@
-{ pkgs, ... }:
 {
+  config,
+  pkgs,
+  ...
+}: {
   services.postgresql = {
     package = pkgs.postgresql_14;
     enable = true;
@@ -25,11 +28,16 @@
         name = "nextcloud";
         ensureDBOwnership = true;
       }
+      {
+        name = config.services.authelia.instances.main.user;
+        ensureDBOwnership = true;
+      }
     ];
 
     ensureDatabases = [
       "grafana"
       "nextcloud"
+      config.services.authelia.instances.main.user
     ];
   };
 }
diff --git a/configuration/services/starbound.nix b/configuration/services/starbound.nix
index f5b23c3..6da890e 100644
--- a/configuration/services/starbound.nix
+++ b/configuration/services/starbound.nix
@@ -1,14 +1,16 @@
-{ pkgs, lib, ... }:
-let
-  inherit (lib) concatStringsSep;
-in
 {
+  pkgs,
+  lib,
+  ...
+}: let
+  inherit (lib) concatStringsSep;
+in {
   # Sadly, steam-run requires some X libs
   environment.noXlibs = false;
 
   systemd.services.starbound = {
     description = "Starbound";
-    after = [ "network.target" ];
+    after = ["network.target"];
 
     serviceConfig = {
       ExecStart = "${pkgs.local.starbound}/bin/launch-starbound ${./configs/starbound.json}";
@@ -65,7 +67,7 @@ in
       # Game servers shouldn't use cgroups themselves either
       ProtectControlGroups = true;
       # Most game servers will never need other socket types
-      RestrictAddressFamilies = [ "AF_UNIX AF_INET AF_INET6" ];
+      RestrictAddressFamilies = ["AF_UNIX AF_INET AF_INET6"];
       # Also a no-brainer, no game server should ever need this
       LockPersonality = true;
       # Some game servers will probably try to set this, but they
@@ -111,7 +113,9 @@ in
 
   services.backups.starbound = {
     user = "root";
-    paths = [ "/var/lib/private/starbound/storage/universe/" ];
-    pauseServices = [ "starbound.service" ];
+    paths = [
+      "/var/lib/private/starbound/storage/universe/"
+    ];
+    pauseServices = ["starbound.service"];
   };
 }
diff --git a/configuration/services/webserver.nix b/configuration/services/webserver.nix
index 864f6c0..387df57 100644
--- a/configuration/services/webserver.nix
+++ b/configuration/services/webserver.nix
@@ -1,8 +1,6 @@
-{ config, ... }:
-let
-  inherit (config.services.nginx) domain;
-in
-{
+{config, ...}: let
+  domain = config.services.nginx.domain;
+in {
   services.tlaternet-webserver = {
     enable = true;
     listen = {
@@ -12,17 +10,15 @@ in
   };
 
   # Set up SSL
-  services.nginx.virtualHosts."${domain}" =
-    let
-      inherit (config.services.tlaternet-webserver.listen) addr port;
-    in
-    {
-      serverAliases = [ "www.${domain}" ];
+  services.nginx.virtualHosts."${domain}" = let
+    inherit (config.services.tlaternet-webserver.listen) addr port;
+  in {
+    serverAliases = ["www.${domain}"];
 
-      forceSSL = true;
-      useACMEHost = "tlater.net";
-      enableHSTS = true;
+    forceSSL = true;
+    useACMEHost = "tlater.net";
+    enableHSTS = true;
 
-      locations."/".proxyPass = "http://${addr}:${toString port}";
-    };
+    locations."/".proxyPass = "http://${addr}:${toString port}";
+  };
 }
diff --git a/configuration/services/wireguard.nix b/configuration/services/wireguard.nix
index bbab22e..1ae6aac 100644
--- a/configuration/services/wireguard.nix
+++ b/configuration/services/wireguard.nix
@@ -1,5 +1,4 @@
-{ config, ... }:
-{
+{config, ...}: {
   # iptables needs to permit forwarding from wg0 to wg0
   networking.firewall.extraCommands = ''
     iptables -A FORWARD -i wg0 -o wg0 -j ACCEPT
@@ -24,10 +23,20 @@
         };
 
         wireguardPeers = [
-          # yui
           {
-            AllowedIPs = [ "10.45.249.2/32" ];
-            PublicKey = "5mlnqEVJWks5OqgeFA2bLIrvST9TlCE81Btl+j4myz0=";
+            # yui
+            wireguardPeerConfig = {
+              AllowedIPs = ["10.45.249.2/32"];
+              PublicKey = "5mlnqEVJWks5OqgeFA2bLIrvST9TlCE81Btl+j4myz0=";
+            };
+          }
+
+          {
+            # yuanyuan
+            wireguardPeerConfig = {
+              AllowedIPs = ["10.45.249.10/32"];
+              PublicKey = "0UsFE2atz/O5P3OKQ8UHyyyGQNJbp1MeIWUJLuoerwE=";
+            };
           }
         ];
       };
@@ -38,23 +47,23 @@
         matchConfig.Name = "wg0";
 
         networkConfig = {
-          Description = "VLAN";
-
           Address = [
             "10.45.249.1/32"
             # TODO(tlater): Add IPv6 whenever that becomes relevant
           ];
 
-          IPv4Forwarding = "yes";
+          IPForward = "yes";
           IPv4ProxyARP = "yes";
         };
 
         routes = [
           {
-            Source = "10.45.249.0/24";
-            Destination = "10.45.249.0/24";
-            Gateway = "10.45.249.1";
-            GatewayOnLink = "no";
+            routeConfig = {
+              Source = "10.45.249.0/24";
+              Destination = "10.45.249.0/24";
+              Gateway = "10.45.249.1";
+              GatewayOnLink = "no";
+            };
           }
         ];
 
diff --git a/configuration/sops.nix b/configuration/sops.nix
index 0337438..0746133 100644
--- a/configuration/sops.nix
+++ b/configuration/sops.nix
@@ -3,9 +3,15 @@
     defaultSopsFile = ../keys/production.yaml;
 
     secrets = {
-      "battery-manager/email" = { };
+      "battery-manager/email" = {
+        owner = "battery-manager";
+        group = "battery-manager";
+      };
 
-      "battery-manager/password" = { };
+      "battery-manager/password" = {
+        owner = "battery-manager";
+        group = "battery-manager";
+      };
 
       # Gitea
       "forgejo/metrics-token" = {
@@ -25,12 +31,12 @@
       };
 
       # Heisenbridge
-      "heisenbridge/as-token" = { };
-      "heisenbridge/hs-token" = { };
+      "heisenbridge/as-token" = {};
+      "heisenbridge/hs-token" = {};
 
-      # Matrix-hookshot
-      "matrix-hookshot/as-token" = { };
-      "matrix-hookshot/hs-token" = { };
+      "hetzner-api" = {
+        owner = "acme";
+      };
 
       # Nextcloud
       "nextcloud/tlater" = {
@@ -38,14 +44,6 @@
         group = "nextcloud";
       };
 
-      # Porkbub/ACME
-      "porkbun/api-key" = {
-        owner = "acme";
-      };
-      "porkbun/secret-api-key" = {
-        owner = "acme";
-      };
-
       # Restic
       "restic/local-backups" = {
         owner = "root";
@@ -64,10 +62,10 @@
       };
 
       # Steam
-      "steam/tlater" = { };
+      "steam/tlater" = {};
 
       # Turn
-      "turn/env" = { };
+      "turn/env" = {};
       "turn/secret" = {
         owner = "turnserver";
       };
diff --git a/flake.lock b/flake.lock
index 885b579..3f8dcb9 100644
--- a/flake.lock
+++ b/flake.lock
@@ -1,206 +1,5 @@
 {
   "nodes": {
-    "cachix": {
-      "inputs": {
-        "devenv": [
-          "sonnenshift",
-          "crate2nix"
-        ],
-        "flake-compat": [
-          "sonnenshift",
-          "crate2nix"
-        ],
-        "nixpkgs": "nixpkgs_3",
-        "pre-commit-hooks": [
-          "sonnenshift",
-          "crate2nix"
-        ]
-      },
-      "locked": {
-        "lastModified": 1709700175,
-        "narHash": "sha256-A0/6ZjLmT9qdYzKHmevnEIC7G+GiZ4UCr8v0poRPzds=",
-        "owner": "cachix",
-        "repo": "cachix",
-        "rev": "be97b37989f11b724197b5f4c7ffd78f12c8c4bf",
-        "type": "github"
-      },
-      "original": {
-        "owner": "cachix",
-        "ref": "latest",
-        "repo": "cachix",
-        "type": "github"
-      }
-    },
-    "cachix_2": {
-      "inputs": {
-        "devenv": [
-          "sonnenshift",
-          "crate2nix",
-          "crate2nix_stable"
-        ],
-        "flake-compat": [
-          "sonnenshift",
-          "crate2nix",
-          "crate2nix_stable"
-        ],
-        "nixpkgs": "nixpkgs_4",
-        "pre-commit-hooks": [
-          "sonnenshift",
-          "crate2nix",
-          "crate2nix_stable"
-        ]
-      },
-      "locked": {
-        "lastModified": 1716549461,
-        "narHash": "sha256-lHy5kgx6J8uD+16SO47dPrbob98sh+W1tf4ceSqPVK4=",
-        "owner": "cachix",
-        "repo": "cachix",
-        "rev": "e2bb269fb8c0828d5d4d2d7b8d09ea85abcacbd4",
-        "type": "github"
-      },
-      "original": {
-        "owner": "cachix",
-        "ref": "latest",
-        "repo": "cachix",
-        "type": "github"
-      }
-    },
-    "cachix_3": {
-      "inputs": {
-        "devenv": [
-          "sonnenshift",
-          "crate2nix",
-          "crate2nix_stable",
-          "crate2nix_stable"
-        ],
-        "flake-compat": [
-          "sonnenshift",
-          "crate2nix",
-          "crate2nix_stable",
-          "crate2nix_stable"
-        ],
-        "nixpkgs": "nixpkgs_5",
-        "pre-commit-hooks": [
-          "sonnenshift",
-          "crate2nix",
-          "crate2nix_stable",
-          "crate2nix_stable"
-        ]
-      },
-      "locked": {
-        "lastModified": 1716549461,
-        "narHash": "sha256-lHy5kgx6J8uD+16SO47dPrbob98sh+W1tf4ceSqPVK4=",
-        "owner": "cachix",
-        "repo": "cachix",
-        "rev": "e2bb269fb8c0828d5d4d2d7b8d09ea85abcacbd4",
-        "type": "github"
-      },
-      "original": {
-        "owner": "cachix",
-        "ref": "latest",
-        "repo": "cachix",
-        "type": "github"
-      }
-    },
-    "crate2nix": {
-      "inputs": {
-        "cachix": "cachix",
-        "crate2nix_stable": "crate2nix_stable",
-        "devshell": "devshell_3",
-        "flake-compat": "flake-compat_4",
-        "flake-parts": "flake-parts_3",
-        "nix-test-runner": "nix-test-runner_3",
-        "nixpkgs": [
-          "sonnenshift",
-          "nixpkgs"
-        ],
-        "pre-commit-hooks": "pre-commit-hooks_3"
-      },
-      "locked": {
-        "lastModified": 1739473963,
-        "narHash": "sha256-ItAhpjNUzEWd/cgZVyW/jvoGbCec4TK29e1Mnmn1oJE=",
-        "owner": "nix-community",
-        "repo": "crate2nix",
-        "rev": "be31feae9a82c225c0fd1bdf978565dc452a483a",
-        "type": "github"
-      },
-      "original": {
-        "owner": "nix-community",
-        "repo": "crate2nix",
-        "type": "github"
-      }
-    },
-    "crate2nix_stable": {
-      "inputs": {
-        "cachix": "cachix_2",
-        "crate2nix_stable": "crate2nix_stable_2",
-        "devshell": "devshell_2",
-        "flake-compat": "flake-compat_3",
-        "flake-parts": "flake-parts_2",
-        "nix-test-runner": "nix-test-runner_2",
-        "nixpkgs": "nixpkgs_7",
-        "pre-commit-hooks": "pre-commit-hooks_2"
-      },
-      "locked": {
-        "lastModified": 1719760004,
-        "narHash": "sha256-esWhRnt7FhiYq0CcIxw9pvH+ybOQmWBfHYMtleaMhBE=",
-        "owner": "nix-community",
-        "repo": "crate2nix",
-        "rev": "1dee214bb20855fa3e1e7bb98d28922ddaff8c57",
-        "type": "github"
-      },
-      "original": {
-        "owner": "nix-community",
-        "ref": "0.14.1",
-        "repo": "crate2nix",
-        "type": "github"
-      }
-    },
-    "crate2nix_stable_2": {
-      "inputs": {
-        "cachix": "cachix_3",
-        "crate2nix_stable": "crate2nix_stable_3",
-        "devshell": "devshell",
-        "flake-compat": "flake-compat_2",
-        "flake-parts": "flake-parts",
-        "nix-test-runner": "nix-test-runner",
-        "nixpkgs": "nixpkgs_6",
-        "pre-commit-hooks": "pre-commit-hooks"
-      },
-      "locked": {
-        "lastModified": 1712821484,
-        "narHash": "sha256-rGT3CW64cJS9nlnWPFWSc1iEa3dNZecVVuPVGzcsHe8=",
-        "owner": "nix-community",
-        "repo": "crate2nix",
-        "rev": "42883afcad3823fa5811e967fb7bff54bc3c9d6d",
-        "type": "github"
-      },
-      "original": {
-        "owner": "nix-community",
-        "ref": "0.14.0",
-        "repo": "crate2nix",
-        "type": "github"
-      }
-    },
-    "crate2nix_stable_3": {
-      "inputs": {
-        "flake-utils": "flake-utils"
-      },
-      "locked": {
-        "lastModified": 1702842982,
-        "narHash": "sha256-A9AowkHIjsy1a4LuiPiVP88FMxyCWK41flZEZOUuwQM=",
-        "owner": "nix-community",
-        "repo": "crate2nix",
-        "rev": "75ac2973affa6b9b4f661a7b592cba6e4f51d426",
-        "type": "github"
-      },
-      "original": {
-        "owner": "nix-community",
-        "ref": "0.12.0",
-        "repo": "crate2nix",
-        "type": "github"
-      }
-    },
     "deploy-rs": {
       "inputs": {
         "flake-compat": "flake-compat",
@@ -208,11 +7,11 @@
         "utils": "utils"
       },
       "locked": {
-        "lastModified": 1727447169,
-        "narHash": "sha256-3KyjMPUKHkiWhwR91J1YchF6zb6gvckCAY1jOE+ne0U=",
+        "lastModified": 1711973905,
+        "narHash": "sha256-UFKME/N1pbUtn+2Aqnk+agUt8CekbpuqwzljivfIme8=",
         "owner": "serokell",
         "repo": "deploy-rs",
-        "rev": "aa07eb05537d4cd025e2310397a6adcedfe72c76",
+        "rev": "88b3059b020da69cbe16526b8d639bd5e0b51c8b",
         "type": "github"
       },
       "original": {
@@ -221,78 +20,6 @@
         "type": "github"
       }
     },
-    "devshell": {
-      "inputs": {
-        "flake-utils": "flake-utils_2",
-        "nixpkgs": [
-          "sonnenshift",
-          "crate2nix",
-          "crate2nix_stable",
-          "crate2nix_stable",
-          "nixpkgs"
-        ]
-      },
-      "locked": {
-        "lastModified": 1717408969,
-        "narHash": "sha256-Q0OEFqe35fZbbRPPRdrjTUUChKVhhWXz3T9ZSKmaoVY=",
-        "owner": "numtide",
-        "repo": "devshell",
-        "rev": "1ebbe68d57457c8cae98145410b164b5477761f4",
-        "type": "github"
-      },
-      "original": {
-        "owner": "numtide",
-        "repo": "devshell",
-        "type": "github"
-      }
-    },
-    "devshell_2": {
-      "inputs": {
-        "flake-utils": "flake-utils_3",
-        "nixpkgs": [
-          "sonnenshift",
-          "crate2nix",
-          "crate2nix_stable",
-          "nixpkgs"
-        ]
-      },
-      "locked": {
-        "lastModified": 1717408969,
-        "narHash": "sha256-Q0OEFqe35fZbbRPPRdrjTUUChKVhhWXz3T9ZSKmaoVY=",
-        "owner": "numtide",
-        "repo": "devshell",
-        "rev": "1ebbe68d57457c8cae98145410b164b5477761f4",
-        "type": "github"
-      },
-      "original": {
-        "owner": "numtide",
-        "repo": "devshell",
-        "type": "github"
-      }
-    },
-    "devshell_3": {
-      "inputs": {
-        "flake-utils": "flake-utils_4",
-        "nixpkgs": [
-          "sonnenshift",
-          "crate2nix",
-          "nixpkgs"
-        ]
-      },
-      "locked": {
-        "lastModified": 1711099426,
-        "narHash": "sha256-HzpgM/wc3aqpnHJJ2oDqPBkNsqWbW0WfWUO8lKu8nGk=",
-        "owner": "numtide",
-        "repo": "devshell",
-        "rev": "2d45b54ca4a183f2fdcf4b19c895b64fbf620ee8",
-        "type": "github"
-      },
-      "original": {
-        "owner": "numtide",
-        "repo": "devshell",
-        "type": "github"
-      }
-    },
     "disko": {
       "inputs": {
         "nixpkgs": [
@@ -300,11 +27,11 @@
         ]
       },
       "locked": {
-        "lastModified": 1747742835,
-        "narHash": "sha256-kYL4GCwwznsypvsnA20oyvW8zB/Dvn6K5G/tgMjVMT4=",
+        "lastModified": 1714103775,
+        "narHash": "sha256-kcBiIrmqzt3bNTr2GMBfAyA+on8BEKO1iKzzDFQZkjI=",
         "owner": "nix-community",
         "repo": "disko",
-        "rev": "df522e787fdffc4f32ed3e1fca9ed0968a384d62",
+        "rev": "285e26465a0bae510897ca04da26ce6307c652b4",
         "type": "github"
       },
       "original": {
@@ -315,16 +42,16 @@
     },
     "dream2nix": {
       "inputs": {
-        "nixpkgs": "nixpkgs_8",
+        "nixpkgs": "nixpkgs_3",
         "purescript-overlay": "purescript-overlay",
         "pyproject-nix": "pyproject-nix"
       },
       "locked": {
-        "lastModified": 1735160684,
-        "narHash": "sha256-n5CwhmqKxifuD4Sq4WuRP/h5LO6f23cGnSAuJemnd/4=",
+        "lastModified": 1702457430,
+        "narHash": "sha256-8NQiXtYCOiC7XFayy6GPGDudCBrPROry3mfWjpdVj5g=",
         "owner": "nix-community",
         "repo": "dream2nix",
-        "rev": "8ce6284ff58208ed8961681276f82c2f8f978ef4",
+        "rev": "262198033e23e9ee832f0cc8133d38f07598f555",
         "type": "github"
       },
       "original": {
@@ -342,11 +69,11 @@
         "rust-analyzer-src": "rust-analyzer-src"
       },
       "locked": {
-        "lastModified": 1737181903,
-        "narHash": "sha256-lvp77MhGzSN+ICd0MugppCjQR6cmlM2iAC5cjy2ZsaA=",
+        "lastModified": 1704003651,
+        "narHash": "sha256-bA3d4E1CX5G7TVbKwJOm9jZfVOGOPp6u5CKEUzNsE8E=",
         "owner": "nix-community",
         "repo": "fenix",
-        "rev": "ac79bb490b8c1af4bbc587b84c76f9527d6b14f7",
+        "rev": "c6d82e087ac96f24b90c5787a17e29a72566c2b4",
         "type": "github"
       },
       "original": {
@@ -372,48 +99,6 @@
       }
     },
     "flake-compat_2": {
-      "locked": {
-        "lastModified": 1696426674,
-        "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
-        "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
-        "revCount": 57,
-        "type": "tarball",
-        "url": "https://api.flakehub.com/f/pinned/edolstra/flake-compat/1.0.1/018afb31-abd1-7bff-a5e4-cff7e18efb7a/source.tar.gz"
-      },
-      "original": {
-        "type": "tarball",
-        "url": "https://flakehub.com/f/edolstra/flake-compat/1.tar.gz"
-      }
-    },
-    "flake-compat_3": {
-      "locked": {
-        "lastModified": 1696426674,
-        "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
-        "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
-        "revCount": 57,
-        "type": "tarball",
-        "url": "https://api.flakehub.com/f/pinned/edolstra/flake-compat/1.0.1/018afb31-abd1-7bff-a5e4-cff7e18efb7a/source.tar.gz"
-      },
-      "original": {
-        "type": "tarball",
-        "url": "https://flakehub.com/f/edolstra/flake-compat/1.tar.gz"
-      }
-    },
-    "flake-compat_4": {
-      "locked": {
-        "lastModified": 1696426674,
-        "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
-        "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
-        "revCount": 57,
-        "type": "tarball",
-        "url": "https://api.flakehub.com/f/pinned/edolstra/flake-compat/1.0.1/018afb31-abd1-7bff-a5e4-cff7e18efb7a/source.tar.gz"
-      },
-      "original": {
-        "type": "tarball",
-        "url": "https://flakehub.com/f/edolstra/flake-compat/1.tar.gz"
-      }
-    },
-    "flake-compat_5": {
       "flake": false,
       "locked": {
         "lastModified": 1696426674,
@@ -429,151 +114,10 @@
         "type": "github"
       }
     },
-    "flake-parts": {
-      "inputs": {
-        "nixpkgs-lib": [
-          "sonnenshift",
-          "crate2nix",
-          "crate2nix_stable",
-          "crate2nix_stable",
-          "nixpkgs"
-        ]
-      },
-      "locked": {
-        "lastModified": 1719745305,
-        "narHash": "sha256-xwgjVUpqSviudEkpQnioeez1Uo2wzrsMaJKJClh+Bls=",
-        "owner": "hercules-ci",
-        "repo": "flake-parts",
-        "rev": "c3c5ecc05edc7dafba779c6c1a61cd08ac6583e9",
-        "type": "github"
-      },
-      "original": {
-        "owner": "hercules-ci",
-        "repo": "flake-parts",
-        "type": "github"
-      }
-    },
-    "flake-parts_2": {
-      "inputs": {
-        "nixpkgs-lib": [
-          "sonnenshift",
-          "crate2nix",
-          "crate2nix_stable",
-          "nixpkgs"
-        ]
-      },
-      "locked": {
-        "lastModified": 1719745305,
-        "narHash": "sha256-xwgjVUpqSviudEkpQnioeez1Uo2wzrsMaJKJClh+Bls=",
-        "owner": "hercules-ci",
-        "repo": "flake-parts",
-        "rev": "c3c5ecc05edc7dafba779c6c1a61cd08ac6583e9",
-        "type": "github"
-      },
-      "original": {
-        "owner": "hercules-ci",
-        "repo": "flake-parts",
-        "type": "github"
-      }
-    },
-    "flake-parts_3": {
-      "inputs": {
-        "nixpkgs-lib": [
-          "sonnenshift",
-          "crate2nix",
-          "nixpkgs"
-        ]
-      },
-      "locked": {
-        "lastModified": 1712014858,
-        "narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=",
-        "owner": "hercules-ci",
-        "repo": "flake-parts",
-        "rev": "9126214d0a59633752a136528f5f3b9aa8565b7d",
-        "type": "github"
-      },
-      "original": {
-        "owner": "hercules-ci",
-        "repo": "flake-parts",
-        "type": "github"
-      }
-    },
     "flake-utils": {
       "inputs": {
         "systems": "systems_2"
       },
-      "locked": {
-        "lastModified": 1694529238,
-        "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=",
-        "owner": "numtide",
-        "repo": "flake-utils",
-        "rev": "ff7b65b44d01cf9ba6a71320833626af21126384",
-        "type": "github"
-      },
-      "original": {
-        "owner": "numtide",
-        "repo": "flake-utils",
-        "type": "github"
-      }
-    },
-    "flake-utils_2": {
-      "inputs": {
-        "systems": "systems_3"
-      },
-      "locked": {
-        "lastModified": 1701680307,
-        "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
-        "owner": "numtide",
-        "repo": "flake-utils",
-        "rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
-        "type": "github"
-      },
-      "original": {
-        "owner": "numtide",
-        "repo": "flake-utils",
-        "type": "github"
-      }
-    },
-    "flake-utils_3": {
-      "inputs": {
-        "systems": "systems_4"
-      },
-      "locked": {
-        "lastModified": 1701680307,
-        "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
-        "owner": "numtide",
-        "repo": "flake-utils",
-        "rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
-        "type": "github"
-      },
-      "original": {
-        "owner": "numtide",
-        "repo": "flake-utils",
-        "type": "github"
-      }
-    },
-    "flake-utils_4": {
-      "inputs": {
-        "systems": "systems_5"
-      },
-      "locked": {
-        "lastModified": 1701680307,
-        "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
-        "owner": "numtide",
-        "repo": "flake-utils",
-        "rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
-        "type": "github"
-      },
-      "original": {
-        "owner": "numtide",
-        "repo": "flake-utils",
-        "type": "github"
-      }
-    },
-    "flake-utils_5": {
-      "inputs": {
-        "systems": "systems_6"
-      },
       "locked": {
         "lastModified": 1710146030,
         "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
@@ -588,6 +132,24 @@
         "type": "github"
       }
     },
+    "flake-utils_2": {
+      "inputs": {
+        "systems": "systems_3"
+      },
+      "locked": {
+        "lastModified": 1705309234,
+        "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=",
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26",
+        "type": "github"
+      },
+      "original": {
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "type": "github"
+      }
+    },
     "foundryvtt": {
       "inputs": {
         "nixpkgs": [
@@ -595,11 +157,11 @@
         ]
       },
       "locked": {
-        "lastModified": 1746877938,
-        "narHash": "sha256-N9J96pSPg4vbozV+ZZ++dwLnMIf2Le6ONNMO0kZCj1M=",
+        "lastModified": 1712623723,
+        "narHash": "sha256-jPD5+M+QPyMRk52zfFMIeHdv7yXYJ/yNGqwS0PhYF+E=",
         "owner": "reckenrode",
         "repo": "nix-foundryvtt",
-        "rev": "f1b401831d796dd94cf5a11b65fd169a199d4ff0",
+        "rev": "6025615b431170558c3c13f16b549fc0126425e1",
         "type": "github"
       },
       "original": {
@@ -608,123 +170,25 @@
         "type": "github"
       }
     },
-    "gitignore": {
+    "nix-github-actions": {
       "inputs": {
         "nixpkgs": [
           "sonnenshift",
-          "crate2nix",
-          "crate2nix_stable",
-          "crate2nix_stable",
-          "pre-commit-hooks",
+          "poetry2nixi",
           "nixpkgs"
         ]
       },
       "locked": {
-        "lastModified": 1709087332,
-        "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
-        "owner": "hercules-ci",
-        "repo": "gitignore.nix",
-        "rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
+        "lastModified": 1703863825,
+        "narHash": "sha256-rXwqjtwiGKJheXB43ybM8NwWB8rO2dSRrEqes0S7F5Y=",
+        "owner": "nix-community",
+        "repo": "nix-github-actions",
+        "rev": "5163432afc817cf8bd1f031418d1869e4c9d5547",
         "type": "github"
       },
       "original": {
-        "owner": "hercules-ci",
-        "repo": "gitignore.nix",
-        "type": "github"
-      }
-    },
-    "gitignore_2": {
-      "inputs": {
-        "nixpkgs": [
-          "sonnenshift",
-          "crate2nix",
-          "crate2nix_stable",
-          "pre-commit-hooks",
-          "nixpkgs"
-        ]
-      },
-      "locked": {
-        "lastModified": 1709087332,
-        "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
-        "owner": "hercules-ci",
-        "repo": "gitignore.nix",
-        "rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
-        "type": "github"
-      },
-      "original": {
-        "owner": "hercules-ci",
-        "repo": "gitignore.nix",
-        "type": "github"
-      }
-    },
-    "gitignore_3": {
-      "inputs": {
-        "nixpkgs": [
-          "sonnenshift",
-          "crate2nix",
-          "pre-commit-hooks",
-          "nixpkgs"
-        ]
-      },
-      "locked": {
-        "lastModified": 1709087332,
-        "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
-        "owner": "hercules-ci",
-        "repo": "gitignore.nix",
-        "rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
-        "type": "github"
-      },
-      "original": {
-        "owner": "hercules-ci",
-        "repo": "gitignore.nix",
-        "type": "github"
-      }
-    },
-    "nix-test-runner": {
-      "flake": false,
-      "locked": {
-        "lastModified": 1588761593,
-        "narHash": "sha256-FKJykltAN/g3eIceJl4SfDnnyuH2jHImhMrXS2KvGIs=",
-        "owner": "stoeffel",
-        "repo": "nix-test-runner",
-        "rev": "c45d45b11ecef3eb9d834c3b6304c05c49b06ca2",
-        "type": "github"
-      },
-      "original": {
-        "owner": "stoeffel",
-        "repo": "nix-test-runner",
-        "type": "github"
-      }
-    },
-    "nix-test-runner_2": {
-      "flake": false,
-      "locked": {
-        "lastModified": 1588761593,
-        "narHash": "sha256-FKJykltAN/g3eIceJl4SfDnnyuH2jHImhMrXS2KvGIs=",
-        "owner": "stoeffel",
-        "repo": "nix-test-runner",
-        "rev": "c45d45b11ecef3eb9d834c3b6304c05c49b06ca2",
-        "type": "github"
-      },
-      "original": {
-        "owner": "stoeffel",
-        "repo": "nix-test-runner",
-        "type": "github"
-      }
-    },
-    "nix-test-runner_3": {
-      "flake": false,
-      "locked": {
-        "lastModified": 1588761593,
-        "narHash": "sha256-FKJykltAN/g3eIceJl4SfDnnyuH2jHImhMrXS2KvGIs=",
-        "owner": "stoeffel",
-        "repo": "nix-test-runner",
-        "rev": "c45d45b11ecef3eb9d834c3b6304c05c49b06ca2",
-        "type": "github"
-      },
-      "original": {
-        "owner": "stoeffel",
-        "repo": "nix-test-runner",
+        "owner": "nix-community",
+        "repo": "nix-github-actions",
         "type": "github"
       }
     },
@@ -744,103 +208,61 @@
         "type": "github"
       }
     },
-    "nixpkgs_2": {
+    "nixpkgs-stable": {
       "locked": {
-        "lastModified": 1748085680,
-        "narHash": "sha256-XG90Q/040NiV70gAVvoYbXg1lULbiwIzfkWmwSINyGQ=",
+        "lastModified": 1713638189,
+        "narHash": "sha256-q7APLfB6FmmSMI1Su5ihW9IwntBsk2hWNXh8XtSdSIk=",
+        "owner": "NixOS",
+        "repo": "nixpkgs",
+        "rev": "74574c38577914733b4f7a775dd77d24245081dd",
+        "type": "github"
+      },
+      "original": {
+        "owner": "NixOS",
+        "ref": "release-23.11",
+        "repo": "nixpkgs",
+        "type": "github"
+      }
+    },
+    "nixpkgs-unstable": {
+      "locked": {
+        "lastModified": 1714253743,
+        "narHash": "sha256-mdTQw2XlariysyScCv2tTE45QSU9v/ezLcHJ22f0Nxc=",
         "owner": "nixos",
         "repo": "nixpkgs",
-        "rev": "4e6eeca5ed45465087274fc9dc6bc2011254a0f3",
+        "rev": "58a1abdbae3217ca6b702f03d3b35125d88a2994",
         "type": "github"
       },
       "original": {
         "owner": "nixos",
-        "ref": "nixos-25.05-small",
+        "ref": "nixos-unstable",
+        "repo": "nixpkgs",
+        "type": "github"
+      }
+    },
+    "nixpkgs_2": {
+      "locked": {
+        "lastModified": 1718208800,
+        "narHash": "sha256-US1tAChvPxT52RV8GksWZS415tTS7PV42KTc2PNDBmc=",
+        "owner": "nixos",
+        "repo": "nixpkgs",
+        "rev": "cc54fb41d13736e92229c21627ea4f22199fee6b",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nixos",
+        "ref": "nixos-24.05",
         "repo": "nixpkgs",
         "type": "github"
       }
     },
     "nixpkgs_3": {
       "locked": {
-        "lastModified": 1700612854,
-        "narHash": "sha256-yrQ8osMD+vDLGFX7pcwsY/Qr5PUd6OmDMYJZzZi0+zc=",
+        "lastModified": 1702272962,
+        "narHash": "sha256-D+zHwkwPc6oYQ4G3A1HuadopqRwUY/JkMwHz1YF7j4Q=",
         "owner": "NixOS",
         "repo": "nixpkgs",
-        "rev": "19cbff58383a4ae384dea4d1d0c823d72b49d614",
-        "type": "github"
-      },
-      "original": {
-        "owner": "NixOS",
-        "ref": "nixos-unstable",
-        "repo": "nixpkgs",
-        "type": "github"
-      }
-    },
-    "nixpkgs_4": {
-      "locked": {
-        "lastModified": 1715534503,
-        "narHash": "sha256-5ZSVkFadZbFP1THataCaSf0JH2cAH3S29hU9rrxTEqk=",
-        "owner": "NixOS",
-        "repo": "nixpkgs",
-        "rev": "2057814051972fa1453ddfb0d98badbea9b83c06",
-        "type": "github"
-      },
-      "original": {
-        "owner": "NixOS",
-        "ref": "nixos-unstable",
-        "repo": "nixpkgs",
-        "type": "github"
-      }
-    },
-    "nixpkgs_5": {
-      "locked": {
-        "lastModified": 1715534503,
-        "narHash": "sha256-5ZSVkFadZbFP1THataCaSf0JH2cAH3S29hU9rrxTEqk=",
-        "owner": "NixOS",
-        "repo": "nixpkgs",
-        "rev": "2057814051972fa1453ddfb0d98badbea9b83c06",
-        "type": "github"
-      },
-      "original": {
-        "owner": "NixOS",
-        "ref": "nixos-unstable",
-        "repo": "nixpkgs",
-        "type": "github"
-      }
-    },
-    "nixpkgs_6": {
-      "locked": {
-        "lastModified": 1719506693,
-        "narHash": "sha256-C8e9S7RzshSdHB7L+v9I51af1gDM5unhJ2xO1ywxNH8=",
-        "path": "/nix/store/4p0avw1s3vf27hspgqsrqs37gxk4i83i-source",
-        "rev": "b2852eb9365c6de48ffb0dc2c9562591f652242a",
-        "type": "path"
-      },
-      "original": {
-        "id": "nixpkgs",
-        "type": "indirect"
-      }
-    },
-    "nixpkgs_7": {
-      "locked": {
-        "lastModified": 1719506693,
-        "narHash": "sha256-C8e9S7RzshSdHB7L+v9I51af1gDM5unhJ2xO1ywxNH8=",
-        "path": "/nix/store/4p0avw1s3vf27hspgqsrqs37gxk4i83i-source",
-        "rev": "b2852eb9365c6de48ffb0dc2c9562591f652242a",
-        "type": "path"
-      },
-      "original": {
-        "id": "nixpkgs",
-        "type": "indirect"
-      }
-    },
-    "nixpkgs_8": {
-      "locked": {
-        "lastModified": 1729850857,
-        "narHash": "sha256-WvLXzNNnnw+qpFOmgaM3JUlNEH+T4s22b5i2oyyCpXE=",
-        "owner": "NixOS",
-        "repo": "nixpkgs",
-        "rev": "41dea55321e5a999b17033296ac05fe8a8b5a257",
+        "rev": "e97b3e4186bcadf0ef1b6be22b8558eab1cdeb5d",
         "type": "github"
       },
       "original": {
@@ -850,118 +272,55 @@
         "type": "github"
       }
     },
-    "pre-commit-hooks": {
+    "nvfetcher": {
       "inputs": {
-        "flake-compat": [
-          "sonnenshift",
-          "crate2nix",
-          "crate2nix_stable",
-          "crate2nix_stable",
-          "flake-compat"
-        ],
-        "gitignore": "gitignore",
+        "flake-compat": "flake-compat_2",
+        "flake-utils": "flake-utils",
         "nixpkgs": [
-          "sonnenshift",
-          "crate2nix",
-          "crate2nix_stable",
-          "crate2nix_stable",
-          "nixpkgs"
-        ],
-        "nixpkgs-stable": [
-          "sonnenshift",
-          "crate2nix",
-          "crate2nix_stable",
-          "crate2nix_stable",
           "nixpkgs"
         ]
       },
       "locked": {
-        "lastModified": 1719259945,
-        "narHash": "sha256-F1h+XIsGKT9TkGO3omxDLEb/9jOOsI6NnzsXFsZhry4=",
-        "owner": "cachix",
-        "repo": "pre-commit-hooks.nix",
-        "rev": "0ff4381bbb8f7a52ca4a851660fc7a437a4c6e07",
+        "lastModified": 1713333471,
+        "narHash": "sha256-sIVQKOXzruxtTYiBRHZa8UQH+CSIa9K5MZlY6vavYfA=",
+        "owner": "berberman",
+        "repo": "nvfetcher",
+        "rev": "2a824322dc6a755ffda83a13b948d42304521e4d",
         "type": "github"
       },
       "original": {
-        "owner": "cachix",
-        "repo": "pre-commit-hooks.nix",
+        "owner": "berberman",
+        "repo": "nvfetcher",
         "type": "github"
       }
     },
-    "pre-commit-hooks_2": {
+    "poetry2nixi": {
       "inputs": {
-        "flake-compat": [
-          "sonnenshift",
-          "crate2nix",
-          "crate2nix_stable",
-          "flake-compat"
-        ],
-        "gitignore": "gitignore_2",
+        "flake-utils": "flake-utils_2",
+        "nix-github-actions": "nix-github-actions",
         "nixpkgs": [
           "sonnenshift",
-          "crate2nix",
-          "crate2nix_stable",
           "nixpkgs"
         ],
-        "nixpkgs-stable": [
-          "sonnenshift",
-          "crate2nix",
-          "crate2nix_stable",
-          "nixpkgs"
-        ]
+        "systems": "systems_4",
+        "treefmt-nix": "treefmt-nix"
       },
       "locked": {
-        "lastModified": 1719259945,
-        "narHash": "sha256-F1h+XIsGKT9TkGO3omxDLEb/9jOOsI6NnzsXFsZhry4=",
-        "owner": "cachix",
-        "repo": "pre-commit-hooks.nix",
-        "rev": "0ff4381bbb8f7a52ca4a851660fc7a437a4c6e07",
+        "lastModified": 1708589824,
+        "narHash": "sha256-2GOiFTkvs5MtVF65sC78KNVxQSmsxtk0WmV1wJ9V2ck=",
+        "owner": "nix-community",
+        "repo": "poetry2nix",
+        "rev": "3c92540611f42d3fb2d0d084a6c694cd6544b609",
         "type": "github"
       },
       "original": {
-        "owner": "cachix",
-        "repo": "pre-commit-hooks.nix",
-        "type": "github"
-      }
-    },
-    "pre-commit-hooks_3": {
-      "inputs": {
-        "flake-compat": [
-          "sonnenshift",
-          "crate2nix",
-          "flake-compat"
-        ],
-        "flake-utils": "flake-utils_5",
-        "gitignore": "gitignore_3",
-        "nixpkgs": [
-          "sonnenshift",
-          "crate2nix",
-          "nixpkgs"
-        ],
-        "nixpkgs-stable": [
-          "sonnenshift",
-          "crate2nix",
-          "nixpkgs"
-        ]
-      },
-      "locked": {
-        "lastModified": 1712055707,
-        "narHash": "sha256-4XLvuSIDZJGS17xEwSrNuJLL7UjDYKGJSbK1WWX2AK8=",
-        "owner": "cachix",
-        "repo": "pre-commit-hooks.nix",
-        "rev": "e35aed5fda3cc79f88ed7f1795021e559582093a",
-        "type": "github"
-      },
-      "original": {
-        "owner": "cachix",
-        "repo": "pre-commit-hooks.nix",
+        "owner": "nix-community",
+        "repo": "poetry2nix",
         "type": "github"
       }
     },
     "purescript-overlay": {
       "inputs": {
-        "flake-compat": "flake-compat_5",
         "nixpkgs": [
           "tlaternet-webserver",
           "dream2nix",
@@ -970,11 +329,11 @@
         "slimlock": "slimlock"
       },
       "locked": {
-        "lastModified": 1728546539,
-        "narHash": "sha256-Sws7w0tlnjD+Bjck1nv29NjC5DbL6nH5auL9Ex9Iz2A=",
+        "lastModified": 1696022621,
+        "narHash": "sha256-eMjFmsj2G1E0Q5XiibUNgFjTiSz0GxIeSSzzVdoN730=",
         "owner": "thomashoneyman",
         "repo": "purescript-overlay",
-        "rev": "4ad4c15d07bd899d7346b331f377606631eb0ee4",
+        "rev": "047c7933abd6da8aa239904422e22d190ce55ead",
         "type": "github"
       },
       "original": {
@@ -1006,6 +365,8 @@
         "disko": "disko",
         "foundryvtt": "foundryvtt",
         "nixpkgs": "nixpkgs_2",
+        "nixpkgs-unstable": "nixpkgs-unstable",
+        "nvfetcher": "nvfetcher",
         "sonnenshift": "sonnenshift",
         "sops-nix": "sops-nix",
         "tlaternet-webserver": "tlaternet-webserver"
@@ -1014,11 +375,11 @@
     "rust-analyzer-src": {
       "flake": false,
       "locked": {
-        "lastModified": 1737140097,
-        "narHash": "sha256-m4SN8DeKzsP10EQFS7+2zgGfCrMhTfTt1H0QRNesD08=",
+        "lastModified": 1703965384,
+        "narHash": "sha256-3iyouqkBvhh/E48TkBlt4JmmcIEyfQwY7pokKBx9WNg=",
         "owner": "rust-lang",
         "repo": "rust-analyzer",
-        "rev": "f61bfa4d7feb84d07538d361fe77d34a29e3b375",
+        "rev": "e872f5085cf5b0e44558442365c1c033d486eff2",
         "type": "github"
       },
       "original": {
@@ -1038,11 +399,11 @@
         ]
       },
       "locked": {
-        "lastModified": 1688756706,
-        "narHash": "sha256-xzkkMv3neJJJ89zo3o2ojp7nFeaZc2G0fYwNXNJRFlo=",
+        "lastModified": 1688610262,
+        "narHash": "sha256-Wg0ViDotFWGWqKIQzyYCgayeH8s4U1OZcTiWTQYdAp4=",
         "owner": "thomashoneyman",
         "repo": "slimlock",
-        "rev": "cf72723f59e2340d24881fd7bf61cb113b4c407c",
+        "rev": "b5c6cdcaf636ebbebd0a1f32520929394493f1a6",
         "type": "github"
       },
       "original": {
@@ -1053,21 +414,22 @@
     },
     "sonnenshift": {
       "inputs": {
-        "crate2nix": "crate2nix",
         "nixpkgs": [
           "nixpkgs"
-        ]
+        ],
+        "poetry2nixi": "poetry2nixi"
       },
       "locked": {
-        "lastModified": 1740082109,
-        "narHash": "sha256-WdRNkwsIotFOSymee/yQyH46RmYtuxd1FENhvGL4KRc=",
-        "ref": "refs/heads/main",
-        "rev": "a1b48cf2ba194054e2d8816c94a84cebc4fb5de0",
-        "revCount": 23,
+        "lastModified": 1712437610,
+        "narHash": "sha256-U0xIqSupo6yBVWQcHIcLPDKg5ShQIkV/0Ay+Xg3GIlk=",
+        "ref": "tlater/implement-nix-module",
+        "rev": "43c4f591328f194ca989b86cea49773104f75549",
+        "revCount": 7,
         "type": "git",
         "url": "ssh://git@github.com/sonnenshift/battery-manager"
       },
       "original": {
+        "ref": "tlater/implement-nix-module",
         "type": "git",
         "url": "ssh://git@github.com/sonnenshift/battery-manager"
       }
@@ -1076,14 +438,15 @@
       "inputs": {
         "nixpkgs": [
           "nixpkgs"
-        ]
+        ],
+        "nixpkgs-stable": "nixpkgs-stable"
       },
       "locked": {
-        "lastModified": 1747603214,
-        "narHash": "sha256-lAblXm0VwifYCJ/ILPXJwlz0qNY07DDYdLD+9H+Wc8o=",
+        "lastModified": 1713892811,
+        "narHash": "sha256-uIGmA2xq41vVFETCF1WW4fFWFT2tqBln+aXnWrvjGRE=",
         "owner": "Mic92",
         "repo": "sops-nix",
-        "rev": "8d215e1c981be3aa37e47aeabd4e61bb069548fd",
+        "rev": "f1b0adc27265274e3b0c9b872a8f476a098679bd",
         "type": "github"
       },
       "original": {
@@ -1147,39 +510,8 @@
         "type": "github"
       },
       "original": {
-        "owner": "nix-systems",
-        "repo": "default",
-        "type": "github"
-      }
-    },
-    "systems_5": {
-      "locked": {
-        "lastModified": 1681028828,
-        "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
-        "owner": "nix-systems",
-        "repo": "default",
-        "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
-        "type": "github"
-      },
-      "original": {
-        "owner": "nix-systems",
-        "repo": "default",
-        "type": "github"
-      }
-    },
-    "systems_6": {
-      "locked": {
-        "lastModified": 1681028828,
-        "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
-        "owner": "nix-systems",
-        "repo": "default",
-        "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
-        "type": "github"
-      },
-      "original": {
-        "owner": "nix-systems",
-        "repo": "default",
-        "type": "github"
+        "id": "systems",
+        "type": "indirect"
       }
     },
     "tlaternet-webserver": {
@@ -1191,11 +523,11 @@
         ]
       },
       "locked": {
-        "lastModified": 1737271785,
-        "narHash": "sha256-yVdaaawYK1/q9V5btfGpxVCQBdyQx1WcFHYO0yX5bP8=",
+        "lastModified": 1704840002,
+        "narHash": "sha256-ik2LeuRjcnRXwBLoRSOyGEMXscE+coO8G79IFhZhdJk=",
         "ref": "refs/heads/master",
-        "rev": "5d3d84836101ec9b9867a5f754c9ee1b9d4dc538",
-        "revCount": 76,
+        "rev": "d14f50c8dcc8ab30a5e5fa907b392ac0df6c7b52",
+        "revCount": 73,
         "type": "git",
         "url": "https://gitea.tlater.net/tlaternet/tlaternet.git"
       },
@@ -1204,6 +536,28 @@
         "url": "https://gitea.tlater.net/tlaternet/tlaternet.git"
       }
     },
+    "treefmt-nix": {
+      "inputs": {
+        "nixpkgs": [
+          "sonnenshift",
+          "poetry2nixi",
+          "nixpkgs"
+        ]
+      },
+      "locked": {
+        "lastModified": 1708335038,
+        "narHash": "sha256-ETLZNFBVCabo7lJrpjD6cAbnE11eDOjaQnznmg/6hAE=",
+        "owner": "numtide",
+        "repo": "treefmt-nix",
+        "rev": "e504621290a1fd896631ddbc5e9c16f4366c9f65",
+        "type": "github"
+      },
+      "original": {
+        "owner": "numtide",
+        "repo": "treefmt-nix",
+        "type": "github"
+      }
+    },
     "utils": {
       "inputs": {
         "systems": "systems"
diff --git a/flake.nix b/flake.nix
index da8455f..09a74ac 100644
--- a/flake.nix
+++ b/flake.nix
@@ -2,7 +2,8 @@
   description = "tlater.net host configuration";
 
   inputs = {
-    nixpkgs.url = "github:nixos/nixpkgs/nixos-25.05-small";
+    nixpkgs.url = "github:nixos/nixpkgs/nixos-24.05";
+    nixpkgs-unstable.url = "github:nixos/nixpkgs/nixos-unstable";
     disko = {
       url = "github:nix-community/disko";
       inputs.nixpkgs.follows = "nixpkgs";
@@ -12,6 +13,10 @@
       url = "github:Mic92/sops-nix";
       inputs.nixpkgs.follows = "nixpkgs";
     };
+    nvfetcher = {
+      url = "github:berberman/nvfetcher";
+      inputs.nixpkgs.follows = "nixpkgs";
+    };
     tlaternet-webserver = {
       url = "git+https://gitea.tlater.net/tlaternet/tlaternet.git";
       inputs.nixpkgs.follows = "nixpkgs";
@@ -22,146 +27,131 @@
     };
 
     sonnenshift = {
-      url = "git+ssh://git@github.com/sonnenshift/battery-manager";
+      url = "git+ssh://git@github.com/sonnenshift/battery-manager?ref=tlater/implement-nix-module";
       inputs.nixpkgs.follows = "nixpkgs";
     };
   };
 
-  outputs =
-    {
-      self,
-      nixpkgs,
-      sops-nix,
-      deploy-rs,
-      ...
-    }@inputs:
-    let
-      system = "x86_64-linux";
-      pkgs = nixpkgs.legacyPackages.${system};
-
-      vm = nixpkgs.lib.nixosSystem {
+  outputs = {
+    self,
+    nixpkgs,
+    sops-nix,
+    nvfetcher,
+    deploy-rs,
+    ...
+  } @ inputs: let
+    system = "x86_64-linux";
+    pkgs = nixpkgs.legacyPackages.${system};
+  in {
+    ##################
+    # Configurations #
+    ##################
+    nixosConfigurations = {
+      # The actual system definition
+      hetzner-1 = nixpkgs.lib.nixosSystem {
         inherit system;
         specialArgs.flake-inputs = inputs;
 
         modules = [
           ./configuration
-          ./configuration/hardware-specific/vm.nix
-        ];
-      };
-    in
-    {
-      ##################
-      # Configurations #
-      ##################
-      nixosConfigurations = {
-        # The actual system definition
-        hetzner-1 = nixpkgs.lib.nixosSystem {
-          inherit system;
-          specialArgs.flake-inputs = inputs;
-
-          modules = [
-            ./configuration
-            ./configuration/hardware-specific/hetzner
-          ];
-        };
-      };
-
-      ############################
-      # Deployment configuration #
-      ############################
-      deploy.nodes = {
-        hetzner-1 = {
-          hostname = "116.202.158.55";
-
-          profiles.system = {
-            user = "root";
-            path = deploy-rs.lib.${system}.activate.nixos self.nixosConfigurations.hetzner-1;
-          };
-
-          sshUser = "tlater";
-          sshOpts = [
-            "-p"
-            "2222"
-            "-o"
-            "ForwardAgent=yes"
-          ];
-        };
-      };
-
-      #########
-      # Tests #
-      #########
-      checks.${system} = import ./checks (inputs // { inherit system; });
-
-      ###########################
-      # Garbage collection root #
-      ###########################
-
-      packages.${system} =
-        let
-          localPkgs = import ./pkgs { inherit pkgs; };
-        in
-        {
-          default = vm.config.system.build.vm;
-          crowdsec-hub = localPkgs.crowdsec.hub;
-          crowdsec-firewall-bouncer = localPkgs.crowdsec.firewall-bouncer;
-        };
-
-      ###################
-      # Utility scripts #
-      ###################
-      apps.${system} = {
-        default = self.apps.${system}.run-vm;
-
-        run-vm = {
-          type = "app";
-          program =
-            (pkgs.writeShellScript "" ''
-              ${vm.config.system.build.vm.outPath}/bin/run-testvm-vm
-            '').outPath;
-        };
-
-        update-crowdsec-packages =
-          let
-            git = pkgs.lib.getExe pkgs.git;
-            nvfetcher = pkgs.lib.getExe pkgs.nvfetcher;
-          in
-          {
-            type = "app";
-            program =
-              (pkgs.writeShellScript "update-crowdsec-packages" ''
-                cd "$(${git} rev-parse --show-toplevel)"
-                cd ./pkgs/crowdsec
-                ${nvfetcher}
-                echo 'Remember to update the vendorHash of any go packages!'
-              '').outPath;
-          };
-      };
-
-      ###########################
-      # Development environment #
-      ###########################
-      devShells.${system}.default = nixpkgs.legacyPackages.${system}.mkShell {
-        sopsPGPKeyDirs = [
-          "./keys/hosts/"
-          "./keys/users/"
-        ];
-        nativeBuildInputs = [ sops-nix.packages.${system}.sops-import-keys-hook ];
-
-        packages = with pkgs; [
-          sops-nix.packages.${system}.sops-init-gpg-key
-          deploy-rs.packages.${system}.default
-
-          nixpkgs-fmt
-
-          cargo
-          clippy
-          rustc
-          rustfmt
-          rust-analyzer
-          pkg-config
-          openssl
+          ./configuration/hardware-specific/hetzner
         ];
       };
     };
+
+    ############################
+    # Deployment configuration #
+    ############################
+    deploy.nodes = {
+      hetzner-1 = {
+        hostname = "116.202.158.55";
+
+        profiles.system = {
+          user = "root";
+          path = deploy-rs.lib.${system}.activate.nixos self.nixosConfigurations.hetzner-1;
+        };
+
+        sshUser = "tlater";
+        sshOpts = ["-p" "2222" "-o" "ForwardAgent=yes"];
+      };
+    };
+
+    #########
+    # Tests #
+    #########
+    checks = builtins.mapAttrs (system: deployLib: deployLib.deployChecks self.deploy) deploy-rs.lib;
+
+    ###################
+    # Utility scripts #
+    ###################
+    apps.${system} = {
+      default = self.apps.${system}.run-vm;
+
+      run-vm = {
+        type = "app";
+        program = let
+          vm = nixpkgs.lib.nixosSystem {
+            inherit system;
+            specialArgs.flake-inputs = inputs;
+
+            modules = [
+              ./configuration
+              ./configuration/hardware-specific/vm.nix
+            ];
+          };
+        in
+          (pkgs.writeShellScript "" ''
+            ${vm.config.system.build.vm.outPath}/bin/run-testvm-vm
+          '')
+          .outPath;
+      };
+
+      update-pkgs = {
+        type = "app";
+        program = let
+          nvfetcher-bin = "${nvfetcher.packages.${system}.default}/bin/nvfetcher";
+        in
+          (pkgs.writeShellScript "update-pkgs" ''
+            cd "$(git rev-parse --show-toplevel)/pkgs"
+            ${nvfetcher-bin} -o _sources_pkgs -c nvfetcher.toml
+          '')
+          .outPath;
+      };
+
+      update-nextcloud-apps = {
+        type = "app";
+        program = let
+          nvfetcher-bin = "${nvfetcher.packages.${system}.default}/bin/nvfetcher";
+        in
+          (pkgs.writeShellScript "update-nextcloud-apps" ''
+            cd "$(git rev-parse --show-toplevel)/pkgs"
+            ${nvfetcher-bin} -o _sources_nextcloud -c nextcloud-apps.toml
+          '')
+          .outPath;
+      };
+    };
+
+    ###########################
+    # Development environment #
+    ###########################
+    devShells.${system}.default = nixpkgs.legacyPackages.${system}.mkShell {
+      sopsPGPKeyDirs = ["./keys/hosts/" "./keys/users/"];
+      nativeBuildInputs = [
+        sops-nix.packages.${system}.sops-import-keys-hook
+      ];
+
+      packages = with pkgs; [
+        sops-nix.packages.${system}.sops-init-gpg-key
+        deploy-rs.packages.${system}.default
+
+        cargo
+        clippy
+        rustc
+        rustfmt
+        rust-analyzer
+        pkg-config
+        openssl
+      ];
+    };
+  };
 }
diff --git a/keys/production.yaml b/keys/production.yaml
index 0e906bd..43b3578 100644
--- a/keys/production.yaml
+++ b/keys/production.yaml
@@ -1,6 +1,6 @@
-porkbun:
-    api-key: ENC[AES256_GCM,data:p3lqvGc8m2U/12rBPjoNR7hxQyD52CyEen/V8q59k5CSJZSqzZS8M5vEXFBsUMjz2lrmKM4pgtz4wa2fWK6Ty4LJCaI=,iv:OQC3FpwTtPmqHvDbA41mWF7LGYwC/jD2ZMBsE8ktNOg=,tag:kq5hUR7TBgczuGcXpsdu2A==,type:str]
-    secret-api-key: ENC[AES256_GCM,data:zV5PTKf45Zab8uW8mbuXmPNzciq6tV9OF0wUND7YnRk/DjZneYWItAsNBVoM+iHA+XsUPDoeKo6hoJiGkH/cCQ8WvuM=,iv:yr1M5DlgI8k6BgzNz3HRnqspHOrQuf2PmoZS1HGp0v8=,tag:JkNNziMMfKFZV2hnx5lXRg==,type:str]
+authelia:
+    storageEncryptionKey: ENC[AES256_GCM,data:OUCC+6Gcr6U7Mub1+DaIyswTV6da1wd1u0WGEm4wpJ8L0mi7WSpEmVjH79YyRhp7AmiZhdFFDXFeEYthBb2AZl+xoS9gqs6rWyfU4ezaCbXBiS/dIhsA5foPg13wq5A33qJWtPTy7DJEgqHaIonnaBuVJIBwH3wzPTHc3bDvBo4=,iv:intiZzngz5cMTtjEI9rTKMW0Xv3KB3ZEgtYN3amwKCE=,tag:AKxfbeZlPs54esHCsVnNCg==,type:str]
+    sessionSecret: ENC[AES256_GCM,data:GEMWhBltOIOs0g9FsWk3OQGs6dMcbwz3ZuhlyBFYROylsIZb4xTXWLgNwIpHwQukQU3TgvIxbCW/fGRWiALPanE2koSVAHNx0UU0hj1mVNRFQGK4H3EL10tPp7l4PofrcdeCbLPrOwM/xLOuPt+52sKlcbL2Awz5/MmpUVpCKXc=,iv:kWX2ptOpTgW3obBgri0MvVv6gCEPR3o77sldOXFQeks=,tag:je4pqLcEOhuBTQkoZHYNCw==,type:str]
 battery-manager:
     email: ENC[AES256_GCM,data:rYLUACXR/n+bLBmZ,iv:sUBEkh2+7qGjHZ5R23e/hoCiyTA7GTL4bJvXmxjZ5Sw=,tag:fdPMllaQQfRgX0WZKIre4g==,type:str]
     password: ENC[AES256_GCM,data:7cokZa6Q6ahSeiFPz+cV,iv:vz405P0IcG9FsAQXlY7mi78GuushQUKJm2irG6buGzc=,tag:JLHG2jTkJDGbinAq9dXRsQ==,type:str]
@@ -16,9 +16,6 @@ steam:
 heisenbridge:
     as-token: ENC[AES256_GCM,data:+2yo6T18j34622H8ZWblAFB2phLw1q0k0vUQEZ5sFj7dQaRnkEiAMi0R3p17Zq0pOtGEC0RRZuPLYkcZ1oKP0w==,iv:lGwrQYp//FufpmJocrLIVyy9RK7lEEVcpAi0wmkjr34=,tag:yV06UbhAYJQz36O2XdhY+A==,type:str]
     hs-token: ENC[AES256_GCM,data:u52WpkQFd/J7JFoE/rfNluebyZQLOokvkVdL7+AEAvrhJhrkJli1ztkD79lbC+6tGUH4tT3T+nX9wvGKnrRUQg==,iv:as+9fVuvMg2IoE2WIKD9mHi+znhNcWRh5Zq+yr0xcDQ=,tag:mZ7fh7U0MfgI8hyq/28Bcg==,type:str]
-matrix-hookshot:
-    as-token: ENC[AES256_GCM,data:nXTanPhDyDF7R3AllLqpM5dzljBrHwlh1KJnTGIi5PhbDY2lPj4+uXkMEwvm1u+hQjPyM7vKZPfK+0/dms6Y7A==,iv:fSakJN+yai0gfOJKFxxaxgyUtk0pNmIeqVgrdq92/24=,tag:Qc7+SUnm5/Nq5+QIScR9kQ==,type:str]
-    hs-token: ENC[AES256_GCM,data:Bwyj0JTTN0NNnwOs1zA8CqbtZSNcvlINeT7QVc2eJiHda92J6vQk7bSxy6KuqCN9DxlUsK13ggYjNORY2vic5w==,iv:Npnp8arYQ3Yb6CXrnKgE03hD7ZjGINPa/DwFI8D+5tA=,tag:FqNE6yI0nF4puEUw9MGAjQ==,type:str]
 wireguard:
     server-key: ENC[AES256_GCM,data:mXb7ZznJHf5CgV8rI4uzPBATMRbmd7LimgtCkQM9kAjbIaGwUBqJZBN3fXs=,iv:3Po1Orinzov9rnEm9cLzgJY1PeD+5Jl9115MriABHh8=,tag:E/2CjDO1JCvJzxCnqKcNyw==,type:str]
 restic:
@@ -37,63 +34,63 @@ sops:
     azure_kv: []
     hc_vault: []
     age: []
-    lastmodified: "2025-02-07T19:44:49Z"
-    mac: ENC[AES256_GCM,data:+0hpd/E7GxK/27f2Itf0hDV+3Ga4gHb8xxLutJ32HLBWLZ5Y+dN03xgkz8jBTiM+BeHwS4gz70Cs9X3zLMHbosWVuIV9DLuRaHRq/IU9KiADwqmCySZALqCf3+T5QKZr3Qs4AZJHwaAXkRX9HbnRFriIAFDJW/BGdIHdoROquxY=,iv:TeXI8LGqHVa5wo61sGdNbZ2nJvSlPdgn9R3Lq5qUggU=,tag:TFort5wxVTdi9LMlMeT/DQ==,type:str]
+    lastmodified: "2024-04-11T23:38:56Z"
+    mac: ENC[AES256_GCM,data:GjIB0EbWsh4o+QoFSyIXgGYnNhRlvfSmue1LyTt6oUlIjNgODhdIB8px8LnRo0rmm/f1YHbDq2MFOxlgdm3PTNaqm/MoKyW3r/wuAeWADsYayQszLNxyhTMXcjWtfm6zCRIuc/+YyM44pXRfVrOZRAin9B6pmJZsRJwBAZpogbU=,iv:r/ZQZvrP0E9dOW5fhBH2I21Z0uv2e3njdEGmadxEALg=,tag:iZvbGTvRJFo80n8aoKSSmQ==,type:str]
     pgp:
-        - created_at: "2025-01-21T17:55:44Z"
+        - created_at: "2024-03-18T04:02:00Z"
           enc: |-
             -----BEGIN PGP MESSAGE-----
 
-            hQEMA7x7stsXx45CAQf6AjhhYCHhRvJx4xxiXPMTfpIpAvseB1CXVfuhfj2r2yrf
-            3HfJnNOSDBcmHdp9fiLJattqfsykcGisUDVIplCeA7cIJjH3sf8MuIJXDTLmvE0Z
-            BmV8LuwP/UPQSZzY3w1eTAoYT/by35gfJm4ofipft2qyIjQuBgOlrg/0swvBl7LK
-            47mI6mVdds8RutHw/xhJZeNjKF/5tADPJ2CHjOHbCQhLji13Kpm3yObOnM8K2SfV
-            P9uVudCFN/ZBiTlVkB7PsuitwZx1fW0SR3jcWxbRd17M2k2RAQQDUCqPKaoJ3T3f
-            r2ExwmyO4j7G2vkFv1RgQnhAoHqqRZ1nSjNw1+27MNJeAbT1ddia2TC5Q2zjRZY9
-            tRJi2pNZH9A424lpIBLnkPl5rpCR/UZ+bqhaQ9C2kFMldSldPn8dMiBy8XG0Trji
-            B6X44Q/0RCsJD3FS61GASIjaWdEX0DdSOdhtbtBLLQ==
-            =yLOx
+            hQEMA7x7stsXx45CAQf7BjF+HR3WKdMyAV6R1M0+lqDz6hBHKyGH7YBB/QZBqRbK
+            3hdABIwWUsqpHjleEOp/Gj0VhZqwagqHxK4Fp5G0r3QBupbAO8u/+DNI8wll0Nva
+            dlOh0Jqp4E17TkERMQL02rrQ1ZmpOYmPkCd2//xkmWAQ1LatHWeRVSRxQBuMtPQi
+            btrefcQNjQCvS9/60dp8oTu8nxlFA4iHCBQKNIKVGqQH7jkdIfMPdUILjCkCiyCc
+            h+OxlHZZnpU6U9A+hjMBinvCzebSkZh48VX/T33Kr+4b0CBr1gR9MSXKG9f2MPQP
+            PMl6rPvqSqG6ddN9QDI+0HEHYaRvxPIV8uDS36tVxNJeAQHB5/6Lt7hJdYWgwf5E
+            TLgbZ0IxB17++6K++GlaG8WHO65l1jzmkPlN+ZGcwnhibDxnZjP6kqGqDFcZP4ge
+            cnV0KnhYcC59IooQYrWKzAJex9rnwPo7MGKV6XwZOQ==
+            =Hy9T
             -----END PGP MESSAGE-----
-          fp: B9C7AE554602D82B1AC2CFD0BC7BB2DB17C78E42!
-        - created_at: "2025-01-21T17:55:44Z"
+          fp: 535B61015823443941C744DD12264F6BBDFABA89
+        - created_at: "2024-03-18T04:02:00Z"
           enc: |-
             -----BEGIN PGP MESSAGE-----
 
-            hQIMA9ahl2ynTH87AQ/9F/lyLXn60X5Slcd9I7VPdM6x4IjFI0UwMAhMVv0GeimD
-            9Mj1vKW+l1v0gFtqCGjANEEYpXMr109CES+m6z7dNXcIQeR6pyjW6uCTiPUK/ZO/
-            ZQyhJR0zsT15BTHc6c9Eso9YedsggpRAbjjVIlmrTJx52LfqYYGoRA0XqcLE6LUS
-            6v6KR/299zzSHsTFVon6wac9wY7i1XvL0Q7JiYSNV2cNADiCJdCIBw1UkaSVH1uz
-            sTVdoEoQ1m7hu9pgg4UksUzi9IV3xBpte82tlEK9p0M+RXK/02fjMTWcx3q5fnjN
-            pBu3HkVzLpAjAQHMQ/O2cgbjFmwgjp5APUhn3IBx5M3F2ypGICnAvsrjfzalTChN
-            nkCmTfLynNIL1bX7PViABX2Q/yXGzPqFDe96pPw2hGjOXmxjKxjDoLT2IHixYp4D
-            cxb5519/WmOMrFd4SJeGiUR2Ph/VBZBVGafRfGAKMzxi666igWjwSU0YSDYeEUW/
-            BalkkWoz/KQZ5HgQwL9dyp26/cjDtpIwe8jLKVNI6aWhZ2ZmBxFwNEB6fE1txOjf
-            ceIJAfm8y2qIolw3TpBAFk6s53jir17SrEux9VzfiEfFeQ0g5q7cjAs5HfhTOMfh
-            iuUnHcCFy27wd+8bPxEaRYR57u7hneenTn3BMuu1CZputDFJWRvweMZk3cH8tiXS
-            WAGpXw7aKGIOpxe7Ye5X+T3xvYCBN77aFQKkrOmHMWFCbkr2QfnwVanxmmL9BwxF
-            rf8pG+H0URxBAsy9RZzSC+dXugnwnNBse3wupXf5YkipLx3rX9gtz4Y=
-            =a7xO
+            hQIMA9ahl2ynTH87AQ/+LNXxC3acjs2+c38gHZRW6Am4XFx1t/4tfxIgaaK/Boq8
+            PGU5CFNOMDGv8u/cwyDbfNM7GuL5g7vrLmBXzSV5ErZqc6bJ0+ZCNPTRIxP1Vxph
+            tWiDIyTwuqUzxWpOlSzii2Sqhlp8CyiWzBe95eIr96XzDCCtfzyCZ0BYyKgpHHxB
+            BltH0/+0JZFiR19zvf6M99AHwM8OddRQkXav+mRIJQpA87ovVZcAv5skYGJgNCqN
+            55fbskuYmqEnloQCZVJ2+2ZXK5Qn/uq4fLJCiIdZm4YsctJnV5spzZIL+dcOty65
+            Plk77BWzLaU5UOKCBAJWrK8oZSTGOrp4VZqb62DuqMRejG0JXmneIVk7p79yn5eA
+            ANVMGRF4b8RP9YUhzE8HACFzQebKpUU8XKv9+qsmO9Le5jUhU3UQeCSSzT/T5Dr8
+            kLDNtmW+mliQnxFlKcVWq2JIG+HaQD1KLOAl0JBNCOSLif2ofaHahuZ15agbYeis
+            hyrBY92EhzqYXHk/Kzv4ff4r+WUs9NN7R4Gg+wfWvMcTtVfbi4Ht+pjjTtCZwK1C
+            M8JebQn0NZSpVi3e7Xaz1fQ5Tqrg8PHZtkYGoIHLRPJQwLn9PHYtGzC3rFAk+Fqq
+            5WWHELxfcsZ6DakAGSXPK/80QhEZkpGmKizTwrEde+7fpEPxjdzUqlmH3rv7mFzS
+            WAGSiBIMjLR6ofb65vpghbwh6gXkpCtgUyINRhx/D+Kj5Z4lGD1u1I05DT1xD6VJ
+            FAbnH7oZ3PJecoAXgRT05FndFA1xfPMCkugmec8ML/sEZt+c3kbrXaA=
+            =MqS3
             -----END PGP MESSAGE-----
           fp: 8a3737d48f1035fe6c3a0a8fd6a1976ca74c7f3b
-        - created_at: "2025-01-21T17:55:44Z"
+        - created_at: "2024-03-18T04:02:00Z"
           enc: |-
             -----BEGIN PGP MESSAGE-----
 
-            hQIMA0f3HaPlrXn5ARAAph6bpRqnO1TKEE7K6cMCxCA9k3xj7hknBdHN1KlIQkeb
-            LUOW9lvxh96q/fQ3NvZXpEQqZoEgdhnYTHgr/Hqx7VSksSicOkJLorrBLELjP1Xo
-            FKTxRYQmgZmgmBc0u7+lF2t0VLey6DfsC9ehY2LQcRuY9WxxCp2nDjjR4wrfmMKi
-            i/SbpaM9Q29DpdNpwkSlTe2sKr+uMDVA2rCrjpisSDiUBgPe2eJEYPQJ7DHFxB4N
-            26NHS+QtUgZhAA1DOGYZvrXzqcZSeAPGk8WXY44KA7o2iRFw+TczpF/VaMHRR5vN
-            M62IoXGqnMIBau+tsiE1JEVg60DIHpBjsEY/WUgbNX02zfaHcp1OenxU4p7W19Xn
-            Y8psPm7JNzmi3+nItB2i3+OLQrumk6VaT021ykterc52tR/3ejCzQCsfbqDLnc/6
-            r2SvNVDiKpxQ/iFyHb0uLHWy0Jx0lYwmMUrThe1f4k/+m9fEPrA5v/1kTGY5o1Iy
-            aTuHE19VtB6V+g1H6ZRPr8g8wn/3pg1nC2vUoRzT+oUudxrijrUH8SSlwhu59y1z
-            7SrdMmtqA1/JsGNMawCLWd63u+/3GC9LmZzK2h/bV/R4DG5f8tsCpy9BrFAHqTMs
-            lZ3SONUUeM3uMOggQ/JT26EVxECmNGYIX1TYLOxcqisRXLbfco5LEc6T8PJvdivS
-            WAFr6kBMdoeWhUT8MdT5AJBm9mo95A0WM8I14pGrszaezfgo9zc2zs0ebdLEjhI4
-            jlTf4tXgK3RG455CRWOd9OA3ukR83W7UW6LYjoNaDWHw2RXZlb+hv8E=
-            =+MFT
+            hQIMA0f3HaPlrXn5AQ/+N5NK5UJdtw5e7O9T4hfIhtMXci/og1cJiI64daSyNeDH
+            jq+CPJ8e73yiTPwu6wHHqfuEhlEuI6sJY0ZJVFU+h4SIBtG21veGEz7GzlYgBCJm
+            xvJHXjtM8AprqnFVO7Fj9QA/ik5QBP6ZpkOY7j8/qf1G7alOIne/MYRALXDvvIMH
+            HTWE+Y2N57yZK55Pokmdw10hawbrn/N1nt2Y7sa1+5TlRNtuA/+zLkXtEjRr5U3N
+            DId+hqCKgXDqKLBMkh4mZUTGOGsk8eeKAWwyPp9+8A5/0rfy+xOJYEjjHICXQMSE
+            zfe6qvj/fRJKGzT5lEzD+ZKHlR0zHEwGRfHqrVUTdPcPdKj3DZILjsoe5ba4VlAp
+            sS0CAYTg3YuWMT4iHuOQlY5IoQxHHrn7k8ox5iZULFecg58f6r6iJL3AepDYWAey
+            gtQXYBeaeCm5Ddwmd6TBVz8Q4bCVYIrHbVeAhSDkxfrWLc5UORggvLEWiXilGDJi
+            DzAv0MVHE2Wa3eOJLq05K2/LBqRBD1XYM3dcS6JSdFxWWMzvLdUOB4dAuPt9gpl9
+            liaA13Blw/ev+U4ADxptrl+QuYRbWz3z6rniYpluSrTbVCKFRoHXSGFPy5u8/N6O
+            QyjfoovIBxXKnbUq2kMoFa/qFpc1pDUn0sjQNsUBdtorAu3Up4icyoih7qwx2J3S
+            WAGB1jHWMfcsBJqPwjRYkqBf6MuwHAZWdd+zvj/fKfft9jtxLcCGOIM6QdfiWbl0
+            Wq4gHdH7OhSy+ZgRnaBRt/GAkzkHvfG68HfulviHZ1h2mrQN1y3mxpg=
+            =RCYB
             -----END PGP MESSAGE-----
           fp: 0af7641adb8aa843136cf6d047f71da3e5ad79f9
     unencrypted_suffix: _unencrypted
-    version: 3.9.2
+    version: 3.8.1
diff --git a/keys/staging.yaml b/keys/staging.yaml
index 876d60e..ebdd628 100644
--- a/keys/staging.yaml
+++ b/keys/staging.yaml
@@ -1,6 +1,7 @@
-porkbun:
-    api-key: ENC[AES256_GCM,data:A5J1sqwq6hs=,iv:77Mar3IX7mq7z7x6s9sSeGNVYc1Wv78HptJElEC7z3Q=,tag:eM/EF9TxKu+zcbJ1SYXiuA==,type:str]
-    secret-api-key: ENC[AES256_GCM,data:8Xv+jWYaWMI=,iv:li4tdY0pch5lksftMmfMVS729caAwfaacoztaQ49az0=,tag:KhfElBGzVH4ByFPfuQsdhw==,type:str]
+authelia:
+    storageEncryptionKey: ENC[AES256_GCM,data:8X6Zvq7ct1MkMH+lc5kTTHXOo6rGyhSzc3aWxGRA5tnBet+TGcENo0RYTYmactsPGVpTIUGGplaG7B7dqRPhkdDHhbCCZCm2nLaYjpVJ241DrpUNKHn8lvg/bMxUQ/Dvw76ByYuWN6bREr3XRaBztBSPzld8zTSYx71I0CKY7vk=,iv:cJSwfuVWO39qqKCGt2Mvw7pN8+hD6kRH9v4c/u4hLuk=,tag:YhdlXuX2ETxjb443RI8MsA==,type:str]
+    sessionSecret: ENC[AES256_GCM,data:dnoWmc4HND62w3jMXL+akncAEb61c/I70DgRytx55Wxcn4rMiswp6zCkRdsP4CkouTQ1lyAcQrubp5I8M9Kyow/KBMYz9dPkr4+2xJ9w0SEmAVhyPe2DFvYos3x0Uvx5S0B3o1mXoXqbg78e4w5yEIbALiJT8VPGrWK8Cl4nVPo=,iv:FHDXUW2DWUmEZzWUYkYduogdVOtvMlRH4/fVg05cZaI=,tag:u282WQnHpBsZGYJH7mFFKA==,type:str]
+    jwtSecret: ENC[AES256_GCM,data:0M3AyoMp+orrljl5NsxmthzrHMmu0REcz7+9fpFKbwwqV6KqlpgGddjYZIsTpHEWEq9zhZ2YWLJkMxKdDgROVHUFZGKut28JPSAjjY+1V0wxNBnfSCnxEv5BUw2+cCxcpCwYQyNfRK6SotTt8aqpxvda4oRXpzxV6SW7ogDjc6E=,iv:D57SynZkW2JuFyX6bpZYkxpR2KtkOmKaySg1Bxim0r8=,tag:JCPGZaumdHrtgcH16A7b+g==,type:str]
 battery-manager:
     email: ENC[AES256_GCM,data:LM/EGzWHfVQ=,iv:jFaoUQuUfuGoOyj/GFpdI8TerH/c8D9fjvio+IEt2Tc=,tag:IWLiN011JEnHRLIXWQgfmA==,type:str]
     password: ENC[AES256_GCM,data:SUxjqS7SJHM=,iv:LvdKk88S+nSImh6/ZezbFGLCUBu1Lpdu+neF2xyHdBg=,tag:rcMyZuW4FVNbcbz00wQKBg==,type:str]
@@ -16,9 +17,6 @@ steam:
 heisenbridge:
     as-token: ENC[AES256_GCM,data:tXbOeo7nv8I=,iv:wJAKcOXX9nGIw4n38ThOoj29u7dUWhsxSQG/p79JlEw=,tag:rTVaGS2UuWcea1uBa8YX2g==,type:str]
     hs-token: ENC[AES256_GCM,data:VBwvwomv0Xg=,iv:q6INtJ+rg+QiXj8uBdBzQYQZUBBXp+9odxDHwvu8Jxc=,tag:XKhm8nxygAkKaiVPJ2Fcdg==,type:str]
-matrix-hookshot:
-    as-token: ENC[AES256_GCM,data:uSUOo4f2KqA=,iv:Xb9G8Ecv6m59m51kDw2bOfq3SMJt4g9/6/EdH74R+KM=,tag:K9MSfO2c2Y4rlf0eYrmTnw==,type:str]
-    hs-token: ENC[AES256_GCM,data:0KsyA06InL4=,iv:zAR0Y1fk8SyodcSLBHlQ8I+BAmttz9Hkd8Q3OREFqs4=,tag:t1Et8N/3seq95DeGoUd7Sw==,type:str]
 wireguard:
     server-key: ENC[AES256_GCM,data:FvY897XdKoa/mckE8JQLCkklsnYD6Wz1wpsu5t3uhEnW3iarnDQxF9msuYU=,iv:jqGXfekM+Vs+J9b5nlZ5Skd1ZKHajoUo2Dc4tMYPm1w=,tag:EehikjI/FCU8wqtpvJRamQ==,type:str]
 restic:
@@ -37,43 +35,43 @@ sops:
     azure_kv: []
     hc_vault: []
     age: []
-    lastmodified: "2025-02-07T17:43:24Z"
-    mac: ENC[AES256_GCM,data:akmD/bfgeTyFzW1quvM16cdj0fC6+CbJ8WyX9173H11yKGxvE1USQYcErpl1SHOx9Jk8LVb7f+MsUm2fjQF1MEq6xaWI74jem12lZ9CGXFaTL7e87JvfbK7pV+aKpxSBBNFyJgbYm30ibdUwxwKmNVfPb1e0HT9qwenvoV7RobM=,iv:mKqOW0ULXL711uczUbRf9NPo6uPTQoS/IbR46S+JID4=,tag:vE6NYzYLbQHDImov1XGTcg==,type:str]
+    lastmodified: "2024-04-12T01:00:31Z"
+    mac: ENC[AES256_GCM,data:fVnMwfvGi7vtP1Fg4NLrhGvLF2PcIgZPOcwk4Ssm4iw5iSj0K1npOX3pd5BWzyszqchfYYRHY99GllAump0bZmprVAld9rf70B2HZIVvowBPuUXfc9Cz/5q0z+s8bQ5vCdElW1Bh7h8W/POePdc8cFGAyBS4i1ZVNheIDOHdDjI=,iv:Bi6rekXOx3/dwwPRryF3CoAoQi3D06ABysRF1oBeG5A=,tag:0TCra+AkhBDczj4uvAzKMw==,type:str]
     pgp:
-        - created_at: "2025-01-21T17:55:30Z"
-          enc: |-
+        - created_at: "2023-12-29T15:25:27Z"
+          enc: |
             -----BEGIN PGP MESSAGE-----
 
-            hQEMA7x7stsXx45CAQf9ELnm9TdXCIO6fTPiSCkKthx0tSHqBWX/s63158k4IUu7
-            v0WWgQy0SKFU3AwIFuVaAYEXB32SaOWKq2WbVAbFZU+xhyUmNe9asg9Fl24+zjGI
-            oYnPzv3lz/5vcI6Q9rZi8F2uIi2GQZnbscS1XfjA5u17uOalQpb0hjUXr0LMaUvU
-            Sggm1ZMKE1o1mHAWK6ZT9SrTMIroWFArJRZLS1eY/vI9ja7I5YR3z0MkLqIvdIp+
-            B4DsOXLlqAqtVoPGcK1CixQiXzzwyQAYHyJc3JFDpaF9Y4S5/bkMLGyQVMA259a+
-            W7ge+EngdJdXV8Unj4s7ndB1e1iM87Jc+4YOcA7jC9JeAR98n8GL+MN9vE8q2AsB
-            qSOXiGSwmAkhq7+ZwJHWlivlB1In0coyJ4eMd/yhyBuc2NrstO0t595HlA93GcLN
-            5JsXIFMqklqGSzE2KgEXhxa2aUoJxcpApVz2BLFPvg==
-            =X72N
+            hQEMA7x7stsXx45CAQf/RWxP6z7xjV5TqiA6lFhtygjrH9x3y1DUWG9aUb/dO+xH
+            zDbGMYqGe9RPlgi5sWPstdKXvCgs+AKNj93qJYMwEtaasJOinYXCGeAQmzg90+pt
+            bS6SoBHhGIxAvvLKKPtYx0V50I2reYR+32ux9bcrnzwIsV0P7/SSp1Cl8H+sotB8
+            yf+0ULXcpC+SYECmZqzR9qQ3S+3I6/+QS+QgWj4NsyF+apxnE9oQDcBLdYP4aKgR
+            JHERA9HYfDTKoS137pFHxgINqHkFRY6lhoZdz1yDzOjiPxd8YVfPdKyf022Rg+cX
+            J/Q2P+OhNZEG3gapNATp6wH3niovA89KwZKSmbTZOdJeAZ6NV6TiUP+TgGg5+CmV
+            pSLaGel2NZRnFVNdDFi0dsOwhHv3FpKhIpALJh08/jsmAAslfE7vVlcEnaoUJPTS
+            3v86AACUC5D/gUxmFrrED1qoxbELCmZ17xTwjQzxwg==
+            =KzdF
             -----END PGP MESSAGE-----
-          fp: B9C7AE554602D82B1AC2CFD0BC7BB2DB17C78E42!
-        - created_at: "2025-01-21T17:55:30Z"
-          enc: |-
+          fp: 535B61015823443941C744DD12264F6BBDFABA89
+        - created_at: "2023-12-29T15:25:27Z"
+          enc: |
             -----BEGIN PGP MESSAGE-----
 
-            hQIMA/3lh+ZzfS28AQ//TPVk5x8wZtKnNcbZjqmXUvi7FzaDMGYFNtiX+bQ17Lkm
-            qdo9TcH8gZI+xni6wlROQBVmu5MAVkT+NWnZIyxN7pokQPmb2v9zo7oyUPV70owU
-            dD/LDNVqgyFTVzWKco4wr8CUwQWhOJI9wM0sjTCOTTbCT5hYVnJOLe630sNG9G5b
-            cJYvaQFxKJeHKJq/DL1I9wT02gOE7vu2xh2OyEozEz3SlB7Bp2mGTLNSiOZBhbzh
-            DNwFRTeDc1Z/ACQJyoGEXkmj1VLyBFuCfXu4UvUQSmlfyCXKTD1/CUo2uiw1N44W
-            sYh5UpIrCU2eVAAXAiD1nB6dHxiau3QlapNQfbY2sOZVSnsB21yIja9C3aQN/0Q5
-            gRrKYcwULzLZ0Z3oQqQxQG9acU8L9CwjKJ1vOKgPVF7hcWkba2bLVsMMaK6seVNc
-            jazp9gDAj440S2aE86CdQvgcOEsfgPhBZrulYglbhW8ZaIN2SdjDN/xP1Tn5PadP
-            gS2DVgqILKPRMF4VavzV3uhEA77QF39Hr18SeToNWcDmfqNPNNf7HpnogstGPf3y
-            xrFAysLbD8IClU4LqI5M19akODON8qeQa5QD+jHOJmAYnMYNRmY/IwHb/SC5WyIm
-            EPNZg5E+q9cNrTKtEIuWec0SObqpaUz2E/Vt9+dge0uVgTA/QqPvMP7x19XBrRDS
-            WAFiKkv7MxImNgcqqe7D3StZoeNm+RXJiULaxxuR8qmMnmvaC+L7ggI4QR3TPQw2
-            mSoi7SkdjQUCa8ut15UwNHTGO+smbRs9aonGP4G7c1cOH90YYvR+BTs=
-            =q2IG
+            hQIMA/3lh+ZzfS28ARAAm729dMouF7juUeHAb+aHMoyZVKsXapxnxebkjE/LSIbz
+            IEZwegTNrtxQJLclV4Km2gUaBTcE4vLJCpB7YxZvk7JV9OdVKi97o9PcXUXbz9ej
+            /WomnEvFyyxTZGTiHU+L4kNudl8UAKhTt3P4fR3PLpTily75Kn53tzLFJuCO8fAY
+            I/YwQAzayxhPcxk3FuPsD/ONiG7mW8n2ZwfwgOkKXwnrlJv7DreKJRYzu/EeuvX/
+            d4oz+k+xofniOeZmQjZllzR7/++MBg/e1U9VocN1EAWpWHP5taLiThfnVSGDhlQM
+            +4WT5ezH6EuUQlAyQNpDaCincBvCHInhrNlUPOpW51nHMb0y3n4x2hMtZA0JbYEu
+            mkWTYDe65cHjImHXQk9oO2/v4oIyq7ywHX7g2hqVbbiLHZqqTaGfV8lP30+r6/UQ
+            29iAdWac1hY5HDzwbqpY6b38i60j4bkiS83xqrGYBy037bCFk1oHJqwxp5P7vrzr
+            rTv5NBr95BlwF+s8xPEPZneaEu7N3UnhhSzDWp1jgsCxN9b/XHarchNt70xEt2VS
+            xpgs9GEXhsJcbrFNPYqTkFb8vjLFI+poGPTfadW17j4Pp5ftIBRNdKvDG0ni/AIp
+            K98R/nvaHEFuX31SkL8ZUIRqhJm3JVqilFxLAJrqGuSN3jA6wKrimUYpK+t+64jS
+            WAEN9jHYFQDTVHix3g15S5YTGh5ROyqxouDhvSDFTmGtbm5W/HYgnkZmh53TgVeJ
+            Rph/O9QptculzTN+nEqshBhbjhl/uDsLsjLYo/O1AyCwTUSd3OKn6uU=
+            =zThh
             -----END PGP MESSAGE-----
           fp: 2f5caa73e7ceea4fcc8d2881fde587e6737d2dbc
     unencrypted_suffix: _unencrypted
-    version: 3.9.2
+    version: 3.8.1
diff --git a/keys/users/tlater.asc b/keys/users/tlater.asc
index d94a304..d9c50dd 100644
--- a/keys/users/tlater.asc
+++ b/keys/users/tlater.asc
@@ -11,230 +11,62 @@ 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
-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
-GmeDneCrlseO
-=ncr2
+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
+PhCB2Dz76iQ5nDY4MQ==
+=R7Pm
 -----END PGP PUBLIC KEY BLOCK-----
diff --git a/modules/crowdsec/default.nix b/modules/crowdsec/default.nix
deleted file mode 100644
index 44e6bc5..0000000
--- a/modules/crowdsec/default.nix
+++ /dev/null
@@ -1,383 +0,0 @@
-{
-  pkgs,
-  lib,
-  config,
-  ...
-}:
-let
-  cfg = config.security.crowdsec;
-  settingsFormat = pkgs.formats.yaml { };
-
-  hub = pkgs.fetchFromGitHub {
-    owner = "crowdsecurity";
-    repo = "hub";
-    rev = "7a3b4753f4577257c0cbeb8f8f90c7f17d2ae008";
-    hash = "sha256-HB4jHyhiO8gjBkLmpo6bDbwhfm5m5nAtNlKhDkZjt2I=";
-  };
-
-  cscli = pkgs.writeShellScriptBin "cscli" ''
-    export PATH="$PATH:${cfg.package}/bin/"
-
-    sudo=exec
-    if [ "$USER" != "crowdsec" ]; then
-        sudo='exec /run/wrappers/bin/sudo -u crowdsec'
-    fi
-
-    $sudo ${cfg.package}/bin/cscli "$@"
-  '';
-
-  acquisitions = ''
-    ---
-    ${lib.concatMapStringsSep "\n---\n" builtins.toJSON cfg.acquisitions}
-    ---
-  '';
-in
-{
-  imports = [ ./remediations ];
-
-  options.security.crowdsec =
-    let
-      inherit (lib.types)
-        nullOr
-        listOf
-        package
-        path
-        str
-        ;
-    in
-    {
-      enable = lib.mkEnableOption "crowdsec";
-
-      package = lib.mkOption {
-        type = package;
-        default = pkgs.crowdsec;
-      };
-
-      stateDirectory = lib.mkOption {
-        type = path;
-        readOnly = true;
-
-        description = ''
-          The state directory of the crowdsec instance. Cannot be
-          changed, but is exposed for downstream use.
-        '';
-      };
-
-      settings = lib.mkOption {
-        inherit (settingsFormat) type;
-        default = { };
-
-        description = ''
-          The crowdsec configuration. Refer to
-          <https://docs.crowdsec.net/docs/next/configuration/crowdsec_configuration/>
-          for details on supported values.
-        '';
-      };
-
-      parserWhitelist = lib.mkOption {
-        type = listOf str;
-        default = [ ];
-        description = ''
-          Set of IP addresses to add to a parser-based whitelist.
-
-          Addresses can be specified either as plain IP addresses or
-          in CIDR notation.
-        '';
-      };
-
-      acquisitions = lib.mkOption {
-        type = listOf settingsFormat.type;
-        default = [ ];
-        description = ''
-          Log acquisitions.
-        '';
-      };
-
-      extraGroups = lib.mkOption {
-        type = listOf str;
-        default = [ ];
-        description = ''
-          Additional groups to make the service part of.
-
-          Required to permit reading from various log sources.
-        '';
-      };
-
-      hubConfigurations = {
-        collections = lib.mkOption {
-          type = listOf str;
-          description = ''
-            List of pre-made crowdsec collections to install.
-          '';
-        };
-
-        scenarios = lib.mkOption {
-          type = listOf str;
-          description = ''
-            List of pre-made crowdsec scenarios to install.
-          '';
-        };
-
-        parsers = lib.mkOption {
-          type = listOf str;
-          description = ''
-            List of pre-made crowdsec parsers to install.
-          '';
-        };
-
-        postoverflows = lib.mkOption {
-          type = listOf str;
-          description = ''
-            List of pre-made crowdsec postoverflows to install.
-          '';
-        };
-
-        appsecConfigs = lib.mkOption {
-          type = listOf str;
-          description = ''
-            List of pre-made crowdsec appsec configurations to install.
-          '';
-        };
-
-        appsecRules = lib.mkOption {
-          type = listOf str;
-          description = ''
-            List of pre-made crowdsec appsec rules to install.
-          '';
-        };
-      };
-
-      centralApiCredentials = lib.mkOption {
-        type = nullOr path;
-        default = null;
-
-        description = ''
-          The API key to access crowdsec's central API - this is
-          required to access any of the shared blocklists.
-
-          Use of this feature is optional, entering no API key (the
-          default) turns all sharing or receiving of blocked IPs off.
-
-          Note that adding the API key by itself does not enable
-          sharing of blocked IPs with the central API. This limits the
-          types of blocklists this instance can access.
-
-          To also turn sharing blocked IPs on, set
-          `api.server.online_client.sharing = true;`.
-        '';
-      };
-
-      ctiApiKey = lib.mkOption {
-        type = nullOr path;
-        default = null;
-
-        description = ''
-          The API key for crowdsec's CTI offering.
-        '';
-      };
-    };
-
-  config = lib.mkIf cfg.enable {
-    # Set up default settings; anything that *shouldn't* be changed is
-    # set to the default priority so that users need to use
-    # `lib.mkForce`.
-    security.crowdsec = {
-      stateDirectory = "/var/lib/crowdsec";
-
-      settings = {
-        common = {
-          daemonize = true;
-          # The default logs to files, which isn't the preferred way
-          # on NixOS
-          log_media = "stdout";
-        };
-
-        config_paths = {
-          config_dir = "${cfg.stateDirectory}/config/";
-          data_dir = "${cfg.stateDirectory}/data/";
-          # This "config" file is intended to be written to using the
-          # cscli tool, so you can temporarily make it so rules don't
-          # do anything but log what they *would* do for
-          # experimentation.
-          simulation_path = "${cfg.stateDirectory}/config/simulation.yaml";
-
-          pattern_dir = lib.mkDefault "${cfg.package}/share/crowdsec/config/patterns";
-
-          hub_dir = hub;
-          index_path = "${hub}/.index.json";
-
-          # Integrations aren't supported for now
-          notification_dir = lib.mkDefault "/var/empty/";
-          plugin_dir = lib.mkDefault "/var/empty/";
-        };
-
-        crowdsec_service.acquisition_path =
-          # Using an if/else here because `mkMerge` does not work in
-          # YAML-type options
-          if cfg.acquisitions == [ ] then
-            "${cfg.package}/share/crowdsec/config/acquis.yaml"
-          else
-            pkgs.writeText "acquis.yaml" acquisitions;
-
-        cscli = {
-          prometheus_uri = lib.mkDefault "127.0.0.1:6060";
-        };
-
-        db_config = {
-          type = lib.mkDefault "sqlite";
-          db_path = lib.mkDefault "${cfg.stateDirectory}/data/crowdsec.db";
-          use_wal = lib.mkDefault true;
-          flush = {
-            max_items = lib.mkDefault 5000;
-            max_age = lib.mkDefault "7d";
-          };
-        };
-
-        api = {
-          cti = {
-            enabled = cfg.ctiApiKey != null;
-            key = cfg.ctiApiKey;
-          };
-          client.credentials_path = "${cfg.stateDirectory}/local_credentials.yaml";
-          server = {
-            listen_uri = lib.mkDefault "127.0.0.1:8080";
-            profiles_path = lib.mkDefault "${cfg.package}/share/crowdsec/config/profiles.yaml";
-            console_path = lib.mkDefault "${cfg.package}/share/crowdsec/config/console.yaml";
-
-            online_client = {
-              # By default, we don't let crowdsec phone home, since
-              # this is usually within NixOS users' concerns.
-              sharing = lib.mkDefault false;
-              credentials_path = cfg.centralApiCredentials;
-            };
-          };
-        };
-
-        # We enable prometheus by default, since cscli relies on it
-        # for metrics
-        prometheus = {
-          enabled = lib.mkDefault true;
-          level = lib.mkDefault "full";
-          listen_addr = lib.mkDefault "127.0.0.1";
-          listen_port = lib.mkDefault 6060;
-        };
-      };
-    };
-
-    systemd.packages = [ cfg.package ];
-
-    environment = {
-      systemPackages = [
-        # To add completions; sadly need to hand-roll this since
-        # neither `symlinkJoin` nor `buildEnv` have collision
-        # handling.
-        (pkgs.runCommandNoCCLocal "cscli" { } ''
-          mkdir -p $out
-          ln -s ${cscli}/bin $out/bin
-          ln -s ${cfg.package}/share $out/share
-        '')
-      ];
-
-      etc."crowdsec/config.yaml".source = settingsFormat.generate "crowdsec-settings.yaml" cfg.settings;
-    };
-
-    systemd = {
-      tmpfiles.settings."10-crowdsec" = {
-        "${cfg.stateDirectory}".d = {
-          user = "crowdsec";
-          group = "crowdsec";
-          mode = "0700";
-        };
-
-        # This must be created for the setup service to work
-        "${cfg.stateDirectory}/config".d = {
-          user = "crowdsec";
-          group = "crowdsec";
-          mode = "0700";
-        };
-
-        "${cfg.stateDirectory}/config/parsers".d = lib.mkIf (cfg.parserWhitelist != [ ]) {
-          user = "crowdsec";
-          group = "crowdsec";
-          mode = "0700";
-        };
-
-        "${cfg.stateDirectory}/config/parsers/s02-enrich".d = lib.mkIf (cfg.parserWhitelist != [ ]) {
-          user = "crowdsec";
-          group = "crowdsec";
-          mode = "0700";
-        };
-
-        "${cfg.stateDirectory}/config/parsers/s02-enrich/nixos-whitelist.yaml" =
-          lib.mkIf (cfg.parserWhitelist != [ ])
-            {
-              "L+".argument =
-                (settingsFormat.generate "crowdsec-nixos-whitelist.yaml" {
-                  name = "nixos/parser-whitelist";
-                  description = "Parser whitelist generated by the crowdsec NixOS module";
-                  whitelist = {
-                    reason = "Filtered by NixOS whitelist";
-                    ip = lib.lists.filter (ip: !(lib.hasInfix "/" ip)) cfg.parserWhitelist;
-                    cidr = lib.lists.filter (ip: lib.hasInfix "/" ip) cfg.parserWhitelist;
-                  };
-                }).outPath;
-            };
-      };
-
-      services = {
-        crowdsec-setup = {
-          # TODO(tlater): Depend on tmpfiles path for
-          # /var/lib/crowdsec/config
-          description = "Crowdsec database and config preparation";
-
-          script = ''
-            if [ ! -e '${cfg.settings.config_paths.simulation_path}' ]; then
-                cp '${cfg.package}/share/crowdsec/config/simulation.yaml' '${cfg.settings.config_paths.simulation_path}'
-            fi
-
-            if [ ! -e '${cfg.settings.api.client.credentials_path}' ]; then
-                ${cfg.package}/bin/cscli machines add --auto --file '${cfg.settings.api.client.credentials_path}'
-            fi
-          '';
-
-          serviceConfig = {
-            User = "crowdsec";
-            Group = "crowdsec";
-            StateDirectory = "crowdsec";
-
-            Type = "oneshot";
-            RemainAfterExit = true;
-          };
-        };
-
-        # Note that the service basics are already defined upstream
-        crowdsec = {
-          enable = true;
-
-          after = [ "crowdsec-setup.service" ];
-          bindsTo = [ "crowdsec-setup.service" ];
-          wantedBy = [ "multi-user.target" ];
-
-          serviceConfig = {
-            User = "crowdsec";
-            Group = "crowdsec";
-            SupplementaryGroups = cfg.extraGroups;
-
-            StateDirectory = "crowdsec";
-          };
-        };
-      };
-    };
-
-    users = {
-      users.crowdsec = {
-        isSystemUser = true;
-        home = cfg.stateDirectory;
-        group = "crowdsec";
-      };
-      groups = {
-        crowdsec = { };
-      };
-    };
-  };
-}
diff --git a/modules/crowdsec/remediations/cs-firewall-bouncer.nix b/modules/crowdsec/remediations/cs-firewall-bouncer.nix
deleted file mode 100644
index 42accc6..0000000
--- a/modules/crowdsec/remediations/cs-firewall-bouncer.nix
+++ /dev/null
@@ -1,87 +0,0 @@
-{
-  flake-inputs,
-  pkgs,
-  lib,
-  config,
-  ...
-}:
-let
-  inherit (flake-inputs.self.packages.${pkgs.system}) crowdsec-firewall-bouncer;
-
-  crowdsecCfg = config.security.crowdsec;
-  cfg = crowdsecCfg.remediationComponents.firewallBouncer;
-  settingsFormat = pkgs.formats.yaml { };
-in
-{
-  options.security.crowdsec.remediationComponents.firewallBouncer = {
-    enable = lib.mkEnableOption "cs-firewall-bouncer";
-
-    settings = lib.mkOption {
-      inherit (settingsFormat) type;
-      default = { };
-
-      description = ''
-        The bouncer configuration. Refer to
-        <https://docs.crowdsec.net/u/bouncers/firewall/> for details
-        on supported values.
-      '';
-    };
-  };
-
-  config = lib.mkIf cfg.enable {
-    security.crowdsec.remediationComponents.firewallBouncer.settings = {
-      mode = lib.mkDefault "${if config.networking.nftables.enable then "nftables" else "iptables"}";
-      log_mode = "stdout";
-      iptables_chains = [ "nixos-fw" ];
-
-      # Don't let users easily override this; unfortunately we need to
-      # set up this key through substitution at runtime.
-      api_key = lib.mkForce "\${API_KEY}";
-      api_url = lib.mkDefault "http://${crowdsecCfg.settings.api.server.listen_uri}";
-    };
-
-    systemd = {
-      packages = [ crowdsec-firewall-bouncer ];
-
-      services = {
-        crowdsec-firewall-bouncer-setup = {
-          description = "Crowdsec firewall bouncer config preparation";
-          script = ''
-            if [ ! -e '${crowdsecCfg.stateDirectory}/firewall_bouncer_credentials.yaml' ]; then
-                ${crowdsecCfg.package}/bin/cscli -oraw bouncers add "cs-firewall-bouncer-$(${pkgs.coreutils}/bin/date +%s)" > \
-                  ${crowdsecCfg.stateDirectory}/firewall_bouncer_credentials.yaml
-            fi
-
-            # Stdout redirection is deliberately used to forcibly
-            # overwrite the file if it exists
-            API_KEY="$(<${crowdsecCfg.stateDirectory}/firewall_bouncer_credentials.yaml)" \
-              ${lib.getExe pkgs.envsubst} \
-              -i ${settingsFormat.generate "crowdsec-firewall-bouncer.yaml" cfg.settings} \
-              > /var/lib/crowdsec/config/crowdsec-firewall-bouncer.yaml
-          '';
-
-          serviceConfig = {
-            User = "crowdsec";
-            Group = "crowdsec";
-
-            Type = "oneshot";
-            RemainAfterExit = true;
-          };
-        };
-
-        crowdsec-firewall-bouncer = {
-          enable = true;
-
-          after = [ "crowdsec-firewall-bouncer-setup.service" ];
-          bindsTo = [ "crowdsec-firewall-bouncer-setup.service" ];
-          requiredBy = [ "crowdsec.service" ];
-
-          path =
-            lib.optionals (cfg.settings.mode == "ipset" || cfg.settings.mode == "iptables") [ pkgs.ipset ]
-            ++ lib.optional (cfg.settings.mode == "iptables") pkgs.iptables
-            ++ lib.optional (cfg.settings.mode == "nftables") pkgs.nftables;
-        };
-      };
-    };
-  };
-}
diff --git a/modules/crowdsec/remediations/default.nix b/modules/crowdsec/remediations/default.nix
deleted file mode 100644
index c3c0790..0000000
--- a/modules/crowdsec/remediations/default.nix
+++ /dev/null
@@ -1 +0,0 @@
-{ imports = [ ./cs-firewall-bouncer.nix ]; }
diff --git a/modules/default.nix b/modules/default.nix
index 89f1752..9341a5a 100644
--- a/modules/default.nix
+++ b/modules/default.nix
@@ -1,6 +1,5 @@
 {
   imports = [
-    ./crowdsec
     ./nginxExtensions.nix
   ];
 }
diff --git a/modules/nginxExtensions.nix b/modules/nginxExtensions.nix
index bd505d3..8c19cc0 100644
--- a/modules/nginxExtensions.nix
+++ b/modules/nginxExtensions.nix
@@ -3,57 +3,156 @@
   pkgs,
   lib,
   ...
-}:
-{
+}: {
   options = {
     services.nginx.domain = lib.mkOption {
       type = lib.types.str;
       description = "The base domain name to append to virtual domain names";
     };
 
-    services.nginx.virtualHosts =
-      let
-        extraVirtualHostOptions =
-          { name, config, ... }:
-          {
-            options = {
-              enableHSTS = lib.mkEnableOption "Enable HSTS";
+    services.nginx.virtualHosts = let
+      autheliaDomain = "auth.${config.services.nginx.domain}";
+      extraLocationOptions = {config, ...}: {
+        options = {
+          enableAutheliaProxy = lib.mkEnableOption "Enable recommended authelia proxy settings";
+          enableAuthorization = lib.mkEnableOption "Enable authorization via authelia";
+        };
 
-              addAccessLog = lib.mkOption {
-                type = lib.types.bool;
-                default = true;
-                description = ''
-                  Add special logging to `/var/log/nginx/''${serverName}`
-                '';
-              };
-            };
+        config = {
+          recommendedProxySettings = lib.mkIf config.enableAutheliaProxy false;
 
-            config = {
-              extraConfig = lib.concatStringsSep "\n" [
-                (lib.optionalString config.enableHSTS ''
-                  add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always;
-                '')
-                (lib.optionalString config.addAccessLog ''
-                  access_log /var/log/nginx/${name}/access.log upstream_time;
-                '')
-              ];
+          extraConfig = lib.concatStringsSep "\n" [
+            (lib.optionalString config.enableAutheliaProxy ''
+              proxy_set_header Host $host;
+              proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
+              proxy_set_header X-Forwarded-Proto $scheme;
+              proxy_set_header X-Forwarded-Host $http_host;
+              proxy_set_header X-Forwarded-URI $request_uri;
+              proxy_set_header X-Forwarded-Ssl on;
+              proxy_set_header X-Forwarded-For $remote_addr;
+              proxy_set_header X-Real-IP $remote_addr;
+              proxy_set_header Connection "";
+
+              client_body_buffer_size 128k;
+              proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
+              proxy_redirect http:// $scheme://;
+              proxy_http_version 1.1;
+              proxy_cache_bypass $cookie_session;
+              proxy_no_cache $cookie_session;
+              proxy_buffers 64 256k;
+
+              real_ip_header X-Forwarded-For;
+              real_ip_recursive on;
+
+              send_timeout 5m;
+              proxy_read_timeout 360;
+              proxy_send_timeout 360;
+              proxy_connect_timeout 360;
+            '')
+            (lib.optionalString config.enableAuthorization ''
+              auth_request /authelia;
+
+              set_escape_uri $target_url $scheme://$http_host$request_uri;
+
+              auth_request_set $user $upstream_http_remote_user;
+              auth_request_set $groups $upstream_http_remote_groups;
+              auth_request_set $name $upstream_http_remote_name;
+              auth_request_set $email $upstream_http_remote_email;
+
+              proxy_set_header Remote-User $user;
+              proxy_set_header Remote-Groups $groups;
+              proxy_set_header Remote-Email $email;
+              proxy_set_header Remote-Name $name;
+
+              error_page 401 =302 https://${autheliaDomain}/?rd=$target_url;
+            '')
+          ];
+        };
+      };
+
+      extraVirtualHostOptions = {
+        name,
+        config,
+        ...
+      }: {
+        options = {
+          enableAuthorization = lib.mkEnableOption "Enable authorization via authelia";
+          enableHSTS = lib.mkEnableOption "Enable HSTS";
+
+          addAccessLog = lib.mkOption {
+            type = lib.types.bool;
+            default = true;
+            description = ''
+              Add special logging to `/var/log/nginx/''${serverName}`
+            '';
+          };
+
+          locations = lib.mkOption {
+            type = lib.types.attrsOf (lib.types.submodule extraLocationOptions);
+          };
+        };
+
+        config = {
+          extraConfig = lib.concatStringsSep "\n" [
+            (lib.optionalString config.enableHSTS ''
+              add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always;
+            '')
+            (lib.optionalString config.addAccessLog ''
+              access_log /var/log/nginx/${name}/access.log upstream_time;
+            '')
+          ];
+
+          locations = lib.mkIf config.enableAuthorization {
+            "/".enableAuthorization = true;
+            "/authelia" = {
+              proxyPass = "http://127.0.0.1:9091/api/verify";
+              recommendedProxySettings = false;
+              extraConfig = ''
+                internal;
+
+                proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
+                proxy_set_header X-Original-Method $request_method;
+                proxy_set_header X-Forwarded-Method $request_method;
+                proxy_set_header X-Forwarded-Proto $scheme;
+                proxy_set_header X-Forwarded-Host $http_host;
+                proxy_set_header X-Forwarded-Uri $request_uri;
+                proxy_set_header X-Forwarded-For $remote_addr;
+                proxy_set_header Content-Length "";
+                proxy_set_header Connection "";
+
+                proxy_pass_request_body off;
+                proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
+                proxy_redirect http:// $scheme://;
+                proxy_http_version 1.1;
+                proxy_cache_bypass $cookie_session;
+                proxy_no_cache $cookie_session;
+                proxy_buffers 4 32k;
+                client_body_buffer_size 128k;
+
+                send_timeout 5m;
+                proxy_read_timeout 240;
+                proxy_send_timeout 240;
+                proxy_connect_timeout 240;
+              '';
             };
           };
-      in
-      lib.mkOption { type = lib.types.attrsOf (lib.types.submodule extraVirtualHostOptions); };
+        };
+      };
+    in
+      lib.mkOption {
+        type = lib.types.attrsOf (lib.types.submodule extraVirtualHostOptions);
+      };
   };
 
   config = {
     # Don't attempt to run acme if the domain name is not tlater.net
-    systemd.services =
-      let
-        confirm = ''[[ "tlater.net" = ${config.services.nginx.domain} ]]'';
-      in
-      lib.mapAttrs' (
-        cert: _:
+    systemd.services = let
+      confirm = ''[[ "tlater.net" = ${config.services.nginx.domain} ]]'';
+    in
+      lib.mapAttrs' (cert: _:
         lib.nameValuePair "acme-${cert}" {
           serviceConfig.ExecCondition = ''${pkgs.runtimeShell} -c '${confirm}' '';
-        }
-      ) config.security.acme.certs;
+        })
+      config.security.acme.certs;
   };
 }
diff --git a/pkgs/_sources_nextcloud/generated.json b/pkgs/_sources_nextcloud/generated.json
new file mode 100644
index 0000000..ce9ac04
--- /dev/null
+++ b/pkgs/_sources_nextcloud/generated.json
@@ -0,0 +1,86 @@
+{
+    "bookmarks": {
+        "cargoLocks": null,
+        "date": null,
+        "extract": null,
+        "name": "bookmarks",
+        "passthru": null,
+        "pinned": false,
+        "src": {
+            "sha256": "sha256-JXNQNnWXoii71QhtKktuEBEIqzmONVetULBhpSjM9xo=",
+            "type": "tarball",
+            "url": "https://github.com/nextcloud/bookmarks/releases/download/v13.1.3/bookmarks-13.1.3.tar.gz"
+        },
+        "version": "13.1.3"
+    },
+    "calendar": {
+        "cargoLocks": null,
+        "date": null,
+        "extract": null,
+        "name": "calendar",
+        "passthru": null,
+        "pinned": false,
+        "src": {
+            "sha256": "sha256-hZfjWAMi/0qs5xMMgOlcoSXG6kcZ2aeDaez+NqSZFKI=",
+            "type": "tarball",
+            "url": "https://github.com/nextcloud-releases/calendar/releases/download/v4.6.7/calendar-v4.6.7.tar.gz"
+        },
+        "version": "v4.6.7"
+    },
+    "contacts": {
+        "cargoLocks": null,
+        "date": null,
+        "extract": null,
+        "name": "contacts",
+        "passthru": null,
+        "pinned": false,
+        "src": {
+            "sha256": "sha256-HCEjiAqn6sTNXKW6O5X6Ta9Ll4ehvzmGZUj1c0ue2Xc=",
+            "type": "tarball",
+            "url": "https://github.com/nextcloud-releases/contacts/releases/download/v5.5.3/contacts-v5.5.3.tar.gz"
+        },
+        "version": "v5.5.3"
+    },
+    "cookbook": {
+        "cargoLocks": null,
+        "date": null,
+        "extract": null,
+        "name": "cookbook",
+        "passthru": null,
+        "pinned": false,
+        "src": {
+            "sha256": "sha256-TE/w8SgyIPaGl5wZUAsG234nxoPj25QoRPF3zjbMoRk=",
+            "type": "tarball",
+            "url": "https://github.com/christianlupus-nextcloud/cookbook-releases/releases/download/v0.10.5/Cookbook-0.10.5.tar.gz"
+        },
+        "version": "0.10.5"
+    },
+    "news": {
+        "cargoLocks": null,
+        "date": null,
+        "extract": null,
+        "name": "news",
+        "passthru": null,
+        "pinned": false,
+        "src": {
+            "sha256": "sha256-cfJkKRNSz15L4E3w1tnEb+t4MrVwVzb8lb6vCOA4cK4=",
+            "type": "tarball",
+            "url": "https://github.com/nextcloud/news/releases/download/24.0.0/news.tar.gz"
+        },
+        "version": "24.0.0"
+    },
+    "notes": {
+        "cargoLocks": null,
+        "date": null,
+        "extract": null,
+        "name": "notes",
+        "passthru": null,
+        "pinned": false,
+        "src": {
+            "sha256": "sha256-ydpxatwuZUz7XIgK8FMklZlxNQklpsP8Uqpxvt3iK0k=",
+            "type": "tarball",
+            "url": "https://github.com/nextcloud/notes/releases/download/v4.10.0/notes.tar.gz"
+        },
+        "version": "v4.10.0"
+    }
+}
\ No newline at end of file
diff --git a/pkgs/_sources_nextcloud/generated.nix b/pkgs/_sources_nextcloud/generated.nix
new file mode 100644
index 0000000..c77aa9c
--- /dev/null
+++ b/pkgs/_sources_nextcloud/generated.nix
@@ -0,0 +1,52 @@
+# This file was generated by nvfetcher, please do not modify it manually.
+{ fetchgit, fetchurl, fetchFromGitHub, dockerTools }:
+{
+  bookmarks = {
+    pname = "bookmarks";
+    version = "13.1.3";
+    src = fetchTarball {
+      url = "https://github.com/nextcloud/bookmarks/releases/download/v13.1.3/bookmarks-13.1.3.tar.gz";
+      sha256 = "sha256-JXNQNnWXoii71QhtKktuEBEIqzmONVetULBhpSjM9xo=";
+    };
+  };
+  calendar = {
+    pname = "calendar";
+    version = "v4.6.7";
+    src = fetchTarball {
+      url = "https://github.com/nextcloud-releases/calendar/releases/download/v4.6.7/calendar-v4.6.7.tar.gz";
+      sha256 = "sha256-hZfjWAMi/0qs5xMMgOlcoSXG6kcZ2aeDaez+NqSZFKI=";
+    };
+  };
+  contacts = {
+    pname = "contacts";
+    version = "v5.5.3";
+    src = fetchTarball {
+      url = "https://github.com/nextcloud-releases/contacts/releases/download/v5.5.3/contacts-v5.5.3.tar.gz";
+      sha256 = "sha256-HCEjiAqn6sTNXKW6O5X6Ta9Ll4ehvzmGZUj1c0ue2Xc=";
+    };
+  };
+  cookbook = {
+    pname = "cookbook";
+    version = "0.10.5";
+    src = fetchTarball {
+      url = "https://github.com/christianlupus-nextcloud/cookbook-releases/releases/download/v0.10.5/Cookbook-0.10.5.tar.gz";
+      sha256 = "sha256-TE/w8SgyIPaGl5wZUAsG234nxoPj25QoRPF3zjbMoRk=";
+    };
+  };
+  news = {
+    pname = "news";
+    version = "24.0.0";
+    src = fetchTarball {
+      url = "https://github.com/nextcloud/news/releases/download/24.0.0/news.tar.gz";
+      sha256 = "sha256-cfJkKRNSz15L4E3w1tnEb+t4MrVwVzb8lb6vCOA4cK4=";
+    };
+  };
+  notes = {
+    pname = "notes";
+    version = "v4.10.0";
+    src = fetchTarball {
+      url = "https://github.com/nextcloud/notes/releases/download/v4.10.0/notes.tar.gz";
+      sha256 = "sha256-ydpxatwuZUz7XIgK8FMklZlxNQklpsP8Uqpxvt3iK0k=";
+    };
+  };
+}
diff --git a/pkgs/_sources_pkgs/generated.json b/pkgs/_sources_pkgs/generated.json
new file mode 100644
index 0000000..fc6c343
--- /dev/null
+++ b/pkgs/_sources_pkgs/generated.json
@@ -0,0 +1,21 @@
+{
+    "prometheus-fail2ban-exporter": {
+        "cargoLocks": null,
+        "date": null,
+        "extract": null,
+        "name": "prometheus-fail2ban-exporter",
+        "passthru": null,
+        "pinned": false,
+        "src": {
+            "deepClone": false,
+            "fetchSubmodules": false,
+            "leaveDotGit": false,
+            "name": null,
+            "rev": "v0.10.1",
+            "sha256": "sha256-zGEhDy3uXIbvx4agSA8Mx7bRtiZZtoDZGbNbHc9L+yI=",
+            "type": "git",
+            "url": "https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter"
+        },
+        "version": "v0.10.1"
+    }
+}
\ No newline at end of file
diff --git a/pkgs/_sources_pkgs/generated.nix b/pkgs/_sources_pkgs/generated.nix
new file mode 100644
index 0000000..39dc6d0
--- /dev/null
+++ b/pkgs/_sources_pkgs/generated.nix
@@ -0,0 +1,16 @@
+# This file was generated by nvfetcher, please do not modify it manually.
+{ fetchgit, fetchurl, fetchFromGitHub, dockerTools }:
+{
+  prometheus-fail2ban-exporter = {
+    pname = "prometheus-fail2ban-exporter";
+    version = "v0.10.1";
+    src = fetchgit {
+      url = "https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter";
+      rev = "v0.10.1";
+      fetchSubmodules = false;
+      deepClone = false;
+      leaveDotGit = false;
+      sha256 = "sha256-zGEhDy3uXIbvx4agSA8Mx7bRtiZZtoDZGbNbHc9L+yI=";
+    };
+  };
+}
diff --git a/pkgs/afvalcalendar/Cargo.lock b/pkgs/afvalcalendar/Cargo.lock
new file mode 100644
index 0000000..24cace4
--- /dev/null
+++ b/pkgs/afvalcalendar/Cargo.lock
@@ -0,0 +1,1430 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "addr2line"
+version = "0.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
+name = "afvalcalendar"
+version = "0.1.0"
+dependencies = [
+ "chrono",
+ "hostname",
+ "icalendar",
+ "reqwest",
+ "serde",
+ "serde_json",
+ "serde_repr",
+ "tokio",
+]
+
+[[package]]
+name = "android-tzdata"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "backtrace"
+version = "0.3.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
+dependencies = [
+ "addr2line",
+ "cc",
+ "cfg-if",
+ "libc",
+ "miniz_oxide",
+ "object",
+ "rustc-demangle",
+]
+
+[[package]]
+name = "base64"
+version = "0.21.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
+version = "2.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
+
+[[package]]
+name = "bumpalo"
+version = "3.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d32a994c2b3ca201d9b263612a374263f05e7adde37c4707f693dcd375076d1f"
+
+[[package]]
+name = "bytes"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
+
+[[package]]
+name = "cc"
+version = "1.0.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "chrono"
+version = "0.4.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b"
+dependencies = [
+ "android-tzdata",
+ "iana-time-zone",
+ "js-sys",
+ "num-traits",
+ "serde",
+ "wasm-bindgen",
+ "windows-targets 0.52.0",
+]
+
+[[package]]
+name = "cookie"
+version = "0.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7efb37c3e1ccb1ff97164ad95ac1606e8ccd35b3fa0a7d99a304c7f4a428cc24"
+dependencies = [
+ "percent-encoding",
+ "time",
+ "version_check",
+]
+
+[[package]]
+name = "cookie_store"
+version = "0.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "387461abbc748185c3a6e1673d826918b450b87ff22639429c694619a83b6cf6"
+dependencies = [
+ "cookie",
+ "idna 0.3.0",
+ "log",
+ "publicsuffix",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "time",
+ "url",
+]
+
+[[package]]
+name = "core-foundation"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
+
+[[package]]
+name = "deranged"
+version = "0.3.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
+dependencies = [
+ "powerfmt",
+]
+
+[[package]]
+name = "encoding_rs"
+version = "0.8.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "equivalent"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+
+[[package]]
+name = "errno"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
+dependencies = [
+ "libc",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "fastrand"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "foreign-types"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
+dependencies = [
+ "foreign-types-shared",
+]
+
+[[package]]
+name = "foreign-types-shared"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
+
+[[package]]
+name = "form_urlencoded"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
+dependencies = [
+ "percent-encoding",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
+dependencies = [
+ "futures-core",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
+
+[[package]]
+name = "futures-sink"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
+
+[[package]]
+name = "futures-task"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
+
+[[package]]
+name = "futures-util"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "pin-project-lite",
+ "pin-utils",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "libc",
+ "wasi",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "gimli"
+version = "0.28.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
+
+[[package]]
+name = "h2"
+version = "0.3.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9"
+dependencies = [
+ "bytes",
+ "fnv",
+ "futures-core",
+ "futures-sink",
+ "futures-util",
+ "http",
+ "indexmap",
+ "slab",
+ "tokio",
+ "tokio-util",
+ "tracing",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.14.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
+
+[[package]]
+name = "hermit-abi"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd5256b483761cd23699d0da46cc6fd2ee3be420bbe6d020ae4a091e70b7e9fd"
+
+[[package]]
+name = "hostname"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867"
+dependencies = [
+ "libc",
+ "match_cfg",
+ "winapi",
+]
+
+[[package]]
+name = "http"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb"
+dependencies = [
+ "bytes",
+ "fnv",
+ "itoa",
+]
+
+[[package]]
+name = "http-body"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
+dependencies = [
+ "bytes",
+ "http",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "httparse"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
+
+[[package]]
+name = "httpdate"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
+
+[[package]]
+name = "hyper"
+version = "0.14.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80"
+dependencies = [
+ "bytes",
+ "futures-channel",
+ "futures-core",
+ "futures-util",
+ "h2",
+ "http",
+ "http-body",
+ "httparse",
+ "httpdate",
+ "itoa",
+ "pin-project-lite",
+ "socket2",
+ "tokio",
+ "tower-service",
+ "tracing",
+ "want",
+]
+
+[[package]]
+name = "hyper-tls"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
+dependencies = [
+ "bytes",
+ "hyper",
+ "native-tls",
+ "tokio",
+ "tokio-native-tls",
+]
+
+[[package]]
+name = "iana-time-zone"
+version = "0.1.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "wasm-bindgen",
+ "windows-core",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "icalendar"
+version = "0.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa4ffbcf3325ae94554c5259ce0b8907d37133c066cb3d424a2fa96aa1f10088"
+dependencies = [
+ "chrono",
+ "iso8601",
+ "nom",
+ "uuid",
+]
+
+[[package]]
+name = "idna"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6"
+dependencies = [
+ "unicode-bidi",
+ "unicode-normalization",
+]
+
+[[package]]
+name = "idna"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
+dependencies = [
+ "unicode-bidi",
+ "unicode-normalization",
+]
+
+[[package]]
+name = "indexmap"
+version = "2.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177"
+dependencies = [
+ "equivalent",
+ "hashbrown",
+]
+
+[[package]]
+name = "ipnet"
+version = "2.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
+
+[[package]]
+name = "iso8601"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "924e5d73ea28f59011fec52a0d12185d496a9b075d360657aed2a5707f701153"
+dependencies = [
+ "nom",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
+
+[[package]]
+name = "js-sys"
+version = "0.3.68"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "libc"
+version = "0.2.153"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
+
+[[package]]
+name = "log"
+version = "0.4.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
+
+[[package]]
+name = "match_cfg"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4"
+
+[[package]]
+name = "memchr"
+version = "2.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
+
+[[package]]
+name = "mime"
+version = "0.3.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
+
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
+[[package]]
+name = "miniz_oxide"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7"
+dependencies = [
+ "adler",
+]
+
+[[package]]
+name = "mio"
+version = "0.8.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09"
+dependencies = [
+ "libc",
+ "wasi",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "native-tls"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e"
+dependencies = [
+ "lazy_static",
+ "libc",
+ "log",
+ "openssl",
+ "openssl-probe",
+ "openssl-sys",
+ "schannel",
+ "security-framework",
+ "security-framework-sys",
+ "tempfile",
+]
+
+[[package]]
+name = "nom"
+version = "7.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]
+
+[[package]]
+name = "num-conv"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
+
+[[package]]
+name = "num-traits"
+version = "0.2.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
+dependencies = [
+ "hermit-abi",
+ "libc",
+]
+
+[[package]]
+name = "object"
+version = "0.32.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+
+[[package]]
+name = "openssl"
+version = "0.10.64"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f"
+dependencies = [
+ "bitflags 2.4.2",
+ "cfg-if",
+ "foreign-types",
+ "libc",
+ "once_cell",
+ "openssl-macros",
+ "openssl-sys",
+]
+
+[[package]]
+name = "openssl-macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "openssl-probe"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
+
+[[package]]
+name = "openssl-sys"
+version = "0.9.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae94056a791d0e1217d18b6cbdccb02c61e3054fc69893607f4067e3bb0b1fd1"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "pkg-config"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
+
+[[package]]
+name = "powerfmt"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.78"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "psl-types"
+version = "2.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac"
+
+[[package]]
+name = "publicsuffix"
+version = "2.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96a8c1bda5ae1af7f99a2962e49df150414a43d62404644d98dd5c3a93d07457"
+dependencies = [
+ "idna 0.3.0",
+ "psl-types",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "reqwest"
+version = "0.11.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c6920094eb85afde5e4a138be3f2de8bbdf28000f0029e72c45025a56b042251"
+dependencies = [
+ "base64",
+ "bytes",
+ "cookie",
+ "cookie_store",
+ "encoding_rs",
+ "futures-core",
+ "futures-util",
+ "h2",
+ "http",
+ "http-body",
+ "hyper",
+ "hyper-tls",
+ "ipnet",
+ "js-sys",
+ "log",
+ "mime",
+ "native-tls",
+ "once_cell",
+ "percent-encoding",
+ "pin-project-lite",
+ "rustls-pemfile",
+ "serde",
+ "serde_json",
+ "serde_urlencoded",
+ "sync_wrapper",
+ "system-configuration",
+ "tokio",
+ "tokio-native-tls",
+ "tower-service",
+ "url",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "winreg",
+]
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
+
+[[package]]
+name = "rustix"
+version = "0.38.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949"
+dependencies = [
+ "bitflags 2.4.2",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "rustls-pemfile"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c"
+dependencies = [
+ "base64",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
+
+[[package]]
+name = "schannel"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534"
+dependencies = [
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "security-framework"
+version = "2.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de"
+dependencies = [
+ "bitflags 1.3.2",
+ "core-foundation",
+ "core-foundation-sys",
+ "libc",
+ "security-framework-sys",
+]
+
+[[package]]
+name = "security-framework-sys"
+version = "2.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "serde"
+version = "1.0.196"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.196"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.114"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "serde_repr"
+version = "0.1.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_urlencoded"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
+dependencies = [
+ "form_urlencoded",
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "slab"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "socket2"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9"
+dependencies = [
+ "libc",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.49"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "915aea9e586f80826ee59f8453c1101f9d1c4b3964cd2460185ee8e299ada496"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "sync_wrapper"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
+
+[[package]]
+name = "system-configuration"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
+dependencies = [
+ "bitflags 1.3.2",
+ "core-foundation",
+ "system-configuration-sys",
+]
+
+[[package]]
+name = "system-configuration-sys"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "tempfile"
+version = "3.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a365e8cd18e44762ef95d87f284f4b5cd04107fec2ff3052bd6a3e6069669e67"
+dependencies = [
+ "cfg-if",
+ "fastrand",
+ "rustix",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "time"
+version = "0.3.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749"
+dependencies = [
+ "deranged",
+ "itoa",
+ "num-conv",
+ "powerfmt",
+ "serde",
+ "time-core",
+ "time-macros",
+]
+
+[[package]]
+name = "time-core"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
+
+[[package]]
+name = "time-macros"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774"
+dependencies = [
+ "num-conv",
+ "time-core",
+]
+
+[[package]]
+name = "tinyvec"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
+dependencies = [
+ "tinyvec_macros",
+]
+
+[[package]]
+name = "tinyvec_macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
+
+[[package]]
+name = "tokio"
+version = "1.36.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931"
+dependencies = [
+ "backtrace",
+ "bytes",
+ "libc",
+ "mio",
+ "num_cpus",
+ "pin-project-lite",
+ "socket2",
+ "tokio-macros",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "tokio-macros"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tokio-native-tls"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
+dependencies = [
+ "native-tls",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-util"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "futures-sink",
+ "pin-project-lite",
+ "tokio",
+ "tracing",
+]
+
+[[package]]
+name = "tower-service"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
+
+[[package]]
+name = "tracing"
+version = "0.1.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
+dependencies = [
+ "pin-project-lite",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
+dependencies = [
+ "once_cell",
+]
+
+[[package]]
+name = "try-lock"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
+
+[[package]]
+name = "unicode-bidi"
+version = "0.3.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
+name = "unicode-normalization"
+version = "0.1.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
+dependencies = [
+ "tinyvec",
+]
+
+[[package]]
+name = "url"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
+dependencies = [
+ "form_urlencoded",
+ "idna 0.5.0",
+ "percent-encoding",
+]
+
+[[package]]
+name = "uuid"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a"
+dependencies = [
+ "getrandom",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "vcpkg"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "want"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
+dependencies = [
+ "try-lock",
+]
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.91"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.91"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.41"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "877b9c3f61ceea0e56331985743b13f3d25c406a7098d45180fb5f09bc19ed97"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.91"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.91"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.91"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838"
+
+[[package]]
+name = "web-sys"
+version = "0.3.68"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96565907687f7aceb35bc5fc03770a8a0471d82e479f25832f54a0e3f4b28446"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-core"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
+dependencies = [
+ "windows-targets 0.52.0",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets 0.52.0",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "windows_aarch64_gnullvm 0.48.5",
+ "windows_aarch64_msvc 0.48.5",
+ "windows_i686_gnu 0.48.5",
+ "windows_i686_msvc 0.48.5",
+ "windows_x86_64_gnu 0.48.5",
+ "windows_x86_64_gnullvm 0.48.5",
+ "windows_x86_64_msvc 0.48.5",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.0",
+ "windows_aarch64_msvc 0.52.0",
+ "windows_i686_gnu 0.52.0",
+ "windows_i686_msvc 0.52.0",
+ "windows_x86_64_gnu 0.52.0",
+ "windows_x86_64_gnullvm 0.52.0",
+ "windows_x86_64_msvc 0.52.0",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
+
+[[package]]
+name = "winreg"
+version = "0.50.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1"
+dependencies = [
+ "cfg-if",
+ "windows-sys 0.48.0",
+]
diff --git a/pkgs/afvalcalendar/Cargo.toml b/pkgs/afvalcalendar/Cargo.toml
new file mode 100644
index 0000000..f37dff1
--- /dev/null
+++ b/pkgs/afvalcalendar/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+name = "afvalcalendar"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+icalendar = "0.16.0"
+serde = { version = "1.0.195", features = ["derive"] }
+tokio = { version = "1.35.1", features = ["macros", "rt-multi-thread"] }
+reqwest = { version = "0.11", features = ["cookies", "json"] }
+chrono = { version = "0.4.34", features = ["serde"] }
+serde_json = "1.0.114"
+serde_repr = "0.1.18"
+hostname = "0.3.1"
diff --git a/pkgs/afvalcalendar/default.nix b/pkgs/afvalcalendar/default.nix
new file mode 100644
index 0000000..12e2af5
--- /dev/null
+++ b/pkgs/afvalcalendar/default.nix
@@ -0,0 +1,20 @@
+{
+  pkgs,
+  rustPlatform,
+  ...
+}:
+rustPlatform.buildRustPackage {
+  pname = "afvalcalendar";
+  version = "0.1.0";
+  src = ./.;
+
+  nativeBuildInputs = with pkgs; [
+    pkg-config
+  ];
+
+  buildInputs = with pkgs; [
+    openssl
+  ];
+
+  cargoHash = "sha256-JXx6aUKdKbUTBCwlBw5i1hZy8ofCfSrhLCwFzqdA8cI=";
+}
diff --git a/pkgs/afvalcalendar/src/calendar.rs b/pkgs/afvalcalendar/src/calendar.rs
new file mode 100644
index 0000000..2c76d7f
--- /dev/null
+++ b/pkgs/afvalcalendar/src/calendar.rs
@@ -0,0 +1,43 @@
+use chrono::{Duration, NaiveDate};
+use icalendar::{Alarm, Calendar, Component, Event, EventLike, Property};
+
+use crate::trash::TrashType;
+
+pub(crate) fn calendar_from_pickup_dates(dates: Vec<(TrashType, NaiveDate)>) -> Calendar {
+    let mut ical = Calendar::new();
+    ical.name("Twente Milieu Afvalkalender");
+
+    let events = dates.iter().map(|date| {
+        let description = match date.0 {
+            TrashType::Grey => "Restafval wordt opgehaald",
+            TrashType::Green => "GFT wordt opgehaald",
+            TrashType::Paper => "Papier wordt opgehaald",
+            TrashType::Packages => "Verpakkingen worden opgehaald",
+        };
+
+        let color = Property::new(
+            "COLOR",
+            match date.0 {
+                TrashType::Grey => "darkgray",
+                TrashType::Green => "darkgreen",
+                TrashType::Paper => "royalblue",
+                TrashType::Packages => "darkorange",
+            },
+        );
+
+        let reminder = Alarm::display(description, -Duration::hours(5));
+
+        Event::new()
+            .all_day(date.1)
+            .summary(description)
+            .append_property(color)
+            .alarm(reminder)
+            .done()
+    });
+
+    for event in events {
+        ical.push(event);
+    }
+
+    ical.done()
+}
diff --git a/pkgs/afvalcalendar/src/main.rs b/pkgs/afvalcalendar/src/main.rs
new file mode 100644
index 0000000..e8023f7
--- /dev/null
+++ b/pkgs/afvalcalendar/src/main.rs
@@ -0,0 +1,15 @@
+mod calendar;
+mod trash;
+
+#[tokio::main]
+async fn main() {
+    match trash::get_pickup_dates().await {
+        Ok(dates) => {
+            let calendar = calendar::calendar_from_pickup_dates(dates);
+            calendar.print().unwrap();
+        }
+        Err(error) => {
+            eprintln!("{}", error);
+        }
+    }
+}
diff --git a/pkgs/afvalcalendar/src/trash.rs b/pkgs/afvalcalendar/src/trash.rs
new file mode 100644
index 0000000..89a84d0
--- /dev/null
+++ b/pkgs/afvalcalendar/src/trash.rs
@@ -0,0 +1,59 @@
+use chrono::{Months, NaiveDate, NaiveDateTime, Utc};
+use serde::Deserialize;
+use serde_repr::Deserialize_repr;
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct CalendarAPIDatum {
+    pickup_dates: Vec<NaiveDateTime>,
+    pickup_type: TrashType,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct CalendarAPIResponse {
+    data_list: Vec<CalendarAPIDatum>,
+}
+
+#[derive(Copy, Clone, Deserialize_repr, Debug)]
+#[repr(u8)]
+pub(crate) enum TrashType {
+    Grey = 0,
+    Green = 1,
+    Paper = 2,
+    Packages = 10,
+}
+
+pub(crate) async fn get_pickup_dates() -> Result<Vec<(TrashType, NaiveDate)>, reqwest::Error> {
+    let today = Utc::now().date_naive();
+    let next_month = (today + Months::new(1)).to_string();
+    let today = today.to_string();
+
+    let client = reqwest::Client::new();
+
+    let params = [
+        ("companyCode", "8d97bb56-5afd-4cbc-a651-b4f7314264b4"),
+        ("uniqueAddressID", "1300002485"),
+        ("startDate", &today),
+        ("endDate", &next_month),
+    ];
+
+    let calendar = client
+        .post("https://twentemilieuapi.ximmio.com/api/GetCalendar")
+        .form(&params)
+        .send()
+        .await?
+        .json::<CalendarAPIResponse>()
+        .await?;
+
+    Ok(calendar
+        .data_list
+        .iter()
+        .flat_map(|datum| {
+            datum
+                .pickup_dates
+                .iter()
+                .map(|date| (datum.pickup_type, NaiveDate::from(*date)))
+        })
+        .collect::<Vec<(TrashType, NaiveDate)>>())
+}
diff --git a/pkgs/afvalcalendar/test.rest b/pkgs/afvalcalendar/test.rest
new file mode 100644
index 0000000..d2d409e
--- /dev/null
+++ b/pkgs/afvalcalendar/test.rest
@@ -0,0 +1,4 @@
+POST https://twentemilieuapi.ximmio.com/api/GetCalendar
+Content-Type: application/x-www-form-urlencoded
+
+companyCode=8d97bb56-5afd-4cbc-a651-b4f7314264b4&uniqueAddressID=1300002485&startDate=2024-02-01&endDate=2024-02-29
diff --git a/pkgs/crowdsec/_sources/generated.json b/pkgs/crowdsec/_sources/generated.json
deleted file mode 100644
index f3c8f01..0000000
--- a/pkgs/crowdsec/_sources/generated.json
+++ /dev/null
@@ -1,42 +0,0 @@
-{
-    "crowdsec-firewall-bouncer": {
-        "cargoLocks": null,
-        "date": null,
-        "extract": null,
-        "name": "crowdsec-firewall-bouncer",
-        "passthru": null,
-        "pinned": false,
-        "src": {
-            "deepClone": false,
-            "fetchSubmodules": false,
-            "leaveDotGit": false,
-            "name": null,
-            "owner": "crowdsecurity",
-            "repo": "cs-firewall-bouncer",
-            "rev": "v0.0.31",
-            "sha256": "sha256-59MWll8v00CF4WA53gjHZSTFc8hpYaHENg9O7LgTCrA=",
-            "type": "github"
-        },
-        "version": "v0.0.31"
-    },
-    "crowdsec-hub": {
-        "cargoLocks": null,
-        "date": "2025-05-17",
-        "extract": null,
-        "name": "crowdsec-hub",
-        "passthru": null,
-        "pinned": false,
-        "src": {
-            "deepClone": false,
-            "fetchSubmodules": false,
-            "leaveDotGit": false,
-            "name": null,
-            "owner": "crowdsecurity",
-            "repo": "hub",
-            "rev": "850614b9fcd4298f559b422c5ac685a69aa2e5ff",
-            "sha256": "sha256-96MMwFN5KongQA3YJVSuk7Kanbr1gR94CCyiflmez2k=",
-            "type": "github"
-        },
-        "version": "850614b9fcd4298f559b422c5ac685a69aa2e5ff"
-    }
-}
\ No newline at end of file
diff --git a/pkgs/crowdsec/_sources/generated.nix b/pkgs/crowdsec/_sources/generated.nix
deleted file mode 100644
index 19a7f5a..0000000
--- a/pkgs/crowdsec/_sources/generated.nix
+++ /dev/null
@@ -1,27 +0,0 @@
-# This file was generated by nvfetcher, please do not modify it manually.
-{ fetchgit, fetchurl, fetchFromGitHub, dockerTools }:
-{
-  crowdsec-firewall-bouncer = {
-    pname = "crowdsec-firewall-bouncer";
-    version = "v0.0.31";
-    src = fetchFromGitHub {
-      owner = "crowdsecurity";
-      repo = "cs-firewall-bouncer";
-      rev = "v0.0.31";
-      fetchSubmodules = false;
-      sha256 = "sha256-59MWll8v00CF4WA53gjHZSTFc8hpYaHENg9O7LgTCrA=";
-    };
-  };
-  crowdsec-hub = {
-    pname = "crowdsec-hub";
-    version = "850614b9fcd4298f559b422c5ac685a69aa2e5ff";
-    src = fetchFromGitHub {
-      owner = "crowdsecurity";
-      repo = "hub";
-      rev = "850614b9fcd4298f559b422c5ac685a69aa2e5ff";
-      fetchSubmodules = false;
-      sha256 = "sha256-96MMwFN5KongQA3YJVSuk7Kanbr1gR94CCyiflmez2k=";
-    };
-    date = "2025-05-17";
-  };
-}
diff --git a/pkgs/crowdsec/default.nix b/pkgs/crowdsec/default.nix
deleted file mode 100644
index 66faac3..0000000
--- a/pkgs/crowdsec/default.nix
+++ /dev/null
@@ -1,9 +0,0 @@
-{ pkgs }:
-let
-  sources = pkgs.callPackage ./_sources/generated.nix { };
-  callPackage = pkgs.lib.callPackageWith (pkgs // { inherit sources; });
-in
-{
-  hub = callPackage ./hub.nix { };
-  firewall-bouncer = callPackage ./firewall-bouncer.nix { };
-}
diff --git a/pkgs/crowdsec/firewall-bouncer.nix b/pkgs/crowdsec/firewall-bouncer.nix
deleted file mode 100644
index 86370c4..0000000
--- a/pkgs/crowdsec/firewall-bouncer.nix
+++ /dev/null
@@ -1,26 +0,0 @@
-{
-  lib,
-  sources,
-  buildGoModule,
-  envsubst,
-  coreutils,
-}:
-let
-  envsubstBin = lib.getExe envsubst;
-in
-buildGoModule {
-  inherit (sources.crowdsec-firewall-bouncer) pname version src;
-
-  vendorHash = "sha256-7Jxvg8UEjUxnIz1llvXyI2AefJ31OVdNzhWD/C8wU/Y=";
-
-  postInstall = ''
-    mkdir -p $out/lib/systemd/system
-
-    CFG=/var/lib/crowdsec/config BIN=$out/bin/cs-firewall-bouncer ${envsubstBin} \
-      -i ./config/crowdsec-firewall-bouncer.service \
-      -o $out/lib/systemd/system/crowdsec-firewall-bouncer.service
-
-    substituteInPlace $out/lib/systemd/system/crowdsec-firewall-bouncer.service \
-      --replace-fail /bin/sleep ${coreutils}/bin/sleep
-  '';
-}
diff --git a/pkgs/crowdsec/hub.nix b/pkgs/crowdsec/hub.nix
deleted file mode 100644
index 1b8c9b3..0000000
--- a/pkgs/crowdsec/hub.nix
+++ /dev/null
@@ -1 +0,0 @@
-{ sources }: sources.crowdsec-hub.src
diff --git a/pkgs/crowdsec/nvfetcher.toml b/pkgs/crowdsec/nvfetcher.toml
deleted file mode 100644
index 2287dba..0000000
--- a/pkgs/crowdsec/nvfetcher.toml
+++ /dev/null
@@ -1,7 +0,0 @@
-[crowdsec-hub]
-src.git = "https://github.com/crowdsecurity/hub.git"
-fetch.github = "crowdsecurity/hub"
-
-[crowdsec-firewall-bouncer]
-src.github = "crowdsecurity/cs-firewall-bouncer"
-fetch.github = "crowdsecurity/cs-firewall-bouncer"
diff --git a/pkgs/default.nix b/pkgs/default.nix
index 0e5de7a..3162787 100644
--- a/pkgs/default.nix
+++ b/pkgs/default.nix
@@ -1,5 +1,22 @@
-{ pkgs }:
 {
-  crowdsec = import ./crowdsec { inherit pkgs; };
-  starbound = pkgs.callPackage ./starbound { };
-}
+  pkgs,
+  lib,
+}: let
+  inherit (builtins) fromJSON mapAttrs readFile;
+  inherit (pkgs) callPackage;
+in
+  {
+    starbound = callPackage ./starbound {};
+    prometheus-fail2ban-exporter = callPackage ./prometheus/fail2ban-exporter.nix {
+      sources = pkgs.callPackage ./_sources_pkgs/generated.nix {};
+    };
+    afvalcalendar = callPackage ./afvalcalendar {};
+  }
+  // (
+    # Add nextcloud apps
+    let
+      mkNextcloudApp = pkgs.callPackage ./mkNextcloudApp.nix {};
+      sources = fromJSON (readFile ./_sources_nextcloud/generated.json);
+    in
+      mapAttrs (_: source: mkNextcloudApp source) sources
+  )
diff --git a/pkgs/mkNextcloudApp.nix b/pkgs/mkNextcloudApp.nix
new file mode 100644
index 0000000..9bf6b26
--- /dev/null
+++ b/pkgs/mkNextcloudApp.nix
@@ -0,0 +1,9 @@
+{
+  fetchNextcloudApp,
+  lib,
+}: source:
+fetchNextcloudApp {
+  url = source.src.url;
+  sha256 = source.src.sha256;
+  license = "unlicense"; # Blatant lie
+}
diff --git a/pkgs/nextcloud-apps.toml b/pkgs/nextcloud-apps.toml
new file mode 100644
index 0000000..36566db
--- /dev/null
+++ b/pkgs/nextcloud-apps.toml
@@ -0,0 +1,31 @@
+[bookmarks]
+# src.github = "nextcloud/bookmarks"
+src.prefix = "v"
+src.manual = "v13.1.3"
+fetch.tarball = "https://github.com/nextcloud/bookmarks/releases/download/v$ver/bookmarks-$ver.tar.gz"
+
+[calendar]
+# src.github = "nextcloud-releases/calendar"
+src.manual = "v4.6.7"
+fetch.tarball = "https://github.com/nextcloud-releases/calendar/releases/download/$ver/calendar-$ver.tar.gz"
+
+[contacts]
+# src.github = "nextcloud-releases/contacts"
+src.manual = "v5.5.3"
+fetch.tarball = "https://github.com/nextcloud-releases/contacts/releases/download/$ver/contacts-$ver.tar.gz"
+
+[cookbook]
+# src.github = "christianlupus-nextcloud/cookbook-releases"
+src.prefix = "v"
+src.manual = "0.10.5"
+fetch.tarball = "https://github.com/christianlupus-nextcloud/cookbook-releases/releases/download/v$ver/Cookbook-$ver.tar.gz"
+
+[news]
+# src.github = "nextcloud/news"
+# Update to 25 when angular rewrite is done/the alpha when I need to switch to nextcloud 28+
+src.manual = "24.0.0"
+fetch.tarball = "https://github.com/nextcloud/news/releases/download/$ver/news.tar.gz"
+
+[notes]
+src.github = "nextcloud/notes"
+fetch.tarball = "https://github.com/nextcloud/notes/releases/download/$ver/notes.tar.gz"
diff --git a/pkgs/nvfetcher.toml b/pkgs/nvfetcher.toml
new file mode 100644
index 0000000..d0dfbe5
--- /dev/null
+++ b/pkgs/nvfetcher.toml
@@ -0,0 +1,3 @@
+[prometheus-fail2ban-exporter]
+src.manual = "v0.10.1" # No gitlab support in nvfetcher
+fetch.git = "https://gitlab.com/hectorjsmith/fail2ban-prometheus-exporter"
diff --git a/pkgs/prometheus/fail2ban-exporter.nix b/pkgs/prometheus/fail2ban-exporter.nix
new file mode 100644
index 0000000..b2c6a25
--- /dev/null
+++ b/pkgs/prometheus/fail2ban-exporter.nix
@@ -0,0 +1,8 @@
+{
+  buildGoModule,
+  sources,
+}:
+buildGoModule {
+  inherit (sources.prometheus-fail2ban-exporter) pname src version;
+  vendorHash = "sha256-5o8p5p0U/c0WAIV5dACnWA3ThzSh2tt5LIFMb59i9GY=";
+}
diff --git a/pkgs/starbound/default.nix b/pkgs/starbound/default.nix
index 26f2184..304f0f3 100644
--- a/pkgs/starbound/default.nix
+++ b/pkgs/starbound/default.nix
@@ -5,33 +5,30 @@
   patchelf,
   steamPackages,
   replace-secret,
-}:
-let
+}: let
   # Use the directory in which starbound is installed so steamcmd
   # doesn't have to be reinstalled constantly (we're using DynamicUser
   # with StateDirectory to persist this).
-  steamcmd = steamPackages.steamcmd.override { steamRoot = "/var/lib/starbound/.steamcmd"; };
-  wrapperPath = lib.makeBinPath [
-    patchelf
-    steamcmd
-    replace-secret
-  ];
+  steamcmd = steamPackages.steamcmd.override {
+    steamRoot = "/var/lib/starbound/.steamcmd";
+  };
+  wrapperPath = lib.makeBinPath [patchelf steamcmd replace-secret];
 in
-stdenv.mkDerivation {
-  name = "starbound-update-script";
-  nativeBuildInputs = [ makeWrapper ];
-  dontUnpack = true;
-  patchPhase = ''
-    interpreter="$(cat $NIX_CC/nix-support/dynamic-linker)"
-    substitute ${./launch-starbound.sh} launch-starbound --subst-var interpreter
-  '';
-  installPhase = ''
-    mkdir -p $out/bin
-    cp launch-starbound $out/bin/launch-starbound
-    chmod +x $out/bin/launch-starbound
-  '';
-  postFixup = ''
-    wrapProgram $out/bin/launch-starbound \
-        --prefix PATH : "${wrapperPath}"
-  '';
-}
+  stdenv.mkDerivation {
+    name = "starbound-update-script";
+    nativeBuildInputs = [makeWrapper];
+    dontUnpack = true;
+    patchPhase = ''
+      interpreter="$(cat $NIX_CC/nix-support/dynamic-linker)"
+      substitute ${./launch-starbound.sh} launch-starbound --subst-var interpreter
+    '';
+    installPhase = ''
+      mkdir -p $out/bin
+      cp launch-starbound $out/bin/launch-starbound
+      chmod +x $out/bin/launch-starbound
+    '';
+    postFixup = ''
+      wrapProgram $out/bin/launch-starbound \
+          --prefix PATH : "${wrapperPath}"
+    '';
+  }