ctucx.git: nixfiles

ctucx' nixfiles

commit 373ada0f2b0a78125c5f9c8f0579e59c15c7bcec
parent 81e9ea1ac6189d4b8a5cf990fcba4074fc23c54f
Author: Leah (ctucx) <git@ctu.cx>
Date: Sat, 13 May 2023 17:17:00 +0200

cleanup
10 files changed, 319 insertions(+), 1793 deletions(-)
M
machines/trabbi/configuration.nix
|
2
+-
A
machines/trabbi/git.nix
|
272
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
D
machines/trabbi/git/cgit-assets/cgit.css
|
1004
-------------------------------------------------------------------------------
D
machines/trabbi/git/cgit.nix
|
97
-------------------------------------------------------------------------------
D
machines/trabbi/git/default.nix
|
32
--------------------------------
D
machines/trabbi/git/stagit.nix
|
250
-------------------------------------------------------------------------------
D
machines/trabbi/maddy.nix
|
359
-------------------------------------------------------------------------------
M
machines/trabbi/mail.nix
|
6
+++---
M
machines/trabbi/matrix-synapse.nix
|
8
++++----
M
machines/trabbi/websites/bikemap.ctu.cx.nix
|
82
++++++++++++++++++++++++++++++++++++++-----------------------------------------
diff --git a/machines/trabbi/configuration.nix b/machines/trabbi/configuration.nix
@@ -9,7 +9,7 @@
     ../../configurations/linux/services/dns.nix
 
     # git server (gitolite+stagit)
-    ./git
+    ./git.nix
 
     # monitoring
     ../../configurations/linux/services/prometheus-exporters.nix
