katja's git: nixfiles

ctucx' nixfiles

commit aa014869797cd5ce5e54acc01d8e6ba32f593f55
parent 032fd98d74c0b3a8268e8c2126e852aa88e86467
Author: Katja (ctucx) <git@ctu.cx>
Date: Sat, 26 Apr 2025 17:09:51 +0200

configurations/websites: move `git.ctu.cx` to `git.katja.wtf` (and to host `rabbit`)
11 files changed, 424 insertions(+), 418 deletions(-)
M
configurations/homeManager/programs/ssh.nix
|
2
+-
D
configurations/nixos/websites/bikemap.ctu.cx.nix
|
94
-------------------------------------------------------------------------------
A
configurations/nixos/websites/bikemap.katja.wtf.nix
|
94
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
D
configurations/nixos/websites/git.ctu.cx.nix
|
253
-------------------------------------------------------------------------------
A
configurations/nixos/websites/git.katja.wtf.nix
|
261
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
M
flake.lock
|
82
++++++++++++++++++++++++++++++++++++++++----------------------------------------
M
flake.nix
|
22
+++++++++++-----------
M
nodes/hector/default.nix
|
7
-------
M
nodes/rabbit/default.nix
|
4
++++
D
secrets/hector/restic/gitolite.age
|
11
-----------
A
secrets/rabbit/restic/gitolite.age
|
12
++++++++++++
diff --git a/configurations/homeManager/programs/ssh.nix b/configurations/homeManager/programs/ssh.nix
@@ -12,7 +12,7 @@
     matchBlocks = {
       "ctucx-git" = {
         user     = "git";
-        hostname = "hector.ctu.cx";
+        hostname = "git.katja.wtf";
       };
     };
   };
