diff --git a/.github/workflows/fmt.yaml b/.github/workflows/fmt.yaml
deleted file mode 100644
index 93d16c5..0000000
--- a/.github/workflows/fmt.yaml
+++ /dev/null
@@ -1,27 +0,0 @@
-name: main
-
-on:
-  push:
-    branches:
-      - main
-  pull_request:
-    branches:
-      - main
-
-jobs:
-  check-flake:
-    name: Nixpkgs Formatting
-    runs-on: ubuntu-latest
-
-    steps:
-      - uses: actions/checkout@v3
-
-      - name: Install Nix
-        uses: cachix/install-nix-action@v18
-        with:
-          extra_nix_config: |
-            experimental-features = nix-command flakes
-
-      - run: nix-channel --add https://nixos.org/channels/nixos-22.11 nixos 
-      - run: nix-channel --update
-      - run: nix shell nixpkgs#nixpkgs-fmt -c nixpkgs-fmt . --check
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
deleted file mode 100644
index 8f203d2..0000000
--- a/.github/workflows/main.yml
+++ /dev/null
@@ -1,34 +0,0 @@
-name: main
-
-on:
-  push:
-    branches:
-      - main
-  pull_request:
-    branches:
-      - main
-
-jobs:
-  check-flake:
-    name: Check Flake
-    runs-on: ubuntu-latest
-
-    steps:
-      - uses: actions/checkout@v3
-
-      - name: Install Nix
-        uses: cachix/install-nix-action@v18
-        with:
-          install_url: https://releases.nixos.org/nix/nix-2.13.3/install
-          extra_nix_config: |
-            experimental-features = nix-command flakes
-
-      - uses: cachix/cachix-action@v12
-        with:
-          name: fruitbasket
-          authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
-          extraPullNames: nix-community
-
-      - run: nix build
-
-      - run: nix flake check
diff --git a/.sops.yaml b/.sops.yaml
index 0b6cb06..7513f79 100755
--- a/.sops.yaml
+++ b/.sops.yaml
@@ -7,9 +7,11 @@ keys:
   - &helene B43C3A8A92CA28486AC6C4E2F115100C787C1C19
   - &fugi BF37903AE6FD294C4C674EE24472A20091BFA792
   - &emmanuel E83F398E6423179FE4F63D4FF085CAD394DE329D
-  - &jonas A4F92BC7B792108A463995827C1F2DA2BC929412
   - &joachim  B1A16011B86BACB56ADB713DB712039D23133661
+  - &hendrik FBBFAC260D9283D1EF2397DD3CA65E9DD6EB319D
+  - &frieder age1x76ajqw8w4l5vlkwt5s3flz5a5jq5qlxv7uppmnf8ckj9egh9ekqjclzt6
   - &quitte age1wvdnprpnq2rcc4se3zpx2p267n0apxg2jucvlm93e3pfj439ephqh2506t
+  - &tomate age18lwgjazaxujqgcc5j0gjllnykhtjn6p0q44jzrsk4au2a5k6nd9s77kd6d
 
 creation_rules:
   - path_regex: secrets/quitte\.yaml$
@@ -21,9 +23,23 @@ creation_rules:
         - *rouven
         - *fugi
         - *joachim
-        - *jonas
+        - *hendrik
         age:
+        - *frieder
         - *quitte
+  - path_regex: secrets/tomate\.yaml$
+    key_groups:
+      - pgp:
+        - *bennofs
+        - *revol-xut
+        - *felix
+        - *rouven
+        - *fugi
+        - *joachim
+        - *hendrik
+        age:
+        - *frieder
+        - *tomate
   - path_regex: secrets/admin\.yaml$
     key_groups:
       - pgp:
@@ -33,4 +49,5 @@ creation_rules:
         - *rouven
         - *fugi
         - *joachim
-        - *jonas
+        - *hendrik
+        - *frieder
diff --git a/flake.lock b/flake.lock
index bfeb662..4da82c5 100644
--- a/flake.lock
+++ b/flake.lock
@@ -1,19 +1,61 @@
 {
   "nodes": {
+    "authentik": {
+      "inputs": {
+        "authentik-src": "authentik-src",
+        "flake-compat": "flake-compat",
+        "flake-parts": "flake-parts",
+        "flake-utils": "flake-utils",
+        "napalm": "napalm",
+        "nixpkgs": "nixpkgs",
+        "pyproject-build-systems": "pyproject-build-systems",
+        "pyproject-nix": "pyproject-nix",
+        "systems": "systems",
+        "uv2nix": "uv2nix"
+      },
+      "locked": {
+        "lastModified": 1746294280,
+        "narHash": "sha256-Y8JGnaYXk71ipBYFw83dvS1zKBftppT1RnRT/XsWKIM=",
+        "owner": "MarcelCoding",
+        "repo": "authentik-nix",
+        "rev": "c2a6bb12f90241df93fe2d5553c8bca476dcb52b",
+        "type": "github"
+      },
+      "original": {
+        "owner": "MarcelCoding",
+        "repo": "authentik-nix",
+        "type": "github"
+      }
+    },
+    "authentik-src": {
+      "flake": false,
+      "locked": {
+        "lastModified": 1745954192,
+        "narHash": "sha256-QuIgeu3CN6S44/zSiaj+iIkDz2494mb1MWvD3eYYkVE=",
+        "owner": "goauthentik",
+        "repo": "authentik",
+        "rev": "22412729e2379d645da2ac0c0270a0ac6147945e",
+        "type": "github"
+      },
+      "original": {
+        "owner": "goauthentik",
+        "ref": "version/2025.4.0",
+        "repo": "authentik",
+        "type": "github"
+      }
+    },
     "course-management": {
       "inputs": {
-        "flake-utils": "flake-utils",
-        "nixpkgs": [
-          "nixpkgs"
-        ],
+        "flake-utils": "flake-utils_2",
+        "nixpkgs": "nixpkgs_2",
         "poetry2nix": "poetry2nix"
       },
       "locked": {
-        "lastModified": 1694358978,
-        "narHash": "sha256-gHWIIYJZepq1/3oFVkUkl0n52bRJWnNgmGaiZ2aGEwc=",
+        "lastModified": 1730751072,
+        "narHash": "sha256-+FQjzCNV3k8U4BfNcFmoZTRf8aO9ufn3s7kkzHj/b7s=",
         "owner": "fsr",
         "repo": "course-management",
-        "rev": "5ccbee8151c5caa519ebdb2ce2b8ec52b7749949",
+        "rev": "60b7062ce47ee9f0609e701ad5eb5e3e0a857ff2",
         "type": "github"
       },
       "original": {
@@ -22,16 +64,73 @@
         "type": "github"
       }
     },
-    "flake-utils": {
+    "ese-manual": {
       "inputs": {
-        "systems": "systems"
+        "nixpkgs": [
+          "nixpkgs"
+        ]
       },
       "locked": {
-        "lastModified": 1687709756,
-        "narHash": "sha256-Y5wKlQSkgEK2weWdOu4J3riRd+kV/VCgHsqLNTTWQ/0=",
+        "lastModified": 1730889586,
+        "narHash": "sha256-SLgo7UjWLaFaaUPFqzKbr9DLAGzm5kparfxuJHEpK3w=",
+        "ref": "refs/heads/main",
+        "rev": "a111147ce5eaea4f1d691afe1203e7529d68522d",
+        "revCount": 9,
+        "type": "git",
+        "url": "https://git.ifsr.de/ese/manual-website"
+      },
+      "original": {
+        "type": "git",
+        "url": "https://git.ifsr.de/ese/manual-website"
+      }
+    },
+    "flake-compat": {
+      "flake": false,
+      "locked": {
+        "lastModified": 1733328505,
+        "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=",
+        "owner": "edolstra",
+        "repo": "flake-compat",
+        "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec",
+        "type": "github"
+      },
+      "original": {
+        "owner": "edolstra",
+        "repo": "flake-compat",
+        "type": "github"
+      }
+    },
+    "flake-parts": {
+      "inputs": {
+        "nixpkgs-lib": "nixpkgs-lib"
+      },
+      "locked": {
+        "lastModified": 1743550720,
+        "narHash": "sha256-hIshGgKZCgWh6AYJpJmRgFdR3WUbkY04o82X05xqQiY=",
+        "owner": "hercules-ci",
+        "repo": "flake-parts",
+        "rev": "c621e8422220273271f52058f618c94e405bb0f5",
+        "type": "github"
+      },
+      "original": {
+        "owner": "hercules-ci",
+        "repo": "flake-parts",
+        "type": "github"
+      }
+    },
+    "flake-utils": {
+      "inputs": {
+        "systems": [
+          "authentik",
+          "systems"
+        ]
+      },
+      "locked": {
+        "lastModified": 1731533236,
+        "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
         "owner": "numtide",
         "repo": "flake-utils",
-        "rev": "dbabf0ca0c0c4bce6ea5eaf65af5cb694d2082c7",
+        "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
         "type": "github"
       },
       "original": {
@@ -45,11 +144,47 @@
         "systems": "systems_2"
       },
       "locked": {
-        "lastModified": 1687709756,
-        "narHash": "sha256-Y5wKlQSkgEK2weWdOu4J3riRd+kV/VCgHsqLNTTWQ/0=",
+        "lastModified": 1726560853,
+        "narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=",
         "owner": "numtide",
         "repo": "flake-utils",
-        "rev": "dbabf0ca0c0c4bce6ea5eaf65af5cb694d2082c7",
+        "rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a",
+        "type": "github"
+      },
+      "original": {
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "type": "github"
+      }
+    },
+    "flake-utils_3": {
+      "inputs": {
+        "systems": "systems_3"
+      },
+      "locked": {
+        "lastModified": 1726560853,
+        "narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=",
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a",
+        "type": "github"
+      },
+      "original": {
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "type": "github"
+      }
+    },
+    "flake-utils_4": {
+      "inputs": {
+        "systems": "systems_6"
+      },
+      "locked": {
+        "lastModified": 1681202837,
+        "narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=",
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "rev": "cfacdce06f30d2b68473a46042957675eebb3401",
         "type": "github"
       },
       "original": {
@@ -65,11 +200,11 @@
         ]
       },
       "locked": {
-        "lastModified": 1693305731,
-        "narHash": "sha256-ku0FU1pn6eXGdoEx0Tg0Kp8c8wmd6TF7IrdOnX0Uco0=",
+        "lastModified": 1744024964,
+        "narHash": "sha256-zmYWGZ7/tRSCy/PzghdguMpAdauWiYr6AJnbYCVHBFE=",
         "owner": "fsr",
         "repo": "kpp",
-        "rev": "7c04f958bb652de680ae3311b6eab080ac64b3ad",
+        "rev": "03e9650edb8d1e9ff424c2c2799736fbae56314b",
         "type": "github"
       },
       "original": {
@@ -78,52 +213,189 @@
         "type": "github"
       }
     },
+    "napalm": {
+      "inputs": {
+        "flake-utils": [
+          "authentik",
+          "flake-utils"
+        ],
+        "nixpkgs": [
+          "authentik",
+          "nixpkgs"
+        ]
+      },
+      "locked": {
+        "lastModified": 1725806412,
+        "narHash": "sha256-lGZjkjds0p924QEhm/r0BhAxbHBJE1xMOldB/HmQH04=",
+        "owner": "willibutz",
+        "repo": "napalm",
+        "rev": "b492440d9e64ae20736d3bec5c7715ffcbde83f5",
+        "type": "github"
+      },
+      "original": {
+        "owner": "willibutz",
+        "ref": "avoid-foldl-stack-overflow",
+        "repo": "napalm",
+        "type": "github"
+      }
+    },
+    "nix-github-actions": {
+      "inputs": {
+        "nixpkgs": [
+          "course-management",
+          "poetry2nix",
+          "nixpkgs"
+        ]
+      },
+      "locked": {
+        "lastModified": 1729742964,
+        "narHash": "sha256-B4mzTcQ0FZHdpeWcpDYPERtyjJd/NIuaQ9+BV1h+MpA=",
+        "owner": "nix-community",
+        "repo": "nix-github-actions",
+        "rev": "e04df33f62cdcf93d73e9a04142464753a16db67",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nix-community",
+        "repo": "nix-github-actions",
+        "type": "github"
+      }
+    },
+    "nix-index-database": {
+      "inputs": {
+        "nixpkgs": [
+          "nixpkgs"
+        ]
+      },
+      "locked": {
+        "lastModified": 1746330942,
+        "narHash": "sha256-ShizFaJCAST23tSrHHtFFGF0fwd72AG+KhPZFFQX/0o=",
+        "owner": "nix-community",
+        "repo": "nix-index-database",
+        "rev": "137fd2bd726fff343874f85601b51769b48685cc",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nix-community",
+        "repo": "nix-index-database",
+        "type": "github"
+      }
+    },
     "nixpkgs": {
       "locked": {
-        "lastModified": 1694499547,
-        "narHash": "sha256-R7xMz1Iia6JthWRHDn36s/E248WB1/je62ovC/dUVKI=",
-        "owner": "nixos",
+        "lastModified": 1746183838,
+        "narHash": "sha256-kwaaguGkAqTZ1oK0yXeQ3ayYjs8u/W7eEfrFpFfIDFA=",
+        "owner": "NixOS",
         "repo": "nixpkgs",
-        "rev": "e5f018cf150e29aac26c61dac0790ea023c46b24",
+        "rev": "bf3287dac860542719fe7554e21e686108716879",
         "type": "github"
       },
       "original": {
-        "owner": "nixos",
-        "ref": "nixos-23.05",
+        "owner": "NixOS",
+        "ref": "nixos-24.11",
         "repo": "nixpkgs",
         "type": "github"
       }
     },
-    "nixpkgs-stable": {
+    "nixpkgs-lib": {
       "locked": {
-        "lastModified": 1693675694,
-        "narHash": "sha256-2pIOyQwGyy2FtFAUIb8YeKVmOCcPOTVphbAvmshudLE=",
-        "owner": "NixOS",
-        "repo": "nixpkgs",
-        "rev": "5601118d39ca9105f8e7b39d4c221d3388c0419d",
+        "lastModified": 1743296961,
+        "narHash": "sha256-b1EdN3cULCqtorQ4QeWgLMrd5ZGOjLSLemfa00heasc=",
+        "owner": "nix-community",
+        "repo": "nixpkgs.lib",
+        "rev": "e4822aea2a6d1cdd36653c134cacfd64c97ff4fa",
         "type": "github"
       },
       "original": {
-        "owner": "NixOS",
-        "ref": "release-23.05",
+        "owner": "nix-community",
+        "repo": "nixpkgs.lib",
+        "type": "github"
+      }
+    },
+    "nixpkgs_2": {
+      "locked": {
+        "lastModified": 1730531603,
+        "narHash": "sha256-Dqg6si5CqIzm87sp57j5nTaeBbWhHFaVyG7V6L8k3lY=",
+        "owner": "nixos",
+        "repo": "nixpkgs",
+        "rev": "7ffd9ae656aec493492b44d0ddfb28e79a1ea25d",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nixos",
+        "ref": "nixos-unstable",
         "repo": "nixpkgs",
         "type": "github"
       }
     },
+    "nixpkgs_3": {
+      "locked": {
+        "lastModified": 1746557022,
+        "narHash": "sha256-QkNoyEf6TbaTW5UZYX0OkwIJ/ZMeKSSoOMnSDPQuol0=",
+        "owner": "nixos",
+        "repo": "nixpkgs",
+        "rev": "1d3aeb5a193b9ff13f63f4d9cc169fb88129f860",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nixos",
+        "ref": "nixos-24.11",
+        "repo": "nixpkgs",
+        "type": "github"
+      }
+    },
+    "nixpkgs_4": {
+      "locked": {
+        "lastModified": 1682134069,
+        "narHash": "sha256-TnI/ZXSmRxQDt2sjRYK/8j8iha4B4zP2cnQCZZ3vp7k=",
+        "owner": "NixOS",
+        "repo": "nixpkgs",
+        "rev": "fd901ef4bf93499374c5af385b2943f5801c0833",
+        "type": "github"
+      },
+      "original": {
+        "id": "nixpkgs",
+        "type": "indirect"
+      }
+    },
+    "notenrechner": {
+      "inputs": {
+        "nixpkgs": [
+          "nixpkgs"
+        ],
+        "utils": "utils"
+      },
+      "locked": {
+        "lastModified": 1742228793,
+        "narHash": "sha256-USud87Uu/ZI6R+4vM0hxLdkOUr6nsJCnAEeIrtSRkCU=",
+        "ref": "refs/heads/main",
+        "rev": "c100e3dba23a089fbdf403d2ba31cf87614ee035",
+        "revCount": 10,
+        "type": "git",
+        "url": "https://git.ifsr.de/frieder.hannenheim/notenrechner.git"
+      },
+      "original": {
+        "type": "git",
+        "url": "https://git.ifsr.de/frieder.hannenheim/notenrechner.git"
+      }
+    },
     "poetry2nix": {
       "inputs": {
-        "flake-utils": "flake-utils_2",
+        "flake-utils": "flake-utils_3",
+        "nix-github-actions": "nix-github-actions",
         "nixpkgs": [
           "course-management",
           "nixpkgs"
-        ]
+        ],
+        "systems": "systems_4",
+        "treefmt-nix": "treefmt-nix"
       },
       "locked": {
-        "lastModified": 1688440303,
-        "narHash": "sha256-hFfOyityHdVFI0HNM+sqZfpi9Fbvjvy0N9O7FjuqPWY=",
+        "lastModified": 1730284601,
+        "narHash": "sha256-eHYcKVLIRRv3J1vjmxurS6HVdGphB53qxUeAkylYrZY=",
         "owner": "nix-community",
         "repo": "poetry2nix",
-        "rev": "04714155bae013fb9b207e54d1faf9f0c3d08706",
+        "rev": "43a898b4d76f7f3f70df77a2cc2d40096bc9d75e",
         "type": "github"
       },
       "original": {
@@ -132,27 +404,102 @@
         "type": "github"
       }
     },
+    "print-interface": {
+      "inputs": {
+        "nixpkgs": [
+          "nixpkgs"
+        ]
+      },
+      "locked": {
+        "lastModified": 1706540741,
+        "narHash": "sha256-4/JI3xhw76Z1oa8Ivn3AzR6zNqXkmSEgHl+v0PRGnTc=",
+        "owner": "fsr",
+        "repo": "print-interface",
+        "rev": "ca830bc64ee92ec24562e707ddf36c19a5607a94",
+        "type": "github"
+      },
+      "original": {
+        "owner": "fsr",
+        "repo": "print-interface",
+        "type": "github"
+      }
+    },
+    "pyproject-build-systems": {
+      "inputs": {
+        "nixpkgs": [
+          "authentik",
+          "nixpkgs"
+        ],
+        "pyproject-nix": [
+          "authentik",
+          "pyproject-nix"
+        ],
+        "uv2nix": [
+          "authentik",
+          "uv2nix"
+        ]
+      },
+      "locked": {
+        "lastModified": 1744599653,
+        "narHash": "sha256-nysSwVVjG4hKoOjhjvE6U5lIKA8sEr1d1QzEfZsannU=",
+        "owner": "pyproject-nix",
+        "repo": "build-system-pkgs",
+        "rev": "7dba6dbc73120e15b558754c26024f6c93015dd7",
+        "type": "github"
+      },
+      "original": {
+        "owner": "pyproject-nix",
+        "repo": "build-system-pkgs",
+        "type": "github"
+      }
+    },
+    "pyproject-nix": {
+      "inputs": {
+        "nixpkgs": [
+          "authentik",
+          "nixpkgs"
+        ]
+      },
+      "locked": {
+        "lastModified": 1746146146,
+        "narHash": "sha256-60+mzI2lbgn+G8F5mz+cmkDvHFn4s5oqcOna1SzYy74=",
+        "owner": "pyproject-nix",
+        "repo": "pyproject.nix",
+        "rev": "3e9623bdd86a3c545e82b7f97cfdba5f07232d9a",
+        "type": "github"
+      },
+      "original": {
+        "owner": "pyproject-nix",
+        "repo": "pyproject.nix",
+        "type": "github"
+      }
+    },
     "root": {
       "inputs": {
+        "authentik": "authentik",
         "course-management": "course-management",
+        "ese-manual": "ese-manual",
         "kpp": "kpp",
-        "nixpkgs": "nixpkgs",
-        "sops-nix": "sops-nix"
+        "nix-index-database": "nix-index-database",
+        "nixpkgs": "nixpkgs_3",
+        "notenrechner": "notenrechner",
+        "print-interface": "print-interface",
+        "sops-nix": "sops-nix",
+        "vscode-server": "vscode-server"
       }
     },
     "sops-nix": {
       "inputs": {
         "nixpkgs": [
           "nixpkgs"
-        ],
-        "nixpkgs-stable": "nixpkgs-stable"
+        ]
       },
       "locked": {
-        "lastModified": 1694495315,
-        "narHash": "sha256-sZEYXs9T1NVHZSSbMqBEtEm2PGa7dEDcx0ttQkArORc=",
+        "lastModified": 1746485181,
+        "narHash": "sha256-PxrrSFLaC7YuItShxmYbMgSuFFuwxBB+qsl9BZUnRvg=",
         "owner": "Mic92",
         "repo": "sops-nix",
-        "rev": "ea208e55f8742fdcc0986b256bdfa8986f5e4415",
+        "rev": "e93ee1d900ad264d65e9701a5c6f895683433386",
         "type": "github"
       },
       "original": {
@@ -163,16 +510,16 @@
     },
     "systems": {
       "locked": {
-        "lastModified": 1681028828,
-        "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+        "lastModified": 1689347949,
+        "narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=",
         "owner": "nix-systems",
-        "repo": "default",
-        "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+        "repo": "default-linux",
+        "rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68",
         "type": "github"
       },
       "original": {
         "owner": "nix-systems",
-        "repo": "default",
+        "repo": "default-linux",
         "type": "github"
       }
     },
@@ -190,6 +537,149 @@
         "repo": "default",
         "type": "github"
       }
+    },
+    "systems_3": {
+      "locked": {
+        "lastModified": 1681028828,
+        "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+        "owner": "nix-systems",
+        "repo": "default",
+        "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nix-systems",
+        "repo": "default",
+        "type": "github"
+      }
+    },
+    "systems_4": {
+      "locked": {
+        "lastModified": 1681028828,
+        "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+        "owner": "nix-systems",
+        "repo": "default",
+        "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+        "type": "github"
+      },
+      "original": {
+        "id": "systems",
+        "type": "indirect"
+      }
+    },
+    "systems_5": {
+      "locked": {
+        "lastModified": 1681028828,
+        "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+        "owner": "nix-systems",
+        "repo": "default",
+        "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nix-systems",
+        "repo": "default",
+        "type": "github"
+      }
+    },
+    "systems_6": {
+      "locked": {
+        "lastModified": 1681028828,
+        "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+        "owner": "nix-systems",
+        "repo": "default",
+        "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nix-systems",
+        "repo": "default",
+        "type": "github"
+      }
+    },
+    "treefmt-nix": {
+      "inputs": {
+        "nixpkgs": [
+          "course-management",
+          "poetry2nix",
+          "nixpkgs"
+        ]
+      },
+      "locked": {
+        "lastModified": 1730120726,
+        "narHash": "sha256-LqHYIxMrl/1p3/kvm2ir925tZ8DkI0KA10djk8wecSk=",
+        "owner": "numtide",
+        "repo": "treefmt-nix",
+        "rev": "9ef337e492a5555d8e17a51c911ff1f02635be15",
+        "type": "github"
+      },
+      "original": {
+        "owner": "numtide",
+        "repo": "treefmt-nix",
+        "type": "github"
+      }
+    },
+    "utils": {
+      "inputs": {
+        "systems": "systems_5"
+      },
+      "locked": {
+        "lastModified": 1731533236,
+        "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
+        "type": "github"
+      },
+      "original": {
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "type": "github"
+      }
+    },
+    "uv2nix": {
+      "inputs": {
+        "nixpkgs": [
+          "authentik",
+          "nixpkgs"
+        ],
+        "pyproject-nix": [
+          "authentik",
+          "pyproject-nix"
+        ]
+      },
+      "locked": {
+        "lastModified": 1746048139,
+        "narHash": "sha256-LdCLyiihLg6P2/mjzP0+W7RtraDSIaJJPTy6SCtW5Ag=",
+        "owner": "pyproject-nix",
+        "repo": "uv2nix",
+        "rev": "680e2f8e637bc79b84268949d2f2b2f5e5f1d81c",
+        "type": "github"
+      },
+      "original": {
+        "owner": "pyproject-nix",
+        "repo": "uv2nix",
+        "type": "github"
+      }
+    },
+    "vscode-server": {
+      "inputs": {
+        "flake-utils": "flake-utils_4",
+        "nixpkgs": "nixpkgs_4"
+      },
+      "locked": {
+        "lastModified": 1729422940,
+        "narHash": "sha256-DlvJv33ml5UTKgu4b0HauOfFIoDx6QXtbqUF3vWeRCY=",
+        "owner": "nix-community",
+        "repo": "nixos-vscode-server",
+        "rev": "8b6db451de46ecf9b4ab3d01ef76e59957ff549f",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nix-community",
+        "repo": "nixos-vscode-server",
+        "type": "github"
+      }
     }
   },
   "root": "root",
diff --git a/flake.nix b/flake.nix
index 9f0c453..1ec3f36 100755
--- a/flake.nix
+++ b/flake.nix
@@ -1,61 +1,135 @@
 {
   inputs = {
-    nixpkgs.url = github:nixos/nixpkgs/nixos-23.05;
-    sops-nix.url = github:Mic92/sops-nix;
+    nixpkgs.url = "github:nixos/nixpkgs/nixos-24.11";
+    sops-nix.url = "github:Mic92/sops-nix";
     sops-nix.inputs.nixpkgs.follows = "nixpkgs";
+    nix-index-database.url = "github:nix-community/nix-index-database";
+    nix-index-database.inputs.nixpkgs.follows = "nixpkgs";
     kpp.url = "github:fsr/kpp";
     kpp.inputs.nixpkgs.follows = "nixpkgs";
-    course-management = {
-      url = "github:fsr/course-management";
+    print-interface = {
+      url = "github:fsr/print-interface";
       inputs.nixpkgs.follows = "nixpkgs";
     };
-  };
-  outputs = { self, nixpkgs, sops-nix, kpp, course-management, ... }@inputs:
-    {
-      packages."x86_64-linux".quitte = self.nixosConfigurations.quitte.config.system.build.toplevel;
-      packages."x86_64-linux".default = self.packages."x86_64-linux".quitte;
-      formatter.x86_64-linux = nixpkgs.legacyPackages.x86_64-linux.nixpkgs-fmt;
-      hydraJobs."x86-64-linux".quitte = self.packages."x86_64-linux".quitte;
+    ese-manual.url = "git+https://git.ifsr.de/ese/manual-website";
+    ese-manual.inputs.nixpkgs.follows = "nixpkgs";
+    vscode-server.url = "github:nix-community/nixos-vscode-server";
+    notenrechner.url = "git+https://git.ifsr.de/frieder.hannenheim/notenrechner.git";
+    notenrechner.inputs.nixpkgs.follows = "nixpkgs";
+    authentik = {
+      # change to old one when we are at 25.05
+      # see https://github.com/nix-community/authentik-nix/issues/56 for context
+      url = "github:MarcelCoding/authentik-nix";
+      # url = "github:nix-community/authentik-nix";
+    };
 
+
+    course-management = {
+      url = "github:fsr/course-management";
+      # inputs.nixpkgs.follows = "nixpkgs";
+    };
+  };
+  outputs =
+    { self
+    , nixpkgs
+    , sops-nix
+    , nix-index-database
+    , kpp
+    , ese-manual
+    , vscode-server
+    , course-management
+    , print-interface
+    , authentik
+    , ...
+    }@inputs:
+    let
+      supportedSystems = [ "x86_64-linux" ];
+      forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
+      pkgs = forAllSystems (system: nixpkgs.legacyPackages.${system});
+
+    in
+    {
+      packages = forAllSystems (system: rec {
+        default = quitte;
+        quitte = self.nixosConfigurations.quitte.config.system.build.toplevel;
+        tomate = self.nixosConfigurations.tomate.config.system.build.toplevel;
+      });
+      formatter = forAllSystems (system: pkgs.${system}.nixpkgs-fmt);
+      hydraJobs = forAllSystems (system: {
+        quitte = self.packages.${system}.quitte;
+      });
+
+      devShells = forAllSystems (system: {
+        default = pkgs.${system}.mkShell {
+          packages = with pkgs.${system}; [
+            sops
+          ];
+        };
+      });
+      overlays.default = import ./overlays;
       nixosConfigurations = {
-        quitte = nixpkgs.lib.nixosSystem {
+        quitte = nixpkgs.lib.nixosSystem rec {
           system = "x86_64-linux";
+          specialArgs = inputs // { inherit system; };
           modules = [
             inputs.sops-nix.nixosModules.sops
             inputs.kpp.nixosModules.default
+            inputs.nix-index-database.nixosModules.nix-index
+            ese-manual.nixosModules.default
             course-management.nixosModules.default
+            vscode-server.nixosModules.default
+            authentik.nixosModules.default
+
             ./hosts/quitte/configuration.nix
-            ./modules/bacula.nix
-            ./modules/options.nix
-            ./modules/base.nix
-            ./modules/sops.nix
-            ./modules/kpp.nix
+            ./options
+
+            ./modules/core
+            ./modules/authentik
             ./modules/ldap
             ./modules/mail
-            ./modules/mailman.nix
-            ./modules/nginx.nix
-            ./modules/hydra.nix
-            ./modules/userdir.nix
+            ./modules/web
+            ./modules/courses
+            ./modules/wiki
+            ./modules/matrix
+            ./modules/keycloak
+            ./modules/monitoring
+
+            ./modules/nix-serve.nix
             ./modules/hedgedoc.nix
             ./modules/padlist.nix
-            ./modules/postgres.nix
-            ./modules/wiki.nix
-            ./modules/ftp.nix
-            ./modules/stream.nix
             ./modules/nextcloud.nix
-            ./modules/matrix.nix
-            ./modules/mautrix-telegram.nix
-            ./modules/sogo.nix
             ./modules/vaultwarden.nix
-            ./modules/website.nix
-            ./modules/zsh.nix
-            ./modules/course-management.nix
-            ./modules/gitea.nix
+            ./modules/forgejo
+            ./modules/kanboard.nix
+            ./modules/zammad.nix
+            # ./modules/decisions.nix
+            ./modules/stream.nix
+            # ./modules/struktur-bot.nix
             {
+              nixpkgs.overlays = [
+                self.overlays.default
+              ];
               sops.defaultSopsFile = ./secrets/quitte.yaml;
             }
           ];
         };
+        tomate = nixpkgs.lib.nixosSystem {
+          system = "x86_64-linux";
+          specialArgs = inputs;
+          modules = [
+            inputs.sops-nix.nixosModules.sops
+            inputs.nix-index-database.nixosModules.nix-index
+            vscode-server.nixosModules.default
+            print-interface.nixosModules.default
+            ./hosts/tomate/configuration.nix
+            ./modules/core/base.nix
+            ./modules/core/zsh.nix
+            ./modules/core/sssd.nix
+            {
+              sops.defaultSopsFile = ./secrets/tomate.yaml;
+            }
+          ];
+        };
       };
     };
 }
diff --git a/hosts/quitte/configuration.nix b/hosts/quitte/configuration.nix
index 98b70a7..7f75d9d 100644
--- a/hosts/quitte/configuration.nix
+++ b/hosts/quitte/configuration.nix
@@ -7,47 +7,61 @@
       ./network.nix
     ];
 
-  # Use the systemd-boot EFI boot loader.
-  boot.loader.systemd-boot.enable = true;
-  #boot.kernelPackages = config.boot.zfs.package.latestCompatibleLinuxPackages;
-  #boot.kernelParams = [ "video=VGA-1:1024x768@30" ];
+  boot.loader.systemd-boot = {
+    enable = true;
+    extraInstallCommands = ''
+      ${pkgs.coreutils}/bin/cp -r /boot/* /boot2
+    '';
+  };
+  # boot.kernelParams = [ "video=VGA-1:1024x768@30" ];
   boot.loader.efi.canTouchEfiVariables = true;
-  #boot.supportedFilesystems = [ "zfs" ];
-  #boot.zfs.devNodes = "/dev/";
+  boot.supportedFilesystems = [ "zfs" ];
 
-  services.qemuGuest.enable = true;
-
-  # don't freeze the whole system while building something
-  nix.settings.cores = 16;
-
-  # allows everyone in the nix-trusted group to perform builds
-  nix.settings.trusted-users = [ "@nix-trusted" ];
+  services.zfs = {
+    trim.enable = true;
+    autoScrub.enable = true;
+  };
 
   # Set your time zone.
   time.timeZone = "Europe/Berlin";
+  i18n.defaultLocale = "en_US.UTF-8";
 
-  # List packages installed in system profile. To search, run:
-  # $ nix search wget
-  environment.systemPackages = with pkgs; [
-    vim
-    wget
-    git
+  security.sudo.extraRules = [
+    {
+      commands = [
+        {
+          command = "ALL";
+          options = [ "NOPASSWD" ];
+        }
+      ];
+      groups = [ "admins" ];
+    }
+  ];
+  # prevent fork bombs
+  security.pam.loginLimits = [
+    {
+      domain = "@users";
+      item = "nproc";
+      type = "hard";
+      value = "2000";
+    }
+    {
+      domain = "@nixbld";
+      item = "nproc";
+      type = "hard";
+      value = "10000";
+    }
   ];
 
-  # Enable the OpenSSH daemon.
-  services.openssh.enable = true;
-  services.openssh.settings.PermitRootLogin = "yes";
-
-  # Open ports in the firewall.
-  networking.firewall.allowedTCPPorts = [ 443 80 ];
-  # networking.firewall.allowedUDPPorts = [ ... ];
-  # Or disable the firewall altogether.
-  # networking.firewall.enable = false;
-
-  # Copy the NixOS configuration file and link it from the resulting system
-  # (/run/current-system/configuration.nix). This is useful in case you
-  # accidentally delete configuration.nix.
-  # system.copySystemConfiguration = true;
+  systemd = {
+    services.nix-daemon.serviceConfig = {
+      MemoryMax = "32G";
+    };
+    # all users together may not use more than $MemoryMax of RAM
+    slices."user".sliceConfig = {
+      MemoryMax = "32G";
+    };
+  };
 
   # This value determines the NixOS release from which the default
   # settings for stateful data, like file locations and database versions
diff --git a/hosts/quitte/hardware-configuration.nix b/hosts/quitte/hardware-configuration.nix
index 3c7c759..5dad929 100644
--- a/hosts/quitte/hardware-configuration.nix
+++ b/hosts/quitte/hardware-configuration.nix
@@ -1,42 +1,52 @@
-# Do not modify this file!  It was generated by ‘nixos-generate-config’
-# and may be overwritten by future invocations.  Please make changes
-# to /etc/nixos/configuration.nix instead.
 { config, lib, modulesPath, ... }:
 
 {
-  imports =
-    [
-      (modulesPath + "/profiles/qemu-guest.nix")
-    ];
+  imports = [
+    (modulesPath + "/installer/scan/not-detected.nix")
+  ];
 
-  boot.initrd.availableKernelModules = [ "ata_piix" "uhci_hcd" "virtio_pci" "virtio_scsi" "sd_mod" "sr_mod" ];
+  boot.initrd.availableKernelModules = [ "megaraid_sas" "xhci_pci" "nvme" "ahci" "usbhid" "usb_storage" "sd_mod" "sr_mod" ];
   boot.initrd.kernelModules = [ ];
-  boot.kernelModules = [ ];
+  boot.kernelModules = [ "kvm-amd" ];
   boot.extraModulePackages = [ ];
 
-  fileSystems."/" =
-    {
-      device = "/dev/disk/by-uuid/4d57c7c1-ed70-4fb1-af4c-4ba027b75248";
-      fsType = "ext4";
-    };
+  fileSystems."/" = {
+    device = "rpool/nixos/root";
+    fsType = "zfs";
+  };
 
-  boot.initrd.luks.devices."luksroot".device = "/dev/disk/by-uuid/cfb9b37e-152d-45e9-b75d-88d71471be45";
+  fileSystems."/home" = {
+    device = "rpool/nixos/home";
+    fsType = "zfs";
+  };
 
-  fileSystems."/boot" =
-    {
-      device = "/dev/disk/by-uuid/06C4-1FDB";
-      fsType = "vfat";
-    };
+  fileSystems."/nix" = {
+    device = "rpool/nixos/nixnew";
+    fsType = "zfs";
+  };
+
+  fileSystems."/var/lib" = {
+    device = "rpool/nixos/var/lib";
+    fsType = "zfs";
+  };
+
+  fileSystems."/var/log" = {
+    device = "rpool/nixos/var/log";
+    fsType = "zfs";
+  };
+
+  fileSystems."/boot" = {
+    device = "/dev/disk/by-uuid/3278-8D00";
+    fsType = "vfat";
+    options = [ "nofail" ];
+  };
+  fileSystems."/boot2" = {
+    device = "/dev/disk/by-uuid/3366-F71E";
+    fsType = "vfat";
+    options = [ "nofail" ];
+  };
 
   swapDevices = [ ];
-
-  # Enables DHCP on each ethernet and wireless interface. In case of scripted networking
-  # (the default) this is the recommended approach. When using systemd-networkd it's
-  # still possible to use this option, but it's recommended to use it in conjunction
-  # with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
-  networking.useDHCP = lib.mkDefault true;
-  # networking.interfaces.ens18.useDHCP = lib.mkDefault true;
-
   nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
   hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
 }
diff --git a/hosts/quitte/network.nix b/hosts/quitte/network.nix
index 858f70e..f984edd 100644
--- a/hosts/quitte/network.nix
+++ b/hosts/quitte/network.nix
@@ -1,88 +1,59 @@
-{ config, ... }:
-let
-  wireguard_port = 51820;
-in
+{ config, lib, ... }:
 {
-  sops.secrets = {
-    "wg-fsr" = {
-      owner = config.users.users.systemd-network.name;
-    };
-  };
-
   networking = {
+    # portunus module does weird things to this, so we force it to some sane values
+    hosts = {
+      "127.0.0.1" = lib.mkForce [ "quitte.ifsr.de" "quitte" ];
+      "::1" = lib.mkForce [ "quitte.ifsr.de" "quitte" ];
+    };
     hostId = "a71c81fc";
     domain = "ifsr.de";
     hostName = "quitte";
     rDNS = config.networking.fqdn;
-    enableIPv6 = true;
-    useDHCP = true;
-    interfaces.ens18.useDHCP = true;
     useNetworkd = true;
+    nftables.enable = true;
 
-    firewall.allowedUDPPorts = [ wireguard_port ];
-    wireguard.enable = true;
+    firewall = {
+      logRefusedConnections = false;
+      trustedInterfaces = [ "podman0"];
+    };
   };
 
   services.resolved = {
     enable = true;
-    #dnssec = "false";
-    fallbackDns = [ "1.1.1.1" ];
+    fallbackDns = [ "9.9.9.9" ];
   };
 
-  # workaround for networkd waiting for shit
-  systemd.services.systemd-networkd-wait-online.serviceConfig.ExecStart = [
-    "" # clear old command
-    "${config.systemd.package}/lib/systemd/systemd-networkd-wait-online --any"
-  ];
-
   systemd.network = {
     enable = true;
+    wait-online.anyInterface = true;
 
     # Interfaces on the machine
-    networks."10-ether-bond" = {
-      matchConfig.Name = "ens18";
+    networks."10-wired-default" = {
+      matchConfig.Name = "enp65s0f0np0";
 
-      address = [ "141.30.30.169/25" ];
+      address = [
+
+        "141.30.30.194/26"
+        "2a13:dd85:b23:1::1337/64"
+      ];
       routes = [
         {
-          routeConfig.Gateway = "141.30.30.129";
+          Gateway = "141.30.30.193";
         }
-      ];
-      networkConfig = {
-        DNS = "141.30.1.1";
-        #IPv6AcceptRA = true;
-      };
-    };
-
-    # defining network device for wireguard connections
-    netdevs."fsr-wg" = {
-      netdevConfig = {
-        Kind = "wireguard";
-        Name = "fsr-wg";
-        Description = "fsr enterprise wireguard";
-      };
-      wireguardConfig = {
-        PrivateKeyFile = config.sops.secrets."wg-fsr".path;
-        ListenPort = wireguard_port;
-      };
-      wireguardPeers = [
         {
-          # tassilo
-          wireguardPeerConfig = {
-            PublicKey = "vgo3le9xrFsIbbDZsAhQZpIlX+TuWjfEyUcwkoqUl2Y=";
-            AllowedIPs = [ "10.66.66.100/32" ];
-            PersistentKeepalive = 25;
-          };
+          Gateway = "fe80::7a24:59ff:fe5e:6e2f";
         }
       ];
-    };
-
-    # fsr wireguard server
-    networks."fsr-wg" = {
-      matchConfig.Name = "fsr-wg";
       networkConfig = {
-        Address = "10.66.66.1/24";
-        IPForward = "ipv4";
+        DNS = [
+          "9.9.9.9"
+          "149.112.112.112"
+          "2620:fe::fe"
+          "2620:fe::9"
+        ];
+        LLDP = true;
+        EmitLLDP = "nearest-bridge";
       };
     };
   };
diff --git a/hosts/tomate/configuration.nix b/hosts/tomate/configuration.nix
new file mode 100644
index 0000000..dffdcea
--- /dev/null
+++ b/hosts/tomate/configuration.nix
@@ -0,0 +1,176 @@
+# Edit this configuration file to define what should be installed on
+# your system.  Help is available in the configuration.nix(5) man page
+# and in the NixOS manual (accessible by running ‘nixos-help’).
+
+{ config, pkgs, ... }:
+
+{
+  imports =
+    [
+      # Include the results of the hardware scan.
+      ./network.nix
+      ./hardware-configuration.nix
+    ];
+
+  # Bootloader.
+  boot.loader.systemd-boot.enable = true;
+  boot.loader.efi.canTouchEfiVariables = true;
+
+
+  nix = {
+    settings = {
+      substituters = [
+        "https://cache.ifsr.de"
+      ];
+      trusted-public-keys = [
+        "cache.ifsr.de:y55KBAMF4YkjIzXwYOKVk9fcQS+CZ9RM1zAAMYQJtsg="
+      ];
+    };
+  };
+
+  # Set your time zone.
+  time.timeZone = "Europe/Berlin";
+
+  # Select internationalisation properties.
+  i18n.defaultLocale = "de_DE.UTF-8";
+
+  i18n.extraLocaleSettings = {
+    LC_ADDRESS = "de_DE.UTF-8";
+    LC_IDENTIFICATION = "de_DE.UTF-8";
+    LC_MEASUREMENT = "de_DE.UTF-8";
+    LC_MONETARY = "de_DE.UTF-8";
+    LC_NAME = "de_DE.UTF-8";
+    LC_NUMERIC = "de_DE.UTF-8";
+    LC_PAPER = "de_DE.UTF-8";
+    LC_TELEPHONE = "de_DE.UTF-8";
+    LC_TIME = "de_DE.UTF-8";
+  };
+
+  # Enable the X11 windowing system.
+  services.xserver.enable = true;
+
+  # Enable the KDE Plasma Desktop Environment.
+  services.displayManager.sddm.enable = true;
+  services.xserver.desktopManager.plasma5.enable = true;
+
+  # Configure keymap in X11
+  services.xserver = {
+    xkb.layout = "de";
+    xkb.variant = "";
+  };
+
+  # Configure console keymap
+  console.keyMap = "de";
+
+
+
+  services.printing = {
+    enable = true;
+    stateless = true;
+    drivers = with pkgs; [ cups-kyocera ];
+    browsing = true;
+    defaultShared = true;
+    # todo fix
+    allowFrom = [ "all" ];
+    listenAddresses = [ "0.0.0.0:631" ];
+  };
+
+  sops.secrets."print/smtp-password" = {
+    owner = config.services.print-interface.user;
+    group = config.services.print-interface.group;
+  };
+
+  services.print-interface = {
+    enable = true;
+    smtp = {
+      username = "print";
+      passwordFile = config.sops.secrets."print/smtp-password".path;
+    };
+  };
+
+  services.avahi = {
+    enable = true;
+    nssmdns4 = true;
+    openFirewall = true;
+    publish = {
+      enable = true;
+      userServices = true;
+    };
+  };
+  networking.firewall = {
+    allowedTCPPorts = [
+      631
+      config.services.print-interface.listenPort
+    ];
+    allowedUDPPorts = [ 631 ];
+  };
+
+  # Enable sound with pipewire.
+  hardware.pulseaudio.enable = false;
+  security.rtkit.enable = true;
+  services.pipewire = {
+    enable = true;
+    alsa.enable = true;
+    alsa.support32Bit = true;
+    pulse.enable = true;
+    # If you want to use JACK applications, uncomment this
+    #jack.enable = true;
+
+    # use the example session manager (no others are packaged yet so this is enabled by default,
+    # no need to redefine it in your config for now)
+    #media-session.enable = true;
+  };
+
+  # Enable touchpad support (enabled default in most desktopManager).
+  # services.xserver.libinput.enable = true;
+
+  # Allow unfree packages
+  nixpkgs.config.allowUnfree = true;
+
+  # List packages installed in system profile. To search, run:
+  # $ nix search wget
+  environment.systemPackages = with pkgs; [
+    #  vim # Do not forget to add an editor to edit configuration.nix! The Nano editor is also installed by default.
+    #  wget
+  ];
+  security = {
+    pam = {
+      u2f = {
+        enable = true;
+      };
+      services = {
+        login.u2fAuth = true;
+        sudo.u2fAuth = true;
+      };
+    };
+
+  };
+
+  # Some programs need SUID wrappers, can be configured further or are
+  # started in user sessions.
+  # programs.mtr.enable = true;
+  # programs.gnupg.agent = {
+  #   enable = true;
+  #   enableSSHSupport = true;
+  # };
+
+  # List services that you want to enable:
+
+  # Enable the OpenSSH daemon.
+  services.openssh.enable = true;
+
+  # Open ports in the firewall.
+  # networking.firewall.allowedTCPPorts = [ ... ];
+  # networking.firewall.allowedUDPPorts = [ ... ];
+  # Or disable the firewall altogether.
+  # networking.firewall.enable = false;
+
+  # This value determines the NixOS release from which the default
+  # settings for stateful data, like file locations and database versions
+  # on your system were taken. It‘s perfectly fine and recommended to leave
+  # this value at the release version of the first install of this system.
+  # Before changing this value read the documentation for this option
+  # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
+  system.stateVersion = "23.05"; # Did you read the comment?
+
+}
diff --git a/hosts/tomate/hardware-configuration.nix b/hosts/tomate/hardware-configuration.nix
new file mode 100644
index 0000000..4d71ba7
--- /dev/null
+++ b/hosts/tomate/hardware-configuration.nix
@@ -0,0 +1,42 @@
+# Do not modify this file!  It was generated by ‘nixos-generate-config’
+# and may be overwritten by future invocations.  Please make changes
+# to /etc/nixos/configuration.nix instead.
+{ config, lib, modulesPath, ... }:
+
+{
+  imports =
+    [
+      (modulesPath + "/installer/scan/not-detected.nix")
+    ];
+
+  boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "ohci_pci" "ehci_pci" "usbhid" "usb_storage" "sd_mod" ];
+  boot.initrd.kernelModules = [ ];
+  boot.kernelModules = [ "kvm-amd" ];
+  boot.extraModulePackages = [ ];
+
+  fileSystems."/" =
+    {
+      device = "/dev/disk/by-uuid/618e281f-a8bf-4129-bfc1-aa47f86a8c54";
+      fsType = "ext4";
+    };
+
+  fileSystems."/boot" =
+    {
+      device = "/dev/disk/by-uuid/0844-2A73";
+      fsType = "vfat";
+    };
+
+  swapDevices =
+    [{ device = "/dev/disk/by-uuid/8bdeb0c1-8f1e-43a7-b4b9-c06e27a94460"; }];
+
+  # Enables DHCP on each ethernet and wireless interface. In case of scripted networking
+  # (the default) this is the recommended approach. When using systemd-networkd it's
+  # still possible to use this option, but it's recommended to use it in conjunction
+  # with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
+  networking.useDHCP = lib.mkDefault true;
+  # networking.interfaces.enp3s0.useDHCP = lib.mkDefault true;
+
+  nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
+  powerManagement.cpuFreqGovernor = lib.mkDefault "ondemand";
+  hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
+}
diff --git a/hosts/tomate/network.nix b/hosts/tomate/network.nix
new file mode 100644
index 0000000..dd04916
--- /dev/null
+++ b/hosts/tomate/network.nix
@@ -0,0 +1,40 @@
+{ config, ... }:
+{
+  sops.secrets.ifsr-apb-auth = { };
+  networking = {
+    domain = "ifsr.de";
+    hostName = "tomate";
+    useNetworkd = true;
+    nftables.enable = true;
+    # Radius authentification
+    supplicant."enp3s0" = {
+      driver = "wired";
+      configFile.path = config.sops.secrets.ifsr-apb-auth.path;
+    };
+  };
+
+  services.resolved = {
+    enable = true;
+    fallbackDns = [ "9.9.9.9" ];
+  };
+
+  systemd.network = {
+    enable = true;
+
+    networks."10-wired-default" = {
+      matchConfig.Name = "enp3s0";
+
+      address = [ "141.30.86.196/26" ];
+      routes = [
+        {
+          Gateway = "141.30.86.193";
+        }
+      ];
+      networkConfig = {
+        DNS = "141.30.1.1";
+        LLDP = true;
+        EmitLLDP = "nearest-bridge";
+      };
+    };
+  };
+}
diff --git a/keys/pgp/hendrik.asc b/keys/pgp/hendrik.asc
new file mode 100644
index 0000000..ad13ab9
--- /dev/null
+++ b/keys/pgp/hendrik.asc
@@ -0,0 +1,23 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mDMEZNJqYBYJKwYBBAHaRw8BAQdAKncDaEdOUQGOqVBQuEsJ42wCcyLB7x1XcNDZ
+VEQpVyO0JkhlbmRyaWsgV29sZmYgPGhlbmRyaWsud29sZmZAYWdkc24ubWU+iJAE
+ExYIADgWIQT7v6wmDZKD0e8jl908pl6d1usxnQUCZNJqYAIbAwULCQgHAgYVCgkI
+CwIEFgIDAQIeAQIXgAAKCRA8pl6d1usxnX6zAP9Rut+Yg31zBAiRdQxV4tlK+hko
+wCq9WIKtIbBvrqv5/AEAujkRCgBpFeHzhId55QmvK0FXZgFgfy9wm/QtXb4+lQ64
+MwRk0mrEFgkrBgEEAdpHDwEBB0AidcMADt+W+eSbrInHeCPZThyd1V7NKEMhk3sL
+xJApx4j1BBgWCAAmFiEE+7+sJg2Sg9HvI5fdPKZendbrMZ0FAmTSasQCGwIFCQeE
+zgAAgQkQPKZendbrMZ12IAQZFggAHRYhBEK0YmsN4JpCoNWvKp5LZR/BVBjgBQJk
+0mrEAAoJEJ5LZR/BVBjg6ogBAOcFh/S99L/aN6bQu9bYRPomakbNqypHA1YbodjG
+1IQgAPwLj19BXNnQmTgYzY3bWmtcAc8lsGWTNkDDTZMRRTP+BSS1AP9qBuCeU/fj
+2hpa17LiV6sjdRquxWQXjKxTlBRV8oKj1gD/WarlxiHt8nMn527FXuBrGZC+mZq2
+NvvoTb+uvZNliAq4OARk0mtEEgorBgEEAZdVAQUBAQdAkK0jBo/37NbRHMOYCal0
+9vGuK3KaxU3Cl9No+VbZDEYDAQgHiH4EGBYIACYWIQT7v6wmDZKD0e8jl908pl6d
+1usxnQUCZNJrRAIbDAUJB4TOAAAKCRA8pl6d1usxnbaqAP9abTf+DibaAR6hdU9y
+CEE5TD32EB+ySw/v45yCi28B8AEA5PcpwMD6emVrNQGeVChkOlwauwA3HkE6DDTO
+yeebAwi4MwRk0mvNFgkrBgEEAdpHDwEBB0DSYGCNq15sOLj1wDJjoKoCRMGH8I/y
+ARMUws7PQ4KPkYh+BBgWCAAmFiEE+7+sJg2Sg9HvI5fdPKZendbrMZ0FAmTSa80C
+GyAFCQeEzgAACgkQPKZendbrMZ1HggEAxSBYuJ4BTr9GCl8e79HTSwg8iIIJx8Nc
+REFvro0BrnEA/3AbyQYBQVAhqIwSSza5dr4+FiLbbVhPFcxU98TLBTQJ
+=sA2V
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/keys/pgp/jonas.asc b/keys/pgp/jonas.asc
deleted file mode 100644
index db77ff9..0000000
--- a/keys/pgp/jonas.asc
+++ /dev/null
@@ -1,77 +0,0 @@
------BEGIN PGP PUBLIC KEY BLOCK-----
-
-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
-=Fs3l
------END PGP PUBLIC KEY BLOCK-----
diff --git a/keys/ssh/frieder b/keys/ssh/frieder
new file mode 100644
index 0000000..1e1228e
--- /dev/null
+++ b/keys/ssh/frieder
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH70IC7DaiGBYdftUhuOE9CatcdYj2L50eZfztQA+pVs fried@Frieders-Void-Laptop
diff --git a/keys/ssh/jonasga b/keys/ssh/jonasga
deleted file mode 100644
index 5081d1f..0000000
--- a/keys/ssh/jonasga
+++ /dev/null
@@ -1 +0,0 @@
-ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDpOQuIl31BL16yXdLlbzSDCle6bjE3WNVXzOV9ibdzEC3PpUufJDTU7FMW3WCO9fnYJ5osPKbV9nou5/10mPuN0g+k1e0NWUZNHbG+5zRqS7QYGFmtDC8EUTx1xnri5zMBMn9jzjNE8BkqvsjGrHcVCtI2T51slwFjE60GFkloQ7izRDrNkge1iM57KhoXz5MeYJtolDqeOh5P7nfAUR4bGT/gGtYVd85oCvbsHcjF9vgDovAfNP+zQhUn51ZOXvGp8+1/MAJVtxLfjC9Ma3LRiiliD6w5zcsksG5cUGcj2Sk9i/7nTm7g5MGo4EKwgPMw/MRzSRzvlZ76oPSPSLKn jonas@T14s
\ No newline at end of file
diff --git a/modules/authentik/default.nix b/modules/authentik/default.nix
new file mode 100644
index 0000000..eb65477
--- /dev/null
+++ b/modules/authentik/default.nix
@@ -0,0 +1,16 @@
+{ config, lib, ... }:
+let
+  domain = "idm.${config.networking.domain}";
+in
+{
+  sops.secrets."authentik/env" = { };
+  services.authentik = {
+    enable = true;
+    nginx = {
+      enable = true;
+      host = domain;
+      enableACME = true;
+    };
+    environmentFile = config.sops.secrets."authentik/env".path;
+  };
+}
diff --git a/modules/bacula.nix b/modules/bacula.nix
deleted file mode 100644
index abb6c96..0000000
--- a/modules/bacula.nix
+++ /dev/null
@@ -1,78 +0,0 @@
-{ pkgs, config, lib, ... }:
-with lib;
-
-let
-  # We write a custom config file because the upstream config has some flaws
-  fd_cfg = config.services.bacula-fd;
-  fd_conf = pkgs.writeText "bacula-fd.conf" ''
-    Client {
-      Name = ${fd_cfg.name}
-      FDPort = ${toString fd_cfg.port}
-      WorkingDirectory = /var/lib/bacula
-      Pid Directory = /run
-      ${fd_cfg.extraClientConfig}
-    }
-
-    ${concatStringsSep "\n" (mapAttrsToList (name: value: ''
-    Director {
-      Name = ${name}
-      Password = ${value.password}
-      Monitor = ${value.monitor}
-    }
-    '') fd_cfg.director)}
-
-    Messages {
-      Name = Standard;
-      syslog = all, !skipped, !restored
-      ${fd_cfg.extraMessagesConfig}
-    }
-  '';
-  # AGDSN is running an outdated version that we have to comply to
-  bacula_package = (pkgs.bacula.overrideAttrs (old: rec {
-    version = "9.6.7";
-    src = pkgs.fetchurl {
-      url = "mirror://sourceforge/bacula/${old.pname}-${version}.tar.gz";
-      sha256 = "sha256-3w+FJezbo4DnS1N8pxrfO3WWWT8CGJtZqw6//IXMyN4=";
-    };
-  }));
-in
-{
-  sops.secrets = {
-    "bacula/password".owner = "bacula";
-    "bacula/keypair".owner = "bacula";
-    "bacula/masterkey".owner = "bacula";
-  };
-  networking.firewall.allowedTCPPorts = [ config.services.bacula-fd.port ];
-  networking.firewall.allowedUDPPorts = [ config.services.bacula-fd.port ];
-  services.bacula-fd = {
-    enable = true;
-    name = "ifsr-quitte";
-    extraClientConfig = ''
-      Maximum Concurrent Jobs = 20
-      FDAddress = 141.30.30.169
-      PKI Signatures = Yes
-      PKI Encryption = Yes
-      PKI Keypair = ${config.sops.secrets."bacula/keypair".path}
-      PKI Master Key = ${config.sops.secrets."bacula/masterkey".path}
-    '';
-    extraMessagesConfig = ''
-      director = abel-dir = all, !skipped, !restored
-      mailcommand = "${bacula_package}/bin/bsmtp -f \"Bacula <bacula@${config.networking.domain}>\" -s \"Bacula report" %r"
-      mail = root+backup = all, !skipped
-    '';
-    director."abel-dir".password = "@${config.sops.secrets."bacula/password".path}";
-  };
-  environment.etc."bacula/bconsole.conf".text = ''
-    Director {
-      Name = abel-dir
-      DIRport = 9101
-      address = 10.144.0.11
-      Password = @${config.sops.secrets."bacula/password".path}
-    }
-    Console {
-      Name = ifsr-quitte-console
-      Password = @${config.sops.secrets."bacula/password".path}
-    }
-  '';
-  systemd.services.bacula-fd.serviceConfig.ExecStart = lib.mkForce "${bacula_package}/sbin/bacula-fd -f -u root -g bacula -c ${fd_conf}";
-}
diff --git a/modules/core/bacula.nix b/modules/core/bacula.nix
new file mode 100644
index 0000000..c28a7d2
--- /dev/null
+++ b/modules/core/bacula.nix
@@ -0,0 +1,47 @@
+{ pkgs, config, ... }:
+{
+  sops.secrets = {
+    "bacula/password".owner = "bacula";
+    "bacula/keypair".owner = "bacula";
+    "bacula/masterkey".owner = "bacula";
+  };
+  networking.firewall = {
+    extraInputRules = ''
+      ip saddr 10.144.0.11 tcp dport ${builtins.toString config.services.bacula-fd.port} accept comment "Only allow Bacula access from Abel"
+    '';
+  };
+  services.bacula-fd = {
+    enable = true;
+    name = "ifsr-quitte";
+    extraClientConfig = ''
+      Comm Compression = no
+      Maximum Concurrent Jobs = 20
+      FDAddress = 141.30.30.194
+      PKI Signatures = Yes
+      PKI Encryption = Yes
+      PKI Keypair = ${config.sops.secrets."bacula/keypair".path}
+      PKI Master Key = ${config.sops.secrets."bacula/masterkey".path}
+    '';
+    extraMessagesConfig = ''
+      director = abel-dir = all, !skipped, !restored
+      mailcommand = "${pkgs.bacula}/bin/bsmtp -f \"Bacula <bacula@${config.networking.domain}>\" -s \"Bacula report" %r"
+      mail = root+backup = all, !skipped
+    '';
+    director."abel-dir" = {
+      password = "@${config.sops.secrets."bacula/password".path}";
+      tls.enable = false;
+    };
+  };
+  environment.etc."bacula/bconsole.conf".text = ''
+    Director {
+      Name = abel-dir
+      DIRport = 9101
+      address = 10.144.0.11
+      Password = @${config.sops.secrets."bacula/password".path}
+    }
+    Console {
+      Name = ifsr-quitte-console
+      Password = @${config.sops.secrets."bacula/password".path}
+    }
+  '';
+}
diff --git a/modules/base.nix b/modules/core/base.nix
similarity index 72%
rename from modules/base.nix
rename to modules/core/base.nix
index a19083d..906aa65 100755
--- a/modules/base.nix
+++ b/modules/core/base.nix
@@ -1,6 +1,5 @@
 { pkgs, config, ... }: {
   nix = {
-    package = pkgs.nixUnstable; # or versioned attributes like nix_2_4
     extraOptions = ''
       experimental-features = nix-command flakes
     '';
@@ -11,10 +10,17 @@
       echo System package diff:
       ${config.nix.package}/bin/nix store diff-closures /run/current-system $systemConfig || true
     fi
+
+    NO_FORMAT="\033[0m"
+    F_BOLD="\033[1m"
+    C_RED="\033[38;5;9m"
+    ${pkgs.diffutils}/bin/cmp --silent \
+      <(readlink /run/current-system/{kernel,kernel-modules}) \
+      <(readlink $systemConfig/{kernel,kernel-modules}) \
+      || echo -e "''${F_BOLD}''${C_RED}Kernel version changed, reboot is advised.''${NO_FORMAT}"
   '';
 
   # Select internationalisation properties.
-  i18n.defaultLocale = "en_US.UTF-8";
   console = {
     #font = "Lat2-Terminus16";
     font = "${pkgs.terminus_font}/share/consolefonts/ter-u28n.psf.gz";
@@ -22,7 +28,17 @@
   };
 
   # Enable the OpenSSH daemon.
-  services.openssh.enable = true;
+  services.openssh = {
+    enable = true;
+    settings = {
+      PermitRootLogin = "yes";
+      PasswordAuthentication = false;
+    };
+  };
+  programs.mosh.enable = true;
+
+  # vs code server
+  services.vscode-server.enable = true;
 
   # set root ssh keys
   users.users.root.openssh.authorizedKeys = {
@@ -40,32 +56,33 @@
       # "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEXMHwy4AZ9B4pMRBa/P/rb7N3SCas9e7Lp89plTHdFS halcyon@eisvogel.moe"
       # "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAJ7qUGZUjiDhQ6Se+aXr9DbgRTG2tx69owqVMkd2bna simon@mayushii"
       "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBLlITzcTVnSi8EpEW3leSuqYCDhbnJyoGCjFOtIJ0Dl5uRNm0UNXS7AbQtLLylEeI1+/qinQDEWAJ6cBDAaPfNw= rouven@thinkpad"
-
+      "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINJgYI2rXmw4uPXAMmOgqgJEwYfwj/IBExTCzs9Dgo+R w0lff"
     ];
     keyFiles = [
-      ../keys/ssh/marcus-sapphire
-      ../keys/ssh/schrader
-      ../keys/ssh/jannusch
-      ../keys/ssh/jannusch-arch
-      ../keys/ssh/tassilo
-      ../keys/ssh/jonasga
-      ../keys/ssh/rouven
-      ../keys/ssh/joachim
+      ../../keys/ssh/marcus-sapphire
+      ../../keys/ssh/schrader
+      ../../keys/ssh/jannusch
+      ../../keys/ssh/jannusch-arch
+      ../../keys/ssh/tassilo
+      ../../keys/ssh/rouven
+      ../../keys/ssh/joachim
     ];
   };
 
   time.timeZone = "Europe/Berlin";
 
   # basic shell & editor
+  programs.vim.enable = true;
   programs.vim.defaultEditor = true;
 
   # List packages installed in system profile. To search, run:
   # $ nix search wget
   environment.systemPackages = with pkgs; [
     atop
+    btop
     bat
     git
-    htop
+    htop-vim
     fd
     ripgrep
     tldr
@@ -73,6 +90,7 @@
     usbutils
     wget
     neovim
+    helix
     nmap
     tcpdump
     bat
@@ -86,13 +104,17 @@
     ltrace
     strace
     mtr
+    nix-output-monitor
     traceroute
     smartmontools
     sysstat
     tree
     whois
-    exa
+    eza
     zsh
+    unzip
+    yazi
+    imagemagick
   ];
 }
 
diff --git a/modules/core/default.nix b/modules/core/default.nix
new file mode 100755
index 0000000..de763c0
--- /dev/null
+++ b/modules/core/default.nix
@@ -0,0 +1,15 @@
+{ ... }: {
+  imports = [
+    ./base.nix
+    ./logging.nix
+    ./bacula.nix
+    ./fail2ban.nix
+    ./initrd-ssh.nix
+    ./mysql.nix
+    ./nginx.nix
+    ./podman.nix
+    ./postgres.nix
+    ./sssd.nix
+    ./zsh.nix
+  ];
+}
diff --git a/modules/core/fail2ban.nix b/modules/core/fail2ban.nix
new file mode 100644
index 0000000..5c08578
--- /dev/null
+++ b/modules/core/fail2ban.nix
@@ -0,0 +1,28 @@
+{ ... }:
+{
+  services.fail2ban = {
+    enable = true;
+    ignoreIP = [
+      "141.30.0.0/16"
+      "141.76.0.0/16"
+    ];
+    bantime-increment = {
+      enable = true;
+    };
+
+    jails = {
+      dovecot = ''
+        enabled = true
+        # aggressive mode to add blocking for aborted connections
+        filter = dovecot[mode=aggressive]
+        maxretry = 15
+      '';
+      postfix = ''
+        enabled = true
+        filter = postfix[mode=aggressive]
+        maxretry = 15
+      '';
+      sshd.settings.maxretry = 15;
+    };
+  };
+}
diff --git a/modules/core/initrd-ssh.nix b/modules/core/initrd-ssh.nix
new file mode 100644
index 0000000..a244b21
--- /dev/null
+++ b/modules/core/initrd-ssh.nix
@@ -0,0 +1,29 @@
+# Find the required kernel module for the network adapter using `lspci -v` and add it to `boot.initrd.availableKernelModules`.
+# Enable `networking.useDHCP` or set a static ip using the `ip=` kernel parameter.
+# Generate another SSH host key for the machine:
+# $ ssh-keygen -t ed25519 -N "" -f /etc/ssh/ssh_host_ed25519_key_initrd -C HOSTNAME-initrd
+# Add the public key to your known_hosts and create an ssh config entry.
+{ config, ... }:
+{
+  boot.initrd = {
+    availableKernelModules = [ "mlx5_core" ];
+    systemd = {
+      enable = true;
+      network = {
+        enable = true;
+        networks."10-wired-default" = config.systemd.network.networks."10-wired-default";
+      };
+      users.root.shell = "/bin/systemd-tty-ask-password-agent";
+    };
+    network = {
+      enable = true;
+      ssh = {
+        enable = true;
+        port = 222;
+        hostKeys = [ "/etc/ssh/ssh_host_ed25519_key_initrd" ];
+        # authorizedKeys option inherits root's authorizedKeys.keys, but not keyFiles
+      };
+    };
+  };
+}
+
diff --git a/modules/core/logging.nix b/modules/core/logging.nix
new file mode 100644
index 0000000..75f482f
--- /dev/null
+++ b/modules/core/logging.nix
@@ -0,0 +1,35 @@
+{ pkgs, ... }:
+{
+  services.rsyslogd = {
+    enable = true;
+    defaultConfig = ''
+      $FileCreateMode 0640
+      :programname, isequal, "postfix" /var/log/postfix.log
+      :programname, isequal, "portunus" /var/log/portunus.log
+
+      auth.*                          -/var/log/auth.log
+    '';
+  };
+  services.logrotate.configFile = pkgs.writeText "logrotate.conf" ''
+    weekly
+    missingok
+    notifempty
+    rotate 4
+    "/var/log/postfix.log" {
+      compress
+      delaycompress
+      weekly
+      rotate 156
+    }
+    "/var/log/nginx/*.log" {
+      compress
+      delaycompress
+      weekly
+      postrotate
+        [ ! -f /var/run/nginx/nginx.pid ] || kill -USR1 `cat /var/run/nginx/nginx.pid`
+      endscript
+      rotate 26
+      su nginx nginx
+    }
+  '';
+}
diff --git a/modules/core/mysql.nix b/modules/core/mysql.nix
new file mode 100644
index 0000000..f35b278
--- /dev/null
+++ b/modules/core/mysql.nix
@@ -0,0 +1,19 @@
+{ pkgs, ... }:
+{
+  services.mysql = {
+    enable = true;
+    package = pkgs.mariadb;
+    settings.mysqld.bind_address = "127.0.0.1";
+  };
+  services.mysqlBackup = {
+    enable = true;
+    user = "mysql";
+    location = "/var/lib/backup/mysql";
+    databases = [
+      "fsrewsp"
+      "nightline"
+      "wiki_ese"
+      "wiki_vernetzung"
+    ];
+  };
+}
diff --git a/modules/core/nginx.nix b/modules/core/nginx.nix
new file mode 100644
index 0000000..36e596e
--- /dev/null
+++ b/modules/core/nginx.nix
@@ -0,0 +1,64 @@
+{ lib, config, pkgs, ... }:
+{
+  # set default options for virtualHosts
+  options = with lib; {
+    services.nginx.virtualHosts = mkOption {
+      type = types.attrsOf (types.submodule
+        ({ name, ... }: {
+          enableACME = true;
+          forceSSL = true;
+          # enable http3 for all hosts
+          quic = true;
+          http3 = true;
+          # split up nginx access logs per vhost
+          extraConfig = ''
+            access_log /var/log/nginx/${name}_access.log;
+            error_log /var/log/nginx/${name}_error.log;
+            add_header Alt-Svc 'h3=":443"; ma=86400';
+          '';
+        })
+      );
+    };
+  };
+
+  config = {
+    networking.firewall.allowedTCPPorts = [ 80 443 ];
+    networking.firewall.allowedUDPPorts = [ 443 ];
+    services.nginx = {
+      enable = true;
+      package = pkgs.nginxQuic;
+      additionalModules = [ pkgs.nginxModules.pam ];
+      recommendedProxySettings = true;
+      recommendedGzipSettings = true;
+      recommendedOptimisation = true;
+      recommendedTlsSettings = true;
+
+      # appendHttpConfig = ''
+      #   map $remote_addr $remote_addr_anon {
+      #            ~(?P<ip>\d+\.\d+\.\d+)\.    $ip.0;
+      #            ~(?P<ip>[^:]+:[^:]+):       $ip::;
+      #            # IP addresses to not anonymize
+      #            127.0.0.1                   $remote_addr;
+      #            ::1                         $remote_addr;
+      #            default                     0.0.0.0;
+      #   }
+      #   log_format  anon_ip   '$remote_addr_anon - $remote_user [$time_local] "$request" '
+      #                         '$status $body_bytes_sent "$http_referer" '
+      #                         '"$http_user_agent" "$http_x_forwarded_for"';
+
+      #   access_log  /var/log/nginx/access.log  anon_ip;
+      # '';
+    };
+    security.acme = {
+      acceptTerms = true;
+      defaults = {
+        #server = "https://acme-staging-v02.api.letsencrypt.org/directory";
+        email = "root@${config.networking.domain}";
+      };
+    };
+    security.pam.services.nginx.text = ''
+      auth required ${pkgs.nss_pam_ldapd}/lib/security/pam_ldap.so
+      account required ${pkgs.nss_pam_ldapd}/lib/security/pam_ldap.so
+    '';
+  };
+}
diff --git a/modules/core/podman.nix b/modules/core/podman.nix
new file mode 100644
index 0000000..9927a43
--- /dev/null
+++ b/modules/core/podman.nix
@@ -0,0 +1,26 @@
+{ pkgs, ... }:
+{
+  # From: https://nixos.wiki/wiki/Podman
+  virtualisation.containers.enable = true;
+  virtualisation = {
+    podman = {
+      enable = true;
+
+      # Create a `docker` alias for podman, to use it as a drop-in replacement
+      dockerCompat = true;
+
+      # Required for containers under podman-compose to be able to talk to each other.
+      defaultNetwork.settings.dns_enabled = true;
+    };
+  };
+  virtualisation.oci-containers.backend = "podman";
+
+
+  # Useful otherdevelopment tools
+  environment.systemPackages = with pkgs; [
+    dive # look into docker image layers
+    podman-tui # status of containers in the terminal
+    #docker-compose # start group of containers for dev
+    #podman-compose # start group of containers for dev
+  ];
+}
diff --git a/modules/postgres.nix b/modules/core/postgres.nix
similarity index 73%
rename from modules/postgres.nix
rename to modules/core/postgres.nix
index 331c24a..daf44ff 100644
--- a/modules/postgres.nix
+++ b/modules/core/postgres.nix
@@ -6,8 +6,10 @@
     location = "/var/lib/backup/postgresql";
     databases = [
       "course-management"
-      "gitea"
+      "git"
+      "grafana"
       "hedgedoc"
+      "keycloak"
       "matrix-synapse"
       "mautrix-telegram"
       "mediawiki"
@@ -16,7 +18,10 @@
       "sogo"
       "vaultwarden"
       "mailman"
-      "mailmanweb"
+      "mailman-web"
+      "zammad"
     ];
   };
+
+  services.postgresql.settings.max_connections = 1000;
 }
diff --git a/modules/core/sssd.nix b/modules/core/sssd.nix
new file mode 100644
index 0000000..a23a0bb
--- /dev/null
+++ b/modules/core/sssd.nix
@@ -0,0 +1,41 @@
+{ config, ... }:
+{
+  sops.secrets = {
+    "sssd/env" = { };
+
+  };
+  services.sssd = {
+    enable = true;
+    environmentFile = config.sops.secrets."sssd/env".path;
+    sshAuthorizedKeysIntegration = true;
+    config = ''
+      [sssd]
+      config_file_version = 2
+      services = nss, pam, ssh
+      domains = ldap
+
+      [ssh]
+
+      [nss]
+
+      [pam]
+
+      [domain/ldap]
+      auth_provider = ldap
+      ldap_uri = ldaps://auth.ifsr.de
+      ldap_default_authtok_type = password
+      ldap_default_authtok = $SSSD_LDAP_DEFAULT_AUTHTOK
+      ldap_search_base = dc=ifsr,dc=de
+      id_provider = ldap
+      ldap_default_bind_dn = uid=search,ou=users,dc=ifsr,dc=de
+      cache_credentials = True
+      ldap_tls_cacert = /etc/ssl/certs/ca-bundle.crt
+      ldap_tls_reqcert = hard
+    '';
+
+  };
+  security.pam.services = {
+    sshd.makeHomeDir = true;
+    login.makeHomeDir = true;
+  };
+}
diff --git a/modules/core/zsh.nix b/modules/core/zsh.nix
new file mode 100644
index 0000000..349f3dd
--- /dev/null
+++ b/modules/core/zsh.nix
@@ -0,0 +1,35 @@
+{ lib, pkgs, ... }:
+{
+  users.users.root.shell = pkgs.zsh;
+  programs.command-not-found.enable = false;
+  programs.nix-index-database.comma.enable = true;
+  environment.systemPackages = with pkgs; [
+    # fzf
+    bat
+    duf
+  ];
+  programs.fzf = {
+    keybindings = true;
+  };
+  programs.zsh = {
+    enable = true;
+    autosuggestions = {
+      enable = true;
+      highlightStyle = "fg=#00bbbb,bold";
+    };
+
+    # don't override agdsn-zsh-config aliases
+    shellAliases = lib.mkForce { };
+
+    shellInit = ''
+      zsh-newuser-install () {}
+    '';
+    interactiveShellInit = ''
+      source ${pkgs.zsh-fzf-tab}/share/fzf-tab/fzf-tab.plugin.zsh
+      HW_CONF_ALIASES_GIT_AUTHOR_REMINDER=0
+      source ${pkgs.agdsn-zsh-config}/etc/zsh/zshrc
+    '';
+    promptInit = "";
+  };
+}
+
diff --git a/modules/course-management.nix b/modules/courses/default.nix
similarity index 66%
rename from modules/course-management.nix
rename to modules/courses/default.nix
index 88cbc3b..9b971fd 100644
--- a/modules/course-management.nix
+++ b/modules/courses/default.nix
@@ -38,15 +38,28 @@ in
     enable = lib.mkForce true; # upstream bacula config wants to disable it, so we need to force
     ensureUsers = [{
       name = "course-management";
-      ensurePermissions = {
-        "DATABASE \"course-management\"" = "ALL PRIVILEGES";
-      };
+      ensureDBOwnership = true;
     }];
     ensureDatabases = [ "course-management" ];
   };
 
   services.nginx.virtualHosts.${hostName} = {
-    enableACME = true;
-    forceSSL = true;
+    # phil redirects
+    locations =
+      let
+        philDomain = "https://kurse-phil.ifsr.de";
+        courses = [ "238" "239" "240" "241" "242" "243" ];
+        subjects = [
+          "ESE 2023 PHIL Campustour"
+          "ESE 2023 PHIL Bowlingabend"
+          "ESE 2023 PHIL Filmabend"
+          "ESE 2023 PHIL Wandern"
+          "ESE 2023 PHIL Spieleabend Pen and Paper"
+        ];
+      in
+      {
+        "~ \"^/course/(${builtins.concatStringsSep "|" courses})/\"".return = "301 ${philDomain}/course/$1";
+        "~ \"^/subject/(${builtins.concatStringsSep "|" subjects})/\"".return = "301 ${philDomain}/subject/$1";
+      };
   };
 }
diff --git a/modules/courses/phil.nix b/modules/courses/phil.nix
new file mode 100644
index 0000000..7bd1b69
--- /dev/null
+++ b/modules/courses/phil.nix
@@ -0,0 +1,93 @@
+{ config, lib, course-management, ... }:
+let
+  hostName = "kurse-phil.${config.networking.domain}";
+in
+{
+  services.nginx.virtualHosts."${hostName}" = {
+    locations."/".proxyPass = "http://127.0.0.1:8084";
+    enableACME = true;
+    forceSSL = true;
+  };
+
+  sops.secrets = {
+    "course-management-phil/secret-key" = { };
+    "course-management-phil/adminpass" = { };
+  };
+  containers."courses-phil" = {
+    autoStart = true;
+    extraFlags = [
+      "--load-credential=course-secret-key:${config.sops.secrets."course-management-phil/secret-key".path}"
+      "--load-credential=course-adminpass:${config.sops.secrets."course-management-phil/adminpass".path}"
+    ];
+    config = { config, ... }: {
+      system.stateVersion = "23.05";
+      networking.domain = "ifsr.de";
+      imports = [
+        course-management.nixosModules.default
+      ];
+      systemd.services.course-management = {
+        after = [ "postgresql.service" ];
+        serviceConfig = {
+          LoadCredential = [
+            "secret-key:course-secret-key"
+            "adminpass:course-adminpass"
+          ];
+        };
+      };
+      services.course-management = {
+        inherit hostName;
+        enable = true;
+        listenPort = 5001;
+
+        settings = {
+          secretKeyFile = "$CREDENTIALS_DIRECTORY/secret-key";
+          adminPassFile = "$CREDENTIALS_DIRECTORY/adminpass";
+          admins = [{
+            name = "Root iFSR";
+            email = "root@${config.networking.domain}";
+          }];
+          database = {
+            ENGINE = "django.db.backends.postgresql";
+            NAME = "course-management";
+          };
+          email = lib.mkDefault {
+            fromEmail = "noreply@${config.networking.domain}";
+            serverEmail = "root@${config.networking.domain}";
+          };
+        };
+      };
+      security.acme = {
+        acceptTerms = true;
+        defaults = {
+          email = "root@${config.networking.domain}";
+        };
+      };
+      services.postgresql = {
+        enable = true;
+        enableTCPIP = lib.mkForce false;
+        ensureUsers = [{
+          name = "course-management";
+          ensureDBOwnership = true;
+        }];
+        ensureDatabases = [ "course-management" ];
+      };
+      systemd.services.postgresql.serviceConfig.ExecStart = lib.mkForce "${config.services.postgresql.package}/bin/postgres -c listen_addresses=''";
+      services.nginx = {
+        enable = true;
+        recommendedProxySettings = true;
+        recommendedGzipSettings = true;
+        recommendedOptimisation = true;
+        recommendedTlsSettings = true;
+
+
+        virtualHosts.${hostName} = {
+          listen = [{
+            addr = "127.0.0.1";
+            port = 8084;
+          }];
+        };
+      };
+
+    };
+  };
+}
diff --git a/modules/decisions.nix b/modules/decisions.nix
new file mode 100644
index 0000000..c19085d
--- /dev/null
+++ b/modules/decisions.nix
@@ -0,0 +1,46 @@
+{ config, ... }:
+let
+  domain = "decisions.${config.networking.domain}";
+in
+{
+  sops.secrets."decisions_env" = { };
+  virtualisation.oci-containers = {
+    containers.decisions = {
+      image = "ghcr.io/fsr/decisions";
+      volumes = [
+        "/var/lib/nextcloud/data/root/files/FSR/protokolle:/protokolle:ro"
+      ];
+      extraOptions = [ "--network=host" ];
+      environmentFiles = [
+        config.sops.secrets."decisions_env".path
+      ];
+    };
+  };
+
+  services.nginx = {
+    virtualHosts."${domain}" = {
+      locations."/" = {
+        proxyPass = "http://127.0.0.1:5055";
+      };
+    };
+  };
+
+  systemd.timers."decisions-to-db" = {
+    wantedBy = [ "timers.target" ];
+    timerConfig = {
+      OnCalendar = "01:11:00";
+      Unit = "decisions-to-db.service";
+    };
+  };
+
+  # systemd.services."decisions-to-db" = {
+  #   script = ''
+  #     set -eu
+  #     ${pkgs.podman}/bin/podman exec decisions python tex_to_db.py
+  #   '';
+  #   serviceConfig = {
+  #     Type = "oneshot";
+  #     User = "root";
+  #   };
+  # };
+}
diff --git a/modules/forgejo/actions.nix b/modules/forgejo/actions.nix
new file mode 100644
index 0000000..16d6d24
--- /dev/null
+++ b/modules/forgejo/actions.nix
@@ -0,0 +1,30 @@
+{ config, pkgs, ... }:
+{
+  sops.secrets."forgejo/runner-token" = { };
+  services.gitea-actions-runner = {
+    package = pkgs.forgejo-actions-runner;
+    instances."quitte" = {
+      enable = true;
+      labels = [
+        # provide a debian base with nodejs for actions
+        "debian-latest:docker://node:18-bullseye"
+        # fake the ubuntu name, because node provides no ubuntu builds
+        "ubuntu-latest:docker://node:18-bullseye"
+        # provide native execution on the host
+        # "native:host"
+      ];
+      tokenFile = config.sops.secrets."forgejo/runner-token".path;
+      url = "https://git.ifsr.de";
+      name = "quitte";
+      settings = {
+        container = {
+          # use podman's default network, otherwise dns was not working for some reason
+          network = "podman";
+          # don't mount the docker socket into the build containers,
+          # this would basically mean root on the host...
+          docker_host = "-";
+        };
+      };
+    };
+  };
+}
diff --git a/modules/gitea.nix b/modules/forgejo/default.nix
similarity index 78%
rename from modules/gitea.nix
rename to modules/forgejo/default.nix
index 9924f61..aee832d 100644
--- a/modules/gitea.nix
+++ b/modules/forgejo/default.nix
@@ -1,40 +1,46 @@
 { config, lib, pkgs, ... }:
 let
   domain = "git.${config.networking.domain}";
-  giteaUser = "git";
+  gitUser = "git";
 in
 {
+  imports = [
+    ./actions.nix
+  ];
   sops.secrets.gitea_ldap_search = {
     key = "portunus/search-password";
-    owner = config.services.gitea.user;
+    owner = config.services.forgejo.user;
   };
 
-  users.users.${giteaUser} = {
+  users.users.${gitUser} = {
     isSystemUser = true;
-    home = config.services.gitea.stateDir;
-    group = giteaUser;
+    home = config.services.forgejo.stateDir;
+    group = gitUser;
     useDefaultShell = true;
   };
-  users.groups.${giteaUser} = { };
+  users.groups.${gitUser} = { };
 
-  services.gitea = {
+  services.forgejo = {
     enable = true;
-    package = pkgs.forgejo; # community fork
-    user = giteaUser;
-    group = giteaUser;
-    appName = "iFSR Git";
+    user = gitUser;
+    group = gitUser;
+    package = pkgs.forgejo;
     lfs.enable = true;
 
     database = {
       type = "postgres";
+      name = "git"; # legacy
       createDatabase = true;
-      user = giteaUser;
+      user = gitUser;
     };
 
     # TODO: enable periodic dumps of the DB and repos, maybe use this for backups?
     # dump = { };
 
     settings = {
+      DEFAULT = {
+        APP_NAME = "iFSR Git";
+      };
       server = {
         PROTOCOL = "http+unix";
         DOMAIN = domain;
@@ -42,6 +48,7 @@ in
         ROOT_URL = "https://${domain}";
         OFFLINE_MODE = true; # disable use of CDNs
       };
+      log.LEVEL = "Warn";
       database.LOG_SQL = false;
       service = {
         DISABLE_REGISTRATION = true;
@@ -63,12 +70,15 @@ in
         COOKIE_SECURE = true;
         PROVIDER = "db";
       };
+      actions.ENABLED = true;
+      # federation.ENABLED = true;
+      webhook.ALLOWED_HOST_LIST = "*.ifsr.de";
     };
   };
 
-  systemd.services.gitea.preStart =
+  systemd.services.forgejo.preStart =
     let
-      exe = lib.getExe config.services.gitea.package;
+      exe = lib.getExe config.services.forgejo.package;
       portunus = config.services.portunus;
       basedn = "ou=users,${portunus.ldap.suffix}";
       ldapConfigArgs = ''
@@ -105,10 +115,8 @@ in
     '';
 
   services.nginx.virtualHosts.${domain} = {
-    enableACME = true;
-    forceSSL = true;
     locations."/" = {
-      proxyPass = "http://unix:${config.services.gitea.settings.server.HTTP_ADDR}:/";
+      proxyPass = "http://unix:${config.services.forgejo.settings.server.HTTP_ADDR}:/";
       proxyWebsockets = true;
     };
     locations."/api/v1/users/search".return = "403";
diff --git a/modules/ftp.nix b/modules/ftp.nix
deleted file mode 100644
index 205ed7c..0000000
--- a/modules/ftp.nix
+++ /dev/null
@@ -1,23 +0,0 @@
-{ config, pkgs, ... }:
-let
-  domain = "ftp.${config.networking.domain}";
-in
-{
-  services.nginx.additionalModules = [ pkgs.nginxModules.fancyindex ];
-  services.nginx.virtualHosts."${domain}" = {
-    enableACME = true;
-    forceSSL = true;
-    root = "/srv/ftp";
-    extraConfig = ''
-      fancyindex on;
-      fancyindex_exact_size off;
-    '';
-    locations."~/(klausuren|uebungen|skripte|abschlussarbeiten)".extraConfig = ''
-      allow 141.30.0.0/16;
-      allow 141.76.0.0/16;
-      allow 172.16.0.0/16;
-      deny all;
-    '';
-
-  };
-}
diff --git a/modules/hedgedoc.nix b/modules/hedgedoc.nix
index bbe2c47..244734a 100644
--- a/modules/hedgedoc.nix
+++ b/modules/hedgedoc.nix
@@ -14,9 +14,7 @@ in
       ensureUsers = [
         {
           name = "hedgedoc";
-          ensurePermissions = {
-            "DATABASE hedgedoc" = "ALL PRIVILEGES";
-          };
+          ensureDBOwnership = true;
         }
       ];
       ensureDatabases = [ "hedgedoc" ];
@@ -51,6 +49,7 @@ in
         # allow anonymous editing, but not creation of pads
         allowAnonymous = false;
         allowAnonymousEdits = true;
+        allowAnonymousUploads = false;
         defaultPermission = "limited";
         defaultNotePath = builtins.toString template;
         # ldap auth
@@ -70,12 +69,16 @@ in
       recommendedProxySettings = true;
       virtualHosts = {
         "${domain}" = {
-          enableACME = true;
-          forceSSL = true;
           locations."/" = {
             proxyPass = "http://[::1]:${toString config.services.hedgedoc.settings.port}";
             proxyWebsockets = true;
           };
+          locations."/robots.txt" = {
+            extraConfig = ''
+              add_header  Content-Type  text/plain;
+              return 200 "User-agent: *\nDisallow: /\n";
+            '';
+          };
         };
       };
     };
diff --git a/modules/hydra.nix b/modules/hydra.nix
index 8a252d5..77c347e 100644
--- a/modules/hydra.nix
+++ b/modules/hydra.nix
@@ -4,6 +4,7 @@ let
 in
 {
   sops.secrets."hydra_ldap_search" = { owner = "hydra"; group = "hydra"; mode = "440"; };
+  nix.settings.allowed-uris = [ "https://github.com/nix-community" ]; # whitelisted to fetch nix-index
   services.hydra = {
     enable = true;
     port = 4000;
@@ -59,8 +60,6 @@ in
 
   };
   services.nginx.virtualHosts."${domain}" = {
-    enableACME = true;
-    forceSSL = true;
     locations."/" = {
       proxyPass = "http://127.0.0.1:${toString config.services.hydra.port}";
     };
diff --git a/modules/kanboard.nix b/modules/kanboard.nix
new file mode 100644
index 0000000..2416ed8
--- /dev/null
+++ b/modules/kanboard.nix
@@ -0,0 +1,34 @@
+{ config, ... }:
+let
+  domain = "kanboard.${config.networking.domain}";
+  domain_short = "kb.${config.networking.domain}";
+in
+{
+  sops.secrets."kanboard_env" = { };
+
+  virtualisation.oci-containers = {
+    containers.kanboard = {
+      image = "ghcr.io/kanboard/kanboard:v1.2.43";
+      volumes = [
+        "kanboard_data:/var/www/app/data"
+        "kanboard_plugins:/var/www/app/plugins"
+      ];
+      ports = [ "127.0.0.1:8045:80" ];
+      environmentFiles = [
+        config.sops.secrets."kanboard_env".path
+      ];
+    };
+  };
+
+  services.nginx = {
+    virtualHosts."${domain_short}" = {
+      locations."/".return = "301 $scheme://${domain}$request_uri";
+    };
+
+    virtualHosts."${domain}" = {
+      locations."/" = {
+        proxyPass = "http://127.0.0.1:8045";
+      };
+    };
+  };
+}
diff --git a/modules/keycloak/default.nix b/modules/keycloak/default.nix
new file mode 100644
index 0000000..6aa1afb
--- /dev/null
+++ b/modules/keycloak/default.nix
@@ -0,0 +1,39 @@
+{ config, pkgs, ... }:
+let
+  domain = "sso.${config.networking.domain}";
+in
+{
+  sops.secrets."keycloak/db" = { };
+  services.keycloak = {
+    enable = true;
+    # we use unstable as the release in stable is insecure
+    # package = nixpkgs-unstable.legacyPackages.x86_64-linux.keycloak;
+    settings = {
+      http-port = 8086;
+      https-port = 19000;
+      hostname = domain;
+      proxy-headers = "xforwarded";
+      http-enabled = true;
+      hostname-strict-https = false;
+    };
+    # The module requires a password for the DB and works best with its own DB config
+    # Does an automatic Postgresql configuration
+    database = {
+      passwordFile = config.sops.secrets."keycloak/db".path;
+    };
+    initialAdminPassword = "plschangeme";
+    themes = with pkgs ; {
+      ifsr = keycloak_ifsr_theme;
+    };
+  };
+  services.nginx.virtualHosts."${domain}" = {
+    locations."/" = {
+      proxyPass = "http://127.0.0.1:${toString config.services.keycloak.settings.http-port}";
+      extraConfig = ''
+        proxy_buffer_size 128k;
+        proxy_buffers 4 256k;
+        proxy_busy_buffers_size 256k;
+      '';
+    };
+  };
+}
diff --git a/modules/keycloak/theme.nix b/modules/keycloak/theme.nix
new file mode 100644
index 0000000..0500e47
--- /dev/null
+++ b/modules/keycloak/theme.nix
@@ -0,0 +1,15 @@
+{ stdenv }:
+stdenv.mkDerivation rec {
+  name = "keycloak_ifsr_theme";
+  version = "1.1";
+
+  src = ./theme;
+
+  nativeBuildInputs = [ ];
+  buildInputs = [ ];
+
+  installPhase = ''
+    mkdir -p $out
+    cp -a login $out
+  '';
+}
diff --git a/modules/keycloak/theme/login/resources/css/login.css b/modules/keycloak/theme/login/resources/css/login.css
new file mode 100644
index 0000000..6314ff8
--- /dev/null
+++ b/modules/keycloak/theme/login/resources/css/login.css
@@ -0,0 +1,772 @@
+.login-pf {
+    background: none;
+}
+
+.login-pf body {
+    background: url(../img/background.jpg) no-repeat center center fixed;
+    background-size: cover;
+    height: 100%;
+}
+
+/*IE compatibility*/
+.pf-c-form-control {
+    font-size: 14px;
+    font-size: var(--pf-global--FontSize--sm);
+    border-width: 1px;
+    border-width: var(--pf-global--BorderWidth--sm);;
+    border-color: #EDEDED #EDEDED #8A8D90 #EDEDED;
+    border-color: var(--pf-global--BorderColor--300) var(--pf-global--BorderColor--300) var(--pf-global--BorderColor--200) var(--pf-global--BorderColor--300);
+    background-color: #FFFFFF;
+    background-color: var(--pf-global--BackgroundColor--100);
+    height: 36px;
+    height: calc(var(--pf-c-form-control--FontSize) * var(--pf-c-form-control--LineHeight) + var(--pf-c-form-control--BorderWidth) * 2 + var(--pf-c-form-control--PaddingTop) + var(--pf-c-form-control--PaddingBottom));
+    padding: 5px 0.5rem;
+    padding: var(--pf-c-form-control--PaddingTop) var(--pf-c-form-control--PaddingRight) var(--pf-c-form-control--PaddingBottom) var(--pf-c-form-control--PaddingLeft);
+}
+
+textarea.pf-c-form-control {
+	height: auto;
+}
+
+.pf-c-form-control:hover, .pf-c-form-control:focus {
+    border-bottom-color: #0066CC;
+    border-bottom-color: var(--pf-global--primary-color--100);
+    border-bottom-width: 2px;
+    border-bottom-width: var(--pf-global--BorderWidth--md);
+}
+
+.pf-c-form-control[aria-invalid=true] {
+    border-bottom-color: #C9190B;
+    border-bottom-color: var(--pf-global--danger-color--100);
+    border-bottom-width: 2px;
+    border-bottom-width: var(--pf-global--BorderWidth--md);
+}
+
+.pf-c-check__label, .pf-c-radio__label {
+	font-size: 14px;
+	font-size: var(--pf-global--FontSize--sm);
+}
+
+.pf-c-alert.pf-m-inline {
+    margin-bottom: 0.5rem; /* default - IE compatibility */
+    margin-bottom: var(--pf-global--spacer--sm);
+    padding: 0.25rem;
+    padding: var(--pf-global--spacer--xs);
+    border: solid #ededed;
+    border: solid var(--pf-global--BorderColor--300);
+    border-width: 1px;
+    border-width: var(--pf-c-alert--m-inline--BorderTopWidth) var(--pf-c-alert--m-inline--BorderRightWidth) var(--pf-c-alert--m-inline--BorderBottomWidth) var(--pf-c-alert--m-inline--BorderLeftWidth);
+    display: -ms-flexbox;
+    display: grid;
+    -ms-grid-columns: max-content 1fr max-content;
+    grid-template-columns:max-content 1fr max-content;
+    grid-template-columns: var(--pf-c-alert--grid-template-columns);
+    grid-template-rows: 1fr auto;
+    grid-template-rows: var(--pf-c-alert--grid-template-rows);
+}
+
+.pf-c-alert.pf-m-inline::before {
+    position: absolute;
+    top: -1px;
+    top: var(--pf-c-alert--m-inline--before--Top);
+    bottom: -1px;
+    bottom: var(--pf-c-alert--m-inline--before--Bottom);
+    left: 0;
+    width: 3px;
+    width: var(--pf-c-alert--m-inline--before--Width);
+    content: ;
+    background-color: #FFFFFF;
+    background-color: var(--pf-global--BackgroundColor--100);
+}
+
+.pf-c-alert.pf-m-inline.pf-m-success::before {
+    background-color: #92D400;
+    background-color: var(--pf-global--success-color--100);
+}
+
+.pf-c-alert.pf-m-inline.pf-m-danger::before {
+    background-color: #C9190B;
+    background-color: var(--pf-global--danger-color--100);
+}
+
+.pf-c-alert.pf-m-inline.pf-m-warning::before {
+    background-color: #F0AB00;
+    background-color: var(--pf-global--warning-color--100);
+}
+
+.pf-c-alert.pf-m-inline .pf-c-alert__icon {
+    padding: 1rem 0.5rem 1rem 1rem;
+    padding: var(--pf-c-alert--m-inline__icon--PaddingTop) var(--pf-c-alert--m-inline__icon--PaddingRight) var(--pf-c-alert--m-inline__icon--PaddingBottom) var(--pf-c-alert--m-inline__icon--PaddingLeft);
+    font-size: 16px;
+    font-size: var(--pf-c-alert--m-inline__icon--FontSize);
+}
+
+.pf-c-alert.pf-m-success .pf-c-alert__icon {
+    color: #92D400;
+    color: var(--pf-global--success-color--100);
+}
+
+.pf-c-alert.pf-m-success .pf-c-alert__title {
+    color: #486B00;
+    color: var(--pf-global--success-color--200);
+}
+
+.pf-c-alert.pf-m-danger .pf-c-alert__icon {
+    color: #C9190B;
+    color: var(--pf-global--danger-color--100);
+}
+
+.pf-c-alert.pf-m-danger .pf-c-alert__title {
+    color: #A30000;
+    color: var(--pf-global--danger-color--200);
+}
+
+.pf-c-alert.pf-m-warning .pf-c-alert__icon {
+    color: #F0AB00;
+    color: var(--pf-global--warning-color--100);
+}
+
+.pf-c-alert.pf-m-warning .pf-c-alert__title {
+    color: #795600;
+    color: var(--pf-global--warning-color--200);
+}
+
+.pf-c-alert__title {
+    font-size: 14px; /* default - IE compatibility */
+    font-size: var(--pf-global--FontSize--sm);
+    padding: 5px 8px;
+    padding: var(--pf-c-alert__title--PaddingTop) var(--pf-c-alert__title--PaddingRight) var(--pf-c-alert__title--PaddingBottom) var(--pf-c-alert__title--PaddingLeft);
+}
+
+.pf-c-button{
+    padding:0.375rem 1rem;
+    padding: var(--pf-global--spacer--form-element) var(--pf-global--spacer--md);
+}
+
+/* default - IE compatibility */
+.pf-m-primary {
+    color: #FFFFFF;
+    background-color: #0066CC;
+    background-color: var(--pf-global--primary-color--100);
+}
+
+/* default - IE compatibility */
+.pf-m-primary:hover {
+    background-color: #004080;
+    background-color: var(--pf-global--primary-color--200);
+}
+
+/* default - IE compatibility */
+.pf-c-button.pf-m-control {
+    border: solid 1px;
+    border: solid var(--pf-global--BorderWidth--sm);
+    border-color: rgba(230, 230, 230, 0.5);
+}
+/*End of IE compatibility*/
+h1#kc-page-title {
+    margin-top: 10px;
+}
+
+#kc-locale ul {
+    background-color: #FFF;
+    background-color: var(--pf-global--BackgroundColor--100);
+    display: none;
+    top: 20px;
+    min-width: 100px;
+    padding: 0;
+}
+
+#kc-locale-dropdown{
+    display: inline-block;
+}
+
+#kc-locale-dropdown:hover ul {
+    display:block;
+}
+
+/* IE compatibility */
+#kc-locale-dropdown a {
+    color: #6A6E73;
+    color: var(--pf-global--Color--200);
+    text-align: right;
+    font-size: 14px;
+    font-size: var(--pf-global--FontSize--sm);
+}
+
+/* IE compatibility */
+a#kc-current-locale-link::after {
+    content: 2c5;
+    margin-left: 4px;
+    margin-left: var(--pf-global--spacer--xs)
+}
+
+.login-pf .container {
+    padding-top: 40px;
+}
+
+.login-pf a:hover {
+    color: #0099d3;
+}
+
+#kc-logo {
+    width: 100%;
+}
+
+div.kc-logo-text {
+    background-image: url(../img/agdsn_logo.png);
+    background-repeat: no-repeat;
+       background-size: auto;
+   position: relative;
+    top: 0%;
+    left: 25%;
+    width: 950px;
+  height: 250px;
+ 
+   
+}
+
+div.kc-logo-text span {
+    display: none;
+}
+
+#kc-header {
+    color: #ededed;
+    overflow: visible;
+    white-space: nowrap;
+}
+
+#kc-header-wrapper {
+    font-size: 29px;
+    text-transform: uppercase;
+    letter-spacing: 3px;
+    line-height: 1.2em;
+    padding: 62px 10px 20px;
+    white-space: normal;
+}
+
+#kc-content {
+    width: 100%;
+}
+
+#kc-attempted-username {
+    font-size: 20px;
+    font-family: inherit;
+    font-weight: normal;
+    padding-right: 10px;
+}
+
+#kc-username {
+    text-align: center;
+    margin-bottom:-10px;
+}
+
+#kc-webauthn-settings-form {
+    padding-top: 8px;
+}
+
+#kc-form-webauthn .select-auth-box-parent {
+    pointer-events: none;
+}
+
+#kc-form-webauthn .select-auth-box-desc {
+    color: var(--pf-global--palette--black-600);
+}
+
+#kc-form-webauthn .select-auth-box-headline {
+    color: var(--pf-global--Color--300);
+}
+
+#kc-form-webauthn .select-auth-box-icon {
+    flex: 0 0 3em;
+}
+
+#kc-form-webauthn .select-auth-box-icon-properties {
+    margin-top: 10px;
+    font-size: 1.8em;
+}
+
+#kc-form-webauthn .select-auth-box-icon-properties.unknown-transport-class {
+    margin-top: 3px;
+}
+
+#kc-form-webauthn .pf-l-stack__item {
+    margin: -1px 0;
+}
+
+#kc-content-wrapper {
+    margin-top: 20px;
+}
+
+#kc-form-wrapper {
+    margin-top: 10px;
+}
+
+#kc-info {
+    margin: 20px -40px -30px;
+}
+
+#kc-info-wrapper {
+    font-size: 13px;
+    padding: 15px 35px;
+    background-color: #F0F0F0;
+}
+
+#kc-form-options span {
+    display: block;
+}
+
+#kc-form-options .checkbox {
+    margin-top: 0;
+    color: #72767b;
+}
+
+#kc-terms-text {
+    margin-bottom: 20px;
+}
+
+#kc-registration {
+    margin-bottom: 0;
+}
+
+/* TOTP */
+
+.subtitle {
+    text-align: right;
+    margin-top: 30px;
+    color: #909090;
+}
+
+.required {
+    color: #A30000; /* default - IE compatibility */
+    color: var(--pf-global--danger-color--200);
+}
+
+ol#kc-totp-settings {
+    margin: 0;
+    padding-left: 20px;
+}
+
+ul#kc-totp-supported-apps {
+    margin-bottom: 10px;
+}
+
+#kc-totp-secret-qr-code {
+    max-width:150px;
+    max-height:150px;
+}
+
+#kc-totp-secret-key {
+    background-color: #fff;
+    color: #333333;
+    font-size: 16px;
+    padding: 10px 0;
+}
+
+/* OAuth */
+
+#kc-oauth h3 {
+    margin-top: 0;
+}
+
+#kc-oauth ul {
+    list-style: none;
+    padding: 0;
+    margin: 0;
+}
+
+#kc-oauth ul li {
+    border-top: 1px solid rgba(255, 255, 255, 0.1);
+    font-size: 12px;
+    padding: 10px 0;
+}
+
+#kc-oauth ul li:first-of-type {
+    border-top: 0;
+}
+
+#kc-oauth .kc-role {
+    display: inline-block;
+    width: 50%;
+}
+
+/* Code */
+#kc-code textarea {
+    width: 100%;
+    height: 8em;
+}
+
+/* Social */
+.kc-social-links {
+    margin-top: 20px;
+}
+
+.kc-social-provider-logo {
+    font-size: 23px;
+    width: 30px;
+    height: 25px;
+    float: left;
+}
+
+.kc-social-gray {
+    color: #737679; /* default - IE compatibility */
+    color: var(--pf-global--Color--200);
+}
+
+.kc-social-item {
+    margin-bottom: 0.5rem; /* default - IE compatibility */
+    margin-bottom: var(--pf-global--spacer--sm);
+    font-size: 15px;
+    text-align: center;
+}
+
+.kc-social-provider-name {
+    position: relative;
+    top: 3px;
+}
+
+.kc-social-icon-text {
+    left: -15px;
+}
+
+.kc-social-grid {
+    display:grid;
+    grid-column-gap: 10px;
+    grid-row-gap: 5px;
+    grid-column-end: span 6;
+    --pf-l-grid__item--GridColumnEnd: span 6;
+}
+
+.kc-social-grid .kc-social-icon-text {
+    left: -10px;
+}
+
+.kc-login-tooltip {
+    position: relative;
+    display: inline-block;
+}
+
+.kc-social-section {
+    text-align: center;
+}
+
+.kc-social-section hr{
+    margin-bottom: 10px
+}
+
+.kc-login-tooltip .kc-tooltip-text{
+    top:-3px;
+    left:160%;
+    background-color: black;
+    visibility: hidden;
+    color: #fff;
+
+    min-width:130px;
+    text-align: center;
+    border-radius: 2px;
+    box-shadow:0 1px 8px rgba(0,0,0,0.6);
+    padding: 5px;
+
+    position: absolute;
+    opacity:0;
+    transition:opacity 0.5s;
+}
+
+/* Show tooltip */
+.kc-login-tooltip:hover .kc-tooltip-text {
+    visibility: visible;
+    opacity:0.7;
+}
+
+/* Arrow for tooltip */
+.kc-login-tooltip .kc-tooltip-text::after {
+    content:  ;
+    position: absolute;
+    top: 15px;
+    right: 100%;
+    margin-top: -5px;
+    border-width: 5px;
+    border-style: solid;
+    border-color: transparent black transparent transparent;
+}
+
+@media (min-width: 768px) {
+    #kc-container-wrapper {
+        position: absolute;
+        width: 100%;
+    }
+
+    .login-pf .container {
+        padding-right: 80px;
+    }
+
+    #kc-locale {
+        position: relative;
+        text-align: right;
+        z-index: 9999;
+    }
+}
+
+@media (max-width: 767px) {
+
+    .login-pf body {
+        background: white;
+    }
+
+    #kc-header {
+        padding-left: 15px;
+        padding-right: 15px;
+        float: none;
+        text-align: left;
+    }
+
+    #kc-header-wrapper {
+        font-size: 16px;
+        font-weight: bold;
+        padding: 20px 60px 0 0;
+        color: #72767b;
+        letter-spacing: 0;
+    }
+
+    div.kc-logo-text {
+        margin: 0;
+        width: 150px;
+        height: 32px;
+        background-size: 100%;
+    }
+
+    #kc-form {
+        float: none;
+    }
+
+    #kc-info-wrapper {
+        border-top: 1px solid rgba(255, 255, 255, 0.1);
+        background-color: transparent;
+    }
+
+    .login-pf .container {
+        padding-top: 15px;
+        padding-bottom: 15px;
+    }
+
+    #kc-locale {
+        position: absolute;
+        width: 200px;
+        top: 20px;
+        right: 20px;
+        text-align: right;
+        z-index: 9999;
+    }
+}
+
+@media (min-height: 646px) {
+    #kc-container-wrapper {
+        bottom: 12%;
+    }
+}
+
+@media (max-height: 645px) {
+    #kc-container-wrapper {
+        padding-top: 50px;
+        top: 20%;
+    }
+}
+
+.card-pf form.form-actions .btn {
+    float: right;
+    margin-left: 10px;
+}
+
+#kc-form-buttons {
+    margin-top: 20px;
+}
+
+.login-pf-page .login-pf-brand {
+    margin-top: 20px;
+    max-width: 360px;
+    width: 40%;
+}
+
+/* Internet Explorer 11 compatibility workaround for select-authenticator screen */
+@media all and (-ms-high-contrast: none),
+(-ms-high-contrast: active) {
+    .select-auth-box-parent {
+        border-top: 1px solid #f0f0f0;
+        padding-top: 1rem;
+        padding-bottom: 1rem;
+        cursor: pointer;
+    }
+
+    .select-auth-box-headline {
+        font-size: 16px;
+        color: #06c;
+        font-weight: bold;
+    }
+
+    .select-auth-box-desc {
+        font-size: 14px;
+    }
+
+    .pf-l-stack {
+        flex-basis: 100%;
+    }
+}
+/* End of IE11 workaround for select-authenticator screen */
+
+.select-auth-box-arrow{
+    display: flex;
+    align-items: center;
+    margin-right: 2rem;
+}
+
+.select-auth-box-icon{
+    display: flex;
+    flex: 0 0 2em;
+    justify-content: center;
+    margin-right: 1rem;
+    margin-left: 3rem;
+}
+
+.select-auth-box-parent{
+    border-top: 1px solid var(--pf-global--palette--black-200);
+    padding-top: 1rem;
+    padding-bottom: 1rem;
+    cursor: pointer;
+}
+
+.select-auth-box-parent:hover{
+    background-color: #f7f8f8;
+}
+
+.select-auth-container {
+}
+
+.select-auth-box-headline {
+    font-size: var(--pf-global--FontSize--md);
+    color: var(--pf-global--primary-color--100);
+    font-weight: bold;
+}
+
+.select-auth-box-desc {
+    font-size: var(--pf-global--FontSize--sm);
+}
+
+.select-auth-box-paragraph {
+    text-align: center;
+    font-size: var(--pf-global--FontSize--md);
+    margin-bottom: 5px;
+}
+
+.card-pf {
+    margin: 0 auto;
+    box-shadow: var(--pf-global--BoxShadow--lg);
+    padding: 0 20px;
+    max-width: 500px;
+    border-top: 4px solid;
+    border-color: #0066CC; /* default - IE compatibility */
+    border-color: var(--pf-global--primary-color--100);
+}
+
+/*phone*/
+@media (max-width: 767px) {
+    .login-pf-page .card-pf {
+        max-width: none;
+        margin-left: 0;
+        margin-right: 0;
+        padding-top: 0;
+        border-top: 0;
+        box-shadow: 0 0;
+    }
+
+    .kc-social-grid {
+        grid-column-end: 12;
+        --pf-l-grid__item--GridColumnEnd: span 12;
+    }
+
+    .kc-social-grid .kc-social-icon-text {
+        left: -15px;
+    }
+}
+
+.login-pf-page .login-pf-signup {
+    font-size: 15px;
+    color: #72767b;
+}
+#kc-content-wrapper .row {
+    margin-left: 0;
+    margin-right: 0;
+}
+
+.login-pf-page.login-pf-page-accounts {
+    margin-left: auto;
+    margin-right: auto;
+}
+
+.login-pf-page .btn-primary {
+    margin-top: 0;
+}
+
+.login-pf-page .list-view-pf .list-group-item {
+    border-bottom: 1px solid #ededed;
+}
+
+.login-pf-page .list-view-pf-description {
+    width: 100%;
+}
+
+#kc-form-login div.form-group:last-of-type,
+#kc-register-form div.form-group:last-of-type,
+#kc-update-profile-form div.form-group:last-of-type {
+    margin-bottom: 0px;
+}
+
+.no-bottom-margin {
+    margin-bottom: 0;
+}
+
+#kc-back {
+    margin-top: 5px;
+}
+
+/* Recovery codes */
+.kc-recovery-codes-warning {
+    margin-bottom: 32px;
+}
+.kc-recovery-codes-warning .pf-c-alert__description p {
+    font-size: 0.875rem;
+}
+.kc-recovery-codes-list {
+    list-style: none;
+    columns: 2;
+    margin: 16px 0;
+    padding: 16px 16px 8px 16px;
+    border: 1px solid #D2D2D2;
+}
+.kc-recovery-codes-list li {
+    margin-bottom: 8px;
+    font-size: 11px;
+}
+.kc-recovery-codes-list li span {
+    color: #6A6E73;
+    width: 16px;
+    text-align: right;
+    display: inline-block;
+    margin-right: 1px;
+}
+
+.kc-recovery-codes-actions {
+    margin-bottom: 24px;
+}
+.kc-recovery-codes-actions button {
+    padding-left: 0;
+}
+.kc-recovery-codes-actions button i {
+    margin-right: 8px;
+}
+
+.kc-recovery-codes-confirmation {
+    align-items: baseline;
+    margin-bottom: 16px;
+}
+/* End Recovery codes */
+
+
diff --git a/modules/keycloak/theme/login/resources/img/background.jpg b/modules/keycloak/theme/login/resources/img/background.jpg
new file mode 100644
index 0000000..0a1a60d
Binary files /dev/null and b/modules/keycloak/theme/login/resources/img/background.jpg differ
diff --git a/modules/keycloak/theme/login/theme.properties b/modules/keycloak/theme/login/theme.properties
new file mode 100644
index 0000000..c0d3ad2
--- /dev/null
+++ b/modules/keycloak/theme/login/theme.properties
@@ -0,0 +1,4 @@
+parent=keycloak
+import=common/keycloak
+
+styles=css/login.css
diff --git a/modules/ldap/0001-update-user-validation-regex.patch b/modules/ldap/0001-update-user-validation-regex.patch
index 6ae40f9..ce78894 100644
--- a/modules/ldap/0001-update-user-validation-regex.patch
+++ b/modules/ldap/0001-update-user-validation-regex.patch
@@ -1,25 +1,35 @@
-From f5c68898be345fb0dca5ab7b596b9cbe674f5dfb Mon Sep 17 00:00:00 2001
-From: Rouven Seifert <rouven@rfive.de>
-Date: Tue, 4 Jul 2023 15:14:00 +0200
-Subject: [PATCH] update user validation regex
-
----
- internal/core/validation.go | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/internal/core/validation.go b/internal/core/validation.go
-index 3e168b5..10dfc0a 100644
---- a/internal/core/validation.go
-+++ b/internal/core/validation.go
-@@ -30,7 +30,7 @@ import (
- )
- 
- //this regexp copied from useradd(8) manpage
--const posixAccountNamePattern = `[a-z_][a-z0-9_-]*\$?`
-+const posixAccountNamePattern = `[a-z_][a-z0-9._-]*\$?`
+diff --git a/cmd/portunus-orchestrator/config.go b/cmd/portunus-orchestrator/config.go
+index 4db19f2..290128a 100644
+--- a/cmd/portunus-orchestrator/config.go
++++ b/cmd/portunus-orchestrator/config.go
+@@ -23,7 +23,7 @@ type valueCheck struct {
+ }
  
  var (
- 	errIsMissing      = errors.New("is missing")
--- 
-2.41.0
-
+-	userOrGroupPattern = `^[a-z_][a-z0-9_-]*\$?$`
++	userOrGroupPattern = `^[a-z_][a-z0-9._-]*\$?$`
+ 	envDefaults        = map[string]string{
+ 		//empty value = not optional
+ 		"PORTUNUS_DEBUG":              "false",
+diff --git a/internal/grammars/grammars.go b/internal/grammars/grammars.go
+index 1253c05..e458fd0 100644
+--- a/internal/grammars/grammars.go
++++ b/internal/grammars/grammars.go
+@@ -39,7 +39,7 @@ const (
+ 	// This regex is based on the respective format description in the useradd(8) manpage.
+ 	//
+ 	// This is only shown for documentation purposes here; use func IsPOSIXAccountName instead.
+-	POSIXAccountNameRegex = `^[a-z_][a-z0-9_-]*\$?$`
++	POSIXAccountNameRegex = `^[a-z_][a-z0-9._-]*\$?$`
+ )
+ 
+ //TODO There is also some `import "regexp"` in cmd/orchestrator/ldap.go to render
+@@ -159,7 +159,7 @@ func checkByteInPOSIXAccountName(idx, length int, b byte) bool {
+ 	switch {
+ 	case (b >= 'a' && b <= 'z') || b == '_':
+ 		return true
+-	case (b >= '0' && b <= '9') || b == '-':
++	case (b >= '0' && b <= '9') || b == '-' || b == '.':
+ 		return idx != 0 // not allowed at start
+ 	default:
+ 		return false
diff --git a/modules/ldap/0002-both-ldap-and-ldaps.patch b/modules/ldap/0002-both-ldap-and-ldaps.patch
index 9ae71a5..3ebda7e 100644
--- a/modules/ldap/0002-both-ldap-and-ldaps.patch
+++ b/modules/ldap/0002-both-ldap-and-ldaps.patch
@@ -1,8 +1,8 @@
-diff --git a/cmd/orchestrator/ldap.go b/cmd/orchestrator/ldap.go
-index ed0d466..a672046 100644
---- a/cmd/orchestrator/ldap.go
-+++ b/cmd/orchestrator/ldap.go
-@@ -130,7 +130,7 @@ func runLDAPServer(environment map[string]string) {
+diff --git a/cmd/portunus-orchestrator/ldap.go b/cmd/portunus-orchestrator/ldap.go
+index 9564c5e..40cd2d7 100644
+--- a/cmd/portunus-orchestrator/ldap.go
++++ b/cmd/portunus-orchestrator/ldap.go
+@@ -134,7 +134,7 @@ func runLDAPServer(environment map[string]string) {
  
  	bindURL := "ldap:///"
  	if environment["PORTUNUS_SLAPD_TLS_CERTIFICATE"] != "" {
diff --git a/modules/ldap/0003-gecos-ascii-escape.patch b/modules/ldap/0003-gecos-ascii-escape.patch
index c3918cb..83650f4 100644
--- a/modules/ldap/0003-gecos-ascii-escape.patch
+++ b/modules/ldap/0003-gecos-ascii-escape.patch
@@ -1,24 +1,26 @@
-diff --git a/internal/core/user.go b/internal/core/user.go
-index e74ccfe..291c75b 100644
---- a/internal/core/user.go
-+++ b/internal/core/user.go
-@@ -8,6 +8,7 @@ package core
+diff --git a/internal/ldap/object.go b/internal/ldap/object.go
+index d4e5c6f..fcefec7 100644
+--- a/internal/ldap/object.go
++++ b/internal/ldap/object.go
+@@ -8,6 +8,7 @@ package ldap
  
  import (
  	"fmt"
-+	"strconv"
- )
++	"regexp"
  
- // User represents a single user account.
-@@ -86,9 +87,9 @@ func (u User) RenderToLDAP(suffix string, allGroups map[string]Group) LDAPObject
+ 	"github.com/majewsky/portunus/internal/core"
+ )
+@@ -94,10 +95,11 @@ func renderUser(u core.User, dnSuffix string, allGroups []core.Group) Object {
+ 		if u.POSIX.LoginShell != "" {
  			obj.Attributes["loginShell"] = []string{u.POSIX.LoginShell}
  		}
++		var nonASCII = regexp.MustCompile("[^\\x00-\\x7F]")
  		if u.POSIX.GECOS == "" {
 -			obj.Attributes["gecos"] = []string{u.FullName()}
-+			obj.Attributes["gecos"] = []string{strconv.QuoteToASCII(u.FullName())}
++			obj.Attributes["gecos"] = []string{nonASCII.ReplaceAllString(u.FullName(), "")}
  		} else {
 -			obj.Attributes["gecos"] = []string{u.POSIX.GECOS}
-+			obj.Attributes["gecos"] = []string{strconv.QuoteToASCII(u.POSIX.GECOS)}
++			obj.Attributes["gecos"] = []string{nonASCII.ReplaceAllString(u.POSIX.GECOS, "")}
  		}
  		obj.Attributes["objectClass"] = append(obj.Attributes["objectClass"], "posixAccount")
  	}
diff --git a/modules/ldap/0004-make-givenName-optional.patch b/modules/ldap/0004-make-givenName-optional.patch
index 4131252..4b6ecf6 100644
--- a/modules/ldap/0004-make-givenName-optional.patch
+++ b/modules/ldap/0004-make-givenName-optional.patch
@@ -1,8 +1,20 @@
 diff --git a/internal/core/user.go b/internal/core/user.go
-index e74ccfe..ce03eeb 100644
+index f45fdf7..4f93b37 100644
 --- a/internal/core/user.go
 +++ b/internal/core/user.go
-@@ -64,7 +64,6 @@ func (u User) RenderToLDAP(suffix string, allGroups map[string]Group) LDAPObject
+@@ -76,7 +76,6 @@ func (u User) validateLocal(cfg *ValidationConfig) (errs errext.ErrorSet) {
+ 		MustBePosixAccountNameIf(u.LoginName, u.POSIX != nil),
+ 	))
+ 	errs.Add(ref.Field("given_name").WrapFirst(
+-		MustNotBeEmpty(u.GivenName),
+ 		MustNotHaveSurroundingSpaces(u.GivenName),
+ 	))
+ 	errs.Add(ref.Field("family_name").WrapFirst(
+diff --git a/internal/ldap/object.go b/internal/ldap/object.go
+index d4e5c6f..1225084 100644
+--- a/internal/ldap/object.go
++++ b/internal/ldap/object.go
+@@ -73,7 +73,6 @@ func renderUser(u core.User, dnSuffix string, allGroups []core.Group) Object {
  			"uid":          {u.LoginName},
  			"cn":           {u.FullName()},
  			"sn":           {u.FamilyName},
@@ -10,7 +22,7 @@ index e74ccfe..ce03eeb 100644
  			"userPassword": {u.PasswordHash},
  			"isMemberOf":   memberOfGroupDNames,
  			"objectClass":  {"portunusPerson", "inetOrgPerson", "organizationalPerson", "person", "top"},
-@@ -74,6 +73,9 @@ func (u User) RenderToLDAP(suffix string, allGroups map[string]Group) LDAPObject
+@@ -83,6 +82,9 @@ func renderUser(u core.User, dnSuffix string, allGroups []core.Group) Object {
  	if u.EMailAddress != "" {
  		obj.Attributes["mail"] = []string{u.EMailAddress}
  	}
@@ -20,15 +32,3 @@ index e74ccfe..ce03eeb 100644
  	if len(u.SSHPublicKeys) > 0 {
  		obj.Attributes["sshPublicKey"] = u.SSHPublicKeys
  	}
-diff --git a/internal/frontend/users.go b/internal/frontend/users.go
-index 225c5b3..1a961ca 100644
---- a/internal/frontend/users.go
-+++ b/internal/frontend/users.go
-@@ -168,7 +168,6 @@ func buildUserMasterdataFieldset(e core.Engine, u *core.User, state *h.FormState
- 			Name:      "given_name",
- 			Label:     "Given name",
- 			Rules: []h.ValidationRule{
--				core.MustNotBeEmpty,
- 				core.MustNotHaveSurroundingSpaces,
- 			},
- 		},
diff --git a/modules/ldap/default.nix b/modules/ldap/default.nix
index 98274df..bdf3d3b 100644
--- a/modules/ldap/default.nix
+++ b/modules/ldap/default.nix
@@ -1,7 +1,7 @@
-{ config, lib, pkgs, ... }:
+{ config, pkgs, ... }:
 let
   domain = "auth.${config.networking.domain}";
-  seed = {
+  seedSettings = {
     groups = [
       {
         name = "admins";
@@ -46,11 +46,6 @@ in
   sops.secrets = {
     "portunus/admin-password".owner = config.services.portunus.user;
     "portunus/search-password".owner = config.services.portunus.user;
-    "dex/environment".owner = config.systemd.services.dex.serviceConfig.User;
-    nslcd_ldap_search = {
-      key = "portunus/search-password";
-      owner = config.systemd.services.nslcd.serviceConfig.User;
-    };
   };
 
   services.portunus = {
@@ -62,13 +57,11 @@ in
         ./0003-gecos-ascii-escape.patch
         ./0004-make-givenName-optional.patch
       ];
+      doCheck = false; # posix regex related tests break
     });
 
-    inherit domain;
+    inherit domain seedSettings;
     port = 8681;
-    dex.enable = true;
-    seedPath = pkgs.writeText "portunus-seed.json" (builtins.toJSON seed);
-
     ldap = {
       suffix = "dc=ifsr,dc=de";
       searchUserName = "search";
@@ -79,46 +72,13 @@ in
     };
   };
 
-  services.dex.settings.oauth2.skipApprovalScreen = true;
-
-  systemd.services.dex.serviceConfig = {
-    DynamicUser = lib.mkForce false;
-    EnvironmentFile = config.sops.secrets."dex/environment".path;
-    StateDirectory = "dex";
-    User = "dex";
-  };
-
-  users = {
-    users.dex = {
-      group = "dex";
-      isSystemUser = true;
-    };
-    groups.dex = { };
-
-    ldap =
-      let portunus = config.services.portunus; in
-      rec {
-        enable = true;
-        server = "ldap://localhost";
-        base = "${portunus.ldap.suffix}";
-        bind = {
-          distinguishedName = "uid=${portunus.ldap.searchUserName},ou=users,${base}";
-          passwordFile = config.sops.secrets.nslcd_ldap_search.path;
-        };
-        daemon.enable = true;
-      };
-  };
-
   security.pam.services.sshd.makeHomeDir = true;
 
   services.nginx = {
     enable = true;
     virtualHosts."${config.services.portunus.domain}" = {
-      forceSSL = true;
-      enableACME = true;
       locations = {
         "/".proxyPass = "http://localhost:${toString config.services.portunus.port}";
-        "/dex".proxyPass = "http://localhost:${toString config.services.portunus.dex.port}";
       };
     };
   };
diff --git a/modules/mail/default.nix b/modules/mail/default.nix
index 2be0e73..e6af452 100644
--- a/modules/mail/default.nix
+++ b/modules/mail/default.nix
@@ -1,298 +1,17 @@
-{ config, pkgs, ... }:
+{ config, ... }:
 let
   hostname = "mail.${config.networking.domain}";
-  domain = config.networking.domain;
-  rspamd-domain = "rspamd.${config.networking.domain}";
-  dovecot-ldap-args = pkgs.writeText "ldap-args" ''
-    uris = ldap://localhost
-    dn = uid=search, ou=users, dc=ifsr, dc=de
-    auth_bind = yes
-    !include ${config.sops.secrets."dovecot_ldap_search".path}
-
-    ldap_version = 3
-    scope = subtree
-    base = dc=ifsr, dc=de
-    user_filter = (&(objectClass=posixAccount)(uid=%n))
-    pass_filter = (&(objectClass=posixAccount)(uid=%n))
-  '';
-  # see https://www.kuketz-blog.de/e-mail-anbieter-ip-stripping-aus-datenschutzgruenden/
-  header_cleanup = pkgs.writeText "header_cleanup_outgoing" ''
-    /^\s*(Received: from)[^\n]*(.*)/ REPLACE $1 127.0.0.1 (localhost [127.0.0.1])$2
-    /^\s*User-Agent/ IGNORE
-    /^\s*X-Enigmail/ IGNORE
-    /^\s*X-Mailer/ IGNORE
-    /^\s*X-Originating-IP/ IGNORE
-    /^\s*Mime-Version/ IGNORE
-  '';
 in
 {
-  sops.secrets."rspamd-password".owner = config.users.users.rspamd.name;
-  sops.secrets."dovecot_ldap_search".owner = config.services.dovecot2.user;
-  sops.secrets."postfix_ldap_aliases".owner = config.services.postfix.user;
-
-  networking.firewall.allowedTCPPorts = [
-    25 # insecure SMTP
-    143
-    465
-    587 # SMTP
-    993 # IMAP
-    4190 # sieve
+  imports = [
+    ./postfix.nix
+    ./dovecot2.nix
+    ./rspamd.nix
+    ./sogo.nix
+    ./mailman.nix
   ];
-  users.users.postfix.extraGroups = [ "opendkim" ];
-  environment.etc = {
-    "dovecot/sieve-pipe/sa-learn-spam.sh" = {
-      text = ''
-        #!/bin/sh
-        ${pkgs.rspamd}/bin/rspamc learn_spam
-      '';
-      mode = "0555";
-    };
-    "dovecot/sieve-pipe/sa-learn-ham.sh" = {
-      text = ''
-        #!/bin/sh
-        ${pkgs.rspamd}/bin/rspamc learn_ham
-      '';
-      mode = "0555";
-    };
-    "dovecot/sieve/report-spam.sieve" = {
-      source = ./report-spam.sieve;
-      user = "dovecot2";
-      group = "dovecot2";
-      mode = "0544";
-    };
-    "dovecot/sieve/report-ham.sieve" = {
-      source = ./report-ham.sieve;
-      user = "dovecot2";
-      group = "dovecot2";
-      mode = "0544";
-    };
-  };
 
-  services = {
-    postfix = {
-      enable = true;
-      enableSubmission = true;
-      enableSubmissions = true;
-      hostname = "${hostname}";
-      domain = "${domain}";
-      origin = "${domain}";
-      destination = [ "${hostname}" "${domain}" "localhost" ];
-      networksStyle = "host"; # localhost and own public IP
-      sslCert = "/var/lib/acme/${hostname}/fullchain.pem";
-      sslKey = "/var/lib/acme/${hostname}/key.pem";
-      relayDomains = [ "hash:/var/lib/mailman/data/postfix_domains" ];
-      config = {
-        home_mailbox = "Maildir/";
-        # hostname used in helo command. It is recommended to have this match the reverse dns entry
-        smtp_helo_name = config.networking.rDNS;
-        smtp_use_tls = true;
-        # smtp_tls_security_level = "encrypt";
-        smtpd_use_tls = true;
-        # smtpd_tls_security_level = lib.mkForce "encrypt";
-        # smtpd_tls_auth_only = true;
-        smtpd_tls_protocols = [
-          "!SSLv2"
-          "!SSLv3"
-          "!TLSv1"
-          "!TLSv1.1"
-        ];
-        # "reject_non_fqdn_hostname"
-        smtpd_recipient_restrictions = [
-          "permit_sasl_authenticated"
-          "permit_mynetworks"
-          "reject_unauth_destination"
-          "reject_non_fqdn_sender"
-          "reject_non_fqdn_recipient"
-          "reject_unknown_sender_domain"
-          "reject_unknown_recipient_domain"
-          "reject_unauth_destination"
-          "reject_unauth_pipelining"
-          "reject_invalid_hostname"
-          "check_policy_service inet:localhost:12340"
-        ];
-        smtpd_relay_restrictions = [
-          "permit_sasl_authenticated"
-          "permit_mynetworks"
-          "reject_unauth_destination"
-        ];
-        smtp_header_checks = "pcre:${header_cleanup}";
-        # smtpd_sender_login_maps = [ "ldap:${ldap-senders}" ];
-        alias_maps = [ "hash:/etc/aliases" ];
-        alias_database = [ "hash:/etc/aliases" ];
-        # alias_maps = [ "hash:/etc/aliases" "ldap:${ldap-aliases}" ];
-        smtpd_milters = [ "local:/run/opendkim/opendkim.sock" ];
-        non_smtpd_milters = [ "local:/var/run/opendkim/opendkim.sock" ];
-        smtpd_sasl_auth_enable = true;
-        smtpd_sasl_path = "/var/lib/postfix/auth";
-        smtpd_sasl_type = "dovecot";
-        #mailman stuff
-        mailbox_transport = "lmtp:unix:/run/dovecot2/dovecot-lmtp";
-
-        transport_maps = [ "hash:/var/lib/mailman/data/postfix_lmtp" ];
-        virtual_alias_maps = [ "hash:/var/lib/mailman/data/postfix_vmap" ];
-        local_recipient_maps = [ "hash:/var/lib/mailman/data/postfix_lmtp" "ldap:${config.sops.secrets."postfix_ldap_aliases".path}" "$alias_maps" ];
-      };
-    };
-    dovecot2 = {
-      enable = true;
-      enableImap = true;
-      enableQuota = true;
-      quotaGlobalPerUser = "10G";
-      enableLmtp = true;
-      mailLocation = "maildir:~/Maildir";
-      sslServerCert = "/var/lib/acme/${hostname}/fullchain.pem";
-      sslServerKey = "/var/lib/acme/${hostname}/key.pem";
-      protocols = [ "imap" "sieve" ];
-      mailPlugins = {
-        perProtocol = {
-          imap = {
-            enable = [ "imap_sieve" ];
-          };
-          lmtp = {
-            enable = [ "sieve" ];
-          };
-        };
-      };
-      mailboxes = {
-        Spam = {
-          auto = "subscribe";
-          specialUse = "Junk";
-          autoexpunge = "60d";
-        };
-        Sent = {
-          auto = "subscribe";
-          specialUse = "Sent";
-        };
-        Drafts = {
-          auto = "subscribe";
-          specialUse = "Drafts";
-        };
-        Trash = {
-          auto = "subscribe";
-          specialUse = "Trash";
-        };
-      };
-      modules = [
-        pkgs.dovecot_pigeonhole
-      ];
-      extraConfig = ''
-        auth_username_format = %Ln
-        passdb {
-          driver = ldap
-          args = ${dovecot-ldap-args}
-        }
-        userdb {
-          driver = ldap
-          args = ${dovecot-ldap-args}
-        }
-        service auth {
-          unix_listener /var/lib/postfix/auth {
-            group = postfix
-            mode = 0660
-            user = postfix
-          }
-        }
-        service managesieve-login {
-          inet_listener sieve {
-            port = 4190
-          }
-          service_count = 1
-        }
-
-        namespace inbox {
-          separator = /
-          inbox = yes
-        }
-
-        service lmtp {
-          unix_listener dovecot-lmtp {
-            group = postfix
-            mode = 0600
-            user = postfix
-          }
-          client_limit = 1
-        }
-
-
-        mail_plugins = $mail_plugins listescape
-        plugin {
-          sieve_plugins = sieve_imapsieve sieve_extprograms
-          sieve_global_extensions = +vnd.dovecot.pipe
-          sieve_pipe_bin_dir = /etc/dovecot/sieve-pipe
-
-          # Spam: From elsewhere to Spam folder or flag changed in Spam folder
-          imapsieve_mailbox1_name = Spam
-          imapsieve_mailbox1_causes = COPY APPEND FLAG
-          imapsieve_mailbox1_before = file:/etc/dovecot/sieve/report-spam.sieve
-
-          # Ham: From Spam folder to elsewhere
-          imapsieve_mailbox2_name = *
-          imapsieve_mailbox2_from = Spam
-          imapsieve_mailbox2_causes = COPY
-          imapsieve_mailbox2_before = file:/etc/dovecot/sieve/report-ham.sieve
-
-          # https://doc.dovecot.org/configuration_manual/plugins/listescape_plugin/
-          listescape_char = "\\"
-        }
-      '';
-    };
-    opendkim = {
-      enable = true;
-      domains = "csl:${config.networking.domain}";
-      selector = config.networking.hostName;
-      configFile = pkgs.writeText "opendkim-config" ''
-        UMask 0117
-      '';
-    };
-    rspamd = {
-      enable = true;
-      postfix.enable = true;
-      locals = {
-        "worker-controller.inc".source = config.sops.secrets."rspamd-password".path;
-        "redis.conf".text = ''
-          read_servers = "127.0.0.1";
-          write_servers = "127.0.0.1";
-        '';
-        # headers in spamassasin style to not break old sieve scripts
-        "worker-proxy.inc".text = ''
-          spam_header = "X-Spam-Flag";
-        '';
-        "milter_headers.conf".text = ''
-          use = ["x-spam-level", "x-spam-status"];
-        '';
-      };
-    };
-    redis = {
-      vmOverCommit = true;
-      servers.rspamd = {
-        enable = true;
-        port = 6379;
-      };
-    };
-    nginx = {
-      enable = true;
-      recommendedGzipSettings = true;
-      recommendedOptimisation = true;
-      recommendedProxySettings = true;
-      recommendedTlsSettings = true;
-
-      virtualHosts."${hostname}" = {
-        forceSSL = true;
-        enableACME = true;
-      };
-      virtualHosts."${rspamd-domain}" = {
-        forceSSL = true;
-        enableACME = true;
-        locations = {
-          "/" = {
-            proxyPass = "http://127.0.0.1:11334";
-            proxyWebsockets = true;
-          };
-        };
-      };
-    };
-  };
-  security.acme.certs."${domain}" = {
+  security.acme.certs."${hostname}" = {
     reloadServices = [
       "postfix.service"
       "dovecot2.service"
diff --git a/modules/mail/dovecot2.nix b/modules/mail/dovecot2.nix
new file mode 100644
index 0000000..ef3bbcc
--- /dev/null
+++ b/modules/mail/dovecot2.nix
@@ -0,0 +1,158 @@
+{ lib, config, pkgs, ... }:
+let
+  hostname = "mail.${config.networking.domain}";
+  dovecot-ldap-args = pkgs.writeText "ldap-args" ''
+    uris = ldap://localhost
+    dn = uid=search, ou=users, dc=ifsr, dc=de
+    auth_bind = yes
+    !include ${config.sops.secrets."dovecot_ldap_search".path}
+
+    ldap_version = 3
+    scope = subtree
+    base = dc=ifsr, dc=de
+    user_filter = (&(objectClass=posixAccount)(uid=%n))
+    pass_filter = (&(objectClass=posixAccount)(uid=%n))
+  '';
+in
+{
+  networking.firewall.allowedTCPPorts = [
+    993 # IMAPS
+    4190 # Managesieve
+  ];
+  sops.secrets."dovecot_ldap_search".owner = config.services.dovecot2.user;
+  services.dovecot2 = {
+    enable = true;
+    enableImap = true;
+    enableQuota = true;
+    quotaGlobalPerUser = "10G";
+    enableLmtp = true;
+    enablePAM = false;
+    mailLocation = "maildir:~/Maildir";
+    sslServerCert = "/var/lib/acme/${hostname}/fullchain.pem";
+    sslServerKey = "/var/lib/acme/${hostname}/key.pem";
+    protocols = [ "imap" "sieve" ];
+    mailPlugins = {
+      globally.enable = [ "listescape" ];
+      perProtocol = {
+        imap = {
+          enable = [ "imap_sieve" "imap_filter_sieve" ];
+        };
+        lmtp = {
+          enable = [ "sieve" ];
+        };
+      };
+    };
+    mailboxes = {
+      Spam = {
+        auto = "subscribe";
+        specialUse = "Junk";
+        autoexpunge = "60d";
+      };
+      Sent = {
+        auto = "subscribe";
+        specialUse = "Sent";
+      };
+      Drafts = {
+        auto = "subscribe";
+        specialUse = "Drafts";
+      };
+      Trash = {
+        auto = "subscribe";
+        specialUse = "Trash";
+      };
+      Archive = {
+        auto = "no";
+        specialUse = "Archive";
+      };
+    };
+    modules = [
+      pkgs.dovecot_pigeonhole
+    ];
+    # set to satisfy the sieveScripts check, will be overridden by userdb lookups anyways
+    mailUser = "vmail";
+    mailGroup = "vmail";
+    sieve = {
+      # just pot something in here to prevent empty strings
+      extensions = [ "notify" ];
+      pipeBins = map lib.getExe [
+        (pkgs.writeShellScriptBin "learn-ham.sh" "exec ${pkgs.rspamd}/bin/rspamc learn_ham")
+        (pkgs.writeShellScriptBin "learn-spam.sh" "exec ${pkgs.rspamd}/bin/rspamc learn_spam")
+      ];
+      plugins = [
+        "sieve_imapsieve"
+        "sieve_extprograms"
+      ];
+      scripts = {
+        before = pkgs.writeText "spam.sieve" ''
+          require "fileinto";
+
+          if anyof(
+          header :contains "x-spam-flag" "yes",
+          header :contains "X-Spam-Status" "Yes"){
+                  fileinto "Spam";
+          }
+        '';
+      };
+    };
+    imapsieve.mailbox = [
+      {
+        # Spam: From elsewhere to Spam folder or flag changed in Spam folder
+        name = "Spam";
+        causes = [ "COPY" "APPEND" "FLAG" ];
+        before = ./report-spam.sieve;
+
+      }
+      {
+        # From Junk folder to elsewhere
+        name = "*";
+        from = "Spam";
+        causes = [ "COPY" ];
+        before = ./report-ham.sieve;
+      }
+    ];
+    extraConfig = ''
+      auth_username_format = %Ln
+      passdb {
+        driver = ldap
+        args = ${dovecot-ldap-args}
+      }
+      userdb {
+        driver = ldap
+        args = ${dovecot-ldap-args}
+      }
+      service auth {
+        unix_listener /var/lib/postfix/auth {
+          group = postfix
+          mode = 0660
+          user = postfix
+        }
+      }
+      service managesieve-login {
+        inet_listener sieve {
+          port = 4190
+        }
+        service_count = 1
+      }
+
+      namespace inbox {
+        separator = /
+        inbox = yes
+      }
+
+      service lmtp {
+        unix_listener dovecot-lmtp {
+          group = postfix
+          mode = 0600
+          user = postfix
+        }
+        client_limit = 1
+      }
+
+
+      plugin {
+        # https://doc.dovecot.org/configuration_manual/plugins/listescape_plugin/
+        listescape_char = "\\"
+      }
+    '';
+  };
+}
diff --git a/modules/mailman.nix b/modules/mail/mailman.nix
similarity index 59%
rename from modules/mailman.nix
rename to modules/mail/mailman.nix
index efaee90..23d36a9 100644
--- a/modules/mailman.nix
+++ b/modules/mail/mailman.nix
@@ -20,8 +20,10 @@
     webSettings = {
       DATABASES.default = {
         ENGINE = "django.db.backends.postgresql";
-        NAME = "mailmanweb";
+        NAME = "mailman-web";
       };
+      ACCOUNT_EMAIL_UNKNOWN_ACCOUNTS = false;
+      ACCOUNT_PREVENT_ENUMERATION = false;
     };
     ldap = {
       enable = true;
@@ -40,26 +42,43 @@
       superUserGroup = "cn=admins,ou=groups,dc=ifsr,dc=de";
     };
   };
+  services.postfix = {
+    relayDomains = [ "hash:/var/lib/mailman/data/postfix_domains" ];
+    config = {
+      mailbox_transport = "lmtp:unix:/run/dovecot2/dovecot-lmtp";
+      transport_maps = [ "hash:/var/lib/mailman/data/postfix_lmtp" ];
+      virtual_alias_maps = [ "hash:/var/lib/mailman/data/postfix_vmap" ];
+      local_recipient_maps = [ "hash:/var/lib/mailman/data/postfix_lmtp" ];
+    };
+  };
   services.postgresql = {
     enable = true;
     ensureUsers = [
       {
         name = "mailman";
-        ensurePermissions = {
-          "DATABASE mailman" = "ALL PRIVILEGES";
-        };
+        ensureDBOwnership = true;
       }
       {
         name = "mailman-web";
-        ensurePermissions = {
-          "DATABASE mailmanweb" = "ALL PRIVILEGES";
-        };
+        ensureDBOwnership = true;
       }
     ];
-    ensureDatabases = [ "mailman" "mailmanweb" ];
+    ensureDatabases = [ "mailman" "mailman-web" ];
   };
   services.nginx.virtualHosts."lists.${config.networking.domain}" = {
-    enableACME = true;
-    forceSSL = true;
+    locations."/accounts/signup" = {
+      extraConfig = ''
+        allow 141.30.0.0/16;
+        allow 141.76.0.0/16;
+        deny all;
+        uwsgi_pass unix:/run/mailman-web.socket;
+      '';
+    };
+    locations."/robots.txt" = {
+      extraConfig = ''
+        add_header  Content-Type  text/plain;
+        return 200 "User-agent: *\nDisallow: /\n";
+      '';
+    };
   };
 }
diff --git a/modules/mail/postfix.nix b/modules/mail/postfix.nix
new file mode 100644
index 0000000..2ba240a
--- /dev/null
+++ b/modules/mail/postfix.nix
@@ -0,0 +1,98 @@
+{ config, pkgs, ... }:
+let
+  domain = config.networking.domain;
+  hostname = "mail.${config.networking.domain}";
+  # see https://www.kuketz-blog.de/e-mail-anbieter-ip-stripping-aus-datenschutzgruenden/
+  header_cleanup = pkgs.writeText "header_cleanup_outgoing" ''
+    /^\s*(Received: from)[^\n]*(.*)/ REPLACE $1 127.0.0.1 (localhost [127.0.0.1])$2
+    /^\s*User-Agent/ IGNORE
+    /^\s*X-Enigmail/ IGNORE
+    /^\s*X-Mailer/ IGNORE
+    /^\s*X-Originating-IP/ IGNORE
+    /^\s*Mime-Version/ IGNORE
+  '';
+  # https://unix.stackexchange.com/questions/294300/postfix-prevent-users-from-changing-the-real-e-mail-address
+  login_maps = pkgs.writeText "login_maps.pcre" ''
+    # basic username => username@ifsr.de
+    /^([^@+]*)(\+[^@]*)?@ifsr\.de$/ ''${1}
+  '';
+in
+{
+  sops.secrets."postfix_ldap_aliases".owner = config.services.postfix.user;
+
+  networking.firewall.allowedTCPPorts = [
+    25 # SMTP
+    465 # Submissions
+    587 # Submission
+  ];
+  services = {
+    postfix = {
+      enable = true;
+      enableSubmission = true;
+      enableSubmissions = true;
+      hostname = "${hostname}";
+      domain = "${domain}";
+      origin = "${domain}";
+      destination = [ "${hostname}" "${domain}" "localhost" ];
+      networksStyle = "host"; # localhost and own public IP
+      sslCert = "/var/lib/acme/${hostname}/fullchain.pem";
+      sslKey = "/var/lib/acme/${hostname}/key.pem";
+      config = {
+        home_mailbox = "Maildir/";
+        # 25 MiB
+        message_size_limit = "26214400";
+        # hostname used in helo command. It is recommended to have this match the reverse dns entry
+        smtp_helo_name = config.networking.rDNS;
+        smtpd_banner = "${config.networking.rDNS} ESMTP $mail_name";
+        smtp_tls_security_level = "may";
+        smtpd_tls_security_level = "may";
+        smtpd_tls_auth_only = true;
+        smtpd_tls_protocols = [
+          "!SSLv2"
+          "!SSLv3"
+          "!TLSv1"
+          "!TLSv1.1"
+        ];
+        # "reject_non_fqdn_hostname"
+        smtpd_recipient_restrictions = [
+          "permit_sasl_authenticated"
+          "permit_mynetworks"
+          "reject_unauth_destination"
+          "reject_non_fqdn_sender"
+          "reject_non_fqdn_recipient"
+          "reject_unknown_sender_domain"
+          "reject_unknown_recipient_domain"
+          "reject_unauth_destination"
+          "reject_unauth_pipelining"
+          "reject_invalid_hostname"
+          "check_policy_service inet:localhost:12340"
+        ];
+        smtpd_relay_restrictions = [
+          "permit_sasl_authenticated"
+          "permit_mynetworks"
+          "reject_unauth_destination"
+        ];
+        # https://www.postfix.org/smtp-smuggling.html
+        smtpd_data_restrictions = [
+          "reject_unauth_pipelining"
+        ];
+        smtpd_sender_restrictions = [
+          "reject_authenticated_sender_login_mismatch"
+        ];
+        smtpd_sender_login_maps = [
+          "pcre:/etc/special-aliases.pcre"
+          "pcre:${login_maps}"
+        ];
+        smtp_header_checks = "pcre:${header_cleanup}";
+        # smtpd_sender_login_maps = [ "ldap:${ldap-senders}" ];
+        alias_maps = [ "hash:/etc/aliases" ];
+        alias_database = [ "hash:/etc/aliases" ];
+        # alias_maps = [ "hash:/etc/aliases" "ldap:${ldap-aliases}" ];
+        smtpd_sasl_auth_enable = true;
+        smtpd_sasl_path = "/var/lib/postfix/auth";
+        smtpd_sasl_type = "dovecot";
+        local_recipient_maps = [ "ldap:${config.sops.secrets."postfix_ldap_aliases".path}" "$alias_maps" ];
+      };
+    };
+  };
+}
diff --git a/modules/mail/report-ham.sieve b/modules/mail/report-ham.sieve
index a9d30cf..6217a90 100755
--- a/modules/mail/report-ham.sieve
+++ b/modules/mail/report-ham.sieve
@@ -12,4 +12,4 @@ if environment :matches "imap.user" "*" {
   set "username" "${1}";
 }
 
-pipe :copy "sa-learn-ham.sh" [ "${username}" ];
+pipe :copy "learn-ham.sh" [ "${username}" ];
diff --git a/modules/mail/report-spam.sieve b/modules/mail/report-spam.sieve
index 4024b7a..9d4c74b 100755
--- a/modules/mail/report-spam.sieve
+++ b/modules/mail/report-spam.sieve
@@ -4,4 +4,4 @@ if environment :matches "imap.user" "*" {
   set "username" "${1}";
 }
 
-pipe :copy "sa-learn-spam.sh" [ "${username}" ];
\ No newline at end of file
+pipe :copy "learn-spam.sh" [ "${username}" ];
diff --git a/modules/mail/rspamd.nix b/modules/mail/rspamd.nix
new file mode 100644
index 0000000..cab3fd0
--- /dev/null
+++ b/modules/mail/rspamd.nix
@@ -0,0 +1,219 @@
+{ config, pkgs, ... }:
+let
+  domain = "rspamd.${config.networking.domain}";
+in
+{
+  sops.secrets."rspamd-password".owner = config.users.users.rspamd.name;
+  users.users.rspamd.extraGroups = [ "redis-rspamd" ];
+  services = {
+    rspamd = {
+      enable = true;
+      postfix.enable = true;
+      locals = {
+        "worker-controller.inc".source = config.sops.secrets."rspamd-password".path;
+        "redis.conf".text = ''
+          read_servers = "/run/redis-rspamd/redis.sock";
+          write_servers = "/run/redis-rspamd/redis.sock";
+        '';
+        # headers in spamassasin style to not break old sieve scripts
+        "worker-proxy.inc".text = ''
+          spam_header = "X-Spam-Flag";
+        '';
+        "milter_headers.conf".text = ''
+          use = ["x-spam-level", "x-spam-status", "x-spamd-result", "authentication-results" ];
+        '';
+        "neural.conf".text = ''
+          servers = "/run/redis-rspamd/redis.sock";
+          enabled = true;
+        '';
+        "neural_group.conf".text = ''
+          symbols = {
+            "NEURAL_SPAM" {
+              weight = 0.5; # fairly low weight since we don't know how this will behave
+              description = "Neural network spam";
+            }
+            "NEURAL_HAM" {
+              weight = -0.5;
+              description = "Neural network ham";
+            }
+          }
+        '';
+        "dmarc.conf".text = ''
+          reporting {
+            enabled = true;
+            email = 'noreply-dmarc@${config.networking.domain}';
+            domain = '${config.networking.domain}';
+            org_name = '${config.networking.domain}';
+            from_name = 'DMARC Aggregate Report';
+          }
+        '';
+        "dkim_signing.conf".text = ''
+          selector = "quitte2024";
+          allow_username_mismatch = true;
+          allow_hdrfrom_mismatch = true;
+          use_domain_sign_local = "ifsr.de";
+          path = /var/lib/rspamd/dkim/$domain.$selector.key;
+
+        '';
+        "reputation.conf".text = ''
+          rules {
+            ip_reputation = {
+              selector "ip" {
+              }
+              backend "redis" {
+                servers = "/run/redis-rspamd/redis.sock";
+              }
+
+              symbol = "IP_REPUTATION";
+            }
+            spf_reputation =  {
+              selector "spf" {
+              }
+              backend "redis" {
+                servers = "/run/redis-rspamd/redis.sock";
+              }
+
+              symbol = "SPF_REPUTATION";
+            }
+            dkim_reputation =  {
+              selector "dkim" {
+              }
+              backend "redis" {
+                servers = "/run/redis-rspamd/redis.sock";
+              }
+
+              symbol = "DKIM_REPUTATION"; # Also adjusts scores for DKIM_ALLOW, DKIM_REJECT
+            }
+            generic_reputation =  {
+              selector "generic" {
+                selector = "ip"; # see https://rspamd.com/doc/configuration/selectors.html
+              }
+              backend "redis" {
+                servers = "/run/redis-rspamd/redis.sock";
+              }
+
+              symbol = "GENERIC_REPUTATION";
+            }
+          }
+        '';
+        "groups.conf".text = ''
+            group "reputation" {
+              symbols = {
+                  "IP_REPUTATION_HAM" {
+                      weight = 1.0;
+                  }
+                  "IP_REPUTATION_SPAM" {
+                      weight = 4.0;
+                  }
+
+                  "DKIM_REPUTATION" {
+                      weight = 1.0;
+                  }
+
+                  "SPF_REPUTATION_HAM" {
+                      weight = 1.0;
+                  }
+                  "SPF_REPUTATION_SPAM" {
+                      weight = 2.0;
+                  }
+
+                  "GENERIC_REPUTATION" {
+                      weight = 1.0;
+                  }
+              }
+          }
+        '';
+
+        "multimap.conf".text =
+          let
+            local_ips = pkgs.writeText "localhost.map" ''
+              ::1
+              127.0.0.1
+            '';
+            tud_ips = pkgs.writeText "tud.map" ''
+              141.30.0.0/16
+              141.76.0.0/16
+            '';
+          in
+          ''
+            WHITELIST_SENDER_DOMAIN {
+              type = "from";
+              filter = "email:domain";
+              map = "/var/lib/rspamd/whitelist.sender.domain.map";
+              action = "accept";
+              regexp = true;
+            }
+            WHITELIST_SENDER_EMAIL {
+              type = "from";
+              map = "/var/lib/rspamd/whitelist.sender.email.map";
+              action = "accept";
+              regexp = true;
+            }
+            BLACKLIST_SENDER_DOMAIN {
+              type = "from";
+              filter = "email:domain";
+              map = "/var/lib/rspamd/blacklist.sender.domain.map";
+              action = "reject";
+              regexp = true;
+            }
+            BLACKLIST_SENDER_EMAIL {
+              type = "from";
+              map = "/var/lib/rspamd/blacklist.sender.email.map";
+              action = "reject";
+              regexp = true;
+            }
+            BLACKLIST_SUBJECT_KEYWORDS {
+              type = "header";
+              header = "Subject"
+              map = "/var/lib/rspamd/blacklist.keyword.subject.map";
+              action = "reject";
+              regexp = true;
+            }
+            RECEIVED_LOCALHOST {
+              type = "ip";
+              action = "accept";
+              map = ${local_ips};
+            }
+            RECEIVED_TU_NETWORKS {
+              type = "ip";
+              map = ${tud_ips};
+            }
+          '';
+      };
+    };
+    redis = {
+      vmOverCommit = true;
+      servers.rspamd = {
+        port = 0;
+        enable = true;
+      };
+    };
+    nginx = {
+      virtualHosts."${domain}" = {
+        locations = {
+          "/" = {
+            proxyPass = "http://127.0.0.1:11334";
+            proxyWebsockets = true;
+            extraConfig = ''
+              allow 141.30.0.0/16;
+              allow 141.76.0.0/16;
+              deny all;
+            '';
+          };
+        };
+      };
+    };
+  };
+  systemd = {
+    services.rspamd-dmarc-report = {
+      description = "rspamd dmarc reporter";
+      serviceConfig = {
+        Type = "oneshot";
+        ExecStart = "${pkgs.rspamd}/bin/rspamadm dmarc_report -v";
+        User = "rspamd";
+        Group = "rspamd";
+      };
+      startAt = "daily";
+    };
+  };
+}
diff --git a/modules/sogo.nix b/modules/mail/sogo.nix
similarity index 96%
rename from modules/sogo.nix
rename to modules/mail/sogo.nix
index 8b2490b..4999dfd 100644
--- a/modules/sogo.nix
+++ b/modules/mail/sogo.nix
@@ -51,9 +51,7 @@ in
       ensureUsers = [
         {
           name = "sogo";
-          ensurePermissions = {
-            "DATABASE sogo" = "ALL PRIVILEGES";
-          };
+          ensureDBOwnership = true;
         }
       ];
       ensureDatabases = [ "sogo" ];
@@ -67,11 +65,7 @@ in
           proxy_buffers   8 64k;
           proxy_buffer_size   64k;
         '';
-        forceSSL = true;
-        enableACME = true;
         locations = {
-
-
           "^~/SOGo".extraConfig = lib.mkForce ''
             proxy_pass http://127.0.0.1:20000;
             proxy_redirect http://127.0.0.1:20000 default;
diff --git a/modules/matrix.nix b/modules/matrix/default.nix
similarity index 86%
rename from modules/matrix.nix
rename to modules/matrix/default.nix
index 4162020..03d58e1 100644
--- a/modules/matrix.nix
+++ b/modules/matrix/default.nix
@@ -1,7 +1,7 @@
 { config, pkgs, ... }:
 let
-  domainServer = "matrix.staging.${config.networking.domain}";
-  domainClient = "chat.staging.${config.networking.domain}";
+  domainServer = "matrix.${config.networking.domain}";
+  domainClient = "chat.${config.networking.domain}";
 
   clientConfig = {
     "m.homeserver" = {
@@ -19,15 +19,17 @@ let
     return 200 '${builtins.toJSON data}';
   '';
 
-  # build ldap3 plugin from git because it's very outdated in nixpkgs
-  matrix-synapse-ldap3 = pkgs.python3.pkgs.callPackage ../pkgs/matrix-synapse-ldap3.nix { };
-  # matrix-synapse-ldap3 = config.services.matrix-synapse.package.plugins.matrix-synapse-ldap3;
+  matrix-synapse-ldap3 = config.services.matrix-synapse.package.plugins.matrix-synapse-ldap3;
 in
 {
+  imports = [ ./mautrix-telegram.nix ];
   sops.secrets.matrix_ldap_search = {
     key = "portunus/search-password";
     owner = config.systemd.services.matrix-synapse.serviceConfig.User;
   };
+  nixpkgs.config.permittedInsecurePackages = [
+    "olm-3.2.16"
+  ];
 
   services = {
     postgresql = {
@@ -42,9 +44,6 @@ in
       virtualHosts = {
         # synapse
         "${domainServer}" = {
-          enableACME = true;
-          forceSSL = true;
-
           # homeserver discovery
           locations."= /.well-known/matrix/client".extraConfig = mkWellKnown clientConfig;
           locations."= /.well-known/matrix/server".extraConfig = mkWellKnown serverConfig;
@@ -59,12 +58,12 @@ in
 
         # element
         "${domainClient}" = {
-          enableACME = true;
-          forceSSL = true;
-
           root = pkgs.element-web.override {
             conf = {
-              default_server_config = clientConfig;
+              default_server_config = {
+                inherit (clientConfig) "m.homeserver";
+                "m.identity_server".base_url = "";
+              };
               disable_3pid_login = true;
             };
           };
@@ -77,6 +76,10 @@ in
 
       plugins = [ matrix-synapse-ldap3 ];
 
+
+      log = {
+        root.level = "WARNING";
+      };
       settings = {
         server_name = domainServer;
 
diff --git a/modules/mautrix-telegram.nix b/modules/matrix/mautrix-telegram.nix
similarity index 93%
rename from modules/mautrix-telegram.nix
rename to modules/matrix/mautrix-telegram.nix
index 2b96d14..270ccc7 100644
--- a/modules/mautrix-telegram.nix
+++ b/modules/matrix/mautrix-telegram.nix
@@ -10,9 +10,7 @@ in
     enable = true;
     ensureUsers = [{
       name = "mautrix-telegram";
-      ensurePermissions = {
-        "DATABASE \"mautrix-telegram\"" = "ALL PRIVILEGES";
-      };
+      ensureDBOwnership = true;
     }];
     ensureDatabases = [ "mautrix-telegram" ];
   };
@@ -46,12 +44,13 @@ in
         # Use postgresql instead of sqlite
         database = "postgresql:///mautrix-telegram?host=/run/postgresql";
         port = 8082;
-        address = "localhost:${toString port}";
+        address = "http://localhost:${toString port}";
       };
 
       bridge = {
         relaybot.authless_portals = false;
         permissions = {
+          # Add yourself here temporarily
           "@admin:${homeserverDomain}" = "admin";
         };
         relay_user_distinguishers = [ ];
diff --git a/modules/monitoring/default.nix b/modules/monitoring/default.nix
new file mode 100644
index 0000000..4601db9
--- /dev/null
+++ b/modules/monitoring/default.nix
@@ -0,0 +1,97 @@
+{ config, ... }:
+let
+  domain = "monitoring.${config.networking.domain}";
+in
+{
+  sops.secrets."grafana/oidc_secret" = {
+    owner = "grafana";
+  };
+  # grafana configuration
+  services.grafana = {
+    enable = true;
+    settings = {
+      server = {
+        inherit domain;
+        http_addr = "127.0.0.1";
+        http_port = 2342;
+        root_url = "https://monitoring.ifsr.de";
+      };
+      database = {
+        type = "postgres";
+        user = "grafana";
+        host = "/run/postgresql";
+      };
+      "auth.generic_oauth" = {
+        enabled = true;
+        name = "iFSR";
+        allow_sign_up = true;
+        client_id = "grafana";
+        client_secret = "$__file{${config.sops.secrets."grafana/oidc_secret".path}}";
+        scopes = "openid email profile offline_access roles";
+
+        email_attribute_path = "email";
+        login_attribute_path = "username";
+        name_attribute_path = "full_name";
+
+        auth_url = "https://sso.ifsr.de/realms/internal/protocol/openid-connect/auth";
+        token_url = "https://sso.ifsr.de/realms/internal/protocol/openid-connect/token";
+        api_url = "https://sso.ifsr.de/realms/internal/protocol/openid-connect/userinfo";
+        role_attribute_path = "contains(roles[*], 'admin') && 'Admin' || contains(roles[*], 'editor') && 'Editor' || 'Viewer'";
+      };
+    };
+  };
+
+  services.postgresql = {
+    enable = true;
+    ensureUsers = [
+      {
+        name = "grafana";
+        ensureDBOwnership = true;
+      }
+    ];
+    ensureDatabases = [ "grafana" ];
+  };
+
+  services.prometheus = {
+    enable = true;
+    port = 9001;
+    exporters = {
+      node = {
+        enable = true;
+        enabledCollectors = [ "systemd" ];
+        port = 9002;
+      };
+    };
+    scrapeConfigs = [
+      {
+        job_name = "node";
+        static_configs = [{
+          targets = [ "127.0.0.1:${toString config.services.prometheus.exporters.node.port}" ];
+        }];
+        scrape_interval = "15s";
+      }
+      {
+        job_name = "rspamd";
+        static_configs = [{
+          targets = [ "rspamd.ifsr.de:11334" ];
+        }];
+        scrape_interval = "15s";
+      }
+      {
+        job_name = "fabric";
+        static_configs = [{
+          targets = [ "127.0.0.1:25585" ];
+        }];
+        scrape_interval = "60s";
+      }
+    ];
+  };
+
+  # nginx reverse proxy
+  services.nginx.virtualHosts.${domain} = {
+    locations."/" = {
+      proxyPass = "http://localhost:${toString config.services.grafana.settings.server.http_port}";
+      proxyWebsockets = true;
+    };
+  };
+}
diff --git a/modules/nextcloud.nix b/modules/nextcloud.nix
index 5c21be4..b2557cc 100644
--- a/modules/nextcloud.nix
+++ b/modules/nextcloud.nix
@@ -1,7 +1,6 @@
 { config, pkgs, lib, ... }:
 let
-  domain = "nc.staging.${config.networking.domain}";
-  legacy_domain = "oc.${config.networking.domain}";
+  domain = "nc.${config.networking.domain}";
 in
 {
   sops.secrets = {
@@ -15,8 +14,8 @@ in
   services = {
     nextcloud = {
       enable = true;
-      package = pkgs.nextcloud25;
-      enableBrokenCiphersForSSE = false; # disable the openssl warning
+      configureRedis = true;
+      package = pkgs.nextcloud30;
       hostName = domain;
       https = true; # Use https for all urls
       phpExtraExtensions = all: [
@@ -29,17 +28,22 @@ in
       };
       # postgres database is configured automatically
       database.createLocally = true;
-    };
 
-    # Enable ACME and force SSL
-    nginx.virtualHosts.${domain} = {
-      enableACME = true;
-      forceSSL = true;
-    };
-    nginx.virtualHosts.${legacy_domain} = {
-      enableACME = true;
-      forceSSL = true;
-      locations."/".return = "301 https://nc.ifsr.de";
+      # enable HEIC image preview
+      settings.enabledPreviewProviders = [
+        "OC\\Preview\\BMP"
+        "OC\\Preview\\GIF"
+        "OC\\Preview\\JPEG"
+        "OC\\Preview\\Krita"
+        "OC\\Preview\\MarkDown"
+        "OC\\Preview\\MP3"
+        "OC\\Preview\\OpenDocument"
+        "OC\\Preview\\PNG"
+        "OC\\Preview\\TXT"
+        "OC\\Preview\\XBitmap"
+        "OC\\Preview\\HEIC"
+      ];
+
     };
   };
 
@@ -74,6 +78,9 @@ in
       preStart = pkgs.writeScript "nextcloud-preStart" ''
         # enable included LDAP app
         ${occ} app:enable user_ldap
+        ${occ} app:enable calendar
+        ${occ} app:enable tasks
+        ${occ} app:enable polls
 
         # set up new LDAP config if it does not exist
         if ! ${occ} ldap:show-config s01 > /dev/null; then
diff --git a/modules/nginx.nix b/modules/nginx.nix
deleted file mode 100644
index 45cb296..0000000
--- a/modules/nginx.nix
+++ /dev/null
@@ -1,39 +0,0 @@
-{ config, pkgs, ... }:
-{
-  services.nginx = {
-
-    additionalModules = [ pkgs.nginxModules.pam ];
-    enable = true;
-    recommendedProxySettings = true;
-    recommendedGzipSettings = true;
-    recommendedOptimisation = true;
-    recommendedTlsSettings = true;
-
-    appendHttpConfig = ''
-      map $remote_addr $remote_addr_anon {
-               ~(?P<ip>\d+\.\d+\.\d+)\.    $ip.0;
-               ~(?P<ip>[^:]+:[^:]+):       $ip::;
-               # IP addresses to not anonymize
-               127.0.0.1                   $remote_addr;
-               ::1                         $remote_addr;
-               default                     0.0.0.0;
-      }
-      log_format  anon_ip   '$remote_addr_anon - $remote_user [$time_local] "$request" '
-                            '$status $body_bytes_sent "$http_referer" '
-                            '"$http_user_agent" "$http_x_forwarded_for"';
-
-      access_log  /var/log/nginx/access.log  anon_ip;
-    '';
-  };
-  security.acme = {
-    acceptTerms = true;
-    defaults = {
-      #server = "https://acme-staging-v02.api.letsencrypt.org/directory";
-      email = "root@${config.networking.domain}";
-    };
-  };
-  security.pam.services.nginx.text = ''
-    auth required ${pkgs.nss_pam_ldapd}/lib/security/pam_ldap.so
-    account required ${pkgs.nss_pam_ldapd}/lib/security/pam_ldap.so
-  '';
-}
diff --git a/modules/nix-serve.nix b/modules/nix-serve.nix
new file mode 100644
index 0000000..643ceb0
--- /dev/null
+++ b/modules/nix-serve.nix
@@ -0,0 +1,18 @@
+{ config, pkgs, ... }:
+let
+  domain = "cache.${config.networking.domain}";
+in
+{
+  sops.secrets."nix-serve/key" = { };
+  services.nix-serve = {
+    enable = true;
+    package = pkgs.nix-serve-ng;
+    secretKeyFile = config.sops.secrets."nix-serve/key".path;
+    port = 5002;
+  };
+  services.nginx.virtualHosts."${domain}" = {
+    locations."/" = {
+      proxyPass = "http://127.0.0.1:${toString config.services.nix-serve.port}";
+    };
+  };
+}
diff --git a/modules/padlist.nix b/modules/padlist.nix
index 53b2557..c7ea438 100644
--- a/modules/padlist.nix
+++ b/modules/padlist.nix
@@ -24,13 +24,7 @@ in
 
   services.nginx = {
     virtualHosts.${domain} = {
-      root = pkgs.callPackage ../pkgs/padlist { };
-      enableACME = true;
-      forceSSL = true;
-      extraConfig = ''
-        auth_pam "LDAP Authentication Required";
-        auth_pam_service_name "nginx";
-      '';
+      root = "/srv/web/padlist";
       locations = {
         "= /" = {
           extraConfig = ''
@@ -41,13 +35,16 @@ in
           extraConfig = ''
             try_files $uri =404;
             fastcgi_pass unix:${config.services.phpfpm.pools.padlist.socket};
+            fastcgi_split_path_info ^(.+\.php)(/.+)$;
             fastcgi_index index.php;
             include ${pkgs.nginx}/conf/fastcgi_params;
             include ${pkgs.nginx}/conf/fastcgi.conf;
+            fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
           '';
         };
+        "/vendor".return = "403";
+        "/.git".return = "403";
       };
     };
   };
-
 }
diff --git a/modules/sops.nix b/modules/sops.nix
deleted file mode 100644
index d34979f..0000000
--- a/modules/sops.nix
+++ /dev/null
@@ -1,5 +0,0 @@
-{ ... }:
-{
-  sops.age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
-  sops.age.generateKey = false;
-}
diff --git a/modules/stream.nix b/modules/stream.nix
index 3227e45..5d36501 100644
--- a/modules/stream.nix
+++ b/modules/stream.nix
@@ -1,15 +1,12 @@
 { config, ... }:
+let cfg = config.services.owncast;
+in
 {
   services = {
     nginx = {
       virtualHosts = {
         "stream.${config.networking.domain}" = {
-          enableACME = true;
-          forceSSL = true;
           locations."/" =
-            let
-              cfg = config.services.owncast;
-            in
             {
               proxyPass = "http://${toString cfg.listen}:${toString cfg.port}";
               proxyWebsockets = true;
@@ -21,8 +18,12 @@
       enable = true;
       port = 13142;
       listen = "[::ffff:127.0.0.1]";
-      openFirewall = true;
       rtmp-port = 1935;
     };
   };
+  networking.firewall = {
+    extraInputRules = ''
+      ip saddr {141.30.0.0/16, 141.76.0.0/16} tcp dport ${toString cfg.rtmp-port} accept comment "Allow rtmp access from campus nets"
+    '';
+  };
 }
diff --git a/modules/struktur-bot.nix b/modules/struktur-bot.nix
new file mode 100644
index 0000000..9773474
--- /dev/null
+++ b/modules/struktur-bot.nix
@@ -0,0 +1,32 @@
+{ config, pkgs, ... }:
+{
+  sops.secrets."strukturbot_env" = { };
+  # virtualisation.docker.daemon.settings.dns = [ "141.30.1.1" "141.76.14.1" ];
+  virtualisation.oci-containers = {
+    containers.struktur-bot = {
+      image = "struktur-bot";
+      environmentFiles = [
+        config.sops.secrets."strukturbot_env".path
+      ];
+      extraOptions = [ "--network=host" ];
+    };
+  };
+  systemd.timers."overleaf-backup" = {
+    wantedBy = [ "timers.target" ];
+    timerConfig = {
+      OnCalendar = "02:22:00";
+      Unit = "overleaf-backup.service";
+    };
+  };
+
+  systemd.services."overleaf-backup" = {
+    script = ''
+      set -eu
+      ${pkgs.docker}/bin/docker exec struktur-bot python3 backup.py
+    '';
+    serviceConfig = {
+      Type = "oneshot";
+      User = "root";
+    };
+  };
+}
diff --git a/modules/vaultwarden.nix b/modules/vaultwarden.nix
index 3ec5e09..a88a7ca 100644
--- a/modules/vaultwarden.nix
+++ b/modules/vaultwarden.nix
@@ -25,16 +25,12 @@ in
     ensureUsers = [
       {
         name = "vaultwarden";
-        ensurePermissions = {
-          "DATABASE vaultwarden" = "ALL PRIVILEGES";
-        };
+        ensureDBOwnership = true;
       }
     ];
     ensureDatabases = [ "vaultwarden" ];
   };
   services.nginx.virtualHosts."${domain}" = {
-    enableACME = true;
-    forceSSL = true;
     locations."/" = {
       proxyPass = "http://127.0.0.1:${toString config.services.vaultwarden.config.rocketPort}";
     };
diff --git a/modules/web/default.nix b/modules/web/default.nix
new file mode 100644
index 0000000..3be7efd
--- /dev/null
+++ b/modules/web/default.nix
@@ -0,0 +1,17 @@
+{ ... }:
+{
+  imports = [
+    ./ifsrde.nix
+    ./ese.nix
+    ./infoscreen.nix
+    ./kpp.nix
+    ./nightline.nix
+    ./fsrewsp.nix
+    ./manual.nix
+    ./sharepic.nix
+    ./userdir.nix
+    ./ftp.nix
+    ./hyperilo.nix
+    ./notenrechner.nix
+  ];
+}
diff --git a/modules/web/ese.nix b/modules/web/ese.nix
new file mode 100644
index 0000000..3929671
--- /dev/null
+++ b/modules/web/ese.nix
@@ -0,0 +1,33 @@
+{ config, pkgs, ... }:
+let
+  domain = "ese.${config.networking.domain}";
+  webRoot = "/srv/web/ese";
+in
+{
+  services.nginx = {
+    virtualHosts."${domain}" = {
+      locations."= /" = {
+        return = "302 /2025/";
+      };
+      locations."/" = {
+        root = webRoot;
+        tryFiles = "$uri $uri/ =404";
+      };
+      # cache static assets
+      locations."~* \.(?:css|svg|webp|jpg|jpeg|gif|png|ico|mp4|mp3|ogg|ogv|webm|ttf|woff2|woff)$" = {
+        root = webRoot;
+        extraConfig = ''
+          expires 1y;
+        '';
+      };
+    };
+  };
+
+  users.users."ese-deploy" = {
+    isNormalUser = true;
+    openssh.authorizedKeys.keys = [
+      ''command="${pkgs.rrsync}/bin/rrsync ${webRoot}",restrict ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEWGdTdobZN2oSLsTQmHOahdc9vqyuwUBS0PSk5IQhGV''
+    ];
+  };
+
+}
diff --git a/modules/web/fsrewsp.nix b/modules/web/fsrewsp.nix
new file mode 100644
index 0000000..5fe4cd3
--- /dev/null
+++ b/modules/web/fsrewsp.nix
@@ -0,0 +1,73 @@
+{ pkgs, config, lib, ... }:
+let
+  domain = "fsrewsp.de";
+  user = "fsrewsp";
+  group = "fsrewsp";
+in
+{
+  users.users.${user} = {
+    group = group;
+    isSystemUser = true;
+  };
+  users.groups.${group} = { };
+  users.users.nginx = {
+    extraGroups = [ group ];
+  };
+
+  services.phpfpm.pools.fsrewsp = {
+    user = "fsrewsp";
+    group = "fsrewsp";
+    settings = {
+      "listen.owner" = config.services.nginx.user;
+      "pm" = "dynamic";
+      "pm.max_children" = 32;
+      "pm.max_requests" = 500;
+      "pm.start_servers" = 2;
+      "pm.min_spare_servers" = 2;
+      "pm.max_spare_servers" = 5;
+      "php_admin_value[error_log]" = "stderr";
+      "php_admin_flag[log_errors]" = true;
+      "catch_workers_output" = true;
+    };
+    phpEnv."PATH" = lib.makeBinPath [ pkgs.php ];
+  };
+
+
+
+  services.nginx.enable = true;
+  services.nginx = {
+    virtualHosts."www.${domain}" = {
+      locations."/".return = "301 $scheme://${domain}$request_uri";
+    };
+    virtualHosts."${domain}" = {
+      root = "/srv/web/fsrewsp";
+      extraConfig = ''
+        index index.php index.html;
+      '';
+
+      locations = {
+        "/" = {
+          tryFiles = "$uri $uri/ /index.php?$args";
+        };
+        "~ \.php$" = {
+          extraConfig = ''
+            try_files $uri =404;
+            fastcgi_pass unix:${config.services.phpfpm.pools.fsrewsp.socket};
+            fastcgi_split_path_info ^(.+\.php)(/.+)$;
+            fastcgi_index index.php;
+            include ${pkgs.nginx}/conf/fastcgi_params;
+            include ${pkgs.nginx}/conf/fastcgi.conf;
+            fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
+            fastcgi_param HTTP_HOST $host;
+          '';
+        };
+        "~ \.log$".return = "403";
+        "~ ^/\.user\.ini".return = "403";
+        "~* \.(js|css|png|jpg|jpeg|gif|ico)$".extraConfig = ''
+          expires max;
+          log_not_found off;
+        '';
+      };
+    };
+  };
+}
diff --git a/modules/web/ftp.nix b/modules/web/ftp.nix
new file mode 100644
index 0000000..7529169
--- /dev/null
+++ b/modules/web/ftp.nix
@@ -0,0 +1,161 @@
+{ config, pkgs, ... }:
+let
+  domain = "ftp.${config.networking.domain}";
+in
+{
+  services.nginx.additionalModules = [ pkgs.nginxModules.fancyindex ];
+  services.nginx.virtualHosts."${domain}" = {
+    root = "/srv/ftp";
+    extraConfig = ''
+      fancyindex on;
+      fancyindex_exact_size off;
+      error_page 403 /403.html;
+      fancyindex_localtime on;
+      charset utf-8;
+    '';
+    locations."~/(klausuren|uebungen|skripte|abschlussarbeiten)".extraConfig = ''
+      allow 141.30.0.0/16;
+      allow 141.76.0.0/16;
+      deny all;
+    '';
+    locations."~ /komplexpruef".extraConfig = ''
+      default_type text/plain;
+    '';
+    locations."=/403.html" = {
+      root = pkgs.writeTextDir "403.html" ''
+        <!DOCTYPE html>
+        <html>
+        <head>
+            <meta charset="UTF-8">
+            <meta name="viewport" content="width=device-width, initial-scale=1.0">
+            <title>403 Forbidden - iFSR</title>
+            <style>
+                body {
+                    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
+                    background-color: #f8f9fa;
+                    margin: 0;
+                    padding: 1rem;
+                    min-height: 100vh;
+                    display: flex;
+                    align-items: center;
+                    justify-content: center;
+                }
+                .container {
+                    background: white;
+                    padding: 2rem;
+                    border-radius: 12px;
+                    box-shadow: 0 2px 15px rgba(0, 0, 0, 0.1);
+                    text-align: center;
+                    max-width: 600px;
+                    width: 100%;
+                }
+                .error-code {
+                    font-size: 3.5rem;
+                    font-weight: bold;
+                    color: #dc3545;
+                    margin: 0;
+                    line-height: 1;
+                }
+                .error-title {
+                    font-size: 1.5rem;
+                    color: #343a40;
+                    margin: 1rem 0;
+                }
+                .error-message {
+                    color: #495057;
+                    margin: 1rem 0;
+                    line-height: 1.6;
+                }
+                .language-section {
+                    padding: 1.5rem;
+                    margin: 1rem 0;
+                    background: #f8f9fa;
+                    border-radius: 8px;
+                    text-align: left;
+                }
+                .language-header {
+                    display: flex;
+                    align-items: center;
+                    gap: 0.5rem;
+                    font-weight: bold;
+                    margin-bottom: 1rem;
+                    color: #343a40;
+                }
+                .help-list {
+                    margin: 0;
+                    padding-left: 1.2rem;
+                    list-style-type: none;
+                }
+                .help-list li {
+                    margin: 0.5rem 0;
+                    position: relative;
+                }
+                .help-list li:before {
+                    content: "•";
+                    position: absolute;
+                    left: -1.2rem;
+                    color: #6c757d;
+                }
+                .logo {
+                    width: 180px;
+                    height: auto;
+                    margin-bottom: 1.5rem;
+                }
+                @media (max-width: 480px) {
+                    .container {
+                        padding: 1.5rem;
+                    }
+                    .language-section {
+                        padding: 1rem;
+                        margin: 0.5rem 0;
+                    }
+                    .error-code {
+                        font-size: 3rem;
+                    }
+                    .error-title {
+                        font-size: 1.25rem;
+                    }
+                    .logo {
+                        width: 150px;
+                    }
+                }
+            </style>
+        </head>
+        <body>
+            <div class="container">
+                <img src="https://ifsr.de/user/themes/ifsr/images/logo.svg" alt="iFSR Logo" class="logo">
+                <h1 class="error-code">403</h1>
+                <h2 class="error-title">Zugriff verweigert / Access Forbidden</h2>
+                
+                <div class="language-section">
+                    <div class="language-header">
+                        🇩🇪 Deutsch
+                    </div>
+                    <p class="error-message">
+                        Dieser Ordner ist nur aus dem Uni-Netz zugänglich.
+                    </p>
+                    <ul class="help-list">
+                        <li>Stellen Sie sicher, dass Sie mit dem TUD-Netzwerk verbunden sind</li>
+                        <li>Oder wählen Sie sich über VPN ein</li>
+                    </ul>
+                </div>
+
+                <div class="language-section">
+                    <div class="language-header">
+                        🇬🇧 English
+                    </div>
+                    <p class="error-message">
+                        This directory is only accessible from the TUD network.
+                    </p>
+                    <ul class="help-list">
+                        <li>Make sure you are connected to the TUD network</li>
+                        <li>Or connect via VPN</li>
+                    </ul>
+                </div>
+            </div>
+        </body>
+        </html>
+      '';
+    };
+  };
+}
diff --git a/modules/web/hyperilo.nix b/modules/web/hyperilo.nix
new file mode 100644
index 0000000..fd46958
--- /dev/null
+++ b/modules/web/hyperilo.nix
@@ -0,0 +1,35 @@
+{ ... }:
+
+{
+  # provide access to iLO of colocated server
+  # in case of questions, contact @bennofs
+  services.nginx.virtualHosts."hyperilo.deutschland.gmbh" = {
+    forceSSL = true;
+    locations."/".proxyPass = "https://192.168.0.120:443";
+    locations."/".basicAuthFile = "/run/secrets/hyperilo_htaccess";
+    locations."/".extraConfig = ''
+      proxy_ssl_verify off;
+      proxy_http_version 1.1;
+      proxy_set_header Upgrade $http_upgrade;
+      proxy_set_header Connection $connection_upgrade_capitalized;
+      proxy_set_header Authorization ""; # drop the basic auth headers, otherwise remote console doesn't work
+    '';
+  };
+
+  # HP iLO requires uppercase Upgrade, not lowercase "upgrade"
+  services.nginx.commonHttpConfig = ''
+    map $http_upgrade $connection_upgrade_capitalized {
+      default  Upgrade;
+      '''      close;
+    }
+  '';
+
+  systemd.network.networks."20-hyperilo" = {
+    matchConfig.Name = "eno8303";
+    address = [ "192.168.0.1/24" ];
+    networkConfig.LLDP = true;
+    networkConfig.EmitLLDP = "nearest-bridge";
+  };
+
+  sops.secrets."hyperilo_htaccess".owner = "nginx";
+}
diff --git a/modules/website.nix b/modules/web/ifsrde.nix
similarity index 92%
rename from modules/website.nix
rename to modules/web/ifsrde.nix
index d6e8339..84c4ad1 100644
--- a/modules/website.nix
+++ b/modules/web/ifsrde.nix
@@ -10,7 +10,9 @@ in
     isSystemUser = true;
   };
   users.groups.${group} = { };
-
+  users.users.nginx = {
+    extraGroups = [ group ];
+  };
   services.phpfpm.pools.ifsrde = {
     user = user;
     group = group;
@@ -32,14 +34,9 @@ in
   services.nginx = {
 
     virtualHosts."www.${config.networking.domain}" = {
-      enableACME = true;
-      forceSSL = true;
       locations."/".return = "301 $scheme://ifsr.de$request_uri";
-
     };
     virtualHosts."${config.networking.domain}" = {
-      enableACME = true;
-      forceSSL = true;
       root = "/srv/web/ifsrde";
       extraConfig = ''
         index index.html index.php;
@@ -63,6 +60,8 @@ in
         "~ ^/cmd(/?[^\\n|\\r]*)$".return = "301 https://pad.ifsr.de$1";
         "/bbb".return = "301 https://bbb.tu-dresden.de/b/fsr-58o-tmf-yy6";
         "/kpp".return = "301 https://kpp.ifsr.de";
+        "/mese".return = "301 https://ifsr.de/news/mese-and-welcome-back";
+        "/sso".return = "301 https://sso.ifsr.de/realms/internal/account";
         # security
         "~* /(\.git|cache|bin|logs|backup|tests)/.*$".return = "403";
         # deny running scripts inside core system folders
diff --git a/modules/web/infoscreen.nix b/modules/web/infoscreen.nix
new file mode 100644
index 0000000..601d0e2
--- /dev/null
+++ b/modules/web/infoscreen.nix
@@ -0,0 +1,12 @@
+{ config, ... }:
+let
+  domain = "infoscreen.${config.networking.domain}";
+in
+{
+  services.nginx = {
+    enable = true;
+    virtualHosts."${domain}" = {
+      root = "/srv/web/infoscreen/dist";
+    };
+  };
+}
diff --git a/modules/kpp.nix b/modules/web/kpp.nix
similarity index 59%
rename from modules/kpp.nix
rename to modules/web/kpp.nix
index 262da76..bf30ec2 100644
--- a/modules/kpp.nix
+++ b/modules/web/kpp.nix
@@ -7,9 +7,4 @@ in
     enable = true;
     hostName = domain;
   };
-  services.nginx.virtualHosts."${domain}" = {
-    enableACME = true;
-    forceSSL = true;
-  };
-
 }
diff --git a/modules/web/manual.nix b/modules/web/manual.nix
new file mode 100644
index 0000000..afcc154
--- /dev/null
+++ b/modules/web/manual.nix
@@ -0,0 +1,10 @@
+{ config, ... }:
+let
+  domain = "manual.${config.networking.domain}";
+in
+{
+  services.ese-manual = {
+    enable = true;
+    hostName = domain;
+  };
+}
diff --git a/modules/web/nightline.nix b/modules/web/nightline.nix
new file mode 100644
index 0000000..8abd76d
--- /dev/null
+++ b/modules/web/nightline.nix
@@ -0,0 +1,70 @@
+{ pkgs, config, lib, ... }:
+let
+  domain = "nightline-dresden.de";
+  user = "nightline";
+  group = "nightline";
+in
+{
+  users.users.${user} = {
+    group = group;
+    isSystemUser = true;
+  };
+  users.users.nginx = {
+    extraGroups = [ group ];
+  };
+  users.groups.${group} = { };
+
+  services.phpfpm.pools.nightline = {
+    user = "nightline";
+    group = "nightline";
+    settings = {
+      "listen.owner" = config.services.nginx.user;
+      "pm" = "dynamic";
+      "pm.max_children" = 32;
+      "pm.max_requests" = 500;
+      "pm.start_servers" = 2;
+      "pm.min_spare_servers" = 2;
+      "pm.max_spare_servers" = 5;
+      "php_admin_value[error_log]" = "stderr";
+      "php_admin_flag[log_errors]" = true;
+      "catch_workers_output" = true;
+    };
+    phpEnv."PATH" = lib.makeBinPath [ pkgs.php ];
+  };
+
+  services.nginx = {
+    virtualHosts."www.${domain}" = {
+      locations."/".return = "301 $scheme://${domain}$request_uri";
+    };
+    virtualHosts."${domain}" = {
+      root = "/srv/web/nightline";
+      extraConfig = ''
+        index index.php index.html;
+      '';
+
+      locations = {
+        "/" = {
+          tryFiles = "$uri $uri/ /index.php?$args";
+        };
+        "~ \.php$" = {
+          extraConfig = ''
+            try_files $uri =404;
+            fastcgi_pass unix:${config.services.phpfpm.pools.nightline.socket};
+            fastcgi_split_path_info ^(.+\.php)(/.+)$;
+            fastcgi_index index.php;
+            include ${pkgs.nginx}/conf/fastcgi_params;
+            include ${pkgs.nginx}/conf/fastcgi.conf;
+            fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
+            fastcgi_param HTTP_HOST $host;
+          '';
+        };
+        "~ \.log$".return = "403";
+        "~ ^/\.user\.ini".return = "403";
+        "~* \.(js|css|png|jpg|jpeg|gif|ico)$".extraConfig = ''
+          expires max;
+          log_not_found off;
+        '';
+      };
+    };
+  };
+}
diff --git a/modules/web/notenrechner.nix b/modules/web/notenrechner.nix
new file mode 100644
index 0000000..06d4d05
--- /dev/null
+++ b/modules/web/notenrechner.nix
@@ -0,0 +1,9 @@
+{ config, specialArgs, ... }:
+let
+  domain = "notenrechner.${config.networking.domain}";
+in
+{
+  services.nginx.virtualHosts."${domain}" = {
+    root = specialArgs.notenrechner.packages."x86_64-linux".default;
+  };
+}
diff --git a/modules/web/sharepic.nix b/modules/web/sharepic.nix
new file mode 100644
index 0000000..6c9e597
--- /dev/null
+++ b/modules/web/sharepic.nix
@@ -0,0 +1,14 @@
+{ pkgs, config, ... }:
+let
+  domain = "sharepic.${config.networking.domain}";
+in
+{
+  services.nginx.virtualHosts."${domain}" = {
+    root = pkgs.fetchFromGitHub {
+      owner = "jannikmenzel";
+      repo = "iFSR-Sharepicgenerator";
+      rev = "ac721d5fff2dba1f046939a6d6532b1a8cfceba8";
+      hash = "sha256-of+N58TDt2BcbDVEriKn6rjQVl0GdV4ZMEblrdUutZk=";
+    };
+  };
+}
diff --git a/modules/userdir.nix b/modules/web/userdir.nix
similarity index 63%
rename from modules/userdir.nix
rename to modules/web/userdir.nix
index 16a8d27..25f5bfa 100644
--- a/modules/userdir.nix
+++ b/modules/web/userdir.nix
@@ -1,4 +1,4 @@
-{ config, ... }:
+{ config, pkgs, ... }:
 let
   domain = "users.${config.networking.domain}";
   port = 8083;
@@ -18,18 +18,21 @@ in
 
       mkdir -p $HOME/public_html
       # public_html dir: apache and $USER have rwx on everything inside
-      setfacl -m u:${apacheUser}:rwx,d:u:${apacheUser}:rwx,d:u:$USER:rwx $HOME/public_html
+      setfacl -m u:${apacheUser}:rwx,d:u:${apacheUser}:rwx,d:u:''${USER}:rwx $HOME/public_html
     fi
   '';
 
   services.httpd = {
     enable = true;
     enablePHP = true;
+    maxClients = 10;
+    mpm = "prefork";
+    extraModules = [ "userdir" ];
 
     virtualHosts.${domain} = {
-      enableUserDir = true;
       extraConfig = ''
-        UserDir /home/users/*/public_html
+        UserDir disabled root
+        UserDir /home/users/*/public_html/
         <Directory "/home/users/*/public_html">
           Options -Indexes +MultiViews +SymLinksIfOwnerMatch +IncludesNoExec
           DirectoryIndex index.php index.html
@@ -47,14 +50,33 @@ in
         inherit port;
       }];
     };
+
+    phpPackage = pkgs.php.buildEnv {
+      extraConfig = ''
+        display_errors=0
+        post_max_size = 40M
+        upload_max_filesize = 40M
+        extension=sysvsem.so
+      '';
+    };
   };
 
   services.nginx.virtualHosts.${domain} = {
-    enableACME = true;
-    forceSSL = true;
-
     locations."/" = {
       proxyPass = "http://localhost:${toString port}";
+      extraConfig = ''
+        proxy_intercept_errors on;
+        error_page 403 404 =404 /404.html;
+        client_max_body_size 40M;
+      '';
     };
+
+    locations."/robots.txt" = {
+      extraConfig = ''
+        add_header  Content-Type  text/plain;
+        return 200 "User-agent: *\nDisallow: /\n";
+      '';
+    };
+
   };
 }
diff --git a/modules/wiki/default.nix b/modules/wiki/default.nix
new file mode 100644
index 0000000..372553e
--- /dev/null
+++ b/modules/wiki/default.nix
@@ -0,0 +1,8 @@
+{ ... }:
+{
+  imports = [
+    ./fsr.nix
+    ./vernetzung.nix
+    ./ese.nix
+  ];
+}
diff --git a/modules/wiki/ese.nix b/modules/wiki/ese.nix
new file mode 100644
index 0000000..4125198
--- /dev/null
+++ b/modules/wiki/ese.nix
@@ -0,0 +1,86 @@
+{ config, lib, pkgs, ... }:
+let
+  domain = "wiki.ese.${config.networking.domain}";
+  user = "wiki-ese";
+  group = "wiki-ese";
+in
+{
+
+  system.activationScripts.hacky-mediawiki-convert = ''
+    cp ${pkgs.imagemagick}/bin/convert /srv/web/wiki.ese/convert
+  '';
+  users.users.${user} = {
+    group = group;
+    isSystemUser = true;
+  };
+  users.groups.${group} = { };
+  services.phpfpm.pools.wiki-ese = {
+    user = user;
+    group = group;
+    settings = {
+      "listen.owner" = config.services.nginx.user;
+      "pm" = "dynamic";
+      "pm.max_children" = 32;
+      "pm.max_requests" = 500;
+      "pm.start_servers" = 2;
+      "pm.min_spare_servers" = 2;
+      "pm.max_spare_servers" = 5;
+      "php_admin_value[error_log]" = "stderr";
+      "php_admin_flag[log_errors]" = true;
+      "catch_workers_output" = true;
+    };
+    phpEnv."PATH" = lib.makeBinPath [ pkgs.php ];
+  };
+  services.nginx = {
+    virtualHosts."${domain}" = {
+      root = "/srv/web/wiki.ese";
+      extraConfig = ''
+        index index.php;
+      '';
+      locations = {
+        "/" = {
+          tryFiles = "$uri $uri/ @rewrite";
+        };
+        "@rewrite".extraConfig = ''
+          rewrite ^/(.*)$ /index.php?title=$1&$args;
+        '';
+        "^~ /maintenance/".return = "403";
+        "~ \.php$" = {
+          extraConfig = ''
+            fastcgi_pass unix:${config.services.phpfpm.pools.wiki-ese.socket};
+            fastcgi_split_path_info ^(.+\.php)(/.+)$;
+            fastcgi_index index.php;
+            include ${pkgs.nginx}/conf/fastcgi_params;
+            include ${pkgs.nginx}/conf/fastcgi.conf;
+            fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
+          '';
+        };
+        "/rest.php" = {
+          tryFiles = "$uri $uri/ /rest.php?$args";
+        };
+        "~* \.(js|css|png|jpg|jpeg|gif|ico)$" = {
+          tryFiles = "$uri /index.php";
+          extraConfig = ''
+            expires max;
+            log_not_found off;
+          '';
+        };
+        "/_.gif" = {
+          extraConfig = ''
+            expires max;
+            empty_gif;
+          '';
+        };
+        "^~ /cache/".extraConfig = ''
+          deny all;
+        '';
+        "/dumps" = {
+          root = "/srv/web/wiki-ese/local";
+          extraConfig = ''
+            autoindex on;
+          '';
+        };
+      };
+    };
+  };
+}
diff --git a/modules/wiki.nix b/modules/wiki/fsr.nix
similarity index 70%
rename from modules/wiki.nix
rename to modules/wiki/fsr.nix
index 1c82037..ac16e41 100644
--- a/modules/wiki.nix
+++ b/modules/wiki/fsr.nix
@@ -38,6 +38,7 @@ in
       };
 
       extraConfig = ''
+        wfLoadSkin( 'MinervaNeue' );
         $wgSitename = "FSR Wiki";
         $wgArticlePath = '/$1';
 
@@ -57,49 +58,54 @@ in
         $wgUseAjax = true;
         $wgEnableMWSuggest = true;
         $wgDefaultSkin = 'timeless';
+        $wgDefaultMobileSkin = 'minerva';
 
         //TODO what about $wgUpgradeKey ?
 
         # Auth
         # https://www.mediawiki.org/wiki/Extension:PluggableAuth
         # https://www.mediawiki.org/wiki/Extension:OpenID_Connect
-        $wgPluggableAuth_EnableLocalLogin = true;
+        $wgOpenIDConnect_MigrateUsersByEmail = true;
+        //$wgOpenIDConnect_MigrateUsersByUserName = true;
+        $wgPluggableAuth_EnableLocalLogin = false;
         $wgPluggableAuth_Config["iFSR Login"] = [
           "plugin" => "OpenIDConnect",
           "data" => [
-            "providerURL" => "${config.services.portunus.domain}/dex",
+            "providerURL" => "https://sso.ifsr.de/realms/internal",
             "clientID" => "wiki",
             "clientsecret" => file_get_contents('${config.sops.secrets."mediawiki/oidc_secret".path}'),
           ],
         ];
       '';
-
       extensions = {
+        # some extensions are included and can enabled by passing null
+        VisualEditor = null;
+        # the dir in the mediawiki-1.42.3.tar.gz inside of the extension folder is called "SyntaxHighlight_GeSHi" not "SyntaxHighlight"
+        SyntaxHighlight_GeSHi = null;
+        MobileFrontend = pkgs.fetchzip {
+          url = "https://extdist.wmflabs.org/dist/extensions/MobileFrontend-REL1_43-3b4cac8.tar.gz";
+          hash = "sha256-aJOArZl+oO/ADjxIhlFVGS8hGmpSp6nsgC7XkKEk1Ks=";
+        };
         PluggableAuth = pkgs.fetchzip {
-          url = "https://web.archive.org/web/20230615112924/https://extdist.wmflabs.org/dist/extensions/PluggableAuth-REL1_39-068be5d.tar.gz";
-          hash = "sha256-kmdSPMQNaO0qgEzb8j0+eLlsNQLmfJfo0Ls4yvYgOFI=";
+          url = "https://extdist.wmflabs.org/dist/extensions/PluggableAuth-REL1_42-1da98f4.tar.gz";
+          hash = "sha256-5uBUy7lrr86ApASYPWgF6Wa09mxxP0o+lXLt1gVswlA=";
         };
         OpenIDConnect = pkgs.fetchzip {
-          url = "https://web.archive.org/web/20230615113527/https://extdist.wmflabs.org/dist/extensions/OpenIDConnect-REL1_39-42e4d75.tar.gz";
-          hash = "sha256-VN0G0Crjlx0DTLeDvaSFtMmYsfB7VzgYkSNDS+nkIyQ=";
-        };
-        VisualEditor = pkgs.fetchzip {
-          url = "https://web.archive.org/web/20230723212424/https://extdist.wmflabs.org/dist/extensions/VisualEditor-REL1_39-b1204c9.tar.gz";
-          hash = "sha256-g/ATW3xkecHynwbwLbmYgawNW+LCVTth0ZlhY7A3N5U=";
+          url = "https://extdist.wmflabs.org/dist/extensions/OpenIDConnect-REL1_42-6c28c16.tar.gz";
+          hash = "sha256-X5kUuvxINbuXaLMKRcLOl2L3qbnMT72lg2NA3A9Daj8=";
         };
       };
     };
 
-    portunus.dex.oidcClients = [{
-      id = "wiki";
-      callbackURL = "https://${domain}/Spezial:PluggableAuthLogin";
-    }];
-
     nginx = {
       recommendedProxySettings = true;
       virtualHosts.${domain} = {
-        enableACME = true;
-        forceSSL = true;
+        locations."/robots.txt" = {
+          extraConfig = ''
+            add_header  Content-Type  text/plain;
+            return 200 "User-agent: *\nDisallow: /\n";
+          '';
+        };
         locations."/" = {
           proxyPass = "http://127.0.0.1:${toString listenPort}";
           proxyWebsockets = true;
diff --git a/modules/wiki/vernetzung.nix b/modules/wiki/vernetzung.nix
new file mode 100644
index 0000000..bd1a4a9
--- /dev/null
+++ b/modules/wiki/vernetzung.nix
@@ -0,0 +1,83 @@
+{ config, lib, pkgs, ... }:
+let
+  domain = "vernetzung.${config.networking.domain}";
+  user = "vernetzung";
+  group = "vernetzung";
+in
+{
+
+  users.users.${user} = {
+    group = group;
+    isSystemUser = true;
+  };
+  users.groups.${group} = { };
+  services.phpfpm.pools.vernetzung = {
+    user = user;
+    group = group;
+    settings = {
+      "listen.owner" = config.services.nginx.user;
+      "pm" = "dynamic";
+      "pm.max_children" = 32;
+      "pm.max_requests" = 500;
+      "pm.start_servers" = 2;
+      "pm.min_spare_servers" = 2;
+      "pm.max_spare_servers" = 5;
+      "php_admin_value[error_log]" = "stderr";
+      "php_admin_flag[log_errors]" = true;
+      "catch_workers_output" = true;
+    };
+    phpEnv."PATH" = lib.makeBinPath [ pkgs.php ];
+  };
+  services.nginx = {
+    virtualHosts."${domain}" = {
+      root = "/srv/web/vernetzung";
+      extraConfig = ''
+        index index.php;
+      '';
+      locations = {
+        "/" = {
+          tryFiles = "$uri $uri/ @rewrite";
+        };
+        "@rewrite".extraConfig = ''
+          rewrite ^/(.*)$ /index.php?title=$1&$args;
+        '';
+        "^~ /maintenance/".return = "403";
+        "~ \.php$" = {
+          extraConfig = ''
+            fastcgi_pass unix:${config.services.phpfpm.pools.vernetzung.socket};
+            fastcgi_split_path_info ^(.+\.php)(/.+)$;
+            fastcgi_index index.php;
+            include ${pkgs.nginx}/conf/fastcgi_params;
+            include ${pkgs.nginx}/conf/fastcgi.conf;
+            fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
+          '';
+        };
+        "/rest.php" = {
+          tryFiles = "$uri $uri/ /rest.php?$args";
+        };
+        "~* \.(js|css|png|jpg|jpeg|gif|ico)$" = {
+          tryFiles = "$uri /index.php";
+          extraConfig = ''
+            expires max;
+            log_not_found off;
+          '';
+        };
+        "/_.gif" = {
+          extraConfig = ''
+            expires max;
+            empty_gif;
+          '';
+        };
+        "^~ /cache/".extraConfig = ''
+          deny all;
+        '';
+        "/dumps" = {
+          root = "/srv/web/vernetzung/local";
+          extraConfig = ''
+            autoindex on;
+          '';
+        };
+      };
+    };
+  };
+}
diff --git a/modules/zammad.nix b/modules/zammad.nix
new file mode 100644
index 0000000..cbff484
--- /dev/null
+++ b/modules/zammad.nix
@@ -0,0 +1,40 @@
+{ config, lib, ... }:
+let
+  domain = "tickets.${config.networking.domain}";
+in
+{
+  services.zammad = {
+    enable = true;
+    database = {
+      createLocally = true;
+      type = "PostgreSQL";
+    };
+    redis.port = 6380;
+    port = 8085;
+    secretKeyBaseFile = config.sops.secrets."zammad_secret".path;
+  };
+
+
+    services.redis = {
+      servers.zammad = {
+        port = lib.mkForce 6380;
+        enable = true;
+      };
+    };
+  # disably spammy logs
+  systemd.services.zammad-web.preStart = ''
+    sed -i -e "s|debug|warn|" ./config/environments/production.rb 
+  '';
+
+  services.nginx.virtualHosts.${domain} = {
+    locations."/" = {
+      proxyPass = "http://localhost:${toString config.services.zammad.port}";
+    };
+    locations."/ws" = {
+      proxyPass = "http://localhost:${toString config.services.zammad.websocketPort}";
+      proxyWebsockets = true;
+    };
+  };
+
+  sops.secrets."zammad_secret".owner = "zammad";
+}
diff --git a/modules/zsh.nix b/modules/zsh.nix
deleted file mode 100644
index bb3ac04..0000000
--- a/modules/zsh.nix
+++ /dev/null
@@ -1,36 +0,0 @@
-{ pkgs, ... }:
-{
-  environment.systemPackages = with pkgs; [
-    # fzf
-    bat
-    duf
-  ];
-  users.defaultUserShell = pkgs.zsh;
-  programs.fzf = {
-    fuzzyCompletion = true;
-    keybindings = true;
-  };
-  programs.zsh = {
-    enable = true;
-    shellAliases = {
-      l = "ls -l";
-      ll = "ls -la";
-      la = "ls -a";
-      less = "bat";
-    };
-    histSize = 100000;
-    histFile = "~/.local/share/zsh/history";
-    autosuggestions = {
-      enable = true;
-      highlightStyle = "fg=#00bbbb,bold";
-    };
-
-    shellInit =
-      ''
-        source ${pkgs.zsh-fzf-tab}/share/fzf-tab/fzf-tab.plugin.zsh
-
-        zsh-newuser-install () {}
-      '';
-  };
-}
-
diff --git a/modules/options.nix b/options/default.nix
similarity index 100%
rename from modules/options.nix
rename to options/default.nix
diff --git a/overlays/default.nix b/overlays/default.nix
new file mode 100644
index 0000000..3d7b533
--- /dev/null
+++ b/overlays/default.nix
@@ -0,0 +1,44 @@
+_final: prev:
+let
+  inherit (prev) fetchurl;
+  inherit (prev) fetchpatch;
+  inherit (prev) callPackage;
+in
+{
+  # AGDSN is running an outdated version that we have to comply to
+  bacula = (prev.bacula.overrideAttrs (old: rec {
+    version = "9.6.7";
+    src = fetchurl {
+      url = "mirror://sourceforge/bacula/${old.pname}-${version}.tar.gz";
+      sha256 = "sha256-3w+FJezbo4DnS1N8pxrfO3WWWT8CGJtZqw6//IXMyN4=";
+    };
+  }));
+  # Mailman internal server error fix
+  # https://gitlab.com/mailman/mailman/-/issues/1137
+  # https://github.com/NixOS/nixpkgs/pull/321136
+  pythonPackagesExtensions = prev.pythonPackagesExtensions ++ [
+    (_python-final: python-prev: {
+      readme-renderer = python-prev.readme-renderer.overridePythonAttrs (_oldAttrs: {
+        propagatedBuildInputs = [ python-prev.cmarkgfm ];
+      });
+    })
+  ];
+
+  keycloak_ifsr_theme = callPackage ../modules/keycloak/theme.nix { };
+  portunus = callPackage ./portunus.nix { };
+  mediawiki = (prev.mediawiki.overrideAttrs (_old: rec {
+    version = "1.43.0";
+
+    src = fetchurl {
+      url = "https://releases.wikimedia.org/mediawiki/${prev.lib.versions.majorMinor version}/mediawiki-${version}.tar.gz";
+      hash = "sha256-VuCn/i/3jlC5yHs9WJ8tjfW8qwAY5FSypKI5yFhr2O4=";
+    };
+
+  }));
+
+  hedgedoc = prev.hedgedoc.overrideAttrs ({ patches ? [ ], ... }: {
+    patches = patches ++ [
+      ./hedgedoc/0001-anonymous-uploads.patch
+    ];
+  });
+}
diff --git a/overlays/hedgedoc/0001-anonymous-uploads.patch b/overlays/hedgedoc/0001-anonymous-uploads.patch
new file mode 100644
index 0000000..83eead2
--- /dev/null
+++ b/overlays/hedgedoc/0001-anonymous-uploads.patch
@@ -0,0 +1,62 @@
+diff --git a/app.js b/app.js
+index d41dbfbd7..faf686cfa 100644
+--- a/app.js
++++ b/app.js
+@@ -203,6 +203,7 @@ app.locals.serverURL = config.serverURL
+ app.locals.sourceURL = config.sourceURL
+ app.locals.allowAnonymous = config.allowAnonymous
+ app.locals.allowAnonymousEdits = config.allowAnonymousEdits
++app.locals.allowAnonymousUploads = config.allowAnonymousUploads
+ app.locals.disableNoteCreation = config.disableNoteCreation
+ app.locals.authProviders = {
+   facebook: config.isFacebookEnable,
+diff --git a/lib/config/default.js b/lib/config/default.js
+index d038e5311..9ab9a6bb1 100644
+--- a/lib/config/default.js
++++ b/lib/config/default.js
+@@ -33,6 +33,7 @@ module.exports = {
+   protocolUseSSL: false,
+   allowAnonymous: true,
+   allowAnonymousEdits: false,
++  allowAnonymousUploads: false,
+   allowFreeURL: false,
+   requireFreeURLAuthentication: false,
+   disableNoteCreation: false,
+diff --git a/lib/config/environment.js b/lib/config/environment.js
+index da50a660d..b74d122f4 100644
+--- a/lib/config/environment.js
++++ b/lib/config/environment.js
+@@ -31,6 +31,7 @@ module.exports = {
+   allowOrigin: toArrayConfig(process.env.CMD_ALLOW_ORIGIN),
+   allowAnonymous: toBooleanConfig(process.env.CMD_ALLOW_ANONYMOUS),
+   allowAnonymousEdits: toBooleanConfig(process.env.CMD_ALLOW_ANONYMOUS_EDITS),
++  allowAnonymousUploads: toBooleanConfig(process.env.CMD_ALLOW_ANONYMOUS_UPLOADS),
+   allowFreeURL: toBooleanConfig(process.env.CMD_ALLOW_FREEURL),
+   requireFreeURLAuthentication: toBooleanConfig(process.env.CMD_REQUIRE_FREEURL_AUTHENTICATION),
+   disableNoteCreation: toBooleanConfig(process.env.CMD_DISABLE_NOTE_CREATION),
+diff --git a/lib/config/hackmdEnvironment.js b/lib/config/hackmdEnvironment.js
+index c40ffc961..20c2da83b 100644
+--- a/lib/config/hackmdEnvironment.js
++++ b/lib/config/hackmdEnvironment.js
+@@ -22,6 +22,7 @@ module.exports = {
+   allowOrigin: toArrayConfig(process.env.HMD_ALLOW_ORIGIN),
+   allowAnonymous: toBooleanConfig(process.env.HMD_ALLOW_ANONYMOUS),
+   allowAnonymousEdits: toBooleanConfig(process.env.HMD_ALLOW_ANONYMOUS_EDITS),
++  allowAnonymousUploads: toBooleanConfig(process.env.HMD_ALLOW_ANONYMOUS_UPLOADS),
+   allowFreeURL: toBooleanConfig(process.env.HMD_ALLOW_FREEURL),
+   defaultPermission: process.env.HMD_DEFAULT_PERMISSION,
+   dbURL: process.env.HMD_DB_URL,
+diff --git a/lib/web/imageRouter/index.js b/lib/web/imageRouter/index.js
+index d9964827b..7321bc805 100644
+--- a/lib/web/imageRouter/index.js
++++ b/lib/web/imageRouter/index.js
+@@ -59,8 +59,7 @@ async function checkUploadType (filePath) {
+ imageRouter.post('/uploadimage', function (req, res) {
+   if (
+     !req.isAuthenticated() &&
+-    !config.allowAnonymous &&
+-    !config.allowAnonymousEdits
++    !config.allowAnonymousUploads
+   ) {
+     logger.error(
+       'Image upload error: Anonymous edits and therefore uploads are not allowed'
diff --git a/overlays/portunus.nix b/overlays/portunus.nix
new file mode 100644
index 0000000..ac09a9b
--- /dev/null
+++ b/overlays/portunus.nix
@@ -0,0 +1,32 @@
+{ lib
+, buildGoModule
+, fetchFromGitHub
+, libxcrypt-legacy
+, nixosTests
+}:
+
+buildGoModule rec {
+  pname = "portunus";
+  version = "2.1.1";
+
+  src = fetchFromGitHub {
+    owner = "majewsky";
+    repo = "portunus";
+    rev = "v${version}";
+    sha256 = "sha256-+pMMIutj+OWKZmOYH5NuA4a7aS5CD+33vAEC9bJmyfM=";
+  };
+
+  buildInputs = [ libxcrypt-legacy ];
+
+  vendorHash = null;
+
+  passthru.tests = { inherit (nixosTests) portunus; };
+
+  meta = with lib; {
+    description = "Self-contained user/group management and authentication service";
+    homepage = "https://github.com/majewsky/portunus";
+    license = licenses.gpl3Plus;
+    platforms = platforms.linux;
+    maintainers = with maintainers; [ majewsky ] ++ teams.c3d2.members;
+  };
+}
diff --git a/overlays/prometheus-postfix-exporter/0001-cleanup-also-catch-milter-reject.patch b/overlays/prometheus-postfix-exporter/0001-cleanup-also-catch-milter-reject.patch
new file mode 100644
index 0000000..2b60316
--- /dev/null
+++ b/overlays/prometheus-postfix-exporter/0001-cleanup-also-catch-milter-reject.patch
@@ -0,0 +1,25 @@
+From f4c5dd5628c873981b2d6d6b8f3bbf036b9fd724 Mon Sep 17 00:00:00 2001
+From: Rouven Seifert <rouven.seifert@ifsr.de>
+Date: Thu, 2 May 2024 11:20:27 +0200
+Subject: [PATCH] cleanup: also catch milter-reject
+
+---
+ postfix_exporter.go | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/postfix_exporter.go b/postfix_exporter.go
+index f20d99c..676d767 100644
+--- a/postfix_exporter.go
++++ b/postfix_exporter.go
+@@ -335,6 +335,8 @@ func (e *PostfixExporter) CollectFromLogLine(line string) {
+ 				e.cleanupProcesses.Inc()
+ 			} else if strings.Contains(remainder, ": reject: ") {
+ 				e.cleanupRejects.Inc()
++			} else if strings.Contains(remainder, ": milter-reject: ") {
++				e.cleanupRejects.Inc()
+ 			} else {
+ 				e.addToUnsupportedLine(line, subprocess, level)
+ 			}
+-- 
+2.44.0
+
diff --git a/pkgs/matrix-synapse-ldap3.nix b/pkgs/matrix-synapse-ldap3.nix
deleted file mode 100644
index 0635ab0..0000000
--- a/pkgs/matrix-synapse-ldap3.nix
+++ /dev/null
@@ -1,21 +0,0 @@
-{ isPy3k, buildPythonPackage, pkgs, service-identity, ldap3, twisted, ldaptor, mock }:
-
-buildPythonPackage rec {
-  pname = "matrix-synapse-ldap3";
-  version = "0.2.2";
-
-  format = "pyproject";
-
-  src = pkgs.fetchFromGitHub {
-    owner = "matrix-org";
-    repo = "matrix-synapse-ldap3";
-    rev = "2584736204165f16c176567183f9c350ee253f74";
-    sha256 = "gMsC5FpC2zt5hypPdGgPbWT/Rwz38EoQz3tj5dQ9BQ8=";
-  };
-
-  propagatedBuildInputs = [ service-identity ldap3 twisted ];
-
-  # ldaptor is not ready for py3 yet
-  doCheck = !isPy3k;
-  checkInputs = [ ldaptor mock ];
-}
diff --git a/pkgs/padlist/default.nix b/pkgs/padlist/default.nix
deleted file mode 100644
index e3f4640..0000000
--- a/pkgs/padlist/default.nix
+++ /dev/null
@@ -1,10 +0,0 @@
-{ stdenvNoCC, ... }:
-stdenvNoCC.mkDerivation {
-  name = "padlister";
-  src = ./.;
-  phases = [ "unpackPhase" "installPhase" ];
-  installPhase = ''
-    mkdir -p $out
-    cp -r $src/index.php $out
-  '';
-}
diff --git a/pkgs/padlist/index.php b/pkgs/padlist/index.php
deleted file mode 100644
index 60bf364..0000000
--- a/pkgs/padlist/index.php
+++ /dev/null
@@ -1,79 +0,0 @@
-<?php
-error_reporting(E_ALL);
-ini_set('display_errors', 1);
-
-
-$host = '/run/postgresql';
-$dbname = 'hedgedoc';
-$user = 'hedgedoc';
-
-try {
-    $dbh = new PDO("pgsql:host=$host;dbname=$dbname", $user);
-} catch (PDOException $e) {
-    echo "Error: " . $e->getMessage();
-    die();
-}
-
-$query = 'SELECT "Notes".title, "Notes"."updatedAt", "Notes"."shortid", "Users".profile  FROM "Notes" JOIN "Users" ON "Notes"."ownerId" = "Users".id WHERE (permission = \'freely\' OR permission = \'editable\' OR permission = \'limited\') AND strpos(content, \'tags: listed\')>0 ORDER BY "Notes"."updatedAt"  DESC';
-try {
-    $stmt = $dbh->query($query);
-    $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
-} catch (PDOException $e) {
-    echo "Error: " . $e->getMessage();
-    die();
-}
-
-function formatDateString($stringDate)
-{
-    $datetime = DateTime::createFromFormat('Y-m-d H:i:s.uP', $stringDate);
-    $formattedDate = $datetime->format('d.m.Y H:i');
-    return $formattedDate;
-}
-?>
-
-<!DOCTYPE html>
-<html lang="de">
-
-<head>
-    <meta charset="UTF-8">
-    <meta http-equiv="X-UA-Compatible" content="IE=edge">
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <title>Pad lister</title>
-    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@1/css/pico.min.css">
-
-</head>
-
-<body>
-    <div class="container">
-        <br><br>
-        <table>
-            <tr>
-                <th>Titel</th>
-                <th>Owner</th>
-                <th>Last edit</th>
-            </tr>
-
-            <?php
-            foreach ($rows as $row) {
-            ?>
-                <tr>
-                    <td>
-                        <a href="https://pad.ifsr.de/<?= $row['shortid'] ?>"><?= $row['title'] ?></a>
-                    </td>
-                    <td>
-                        <?= json_decode($row['profile'])->username ?>
-                    </td>
-                    <td>
-                        <?= formatDateString($row['updatedAt']) ?>
-                    </td>
-                </tr>
-
-            <?php
-            }
-            ?>
-        </table>
-        <br><br>
-    </div>
-</body>
-
-</html>
diff --git a/secrets/admin.yaml b/secrets/admin.yaml
index 7f6d4f3..576f3a8 100644
--- a/secrets/admin.yaml
+++ b/secrets/admin.yaml
@@ -1,135 +1,147 @@
-cachix_password: ENC[AES256_GCM,data:7SleCWYfyhlde2vuIr6hGtAwuSbiz5W8PpUHd8TIh4I=,iv:mAr67t4jvLc7cUn7WQaY/oU3AN1w28tCBJBI1ZfeS3U=,tag:Dodk7V+nnswtSuEH6R5LGw==,type:str]
+cachix_password: ENC[AES256_GCM,data:SjzpKHIFRvXDARjidS03eA0EmzXtsNjfkSnPTsafNhc=,iv:mAr67t4jvLc7cUn7WQaY/oU3AN1w28tCBJBI1ZfeS3U=,tag:VSPF158J1iP5x6qkytGeGA==,type:str]
 sops:
     kms: []
     gcp_kms: []
     azure_kv: []
     hc_vault: []
     age: []
-    lastmodified: "2023-08-14T09:08:46Z"
-    mac: ENC[AES256_GCM,data:Vb5iZpE0D0kQrhrtm18y4WQj7W4c8oT+oFeFPgQBCJ1EJyHemREgn2RskCZeevad896qjWAR3xtk2uGc9SOEqFhWX4OkyhTGAo5h66YygNw3LbCsarfUcYQ7Jthdw2rnozLLIOEZ0yykeaayWEULbdHZjgaJwI+DIwOyTkBgmK8=,iv:96/Ph7+HDjT8su+vmtUB7d24OVY4h4BmfiTudwW+7DQ=,tag:F9JfGnSZOMN3GHdMXCVRPg==,type:str]
+    lastmodified: "2023-12-26T17:03:43Z"
+    mac: ENC[AES256_GCM,data:RJ1qczvz9tRPf0krPFbSDURZJSx5Bx/K7Pz3urNYn8wt4/M1B9EJI0nlHMuun/QjCDYMmiOzvvJMEdOBI/OeRZaQrp9+9LBB+9r4jOhU8BIP5czzKaGpDpZ9o/6avZf38SfrjR0M8NHVuTRGW8vzstu92KyeXaIRqfJ1JX+ucbo=,iv:93dkoIJHFVQaGqNBW/9/QxobRLiv+hd73lsV2ZXJHX4=,tag:H7IJVW+c4EaMPdmJDr+7oA==,type:str]
     pgp:
-        - created_at: "2023-08-14T09:08:12Z"
-          enc: |
+        - created_at: "2024-02-29T15:23:18Z"
+          enc: |-
             -----BEGIN PGP MESSAGE-----
 
-            hF4DntlvaG5T7wcSAQdASuJ50zZbRm83JgWvBkhhqb9CYA7I5b4erFYEG4YAugMw
-            r0nWOEVjWhMiYuvgQkNbD2QLioNbmYrElL7zRpLW66HhXX0F+SSF07SGxBY3DFX5
-            0l4BqOepz3eG9yUO3rWewZZmdFmtgSSgutCqHSA3Z/3dmNupSoScGUl2qVTFTZ0n
-            pZqfDRnLrrRLGdqQ8ChgyzkaD4g6wQULApScmewit/QlRi4s84JBvqVcro6OXXof
-            =8TRl
+            hF4DntlvaG5T7wcSAQdAnAE456PXzGekxSnrumXHqeCY5tm0/20vrPDDidjy3Wcw
+            k4WIu4Sglhukn5LrQkzzcskoFpGHrPj5tN84jilNDjMz8nVR1zniAlrKTP59C/fQ
+            0l4BIQYlqkqkEDc+kuWzy2O0mteKrlo86Byv6NryvY5DseXUFd6pVde8n8ns5tSZ
+            /jSh5Fo3/xkmJ+aS2SturNqUixHYbGBHUpfQ/IakxriSfLdtkf3N82M8e5jJUWB3
+            =ZaVg
             -----END PGP MESSAGE-----
           fp: B8E1727497FC48AA14158BDF947F769D7B95EC2B
-        - created_at: "2023-08-14T09:08:12Z"
-          enc: |
+        - created_at: "2024-02-29T15:23:18Z"
+          enc: |-
             -----BEGIN PGP MESSAGE-----
 
-            hQIMA/YLzOYaRIJJAQ//aIBDcdbJwRjT0xgiRjmMIpiM9ZXfipl/UGP6N3y9Xndx
-            IaVp4trITGXIIvChYo0WylCvPPrXdnUdwthu7sR7P7yjF/ZyDorYnSGB856/bUaV
-            nX6to+zNnk6Zo7VBXW9HIqH+nG1JQW2kv8uF7hZSIvXVTYuuS2FfZMV3Vye1eQJ+
-            5hbraPc0ioJFZe+sz9vLICGGdS7aY73lGrWllgpOgmtKc+S6OAHSH6zSkJ0rGzxq
-            AhkaVytNeB6Wc9QZw0YETwRv5ZKEZNCuMLDsB6OvP11T5Bod+SK59uFk4qFyHk40
-            AcZ/FpdVPyH38rpBEjR68ARypLCXQqcTCUcVMtqW3+xm+msOF5Oax7psqBFLd1C3
-            C1X7MnXNWIcUiqm0Cl/d8DQUB4SNoKLwKMFGH9voPmpcht/1aqudMuloiSJrR8ia
-            B7out4PhQh4UGtdqilbEsntWmoiDmftvYKQiK+iA2PaNZPvFaDET6a21L9Ezk4mW
-            XVZ8AiSxyQRH2c7l2jVPZZ3KZa40EOvdP1lIiCvlrp2KBlj+r+lqduFR/qG7vr/E
-            avRT2qxqR4y+wVlXGcPONyTrb9qGZrn1bnwkZ5at4O0QYYyiOt2PTe16WBgEhOyd
-            WDT2dnb7lG7fDSiY6iVzhYmnywEOREDEZ7ZoGzfGpgz9fDwrVvfpWgWkfLayHDLS
-            XgEGvE2F1rGyAQVrAaLWNH01WL/7waZd8C/M3R0tbvyNVl+cEWnVmtz/LhRQuji1
-            qVXKzZSkc/YKuDLoJnIl4x9pVoi42AWf23YGltdk8osyq5b4eOMmoExhsdXItLg=
-            =dCdG
+            hQIMA/YLzOYaRIJJAQ/9F9PE1/Dpehh2HvyzkbBMxar0ZrWItB9UpMN4VJjipI7x
+            8cMU7iIp3gDrVfwEzdARzXOUGugoxYPttJMe4ZRrIHtjnQBWf+e0TcDy4eqdHqYH
+            COAWRBctYAWqSU7n4Gq/mqtNGe1VDY9Q5rhpDqhPc01KWO72AgLUwH6zoyGM4YBN
+            7u7eF0vqcaZGQyzo4IjasnYlPjgIDehVzdl8Zpy/yYOFNozLFBV7HLqItEl7vdEq
+            DPGzz7vKAK1pHK0Qie5MBTbEMJ2yIBiGIJ/rZv5h9RvAuVu0uqjFbehis5yK2N4s
+            hEBVaECXfvuvc0wP7EIgxdUoDeh+o4OUL6OneQRXeMqdlsw+oq8WnKywi4bf8dDl
+            1gNs7PLa0ROQKyOiB/LAZn4GmnNLSD2KnlTYoyrt3s8tgfRCHOhoWxGmBGfV3Z2C
+            KjbucxT0Wz0gSSBr6ht0lL0DD0UFQ9WJbz2Dns82CCr5T+ehHk5YVGQPSSxPaDlz
+            qnNdS+OvrgbofKAk7JcR9Po/ajoOAEvv6GfA0ujFi3FxyjwgaPS6Je4fC2gUVrwb
+            2z7OCzmpp3vGrFB0BvKSwrCQLIeffWl8M4bOoCDKM+XyMEUML7TsTrFkoQ+wzvqN
+            V53askywgCRfSkfBsEWV1Aoms4QmPnvkuoD0N4F40MRtD2HmhxrgqwmSh3Iu9qPS
+            XgGkJ/TnuWJ0KkBePo8xBqokVTfbhiGufYXXz6B80tHl6Xrmd5rcz8km0tCc/GUZ
+            R8yygpt4uFujJEQzaCdT7Y8kt+WRXaNq7pZ0lsQF7lYtpusZhPBv3pDkb4/6ALs=
+            =nVIs
             -----END PGP MESSAGE-----
           fp: 91EBE87016391323642A6803B966009D57E69CC6
-        - created_at: "2023-08-14T09:08:12Z"
-          enc: |
+        - created_at: "2024-02-29T15:23:18Z"
+          enc: |-
             -----BEGIN PGP MESSAGE-----
 
-            hQIMA8uqUsBLHj6XAQ//b1bA73WQ0H2rYX/scP68fDjnkeIRq227YW6sY3Fm/ob1
-            4vmllEgZBTlzQwZb84fc0FBIP2xBzHXgKCmzt/+EEb0VUAc0KvNyJwa65apOV1MV
-            juzNYzL1rPph+rlk0NVkWQvop6OTGZ1rsq8GgJoffm3dYUAZysoyKJFwn+zy2sh6
-            Q9kbWCsX8O/6ImLp6WgJXGUGt2MkkXUkMTdIvWHQntNuQtgh2E4Fz/HCdSW5TT3z
-            FoV/FaVNPMP1JiG3ym+q4p9zRQTynWb8JqGlWgt86vKdyqf/sBDMZHpjbZeX5O4m
-            RKiTpjAgDoJJYCB7WrnHhHJkVo2E0vi8bM86BJ0G4rubWTYhYlpLOMNIl+iVo1j5
-            H11vtJ/dphat1/8006dpeyA7xlw6Woq09Org6dRGV43sBK2oiNNznbigt6BCKylw
-            m2sUY/VyAEqkd2knYsfkfiVUy6R/yHkgGUJo94QUtfEs7Yw0VMwx8U/N7Gwrx5zB
-            8CUxxOnCNeynDPP3sPsei61RXretbJ0wXxoOv8P1GHIPYhAbEX9Oi3u81FnAmykJ
-            NxM+Ip9RbeS9lRvuasMJSFawqJU10v7XLp6EjB+Ed+vNDzxPBnhbUYNKJRE/Gq4k
-            Phx/ySn900r+MX/1097TvL0GQH5TDK94R/Pip6K+iAeZPoFQFbhh0r4aYlARTpnU
-            aAEJAhCiPLz6o+/CDSsM7HQuATjywPk487/2PZ7SVYXNl1mXPgTfQJaDvOHjlinU
-            yPO2XNwDkKQwtq72AY1oSEODJvFQyTLHSNNCt3Wbfm7LNbzp3UAarQ8g8dFOfCuY
-            p6vOQwd0oQ4B
-            =ZwoK
+            hQIMA8uqUsBLHj6XAQ/+Ofo4GZUZEca+ThmZmF8g5OlPOfgPxRUHuTHoiOthXTuS
+            IO7BSCYPHdZlnDqNMELdQfKZHRFirehibMIFedfoiKEuhJaQ1p9go0CnIhcX7tzA
+            aNaBbbJBmAYxUfPdExkJK9SqDxV7aXC8OHzXHwcZqCc/G38E4fOMVsvUVdD+Nymf
+            lEXmgXpU1ib5vRaH1oCOf8d1C6LFFC+peBBbyzwhNgFgVYP8NrXWkeIiRSiKpFpx
+            3lsLODLIYXteoO0DkkuT2z/0G86EYo+bmSx5Ubi0waxfO3sLdUnDaIIC/6pJqbS8
+            QM3HtEL1kQaAFure/r+K7Ck8l0eTKbfnd52jLoMFK+1g5587KjV70fpx4QBvyq0X
+            d0xJAfJSRptkIlMOS5+tYOXd2SOtoG7XwprrUs+lleE/Z4SaOjyGWkLE0L1D+Qhp
+            /8jw0cU46O69ig0p47H1klvJ5TkZkSroCkarVRUS4AHhpnLLnDMYDkuVoZeKM+rz
+            tOF8Sj/ySF3PA0W5cGf8dK8u8EXgIlApLbo4ZOBHff9yJJvuDJjt2b3hWPZh/uHF
+            ASxPF26cz6+ysGLl8COvWLRlxDD22aP27P2A7+9LYi9p1csNL+wuYwfnF7/rrrLx
+            Suv2InJqsUV5JkLlhoTYnv6kIwrz0oVSnTOFIBgQ3dex3T0DUn/eEdFO1BHY7BfU
+            aAEJAhDp5eds7PB2IJhFuHuYcsYquRDlwlQ6CY0fWTR3cI3bzNTLAe+ITv19CBUV
+            eGi9XUc05QDgUapvUewGXZXlFxo7iDDw1H7S1exSeRY/sbmeDE1G8beZxEqD2Rbq
+            Rlwq7HypFnxX
+            =M/ss
             -----END PGP MESSAGE-----
           fp: F8634A1CFF7D61608503A70B24363525EA0E8A99
-        - created_at: "2023-08-14T09:08:12Z"
+        - created_at: "2024-02-29T15:23:18Z"
           enc: |-
             -----BEGIN PGP MESSAGE-----
 
-            wcFMAzUXo8ZPJwGLAQ//YGciouPJhSuo1gEi8A31dsuXM0ka9INVTCtOP1kcZmAT
-            4ov/00GfaCd0XkoX43ZEV7lZrpQ8zW1iBFNi2Ojknu3HRJrmiRtTj2wv6ZOtsyvz
-            2Ac2ADp91AMaACibt+XzEPGtnU9FitVEl0MM7pbllcu6jqcV/sa8CNf2564OTTQ0
-            6Fy1NsvbpAwJ5lyk5fXxsUlR/SGN8HWJRjytal0DLwyhgQZpnbZro+pMfM771x6U
-            5JztJVTILMGhfomSa5Tjotkncfilymjhg4eVGoD2MVbc9pjmze+pLzH2cjP8NyyA
-            4zrp7UgNT/EQM8RNgYg1KxIPGYYIEa+83253bjfCexR2gh0xlyVqc5o0Ql2xBUVY
-            LckLYVFAowgxuTOFDLIEgs5t0qFotHbLxQsfy1grFShhp/4p+vPRq8UgAHBjTzTJ
-            NKw69yaJySeZ47hR7p+AUODTfjyZ0UrdFraI8qT16LRa9xPH7S8+F50dj8aeeEU+
-            ss+3g1ionuNGztLWJOK7yIm3ZHh/RIP6DyLp7r+PAII9jyWQLOOWZ1+Ugo8irevb
-            xGBMxt7FhqWAxzGMBL+x7qSOGAh+hDv4aIP2692nBjSOwmao5LF5A5jsLk7JbJ3e
-            6DJz435wCdbNklHu09oSn6hoD5LuTFNP1K/q+FmOd8GZKhaqR6SXJuTN6RfPwyHS
-            UQHZexhIfUx4aUQFzY/PbFxjI6QnHoW8PgaZ8vrL598Mpb5Sqd8Dx+NSnN8ifEXz
-            ophXLjNmlamF0wyx8z9bEitGwZvckt5rSdu+KxkF+duVAQ==
-            =QRWO
+            hQIMAzUXo8ZPJwGLAQ/+Jbtq6OyOcXYHWj6T74BDA1hcilHrP7yp5w3Za7DwKWpp
+            sLTcXo1ZywokwSBH8lm1zmhnho5BN968YTbwqb/y4FMX2MJP/VIgjgpkl9KoiZHM
+            py818xRsVttggsqoTnN6mtOYIEOu9lQCtA8zXgc5BIr1xDpRlVmz8VyeG70a32vI
+            PaG6BAmOoIQXNikNqhzyinSKkHHzDFGyxfdYGR2bMuGNSbpIr42DHOHVKWd7mzHW
+            Wm3BOaFHe76aYUByXujBPJ1oXm3PYLAPxXVudc4GCj23V3fj7oczCU74ggmmFLGk
+            PJkRRIRHHqAjb3C6LZ2V1sF2+eazlpSLcvf/KVRc12wFMzoPSpEb+Ft3MHYEhBn6
+            o+7+BC94RhGUzu7OI2dK0oMUMZaeByLqFLHH0iZ171TuD3Tqr3NnsUBTJWT7hPw+
+            FN/yFsf0ZbRiShv4Vvxyib3NRZZfZ3VTG11qlV1xEBEsBnA3QzUr+J923FxPwOkj
+            jBzxv9EJX/imXCj965R3vevCbc/6xf8Hy4x4GlqtTTFsw8uKwaQgoqL9eSejEcpU
+            TXrMaKN4zUEgT/ZPI76teU7HKBxNfS+1yocznaZ6dr3SN5IwX0tX9q3k9Y7D0DyR
+            LI1U0Wrx7JmbXQa/6ArKzp3fuwNFVkfQNbjvNFh7ghBTWQ8rWXtOU920KzAKwL/S
+            XgHlxl1gj7/YMrifU01zjSOerzzLetDRBtBQkl0DKeTUXwIjM82Hd8CP2yFaTAFx
+            AqjVgiJBB5ffCnKLz2wB8QP7BX0IzbVaNAgxwM+dx1tBH5riSXuzCIgrPnuEq8c=
+            =0rd1
             -----END PGP MESSAGE-----
           fp: 116987A8DD3F78FF8601BF4DB95E8FE6B11C4D09
-        - created_at: "2023-08-14T09:08:12Z"
-          enc: |
-            -----BEGIN PGP MESSAGE-----
-
-            hQIMA30JDs8MiK29AQ//bWhIT9dYWez0pXeu/NGoMFBa2OxsxxRiJh4Yz0FPTpBl
-            meDjRhVgg5avcGQT30bFzN8YAxFjoLns0/eWTZvoXWA2boSIwGbH9Yi5tmXp2OKD
-            mKxtxOQzBsDnzCR5wdUYNbgDX7ebgeRL4dodDpG3MSzWaydSLogCvhDgCE6Spfyc
-            N/ZvdN4pFzeylQUMgtMinfuAnqSplU1mgkE6Fpzi/BG1mUZyqDEIwlEzJ8uxQqMj
-            DbiE0aZkt7tOoYaSaX6X/wCkncToIjoVBm3Vvy4nVD/U3HwJMWW+xxZKHEyg3bwm
-            jySJ8bSoj3u9B0OVn3x/CQpRbGfYvWleCgSR/AbB8x7ccL49x+n5U72q3zsijSGJ
-            NmS8HZfEvYCEP82Zp7pCZOphrjZg1TKgHB3MXkRdo6tmy/jtznnMbQyobYkj6lFB
-            NsLg9wcGwG9nK5PK0kB3YrBTi+wSsu8ExxD0MaNpOm12v9ygMbNU0n+bNyUyEseI
-            KZLapICHWSLSRuQWbbk682CadXmqLlzzzF/FP+4mhx1z+S7f6nuzbariK+ccI4XY
-            JeeIE9M0vX1foO782WBYI3TvGUAA/MCWdrze3j304RgTxSOl6m3IKq7e89HCt0xo
-            PUQyfD/e7lsC46dxhuqI62ikKrTgtadVEjTCSUqRlVkJ4DGCkGae5BPsPcF7GpvS
-            XgHm3czu9w4jda9Qhqv3aqNZhletlYeyJEbY8/7V+mSDvvgjRTZxrxfDcPslraLm
-            WqqT1IAZRL339pJnPXtPnvAZyjxU6INTzGQfV8j0AAZbIzCGqMU6xkuaStugASY=
-            =vPdd
-            -----END PGP MESSAGE-----
-          fp: BF37903AE6FD294C4C674EE24472A20091BFA792
-        - created_at: "2023-08-14T09:08:12Z"
+        - created_at: "2024-02-29T15:23:18Z"
           enc: |-
             -----BEGIN PGP MESSAGE-----
 
-            wV4DNffZWjBmO5ASAQdAKSKcjuQ4oCCz1foAgnTSXiRz5FRTE8kGfFMVnZxOpF0w
-            FCTf4e/KeNBkkHs3fU8KikPirMmbO57MxU+w578efXrM8LRgJFvvkkxLr6tMpfkh
-            0lEB6rCNiRb0PzqkowhMZqL6vwqBA7TF0hog1BGkdQPjac7V52oIVdqMyMJU1le2
-            lb6NWDgi4mqYlrX/6+cTHnXC9Mub75r5iYzKV935PmUb3JA=
-            =U9Xi
+            hQIMA30JDs8MiK29ARAAvPwNXd3KkiVXNtNsOg1udXh08URE59ZJJUPBbe/XOHZI
+            aUlrU5kwfswTcpmbHok1Faw8ppw9u9HMmL5HoUQ3LpdYBoZFDUmnu2AAhWjEilMB
+            EoQ/Er+Nq+EfZ1XDufZT3DkHydPVHEIPrau3U+Lf92xzOi6Gqcdx655orHrZSyXl
+            Z7xRUZi/Py43olmJm3jaLhhju3nVTOPJWAr/im+lVEtTL0jEw/OuSo+63GFmkILi
+            bESz682YVL4ZtrPwQKZpdoIzsqwTGUSEO4yjuLEshM5QDL1HFE2E9whtehBAfOt+
+            4wTt756hHFpaweQDUm9e+Ce3sA7lifTVjX/h4ZJ55etW53EPP59bTW327mk4Sxxj
+            X85wo2kD7CaUxwrk01QBkAptuSdUf9d03cw6t3EH/uXYER2mc0BNPdlJB44eYtXE
+            mwUIk1UZ5rZ2tUPKIdUj0xFNywbTJPN+FKgW44y7k1/PcEKQpwndK1Xoi45HemY7
+            cMxMptNSldv1DAQank0CG3xYdCLfkLXfixOrI64Cqx8owTPLvUCic29e7Rw3+vAw
+            LOR6+mSpF2bGMt1NvpZAP0nHTijWWuGg4V8eMyJcdQ6eHnhFjVaq+EVqQlYas9ym
+            gPHLZ+eQ2Zy39nutaV/CgmgeniXS45l/H1K6QFhJahg7bpa4Z/qWRAkF/jaUfBbS
+            XgFhjoVIe3SO2E8YUh+Z0pFyHo5GlKkgENBeO7KJbiOYzwLoIpvpO1baIhHGPyrj
+            Th/Qj0uYyPYknrwgNivl/c+1nZbkNU4Btsp22+bw+4vq8c8jHKgtvMCwKsNbU4Y=
+            =beaI
             -----END PGP MESSAGE-----
-          fp: B1A16011B86BACB56ADB713DB712039D23133661
-        - created_at: "2023-08-14T09:08:12Z"
-          enc: |
+          fp: BF37903AE6FD294C4C674EE24472A20091BFA792
+        - created_at: "2024-02-29T15:23:18Z"
+          enc: |-
             -----BEGIN PGP MESSAGE-----
 
-            hQIMA6MARpDCLIz2AQ/7Bvm+qrO0UdHN3goHTwAcy2TKmthBOrknBjTN12ZVPV0L
-            21fmywd1MAJrARYnshhUkaaZrjd/hkeRfk2GfRAM58sHQi7J16xVxhvurqErVTTF
-            3vOyDeZ1VkscmB9kWYpg9B6dkffs87Yq17N6wTPh/bxStYImRuHx1TmzhdsEZ+9k
-            QmBeOY1nEGeaHYcXsVLNrfHwcL+VX8kCG9f5W0voR5wOcoJwOEQzz8URzKlzgV8L
-            uRAhtEt6zWmXv7BiVW39r1JnWNgTjVwp9PhJI6knghiIfibXndGc9pDhJRuWnSQK
-            d9SrCEPmtWfQxzhdAlFobLI3XkagrZvSj1fIyYbT1dwEiR+4p+FoOp4YuN87xs0I
-            WeDPUoEMABVpYG6PoICWxCvaFU1cv9yqAc7TKhM8vDFQn1tJ4Ante4U9dLZ+iEbw
-            LxFr2XdQXLiqYUOI4dtEWO8IQqjyxva3AxU92clN7LIi3CeCDoEJZcg3l4eCtwyP
-            NUOXozZjnEixw4Q+1EVZZACCTPyAb5Gd994JqtjYmb5575xApWWiQNXf4hGo/WGn
-            zV0ZIfvohRBKxpWvgv3KUZwwHzz13AbWlBjrPIextRDdnQRZWv9jU6xRUFLM99XO
-            PlYbAPp5amaYRmwvG86opjcqmPuUTT/W+ss/aK8ddU69KYM+YI7OzqUXC0zPg4bS
-            XgGpDo6bqCP2YYkSzLYwKzMUHgHz9Ml0IyL5DQw3DMvu54IGL9nkgEd3DyIHdvsn
-            roJxHvGw18eBhHT0mXuWi9iK0vVMJW8tPw7CVZVSKuLFFsm+3LgkC1UftLvT2yY=
-            =NSHU
+            hF4DNffZWjBmO5ASAQdAluq9PmE0yZUumm/G4UtwQpohy8vpNzAh8D1EWVj2KgIw
+            CkO+UYwhgB0Sl4rqYYgyGI1FK0aVQQYEV8jlvVpzWvaKga2bm3yT7kAp1o8b5Nv4
+            1GgBCQIQp3OqASoqHK2MGQURJr/NY3aHQVBltEe2bkgXj4FgBXu9a1L5hwxow9Ze
+            YMDBVLkDaH8oCBBwvQVdqBmQe+LEwyr5eb/r0PtTksOfuQQ1TV3kX1Gj1XN31ilQ
+            LHXyhm6gI7zslw==
+            =zVpt
             -----END PGP MESSAGE-----
-          fp: A4F92BC7B792108A463995827C1F2DA2BC929412
+          fp: B1A16011B86BACB56ADB713DB712039D23133661
+        - created_at: "2024-02-29T15:23:18Z"
+          enc: |-
+            -----BEGIN PGP MESSAGE-----
+
+            hQIMA1tId/HHLgxAARAAkoe0CqLnz2Nythjm3wnMVrwv+U/ZeaVGemBJCaS9MWXd
+            inmKUj9fmGdrbqoIhxwgbMBjuHFWrHWO10ahMjQ+X8qOH22SJEDYhZ7H3I+KkuFa
+            dBrubZizjI7STYSC4vsLftcgNkVtaIWNhc34t4Gv5Dk5rjSXuM3WORm9xrIvJ0N1
+            KXBUP6JF9LF0JZTwn86EWeb7Fa3QDKuQ3T7tM+8kypqyh40gd7sKLx7onXLRacbq
+            U6fcls4Z3m2N+NayNyi+EoKDCUOkDJfms49I21acq7wiigGE/pIDA6MFhL2X5N82
+            lj1V3SySHNXfm3QeHi0HvWiNtLp5W7yDoN/T2kLf76gIsiXlrnSR9BnvY0LrnYTR
+            wY+j9q+942eG3QL0g9gG7L0mM8wZ8QcvdVmbgw3WpbValHSEXwfg4kSuCroZH38e
+            XEzSgqbuBMrEprGnH8/+gp2VxxDcOnpUQApe6tvrSk0d3GmGxoIriIQonrvEPnNJ
+            B/25jIns9X3DK3FxL2tYzUSIXv00plLLuy5P0Sl1fvD3/J3faTJ1uX8xB0z4c9Nv
+            /GG88hE4Al+2da5XRdDhgrS8L5YX0gxKPvwFcALO58v4YU9UxNjp12kEnteqbfPh
+            0X57Tin/2cvPdH3e3g7CDi1RPHK7mcxqpcshmuiPNVhou7I7tTh7BGJpP6vpJB7S
+            XgHwQVfKx/rmB//dUZ3ZcZ6IXActfAxm+F1uXjhRi3hbctLV4GlMAV2/ijLX92Ob
+            WZv+zdCH1fioicsObmRgYuyk6gPlpMyAA9u915MH12o0G/AwZaquIfocmRlDwuU=
+            =zv+X
+            -----END PGP MESSAGE-----
+          fp: FB44F0746DF25F0B24A2EAE586C8A257C3EC82AB
+        - created_at: "2024-02-29T15:23:18Z"
+          enc: |-
+            -----BEGIN PGP MESSAGE-----
+
+            hF4Da5T//DC6DJkSAQdAtDsm8pgrR36jWw5qeWr2Ezxa0Y/feoC5R2rDatrPySsw
+            rXez9HoJLmHPJf5iFMxpEhgO8LAeRkSOUVDoMQAKnwP76CRAI+Y6uDa9qlWvWFDQ
+            0l4Byo0ib5MviXkFCy0ZslKpwAL5NR4VllC87HhndwkynizQgRevmB+ITU4QeNZo
+            +eNJaGphJdn0CuAO1F4vOw/qTHSzVxrLaux9J7Ovy1oM/jbFcAbUIelfkLZc13xR
+            =5kVn
+            -----END PGP MESSAGE-----
+          fp: FBBFAC260D9283D1EF2397DD3CA65E9DD6EB319D
     unencrypted_suffix: _unencrypted
     version: 3.7.3
diff --git a/secrets/quitte.yaml b/secrets/quitte.yaml
index aee5279..108a8ea 100644
--- a/secrets/quitte.yaml
+++ b/secrets/quitte.yaml
@@ -1,167 +1,173 @@
-nextcloud_adminpass: ENC[AES256_GCM,data:AJHISi8zIVXOMPZAghgBhEsIYS9NT4XogmyCC5TI33k=,iv:sb09G5T+7UdUumbaz20XFHuOR2tC8rQIqQP9/wOT77c=,tag:uju5DqwJtFtC7Tn5J5Xmqw==,type:str]
-hedgedoc_session_secret: ENC[AES256_GCM,data:l5a9sd731MDjNRu85XNOWJcGmNkMLsyW1jHbucn9fGg=,iv:SZhcy7IWD0pwCnL03QH5VKHsIvvAL/Ex7zFiSp2JQ60=,tag:chWdf27vLwb4W4SQl/Y7Ug==,type:str]
-wg-fsr: ENC[AES256_GCM,data:g4h7yCox7rLhJOVkvEPJi0g8pVMf6OduI5LpcmXBOrQHEbnyj6OajD3nbO0=,iv:WWyvftIoWwUv7MmcnVobcyFYyLdpbK5zDhj17j30VQk=,tag:ZxSkrRGfSL119/E4eNcntA==,type:str]
-dex:
-    environment: ENC[AES256_GCM,data:98xqoRjGZX40f2+gqTiLITuj3285sa359gapa2XurQQD6zg0mqmDTxgcU2dj0Q6k9GE0l5xF5bkguA1eVofylTY9mbidWy5BThisqb1Krv6/gdfybY8Vft/x/A8Y5S71iAzyQkSeLj9Dyoh2Jjro5eSpiA5GnAgTnFL3q3mhy8herANRNMSP6s2A5Exzsw0qzTC22KGlmAycGP2jlm3OU9cqwLYWR0WItJBE/2hYaRSaow/VVuQLTY2zdqrs6cp8SmOiMDdJ5teu910/FLi13jah9148jNKZqLtKx3gxiHweBKjjFglj+jZMTQu3O9aGF7CR3y/EuzAAnqMR5dahONURu06w/0KSXCHX14DxfFU=,iv:meB+u6JhU24aqOHJLmxH37k7DOuuDc61V7R3rMm/qic=,tag:eAzSueiRa3EfSnpZxxGiXw==,type:str]
+nextcloud_adminpass: ENC[AES256_GCM,data:v6FYsO/RklPSz5uf6aYQDhdudHb0962I1WxJM3VGc0af6s/fEz2j+UTu,iv:WzS+jU7qmNQbd1RWDempdu4nv0ytWeybF/PKoc4mvTc=,tag:1CF3ZnQNDLv11j7UoyYsjg==,type:str]
+hedgedoc_session_secret: ENC[AES256_GCM,data:WO3j/Sp0LHyNC51jdzChKB46KLU7l57TBVNL3v92sjs=,iv:HVizKMCd+d9cTQEzRncRpv9scldg5Nn2fBRz0D58OOg=,tag:8HZttVgZs4Ah8JWTDaTySA==,type:str]
+nix-serve:
+    key: ENC[AES256_GCM,data:GptsUgeXOOrwJctoMZ+mWXcw9DwJ0f0LOlLyMlH/877N4uA5/NtNKIaFHl3z2GWPRBnDLBzDEO1Q6EDuWbakr+Uq4zTJm2MOV6Qf4kM0BlNpXGIdjvh7tD2La7GV4ID+CT8U6p0E,iv:3A/Yy4PHsq9VdhW4SKIYdpd1enQ5cDiKLk5S9VrH0b4=,tag:WZzbct7LZmOhEvx9KVQ8WA==,type:str]
+keycloak:
+    db: ENC[AES256_GCM,data:DVf/pVCHHUed2cQleECk0paBTZ/6Q3NE,iv:j3sWWNL0dqPJBLUx10+jJ7QvdAHvGM55KKDwG2aQEs0=,tag:6VTeE+Prsm+LPemzbEtVYg==,type:str]
 portunus:
-    admin-password: ENC[AES256_GCM,data:9EglcINrzDw1d4VaZALOFy3KddlwAvaWRuUZvXXCmE8=,iv:Z33gf3BqmtvSTNadrAQl0LgU1fZ8fReyO4fFBvy+vlw=,tag:zPTvOeEsc+MvqigesaBMkw==,type:str]
-    search-password: ENC[AES256_GCM,data:Rf69jCganUJJxyR44mbEgB475SitvvqGCmsMXHH5VAw=,iv:ilOVy1r+HAY3t26yJiO6jExtrl7kll8Mi6fqzBcjYRQ=,tag:KyZCbPhlLSkgkxGT2Du5AQ==,type:str]
-dovecot_ldap_search: ENC[AES256_GCM,data:e19xmqOra7xJPPVnW7FtCV6q1JfTDnzgtvEpAY3SFuxCJ8Ucnt/6c/ZlJEM=,iv:XlYw6XpvENraLCnoGpEnqa2pg7VIuU0WyFZJRqjusmc=,tag:UaPkh/Vtv61kRaW3s1dznw==,type:str]
-hydra_ldap_search: ENC[AES256_GCM,data:TkaLjcnB1M8/6PiKqzKb2kiv+ix8k5Jn6msV6xQcfcWDA91LUrLlHpIP,iv:N2KSltfWhbn2Csg8chi6DfO6UcIsP8dA+BDQQ7mGPUM=,tag:FyNtOl2WkQ6mi+5gDnjftw==,type:str]
-rspamd-password: ENC[AES256_GCM,data:mn8UWBlXKG1s7cP/sLW6DWiqbAydEbv8q1rJzX5zNaZEMjpYAxrGj54Md5XmwQ==,iv:C9vvICgL7GbsOsWx5FyFktospIomfZK4qPh8qMqCELo=,tag:stOGL8UmHocAGWjjjRNZVw==,type:str]
+    admin-password: ENC[AES256_GCM,data:fESE6vrKhtslQO6ZJGv0T9t+leOSrgkY291orkwY+HPnOh26g2PSMX3j,iv:qmbCmjg0WsbOzfv6LsKcY3S1ssVXmaRB3lE6ZWzKSww=,tag:t8cP8XRTtto3EnNLEdz0yw==,type:str]
+    search-password: ENC[AES256_GCM,data:xtbWS98IkQbnBu67sN413VNHZLg6eedbStE2uZ2pljS30uoM3coO2d32,iv:lKMTNnQJJfjAG7aX+G0eNnL36Cxmn+cWMRAlTovMJ4Y=,tag:FQGRBqsmY2c9VVIdBvGwCw==,type:str]
+forgejo:
+    runner-token: ENC[AES256_GCM,data:6m2iTuIffqWqVnyD3lo8EazKU4NnKsqvafLF3CpN5qgLshG5ouseaf2RJaX0og==,iv:Nou0N5Z4k4+R9ZdFlTKRBmEJSlIGIPlCQ6E/6i1vdts=,tag:otDe67Hwccoxz1dGmnIqAw==,type:str]
+sssd:
+    env: ENC[AES256_GCM,data:ng189+ulH79xCZKOn9N5kN3KqED9dWqLM8dErukJH3a3ivxhUjyy3Tpa+uSnJDh8tAyOesT1j71mlTgKQKb3phylVEdL,iv:i8NEGR+eQ42q5be4gJdNMf/9DCCcjr3gwkEW/+hrgxs=,tag:16EvtkTu+0M5bIlgxC2j9Q==,type:str]
+dovecot_ldap_search: ENC[AES256_GCM,data:xip5KREy8oqH+58DOtw9QLcVdDlO5Nr0IHki8X0i9J1rrI/BreH2tVPC8aRTDHFPRgpBxiL6,iv:98PSXajEis7sSJ4+IkPuBC05y8w7/XRYQVFH1cripEU=,tag:LcId5rlzz3JjjZIHwoh+AA==,type:str]
+rspamd-password: ENC[AES256_GCM,data:Dd6lTyDh3FFqOTeipY0o5uJz5/Mh6FsVahbI5M1njn5S690avzQ4+8YISrwkuA==,iv:OAuA+t2KzGDvURng2RWFAoMNfw+RNLtM1hLEniuzz9c=,tag:RBN41BmsrvgXKEOa8gCDfw==,type:str]
+authentik:
+    env: ENC[AES256_GCM,data:7Mcqe2/ny5oghO8kfV1b5LksxxmNGTn6u0LCDH1Q8kwkidOD6MXyMbyzN9LRU4ovDXwXy+ztwnNHBZPvGSGMKUMczIn5hhiA5ri93kk9G8Wy4rGjjt+0Z+JKsZV33rlrYgIr6eGy6Ps=,iv:gkzjx9yQQj31g5fBdAVKzAslpTUjPp1yWnOWQyotYy4=,tag:uOSU653xBYUai6DOF1ddYA==,type:str]
+grafana:
+    oidc_secret: ENC[AES256_GCM,data:oH+VCL4e4wve6RyVwlTXPSmirbf+STD5FxUj9OjGDLs=,iv:PhVVCy5JyRa+fOrYAsnjDL+97zYASmKcBzB8t9ZVWIU=,tag:JzGO/FeKem4vd7ApvZ2Zcg==,type:str]
 mediawiki:
-    initial_admin: ENC[AES256_GCM,data:osX7QwHPfmFAJHGuXHY/td7Z+JNzHicixZRvWNLfG+o=,iv:SuKTItyOipoVqx/39+UTDg8npsp0jDaK94k9rPfYdkA=,tag:emNMythpZGwm3Rp8Nx5xCw==,type:str]
-    oidc_secret: ENC[AES256_GCM,data:2JRvHObJ5xB3A0XduUwdF9a7zzeEPz1nElLEzWC6Hz4=,iv:1tKMlme1bR5JcEk07Xpq99+OHxdtCxjc4xHNlBxq7X0=,tag:cyhstQYvEmjI5Zjj2GWtFQ==,type:str]
-mautrix-telegram_env: ENC[AES256_GCM,data:cuQLaLyZ/hSQsN0DnyuBSsIuoZGTUrDsV0lNq6Ky/1pgd5Ull9OYJu5bsgMWauki6lOprxqBE+32TSoWPb8WFUT9/NO7U9b+aoaCs/R8HeePxnaIyiBiGv881YY54Ze8jLXFU3XAxj4VHESSCNSEktApTvJvd1VXJjJGoV3iFWZhl1VbLsE+lQSHSiDLU6JqUyXa6k1BGUepGrkwObGoKyLVFODF3uahXsXtcfgbMQ4UfqZevh44dj+kDDtohXje,iv:jmcWTyVkqu9nDc1ws2NxkMKrHPZ13i3jqDkk4Y0kejw=,tag:h7llbDCz8PmvfxLzrQlrCA==,type:str]
-postfix_ldap_aliases: ENC[AES256_GCM,data:11ARqAOq4bXWFGZqFKqUOGulV6rBOZ4375WXdiAD/khToVYyCdJ30ThCQuTk4q9D3L1CT1RcbQf0nQwJfAz9zODpqhrucFAIWhfreHSPeLxgWIPXVKqEISDHsM80f8cZHoTyh+hr2lNhecum4XgdUYy/keLlmzQrwNWxKQcN6fXRyEhnyIiLMEuClAfpL4vvW1pFka9txPnWFtwjPZecwCNn9pD9VpEhIukETtjVM2oCU1WRT0w6fAeFQnpHqBq/E7lPc32rn71gSpMmmbpGB5qPuGJ051Mr/QLdADX0+f8bebzGCtFudyBK5BrTeW1J2iScmXnPNYphA3Zr+Uq2Ss8=,iv:dQM6LNmIwrwJLF1pGuoHZhoJulfVhgC0HWdZXZS4pxQ=,tag:vyH8Zbh7eSQhIQ4bhwnLOw==,type:str]
-vaultwarden_env: ENC[AES256_GCM,data:lGDemdGgYemaaWhWBPEMuP4yie5Ceum5ZZ0hKRLz8TJENrBNuTGGnG9cQ+L5m0uC4c0+GmRYHZZFAbWxz2JAjx8rR3lGQkSzbynTWKPuVMMulsjMO1DLVcRv5MzLRNAKgZnqiNYGXOMVhGNyTq+qNgp02TEpDJytfP5U1ZzQcjaPKmCUkEUSH0I=,iv:foByTYQw1KnB1MmwSQqmwza9PJJmdYdZbIHKrZ9vog4=,tag:OoqJ5kIjNaJZYyno31x5rA==,type:str]
+    initial_admin: ENC[AES256_GCM,data:JzW2rgXQHXxj1e3vFhkXVkWSgrA3Y88KWlQ81hqUHw2UvnBH4GWtMXbZ,iv:zqKUyEaIOa/7hpwzjJPwk5gfqbEYJrE7Oc1Zqcqm3vo=,tag:T1gObIGtI4uVdpONvIXofA==,type:str]
+    oidc_secret: ENC[AES256_GCM,data:XNbpKd42PLV+orXY/HqnYKOpt+HD4EmVMtAR+lRw+x8=,iv:XtmVdArhYmp0E1xL5lD1LMjJt+vyQPv/lG3g6fnsD00=,tag:onxncWUsG3QuvUebgVpLnQ==,type:str]
+mautrix-telegram_env: ENC[AES256_GCM,data:FyMtJChtir8Ip8S7zlBSvKccjt+7Hl0StHzxmKO7VdwNNA650HHfni9o7akIY52+r86tvP3D/bqHaBZqkq61ZNICnFJuYIkROvt1035uej1cdjlHeCrZBttI2w3ZkkKT/RZq5BOLt52o/fnw5Jlt+3yr6Kzd5mvcz6a2e5V96kFjaib6mMdg/Y6axiXvOSeFOHCjs6Js+ab7MDe90KUM3aLtBezXx9YTeU7RiqEiZl21dxzPIwilj8bhEB0RRIb1,iv:1ojF2NyQfaZbKwlHQND7LEOLWT1SWCpGPQTm2+0Y+xo=,tag:RavBAv49Ldm4rH+2DDGstQ==,type:str]
+postfix_ldap_aliases: ENC[AES256_GCM,data:beJTXpJYlAz4vyv2rAyuMtU2gkwf4JNnsFAG0oKLWuKQZnX/EyqyGTFK7hOs12qye26H9Ysl5vP12iDyVXU4cyYmBOMSOiIS4opPVs7yjp/FH0u6DXHExzd8qs5vwa+D+c9j05kLVZ85EGneDma4ITNBjo/JMjyXCHB0e8EZTFyfR8+fq+qvuyOUmLBfJSO5BK96u370DJ7EmIPLDiCUSO2MCD86yfFEq5J++ljeuKLxUtisqFWDPNeNq3YGjz0EHUgcqqDwzLwEEXyvn5FEI00nR0qBgSBTSWRDrndo5O2k3JMfZWW9UhXXS4kPwCYEkQSM240cwLNV/Rb9XceH2wxzL8PcfTNiy2vd,iv:lb9u3ryu1+G95OIizX17ft+fGK2CA2xt9DhYhtKda1c=,tag:CsS2Q32AgAyS5eZ7Z/Kf8g==,type:str]
+vaultwarden_env: ENC[AES256_GCM,data:JFySiTHahlUFsM+FcuSJPnGYMijphrnZpFFdoNe7DYxWjIgPRWdfH9WC/a5GsK2xCJXllXAASHNxgkYRrdPw2KaCiUR/QhAjtUmyv2NsIBcMYStafDUEK9emddR+ACedScsgS0FtP8f3cz1enTBi+DkYgL8lMAoCw5p8vMRyE9mVOLpTUDOO7T4=,iv:992REuXzHAxxhy2BbeCGNhTZkn8eSi8N2RyBXqqy7U0=,tag:iP5AFQqzoR66AkTGfYAUZg==,type:str]
+kanboard_env: ENC[AES256_GCM,data:AQ3jU78hi8YGzfWXTo2wnS9Q9hucgtKBrB/xiIyrZl/j6QpQmr/HS6gEizgY7Du8ZhkRmRTZ8ks99EOpPUdN0LXhegZB0loCWEozkPCn+N0UZXqKDVAz2UsyQu04Eu4FPRqw9VMIS30qJarqZGjvAJmBWNd8znW9ggtg8bMxqwWuErdyMhCCbXeAsw4O8XasGR27e4SGRJNWR5QH7VX7GqOb0Q2AFr9BQhNyO9MgczmqwldqirqaIACIaSVvOOByh56M+rbWyiaAL2O7BqcHS0dtV+XG2uVpxb02b456iArRyKco41bVC1sSRfi2ewCNLma+yNgR7t1WYZeA8537gMX9LaU5ORnn+L0toM8j2yUnfW9RYA3dqp50Yt2UKH/jjLwW5wKLrOF1G2Pb5TAl12ghPLfTfJiuv1SLgahLK5lP/I/x3dJ/n3gm7/lqu2EPDnaPtPDotV0VWfBLwQoXAjSFvSZVfxwYIon/ErxsACtxgT1Ss4L88Ggc33ae1BFyURX7p7738eizsqUV8WWqa74Jt+uT32nU45B2DyyzFQWfy4mGsgBssuZzgFbzLyYDiXfcq500K16950cWPH9s5Sx1XooCcHeTJYyVHklCJ/0r3Iz2g1TtKktpr5XW7EEcCLKQ86UqpKwg9PwEHVnYgFKe8IuSeAAGzZczeUFvERrRJs8qZqPE1IaufozSr5bGBh4eRdv/kVDFyh7wJ62xStVb7IV+sXogA13m/emfxdy1RBWftHcsgZ03r4pdp7mHzNqRvYYscx4UzB237GNzG82PJ/zLk73XGRCv4iE11KWZs9oyoOI4RFFvGwNS8jV3wWh4I7Is3SWO0cy+41qeuL0oNeRVseVENZ5zqxC1sPIP+z16XiTlGWUefTYinFjKmjojF2+uSS6bGZteB70iynB28FUUEqU4Wa0RwGDOck21cw8PnIMpiP+LWdnaH6sKS+EMl9IXcraH31wNK76dcUy3dPqU257bp1e1OJ0Y/fO/1ZTT4Usm7CrXCon0gcDWFAB+c57c+omfYW3kZ4F99Y2ht5QZEvjK20rEXLQb5e1SqIC0ssjP+7vpc+SfNQ6jQ6B6Vye9cyaNkgzGoWZFwHME7cgehs+2FkCOVgPlJ8hDupSTc1BgFzT3JJtejsflbMeoa13nvTYWZopW5M6Ym81TQGv/awPimMh17sDx9r38bU+kiVs5Y6MVuSQZIRICOtg6cxh5Q+fDzTyirsrctVGdcI96WyW90IwBL2wYI7ntWdNwaAPoTu8OFw0kKW2+JsaNHeXQfGmWZfUtKWIJetnUn22SLAe86J71hFBveVlokehQ7Fcg0MFt2r9mlR0/eP1aWyrN54tyEv5uOekmKE00FN/8PpzgH7qasvRPuuXkotj1gazJYk7Tz0oO9OTM4M/yplrL8fLOwP75Uc5PGGVu3pHmwkfrjhh72V993Su0V3us4p+whv2ItZ/A4O0np9CSvFEJXOS4esCmsXLqr4BbBy2veoxnIiF3MEmEqbkMtgkslnVwM1RVNPCKESxFzu0oU5phyWn0a4JW46g5lx1tm/GWXlHQWa4=,iv:x3+PuXdpZ+SEuqHo7icQVyzGEI3IdEyYjjOFkKbzq2o=,tag:pWoe2PC/tEODmz7o6wcVPQ==,type:str]
 course-management:
-    secret-key: ENC[AES256_GCM,data:wWzoUGt/5Yusy1s/RPhZbDJFdTCjxHiRODKhDnOUUik=,iv:svN+pmwBXlL7ghOiF4y04Tnivv9Zr6ZoVWrCVFzs/gY=,tag:+gTxfipVNLGuYZM8KDsoIg==,type:str]
-    adminpass: ENC[AES256_GCM,data:Y2/WTOCGme1C8jbKla+I0dzNWbf3JtACtZD9pd9V3w0=,iv:zXAtduU460I7E4dQjyln71Icq9PYGP40qxqRfv85WIQ=,tag:dMz5jlyy1S04/sMt2xLb+A==,type:str]
+    secret-key: ENC[AES256_GCM,data:zMoIj8gjNmLdSbQmFo8n1pDIKaUUMzPfVoKkPlqNtm4=,iv:AM5wwvAFXKVss4N2/lK6bKYHV/4Bv5EOz2MVTxAPF1w=,tag:ARzQUVVjz+HhUT+JAISHkA==,type:str]
+    adminpass: ENC[AES256_GCM,data:EariUHHtWirIXuRARj7lEneAOlKcjca9T+J0oH2xPv99w4ac1cRrvEVD,iv:cjC/+AnZdwWXkJOIAE36Hk/if4fqofVFf0H8WkHkRY8=,tag:M+s4hPzSp8eR76M/7TKXPg==,type:str]
 bacula:
-    password: ENC[AES256_GCM,data:je85cIfdDrRl5mkOujiZM57xFG7HrtLyfsDNVab6tBAXMZ6TvcaZrrmzjfKDOYrT,iv:lfPpWXz2h8iSyECaPVJzi8sdks7fxPHewHagaKCBHY0=,tag:1WcE5wRA/q7IB7Y1TgjtvA==,type:str]
-    keypair: ENC[AES256_GCM,data:lYDS3U7+++aXowGA2DLWGs4/pBxzjk9VXivEqqvEcMOLvgPwPiVKuXqHShw4NkH6qcoLzOqqjDpEt9aW5sYcVkIR7gNTiBcGLrVe88ZPHYNa1/mnwZbNVKGutM/S6wuyf8J33mrfbQ0ujkavSIE5zOqFyaHx4kU0wHdxjIl3q8RqUL+6s3+vwOoxubKndBzV7NhfvCZBZ7JRJK5bORuOrbgQMgwrACTysR4gRVyiu39ku/AlHXGCmrl+GBHTAm3EOJyrIBBoZSLNyGkFv5ay82Oe/CvRxzkR5lK2xHi7tdBUtoOOrqT1KNRKZxsXpKUP0N8ctCD5/4pAVYbhDE7KQDGfwWioT6xSJJvHF2IYAnxV+A63SkrgSSMWG/xj4ZtvMqbngJZ6KqGDqS8qmjupB4fS7mMp/lsOFcL4w8w+isAHM637Yb4/Sfw704+oBMViBFGFJ+o4V5BbjGHygIgUQXHabeOk4u17e+4V6CVxEIlrzafRcYPDAwQE1qvJ388dq9z6wws94dQnTgEaW1O6/ye8uAvtvuUzwgfnT5CU4ypqn0OylOzthNqJfr7KfX3/+Nu8tiAZEkzCvCDGDnyla0fzpm+BZwpLcAmhMv47vS5NwUghJ5iCGjssmdEDncclwolwi/vvmtYdPchbUKuKn+ro/R/ZkMDOGvQHvC9fmbn7lEJFUesCQLwtxHNsj6NqR6tYqJJRPIFOtSeak70DKak9/2NCInVwrbZ8Z12Lp6xGgc19RYDDVQ302cGhWkYfcXMoM7Vgxl5Ta57TALDbs0aZnvtlUxmWpUUwF43+t5gxKW6nEbJwHfD60G5Je8tiik0fuO0D/XsV56/ih9yKgqxhTqaMNrWgYoTjqmqg9dfhVwe8AhpXdlUR9R71TKAmkkmNJUHJmRNk4rp5ZIlVu193eybA9WyPPTWojMaKV8GQVdhkRKCUSeIyhttGZov1Xg5xIvpQ/l+TDYVpnF9ol/RR8zqCKguU+D8i1Bju28kCbmmogzlmS4Q3QoMJk5pn0wqX8NytE62mmE+sbmqSt+vK2xXIUvxUQSOvzka/Cyj6lyd0+9W1YiaTK1xe2no4QswJuTVZ7PRMZLQL2O3eGOrAR+BbVnIhsfi4fFNcQ1pxmtqCT+wvulaLd+CH1xUTBjkc30YbMpN+ElLFZBuhXv48RP82iizUSs6FumBNG53QHN+Ucv0u3wpqQwjlGlv9euARxJZVxqA65clXc7f2VzJNHBYXB97LG4eOTykVYUWsUS5fIurCZseGgyVAMCAo/YO2jMXrqcEJiyLs4EKCRo4Yin7wNyf34qhZ3ilpofADhKtSU17vXL8HQwKn0fpt6RI2hSc8xTalOAFF9B4J08a8f/6Mx5jy8FHKSEbu91usKDy9bGi/Cq/f09vBymwjoe0cvPdllOZT0Ik7WO1gl8UU8sXxDkjM0mekslQSE3vzlQpZ2JcjRHSxVsC0u+500jap979aShw63OJTa2sQ8KCQ/EJrw7P/xQJ8STtMxd5JBl/fX1e0AM7tKhb7h2JpwqrR221iQtG8FdJD8JUT6hTOQKMW5L3SZj3//GF4XrmsowP6eEbFJUhCXGUoSHWeVF2RZVALDHm8HC9cWUASIyiNCBJmV0mE1LgrAvSd7jkZE+P0DQcMOjjnMn44xYqnrQgplgI/glTt0QQqdyMfAgGrv8dfwQGn8TafY4rPjMU3L768Y3mvSCfE2//lKued4B88LAG4zBfxh8iF2wEAptU4xHy8cf3Y1cJWUPwg0xBVsslA0FKpxHKQ95LGIU0vq3EYvSOk+9eQ3BBmql9vflfhjlSlS7DUcwLJ6iGurTksapczu5kQu5vDuyc4OFUz21+rSPUph1LaOUj0PgF1B4NGRyevW4/LKIyYFxKj9ibYVWEfNrDvRXJnpXT/ffwT2t68OIMwyBIygNwLKaa2mg5RK6Il6T/F85CSxbNs0XMZnempZfbCDR1THtIY2B8WyPXH1WIEqDlEzxuAq/ldl+LOyql0R9Coos8+wVx6ehCLODsQLq+mwJIxbBc6Y91r54Pmjtk+Y4FNysLOvS1uvgRvuNJ+vz7ZJ5IB4wtFPUKGBFdUue7x9ov4GR67wHWnRARvnpgKGuvoA4ktupLXAsF6zzfgI2z/+aN6cxtUYR7yrGnxABPAA0LlWZnl6NCaGrOTNo/e8chbD+uoayVHUhQY0t4xbFedwHIEeB1c98i56SRbCqc07TpPKOV1w+DmmUvHWunUWfWMSAZs5hu5CipKSyvawnCLaHoEcvpx1nynffspzikgMeYdacxGKzR0UshySv7XWyV8ruPgjjCv7hc7hu/+ixAFKZql36Gl/uObNfSzIWjJ0qKWxzHiCQEfpkT37x9EBpLDZn426GsHix+UCH6Zucyoby4UveM+q24mi/JgBaokGcyItQHuJiTuD5fot+6bEowu4bm98R05aDjfDf3vdO3/W/31J4yYKPMvTf1z67A+pBRIfzNjDBpI8i5zXZoo5Y/urRGt6FdxT1ww1NzpkL5ebz9kbEpRqnJuHYs2JMqrsTPlQdJ25O6sPgHrU+XJPmehN1bKu09v6ThbUelVEn2+TROvzyemoPRzqjmE5BLfMjFrffnnTX4M09rNI2UhXivdDTmnFHZ5hdpZfVjhYFnPJaHwfNOSa9TQ/6zPwii1WcuxelD3JldTnZxx2k6BFsSo3g2qNefcpHBJTS9Nddu1qHAdbJLlHusc+go/yZ93h5mQdzSnO2NfNAv+cX7Liz4PP1Vj9SJiBmlNbowJxyoYF4ji0EQ/OxDvt/iLoQsdDXb5qN7YDecfdsULXY5uTdjmDlwkhvAYrCs0VHTEfPG7o8GxZna0GAK3dIFUzxMhL+ZjagMyq+YUrDe+nRzHOzGQqX87Oxv8/7WW8+uSY3URRQR0P3/gL7YyK2HsHrYR3ENMXg76a3M/pLFB6y95q7u57pJrceNed3W9j7cR5JTci34cNsfQ0VOnW8JcEjrKvve4wA97AedTUBPAjo2Uz7n6UWswbC3hv8Ffzld7SsAEdJW54uqQvUharR1vyp48RLrIv8Y51xtoQ0nfY+i65Ii09zm6LC5WR+/YV2i3Os3VKgDmBjuT/btDvi0tP82288vffR77Q5OToVRLUXfpXDd4FXzct79h4ZhXuy3tIlxPc+U1Oe6Ly6R6cKWRG58jTM+Epql2Dy79tpU+LCJon7A3UhglHlpJlT3tfVGbFyncm+Vfb4e9lTqhDRhqVo5CyWeRrMBhyHKAmBvzjeQp7Qur5c2Gsr9gYibiVTGnYijMAL5iigyndDt2tZoXolOmy908k9PfOWd4TAnXdTcYssoT4bI4C6jiIyZlf+WyxrjdvEBRxZ47+qkj49/wV3fYlpsgE1pzfPwvLDOMIeTr6sw6b22Wtir9LZUVY9OA46NZ7yzqMRDaolYIeH2MMEK1QwIrCVZrQi5NBi4Wy+ghVs4s6x0/W7gimFK7PV/Vjo2bhA2nNLcnrHvddey/k2JD0aG0TvNRYKuzr4fsXb4I92d08VNJ83zfcxeZJgzt2FOvbwLj66R56Rdy/ITKpAUNzYQXBSGyVdPiEPv5Lz8YvyAFIB+BySjXerb6jtdFsqbGRgXOn61kAlBZtADzAbi9en7mB1O0fBcFBW9En/rZ+dUkzd2pOokdXWDT7dKK604QFdcALFCnwEGlSljypU253yKMGoSYaD7E5vtjuhPxLXggq8ZP4yx0mll57VCHEgYjO/z5HzpoXbUzOtaBHsSFyEiecbHdHjlJGDHPh3sz20YyFu7bZLs6p8uKXD0wo3+wm+afkOm1zTH4sGCS1PwdekdknHLCK6PlPZRQ83n2ra6dLPvK66b1zPPvOwqrpf7OE1ieNg/WKRqmfUjViBNnaftpYnQ+wNq6Q0I75r/UfwarBoru3Xm59xRLupCGTRLqJSYEVRsyUOYBfOjhb3fAKvZJS1HSgexvQCD4b4kwCNToTdmpGBAYoPBsDJw1yZpto6eyXLO1Ad5sKmHUvlUFZHxL1tQghNCcSEVP9k/LLAwnHAV/XNPpFlvOGHtFolYSuI9t2s+EVioXLD7X+tlJHVQ+hrnGrJprvSVhjR+JJ/N1jPr6eBhbYzp55GG6C0svmhoxXDqFsG4LRhqTgHCxuajsMHOJc0uWuKKRjVWm5miIOx5qe140OgJ5mXYJv+3VJjrm4XnjppRhNeKM46Om2ovlgkqoJCMRxI4ct+AZtBCnF4NGLg40kYJpKZOSaOyuj2A6gB3G+sCOi2dKOPr6u5WseLFfy9u5fg01Z3G1MiXZIM5E7Ny0Ecos63ofyGYbSm0VXYknBsQt5XuKsgMLHlHcNZaaOpni7sn63hNI7JDsvCdQw191stL01hEL3VtmNOtfKPZXlOdaP6V2M2p933eApLCpV2VJ3eMDpB5z1WTVp0XQxDOD9IThDLKnt/AbhMC81ROklyp3sxTZY+AjHITkWdDGOFmY/zwjs9M2UuwEt8eo/4HkIqKDfK4u0y1jKf5NHzwf25hn7xAo8uvZR8ZSOpO1YNi3ecp9qUgesyY0acxY2Y5F0+Kyx9Mhfc4YetYL1Wtkr83SaFCxMjsMx5VOVNrb2q0IPJTYDY3iy2ElWlrDLKH1HNzUz3QsyH1h8csmblz2PbReHR4lUbwqfkNixNMkh752xNd4qA8CSKjK3shHle4vW44ewJQyaOLfevF9/+JkVrjbWjqdm5N4dCBQhNlfD43EHUuGTQVisbmokikapHE7lZZM1WXjT6hnvR2pnivV09GfaKxyruvFu40UZfIeD+ChXzDMcjom5knRLmB5njjpizlXiZw8cZoXy8QFNu0CtSxkP3b0VkJ8vrq5tJXfIOYO7Uc4FSIQKPig2LyYMXXuaOPPOkkwXxqo16ZFgdpBbqe3/uRciVgaxvYNrhdU0YZZj0DGsZlWgS3ClpRqO7Ka3AIZzzWJaSt5uh/YdXQtNqN9zUuSmfJkjLZ9s+Ojdw6BvJbC5y+Ia/MWOSfqxLTYZUMD5r2S0YLzF6nBGeIbZtzUKIdJ6X0Nq9N1l+2UdUJ9yvmFHfUBjUddRZWiVzIAQQ4rk0WFayHfA36dyr34wp1O8mD6rex+BHDRHPc2wwKEk3wBb2QwtLAJ21tANH//ZRhB4MM8lBLMxO5Ya90r4zXgp+buYuj0lFKz6/IowjvHY9M+fBpAVkrQi6AEPq5z6YtTC8HU1kcJ4Cmts8sacl9fsv8jvexuA0x6c0YTJq68oDgESfrcbEdDXkU1m/XxMUSVPxHGAxyhksELbfRsWRXrgOMUEGSlGC7gf0XnK25M3VdEAlKhKVIo5Qbg1Z0UllwrlAfG0IM+Br3VAghzIQJFDuycdNAjuArIo61U7iBd1veOBBch7g7nzwdbYqOhvPqmTz84pzRp/3yuEWYLkcCwph7YiAaBUh7yRwumxJQkDTk3qBIVh5M04PAXfFDFN0wekGEZ7qzv9JbNKxPl59al+hxrMnfGvvyuhVo9PvPKHa5xJCU22BNfB7LfKWo/OxxhKDVWDruNivz46/cSJcM5iaNPlwyU1hl8s8OXLJMzHSmZajkEDJWk+G677zHD0/2G+bdArdTHchQG2iE/EMq1mbD/so/Yf2T7U25Histn9DPnsi1/4Hq4/tUaPAUK8kEwTQSaWAtF+uf6hZtxoqtCtyGzpPcnxJX/yIthkJDqkhMA8YYPbbX7Uk1FrLEK4UiP758bu6lvuCRD6Y6u5e5HjODFV/RjGpoODKIVPIlVTNGXu9boaK/0wTmhS8LtXlUJfSZt5F/LDGzfDl7o9w7u3otf24DsQAEvvMBRKl7DqknUSuR0rjadxrnwzi2rbrOmMQ7VWGhcTaV8LnKh7owpAAeDdxH8X/Kg7BIvez9rjKiVQYSAV1SqEwUdOrEkaOnqIV2xnwYCETfuykoeNDJQ1M/GaVr2eNgYB0fAjHJdTYtVMEFLXJMJyxSYk95/RHNDMr862I+dHnhSJg5u13fACXricKsHZOg0gxMHgTs9pfieBGxQtG9+2JVK1ws8W73aKjVL2WYTysE7eh4j/J01nPjayRt6rNdkYePf2qhDi6mLc0Z1L7m6LlA5opzDpX3Fh/586k9XdEgizZ6OIC+/emHAdNteSzwBv6P73tXqZk9PsekA2/MYIKZoRVbBOi8C270WJNPA6lRBItyGrIUz/QZiyMTFg73NcX5lskk5AbiO2+0LfR2UCKVxM5dvzE4dYPLk0if6Zvl4uHBI0ayIVN9hi6IZIExsrER+91DqzalZM/AHSa6pl44qtk7o8+yGSJJG8i5TjgdPn7sq+LrXkJ7QJfa1YHGB7y2Q8bIvm+A4MdqDx535vWgNRQ+iYeRtdq0jtxQdJZspFas0ceaOJKYNByPLRIBXB7rBwusEmEdkAN0xMUAN/iO1T0B/A2WL4XZxbmaaEa03CJHf26gyJS/ARnDJLVgfP13EIwXVjD6vMrqC6x2dCpBEkcn48cLyd9wEskylfklEDAi6TLdwTyO/VGxvimdF42MkF5sdly4Ui/4xbjIHY3tNyXmagjJVvtKJnTlwEQT+tiHo1i83z84q9tgwnkL+3BAk+G66xcRmTAHBNfYhu8kBpb/gMeVIt/z2Gpe3tqlUCFyKvlebC+a0+7V1pRhBAgbFb1NmM++EPjEC8fFbilLbgbq9HKDm0Fqcrz3qewh8bAy93ZuAVQusnakUGKherN+tgw9WEcgfLI7lvbdAz37m4fyabustXMK+Zb6PYpF+X6P3n9hf1cZalh9RNytC22TFIksG6j2ZcbBKLF2wf6d8Y083BNceeE0ADW0vc5lEFmgx5AuaZHOUHzKIDX5Y8OXfevibnnmhlCtNjuRVPx1oR+ZD8LYhjv7KWlz+wiLkkKZnB0Hvy+GspSaFsMG6pO6m6vWZ8T14l3mkhe6oV8EO95Rxe2s5kIkq/hCzrKefc2K+GvXJ/aEmwU9OtN1YBS89QICIq8t/OMvx2CjN/Nd5aewc47mZBW2ashKiLute+2/0dJt/8JKG+SxGWHxtkDa7EPSwc4ZZjpoPF0MhXMB3PgbvoZWARegOihW7c6BvHFsxLQS+AjqYVxAYvcnx2NE2y8KS4EkTn/pKaIryG6mAvrAolpnXjVPBFTQU9PaYFlMJ1tQc1ZrUH3Yev4GP,iv:DEzZocX7nChUd6L9v3iqEeMcxWCZY+kbUnDLbikBVe8=,tag:ya/g5UxFYI7LJuiqbc7wPQ==,type:str]
-    masterkey: ENC[AES256_GCM,data:Em5QVv2qQ4qO/XxTKd/Gud0yPGqEJz80Xag7MfHat8CDoBS40dJbqDKBkBrIFBLqUnHf84ilkjw+lqFhkX1XHsBztETX+Cn52fxMUQKo53lKVjbOCJ17vnJKHH7gfDNgKXIOauFesWN9SEATC7podlc4ARP15jEwkVz+R0dCIS+Cs+sYSKbU4Jqkg9wAy2GWvVJXlRnQ0RKPFXww0L3VE0wZtcEqJHOeTvJvp/PAQj9q7FzIaU8krHt0byZuVbQ99drvxOEjjsnE1jwZqpFDgCLmvDsmqctUvjIX63r3mDwxMw5EF2cgE9AumVYovSj17Z8F0n8xlk+htRBTQbLvANa0y0wLB247bZDo68ByRZTW6wpgDRheIcGh6eXMqU5fde8FtNtKGhZJI/Rwx8LWQXmUKHbFReiwCHIYEqmuntZFy4h0Tz8jvJn/jl24Dr4miJI54ZJGhFZSV0hN4C8/e+fE5sWYT/PiiB+BarpRm72OeU8pJew/Xou8mdOZHgiH4JvW7JhG4tuJmVV7aNmeqQn14ICHBNSumn3oXh6n24U7IMmSybIkmQZmx3Y1Uu5jNzX27RUaf+G+n6WEV994/ow1cGT72H0NCIgIjBZI/VkR/lDdfNmo3Gd2e/u24JvpFBKavVtDmpQNwSfbnbdYPujqvplNKzmyclGJ1QKXh/q+fs1HoFDRyHCLprI+rI+hqyTRpQBABi6cNVSU6rgnRJ0U53HI1eQglT12ZVr1ckfHIic/hg4scowbHyXomopYHEzIw/1j2EFqM3tlz+Z4T2D/VozKH/a/Tm9EL0F6+0nP4kWCMgcK/KIFcR1ccG4TVKVjBcqadNO3gqObRB/AIbzNgGHrv4Vw+1LDbLSbXtLSsqR+ivjsXehg8EL4TMhhZmN1wI4QiqCcgG8UQaVyERzYoyzNuQicukECbBshRk9V6Gkc10SP2AXJnie4OiQN7Lv4vZmJ7TAamoCQOYZZyE/c+8SpsAHNoKhDYwqG6OdvgzD9uryZu83WzUY9EANivryS17KUq8RYZ9RItrotHZ9UrdJ9S8FPI4PPQx+cBsfquzsnEnacibFqUJDs8Etj96OS5oB/UvV+e6VCnG9k2OUEuqJ5X3cVfVpjvFdFk7Ercg6EteJcCKGPiFVn3LSweFw4HK9dPAtBGIfUOf2zYWjhmDqLWfRlLLRZjqLC2k6PWUMKeFCVmNMA0ilsiGdpijZzgFnkMJHSZ60I0UbwKw1HANORvZt/HuOQRWkKEI6rM3X+cHVCJSo8JvTJGc6IBCN7xbgO2Z+v8BgnEwlCcZlYC3d4e7dn5tDSmw73L6Kni8J+nFZnomvJMZGHwv06f/d1DG45wHxpZo6tWKKtHc70diQZjcPp/MHSyG/ptePsp3FyAEGhG8spYyQZTBplJdVvMLkJkMVKEGO/jGcu8G6zO9Kvdc67KYcC/l24NSlzsHPIhxeflmE/3zRtMO4qmis6I9TvpGp8CbCLWIuDeD5nrdsgIwKkYIyAreEZ+kc03akv+A8dM++IW/ZP1PGzJ9qedrninfjv2dSLgvaNBYklYYdOo8IGmVK70AGxMgAvYpcVDq6KYgtB5izrzjonYkw9aQ1XE8OJ+H6bJ7Irdmj3BIjnXkDLrH5oTR6u8q+i+JJ5rJRg6PQPhUn0rMjnl5WS5Ht7XQNhPxP0qP4hYm/+kfuGSamXfFGbXEhozRGG2HC1M7IAOiQpHWa5qw2tUv/I9rrL5N0PYbxX2OrHm/tEvLHf2qJTH/IHZk/xUEsIz/vStR6CNSB8FC0B2/XKnG7s4eGRAjixJBZD2jnfUhl01X9F3Rj18QtulW227KY9i8XYvczpf7xKo6aebYfg6FYGg3Amduwx/Z5HGB7IRp4J7rOb2U0PB0/N3dLEWAUDAx23XZ7LLX19CG63StyKHwL59ariRtpuVZCAYt3r7JFt6GNC5ZCZj7P3u3mpZusQ7jbFouS6R6LckLjitdS4yyszLy4lUCxtGXDA0aeA1hAVTxAfgDNC4f/2pVV02hOPG2R75i9UfpAqdsx6LqllZSZjDWsydtIP6BVReDo5twb+OryPv9sNc3DrxLgDK0Ag5+VTc8Jn/A34QpX9onaY2URhBcRcXS9mXCrV9kOar8N+nrmvMJOrmXyqRP+Mc5XtWDEPQHeYULBw74xuYW5jd8Bq58Ja3LIgELWkpW1sO0xYNINEP9Vnc4ToZH9HNuAoRkLx+3xEMdlNczhjiIDeQDCYX59oWrkGCqFHbTju6of191rUKZNv1OgvQW4pm4lLnqE5xzr+E6xmB7pgB8LeL810ccyy5A7DWzqYO7RkiUehBJZU8Rpu0zm5xiCCJNNfwRCtdQfx9Ii7z/BDa2QLYT2/X8ClMXPdUMww8aTd35KF4fyPCu75w6StrkAi+6jjI0X/Nduc9jHHhbMruRpkarYFmmNeJSQQXmT6PAYZm72sWno0AJA3oOml4A+DGJA3G3ppbmh4vn1PeX45u7i0antyqkhvO2Eu/r+2T0SIOjXEuM4uexAnE+B63CDhw29Gei3hVeX8oPRXXHnrY7aasA6CyG9jOaZNCWDnXwrD7mdFNPC9TVB6dnbGZ0MRHeIL1qxlKYf+/4RMJPYXULuhCqhm9jkv1bpMbuDLATKNNwMRr6bzNLfBNILjgYBbJHnEDzgqBpCjckxdc4LmCeX5OwnuhBOR8maCOShRWgbvkUOD+y8uHUBwpMHvpHp38+CVIRAbmPS3i1rKS09IxpN5Ic0DYDK3FngYV0kVCiHenRx3lmYOgRn4iNuB24kLXSyTdQ==,iv:G85YO9nGE6ABZS5LPEBhyaiU/Zei19H3ImERCjk8hQY=,tag:k7docmRD/3anaMDrzKuSpw==,type:str]
+    password: ENC[AES256_GCM,data:MrmA++fEUNNJojl9xAHlaWjhMrpAWjqi2X+6x2dWd1NZU7gDpLR16hDwyj3cfTsK,iv:iVN0pOx4/VrlcUxeHtMuavM/Z0/iZSGE+oY3idCKjtU=,tag:QiWT1xT8ntcyAjOU5SQLGA==,type:str]
+    keypair: ENC[AES256_GCM,data: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,iv:pxhCdbDA0jZLRFLg/2cXy9j18nvWOgIHMHrgkAfYSbo=,tag:4Z73qrehEkiLca2HO1MhKA==,type:str]
+    masterkey: ENC[AES256_GCM,data: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,iv:TZrIcQKmo2UtO0MdBSWJZmn0nIZ0cjStD0SZLoiHkT0=,tag:D0qeJLtY0cwA2yDdCP3UYA==,type:str]
+zammad_secret: ENC[AES256_GCM,data:Ok01cE+lgNaN0+wLZuBD6k2gsyTWDFVXEPprEvdwlIAQvwqYu2nou0GiCEcm/NF2cgsxERH2rYxxS/lPXIQxXjvHHLfovLSMH+Kd1F/T+qWZioDz7tzDV3GBom52c92kZ4XO2F3udku8IQLGsR7J6eA/xY7yj1g2CF7Vt37BMkg=,iv:5cdEBtgjXoJCve8PJDUcLQvXwe7sn/mgZIOUhzJtr/c=,tag:4fLmvfG6Ujcb5J3YGjP7Hg==,type:str]
+hyperilo_htaccess: ENC[AES256_GCM,data:FuHR9S6FhVyraJ6w9j6RTUryCqgVrhpfQg9y2OdnaqMFNcIR239OBmvqn+WlgFxcMqJtpIKe8ixBZq67pjxbSl2p,iv:zKMyhEJ160MN3+54csuurMXvIAFfWG95bv/cIH3hqJo=,tag:Nr0G7qx8cdpNoW3t5P1CBA==,type:str]
 sops:
-    kms: []
-    gcp_kms: []
-    azure_kv: []
-    hc_vault: []
     age:
+        - recipient: age1x76ajqw8w4l5vlkwt5s3flz5a5jq5qlxv7uppmnf8ckj9egh9ekqjclzt6
+          enc: |
+            -----BEGIN AGE ENCRYPTED FILE-----
+            YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBwcEFnOE93NGtwWGdlY1dB
+            Ti9ZQjVSc0VyY05DbE9iZkQ3bGVCQ1FlSEN3CjF3NUZHR01Lbm90SC9qWmFUMjBM
+            TTNoWGtlYnZMOHZRMEhEbVQ3d3pINTQKLS0tIHZnN2REdnlmTWI0aGc1K29jaTNW
+            b2NHTjV3b2xjOUxIam55WGFMM1N4WkEKibrW+oTxXWEkdWLcQA71u4zW0I42MV8V
+            IoQTPOJ0sfnKL1d9LflQ5aClC0sdXe97MZKoq5HV7ZPeL3IIYPuW6Q==
+            -----END AGE ENCRYPTED FILE-----
         - recipient: age1wvdnprpnq2rcc4se3zpx2p267n0apxg2jucvlm93e3pfj439ephqh2506t
           enc: |
             -----BEGIN AGE ENCRYPTED FILE-----
-            YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBVUXlpV1lkOHd4bGs4MHZv
-            VzhCSmEra1h2ZnhFRElqdU5UMUJ4QkNoMHpFCjB5aFBvcGVWN1BFMS8xVHFsUTk2
-            cWZza2p3ZmRRSCtWdHhEOUZSSC9JSmcKLS0tIEpBdGplUngzcnNBdHB3M2pBaXls
-            dFpScGZzYlFQZWMyUEErOVhVVjc4SlkK3KUct/NJwDdeGeWrqbZ5eAIb/G8f/ZCI
-            T4gO0Y/fznXkNf1fm5d3JHwTC8yAzxmSSGu2f/LLzKx1oNeAw0Ll4Q==
+            YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBRdTBXa2wyZWIzQkMxQVg5
+            MjRLVWZwNzVXRGg0cU5lcjlWbllqdWtjdnpNCk1SYnlCcUNKampzYU12ZlE0MHRQ
+            WHYvcForelFWMGVXWXJaOEFmY3I3cUkKLS0tIGlJaHR0STMzRklDbVE2THVlczBr
+            MWM0M3FvbjUzL3p3ZU1zUG94ckV3ZTAKUOAkZ8nlvT36cyPy5USyDzoIG569N818
+            tMM5aQsEQ9vTOaUoK4gtBEXBva7VerMprdcTRYLcSJ/9L1vXdlVT/g==
             -----END AGE ENCRYPTED FILE-----
-    lastmodified: "2023-09-04T06:55:01Z"
-    mac: ENC[AES256_GCM,data:CvdFvN0usYbFG+W6gTXEfoX3vOhKbXkAC0gy9k26zaywPeVNcUaCpkItAp32PL/9r60ZOUplrsUWd0HlOsCvYplS9wrSmSQomkoUjAw5vJFNUT+9Ci6r4T1dRAJnijNvSr5flCv0+Gu6KLBuDuVeS3zXj5HHOOtibrrt+1j7EhM=,iv:bwL9mW23jwfp4jcnuhT2iG4qyCNyfS42B2P3uuX2U0U=,tag:AdxTPlUAPSND9+ysG8O7Mw==,type:str]
+    lastmodified: "2025-04-15T12:57:41Z"
+    mac: ENC[AES256_GCM,data:NKpGBhz9WFt9xbcbIZ+S8fkgbhfOk4g+5vhXSYPz5tVF/uLDjI4+T1nzy1yKVJA+9MGgQ5OHXgQ7kszrXHgn8fm+sG++MUEXJILcX840Poo9wRBhvDxtNL/oLFbSHsQ0FDe9oCcx+/T8Rmg7vYWARlokKDsXZ7wsTYjF9GkBivQ=,iv:SKVBvdyT3cRTfXuenLDEgk0yJJltwIBShZOkrDfnI10=,tag:58eNQ5k5hTUBTr/nwJULug==,type:str]
     pgp:
-        - created_at: "2023-08-14T09:07:55Z"
-          enc: |
+        - created_at: "2025-03-07T23:03:16Z"
+          enc: |-
             -----BEGIN PGP MESSAGE-----
 
-            hF4DntlvaG5T7wcSAQdAd35Nytx4deWRNHb5IOjf+DRVCvYq4C1PsINL6snAk2Qw
-            C24cIS+h7nBFCy0zOiXsFbbKBdIdqUXWTOF+8GLXUlKQpbcWodlWKVxQidw4S3CJ
-            0l4B2ycQM2NFEEVaPbi6urnjw5qTAo8UNXNHXrZThVURihhdh+Bi5mjyiKXaXj3B
-            lrgbuCdYstsnewRCP0BIFcwjKZ5Ou+bmp98nwxorN4VMv2q13R+2pEc8f7Vi0s4b
-            =Ldji
+            hF4DntlvaG5T7wcSAQdAoaGQG1W4yMq0MxUwvYIrxwdzKKBHJgv9hLCPvSyJwBEw
+            f177FgAT9PQW69wM2x3QZm4+HsozWqs3k7SlMRsRy0he0fAtdkGUr0EL0DCFi5ml
+            0lwBJxkI/R2CI08fRqkS0GuM+6HiV6BRypVy15C1oXEhxlE/MhnCYlP/0MeHbQ7H
+            MkStwmbVqvOm8aizboD0EePZpHC0M+61ULgMt9HnzWzQ/ue/YepI1dPfEWBQ1g==
+            =etd/
             -----END PGP MESSAGE-----
           fp: B8E1727497FC48AA14158BDF947F769D7B95EC2B
-        - created_at: "2023-08-14T09:07:55Z"
-          enc: |
+        - created_at: "2025-03-07T23:03:16Z"
+          enc: |-
             -----BEGIN PGP MESSAGE-----
 
-            hQIMA/YLzOYaRIJJARAA1WiV3O66jKhz2CpOq/uow4ywrQmQ318d1CFHUrz5KPy6
-            eH9V8Ry93UF1NlLOLTGCoUcqF5k9E3Et8P3asn7rg8YeB0y25FmqmjBGL1nEmnFR
-            KTtHYgdGpy8SKds4HhWVZBagL6NRW9oQ9Tjcw3bAVNL+QnKDqoTZ74l9FH8KHHj3
-            66/oCp7733d88JjTEFOLIrUOzFIcVOEvLjkqSt2PYfYQhvG5Vm74/0cEM1lI1lyq
-            drA4VDrQgsEkX/NKxI7kJrJ2liDXHMJgMoa/2Op0/T7AZgNJd07BrNSVUElyWRsq
-            HESSZACOTbx8iKL1dvTF4dEjOKrgigsi1eA3YIgcNbgBVN/+jrrUfvZS75BziYfh
-            twM8Yy82bp6T3+LP5OD4UG+PlVV4puLwbhGhb7BKfYYclutjjA3aiS0JZnRA8FEB
-            +qOehgy6DWURvNLRkOiThZLGvQRJXsTPjh7+S9gwl2sauNoenNb8HxI8ukcO99tk
-            UOLmEwcM0Pojvwk1KPzEl7QjsbsXUp6wfrBky8nvOApiSKo9u5Gw1noBTLr+eOmt
-            g20c41JJ1CiibQGhOWY+EIUQImM1jJdUDrQoTEYKXu0sh2Obv71aCsNtJuirNi6D
-            nexdRm+9PYSaDoTLqDo1xGP6qBZZF3qeFZ06tMARDovmPD+V5xHvCCQftPPceznS
-            XgGPKXd/2QSiCEnlYLP5cV0uFu7saRSn/Ez+1WQA6uGK+lMfuSGCCVs10CRwArS8
-            UGO5QYgW8Kbp9D/0PoWWJ68Y3DndUA6gzs434Wbj8mqp7DTNuN6awElQdznXxEw=
-            =OYP/
+            hQIMA/YLzOYaRIJJARAA22jsmHM0HtkFMMH2p4jQn9E1n7eYGwnnD51CxdwrYI2a
+            6xIVwCkSNlIqzGH8RysaPdXth2KCyGUR7Ll9KRO3wIKAnpY7lIDpI8LLz/ad8+ez
+            jnw09oVN0Hiob8Std79pVX2qGZpKwjJ1r8u/+QDhJpY+PkgGU/e8fIYYxb8P4SMJ
+            aWdh9tCL2/z7UDKQk0AjxEKbFrATfVBNYkyI0d+QlXJVsUukaQ99KBtTABboNFW/
+            BYaAqJaJoGkJLvQQ23NnsKZDhZ7eD4S1WTUyJG2C/lBSu7e1t9lftmyv4MekeE5B
+            wo+9G3hGzUayXWQ3W28QnvGJO8hqDkwBzyAzN2532UDymqsUZr4uHsuE9JwEg171
+            x5kxpd4UL5WTcj8LTM12tDLzTyW0exUIiM0mP9U+F8dUkRCH5fqmu5peCb18Con2
+            pDDHhhrqd5cxIHFqFawPufRtw8N88fLSzgGcHYq98bP83/SqIzpRDxWPayBRxL9v
+            84oPPRbRp4WsWlzqBY/D3lhE/YM/rAc+BleWM5Asmf3yVkhfr4rLW0cD8SL255Rh
+            PTFEsfhaNG4RN0Syt2toM6oiKEvO66akijWXhNbKKhr4UjoezNxr9XRi4DIHrwf7
+            HyEv82mN03C4YhiQTARs+UHC1g3LPjOUMyaQuntZruEpQJN+UE3K+Uxldlt9tGPS
+            XAG10G4sLXfeeOEt/HFVe3jf31pAMbNGozEl253oZ06EZdbjqMvnPcREm4qFYsJ3
+            CtF0ReA5W6/vLFwusIR4T64tV7w9vdH7JH0VFisZgb4fZqohbZSRGABbCkib
+            =USB6
             -----END PGP MESSAGE-----
           fp: 91EBE87016391323642A6803B966009D57E69CC6
-        - created_at: "2023-08-14T09:07:55Z"
-          enc: |
+        - created_at: "2025-03-07T23:03:16Z"
+          enc: |-
             -----BEGIN PGP MESSAGE-----
 
-            hQIMA8uqUsBLHj6XAQ/9GYctx6xUG2+i2RHYhTQIytBpLCiEte8zvfLcRM7k4fp9
-            OBo1Rne4VI/TJo7nZP8HBNlD3clLWCbi+O2Xz86ACUaImiDX1wKeI7h5Im1mNFOi
-            /hlKCjnYOt57gic7THt/xtFWfl1QdZ6klgBKK2eAx0sSl8HfpmozHkb2NVo1CPYV
-            rPGBi/bpACyITyt0AW5413b3Ltt1kTfdsSH8myBzobiA8xJqDRbcidtw+9WVXp71
-            AbJzUgXELzZqjcMg/8XZ0rEK/nBMIi4yE7t4lVbNLH32ECLGZvaEB9hqCqOUPcGC
-            VB+fPCYZBbzMuyyFeil6eX0lI9PiLHKNMalMvHz/YR+7okZ+E6N5yHP1E6FSS3ds
-            +IUE47S9bV+mgC7UxHR7rzvu2Sw4ZOuJaaWHEuG/oipOxMxcvLR+KRy7lWynSykp
-            iP0bv85uHeUq4waJ4U9wqWUu3zUXGbpkr2vchSoQP8KohGsU6mJUkwcgssQzG8Et
-            4oNOPw4X/QGn1Sp2zZBV2/b584VW8KA+StyYTeDuW06yCYTzehIwngqcr2AAv6zk
-            8TmlqI7kTTcmPiOe9YgjqMKP8nsYI/xyXb1Rk/X72oh7UKha6x+sD6yWnjBMuiG0
-            XMvXROVl1NkA5fbdym1cW7su7LEBKpwl1a70djE/afl1DJVYQZdX8cR7aQRMa+HU
-            aAEJAhAqALQJDkMiTz2kmMw7WWrPENzgAflQ7d6cuuwZ7OLPwCqv6/Wb9yogOvJ4
-            ws8j97f3BPdgUg4KSqHEqIvOg0Ccn9CrGgzEReMJTHNwltmkJzlvIyJSp5F33y0+
-            /DtN0Yj/UUpA
-            =KgiM
+            hQIMA8uqUsBLHj6XARAAmpdUg8/6iT+S5GHFGUdnI35UyEPOx7h6gjvvSWRDGbY/
+            05CTwj1JBi02FLiiKO0hPus5HAWgMhL6Vi0K//y6soKFhV0lC26s1nJFG/AWrHa1
+            yQiuvcRTsj4fn1S9GruEaj2nn6579+aYCBRnM2SGEE8Us1oFkkuBXsEgzObHDsZ6
+            2trnlFJV8m7Uu7QOLN4/ghCB0A//2TqoV9qHneAyPucL3pP+HBaoLtoc1DzgrZBL
+            S+SngFUBIjiM0sRyAngAORpQYJmsW95zKBBE9Q+CB+VRy6Yxnz1yAruwODQCEyY+
+            bq+yXhWDnyrAAvdgi6ju75eQ7nZvoZXh7hPfFKFjYHQVY2zSuMIkU37IPtL/R5z8
+            Y8xnI3hOlmQ48IcjXr6jwgK0hR756aJ//XVUesQ4VTlA/YnaifSquakZ3iwmOpR3
+            PmNceArxMqpN4FRxlBVDomEt2D9MOhzyxKd/Cs6sCO3uV3k3YFDsV5+oHt5phuuA
+            fBApR1Y80M+duGzujM2QCqTWx7Yi+I6LBcfv6P/ya4W3gdDjOZ0fkGqF6H9mcI20
+            rDOIMcu0pA9hA8H3FzG9xABsGQtKEFzhd4ylMSHLeFU3byUo9f19R3+0rL6eHZMW
+            2kbIhn8dmAeBc2Uygkma1Ru2NiM2ZpHFm3q8YPkolOShE4X8g85pTN6GnhD3FHjS
+            XAGv1hzCc+R+PjGNvtEcb/fcejiQ/fV8ps6RO6GQHrNnRiu8bb22KX1OaiAMjffH
+            hwv/rMTWI0TIBJSxPTolEF4JleD24QvIrQBBhEBXkiSJrcyBaj7evXueV+PQ
+            =cL+E
             -----END PGP MESSAGE-----
           fp: F8634A1CFF7D61608503A70B24363525EA0E8A99
-        - created_at: "2023-08-14T09:07:55Z"
+        - created_at: "2025-03-07T23:03:16Z"
           enc: |-
             -----BEGIN PGP MESSAGE-----
 
-            wcFMAzUXo8ZPJwGLAQ//S2W6YlF3uMy5fohr7s374W+JG/uFB7fFfpUwaXis4wTY
-            y/ITUZ1CtyBsN6HT7iQsHwVN9bDzI1o7qbWkLgoSNNNh2AFT5fmWFZb3GoGPRiac
-            1yVAGXX0V8+KaODGLXK48cIVPUFfwnpR4cw4eq6SnQyA77fYnCeUt9sxyn0KYbOH
-            SQ17FpbaYMk66KzOEqQhnN9GFCfnf+2e/7/jgimeG7mErlCT6FbkYePgdHQ2W2LO
-            ptSBelbuJUIg3gjzbhQHgYSmuMgwKZtRyy4Z8ELzmQorLChM0qlkLXXVAk755WuK
-            MmDnPiXrcB0QsUKn4mhuLA/IKR+FOuFP9nmnN8+sBcS0OaXXDYXrjqs3/dDyfXca
-            qyP1c3NMYkgHfevdXz2PNiq2d7l/pNBcfWKcUa7V6toA1n+UonjE4GhMl+Pr4Si5
-            6NWX38YmgCnnqGmixP+BrKufR8xyt7RZvNUkSlO+fP2rYpg9vuFVcsKF2wVP9w5z
-            bW7vcxPc8PYrTWi0p7DDFqfEZIxnG55hoIIy6RENbvFjKVDZQNX82SFi5hY+9yIX
-            JpW5Pf3liCwHQCxHN8dc3xH8bTmxZwbA4wZ04PMccGvtVL3IY+mboz6iVjwtvNen
-            9nKVE9ad/ZAH2wejlk/zSz+3JOXjtCxLH71cV03NzuzaA61C3OazSJ9K109xZ8/S
-            UQH9XsM1jSbEzawuWk2bnFtaQ/fGGQU+0Ph6byeZLhiNRoBc6nNxiQ3BFV9tCrpD
-            IZBwIwwU+OtB1LNEWrs7wrD0NAgidKNBxZemLrnTUgrc0A==
-            =1eth
+            hQIMAzUXo8ZPJwGLARAAkDuS3LCJqk3fyqqE9orB++QWD1U+chUSQfe7RlBSp9O9
+            84bV5+RgWjVKmJGnr+8iYnAqr4AJRUsPw8d/4v6VOrQwOO0cPefGRBDI69eEqj3D
+            sJcCGdXrhrNaRj5oVtuqWP6zracmpt9mc8zpvJA0cQL7Ldt0YZnK2ohVYNe+obcd
+            twD5L/aVrLjvBl0J1KOHOoaJZilmv9dpbrjJzT6cFI6SbmD87IXVC7ME+sTfAXdw
+            jPrnKWUQ/6ww738EiKf3/rwgn0rIVnV53djy+FHXD8Ki+69TMYFXz8pZOI43jj4y
+            M4BR3PqaoisE0rXd5IbmuPDBelIXalpl5MwHnvlOErpTCmybJ1FyHEhNDHesoss/
+            Kn41QTDeG07qBlbsgYOdA0K1+ZfGTctDllITjcJ21fBE4d3uMfUDo0EDSu5aZSCQ
+            Fz/YGPQQ2J8agv/HLpiruF8pGboG+d0SRfnka/ITlqRhG+x8xmyiMxybgPOfDV08
+            neRB4Jl83zVfbBAdx+JBdW3p0twRWrLCUpUCh/L2z2RH8wVd5mijFH7aM7c2sayy
+            IPmIJYwg7cztM+bTeuNqywDSiNp8Z9HUxQd8QJOiG+1b3NdDswcaLWEIKhvkCQOp
+            cZGaqPXr3ec10Xxkk1y4PR/xPPvOgDZvONcnYLjS2yhHhNLDSiyDDel5grUqWT7S
+            XAG4pnmOC09sTOTCq+VID6qREFTYLMpN2sLVD2tmlU6pICifeLfWaX84vgDqouew
+            av7HX2HXExY9YdhEDEj8VnJAmSzkOI82YNQGFACCCbfMTK0B4EtW7KvR8Njv
+            =n6ef
             -----END PGP MESSAGE-----
           fp: 116987A8DD3F78FF8601BF4DB95E8FE6B11C4D09
-        - created_at: "2023-08-14T09:07:55Z"
-          enc: |
-            -----BEGIN PGP MESSAGE-----
-
-            hQIMA30JDs8MiK29AQ/9Gz9v/L97v6sxNyplvRuvAmSR6lXjRoBIWGYiX1pJonTf
-            LIs/X/6Jtjata3GXeMJ/XOnFvmHrkL0m0njayuuntiA/F+vaUTe9f5gHRdpcDCmr
-            LqAQqXh4KlknEUszqQb/5YG+gK+NnmQigmBucFRzYg47fm6WGXm4zYkQP2mgz0vR
-            LOlC1b7xrCt/Nt5NLYZjrxzZYf+UQ8zLutiOD+Y2jYElym5rmQdUP1wOxx4VWHJS
-            EMYkIgNX6q5Pnk6AkfFXLEQlNbym8ljdO6Nl9fKLU17NPddgUXPKv4aonN52QCwh
-            XRP+40FEa+cvImzgjycj/+V2CUDaDJmlCAckv5PrOLL8z7/v4Qrvev9gF6lhutZk
-            SecHblTxjFaYoNK+Ver3kklV7xE7BO0CykthnaKLBcu9ERfWohm0o4FZ0XMnxlDL
-            ExuEJ/8axaeS7hRVccMjcEyA4/ZbsyUWziE+jEJ7mNimKrTLK1XNjade3VRcsTy4
-            m1lsfN/RDvCbJBrNHRQuInD212Qpc+durZnFdEf2A8ymTvVWlZoK/UtUSPEfbevf
-            UWOToP558fsCHXqgzula3cqfXLJuED+XiPaxdpfTCU7XCI9xaW1kGiUOa42+CPU4
-            o/aGMvlEQJiegZ7A6J7Cl4Z04DUU4RTadLyIZZnFFdhEWKhB+SpgqywOxAbAoHHS
-            XgE5ZZfsZbP2XJ9t48pU0mTRrplg4NFu/pHxnDn6wTMDqDCvF+gqNJsmA5dNmFDD
-            XE19avAuDRd9JPkyhYDZD9CvxoIOlzsJfU24sR2wWqoNIFTDl0vG2txXJVz93D8=
-            =GZZY
-            -----END PGP MESSAGE-----
-          fp: BF37903AE6FD294C4C674EE24472A20091BFA792
-        - created_at: "2023-08-14T09:07:55Z"
+        - created_at: "2025-03-07T23:03:16Z"
           enc: |-
             -----BEGIN PGP MESSAGE-----
 
-            wV4DNffZWjBmO5ASAQdArEfjFNtkwsa6Qz+RgLCqUa/3G38dx8CgynaUkvMsGUIw
-            2UvO8PIbp5pfI+9pdisQO+0AXJMTxisUhSkNWYa64Ey3mlNmRDaH//Qhfb7RI+/i
-            0lEBxRWqhmcNXRmmV7Y7EU4we3NLm73IN7HMNyxNZyzGirdJED1Ie0kPl0XJpOZs
-            WJVCpC6FJ3aivsYMfv7PL/aSt7tNg/TTjxMI8W6m7TmlV6k=
-            =dvRv
+            hQIMA30JDs8MiK29AQ/+IihR4yGJDJnDXqtggP3mfUIFajsUgNSdk1NHJ23MtM2k
+            hdvre0HaHKnJQprHmYPw/cbI5Rwogi+5xGU2sRsiHqBLZk1WEqgWcOeBMV/t8nc9
+            p7M9LoKowaWpQ2S827+An6AemFa058Tj1pa4oXFzWC6jsl3RbB/IC0meyYJjl+Dk
+            gu556cVbpNRS0Z7wMmVJvBkErP6426hZBAHQcxaz3YZG9rqHZTCmd4PkzmuNVbU6
+            iRF5b7Ov+IMmVd4OmCrs4sw7KrHahIu0g2U8V0smQphvV8MDKc6sMIyscT9FFYki
+            6qmqCPhtTivTG1lTTCm9W+CIr2240VxpuwLiS9/OKke63gpSa3NJX+V9mZ+jMy43
+            V1nUa3J45Sv1S3hvVpIsfqnd5xHCSB4w9X+YXJll0AEcMsI38gDQd4/stTAlYRIp
+            O2hygqMY8bjPdt9tS3AHmOMksNdD6vlO72N68ugALgcRYpq8chUhHiImKXKFI5yC
+            YFFBZhsM7r1N42ti1yDR5cBF1f0ZYIYCwUedJ6fLOZsG3nVWh/isho/b0vbnNK3P
+            JLBMW1w2fYpH8fIEDIYasaOF8Reg+yPh87tKA+M1THam8ek5B0pFs03HeYneXJs/
+            geyYOYD43kHmg4bM7SgjOMlwVALz0tYorNpj/p8ojO3cBfIp1lKuegZss2iHImXS
+            XAFpEl60dqYeehT51t4/bjg7e8L0tkQf8/CwVATvMi6PxrPjwlN419fn22F9ZF+u
+            fRg4nX3ZMBTZAE8xhkQ5MVn6alLTHQu26Yal8bhqpD+VJ3JC5CpdUya3VMNK
+            =oelB
             -----END PGP MESSAGE-----
-          fp: B1A16011B86BACB56ADB713DB712039D23133661
-        - created_at: "2023-08-14T09:07:55Z"
-          enc: |
+          fp: BF37903AE6FD294C4C674EE24472A20091BFA792
+        - created_at: "2025-03-07T23:03:16Z"
+          enc: |-
             -----BEGIN PGP MESSAGE-----
 
-            hQIMA6MARpDCLIz2AQ//RwloWGHjB9zkZuDNqiqYqLT7L95ETHD3A0rEnLCmMHp2
-            0/FJVjpghzXeCUcTqCPGGvETy7xsjXAzbbrZgFFiCHj+L62WBO+h7YuMtZygBUY0
-            nvoKyV7247HiL8PZlvJVXIChrypKgF2iTotgTvmB9rJWrZaxl+M8W7ce0DyORwXT
-            ZoWhfR501SWrvXGCe6hdH6+qBdWgUYlZJu/WhLZ6Ii0kQCeIJjC6lG+8fYFv9Hnq
-            mRMD+6piReFBehm0LSK806V64+NCHYkcFPXmoRXhZVQMtIbc4ONjOB/XMVk2sq11
-            x/SRossZnQ7V7x4mP1chTLY3DWn+l1kHM/tD7Fm6xYxdA9FUrKhToEyM6uJ0GUVf
-            V+dx9lHvyCseRtgS1sje2SllTfP6i4tQtFBSlgOtKGP0JBVH9uNlYynIMeRF0ssD
-            P+k/IYaLCB3d85w4kG8093QqhR0urrOzy2XPRaAPE3CFJcxKWZ1pMIJRNjKqgP3f
-            zeuVtprUicb9j7pejcINodG/tQAwy1gTlKkzzgDRHlDzYz64dA7BOAHXL/C8PeQK
-            OrFGJ1+FvnmD6L/fnKr+p/dnHWq/M9OyKj2wPoiq3VZU3S6SAfORKmH1YbLNr+iP
-            ME+XDEXruZZC64Hk5wzkVT6081/NaHfyWfpguaJ87f5Ruto6mqu7A5Y6twIRsNDS
-            XgEmEktsdVD8egMQ3rnRqPuWo9F+Xa0sOjXuQEwxWAKtTIIHuZgkyLYVHJc/2ati
-            Ml6yr7uiXOwtUzbo1a/ocKbvcQg1Qi4X89/aGKyI5L3XCn6YPStmekeyvJRFSBE=
-            =9A7J
+            hF4DNffZWjBmO5ASAQdA07imap27d/V8R6oVOlXUxEYL8QJcTaSiBICEnBnfJnww
+            i69xnBOhGKwnWAgFipvXwFeyiQcmr+LnLbdXB5YGTblDzG46PCEH8JyeMakHpHAI
+            0lwBHOOaak4roVNGoxmDedniFhdH0rmd8c9MtG00iT4Bc95fjM3eSFuTrESWPY9i
+            RUApyfYx9WJq5jZ+lFii3uOSQg/rMpVTIpjjXC3HyJTvfy+MnECBAI6+cAWsjQ==
+            =J26p
             -----END PGP MESSAGE-----
-          fp: A4F92BC7B792108A463995827C1F2DA2BC929412
+          fp: B1A16011B86BACB56ADB713DB712039D23133661
+        - created_at: "2025-03-07T23:03:16Z"
+          enc: |-
+            -----BEGIN PGP MESSAGE-----
+
+            hF4Da5T//DC6DJkSAQdAkFwtDJxwbCFZ8EyxkK1R4v5Ntfy0QIzLpKk1CxXklnkw
+            GKQPVXU2qFH4UwH/58fKENIGTVHlDRXZa6gALeW6EBr3uQFoJ3d/APHpD4nprqOy
+            0lwB45yH3YjdTG2YY4bI3eZKplK6R9mK/lzAVG7zV7nVs+glr6/1XpaeYJxiT1/K
+            p6aV8I+/FTg30d7Rfv2PpPaB31spmUxA3RDIbybzn2uygwOdKB0PQnnGLAOXBA==
+            =vuaK
+            -----END PGP MESSAGE-----
+          fp: FBBFAC260D9283D1EF2397DD3CA65E9DD6EB319D
     unencrypted_suffix: _unencrypted
-    version: 3.7.3
+    version: 3.10.1
diff --git a/secrets/tomate.yaml b/secrets/tomate.yaml
new file mode 100644
index 0000000..01caa04
--- /dev/null
+++ b/secrets/tomate.yaml
@@ -0,0 +1,162 @@
+portunus:
+    search-password: ENC[AES256_GCM,data:lUG8qGioYZOAQHRhDMCBq6rRRFOs9R4ohMHEctxi/f6soE4aQZHyENEW,iv:6wgDgre5wr630SkRlT2kHak4nnOkx3DVFbNcq4FehGw=,tag:S5EiXEsoId+pGYaQ8lq7JA==,type:str]
+print:
+    smtp-password: ENC[AES256_GCM,data:XoaLiEpqAdKapeS9YoBfh2w7HFuTCV9rHIciH+qUbhHcdsgVpnPMsSlC,iv:WxfP5d2K9soJPoRPuS6O6PbNvo4TBQjPGiV0e+a501Q=,tag:ZsTdR+b/oYFAYz/MN73PFg==,type:str]
+sssd:
+    env: ENC[AES256_GCM,data:9IbU7uaElmemQHVUvsM88hcyNl3WFehgQeLZPtUxt2Sd0IECm8qNkQhWJ4kuvoBnQsdsUrFm/0QuW7AfDFOeE7FxMxg0,iv:dyzsYHlqClWbfzsoJ36iYjaXWpidB1ZqHXI7RP7js2Y=,tag:97FMOeVwAEy8Ka79uZKC8Q==,type:str]
+ifsr-apb-auth: ENC[AES256_GCM,data:hxJOvRbgjB//YU3wy04P7yrQbV0Ggoi18wQxwy4hHgbXizTHbmlfiZ/MstITrZQ6qEPVBEW41/iGU3DO2Cg2ofpWvFU5Gr8FM1AC9DKq8SppLGqzel1mEejPfrh4RbQUMe0zZlc/YfhCah5sM0oPnBQNg8bPpveEO+5/bRq5S24jkkv7w6/AAS8tGvjALVf/g95jsCrQO2MYg9jCCEkdhORU0bowGD8cjTr6wnPkNhwzn5tiKoPn6eH6TFBkqNC+Q/5E+os10i9F1c3z/sv8Snrcl7V5higqrQekhEvGRDmax/4lE8Yb3AoxC/2M4/+9x+OPi0JUkkhC6rghETXpmYkuaD7E8+eEtLeSbiJPlPijq2HTtbtsHcSoMUdoGO8644TVe/jDxaEe54p9OWEFjRRpONijQKsfH3wENlUXmqDQDLfMSpoANxIHMh+RmRzktGIvTgvs6rlKXsWp7/gggFVxdM/5QPbE3pUvGr+JPWz4,iv:6c1HxYGrItPwKzAnQ0zUvO3TSejVZ/aWF9zs99ufzl4=,tag:fELOskceJWKmkm74MCsfoA==,type:str]
+sops:
+    kms: []
+    gcp_kms: []
+    azure_kv: []
+    hc_vault: []
+    age:
+        - recipient: age18lwgjazaxujqgcc5j0gjllnykhtjn6p0q44jzrsk4au2a5k6nd9s77kd6d
+          enc: |
+            -----BEGIN AGE ENCRYPTED FILE-----
+            YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB4bW5yM1o5SHNXZldjcWtO
+            NkxENWNqTlpVeXVRS0MvNFhCUnNlWmVDSkZJCi9KNiszcUZHTXl0WXdJMExtcGYw
+            WFZVNGJkZVRrdkNRV3llUEJjay83NmMKLS0tIEhWNGRkREJuYWhaamFWQ2lEZUo2
+            TXVrMHZCNU5zOG5hVnNkdEoxcTZqWXMKA9eG1zM6HeLAAOpIo8Z5+5KD4Z5P3rdc
+            kE8sUXHD3d8SMmSKcTYe6gGVzFuw0xxnMb/AmjAQosvDFTQsWy1sTw==
+            -----END AGE ENCRYPTED FILE-----
+    lastmodified: "2024-04-16T08:58:21Z"
+    mac: ENC[AES256_GCM,data:2aOOVZK7kshJFBWphvW/BqRUXht4p80Q15nGJNA1EbjT05f3tYdrr8QuM5Xd1vJO07rgmokWv4XwbzodRIwqidEXD5xuJ1v+kHC/jJnO3yrBKY7kVMHkia2Wq00bcN/iwdW6G6AP5D4HQbmFNo+rLHyjIVwPvtu9jutKpz12NH0=,iv:YCBX2gSEmiUa6HrHi0VEcRGWDJrXGajD8ZbOZcppFnM=,tag:FK2E4hukl8oL5aZNTCQESA==,type:str]
+    pgp:
+        - created_at: "2024-02-29T15:23:28Z"
+          enc: |-
+            -----BEGIN PGP MESSAGE-----
+
+            hF4DntlvaG5T7wcSAQdAVYr0vThE6byTzCZiUrErtuouL9k2b3uTQKR3pnk1qmcw
+            Pw8+vdUOal5i/M9jFWexJzJ1nenzhIogFWry4FdXRX7V39/nRJQ1mbF3+3T/yldD
+            0l4BdQ3xmtVUiz+PYCzazHC5+wPB4iCVs3fkTiLvNBNzUDEHvj6T7w72eKhld9VT
+            NFcOI2lSDea9EYksEdLef4VnE8gI1DeYxJAc60GXydmBJZO30xeOFMru+XE2N7Cy
+            =S7ex
+            -----END PGP MESSAGE-----
+          fp: B8E1727497FC48AA14158BDF947F769D7B95EC2B
+        - created_at: "2024-02-29T15:23:28Z"
+          enc: |-
+            -----BEGIN PGP MESSAGE-----
+
+            hQIMA/YLzOYaRIJJARAA0fWU9NbHhvooG/4gYcadlwNPtQqOIw+6g/L9Gx8wKEhR
+            i9451S9oez3ElkwIeiF1YPCzokF6TuKv/++nV5SjC9PZVSHnrixrQscdN1FtMvH9
+            ad2dC4GD69fXebq3f1vj77fAZxif6OMcEMpDiRRXHHJetzuUDkLpk0YSR/ZM23m/
+            ag/JrHNUNgSJPLFRjvSNqX/DO/Etf/RfEwuMoPmwpGrQFhBTwtcIjjrJ2zT38q21
+            PhWrjAL0Mxjnt1zFOGLLXwV5wkpmLOj2GGIBJJ+/B1zEawkbOx/ROuzPFKrDmrrh
+            xz29AVi7Ok9JanwgPGRNytnUHmxToisIH+FZqwpDTsop0ZQOCtiUWIZot+i8XGxs
+            rJhHTKxetJfsJCQUe4K2RJtHnVKIluzLDFyxoOb5SVmXoslY/EIQEYJz2lFPG2sc
+            PbP6XUh5ObZTK3IRIFqeQzjjLI1eaLdYjEOr+a4Do98Dd7+vJ1nLwkC9Wo7JLaaR
+            yd4emYpyB8R72Zf5+TPhN6ZWAL97OQdCZCSxyh3hDUZt4Wckg3I2yjw1mh56yVJF
+            fFOCOA/nXWpYXyRTbxuPuvCqjVsmVDEh+STZLIFsARzvz+yrlpEFoQw0G+Xa/XfO
+            aUt+HWGYji15+KVZJnSOXHhN4z4amsg8mtAEKfhCU7pl3jyrxa2MvArPBIU8FB/S
+            XgG8vs/FjyRkm+BvmHHEHmh8JkC9B9Lx1kmMpHw+fMis90IqZ52I82RmA6aO5jqR
+            ywlIrtvDLC/5STZaBdTt6EJwf3OAvRY+H1Br+SLtz9xXUxgzV9JOHWTefzRH2JQ=
+            =3Wnv
+            -----END PGP MESSAGE-----
+          fp: 91EBE87016391323642A6803B966009D57E69CC6
+        - created_at: "2024-02-29T15:23:28Z"
+          enc: |-
+            -----BEGIN PGP MESSAGE-----
+
+            hQIMA8uqUsBLHj6XAQ//SVU6B8zLxtpp1gQ/U+EGpgbP8N3IrIcUPrPVx72dIu+y
+            7acCmdgralvEe/MLG4vNSnkrJdCCk7piBG2JFTMUFbqbFkUVSyZyG1yRsyezB3AZ
+            czSdffSQ0SbxWL73ANO7Z4asRwXkqjDTTKTl3xL3Iw8tDu22INeFwmcZPFV9F19J
+            K/BanSBuofUOiQB73BNv/8lA6ssxqufj+9pDDoutzF5XWpd8wUPn2hIcNWtr6NyY
+            wl9U/Jb/gkhnUR/UHGO2Nz5kAWz1lhysa/dur6I1mrxBJ0mO9I0j8A6U2cBPxdG5
+            JBiTuKlFi4RhKUAraPDC+c3fM3Zp6zIOWhZgRNGlnR0Tu3fTeqdt7DwYh0bCOpJy
+            tTtj779hRj+cnleKG0QB89MY3XFpHQR60iBEMnqg522LLlTzLbvu6BEUvM/4jv+j
+            2aq45zyYyIHC99k8xG9vl5Ou+3XhDqqVRUQ3qCRbavupWRKdibaNqjcaMr/zDqQ3
+            TxmDluLnsbCGnyYmZDocwAqVvTVorHEr8yumjViFpXPImRe+na/0JCuxRHbBcVt+
+            9WynLgKy83rHY3tWKhqYobh20mLXNNcCiUvdGFYI2X/wyUmSRMkuNpT8fsvdr179
+            BtERX0a3VpzaBV41pMsEIj7okx1MScMxXEmAktnDEQgyPBwV2CZpp2lM3YJajVjU
+            aAEJAhBKMkW8iDDTeG/ISzhGv3Qz4B2ujOvb22N1j/LVH6HUcq/Kg9tNJ3nDuT0v
+            zP1O6zxkwZsm23at68ZkfxXdBm6Qwf/sblfxYi2SOvWn+fXSmkSfGPVgw0vDG3+G
+            0DskmbWWbRuD
+            =9+VA
+            -----END PGP MESSAGE-----
+          fp: F8634A1CFF7D61608503A70B24363525EA0E8A99
+        - created_at: "2024-02-29T15:23:28Z"
+          enc: |-
+            -----BEGIN PGP MESSAGE-----
+
+            hQIMAzUXo8ZPJwGLAQ/+MGbYqzJX74UDkNrAC1QUvA54pROBzoKnI45ODtT9gp/5
+            9Cv5Hx3d/1UA/IGjGh6Wjv7ljCjH335R47bWBDmnE4WmrD9O2gopjBqAuF/k/tIs
+            Vc4+8AD7F85PTSSdb1t/2hu/gYO/FAmwpwxLBmWD7iwAwpDZoPB3lBYwvqNOlnmX
+            8cFYALyacBBMskwp5ydNEUtQO02ycHUfr3WVC3TponHva971Bsif2Lq4aQW2jCfm
+            0a+RvO9cdv4RjVkd0/eKXnjpsFRkmggTAmXrlrer1hydENbdq9Fl9QHPxRG/jp6b
+            SjzqSEc38wbxX2zo9GihPWRHPNjXEJbXGWfwAA6MZpHpI16NEU98+B3OOsrFZ6D7
+            Zr7BAhVqYXgriICx9K/czFN+oDp5Dpsy1/9NGhx2mg+KJXx6F66MN4ZB24u/rgTR
+            iC3YGoXfx9vq0tbv2m8zPOoJ3PjmmLzfSwXQszK/GOFvu89r57Hz92K4UpyFiLPf
+            jUoT7GDfEnU+4OFKdmBDzqFV2xm5TCnoCpjCfi+kbpszThoaF2L3ZvgypxK5K4cf
+            SkPoU2HgXwL79sXfFknajKKtBFcE55eLggtKmANoBN2NOf+yUQuqwnJRaN+ELtKp
+            1HJ+ztoPEcrEG3zljNZ77/n8B8kprVA+E4sxzLrNHM+sRzoiDixq+nEB7p5F6+zS
+            XgH/wanI8NFYgP4w48mM50rboUeeadwGoju7XNNoEcaLzCaGWkoUWNbtH+NGnaAR
+            OGux0gslpa4mPnHWySU7b/1LZiIop9PyXCqGDyUzjBCaNKQypMMO3BhmvdHf0ug=
+            =oRNh
+            -----END PGP MESSAGE-----
+          fp: 116987A8DD3F78FF8601BF4DB95E8FE6B11C4D09
+        - created_at: "2024-02-29T15:23:28Z"
+          enc: |-
+            -----BEGIN PGP MESSAGE-----
+
+            hQIMA30JDs8MiK29AQ/+JcDqCXFj6/8NsrMx+mv92Xgqo5b8hQC92DxOEVd2kBrE
+            aqe7LPbhpm8ujuR5rRRNKmnJX1QQXn7GHAua8tA2+7oYVhVJfpIp1m3FlOkv6Jt0
+            Mz2FMotFfts/Lq5MGUB2WU1fkXMcZ2J9gXZGoEwVRFFATu4lHy6IZKtbYHm8onlM
+            WXuhX3+uM1Tw4TCaqnyfi8fEGvocpwD1kT4Y2F7VipNVoSbP9DNf9rVIuKfTzLMX
+            NxueFmfUcLt3Z1/HSV40KkYseaZcLhWOtKbpFTwG/zdzdSIWzCjxPGwcK+nVBDeS
+            OhFdcoKC7c9GKb1bbxaPq5BwSpy29/PBv7SBM2vfUvyc9MrKvXdh1VzOgTkSAjdH
+            DxeEtFNMIhrCeCuMZwjBrIckSr3dFh73YqvEbSV/1Z2nK8qLBWKEy4noAOhI0Kxe
+            T05cCaGFHVJxy34lmb1AMHATLt6ZDDUn+kgiOD13SozMAsS9045MSnJgcVCb953/
+            cxx5LfyN3KJO/17YFgNlq28yVavFTp5h5en/DexY35nvvACBi7uah5WQh8Y3fbB6
+            5Eb0t2FcsHY3L11tbjnVz16oFRE/SuS2NK+k20QEo36eBc272cKjkj9CS1w6D3lq
+            7qFQCBD4NWITn1FgHDNfDVNZI3rocMMp8VgBzpknvBZmRc2PQlW1+jIt/7x+JP3S
+            XgFd5m3vvtbLhJVG5X0GMbFHC4QaBTou6buKdfvuQ8ZqUb8o52MPNXKmTDW99ywM
+            5pItfzPtHZ7q8T+rZKEnNcL2TBhgfHsuFzqC06D2jeC/tulvbhw0VtBcnJzSlSg=
+            =ihPC
+            -----END PGP MESSAGE-----
+          fp: BF37903AE6FD294C4C674EE24472A20091BFA792
+        - created_at: "2024-02-29T15:23:28Z"
+          enc: |-
+            -----BEGIN PGP MESSAGE-----
+
+            hF4DNffZWjBmO5ASAQdAD+Em/15kuzC0vIaYSkTGQS5SwwCGRmBc9V5u5ChsunAw
+            RiXIlOl3EhpR6qzxCfUgLSr+WEXK20AFGo8gEfCpKqAVE3orPGh4btwcV/AzZyID
+            1GgBCQIQO4OYcDhulX1kReGuRHVJWLsjvWlUJQjlYPXPaS7QD6vCmie986wNEOAN
+            kqDyuSsoetM3OdZgTvyj0tmTdNNm9X90xKjyV+wcYKlAkVL82PbnEwIqQhlMoZv/
+            0Uhdu9hQ3VXC1Q==
+            =0iem
+            -----END PGP MESSAGE-----
+          fp: B1A16011B86BACB56ADB713DB712039D23133661
+        - created_at: "2024-02-29T15:23:28Z"
+          enc: |-
+            -----BEGIN PGP MESSAGE-----
+
+            hQIMA1tId/HHLgxAAQ/+P7hzNKzSA3JVSSAqfAV5umI1hACWf3ticSkT9tlfGYx6
+            +xWvkwmtLBAumPYwIrVVvKSG2KBdiD/p0CugbpMUA2164IGrJVQsnBpeyV3fgaNQ
+            GMbb+Jq1Nfh5QsmI0X+D4xcNcPae3Ml+4TXtjXkDlowG2c4x7AHiHKnQj9Agszry
+            F+IUdlVt4dESbMGv+ck6fz6AqJ3OiQaesRps+FTrWtVhzuu9DIuup4E+nb7qaADz
+            knmjPfPxX7rSwfAVbvgmGZ0hRM4KaIqt26Fyd0pnnxc37KslgBcQDl2bIu214SKT
+            vmaSwqCOF1JXe3MAKJjFu0e1Rq+6/Dt9sZxWcXi+uKVqXaQznHXrxXywG8dmV2jZ
+            SS1rLoHKZ7Sk+3EG9WAfNA0SDNpcRQ11TXCXKYaNkbhsNucKBa3ipGO+l3ypFWNj
+            zmJMHR9mZuH/cV/DRC7eyWihbcYSAVfOuNYp61KUsW5Z4aYN/yrBZIDi8wqbN2J5
+            TNI13Opj/3Xvu8mVC4fipORvwRpwlFX5hT1ioDZ/vmtgufWWPNSc7XEN6HIie3OY
+            8nljJqPOhdYuTStejtBkt/qvqWWGlpPILCKndqTEFoMv5h7ussNV/+6eGUIx3+1Q
+            G1Hj0Dptw+w9dx/CAh6BVjSCF05892o8UNljOzr0mdxvZYfOrnrMjm2aqWLYO37S
+            XgGHuVKMpj8zFhIERSZj9q5ZZuH4f7AFgEzeRBghNZCGeMlA9T8BW1ctZ7v20wpL
+            q5F3s6h/Vpif4WrdcuVwxrsF5Sar08mJaVRQdJrps6hFENwy0qs0zn55gKIae/Q=
+            =HZ7i
+            -----END PGP MESSAGE-----
+          fp: FB44F0746DF25F0B24A2EAE586C8A257C3EC82AB
+        - created_at: "2024-02-29T15:23:28Z"
+          enc: |-
+            -----BEGIN PGP MESSAGE-----
+
+            hF4Da5T//DC6DJkSAQdALa6lkOmkWCMYVZj7SE95wbejf6w18ouzh0NeKx1SeTEw
+            NoAN13YgKuk1b30zfSbjbr1LeGvk4xvDF+1nk+8dLccUPFQO8svT0/L2DhAQ8EV3
+            0l4Bf3h1T3Hoc28my9LvjvMo7brUGqX6TDRsZiLdOe/wk/EbnuGnTUCtHytxGUIy
+            dtQa263hpVrA1xRIxHyhHRKACp+4PD3SvmDpQ2u33bVfZ9F9vzRPGXvE6E3Rw8jD
+            =Dxdr
+            -----END PGP MESSAGE-----
+          fp: FBBFAC260D9283D1EF2397DD3CA65E9DD6EB319D
+    unencrypted_suffix: _unencrypted
+    version: 3.8.1