diff --git a/machines/trabbi/git.nix b/machines/trabbi/git.nix
@@ -0,0 +1,272 @@
+{ config, lib, pkgs, ... }:
+
+let
+  rebuildScript = pkgs.writeShellScript "init-stagit" ''
+    systemctl start init-stagit;
+    systemctl status init-stagit;
+  '';
+
+  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
+    }
+
+    make_stagit_index() {
+      printf "Generating stagit index... "
+
+      # set assets if not already there
+      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" "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
+
+      # make index
+      echo "$args" | xargs ${pkgs.stagit}/bin/stagit-index > /var/lib/stagit/index.html
+
+      # set correct permissions
+      chmod 755 /var/lib/stagit/index.html;
+      chown git:git /var/lib/stagit/index.html;
+
+      echo "done"
+    }
+  '';
+
+in {
+
+  dns.zones."ctu.cx".subdomains = {
+    cgit.CNAME = [ "${config.networking.fqdn}." ];
+    git.CNAME  = [ "${config.networking.fqdn}." ];
+  };
+
+  age.secrets.restic-gitolite.file = ./. + "/../../secrets/${config.networking.hostName}/restic/gitolite.age";
+
+  restic-backups.gitolite = {
+    user         = "git";
+    passwordFile = config.age.secrets.restic-gitolite.path;
+    paths        = [ "/var/lib/gitolite" ];
+  };
+
+  security.sudo.extraRules = [{
+    users    = [ "git" ];
+    commands = [
+      { command = "${rebuildScript}"; options = [ "SETENV" "NOPASSWD" ]; }
+    ];
+  }];
+
+  services = {
+  };
+
+  systemd.services.init-stagit = {
+    script = ''
+      ${stagitFunctions}
+
+      make_repo_web() {
+        reponame="$(basename "$1" ".git")"
+        printf "[%s] stagit HTML pages... " "$reponame"
+
+        mkdir -p "/var/lib/stagit/$reponame"
+        cd "/var/lib/stagit/$reponame" || return 1
+
+        # make pages
+        ${pkgs.stagit}/bin/stagit -c '.stagit-build-cache' -n 'ctucx.git' -h 'https://git.ctu.cx/' -s 'git@${config.networking.hostName}.ctu.cx:' "$1"
+
+        echo "done"
+      }
+
+      # clean webdir
+      rm -rf /var/lib/stagit/*
+
+      # make files per repo
+      for repo in "$HOME/repositories/"*.git/; do
+        repo="''${repo%/}"
+        is_public_and_listed "$repo" || continue
+
+        make_repo_web "$repo"
+      done
+
+      make_stagit_index
+    '';
+
+    serviceConfig = {
+      Type = "oneshot";
+
+      User  = "git";
+      Group = "git";
+
+      WorkingDirectory        = "~";
+      StateDirectory          = "stagit";
+      StateDirectoryMode      = "755";
+
+      NoNewPrivileges         = true;
+      PrivateTmp              = true;
+      PrivateDevices          = true;
+
+      RestrictAddressFamilies = "AF_INET AF_INET6";
+      RestrictNamespaces      = true;
+      RestrictRealtime        = true;
+
+      ProtectSystem           = "full";
+      ProtectControlGroups    = true;
+      ProtectKernelModules    = true;
+      ProtectKernelTunables   = true;
+
+      DevicePolicy            = "closed";
+      LockPersonality         = true;
+    };
+  };
+
+  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{UMASK} = 0027;
+        $RC{GIT_CONFIG_KEYS} = ".*";
+        push( @{$RC{ENABLE}}, 'cgit' );
+      '';
+      hooks.postReceive = ''
+        ${stagitFunctions}
+
+        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
+        }
+
+        make_repo_web() {
+          reponame="$(basename "$1" ".git")"
+          printf "[%s] stagit HTML pages... " "$reponame"
+
+          # if forced update, remove directory and cache file
+          is_forced_update && printf "forced update... " && rm -rf "/var/lib/stagit/$reponame"
+
+          mkdir -p "/var/lib/stagit/$reponame"
+          cd "/var/lib/stagit/$reponame" || return 1
+
+          # make pages
+          ${pkgs.stagit}/bin/stagit -c '.stagit-build-cache' -n 'ctucx.git' -h 'https://git.ctu.cx/' -s 'git@${config.networking.hostName}.ctu.cx:' "$1"
+
+          # set correct permissions
+          chmod 755 -R /var/lib/stagit/$reponame;
+          chown git:git -R /var/lib/stagit/$reponame;
+
+          echo "done"
+        }
+
+        update_stagit_repo() {
+          repo="$(pwd)"
+
+          cd "$repo" || return 1
+          is_public_and_listed "$repo" || return 0
+
+          make_repo_web "$repo"
+          make_stagit_index
+        }
+
+        update_stagit_repo "$1"
+
+        #rebuild stagit
+        [ "$GL_REPO" == "gitolite-admin" ] && sudo ${rebuildScript}
+      '';
+
+    };
+
+    fcgiwrap = {
+      enable = true;
+      user   = "git";
+      group  = "git";
+    };
+
+    nginx = {
+      enable = true;
+      virtualHosts = {
+        "cgit.ctu.cx" = {
+          enableACME = true;
+          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" = {
+          enableACME = true;
+          forceSSL   = true;
+          kTLS       = true;
+          root       = "/var/lib/stagit";
+          locations = {
+            "~ '^/[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.socketAddress};
+            '';
+          };
+        };
+
+      };
+    };
+
+  };
+
+}
diff --git a/machines/trabbi/git/cgit-assets/cgit.css b/machines/trabbi/git/cgit-assets/cgit.css
@@ -1,1004 +0,0 @@
-/*
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License v2 as published
- * by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * See <https://www.gnu.org/licenses/>.
- */
-
-div#cgit {
-	padding: 0em;
-	margin: 0em;
-	font-family: sans-serif;
-	font-size: 10pt;
-	color: #333;
-	background: white;
-	padding: 4px;
-}
-
-div#cgit a {
-	color: blue;
-	text-decoration: none;
-}
-
-div#cgit a:hover {
-	text-decoration: underline;
-}
-
-div#cgit table {
-	border-collapse: collapse;
-}
-
-div#cgit table#header {
-	width: 100%;
-	margin-bottom: 1em;
-}
-
-div#cgit table#header td.logo {
-	width: 96px;
-	vertical-align: top;
-}
-
-div#cgit table#header td.main {
-	font-size: 250%;
-	padding-left: 10px;
-	white-space: nowrap;
-}
-
-div#cgit table#header td.main a {
-	color: #000;
-}
-
-div#cgit table#header td.form {
-	text-align: right;
-	vertical-align: bottom;
-	padding-right: 1em;
-	padding-bottom: 2px;
-	white-space: nowrap;
-}
-
-div#cgit table#header td.form form,
-div#cgit table#header td.form input,
-div#cgit table#header td.form select {
-	font-size: 90%;
-}
-
-div#cgit table#header td.sub {
-	color: #777;
-	border-top: solid 1px #ccc;
-	padding-left: 10px;
-}
-
-div#cgit table.tabs {
-	border-bottom: solid 3px #ccc;
-	border-collapse: collapse;
-	margin-top: 2em;
-	margin-bottom: 0px;
-	width: 100%;
-}
-
-div#cgit table.tabs td {
-	padding: 0px 1em;
-	vertical-align: bottom;
-}
-
-div#cgit table.tabs td a {
-	padding: 2px 0.75em;
-	color: #777;
-	font-size: 110%;
-}
-
-div#cgit table.tabs td a.active {
-	color: #000;
-	background-color: #ccc;
-}
-
-div#cgit table.tabs a[href^="http://"]:after, div#cgit table.tabs a[href^="https://"]:after {
-	content: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAQAAAAnOwc2AAAAAmJLR0QA/4ePzL8AAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfgAhcJDQY+gm2TAAAAHWlUWHRDb21tZW50AAAAAABDcmVhdGVkIHdpdGggR0lNUGQuZQcAAABbSURBVAhbY2BABs4MU4CwhYHBh2Erww4wrGFQZHjI8B8IgUIscJWyDHcggltQhI4zGDCcRwhChPggHIggP1QoAVmQkSETrGoHsiAEsACtBYN0oDAMbgU6EBcAAL2eHUt4XUU4AAAAAElFTkSuQmCC);
-	opacity: 0.5;
-	margin: 0 0 0 5px;
-}
-
-div#cgit table.tabs td.form {
-	text-align: right;
-}
-
-div#cgit table.tabs td.form form {
-	padding-bottom: 2px;
-	font-size: 90%;
-	white-space: nowrap;
-}
-
-div#cgit table.tabs td.form input,
-div#cgit table.tabs td.form select {
-	font-size: 90%;
-}
-
-div#cgit div.path {
-	margin: 0px;
-	padding: 5px 2em 2px 2em;
-	color: #000;
-	background-color: #eee;
-}
-
-div#cgit div.content {
-	margin: 0px;
-	padding: 2em;
-	border-bottom: solid 3px #ccc;
-}
-
-
-div#cgit table.list {
-	width: 100%;
-	border: none;
-	border-collapse: collapse;
-}
-
-div#cgit table.list tr {
-	background: white;
-}
-
-div#cgit table.list tr.logheader {
-	background: #eee;
-}
-
-div#cgit table.list tr:nth-child(even) {
-	background: #f7f7f7;
-}
-
-div#cgit table.list tr:nth-child(odd) {
-	background: white;
-}
-
-div#cgit table.list tr:hover {
-	background: #eee;
-}
-
-div#cgit table.list tr.nohover {
-	background: white;
-}
-
-div#cgit table.list tr.nohover:hover {
-	background: white;
-}
-
-div#cgit table.list tr.nohover-highlight:hover:nth-child(even) {
-	background: #f7f7f7;
-}
-
-div#cgit table.list tr.nohover-highlight:hover:nth-child(odd) {
-	background: white;
-}
-
-div#cgit table.list th {
-	font-weight: bold;
-	/* color: #888;
-	border-top: dashed 1px #888;
-	border-bottom: dashed 1px #888;
-	*/
-	padding: 0.1em 0.5em 0.05em 0.5em;
-	vertical-align: baseline;
-}
-
-div#cgit table.list td {
-	border: none;
-	padding: 0.1em 0.5em 0.1em 0.5em;
-}
-
-div#cgit table.list td.commitgraph {
-	font-family: monospace;
-	white-space: pre;
-}
-
-div#cgit table.list td.commitgraph .column1 {
-	color: #a00;
-}
-
-div#cgit table.list td.commitgraph .column2 {
-	color: #0a0;
-}
-
-div#cgit table.list td.commitgraph .column3 {
-	color: #aa0;
-}
-
-div#cgit table.list td.commitgraph .column4 {
-	color: #00a;
-}
-
-div#cgit table.list td.commitgraph .column5 {
-	color: #a0a;
-}
-
-div#cgit table.list td.commitgraph .column6 {
-	color: #0aa;
-}
-
-div#cgit table.list td.logsubject {
-	font-family: monospace;
-	font-weight: bold;
-}
-
-div#cgit table.list td.logmsg {
-	font-family: monospace;
-	white-space: pre;
-	padding: 0 0.5em;
-}
-
-div#cgit table.list td a {
-	color: black;
-}
-
-div#cgit table.list td a.ls-dir {
-	font-weight: bold;
-	color: #00f;
-}
-
-div#cgit table.list td a:hover {
-	color: #00f;
-}
-
-div#cgit img {
-	border: none;
-}
-
-div#cgit input#switch-btn {
-	margin: 2px 0px 0px 0px;
-}
-
-div#cgit td#sidebar input.txt {
-	width: 100%;
-	margin: 2px 0px 0px 0px;
-}
-
-div#cgit table#grid {
-	margin: 0px;
-}
-
-div#cgit td#content {
-	vertical-align: top;
-	padding: 1em 2em 1em 1em;
-	border: none;
-}
-
-div#cgit div#summary {
-	vertical-align: top;
-	margin-bottom: 1em;
-        max-width: 70ch;
-}
-
-div#cgit table#downloads {
-	float: right;
-	border-collapse: collapse;
-	border: solid 1px #777;
-	margin-left: 0.5em;
-	margin-bottom: 0.5em;
-}
-
-div#cgit table#downloads th {
-	background-color: #ccc;
-}
-
-div#cgit div#blob {
-	border: solid 1px black;
-}
-
-div#cgit div.error {
-	color: red;
-	font-weight: bold;
-	margin: 1em 2em;
-}
-
-div#cgit a.ls-blob, div#cgit a.ls-dir, div#cgit .ls-mod {
-	font-family: monospace;
-}
-
-div#cgit td.ls-size {
-	text-align: right;
-	font-family: monospace;
-	width: 10em;
-}
-
-div#cgit td.ls-mode {
-	font-family: monospace;
-	width: 10em;
-}
-
-div#cgit table.blob {
-	margin-top: 0.5em;
-	border-top: solid 1px black;
-}
-
-div#cgit table.blob td.hashes,
-div#cgit table.blob td.lines {
-	margin: 0; padding: 0 0 0 0.5em;
-	vertical-align: top;
-	color: black;
-}
-
-div#cgit table.blob td.linenumbers {
-	margin: 0; padding: 0 0.5em 0 0.5em;
-	vertical-align: top;
-	text-align: right;
-	border-right: 1px solid gray;
-}
-
-div#cgit table.blob pre {
-	padding: 0; margin: 0;
-}
-
-div#cgit table.blob td.linenumbers a,
-div#cgit table.ssdiff td.lineno a {
-	color: gray;
-	text-align: right;
-	text-decoration: none;
-}
-
-div#cgit table.blob td.linenumbers a:hover,
-div#cgit table.ssdiff td.lineno a:hover {
-	color: black;
-}
-
-div#cgit table.blame td.hashes,
-div#cgit table.blame td.lines,
-div#cgit table.blame td.linenumbers {
-	padding: 0;
-}
-
-div#cgit table.blame td.hashes div.alt,
-div#cgit table.blame td.lines div.alt {
-	padding: 0 0.5em 0 0.5em;
-}
-
-div#cgit table.blame td.linenumbers div.alt {
-	padding: 0 0.5em 0 0;
-}
-
-div#cgit table.blame div.alt:nth-child(even) {
-	background: #eee;
-}
-
-div#cgit table.blame div.alt:nth-child(odd) {
-	background: white;
-}
-
-div#cgit table.blame td.lines > div {
-	position: relative;
-}
-
-div#cgit table.blame td.lines > div > pre {
-	padding: 0 0 0 0.5em;
-	position: absolute;
-	top: 0;
-}
-
-div#cgit table.bin-blob {
-	margin-top: 0.5em;
-	border: solid 1px black;
-}
-
-div#cgit table.bin-blob th {
-	font-family: monospace;
-	white-space: pre;
-	border: solid 1px #777;
-	padding: 0.5em 1em;
-}
-
-div#cgit table.bin-blob td {
-	font-family: monospace;
-	white-space: pre;
-	border-left: solid 1px #777;
-	padding: 0em 1em;
-}
-
-div#cgit table.nowrap td {
-	white-space: nowrap;
-}
-
-div#cgit table.commit-info {
-	border-collapse: collapse;
-	margin-top: 1.5em;
-}
-
-div#cgit div.cgit-panel {
-	float: right;
-	margin-top: 1.5em;
-}
-
-div#cgit div.cgit-panel table {
-	border-collapse: collapse;
-	border: solid 1px #aaa;
-	background-color: #eee;
-}
-
-div#cgit div.cgit-panel th {
-	text-align: center;
-}
-
-div#cgit div.cgit-panel td {
-	padding: 0.25em 0.5em;
-}
-
-div#cgit div.cgit-panel td.label {
-	padding-right: 0.5em;
-}
-
-div#cgit div.cgit-panel td.ctrl {
-	padding-left: 0.5em;
-}
-
-div#cgit table.commit-info th {
-	text-align: left;
-	font-weight: normal;
-	padding: 0.1em 1em 0.1em 0.1em;
-	vertical-align: top;
-}
-
-div#cgit table.commit-info td {
-	font-weight: normal;
-	padding: 0.1em 1em 0.1em 0.1em;
-}
-
-div#cgit div.commit-subject {
-	font-weight: bold;
-	font-size: 125%;
-	margin: 1.5em 0em 0.5em 0em;
-	padding: 0em;
-}
-
-div#cgit div.commit-msg {
-	white-space: pre;
-	font-family: monospace;
-}
-
-div#cgit div.notes-header {
-	font-weight: bold;
-	padding-top: 1.5em;
-}
-
-div#cgit div.notes {
-	white-space: pre;
-	font-family: monospace;
-	border: solid 1px #ee9;
-	background-color: #ffd;
-	padding: 0.3em 2em 0.3em 1em;
-	float: left;
-}
-
-div#cgit div.notes-footer {
-	clear: left;
-}
-
-div#cgit div.diffstat-header {
-	font-weight: bold;
-	padding-top: 1.5em;
-}
-
-div#cgit table.diffstat {
-	border-collapse: collapse;
-	border: solid 1px #aaa;
-	background-color: #eee;
-}
-
-div#cgit table.diffstat th {
-	font-weight: normal;
-	text-align: left;
-	text-decoration: underline;
-	padding: 0.1em 1em 0.1em 0.1em;
-	font-size: 100%;
-}
-
-div#cgit table.diffstat td {
-	padding: 0.2em 0.2em 0.1em 0.1em;
-	font-size: 100%;
-	border: none;
-}
-
-div#cgit table.diffstat td.mode {
-	white-space: nowrap;
-}
-
-div#cgit table.diffstat td span.modechange {
-	padding-left: 1em;
-	color: red;
-}
-
-div#cgit table.diffstat td.add a {
-	color: green;
-}
-
-div#cgit table.diffstat td.del a {
-	color: red;
-}
-
-div#cgit table.diffstat td.upd a {
-	color: blue;
-}
-
-div#cgit table.diffstat td.graph {
-	width: 500px;
-	vertical-align: middle;
-}
-
-div#cgit table.diffstat td.graph table {
-	border: none;
-}
-
-div#cgit table.diffstat td.graph td {
-	padding: 0px;
-	border: 0px;
-	height: 7pt;
-}
-
-div#cgit table.diffstat td.graph td.add {
-	background-color: #5c5;
-}
-
-div#cgit table.diffstat td.graph td.rem {
-	background-color: #c55;
-}
-
-div#cgit div.diffstat-summary {
-	color: #888;
-	padding-top: 0.5em;
-}
-
-div#cgit table.diff {
-	width: 100%;
-}
-
-div#cgit table.diff td {
-	font-family: monospace;
-	white-space: pre;
-}
-
-div#cgit table.diff td div.head {
-	font-weight: bold;
-	margin-top: 1em;
-	color: black;
-}
-
-div#cgit table.diff td div.hunk {
-	color: #009;
-}
-
-div#cgit table.diff td div.add {
-	color: green;
-}
-
-div#cgit table.diff td div.del {
-	color: red;
-}
-
-div#cgit .sha1 {
-	font-family: monospace;
-	font-size: 90%;
-}
-
-div#cgit .left {
-	text-align: left;
-}
-
-div#cgit .right {
-	text-align: right;
-}
-
-div#cgit table.list td.reposection {
-	font-style: italic;
-	color: #888;
-}
-
-div#cgit a.button {
-	font-size: 80%;
-	padding: 0em 0.5em;
-}
-
-div#cgit a.primary {
-	font-size: 100%;
-}
-
-div#cgit a.secondary {
-	font-size: 90%;
-}
-
-div#cgit td.toplevel-repo {
-
-}
-
-div#cgit table.list td.sublevel-repo {
-	padding-left: 1.5em;
-}
-
-div#cgit ul.pager {
-	list-style-type: none;
-	text-align: center;
-	margin: 1em 0em 0em 0em;
-	padding: 0;
-}
-
-div#cgit ul.pager li {
-	display: inline-block;
-	margin: 0.25em 0.5em;
-}
-
-div#cgit ul.pager a {
-	color: #777;
-}
-
-div#cgit ul.pager .current {
-	font-weight: bold;
-}
-
-div#cgit span.age-mins {
-	font-weight: bold;
-	color: #080;
-}
-
-div#cgit span.age-hours {
-	color: #080;
-}
-
-div#cgit span.age-days {
-	color: #040;
-}
-
-div#cgit span.age-weeks {
-	color: #444;
-}
-
-div#cgit span.age-months {
-	color: #888;
-}
-
-div#cgit span.age-years {
-	color: #bbb;
-}
-
-div#cgit span.insertions {
-	color: #080;
-}
-
-div#cgit span.deletions {
-	color: #800;
-}
-
-div#cgit div.footer {
-	margin-top: 0.5em;
-	text-align: center;
-	font-size: 80%;
-	color: #ccc;
-}
-
-div#cgit div.footer a {
-	color: #ccc;
-	text-decoration: none;
-}
-
-div#cgit div.footer a:hover {
-	text-decoration: underline;
-}
-
-div#cgit a.branch-deco {
-	color: #000;
-	margin: 0px 0.5em;
-	padding: 0px 0.25em;
-	background-color: #88ff88;
-	border: solid 1px #007700;
-}
-
-div#cgit a.tag-deco {
-	color: #000;
-	margin: 0px 0.5em;
-	padding: 0px 0.25em;
-	background-color: #ffff88;
-	border: solid 1px #777700;
-}
-
-div#cgit a.tag-annotated-deco {
-	color: #000;
-	margin: 0px 0.5em;
-	padding: 0px 0.25em;
-	background-color: #ffcc88;
-	border: solid 1px #777700;
-}
-
-div#cgit a.remote-deco {
-	color: #000;
-	margin: 0px 0.5em;
-	padding: 0px 0.25em;
-	background-color: #ccccff;
-	border: solid 1px #000077;
-}
-
-div#cgit a.deco {
-	color: #000;
-	margin: 0px 0.5em;
-	padding: 0px 0.25em;
-	background-color: #ff8888;
-	border: solid 1px #770000;
-}
-
-div#cgit div.commit-subject a.branch-deco,
-div#cgit div.commit-subject a.tag-deco,
-div#cgit div.commit-subject a.tag-annotated-deco,
-div#cgit div.commit-subject a.remote-deco,
-div#cgit div.commit-subject a.deco {
-	margin-left: 1em;
-	font-size: 75%;
-}
-
-div#cgit table.stats {
-	border: solid 1px black;
-	border-collapse: collapse;
-}
-
-div#cgit table.stats th {
-	text-align: left;
-	padding: 1px 0.5em;
-	background-color: #eee;
-	border: solid 1px black;
-}
-
-div#cgit table.stats td {
-	text-align: right;
-	padding: 1px 0.5em;
-	border: solid 1px black;
-}
-
-div#cgit table.stats td.total {
-	font-weight: bold;
-	text-align: left;
-}
-
-div#cgit table.stats td.sum {
-	color: #c00;
-	font-weight: bold;
-/*	background-color: #eee; */
-}
-
-div#cgit table.stats td.left {
-	text-align: left;
-}
-
-div#cgit table.vgraph {
-	border-collapse: separate;
-	border: solid 1px black;
-	height: 200px;
-}
-
-div#cgit table.vgraph th {
-	background-color: #eee;
-	font-weight: bold;
-	border: solid 1px white;
-	padding: 1px 0.5em;
-}
-
-div#cgit table.vgraph td {
-	vertical-align: bottom;
-	padding: 0px 10px;
-}
-
-div#cgit table.vgraph div.bar {
-	background-color: #eee;
-}
-
-div#cgit table.hgraph {
-	border: solid 1px black;
-	width: 800px;
-}
-
-div#cgit table.hgraph th {
-	background-color: #eee;
-	font-weight: bold;
-	border: solid 1px black;
-	padding: 1px 0.5em;
-}
-
-div#cgit table.hgraph td {
-	vertical-align: middle;
-	padding: 2px 2px;
-}
-
-div#cgit table.hgraph div.bar {
-	background-color: #eee;
-	height: 1em;
-}
-
-div#cgit table.ssdiff {
-	width: 100%;
-}
-
-div#cgit table.ssdiff td {
-	font-size: 75%;
-	font-family: monospace;
-	white-space: pre;
-	padding: 1px 4px 1px 4px;
-	border-left: solid 1px #aaa;
-	border-right: solid 1px #aaa;
-}
-
-div#cgit table.ssdiff td.add {
-	color: black;
-	background: #cfc;
-	min-width: 50%;
-}
-
-div#cgit table.ssdiff td.add_dark {
-	color: black;
-	background: #aca;
-	min-width: 50%;
-}
-
-div#cgit table.ssdiff span.add {
-	background: #cfc;
-	font-weight: bold;
-}
-
-div#cgit table.ssdiff td.del {
-	color: black;
-	background: #fcc;
-	min-width: 50%;
-}
-
-div#cgit table.ssdiff td.del_dark {
-	color: black;
-	background: #caa;
-	min-width: 50%;
-}
-
-div#cgit table.ssdiff span.del {
-	background: #fcc;
-	font-weight: bold;
-}
-
-div#cgit table.ssdiff td.changed {
-	color: black;
-	background: #ffc;
-	min-width: 50%;
-}
-
-div#cgit table.ssdiff td.changed_dark {
-	color: black;
-	background: #cca;
-	min-width: 50%;
-}
-
-div#cgit table.ssdiff td.lineno {
-	color: black;
-	background: #eee;
-	text-align: right;
-	width: 3em;
-	min-width: 3em;
-}
-
-div#cgit table.ssdiff td.hunk {
-	color: black;
-	background: #ccf;
-	border-top: solid 1px #aaa;
-	border-bottom: solid 1px #aaa;
-}
-
-div#cgit table.ssdiff td.head {
-	border-top: solid 1px #aaa;
-	border-bottom: solid 1px #aaa;
-}
-
-div#cgit table.ssdiff td.head div.head {
-	font-weight: bold;
-	color: black;
-}
-
-div#cgit table.ssdiff td.foot {
-	border-top: solid 1px #aaa;
-	border-left: none;
-	border-right: none;
-	border-bottom: none;
-}
-
-div#cgit table.ssdiff td.space {
-	border: none;
-}
-
-div#cgit table.ssdiff td.space div {
-	min-height: 3em;
-}
-* { line-height: 1.25em; }
-
-article {
-  font-family: sans-serif;
-  max-width: 70ch;
-  margin-left: auto;
-  margin-right: auto;
-}
-
-div#cgit {
-  margin: auto;
-  font-family: monospace;
-  -moz-tab-size: 4;
-  tab-size: 4;
-  display: table;
-}
-
-div#cgit table#header {
-  margin-left: auto;
-  margin-right: auto;
-}
-div#cgit table#header td.logo {
-  display: none;
-}
-div#cgit table#header td.main {
-  font-size: 2em;
-  font-weight: bold;
-}
-div#cgit table#header td.sub {
-  border-top: none;
-}
-div#cgit table.tabs {
-  margin-left: auto;
-  margin-right: auto;
-  border-bottom: none;
-}
-div#cgit div.content {
-  border-bottom: none;
-  min-width: 108ch;
-}
-div#cgit div.content div#summary {
-  display: table;
-  margin-left: auto;
-  margin-right: auto;
-}
-div#cgit table.list {
-  margin-left: auto;
-  margin-right: auto;
-}
-div#cgit table.list th a {
-  color: inherit;
-}
-div#cgit table.list tr:nth-child(even) {
-  background: inherit;
-}
-div#cgit table.list tr:hover {
-  background: inherit;
-}
-div#cgit table.list tr.nohover-highlight:hover:nth-child(even) {
-  background: inherit;
-}
-div#cgit table.list td:last-child {
-  width: 0;
-}
-div#cgit div.footer {
-  font-size: 1em;
-  margin-top: 0;
-}
-
-div#cgit table.blob td.linenumbers:nth-last-child(3) {
-  display: none;
-}
-
-div#cgit table.blob td.linenumbers a:target {
-  color: goldenrod;
-  text-decoration: underline;
-  outline: none;
-}
-
-body.hl { background-color:#e0eaee; }
-pre.hl  { color:#000000; background-color:#e0eaee; font-size:10pt; font-family:'Courier New',monospace;}
-.hl.num { color:#b07e00; }
-.hl.esc { color:#ff00ff; }
-.hl.str { color:#bf0303; }
-.hl.pps { color:#818100; }
-.hl.slc { color:#838183; font-style:italic; }
-.hl.com { color:#838183; font-style:italic; }
-.hl.ppc { color:#008200; }
-.hl.opt { color:#000000; }
-.hl.ipl { color:#0057ae; }
-.hl.lin { color:#555555; }
-.hl.kwa { color:#000000; font-weight:bold; }
-.hl.kwb { color:#0057ae; }
-.hl.kwc { color:#000000; font-weight:bold; }
-.hl.kwd { color:#010181; }
diff --git a/machines/trabbi/git/cgit.nix b/machines/trabbi/git/cgit.nix
@@ -1,97 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-let
-  cgitAssets = ./cgit-assets;
-  cgitConfig = pkgs.writeText "cgitrc" (lib.generators.toKeyValue { } {
-    css           = "/assets/cgit.css";
-    logo          = "/cgit.png";
-
-    virtual-root  = "/";
-
-    root-title    = "ctucx.cgit";
-    root-desc     = "my personal git repos";
-
-    local-time    = 1;
-    cache-size    = 30;
-
-    about-filter  = "${pkgs.cgit}/lib/cgit/filters/about-formatting.sh";
-    source-filter = "${pkgs.cgit}/lib/cgit/filters/syntax-highlighting.py";
-
-    clone-url = (lib.concatStringsSep " " [
-      "https://cgit.ctu.cx/$CGIT_REPO_URL"
-      "ssh://git@${config.networking.hostName}.${config.networking.domain}:$CGIT_REPO_URL"
-    ]);
-
-    snapshots = (lib.concatStringsSep " " [
-      "tar.gz"
-      "tar.bz2"
-      "zip"
-    ]);
-
-    max-stats-quarter    = "quarter";
-
-    enable-commit-graph  = 1;
-
-    enable-index-links   = 1;
-    enable-index-owner   = 0;
-
-    enable-log-filecount = 1;
-    enable-log-linecount = 1;
-
-    enable-blame         = 1;
-
-    enable-git-config    = 1;
-
-    remove-suffix        = 1;
-    project-list         = "/var/lib/gitolite/projects.list";
-    scan-path            = "/var/lib/gitolite/repositories";
-  });
-
-in {
-
-  dns.zones."ctu.cx".subdomains.cgit.CNAME = [ "${config.networking.fqdn}." ];
-
-  services = {
-
-    fcgiwrap = {
-      enable = true;
-      user   = "git";
-      group  = "git";
-    };
-
-    nginx = {
-      enable = true;
-      virtualHosts."cgit.ctu.cx" = {
-        enableACME = true;
-        forceSSL   = true;
-        kTLS       = true;
-        locations = {
-          "/".tryFiles     = "$uri @cgit";
-          "/assets/".alias = "${cgitAssets}/";
-          "~ '^/[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.socketAddress};
-          '';
-          "@cgit".extraConfig = ''
-            include "${pkgs.nginx}/conf/fastcgi_params";
-            fastcgi_param CGIT_CONFIG     "${cgitConfig}";
-            fastcgi_param SCRIPT_FILENAME "${pkgs.cgit}/cgit/cgit.cgi";
-            fastcgi_param PATH_INFO       $uri;
-            fastcgi_param QUERY_STRING    $args;
-            fastcgi_param HTTP_HOST       $server_name;
-            fastcgi_pass  unix:${config.services.fcgiwrap.socketAddress};
-          '';
-        };
-      };
-    };
-
-  };
-
-}
diff --git a/machines/trabbi/git/default.nix b/machines/trabbi/git/default.nix
@@ -1,32 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-{
-
-  imports = [
-#    ./cgit.nix
-    ./stagit.nix
-  ];
-
-  age.secrets.restic-gitolite.file = ../../../secrets/trabbi/restic/gitolite.age;
-
-  restic-backups.gitolite = {
-    user         = "git";
-    passwordFile = config.age.secrets.restic-gitolite.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{UMASK} = 0027;
-        $RC{GIT_CONFIG_KEYS} = ".*";
-        push( @{$RC{ENABLE}}, 'cgit' );
-      '';
-    };
-  };
-
-}
diff --git a/machines/trabbi/git/stagit.nix b/machines/trabbi/git/stagit.nix
@@ -1,250 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-let
-  rebuildScript = pkgs.writeShellScript "init-stagit" ''
-    systemctl start init-stagit;
-    systemctl status init-stagit;
-  '';
-
-  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
-    }
-
-    make_stagit_index() {
-      printf "Generating stagit index... "
-
-      # set assets if not already there
-      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" "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
-
-      # make index
-      echo "$args" | xargs ${pkgs.stagit}/bin/stagit-index > /var/lib/stagit/index.html
-
-      # set correct permissions
-      chmod 755 /var/lib/stagit/index.html;
-      chown git:git /var/lib/stagit/index.html;
-
-      echo "done"
-    }
-  '';
-
-in {
-
-  dns.zones."ctu.cx".subdomains = {
-    cgit.CNAME = [ "${config.networking.fqdn}." ];
-    git.CNAME  = [ "${config.networking.fqdn}." ];
-  };
-
-  security.sudo.extraRules = [{
-    users    = [ "git" ];
-    commands = [
-      { command = "${rebuildScript}"; options = [ "SETENV" "NOPASSWD" ]; }
-    ];
-  }];
-
-  systemd = {
-    services.init-stagit = {
-      script = ''
-        ${stagitFunctions}
-
-        make_repo_web() {
-          reponame="$(basename "$1" ".git")"
-          printf "[%s] stagit HTML pages... " "$reponame"
-
-          mkdir -p "/var/lib/stagit/$reponame"
-          cd "/var/lib/stagit/$reponame" || return 1
-
-          # make pages
-          ${pkgs.stagit}/bin/stagit -c '.stagit-build-cache' -n 'ctucx.git' -h 'https://git.ctu.cx/' -s 'git@${config.networking.hostName}.ctu.cx:' "$1"
-
-          echo "done"
-        }
-
-        # clean webdir
-        rm -rf /var/lib/stagit/*
-
-        # make files per repo
-        for repo in "$HOME/repositories/"*.git/; do
-          repo="''${repo%/}"
-          is_public_and_listed "$repo" || continue
-
-          make_repo_web "$repo"
-        done
-
-        make_stagit_index
-      '';
-
-      serviceConfig = {
-        Type = "oneshot";
-
-        User  = "git";
-        Group = "git";
-
-        WorkingDirectory        = "~";
-        StateDirectory          = "stagit";
-        StateDirectoryMode      = "755";
-
-        NoNewPrivileges         = true;
-        PrivateTmp              = true;
-        PrivateDevices          = true;
-
-        RestrictAddressFamilies = "AF_INET AF_INET6";
-        RestrictNamespaces      = true;
-        RestrictRealtime        = true;
-
-        ProtectSystem           = "full";
-        ProtectControlGroups    = true;
-        ProtectKernelModules    = true;
-        ProtectKernelTunables   = true;
-
-        DevicePolicy            = "closed";
-        LockPersonality         = true;
-      };
-    };
-  };
-
-  services = {
-    gitolite.hooks.postReceive = ''
-      ${stagitFunctions}
-
-      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
-      }
-
-      make_repo_web() {
-        reponame="$(basename "$1" ".git")"
-        printf "[%s] stagit HTML pages... " "$reponame"
-
-        # if forced update, remove directory and cache file
-        is_forced_update && printf "forced update... " && rm -rf "/var/lib/stagit/$reponame"
-
-        mkdir -p "/var/lib/stagit/$reponame"
-        cd "/var/lib/stagit/$reponame" || return 1
-
-        # make pages
-        ${pkgs.stagit}/bin/stagit -c '.stagit-build-cache' -n 'ctucx.git' -h 'https://git.ctu.cx/' -s 'git@${config.networking.hostName}.ctu.cx:' "$1"
-
-        # set correct permissions
-        chmod 755 -R /var/lib/stagit/$reponame;
-        chown git:git -R /var/lib/stagit/$reponame;
-
-        echo "done"
-      }
-
-      update_stagit_repo() {
-        repo="$(pwd)"
-
-        cd "$repo" || return 1
-        is_public_and_listed "$repo" || return 0
-
-        make_repo_web "$repo"
-        make_stagit_index
-      }
-
-      update_stagit_repo "$1"
-
-      #rebuild stagit
-      [ "$GL_REPO" == "gitolite-admin" ] && sudo ${rebuildScript}
-    '';
-
-    fcgiwrap = {
-      enable = true;
-      user   = "git";
-      group  = "git";
-    };
-
-    nginx = {
-      enable = true;
-      virtualHosts = {
-        "cgit.ctu.cx" = {
-          enableACME = true;
-          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" = {
-          enableACME = true;
-          forceSSL   = true;
-          kTLS       = true;
-          root       = "/var/lib/stagit";
-          locations = {
-            "~ '^/[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.socketAddress};
-            '';
-          };
-        };
-      };
-    };
-
-  };
-
-}
diff --git a/machines/trabbi/maddy.nix b/machines/trabbi/maddy.nix
@@ -1,359 +0,0 @@
-{ inputs, config, lib, pkgs, ... }:
-
-let
-  mailboxFilterScript = pkgs.writePythonScriptBin "mailbox-filter.py" (ps: [ ps.toml ps.mail-parser ]) ''
-    from email.header import Header, decode_header, make_header
-    import sys, re
-    import toml, mailparser
-
-    def filter_mail(config, sender, recipient, subject):
-      for type in [ 'recipient', 'subject', 'sender' ]:
-        if type not in config:
-          continue
-
-        for key, value in config[type].items():
-          if(re.search("^" + key + "$", str(eval(type)))):
-            print(value.replace(",", "\n"))
-            sys.exit(0)
-
-    try:
-      account_name = sys.argv[1]
-      config       = toml.load('/etc/maddy/filters/mailbox/' + account_name + '.toml')
-
-      if len(sys.argv) > 2:
-        filter_mail(config, sys.argv[2], sys.argv[3], make_header(decode_header(sys.argv[4])))
-      else:
-        sender = subject = ""
-        message          = mailparser.parse_from_string(sys.stdin.read())
-
-        if len(message.from_) > 0:
-          if len(message.from_[0]) == 2:
-            sender = message.from_[0][1]
-
-        if message.subject is not None:
-          subject = message.subject
-
-        for recipient in message.to:
-          filter_mail(config, sender, recipient[1], subject)
-
-    except:
-      pass
-
-    sys.exit(0)
-  '';
-
-  receiveFilterScript = pkgs.writePythonScriptBin "receive-filter.py" (ps: [ ps.toml ]) ''
-    import sys, toml
-
-    try:
-      sender    = sys.argv[1]
-      recipient = sys.argv[2]
-      config    = toml.load('/etc/maddy/filters/receive.toml')
-
-      for type in [ 'recipient', 'sender' ]:
-        if type not in config:
-          continue
-
-        if 'reject' in config[type]:
-          if eval(type) in config[type]['reject']:
-            sys.exit(10)
-
-        if 'quarantine' in config[type]:
-          if eval(type) in config[type]['quarantine']:
-            sys.exit(20)
-
-    except SystemExit as e:
-      sys.exit(e)
-
-    except:
-      pass
-
-    sys.exit(0)
-  '';
-
-in {
-
-  environment.etc."maddy/filters/mailbox/leah@ctu.cx.toml".text = "${inputs.nix-std.lib.serde.toTOML inputs.local-secrets.maddy.mailboxFilter}";
-  environment.etc."maddy/filters/receive.toml".text             = "${inputs.nix-std.lib.serde.toTOML inputs.local-secrets.maddy.receiveFilter}";
-
-  security.acme.certs."${config.networking.fqdn}".reloadServices           = [ "maddy.service" ];
-
-  systemd.services.maddy.serviceConfig.ReadOnlyPaths            = [ "/etc/maddy/filters" ];
-  systemd.services.maddy-restarter = {
-  	script  = "${pkgs.systemd}/bin/systemctl restart maddy.service";
-  	startAt = "0/12:00:00";
-  };
-
-  age.secrets.restic-maddy.file                                 = ../../secrets/trabbi/restic/maddy.age;
-
-  networking.firewall.allowedTCPPorts                           = [ 25 143 465 587 993 ];
-
-  dns.zones = with pkgs.dns.lib.combinators; let
-    TXT   = [ "v=spf1 a mx ip4:185.232.70.80 +ip6:2a03:4000:4e:af1::1 ~all" ];
-    DMARC = "v=DMARC1; p=none";
-    MX    = with mx; [ (mx 10 "${config.networking.fqdn}.") ];
-   in {
-    "ctu.cx" = {
-      inherit MX TXT;
-
-      SRV = [
-        { proto = "tcp"; service = "imaps"; priority = 0; weight = 1; port = 993; target = "${config.networking.fqdn}."; }
-        { proto = "tcp"; service = "imap"; priority = 0; weight = 1; port = 143; target = "${config.networking.fqdn}."; }
-        { proto = "tcp"; service = "submission"; priority = 0; weight = 1; port = 587; target = "${config.networking.fqdn}."; }
-      ];
-
-      subdomains = {
-        _dmarc.TXT               = [ DMARC ];
-        "default._domainkey".TXT = [ "v=DKIM1; k=ed25519; p=nWRKCHE19fL1RHJ2cVkC8Xvfzm9OtgeF5VC2lD+EaEo=" ];
-      };
-    };
-
-    "ctucx.de" = {
-      inherit MX TXT;
-
-      subdomains = {
-        _dmarc.TXT               = [ DMARC ];
-        "default._domainkey".TXT = [ "v=DKIM1; k=ed25519; p=U9JMZlv7BpLXGIpO7WdJ/7ephxwJtJ02jaVUUadyP9s" ];
-      };
-    };
-
-    "thein.ovh" = {
-      inherit MX TXT;
-
-      subdomains = {
-        _dmarc.TXT               = [ DMARC ];
-        "default._domainkey".TXT = [ "v=DKIM1; k=ed25519; p=KYkebiXYSc/+7Rtdz/ZZFRAXAsQnyLPYA6r2uboh5oc=" ];
-      };
-    };
-
-    "zug.network" = {
-      inherit MX TXT;
-
-      subdomains = {
-        _dmarc.TXT               = [ DMARC ];
-        "default._domainkey".TXT = [ "v=DKIM1; k=ed25519; p=dH1h2nvPlmT0lIrGddpYRZFTm0AD6D+fsU36McEso2g=" ];
-      };
-    };
-  };
-
-  users.groups.maddy = {};
-  users.users.maddy = {
-    isSystemUser = true;
-    home         = "/var/lib/maddy";
-    group        = "maddy";
-    extraGroups  = [ "nginx" ];
-  };
-
-  restic-backups.maddy = {
-    user         = "maddy";
-    passwordFile = config.age.secrets.restic-maddy.path;
-    paths        = [ "/var/lib/maddy" ];
-  };
-
-  services.maddy = {
-    enable        = true;
-    user          = "maddy";
-    group         = "maddy";
-    hostname      = config.networking.fqdn;
-    primaryDomain = "ctu.cx";
-    localDomains  = [
-      "$(hostname)"
-      "$(primary_domain)"
-      "thein.ovh"
-      "ctucx.de"
-      "trans-agenda.de"
-      "zug.network"
-    ];
-
-    config = ''
-      #debug on
-      log syslog
-      
-      tls file /var/lib/acme/${config.networking.fqdn}/fullchain.pem /var/lib/acme/${config.networking.fqdn}/key.pem
-      
-      # ----------------------------------------------------------------------------
-      # Local storage & authentication
-      
-      # pass_table provides local hashed passwords storage for authentication of
-      # users. It can be configured to use any "table" module, in default
-      # configuration a table in SQLite DB is used.
-      # Table can be replaced to use e.g. a file for passwords. Or pass_table module
-      # can be replaced altogether to use some external source of credentials (e.g.
-      # PAM, /etc/shadow file).
-      #
-      # If table module supports it (sql_table does) - credentials can be managed
-      # using 'maddyctl creds' command.
-      
-      auth.pass_table local_authdb {
-          table sql_table {
-              driver sqlite3
-              dsn credentials.db
-              table_name passwords
-          }
-      }
-      
-      # imapsql module stores all indexes and metadata necessary for IMAP using a
-      # relational database. It is used by IMAP endpoint for mailbox access and
-      # also by SMTP & Submission endpoints for delivery of local messages.
-      #
-      # IMAP accounts, mailboxes and all message metadata can be inspected using
-      # imap-* subcommands of maddyctl utility.
-      
-      storage.imapsql local_mailboxes {
-          driver sqlite3
-          dsn imapsql.db
-          disable_recent false
-          compression zstd
-
-          imap_filter {
-              command ${mailboxFilterScript} {account_name}
-          }
-      }
-      
-      # ----------------------------------------------------------------------------
-      # SMTP endpoints + message routing
-
-      msgpipeline local_routing {
-          dmarc yes
-          check {
-              require_mx_record
-              dkim
-              spf {
-                  permerr_action ignore
-              }
-              command ${receiveFilterScript} {sender} {rcpts} {
-                  run_on rcpt
-
-                  code 1 ignore
-                  code 2 ignore
-                  code 10 reject
-                  code 20 quarantine
-              }
-          }
-      
-          destination postmaster $(local_domains) {
-              modify {
-                  replace_rcpt static {
-                     entry postmaster             postmaster@$(primary_domain)
-                     entry leon@thein.ovh         leah@ctu.cx
-                     entry aufsicht@zug.network   mail@zug.network
-                     entry verwaltung@zug.network mail@zug.network
-                  }
-      
-                  # Implement plus-address notation.
-                  replace_rcpt regexp "(.+)\+(.+)@(.+)" "$1@$3"
-      
-                  replace_rcpt regexp "(.+)@ctucx.de" "leah@ctu.cx"
-                  replace_rcpt regexp "(.+)@ctu.cx"   "leah@ctu.cx"
-              }
-      
-              deliver_to &local_mailboxes
-          }
-      
-          default_destination {
-              reject 550 5.1.1 "User doesn't exist"
-          }
-      }
-      
-      smtp tcp://0.0.0.0:25 {
-          limits {
-              # Up to 20 msgs/sec across max. 10 SMTP connections.
-              all rate 20 1s
-              all concurrency 10
-          }
-      
-          source $(local_domains) {
-              reject 501 5.1.8 "Use Submission for outgoing SMTP"
-          }
-      
-          default_source {
-              destination postmaster $(local_domains) {
-                  deliver_to &local_routing
-              }
-      
-              default_destination {
-                  reject 550 5.1.1 "User doesn't exist"
-              }
-          }
-      }
-      
-      submission tls://0.0.0.0:465 tcp://0.0.0.0:587 {
-          limits {
-              # Up to 50 msgs/sec across any amount of SMTP connections.
-              all rate 50 1s
-          }
-      
-          auth &local_authdb
-      
-          source $(local_domains) {
-              destination postmaster $(local_domains) {
-                  deliver_to &local_routing
-              }
-      
-              default_destination {
-                  modify {
-                      dkim $(primary_domain) $(local_domains) default {
-                          newkey_algo ed25519
-                      }
-                  }
-                  deliver_to &remote_queue
-              }
-          }
-      
-          default_source {
-              reject 501 5.1.8 "Non-local sender domain"
-          }
-      }
-      
-      target.remote outbound_delivery {
-          limits {
-              # Up to 20 msgs/sec across max. 10 SMTP connections
-              # for each recipient domain.
-              destination rate 20 1s
-              destination concurrency 10
-          }
-      
-          mx_auth {
-              dane
-      
-              mtasts {
-                  cache fs
-                  fs_dir mtasts_cache/
-              }
-      
-              local_policy {
-                  min_tls_level encrypted
-                  min_mx_level none
-              }
-          }
-      }
-      
-      target.queue remote_queue {
-          target &outbound_delivery
-          max_parallelism 16
-          max_tries 4
-      
-          autogenerated_msg_domain $(primary_domain)
-      
-          bounce {
-              destination postmaster $(local_domains) {
-                  deliver_to &local_routing
-              }
-      
-              default_destination {
-                  reject 550 5.0.0 "Refusing to send DSNs to non-local addresses"
-              }
-          }
-      }
-      
-      # ----------------------------------------------------------------------------
-      # IMAP endpoints
-      
-      imap tls://0.0.0.0:993 tcp://0.0.0.0:143 {
-          auth &local_authdb
-          storage &local_mailboxes
-      }
-    '';
-  };
-
-}
diff --git a/machines/trabbi/mail.nix b/machines/trabbi/mail.nix
@@ -6,9 +6,9 @@
     inputs.simple-nixos-mailserver.nixosModule
   ];
 
-  age.secrets.restic-mail.file              = ../../secrets/trabbi/restic/mail.age;
-  age.secrets.mail-password-leah.file       = ../../secrets/trabbi/mail/password-leah-ctu.cx.age;
-  age.secrets.mail-password-zugnetwork.file = ../../secrets/trabbi/mail/password-mail-zug.network.age;
+  age.secrets.restic-mail.file              = ./. + "/../../secrets/${config.networking.hostName}/restic/mail.age";
+  age.secrets.mail-password-leah.file       = ./. + "/../../secrets/${config.networking.hostName}/mail/password-leah-ctu.cx.age";
+  age.secrets.mail-password-zugnetwork.file = ./. + "/../../secrets/${config.networking.hostName}/mail/password-mail-zug.network.age";
 
   dns.zones = with pkgs.dns.lib.combinators; let
     TXT   = [ "v=spf1 a mx ip4:${config.networking.primaryIP4} +ip6:${config.networking.primaryIP} ~all" ];
diff --git a/machines/trabbi/matrix-synapse.nix b/machines/trabbi/matrix-synapse.nix
@@ -2,10 +2,12 @@
 
 {
 
+  dns.zones."ctu.cx".subdomains.matrix.CNAME = [ "${config.networking.fqdn}." ];
+
   age.secrets = {
-    restic-matrix-synapse.file        = ../../secrets/trabbi/restic/matrix-synapse.age;
+    restic-matrix-synapse.file        = ./. + "/../../secrets/${config.networking.hostName}/restic/matrix-synapse.age";
     matrix-registration_shared_secret = {
-      file  = ../../secrets/trabbi/matrix-synapse/registration_shared_secret.age;
+      file  = ./. + "/../../secrets/${config.networking.hostName}/matrix-synapse/registration_shared_secret.age";
       owner = "matrix-synapse";
     };
   };

@@ -19,8 +21,6 @@
 
   systemd.services.matrix-synapse.onFailure = [ "email-notify@%i.service" ];
 
-  dns.zones."ctu.cx".subdomains.matrix.CNAME = [ "${config.networking.fqdn}." ];
-
   services = {
     postgresql = {
       enable        = true;
diff --git a/machines/trabbi/websites/bikemap.ctu.cx.nix b/machines/trabbi/websites/bikemap.ctu.cx.nix
@@ -11,12 +11,10 @@ in {
 
   dns.zones."ctu.cx".subdomains.bikemap.CNAME = [ "${config.networking.fqdn}." ];
 
-  users = {
-    users."bikemap" = {
-      home = "/var/lib/bikemap";
-      group = "git";
-      isSystemUser = true;
-    };
+  users.users."bikemap" = {
+    home = "/var/lib/bikemap";
+    group = "git";
+    isSystemUser = true;
   };
 
   security.sudo.extraRules = [{

@@ -26,56 +24,54 @@ in {
     ];
   }];
 
-  systemd = {
-    services.deploy-bikemap = {
-      script = ''
-        # strict mode
-        set -euo pipefail
-        IFS=$'\n\t'
+  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
+      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
+      ${pkgs.git}/bin/git clone /var/lib/gitolite/repositories/biketracks.git $TMP_DIR/tracks
 
-        mkdir $TMP_DIR/tiles
+      mkdir $TMP_DIR/tiles
 
-        ${pkgs.generateTilesFromGPX}/bin/generateTilesFromGPX $TMP_DIR/tracks $TMP_DIR/tiles
+      ${pkgs.generateTilesFromGPX}/bin/generateTilesFromGPX $TMP_DIR/tracks $TMP_DIR/tiles
 
-        rm -rf ~/*;
+      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
-      '';
+      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";
+    serviceConfig = {
+      Type = "oneshot";
 
-        User  = "bikemap";
-        Group = "git";
+      User  = "bikemap";
+      Group = "git";
 
-        WorkingDirectory        = "~";
-        StateDirectory          = "bikemap";
-        StateDirectoryMode      = "755";
+      WorkingDirectory        = "~";
+      StateDirectory          = "bikemap";
+      StateDirectoryMode      = "755";
 
-        NoNewPrivileges         = true;
-        PrivateTmp              = true;
-        PrivateDevices          = true;
+      NoNewPrivileges         = true;
+      PrivateTmp              = true;
+      PrivateDevices          = true;
 
-        RestrictAddressFamilies = "none";
-        RestrictNamespaces      = true;
-        RestrictRealtime        = true;
+      RestrictAddressFamilies = "none";
+      RestrictNamespaces      = true;
+      RestrictRealtime        = true;
 
-        ProtectSystem           = "full";
-        ProtectControlGroups    = true;
-        ProtectKernelModules    = true;
-        ProtectKernelTunables   = true;
+      ProtectSystem           = "full";
+      ProtectControlGroups    = true;
+      ProtectKernelModules    = true;
+      ProtectKernelTunables   = true;
 
-        DevicePolicy            = "closed";
-        LockPersonality         = true;
-      };
+      DevicePolicy            = "closed";
+      LockPersonality         = true;
     };
   };