diff --git a/configurations/nixos/websites/bikemap.ctu.cx.nix b/configurations/nixos/websites/bikemap.ctu.cx.nix
@@ -1,94 +0,0 @@
-{ config, pkgs, ... }:
-
-let
-
-  deployScript = pkgs.writeShellScript "deploy" ''
-    systemctl start deploy-bikemap;
-    systemctl status deploy-bikemap;
-  '';
-
-in {
-
-  dns.zones."ctu.cx".subdomains.bikemap.CNAME = [ "${config.networking.fqdn}." ];
-
-  users.users."bikemap" = {
-    home = "/var/lib/bikemap";
-    group = "git";
-    isSystemUser = true;
-  };
-
-  security.sudo.extraRules = [{
-    users    = [ "git" ];
-    commands = [
-      { command = "${deployScript}"; options = [ "SETENV" "NOPASSWD" ]; }
-    ];
-  }];
-
-  systemd.services.deploy-bikemap = {
-    script = ''
-      # strict mode
-      set -euo pipefail
-      IFS=$'\n\t'
-
-      TMP_DIR=$(mktemp -d)
-      trap "{ rm -rf "$TMP_DIR"; }" SIGINT SIGTERM ERR EXIT
-
-      ${pkgs.git}/bin/git clone /var/lib/gitolite/repositories/biketracks.git $TMP_DIR/tracks
-
-      mkdir $TMP_DIR/tiles
-
-      ${pkgs.generateTilesFromGPX}/bin/generateTilesFromGPX $TMP_DIR/tracks $TMP_DIR/tiles
-
-      rm -rf ~/*;
-
-      ln -sf ${pkgs.gpx-map}/index.html ~/index.html
-      ln -sf ${pkgs.gpx-map}/bundle.js  ~/bundle.js
-      mv     $TMP_DIR/tiles             ~/tiles;
-      echo "{\"lastUpdated\":\"$(date +"%Y-%m-%d %H:%M")\"}" > ~/lastUpdated.json
-    '';
-
-    serviceConfig = {
-      Type = "oneshot";
-
-      User  = "bikemap";
-      Group = "git";
-
-      WorkingDirectory        = "~";
-      StateDirectory          = "bikemap";
-      StateDirectoryMode      = "755";
-
-      NoNewPrivileges         = true;
-      PrivateTmp              = true;
-      PrivateDevices          = true;
-
-      RestrictAddressFamilies = "none";
-      RestrictNamespaces      = true;
-      RestrictRealtime        = true;
-
-      ProtectSystem           = "full";
-      ProtectControlGroups    = true;
-      ProtectKernelModules    = true;
-      ProtectKernelTunables   = true;
-
-      DevicePolicy            = "closed";
-      LockPersonality         = true;
-    };
-  };
-
-  services = {
-    gitolite.commonHooks.post-receive = ''
-      #deploy bikemap
-      [ "$GL_REPO" == "biketracks" ] && sudo ${deployScript}
-    '';
-
-    nginx = {
-      enable = true;
-      virtualHosts."bikemap.ctu.cx" = {
-        useACMEHost = "${config.networking.fqdn}";
-        forceSSL    = true;
-        kTLS        = true;
-        root        = "/var/lib/bikemap/";
-      };
-    };
-  };
-}
diff --git a/configurations/nixos/websites/bikemap.katja.wtf.nix b/configurations/nixos/websites/bikemap.katja.wtf.nix
@@ -0,0 +1,94 @@
+{ config, pkgs, ... }:
+
+let
+
+  deployScript = pkgs.writeShellScript "deploy" ''
+    systemctl start deploy-bikemap;
+    systemctl status deploy-bikemap;
+  '';
+
+in {
+
+  dns.zones."katja.wtf".subdomains.bikemap.CNAME = [ "${config.networking.fqdn}." ];
+
+  users.users."bikemap" = {
+    home = "/var/lib/bikemap";
+    group = "git";
+    isSystemUser = true;
+  };
+
+  security.sudo.extraRules = [{
+    users    = [ "git" ];
+    commands = [
+      { command = "${deployScript}"; options = [ "SETENV" "NOPASSWD" ]; }
+    ];
+  }];
+
+  systemd.services.deploy-bikemap = {
+    script = ''
+      # strict mode
+      set -euo pipefail
+      IFS=$'\n\t'
+
+      TMP_DIR=$(mktemp -d)
+      trap "{ rm -rf "$TMP_DIR"; }" SIGINT SIGTERM ERR EXIT
+
+      ${pkgs.git}/bin/git clone /var/lib/gitolite/repositories/biketracks.git $TMP_DIR/tracks
+
+      mkdir $TMP_DIR/tiles
+
+      ${pkgs.generateTilesFromGPX}/bin/generateTilesFromGPX $TMP_DIR/tracks $TMP_DIR/tiles
+
+      rm -rf ~/*;
+
+      ln -sf ${pkgs.gpx-map}/index.html ~/index.html
+      ln -sf ${pkgs.gpx-map}/bundle.js  ~/bundle.js
+      mv     $TMP_DIR/tiles             ~/tiles;
+      echo "{\"lastUpdated\":\"$(date +"%Y-%m-%d %H:%M")\"}" > ~/lastUpdated.json
+    '';
+
+    serviceConfig = {
+      Type = "oneshot";
+
+      User  = "bikemap";
+      Group = "git";
+
+      WorkingDirectory        = "~";
+      StateDirectory          = "bikemap";
+      StateDirectoryMode      = "755";
+
+      NoNewPrivileges         = true;
+      PrivateTmp              = true;
+      PrivateDevices          = true;
+
+      RestrictAddressFamilies = "none";
+      RestrictNamespaces      = true;
+      RestrictRealtime        = true;
+
+      ProtectSystem           = "full";
+      ProtectControlGroups    = true;
+      ProtectKernelModules    = true;
+      ProtectKernelTunables   = true;
+
+      DevicePolicy            = "closed";
+      LockPersonality         = true;
+    };
+  };
+
+  services = {
+    gitolite.commonHooks.post-receive = ''
+      #deploy bikemap
+      [ "$GL_REPO" == "biketracks" ] && sudo ${deployScript}
+    '';
+
+    nginx = {
+      enable = true;
+      virtualHosts."bikemap.katja.wtf" = {
+        useACMEHost = "${config.networking.fqdn}";
+        forceSSL    = true;
+        kTLS        = true;
+        root        = "/var/lib/bikemap/";
+      };
+    };
+  };
+}
diff --git a/configurations/nixos/websites/git.ctu.cx.nix b/configurations/nixos/websites/git.ctu.cx.nix
@@ -1,253 +0,0 @@
-{ secrets, config, lib, pkgs, ... }:
-
-let
-  stagitFunctions = pkgs.writeShellScript "stagitFunctions" ''
-    export LC_CTYPE="en_US.UTF-8"
-
-    is_public_and_listed() {
-      if [ ! -f "$1/git-daemon-export-ok" ]; then
-        return 1
-      fi
-      return 0
-    }
-
-    is_forced_update() {
-      test "$oldrev" = "0000000000000000000000000000000000000000" && return 1
-      test "$newrev" = "0000000000000000000000000000000000000000" && return 1
-
-      hasrevs="$(${pkgs.git}/bin/git rev-list "$oldrev" "^$newrev" | ${pkgs.gnused}/bin/sed 1q)"
-      if test -n "$hasrevs"; then
-        return 0
-      fi
-      return 1
-    }
-
-    build_stagit_repo() {
-      reponame="$(basename "$1" ".git")"
-      printf "[%s] Generate stagit HTML pages... " "$reponame"
-
-      mkdir -p "/var/lib/gitolite/stagit-cache"
-      mkdir -p "/var/lib/stagit/$reponame"
-
-      cd "/var/lib/stagit/$reponame" || return 1
-
-      # build repo pages
-      ${pkgs.stagit}/bin/stagit -c "/var/lib/gitolite/stagit-cache/$reponame" -n 'ctucx.git' -h 'https://git.ctu.cx/' -s 'git@${config.networking.hostName}.ctu.cx:' "$1"
-
-      # set correct permissions
-      chown git:git -R /var/lib/stagit/$reponame;
-      chmod 755 -R /var/lib/stagit/$reponame;
-
-      echo "done"
-    }
-
-    build_stagit_index() {
-      printf "Generating stagit index... "
-
-      # set assets if not already there
-      ln -sf "${pkgs.stagit}/share/doc/stagit/highlight.min.js" "/var/lib/stagit/highlight.min.js" 2> /dev/null
-      ln -sf "${pkgs.stagit}/share/doc/stagit/style.css"        "/var/lib/stagit/style.css" 2> /dev/null
-
-      # generate index arguments
-      args="-n 'ctucx.git' -e 'git@ctu.cx'"
-
-      for category in "nix" "etc" "javascript" "nimlang" "nimlang libraries" "archive"; do
-        args="$args -c '$category'"
-        for repo in "$HOME/repositories/"*.git/; do
-          repo="''${repo%/}"
-          is_public_and_listed "$repo" || continue
-
-          [ "$(${pkgs.gawk}/bin/awk -F '=' '/category/ {print $2}' $repo/config | ${pkgs.gnused}/bin/sed -e 's/^[[:space:]]*//')" = "$category" ] && args="$args $repo"
-        done
-      done
-
-      # build index
-      echo "$args" | xargs ${pkgs.stagit}/bin/stagit-index > /var/lib/stagit/index.html
-
-      # set correct permissions
-      chown git:git /var/lib/stagit/index.html;
-      chmod 755 /var/lib/stagit/index.html;
-
-      echo "done"
-    }
-
-
-    update_stagit_repo() {
-      repo="$(pwd)"
-      reponame="$(basename "$repo" ".git")"
-
-      cd "$repo" || return 1
-      is_public_and_listed "$repo" || return 0
-
-      # if forced update, remove directory and cache file
-      is_forced_update && printf "[%s] Forced update, trigger complete regeneration of stagit-pages... \n" "$reponame" && rm -rf "/var/lib/stagit/$reponame" "/var/lib/gitolite/stagit-cache/$reponame"
-
-      build_stagit_repo "$repo"
-      build_stagit_index
-    }
-
-  '';
-
-  rebuildWebdir = ''
-    source ${stagitFunctions}
-
-    # clear webdir
-    rm -rf /var/lib/stagit/*
-
-    # clear cache
-    rm -rf /var/lib/gitolite/stagit-cache/*
-
-    # generate pages per repo
-    for repo in "$HOME/repositories/"*.git/; do
-      repo="''${repo%/}"
-      is_public_and_listed "$repo" || continue
-
-      build_stagit_repo "$repo"
-    done
-
-    # generate index page
-    build_stagit_index
-  '';
-
-in {
-
-  services.openssh.settings.AllowUsers = [ "git" ];
-  services.openssh.settings.Macs = [
-    "hmac-sha2-512"
-    "hmac-sha2-512-etm@openssh.com"
-    "hmac-sha2-256-etm@openssh.com"
-    "umac-128-etm@openssh.com"
-  ];
-
-  dns.zones."ctu.cx".subdomains = {
-    cgit.CNAME = [ "${config.networking.fqdn}." ];
-    git.CNAME  = [ "${config.networking.fqdn}." ];
-  };
-
-  age.secrets.resticGitolite.file = secrets."${config.networking.hostName}".restic.gitolite;
-
-  restic-backups.gitolite = {
-    user         = "git";
-    passwordFile = config.age.secrets.resticGitolite.path;
-    paths        = [ "/var/lib/gitolite" ];
-  };
-
-  services = {
-    gitolite = {
-      enable      = true;
-      user        = "git";
-      group       = "git";
-      adminPubkey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDb2eZ2ymt+Zsf0eTlmjW2jPdS013lbde1+EGkgu6bz9lVTR8aawshF2HcoaWp5a5dJr3SKyihDM8hbWSYB3qyTHihNGyCArqSvAtZRw301ailRVHGqiwUITTfcg1533TtmWvlJZgOIFM1VvSAfdueDRRRzbygmn749fS9nhUTDzLtjqX5LvhpqhzsD+eOqPrV6Ne8E1e42JxQb5AJPY1gj9mk6eAarvtEHQYEe+/hp9ERjtCdN5DfuOJnqfaKS0ytPj/NbQskbX/TMgeUVio11iC2NbXsnAtzMmtbLX4mxlDQrR6aZmU/rHQ4aeJqI/Tj2rrF46icri7s0tnnit1OjT5PSxXgifcOtn06qoxYZMT1x+Dyrt40vNkGmxmxCnirm8B+6MKXgd/Ys+7tnOm1ht8TmLm96x6KdOiF3Zq/tMxhPAzp8JriTKSo7k7U9XxStFghTbhhBNc7OX89ZbpalLEnvbQiz87gZxhcx8cLvzIjslOHmZOSWC5Pgr4wwuj3Akq63i4ya6/BzM6v4UoBuDAB6fz3NHKL4R5X20la7Pvt7OBysQkGClWfj6ipMR1bFE2mfYtlMioXNgTjC+NCpEl1+81MH7dv2565Hk8CLV8FMxv6GujbAZGjjcM47lpWM1cBQvpBMUA/lLkyiCPK0YxNWAB7Co+jYDl6CR0Ubew== cardno:6445161";
-
-      extraGitoliteRc = ''
-        $RC{GIT_CONFIG_KEYS} = ".*";
-        $RC{UMASK}           = 0027;
-
-        push(@{$RC{ENABLE}}, 'cgit');
-        push(@{$RC{ENABLE}}, 'symbolic-ref');
-        push(@{$RC{ENABLE}}, 'rebuild-webdir');
-        push(@{$RC{ENABLE}}, 'rebuild-webdir');
-
-        $RC{NON_CORE} = "rebuild-webdir-trigger POST_COMPILE rebuild-stagit";
-      '';
-
-      triggers.rebuild-webdir = rebuildWebdir;
-      commands.rebuild-webdir = rebuildWebdir;
-      commonHooks.post-receive = ''
-        # update stagit pages
-        source ${stagitFunctions}
-        update_stagit_repo "$1"
-      '';
-    };
-
-    fcgiwrap = {
-      instances.git = {
-        process.user   = "git";
-        process.group  = "git";
-        socket.user    = "nginx";
-        socket.group   = "nginx";
-      };
-    };
-
-    nginx = {
-      enable = true;
-      virtualHosts = {
-        "cgit.ctu.cx" = {
-          useACMEHost = "${config.networking.fqdn}";
-          forceSSL    = true;
-          kTLS        = true;
-          locations = {
-            "~ '^/[a-zA-Z0-9._-]+/(git-(receive|upload)-pack|HEAD|info/refs|objects/(info/(http-)?alternates|packs)|[0-9a-f]{2}/[0-9a-f]{38}|pack/pack-[0-9a-f]{40}\.(pack|idx))$'".return = "307 https://git.ctu.cx$request_uri";
-            "~ '^/([a-zA-Z0-9_.]+)/*$'".return                                      = "307 https://git.ctu.cx/$1";
-            "~ '^/([a-zA-Z0-9_.]+)/tree/([a-zA-Z0-9_./-]+[a-zA-Z0-9_-])/*$'".return = "307 https://git.ctu.cx/$1/tree/$2.html";
-            "~ '^/([a-zA-Z0-9_.]+)/tree/*$'".return                                 = "307 https://git.ctu.cx/$1/tree.html";
-            "~ '^/([a-zA-Z0-9_.]+)/log/*$'".return                                  = "307 https://git.ctu.cx/$1/log.html";
-            "~ '^/([a-zA-Z0-9_.]+)/commit/*$'".extraConfig = ''
-              if ($arg_id) {
-                return 307 https://git.ctu.cx/$1/commit/$arg_id.html;
-              }
-
-              return 307 https://git.ctu.cx/$1/log.html;
-            '';
-          };
-        };
-
-        "git.ctu.cx" = {
-          useACMEHost = "${config.networking.fqdn}";
-          forceSSL    = true;
-          kTLS        = true;
-          root        = "/var/lib/stagit";
-          locations = {
-            "@redir".return = "307 ../log.html";
-            "~ '^/([a-zA-Z0-9_.]+)/commit/.*$'".extraConfig = "error_page 404 = @redir;";
-
-            "~* \.html$".extraConfig = ''
-              add_header Last-Modified $date_gmt;
-              add_header Cache-Control 'private no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
-              if_modified_since off;
-              expires off;
-              etag off;
-            '';
-
-            "~ '^/[a-zA-Z0-9._-]+/raw'".extraConfig = ''
-              types {
-                application/json                                 json;
-
-                application/wasm                                 wasm;
-                font/woff                                        woff;
-                font/woff2                                       woff2;
-
-                application/pdf                                  pdf;
-
-                image/gif                                        gif;
-                image/jpeg                                       jpeg jpg;
-                image/png                                        png;
-                image/svg+xml                                    svg svgz;
-                image/webp                                       webp;
-                image/x-icon                                     ico;
-              }
-
-              default_type   text/plain;
-              try_files $uri =404;
-            '';
-
-            "~ '^/[a-zA-Z0-9._-]+/(git-(receive|upload)-pack|HEAD|info/refs|objects/(info/(http-)?alternates|packs)|[0-9a-f]{2}/[0-9a-f]{38}|pack/pack-[0-9a-f]{40}\.(pack|idx))$'".extraConfig = ''
-              if ($query_string = service=git-receive-pack) {
-                return 403;
-              }
-
-              include "${pkgs.nginx}/conf/fastcgi_params";
-              fastcgi_param SCRIPT_FILENAME  "${pkgs.git}/libexec/git-core/git-http-backend";
-              fastcgi_param GIT_PROJECT_ROOT /var/lib/gitolite/repositories;
-              fastcgi_param PATH_INFO        $uri;
-              fastcgi_pass  unix:${config.services.fcgiwrap.instances.git.socket.address};
-            '';
-          };
-        };
-
-      };
-    };
-
-  };
-
-}
diff --git a/configurations/nixos/websites/git.katja.wtf.nix b/configurations/nixos/websites/git.katja.wtf.nix
@@ -0,0 +1,261 @@
+{ secrets, config, lib, pkgs, ... }:
+
+let
+  stagitFunctions = pkgs.writeShellScript "stagitFunctions" ''
+    export LC_CTYPE="en_US.UTF-8"
+
+    is_public_and_listed() {
+      if [ ! -f "$1/git-daemon-export-ok" ]; then
+        return 1
+      fi
+      return 0
+    }
+
+    is_forced_update() {
+      test "$oldrev" = "0000000000000000000000000000000000000000" && return 1
+      test "$newrev" = "0000000000000000000000000000000000000000" && return 1
+
+      hasrevs="$(${pkgs.git}/bin/git rev-list "$oldrev" "^$newrev" | ${pkgs.gnused}/bin/sed 1q)"
+      if test -n "$hasrevs"; then
+        return 0
+      fi
+      return 1
+    }
+
+    build_stagit_repo() {
+      reponame="$(basename "$1" ".git")"
+      printf "[%s] Generate stagit HTML pages... " "$reponame"
+
+      mkdir -p "/var/lib/gitolite/stagit-cache"
+      mkdir -p "/var/lib/stagit/$reponame"
+
+      cd "/var/lib/stagit/$reponame" || return 1
+
+      # build repo pages
+      ${pkgs.stagit}/bin/stagit -c "/var/lib/gitolite/stagit-cache/$reponame" -n 'ctucx.git' -h 'https://git.ctu.cx/' -s 'git@${config.networking.hostName}.ctu.cx:' "$1"
+
+      # set correct permissions
+      chown git:git -R /var/lib/stagit/$reponame;
+      chmod 755 -R /var/lib/stagit/$reponame;
+
+      echo "done"
+    }
+
+    build_stagit_index() {
+      printf "Generating stagit index... "
+
+      # set assets if not already there
+      ln -sf "${pkgs.stagit}/share/doc/stagit/highlight.min.js" "/var/lib/stagit/highlight.min.js" 2> /dev/null
+      ln -sf "${pkgs.stagit}/share/doc/stagit/style.css"        "/var/lib/stagit/style.css" 2> /dev/null
+
+      # generate index arguments
+      args="-n 'ctucx.git' -e 'git@ctu.cx'"
+
+      for category in "nix" "etc" "javascript" "nimlang" "nimlang libraries" "archive"; do
+        args="$args -c '$category'"
+        for repo in "$HOME/repositories/"*.git/; do
+          repo="''${repo%/}"
+          is_public_and_listed "$repo" || continue
+
+          [ "$(${pkgs.gawk}/bin/awk -F '=' '/category/ {print $2}' $repo/config | ${pkgs.gnused}/bin/sed -e 's/^[[:space:]]*//')" = "$category" ] && args="$args $repo"
+        done
+      done
+
+      # build index
+      echo "$args" | xargs ${pkgs.stagit}/bin/stagit-index > /var/lib/stagit/index.html
+
+      # set correct permissions
+      chown git:git /var/lib/stagit/index.html;
+      chmod 755 /var/lib/stagit/index.html;
+
+      echo "done"
+    }
+
+
+    update_stagit_repo() {
+      repo="$(pwd)"
+      reponame="$(basename "$repo" ".git")"
+
+      cd "$repo" || return 1
+      is_public_and_listed "$repo" || return 0
+
+      # if forced update, remove directory and cache file
+      is_forced_update && printf "[%s] Forced update, trigger complete regeneration of stagit-pages... \n" "$reponame" && rm -rf "/var/lib/stagit/$reponame" "/var/lib/gitolite/stagit-cache/$reponame"
+
+      build_stagit_repo "$repo"
+      build_stagit_index
+    }
+
+  '';
+
+  rebuildWebdir = ''
+    source ${stagitFunctions}
+
+    # clear webdir
+    rm -rf /var/lib/stagit/*
+
+    # clear cache
+    rm -rf /var/lib/gitolite/stagit-cache/*
+
+    # generate pages per repo
+    for repo in "$HOME/repositories/"*.git/; do
+      repo="''${repo%/}"
+      is_public_and_listed "$repo" || continue
+
+      build_stagit_repo "$repo"
+    done
+
+    # generate index page
+    build_stagit_index
+  '';
+
+in {
+
+  services.openssh.settings.AllowUsers = [ "git" ];
+  services.openssh.settings.Macs = [
+    "hmac-sha2-512"
+    "hmac-sha2-512-etm@openssh.com"
+    "hmac-sha2-256-etm@openssh.com"
+    "umac-128-etm@openssh.com"
+  ];
+
+  dns.zones = {
+    "katja.wtf".subdomains.git.CNAME  = [ "${config.networking.fqdn}." ];
+    "ctu.cx".subdomains.git.CNAME     = [ "${config.networking.fqdn}." ];
+    "ctu.cx".subdomains.cgit.CNAME    = [ "${config.networking.fqdn}." ];
+  };
+
+  age.secrets.resticGitolite.file = secrets."${config.networking.hostName}".restic.gitolite;
+
+  restic-backups.gitolite = {
+    user         = "git";
+    passwordFile = config.age.secrets.resticGitolite.path;
+    paths        = [ "/var/lib/gitolite" ];
+  };
+
+  services = {
+    gitolite = {
+      enable      = true;
+      user        = "git";
+      group       = "git";
+      adminPubkey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDb2eZ2ymt+Zsf0eTlmjW2jPdS013lbde1+EGkgu6bz9lVTR8aawshF2HcoaWp5a5dJr3SKyihDM8hbWSYB3qyTHihNGyCArqSvAtZRw301ailRVHGqiwUITTfcg1533TtmWvlJZgOIFM1VvSAfdueDRRRzbygmn749fS9nhUTDzLtjqX5LvhpqhzsD+eOqPrV6Ne8E1e42JxQb5AJPY1gj9mk6eAarvtEHQYEe+/hp9ERjtCdN5DfuOJnqfaKS0ytPj/NbQskbX/TMgeUVio11iC2NbXsnAtzMmtbLX4mxlDQrR6aZmU/rHQ4aeJqI/Tj2rrF46icri7s0tnnit1OjT5PSxXgifcOtn06qoxYZMT1x+Dyrt40vNkGmxmxCnirm8B+6MKXgd/Ys+7tnOm1ht8TmLm96x6KdOiF3Zq/tMxhPAzp8JriTKSo7k7U9XxStFghTbhhBNc7OX89ZbpalLEnvbQiz87gZxhcx8cLvzIjslOHmZOSWC5Pgr4wwuj3Akq63i4ya6/BzM6v4UoBuDAB6fz3NHKL4R5X20la7Pvt7OBysQkGClWfj6ipMR1bFE2mfYtlMioXNgTjC+NCpEl1+81MH7dv2565Hk8CLV8FMxv6GujbAZGjjcM47lpWM1cBQvpBMUA/lLkyiCPK0YxNWAB7Co+jYDl6CR0Ubew== cardno:6445161";
+
+      extraGitoliteRc = ''
+        $RC{GIT_CONFIG_KEYS} = ".*";
+        $RC{UMASK}           = 0027;
+
+        push(@{$RC{ENABLE}}, 'cgit');
+        push(@{$RC{ENABLE}}, 'symbolic-ref');
+        push(@{$RC{ENABLE}}, 'rebuild-webdir');
+        push(@{$RC{ENABLE}}, 'rebuild-webdir');
+
+        $RC{NON_CORE} = "rebuild-webdir-trigger POST_COMPILE rebuild-stagit";
+      '';
+
+      triggers.rebuild-webdir = rebuildWebdir;
+      commands.rebuild-webdir = rebuildWebdir;
+      commonHooks.post-receive = ''
+        # update stagit pages
+        source ${stagitFunctions}
+        update_stagit_repo "$1"
+      '';
+    };
+
+    fcgiwrap = {
+      instances.git = {
+        process.user   = "git";
+        process.group  = "git";
+        socket.user    = "nginx";
+        socket.group   = "nginx";
+      };
+    };
+
+    nginx = {
+      enable = true;
+      virtualHosts = {
+        "cgit.ctu.cx" = {
+          useACMEHost = "${config.networking.fqdn}";
+          forceSSL    = true;
+          kTLS        = true;
+          locations = {
+            "~ '^/[a-zA-Z0-9._-]+/(git-(receive|upload)-pack|HEAD|info/refs|objects/(info/(http-)?alternates|packs)|[0-9a-f]{2}/[0-9a-f]{38}|pack/pack-[0-9a-f]{40}\.(pack|idx))$'".return = "307 https://git.katja.wtf$request_uri";
+            "~ '^/([a-zA-Z0-9_.]+)/*$'".return                                      = "307 https://git.katja.wtf/$1";
+            "~ '^/([a-zA-Z0-9_.]+)/tree/([a-zA-Z0-9_./-]+[a-zA-Z0-9_-])/*$'".return = "307 https://git.katja.wtf/$1/tree/$2.html";
+            "~ '^/([a-zA-Z0-9_.]+)/tree/*$'".return                                 = "307 https://git.katja.wtf/$1/tree.html";
+            "~ '^/([a-zA-Z0-9_.]+)/log/*$'".return                                  = "307 https://git.katja.wtf/$1/log.html";
+            "~ '^/([a-zA-Z0-9_.]+)/commit/*$'".extraConfig = ''
+              if ($arg_id) {
+                return 307 https://git.katja.wtf/$1/commit/$arg_id.html;
+              }
+
+              return 307 https://git.katja.wtf/$1/log.html;
+            '';
+          };
+        };
+
+        "git.ctu.cx" = {
+          useACMEHost = "${config.networking.fqdn}";
+          forceSSL    = true;
+          kTLS        = true;
+          locations."/".return = "307 https://git.katja.wtf$request_uri";
+        };
+
+        "git.katja.wtf" = {
+          useACMEHost = "${config.networking.fqdn}";
+          forceSSL    = true;
+          kTLS        = true;
+          root        = "/var/lib/stagit";
+          locations = {
+            "@redir".return = "307 ../log.html";
+            "~ '^/([a-zA-Z0-9_.]+)/commit/.*$'".extraConfig = "error_page 404 = @redir;";
+
+            "~* \.html$".extraConfig = ''
+              add_header Last-Modified $date_gmt;
+              add_header Cache-Control 'private no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
+              if_modified_since off;
+              expires off;
+              etag off;
+            '';
+
+            "~ '^/[a-zA-Z0-9._-]+/raw'".extraConfig = ''
+              types {
+                application/json                                 json;
+
+                application/wasm                                 wasm;
+                font/woff                                        woff;
+                font/woff2                                       woff2;
+
+                application/pdf                                  pdf;
+
+                image/gif                                        gif;
+                image/jpeg                                       jpeg jpg;
+                image/png                                        png;
+                image/svg+xml                                    svg svgz;
+                image/webp                                       webp;
+                image/x-icon                                     ico;
+              }
+
+              default_type   text/plain;
+              try_files $uri =404;
+            '';
+
+            "~ '^/[a-zA-Z0-9._-]+/(git-(receive|upload)-pack|HEAD|info/refs|objects/(info/(http-)?alternates|packs)|[0-9a-f]{2}/[0-9a-f]{38}|pack/pack-[0-9a-f]{40}\.(pack|idx))$'".extraConfig = ''
+              if ($query_string = service=git-receive-pack) {
+                return 403;
+              }
+
+              include "${pkgs.nginx}/conf/fastcgi_params";
+              fastcgi_param SCRIPT_FILENAME  "${pkgs.git}/libexec/git-core/git-http-backend";
+              fastcgi_param GIT_PROJECT_ROOT /var/lib/gitolite/repositories;
+              fastcgi_param PATH_INFO        $uri;
+              fastcgi_pass  unix:${config.services.fcgiwrap.instances.git.socket.address};
+            '';
+          };
+        };
+
+      };
+    };
+
+  };
+
+}
diff --git a/flake.lock b/flake.lock
@@ -243,11 +243,11 @@
         "rev": "c460a7d103b192ce93c156fe11eaaecebf4bfb3e",
         "revCount": 40,
         "type": "git",
-        "url": "https://git.ctu.cx/gallery"
+        "url": "https://git.katja.wtf/gallery"
       },
       "original": {
         "type": "git",
-        "url": "https://git.ctu.cx/gallery"
+        "url": "https://git.katja.wtf/gallery"
       }
     },
     "ctucxThings": {

@@ -263,11 +263,11 @@
         "rev": "f2857dc6e2248501adf8f98e5d6b834496d84b1e",
         "revCount": 6,
         "type": "git",
-        "url": "https://git.ctu.cx/ctucx.things"
+        "url": "https://git.katja.wtf/ctucx.things"
       },
       "original": {
         "type": "git",
-        "url": "https://git.ctu.cx/ctucx.things"
+        "url": "https://git.katja.wtf/ctucx.things"
       }
     },
     "ctucxWebsite": {

@@ -283,11 +283,11 @@
         "rev": "4edd6c651be10b7768403a810245d146a4afab76",
         "revCount": 13,
         "type": "git",
-        "url": "https://git.ctu.cx/website"
+        "url": "https://git.katja.wtf/website"
       },
       "original": {
         "type": "git",
-        "url": "https://git.ctu.cx/website"
+        "url": "https://git.katja.wtf/website"
       }
     },
     "deploy-rs": {

@@ -363,11 +363,11 @@
         "rev": "fee803f269899172700972c9a3815e52aa741632",
         "revCount": 77,
         "type": "git",
-        "url": "https://git.ctu.cx/dns.nix"
+        "url": "https://git.katja.wtf/dns.nix"
       },
       "original": {
         "type": "git",
-        "url": "https://git.ctu.cx/dns.nix"
+        "url": "https://git.katja.wtf/dns.nix"
       }
     },
     "fenix": {

@@ -607,11 +607,11 @@
         "rev": "527a66cb5616ddae4ef737f060c8ea6856249cba",
         "revCount": 8,
         "type": "git",
-        "url": "https://git.ctu.cx/flauschehorn.sexy"
+        "url": "https://git.katja.wtf/flauschehorn.sexy"
       },
       "original": {
         "type": "git",
-        "url": "https://git.ctu.cx/flauschehorn.sexy"
+        "url": "https://git.katja.wtf/flauschehorn.sexy"
       }
     },
     "git-hooks": {

@@ -701,11 +701,11 @@
         "rev": "368358b4facb8bc55311250926eef78b0328b9a0",
         "revCount": 6,
         "type": "git",
-        "url": "https://git.ctu.cx/gpx-map"
+        "url": "https://git.katja.wtf/gpx-map"
       },
       "original": {
         "type": "git",
-        "url": "https://git.ctu.cx/gpx-map"
+        "url": "https://git.katja.wtf/gpx-map"
       }
     },
     "haumea": {

@@ -884,11 +884,11 @@
         "rev": "fbb1d8bd4521f0f08b6a690a56f6d38c86966f21",
         "revCount": 9,
         "type": "git",
-        "url": "https://git.ctu.cx/mobile-coverage-map"
+        "url": "https://git.katja.wtf/mobile-coverage-map"
       },
       "original": {
         "type": "git",
-        "url": "https://git.ctu.cx/mobile-coverage-map"
+        "url": "https://git.katja.wtf/mobile-coverage-map"
       }
     },
     "mqttWebUI": {

@@ -904,11 +904,11 @@
         "rev": "071d9f35d74c02948e93afd48906c181888ebeec",
         "revCount": 20,
         "type": "git",
-        "url": "https://git.ctu.cx/mqtt-webui"
+        "url": "https://git.katja.wtf/mqtt-webui"
       },
       "original": {
         "type": "git",
-        "url": "https://git.ctu.cx/mqtt-webui"
+        "url": "https://git.katja.wtf/mqtt-webui"
       }
     },
     "nix": {

@@ -1217,6 +1217,26 @@
         "type": "github"
       }
     },
+    "oeffisearch": {
+      "inputs": {
+        "nixpkgs": [
+          "nixpkgs"
+        ]
+      },
+      "locked": {
+        "lastModified": 1745317112,
+        "narHash": "sha256-EaTk6LH54T7f9JYzRC0EiOjGvtAX0vgfLPu89Nx9vo4=",
+        "ref": "refs/heads/main",
+        "rev": "e78fe16457a5f7911d4e616c9543c43daa398676",
+        "revCount": 291,
+        "type": "git",
+        "url": "https://git.katja.wtf/oeffisearch"
+      },
+      "original": {
+        "type": "git",
+        "url": "https://git.katja.wtf/oeffisearch"
+      }
+    },
     "pre-commit-hooks-nix": {
       "inputs": {
         "flake-compat": [

@@ -1291,9 +1311,9 @@
         "nixpkgs": "nixpkgs_6",
         "nixpkgsDarwin": "nixpkgsDarwin",
         "nixpkgsUnstable": "nixpkgsUnstable",
+        "oeffisearch": "oeffisearch",
         "simpleNixosMailserver": "simpleNixosMailserver",
         "stagit": "stagit",
-        "trainsearch": "trainsearch",
         "travelynx2fedi": "travelynx2fedi"
       }
     },

@@ -1380,11 +1400,11 @@
         "rev": "a4b05b204f1854c98e7ae68960fc1582493de890",
         "revCount": 445,
         "type": "git",
-        "url": "https://git.ctu.cx/stagit"
+        "url": "https://git.katja.wtf/stagit"
       },
       "original": {
         "type": "git",
-        "url": "https://git.ctu.cx/stagit"
+        "url": "https://git.katja.wtf/stagit"
       }
     },
     "systems": {

@@ -1402,26 +1422,6 @@
         "type": "github"
       }
     },
-    "trainsearch": {
-      "inputs": {
-        "nixpkgs": [
-          "nixpkgs"
-        ]
-      },
-      "locked": {
-        "lastModified": 1741772566,
-        "narHash": "sha256-w68dNBRR/+rjYjsjhYgaRDYNFeb0MVLL/yifwVAMnhU=",
-        "ref": "refs/heads/main",
-        "rev": "a5e86ad024d3adf0e28605470ce7ee0b8d0cf206",
-        "revCount": 227,
-        "type": "git",
-        "url": "https://git.ctu.cx/trainsearch"
-      },
-      "original": {
-        "type": "git",
-        "url": "https://git.ctu.cx/trainsearch"
-      }
-    },
     "travelynx2fedi": {
       "inputs": {
         "nixpkgs": [

@@ -1435,11 +1435,11 @@
         "rev": "606c2cdd3ce141e3c6d1c1bec0463df67a4c810b",
         "revCount": 9,
         "type": "git",
-        "url": "https://git.ctu.cx/travelynx2fedi"
+        "url": "https://git.katja.wtf/travelynx2fedi"
       },
       "original": {
         "type": "git",
-        "url": "https://git.ctu.cx/travelynx2fedi"
+        "url": "https://git.katja.wtf/travelynx2fedi"
       }
     }
   },
diff --git a/flake.nix b/flake.nix
@@ -180,7 +180,7 @@
     haumea.url = "github:nix-community/haumea";
     haumea.inputs.nixpkgs.follows = "nixpkgs";
 
-    dnsNix.url = "git+https://git.ctu.cx/dns.nix";
+    dnsNix.url = "git+https://git.katja.wtf/dns.nix";
     dnsNix.inputs.nixpkgs.follows = "nixpkgs";
 
     homeManager.url = "github:nix-community/home-manager/release-24.11";

@@ -221,34 +221,34 @@
 
     conduwuit.url = "github:girlbossceo/conduwuit";
 
-    stagit.url = "git+https://git.ctu.cx/stagit";
+    stagit.url = "git+https://git.katja.wtf/stagit";
     stagit.inputs.nixpkgs.follows = "nixpkgs";
 
-    mqttWebUI.url = "git+https://git.ctu.cx/mqtt-webui";
+    mqttWebUI.url = "git+https://git.katja.wtf/mqtt-webui";
     mqttWebUI.inputs.nixpkgs.follows = "nixpkgs";
 
-    oeffisearch.url = "git+https://git.ctu.cx/oeffisearch";
+    oeffisearch.url = "git+https://git.katja.wtf/oeffisearch";
     oeffisearch.inputs.nixpkgs.follows = "nixpkgs";
   
-    ctucxWebsite.url = "git+https://git.ctu.cx/website";
+    ctucxWebsite.url = "git+https://git.katja.wtf/website";
     ctucxWebsite.inputs.nixpkgs.follows = "nixpkgs";
 
-    ctucxThings.url = "git+https://git.ctu.cx/ctucx.things";
+    ctucxThings.url = "git+https://git.katja.wtf/ctucx.things";
     ctucxThings.inputs.nixpkgs.follows = "nixpkgs";
 
-    ctucxGallery.url = "git+https://git.ctu.cx/gallery";
+    ctucxGallery.url = "git+https://git.katja.wtf/gallery";
     ctucxGallery.inputs.nixpkgs.follows = "nixpkgs";
 
-    flauschehornSexy.url = "git+https://git.ctu.cx/flauschehorn.sexy";
+    flauschehornSexy.url = "git+https://git.katja.wtf/flauschehorn.sexy";
     flauschehornSexy.inputs.nixpkgs.follows = "nixpkgs";
 
-    mobileCoverageMap.url = "git+https://git.ctu.cx/mobile-coverage-map";
+    mobileCoverageMap.url = "git+https://git.katja.wtf/mobile-coverage-map";
     mobileCoverageMap.inputs.nixpkgs.follows = "nixpkgs";
 
-    gpxMap.url = "git+https://git.ctu.cx/gpx-map";
+    gpxMap.url = "git+https://git.katja.wtf/gpx-map";
     gpxMap.inputs.nixpkgs.follows = "nixpkgs";
 
-    travelynx2fedi.url = "git+https://git.ctu.cx/travelynx2fedi";
+    travelynx2fedi.url = "git+https://git.katja.wtf/travelynx2fedi";
     travelynx2fedi.inputs.nixpkgs.follows = "nixpkgs";
 
     firefoxGnomeTheme.flake = false;
diff --git a/nodes/hector/default.nix b/nodes/hector/default.nix
@@ -25,13 +25,6 @@
       ctucxConfig.services.dns-server
       ctucxConfig.services.syncthingNginx
 
-      # website / webservices
-      ctucxConfig.websites."bikemap.ctu.cx"
-
-
-      # git server (gitolite+stagit)
-      ctucxConfig.websites."git.ctu.cx"
-
       # matrix-bridges
       ctucxConfig.services.matrixBridges.mautrix-signal
       ctucxConfig.services.matrixBridges.mautrix-whatsapp
diff --git a/nodes/rabbit/default.nix b/nodes/rabbit/default.nix
@@ -32,6 +32,10 @@
       ctucxConfig.websites."ctu.cx"
       ctucxConfig.websites."oeffi.katja.wtf"
       ctucxConfig.websites."things.katja.wtf"
+      ctucxConfig.websites."bikemap.katja.wtf" # depends on git
+
+      # git server (gitolite+stagit)
+      ctucxConfig.websites."git.katja.wtf"
 
       # cal-/card-dav server (radicale)
       ctucxConfig.websites."dav.katja.wtf"
diff --git a/secrets/hector/restic/gitolite.age b/secrets/hector/restic/gitolite.age
@@ -1,11 +0,0 @@
------BEGIN AGE ENCRYPTED FILE-----
-YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB5dmpqZnhiWFhicXNRdTFF
-d3JlT3ZURTg3Q2Z3emJKTUhrbDRQeFRyVDNZClZsYzlrbTMwSUx4RzlDd0NaTjd5
-ZGZNNGJNRWh0UnJ1SWVBcCtURkVtSVEKLT4gc3NoLWVkMjU1MTkgeWFMSFNRIHZw
-algvQWtFRUhIN3NVaWdnMk96WGFZRzh1ZkZWbnV0M1NFeEk0cm5hVlkKR1UvaHNM
-amR5d2NPUVhySGFnWnJhWGp0ZzlmRHZSM1pyQ09yeDkzY0pUTQotPiBoci1ncmVh
-c2UKanRYOTBwa2RyY05kZXZxT2JGUy9XSXRxUVk2VXdTYmhDK0Fnd0hPWWx2eVVL
-dGlTc0trWGNOMkVyTmoveEMxZQpVQQotLS0gOXVsTzFqalNRSWxKMG9saTQxWE4w
-OGNRWXpGQ1ZCcDlBSVpFMmZPcHQzawrpcIEVrVBJkbI84cTxYYEjmfY6AEqNLS+m
-nvCdD1UQ9q6EjsqeaqmxntD5IDHOxgHqDjQR5UeiEvHH
------END AGE ENCRYPTED FILE-----
diff --git a/secrets/rabbit/restic/gitolite.age b/secrets/rabbit/restic/gitolite.age
@@ -0,0 +1,12 @@
+-----BEGIN AGE ENCRYPTED FILE-----
+YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAvaGJjZ2RyS1pKbTBtdEZ6
+YW9qa3FUUjhYNDMxUTFIZlhhelRPMUw3SjIwCjBZNHZtUmdCODR5WHFJUk1sUm9x
+WVQ1bVJteHBFVmxVcjh5TVgwU1l5TjAKLT4gc3NoLWVkMjU1MTkgaGtMdUVnIHd4
+SlhRSkNEU01UbVdMYmtjNTZYNFU1RThPckpWVThFZU91SUZkKzRza2MKR1l2ckdm
+UUlCclk4Y1o1NW5hejdJcDBISGMzUng5cEhwTmNYNTFGZWJzawotPiBBVS55LWdy
+ZWFzZQpBSGFVanRhcDZDYVNZdUNKZjdOTkNRZnhkOUFrRGd0bXFkcTl4dXg0WUpp
+a0Vya0J2aXc1aFAzYnBpTGhBQjBTCm1Wbk9BVndKLzdYSFhrRDVHK1hGbm5PcmMz
+NAotLS0gMDdCbDdqNlFDMFJ6RDNFbEpsSUNYa04yblhnUjhiUVJEU3JyTVJEN3Zy
+SQqMlru+mVPww8OY9S55lFwX4I53YIqbV7onjNXxTAtfjZwWhkH3/GUSKXkUrUQY
+6zqUIJUcES+wAE7s
+-----END AGE ENCRYPTED FILE-----