diff --git a/.sops.yaml b/.sops.yaml
index 800fd37..7513f79 100755
--- a/.sops.yaml
+++ b/.sops.yaml
@@ -8,8 +8,8 @@ keys:
   - &fugi BF37903AE6FD294C4C674EE24472A20091BFA792
   - &emmanuel E83F398E6423179FE4F63D4FF085CAD394DE329D
   - &joachim  B1A16011B86BACB56ADB713DB712039D23133661
-  - &jonasga FB44F0746DF25F0B24A2EAE586C8A257C3EC82AB
   - &hendrik FBBFAC260D9283D1EF2397DD3CA65E9DD6EB319D
+  - &frieder age1x76ajqw8w4l5vlkwt5s3flz5a5jq5qlxv7uppmnf8ckj9egh9ekqjclzt6
   - &quitte age1wvdnprpnq2rcc4se3zpx2p267n0apxg2jucvlm93e3pfj439ephqh2506t
   - &tomate age18lwgjazaxujqgcc5j0gjllnykhtjn6p0q44jzrsk4au2a5k6nd9s77kd6d
 
@@ -23,9 +23,9 @@ creation_rules:
         - *rouven
         - *fugi
         - *joachim
-        - *jonasga
         - *hendrik
         age:
+        - *frieder
         - *quitte
   - path_regex: secrets/tomate\.yaml$
     key_groups:
@@ -36,9 +36,9 @@ creation_rules:
         - *rouven
         - *fugi
         - *joachim
-        - *jonasga
         - *hendrik
         age:
+        - *frieder
         - *tomate
   - path_regex: secrets/admin\.yaml$
     key_groups:
@@ -49,5 +49,5 @@ creation_rules:
         - *rouven
         - *fugi
         - *joachim
-        - *jonasga
         - *hendrik
+        - *frieder
diff --git a/flake.lock b/flake.lock
index ab13262..1319cd4 100644
--- a/flake.lock
+++ b/flake.lock
@@ -1,17 +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": 1746787348,
+        "narHash": "sha256-0nrVo4NRn6ZoAu6JwDxsckzeNT6lP6CM78BwXmNX+S4=",
+        "owner": "MarcelCoding",
+        "repo": "authentik-nix",
+        "rev": "76a84a6a57144909493bf0d2a130d5290100840c",
+        "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": 1714117615,
-        "narHash": "sha256-Ilu7j7tihFI0jtnsQS+7H0SZX4C61NZHaV/7fJ39t/E=",
+        "lastModified": 1730751072,
+        "narHash": "sha256-+FQjzCNV3k8U4BfNcFmoZTRf8aO9ufn3s7kkzHj/b7s=",
         "owner": "fsr",
         "repo": "course-management",
-        "rev": "9e5ab11788b926a9a26d2aaa0e0958c3c5865cc9",
+        "rev": "60b7062ce47ee9f0609e701ad5eb5e3e0a857ff2",
         "type": "github"
       },
       "original": {
@@ -27,11 +71,11 @@
         ]
       },
       "locked": {
-        "lastModified": 1698049587,
-        "narHash": "sha256-gNxpJdxSrpWMTBSGFO4HfXgr+FiAGtwEXCvxd6W8IUQ=",
+        "lastModified": 1730889586,
+        "narHash": "sha256-SLgo7UjWLaFaaUPFqzKbr9DLAGzm5kparfxuJHEpK3w=",
         "ref": "refs/heads/main",
-        "rev": "2d05abcd2b4e59db421c86fa9adaffa3dccb1086",
-        "revCount": 7,
+        "rev": "a111147ce5eaea4f1d691afe1203e7529d68522d",
+        "revCount": 9,
         "type": "git",
         "url": "https://git.ifsr.de/ese/manual-website"
       },
@@ -40,16 +84,53 @@
         "url": "https://git.ifsr.de/ese/manual-website"
       }
     },
-    "flake-utils": {
+    "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": {
-        "systems": "systems"
+        "nixpkgs-lib": "nixpkgs-lib"
       },
       "locked": {
-        "lastModified": 1694529238,
-        "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=",
+        "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": "ff7b65b44d01cf9ba6a71320833626af21126384",
+        "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
         "type": "github"
       },
       "original": {
@@ -63,11 +144,11 @@
         "systems": "systems_2"
       },
       "locked": {
-        "lastModified": 1694529238,
-        "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=",
+        "lastModified": 1726560853,
+        "narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=",
         "owner": "numtide",
         "repo": "flake-utils",
-        "rev": "ff7b65b44d01cf9ba6a71320833626af21126384",
+        "rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a",
         "type": "github"
       },
       "original": {
@@ -78,7 +159,25 @@
     },
     "flake-utils_3": {
       "inputs": {
-        "systems": "systems_4"
+        "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,
@@ -101,11 +200,11 @@
         ]
       },
       "locked": {
-        "lastModified": 1708628927,
-        "narHash": "sha256-1ObvmmEzbW2YjY/jJyfOoxhxIe54zcsOBMzgehnclRg=",
+        "lastModified": 1744024964,
+        "narHash": "sha256-zmYWGZ7/tRSCy/PzghdguMpAdauWiYr6AJnbYCVHBFE=",
         "owner": "fsr",
         "repo": "kpp",
-        "rev": "05e370097af21ddb776bec907942c60e6aebc394",
+        "rev": "03e9650edb8d1e9ff424c2c2799736fbae56314b",
         "type": "github"
       },
       "original": {
@@ -114,6 +213,32 @@
         "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": [
@@ -123,11 +248,11 @@
         ]
       },
       "locked": {
-        "lastModified": 1698974481,
-        "narHash": "sha256-yPncV9Ohdz1zPZxYHQf47S8S0VrnhV7nNhCawY46hDA=",
+        "lastModified": 1729742964,
+        "narHash": "sha256-B4mzTcQ0FZHdpeWcpDYPERtyjJd/NIuaQ9+BV1h+MpA=",
         "owner": "nix-community",
         "repo": "nix-github-actions",
-        "rev": "4bb5e752616262457bc7ca5882192a564c0472d2",
+        "rev": "e04df33f62cdcf93d73e9a04142464753a16db67",
         "type": "github"
       },
       "original": {
@@ -143,11 +268,11 @@
         ]
       },
       "locked": {
-        "lastModified": 1720334033,
-        "narHash": "sha256-X9pEvvHTVWJphhbUYqXvlLedOndNqGB7rvhSvL2CIgU=",
+        "lastModified": 1746934494,
+        "narHash": "sha256-3n6i+F0sDASjkhbvgFDpPDZGp7z19IrRtjfF9TwJpCA=",
         "owner": "nix-community",
         "repo": "nix-index-database",
-        "rev": "685e40e1348007d2cf76747a201bab43d86b38cb",
+        "rev": "e9b21b01e4307176b9718a29ac514838e7f6f4ff",
         "type": "github"
       },
       "original": {
@@ -158,11 +283,42 @@
     },
     "nixpkgs": {
       "locked": {
-        "lastModified": 1701253981,
-        "narHash": "sha256-ztaDIyZ7HrTAfEEUt9AtTDNoCYxUdSd6NrRHaYOIxtk=",
+        "lastModified": 1746183838,
+        "narHash": "sha256-kwaaguGkAqTZ1oK0yXeQ3ayYjs8u/W7eEfrFpFfIDFA=",
+        "owner": "NixOS",
+        "repo": "nixpkgs",
+        "rev": "bf3287dac860542719fe7554e21e686108716879",
+        "type": "github"
+      },
+      "original": {
+        "owner": "NixOS",
+        "ref": "nixos-24.11",
+        "repo": "nixpkgs",
+        "type": "github"
+      }
+    },
+    "nixpkgs-lib": {
+      "locked": {
+        "lastModified": 1743296961,
+        "narHash": "sha256-b1EdN3cULCqtorQ4QeWgLMrd5ZGOjLSLemfa00heasc=",
+        "owner": "nix-community",
+        "repo": "nixpkgs.lib",
+        "rev": "e4822aea2a6d1cdd36653c134cacfd64c97ff4fa",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nix-community",
+        "repo": "nixpkgs.lib",
+        "type": "github"
+      }
+    },
+    "nixpkgs_2": {
+      "locked": {
+        "lastModified": 1730531603,
+        "narHash": "sha256-Dqg6si5CqIzm87sp57j5nTaeBbWhHFaVyG7V6L8k3lY=",
         "owner": "nixos",
         "repo": "nixpkgs",
-        "rev": "e92039b55bcd58469325ded85d4f58dd5a4eaf58",
+        "rev": "7ffd9ae656aec493492b44d0ddfb28e79a1ea25d",
         "type": "github"
       },
       "original": {
@@ -172,39 +328,23 @@
         "type": "github"
       }
     },
-    "nixpkgs-stable": {
-      "locked": {
-        "lastModified": 1720282526,
-        "narHash": "sha256-dudRkHPRivMNOhd04YI+v4sWvn2SnN5ODSPIu5IVbco=",
-        "owner": "NixOS",
-        "repo": "nixpkgs",
-        "rev": "550ac3e955c30fe96dd8b2223e37e0f5d225c927",
-        "type": "github"
-      },
-      "original": {
-        "owner": "NixOS",
-        "ref": "release-24.05",
-        "repo": "nixpkgs",
-        "type": "github"
-      }
-    },
-    "nixpkgs_2": {
-      "locked": {
-        "lastModified": 1720244366,
-        "narHash": "sha256-WrDV0FPMVd2Sq9hkR5LNHudS3OSMmUrs90JUTN+MXpA=",
-        "owner": "nixos",
-        "repo": "nixpkgs",
-        "rev": "49ee0e94463abada1de470c9c07bfc12b36dcf40",
-        "type": "github"
-      },
-      "original": {
-        "owner": "nixos",
-        "ref": "nixos-24.05",
-        "repo": "nixpkgs",
-        "type": "github"
-      }
-    },
     "nixpkgs_3": {
+      "locked": {
+        "lastModified": 1746957726,
+        "narHash": "sha256-k9ut1LSfHCr0AW82ttEQzXVCqmyWVA5+SHJkS5ID/Jo=",
+        "owner": "nixos",
+        "repo": "nixpkgs",
+        "rev": "a39ed32a651fdee6842ec930761e31d1f242cb94",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nixos",
+        "ref": "nixos-24.11",
+        "repo": "nixpkgs",
+        "type": "github"
+      }
+    },
+    "nixpkgs_4": {
       "locked": {
         "lastModified": 1682134069,
         "narHash": "sha256-TnI/ZXSmRxQDt2sjRYK/8j8iha4B4zP2cnQCZZ3vp7k=",
@@ -218,23 +358,44 @@
         "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_3",
+        "systems": "systems_4",
         "treefmt-nix": "treefmt-nix"
       },
       "locked": {
-        "lastModified": 1701399357,
-        "narHash": "sha256-QSGP2J73HQ4gF5yh+MnClv2KUKzcpTmikdmV8ULfq2E=",
+        "lastModified": 1730284601,
+        "narHash": "sha256-eHYcKVLIRRv3J1vjmxurS6HVdGphB53qxUeAkylYrZY=",
         "owner": "nix-community",
         "repo": "poetry2nix",
-        "rev": "7acb78166a659d6afe9b043bb6fe5cb5e86bb75e",
+        "rev": "43a898b4d76f7f3f70df77a2cc2d40096bc9d75e",
         "type": "github"
       },
       "original": {
@@ -263,13 +424,65 @@
         "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",
         "nix-index-database": "nix-index-database",
-        "nixpkgs": "nixpkgs_2",
+        "nixpkgs": "nixpkgs_3",
+        "notenrechner": "notenrechner",
         "print-interface": "print-interface",
         "sops-nix": "sops-nix",
         "vscode-server": "vscode-server"
@@ -279,15 +492,14 @@
       "inputs": {
         "nixpkgs": [
           "nixpkgs"
-        ],
-        "nixpkgs-stable": "nixpkgs-stable"
+        ]
       },
       "locked": {
-        "lastModified": 1720321395,
-        "narHash": "sha256-kcI8q9Nh8/CSj0ygfWq1DLckHl8IHhFarL8ie6g7OEk=",
+        "lastModified": 1746485181,
+        "narHash": "sha256-PxrrSFLaC7YuItShxmYbMgSuFFuwxBB+qsl9BZUnRvg=",
         "owner": "Mic92",
         "repo": "sops-nix",
-        "rev": "c184aca4db5d71c3db0c8cbfcaaec337a5d065ea",
+        "rev": "e93ee1d900ad264d65e9701a5c6f895683433386",
         "type": "github"
       },
       "original": {
@@ -298,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"
       }
     },
@@ -327,6 +539,21 @@
       }
     },
     "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=",
@@ -340,7 +567,22 @@
         "type": "indirect"
       }
     },
-    "systems_4": {
+    "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=",
@@ -364,11 +606,11 @@
         ]
       },
       "locked": {
-        "lastModified": 1699786194,
-        "narHash": "sha256-3h3EH1FXQkIeAuzaWB+nK0XK54uSD46pp+dMD3gAcB4=",
+        "lastModified": 1730120726,
+        "narHash": "sha256-LqHYIxMrl/1p3/kvm2ir925tZ8DkI0KA10djk8wecSk=",
         "owner": "numtide",
         "repo": "treefmt-nix",
-        "rev": "e82f32aa7f06bbbd56d7b12186d555223dc399d1",
+        "rev": "9ef337e492a5555d8e17a51c911ff1f02635be15",
         "type": "github"
       },
       "original": {
@@ -377,17 +619,60 @@
         "type": "github"
       }
     },
-    "vscode-server": {
+    "utils": {
       "inputs": {
-        "flake-utils": "flake-utils_3",
-        "nixpkgs": "nixpkgs_3"
+        "systems": "systems_5"
       },
       "locked": {
-        "lastModified": 1713958148,
-        "narHash": "sha256-8PDNi/dgoI2kyM7uSiU4eoLBqUKoA+3TXuz+VWmuCOc=",
+        "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": "fc900c16efc6a5ed972fb6be87df018bcf3035bc",
+        "rev": "8b6db451de46ecf9b4ab3d01ef76e59957ff549f",
         "type": "github"
       },
       "original": {
diff --git a/flake.nix b/flake.nix
index badd3c2..9ed2342 100755
--- a/flake.nix
+++ b/flake.nix
@@ -1,6 +1,6 @@
 {
   inputs = {
-    nixpkgs.url = "github:nixos/nixpkgs/nixos-24.05";
+    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";
@@ -14,6 +14,15 @@
     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";
@@ -30,12 +39,14 @@
     , 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 {
@@ -67,31 +78,38 @@
             ese-manual.nixosModules.default
             course-management.nixosModules.default
             vscode-server.nixosModules.default
+            authentik.nixosModules.default
+
             ./hosts/quitte/configuration.nix
             ./options
 
             ./modules/core
+            ./modules/authentik
             ./modules/ldap
             ./modules/mail
             ./modules/web
             ./modules/courses
             ./modules/wiki
             ./modules/matrix
+            ./modules/keycloak
+            ./modules/monitoring
+            ./modules/unbound
 
             ./modules/nix-serve.nix
             ./modules/hedgedoc.nix
             ./modules/padlist.nix
             ./modules/nextcloud.nix
-            ./modules/keycloak.nix
-            ./modules/monitoring.nix
             ./modules/vaultwarden.nix
             ./modules/forgejo
             ./modules/kanboard.nix
-            ./modules/zammad.nix
-            ./modules/decisions.nix
+            # ./modules/zammad.nix
+            # ./modules/decisions.nix
+            ./modules/stream.nix
             # ./modules/struktur-bot.nix
             {
-              nixpkgs.overlays = [ self.overlays.default ];
+              nixpkgs.overlays = [
+                self.overlays.default
+              ];
               sops.defaultSopsFile = ./secrets/quitte.yaml;
             }
           ];
diff --git a/hosts/quitte/configuration.nix b/hosts/quitte/configuration.nix
index 91f3c3e..7f75d9d 100644
--- a/hosts/quitte/configuration.nix
+++ b/hosts/quitte/configuration.nix
@@ -1,4 +1,4 @@
-{ pkgs, config, ... }:
+{ pkgs, ... }:
 
 {
   imports =
@@ -16,7 +16,6 @@
   # boot.kernelParams = [ "video=VGA-1:1024x768@30" ];
   boot.loader.efi.canTouchEfiVariables = true;
   boot.supportedFilesystems = [ "zfs" ];
-  boot.kernelPackages = config.boot.zfs.package.latestCompatibleLinuxPackages;
 
   services.zfs = {
     trim.enable = true;
@@ -27,6 +26,17 @@
   time.timeZone = "Europe/Berlin";
   i18n.defaultLocale = "en_US.UTF-8";
 
+  security.sudo.extraRules = [
+    {
+      commands = [
+        {
+          command = "ALL";
+          options = [ "NOPASSWD" ];
+        }
+      ];
+      groups = [ "admins" ];
+    }
+  ];
   # prevent fork bombs
   security.pam.loginLimits = [
     {
diff --git a/hosts/quitte/network.nix b/hosts/quitte/network.nix
index 9ca01f4..ec09503 100644
--- a/hosts/quitte/network.nix
+++ b/hosts/quitte/network.nix
@@ -2,10 +2,10 @@
 {
   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" ];
-    # };
+    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";
@@ -15,6 +15,7 @@
 
     firewall = {
       logRefusedConnections = false;
+      trustedInterfaces = [ "podman0" ];
     };
   };
 
@@ -31,14 +32,24 @@
     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";
+        }
+        {
+          Gateway = "fe80::7a24:59ff:fe5e:6e2f";
         }
       ];
       networkConfig = {
-        DNS = "141.30.1.1";
+        DNS = [
+          "127.0.0.1"
+          "::1"
+        ];
         LLDP = true;
         EmitLLDP = "nearest-bridge";
       };
diff --git a/hosts/tomate/configuration.nix b/hosts/tomate/configuration.nix
index 8058b04..dffdcea 100644
--- a/hosts/tomate/configuration.nix
+++ b/hosts/tomate/configuration.nix
@@ -106,7 +106,6 @@
   };
 
   # Enable sound with pipewire.
-  sound.enable = true;
   hardware.pulseaudio.enable = false;
   security.rtkit.enable = true;
   services.pipewire = {
diff --git a/hosts/tomate/network.nix b/hosts/tomate/network.nix
index 32e98ca..dd04916 100644
--- a/hosts/tomate/network.nix
+++ b/hosts/tomate/network.nix
@@ -27,7 +27,7 @@
       address = [ "141.30.86.196/26" ];
       routes = [
         {
-          routeConfig.Gateway = "141.30.86.193";
+          Gateway = "141.30.86.193";
         }
       ];
       networkConfig = {
diff --git a/keys/pgp/jonasga.asc b/keys/pgp/jonasga.asc
deleted file mode 100644
index feb05ec..0000000
--- a/keys/pgp/jonasga.asc
+++ /dev/null
@@ -1,92 +0,0 @@
------BEGIN PGP PUBLIC KEY BLOCK-----
-
-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
-q0Mleg==
-=M2wX
------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..75f3f92
--- /dev/null
+++ b/modules/authentik/default.nix
@@ -0,0 +1,21 @@
+{ config, ... }:
+let
+  domain = "idm.${config.networking.domain}";
+in
+{
+  sops.secrets."authentik/core" = { };
+  sops.secrets."authentik/ldap" = { };
+  services.authentik = {
+    enable = true;
+    nginx = {
+      enable = true;
+      host = domain;
+      enableACME = true;
+    };
+    environmentFile = config.sops.secrets."authentik/core".path;
+  };
+  services.authentik-ldap = {
+    enable = true;
+    environmentFile = config.sops.secrets."authentik/ldap".path;
+  };
+}
diff --git a/modules/core/bacula.nix b/modules/core/bacula.nix
index 15e309c..c28a7d2 100644
--- a/modules/core/bacula.nix
+++ b/modules/core/bacula.nix
@@ -14,8 +14,9 @@
     enable = true;
     name = "ifsr-quitte";
     extraClientConfig = ''
+      Comm Compression = no
       Maximum Concurrent Jobs = 20
-      FDAddress = 141.30.30.169
+      FDAddress = 141.30.30.194
       PKI Signatures = Yes
       PKI Encryption = Yes
       PKI Keypair = ${config.sops.secrets."bacula/keypair".path}
diff --git a/modules/core/base.nix b/modules/core/base.nix
index 507c8f6..906aa65 100755
--- a/modules/core/base.nix
+++ b/modules/core/base.nix
@@ -64,7 +64,6 @@
       ../../keys/ssh/jannusch
       ../../keys/ssh/jannusch-arch
       ../../keys/ssh/tassilo
-      ../../keys/ssh/jonasga
       ../../keys/ssh/rouven
       ../../keys/ssh/joachim
     ];
@@ -73,6 +72,7 @@
   time.timeZone = "Europe/Berlin";
 
   # basic shell & editor
+  programs.vim.enable = true;
   programs.vim.defaultEditor = true;
 
   # List packages installed in system profile. To search, run:
@@ -104,6 +104,7 @@
     ltrace
     strace
     mtr
+    nix-output-monitor
     traceroute
     smartmontools
     sysstat
@@ -112,6 +113,8 @@
     eza
     zsh
     unzip
+    yazi
+    imagemagick
   ];
 }
 
diff --git a/modules/core/fail2ban.nix b/modules/core/fail2ban.nix
index 2681d43..5c08578 100644
--- a/modules/core/fail2ban.nix
+++ b/modules/core/fail2ban.nix
@@ -15,13 +15,14 @@
         enabled = true
         # aggressive mode to add blocking for aborted connections
         filter = dovecot[mode=aggressive]
-        maxretry = 3
+        maxretry = 15
       '';
       postfix = ''
         enabled = true
         filter = postfix[mode=aggressive]
-        maxretry = 3
+        maxretry = 15
       '';
+      sshd.settings.maxretry = 15;
     };
   };
 }
diff --git a/modules/core/logging.nix b/modules/core/logging.nix
index c242396..75f482f 100644
--- a/modules/core/logging.nix
+++ b/modules/core/logging.nix
@@ -3,7 +3,9 @@
   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
     '';
diff --git a/modules/core/mysql.nix b/modules/core/mysql.nix
index 8d6e673..f35b278 100644
--- a/modules/core/mysql.nix
+++ b/modules/core/mysql.nix
@@ -10,7 +10,6 @@
     user = "mysql";
     location = "/var/lib/backup/mysql";
     databases = [
-      "decisions"
       "fsrewsp"
       "nightline"
       "wiki_ese"
diff --git a/modules/core/nginx.nix b/modules/core/nginx.nix
index 874a122..36e596e 100644
--- a/modules/core/nginx.nix
+++ b/modules/core/nginx.nix
@@ -7,10 +7,14 @@
         ({ 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';
           '';
         })
       );
diff --git a/modules/core/podman.nix b/modules/core/podman.nix
index 625d25b..9927a43 100644
--- a/modules/core/podman.nix
+++ b/modules/core/podman.nix
@@ -1,4 +1,4 @@
-{ config, pkgs, ... }:
+{ pkgs, ... }:
 {
   # From: https://nixos.wiki/wiki/Podman
   virtualisation.containers.enable = true;
diff --git a/modules/core/postgres.nix b/modules/core/postgres.nix
index 2342765..daf44ff 100644
--- a/modules/core/postgres.nix
+++ b/modules/core/postgres.nix
@@ -5,7 +5,6 @@
     enable = true;
     location = "/var/lib/backup/postgresql";
     databases = [
-      "directus_ese"
       "course-management"
       "git"
       "grafana"
diff --git a/modules/courses/default.nix b/modules/courses/default.nix
index 686b734..9b971fd 100644
--- a/modules/courses/default.nix
+++ b/modules/courses/default.nix
@@ -3,7 +3,6 @@ let
   hostName = "kurse.${config.networking.domain}";
 in
 {
-  imports = [ ./phil.nix ];
   sops.secrets =
     let inherit (config.services.course-management) user;
     in
diff --git a/modules/decisions.nix b/modules/decisions.nix
index c3e0c2e..c19085d 100644
--- a/modules/decisions.nix
+++ b/modules/decisions.nix
@@ -1,4 +1,4 @@
-{ config, pkgs, ... }:
+{ config, ... }:
 let
   domain = "decisions.${config.networking.domain}";
 in
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/forgejo/default.nix b/modules/forgejo/default.nix
index d56106a..547582f 100644
--- a/modules/forgejo/default.nix
+++ b/modules/forgejo/default.nix
@@ -4,9 +4,9 @@ let
   gitUser = "git";
 in
 {
-  # imports = [
-  #   ./actions.nix
-  # ];
+  imports = [
+    ./actions.nix
+  ];
   sops.secrets.gitea_ldap_search = {
     key = "portunus/search-password";
     owner = config.services.forgejo.user;
@@ -22,17 +22,9 @@ in
 
   services.forgejo = {
     enable = true;
-    # package = pkgs.forgejo.overrideAttrs (_old: {
-    #   # patches = [
-    #   #   # migration fix
-    #   #   (pkgs.fetchpatch {
-    #   #     url = "https://codeberg.org/forgejo/forgejo/commit/ae463c7c559e02975ce5e758d8780def978eebee.patch";
-    #   #     hash = "sha256-cOXPvkLS0n+ynSBTrmEtumZ2PYBeCZmxPpFktqkw6Fo=";
-    #   #   })
-    #   # ];
-    # });
     user = gitUser;
     group = gitUser;
+    package = pkgs.forgejo;
     lfs.enable = true;
 
     database = {
@@ -79,22 +71,28 @@ in
         PROVIDER = "db";
       };
       actions.ENABLED = true;
+      # federation.ENABLED = true;
+      webhook.ALLOWED_HOST_LIST = "*.ifsr.de";
+      cors = {
+        ENABLED = true;
+      };
     };
   };
 
   systemd.services.forgejo.preStart =
     let
       exe = lib.getExe config.services.forgejo.package;
-      basedn = "ou=users,dc=ifsr,dc=de";
+      portunus = config.services.portunus;
+      basedn = "ou=users,${portunus.ldap.suffix}";
       ldapConfigArgs = ''
         --name LDAP \
         --active \
         --security-protocol unencrypted \
-        --host 'auth.ifsr.de' \
+        --host '${portunus.domain}' \
         --port 389 \
         --user-search-base '${basedn}' \
         --user-filter '(&(objectClass=posixAccount)(uid=%s))' \
-        --admin-filter '(isMemberOf=cn=admins,ou=groups,dc=ifsr,dc=de)' \
+        --admin-filter '(isMemberOf=cn=admins,ou=groups,${portunus.ldap.suffix})' \
         --username-attribute uid \
         --firstname-attribute givenName \
         --surname-attribute sn \
diff --git a/modules/hedgedoc.nix b/modules/hedgedoc.nix
index acfc46e..244734a 100644
--- a/modules/hedgedoc.nix
+++ b/modules/hedgedoc.nix
@@ -49,14 +49,15 @@ in
         # allow anonymous editing, but not creation of pads
         allowAnonymous = false;
         allowAnonymousEdits = true;
+        allowAnonymousUploads = false;
         defaultPermission = "limited";
         defaultNotePath = builtins.toString template;
         # ldap auth
         ldap = rec {
           url = "ldap://localhost";
-          searchBase = "ou=users,dc=ifsr,dc=de";
+          searchBase = "ou=users,${config.services.portunus.ldap.suffix}";
           searchFilter = "(uid={{username}})";
-          bindDn = "uid=search,${searchBase}";
+          bindDn = "uid=${config.services.portunus.ldap.searchUserName},${searchBase}";
           bindCredentials = "\${LDAP_CREDENTIALS}";
           useridField = "uid";
           providerName = "iFSR";
diff --git a/modules/kanboard.nix b/modules/kanboard.nix
index 6b4841f..2416ed8 100644
--- a/modules/kanboard.nix
+++ b/modules/kanboard.nix
@@ -1,4 +1,4 @@
-{ config, pkgs, ... }:
+{ config, ... }:
 let
   domain = "kanboard.${config.networking.domain}";
   domain_short = "kb.${config.networking.domain}";
@@ -8,7 +8,7 @@ in
 
   virtualisation.oci-containers = {
     containers.kanboard = {
-      image = "ghcr.io/kanboard/kanboard:v1.2.36";
+      image = "ghcr.io/kanboard/kanboard:v1.2.43";
       volumes = [
         "kanboard_data:/var/www/app/data"
         "kanboard_plugins:/var/www/app/plugins"
diff --git a/modules/keycloak.nix b/modules/keycloak/default.nix
similarity index 83%
rename from modules/keycloak.nix
rename to modules/keycloak/default.nix
index ccee386..6aa1afb 100644
--- a/modules/keycloak.nix
+++ b/modules/keycloak/default.nix
@@ -1,4 +1,4 @@
-{ config, ... }:
+{ config, pkgs, ... }:
 let
   domain = "sso.${config.networking.domain}";
 in
@@ -12,7 +12,9 @@ in
       http-port = 8086;
       https-port = 19000;
       hostname = domain;
-      proxy = "edge";
+      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
@@ -20,6 +22,9 @@ in
       passwordFile = config.sops.secrets."keycloak/db".path;
     };
     initialAdminPassword = "plschangeme";
+    themes = with pkgs ; {
+      ifsr = keycloak_ifsr_theme;
+    };
   };
   services.nginx.virtualHosts."${domain}" = {
     locations."/" = {
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/default.nix b/modules/ldap/default.nix
index 385e976..bdf3d3b 100644
--- a/modules/ldap/default.nix
+++ b/modules/ldap/default.nix
@@ -1,175 +1,85 @@
-{ config, pkgs, system, ... }:
+{ config, pkgs, ... }:
 let
   domain = "auth.${config.networking.domain}";
-  # seedSettings = {
-  #   groups = [
-  #     {
-  #       name = "admins";
-  #       long_name = "Portunus Admin";
-  #       members = [ "admin" ];
-  #       permissions.portunus.is_admin = true;
-  #     }
-  #     {
-  #       name = "search";
-  #       long_name = "LDAP search group";
-  #       members = [ "search" ];
-  #       permissions.ldap.can_read = true;
-  #     }
-  #     {
-  #       name = "fsr";
-  #       long_name = "Mitglieder des iFSR";
-  #     }
-  #   ];
-  #   users = [
-  #     {
-  #       login_name = "admin";
-  #       given_name = "admin";
-  #       family_name = "admin";
-  #       password.from_command = [
-  #         "${pkgs.coreutils}/bin/cat"
-  #         config.sops.secrets."portunus/admin-password".path
-  #       ];
-  #     }
-  #     {
-  #       login_name = "search";
-  #       given_name = "search";
-  #       family_name = "search";
-  #       password.from_command = [
-  #         "${pkgs.coreutils}/bin/cat"
-  #         config.sops.secrets."portunus/search-password".path
-  #       ];
-  #     }
-  #   ];
-  # };
+  seedSettings = {
+    groups = [
+      {
+        name = "admins";
+        long_name = "Portunus Admin";
+        members = [ "admin" ];
+        permissions.portunus.is_admin = true;
+      }
+      {
+        name = "search";
+        long_name = "LDAP search group";
+        members = [ "search" ];
+        permissions.ldap.can_read = true;
+      }
+      {
+        name = "fsr";
+        long_name = "Mitglieder des iFSR";
+      }
+    ];
+    users = [
+      {
+        login_name = "admin";
+        given_name = "admin";
+        family_name = "admin";
+        password.from_command = [
+          "${pkgs.coreutils}/bin/cat"
+          config.sops.secrets."portunus/admin-password".path
+        ];
+      }
+      {
+        login_name = "search";
+        given_name = "search";
+        family_name = "search";
+        password.from_command = [
+          "${pkgs.coreutils}/bin/cat"
+          config.sops.secrets."portunus/search-password".path
+        ];
+      }
+    ];
+  };
 in
 {
-  # sops.secrets = {
-  #   "portunus/admin-password".owner = config.services.portunus.user;
-  #   "portunus/search-password".owner = config.services.portunus.user;
-  # };
+  sops.secrets = {
+    "portunus/admin-password".owner = config.services.portunus.user;
+    "portunus/search-password".owner = config.services.portunus.user;
+  };
 
-  # services.portunus = {
-  #   enable = true;
-  #   package = pkgs.portunus.overrideAttrs (_old: {
-  #     patches = [
-  #       ./0001-update-user-validation-regex.patch
-  #       ./0002-both-ldap-and-ldaps.patch
-  #       ./0003-gecos-ascii-escape.patch
-  #       ./0004-make-givenName-optional.patch
-  #     ];
-  #     doCheck = false; # posix regex related tests break
-  #   });
-
-  #   inherit domain seedSettings;
-  #   port = 8681;
-  #   ldap = {
-  #     suffix = "dc=ifsr,dc=de";
-  #     searchUserName = "search";
-
-  #     # normally disables port 389 (but not with our patch), use 636 with tls
-  #     # `portunus.domain` resolves to localhost
-  #     tls = true;
-  #   };
-  # };
-  services.openldap = {
+  services.portunus = {
     enable = true;
-    urlList = [ "ldap:///" "ldaps:///" ];
-    settings = {
-      attrs = {
-        olcLogLevel = "conns";
+    package = pkgs.portunus.overrideAttrs (_old: {
+      patches = [
+        ./0001-update-user-validation-regex.patch
+        ./0002-both-ldap-and-ldaps.patch
+        ./0003-gecos-ascii-escape.patch
+        ./0004-make-givenName-optional.patch
+      ];
+      doCheck = false; # posix regex related tests break
+    });
 
-        olcTLSCACertificateFile = "/var/lib/acme/${domain}/full.pem";
-        olcTLSCertificateFile = "/var/lib/acme/${domain}/cert.pem";
-        olcTLSCertificateKeyFile = "/var/lib/acme/${domain}/key.pem";
-        # olcTLSCipherSuite = "HIGH:MEDIUM:+3DES:+RC4:+aNULL";
-        olcTLSCRLCheck = "none";
-        olcTLSVerifyClient = "never";
-        olcTLSProtocolMin = "3.1";
+    inherit domain seedSettings;
+    port = 8681;
+    ldap = {
+      suffix = "dc=ifsr,dc=de";
+      searchUserName = "search";
 
-      };
-      children = {
-        "cn=schema".includes = [
-          "${pkgs.openldap}/etc/schema/core.ldif"
-          # attributetype ( 9999.1.1 NAME 'isMemberOf'
-          # DESC 'back-reference to groups this user is a member of'
-          # SUP distinguishedName )
-          "${pkgs.openldap}/etc/schema/cosine.ldif"
-          "${pkgs.openldap}/etc/schema/inetorgperson.ldif"
-          "${pkgs.openldap}/etc/schema/nis.ldif"
-          # "${pkgs.writeText "openssh.schema" ''
-          # 	attributetype ( 9999.1.2 NAME 'sshPublicKey'
-          # 		DESC 'SSH public key used by this user'
-          # 		SUP name )
-          # ''}"
-        ];
-
-        "olcDatabase={1}mdb" = {
-          attrs = {
-            objectClass = [ "olcDatabaseConfig" "olcMdbConfig" ];
-
-            olcDatabase = "{1}mdb";
-            olcDbDirectory = "/var/lib/openldap/data";
-
-            olcSuffix = "dc=ifsr,dc=de";
-
-            /* your admin account, do not use writeText on a production system */
-            olcRootDN = "cn=portunus,dc=ifsr,dc=de";
-            olcRootPW = "{CRYPT}$y$j9T$xdf4HigfhmQWXn.bw9MgH/$91evhYAV1GP7olNCkQoCpUZrghh5P8dDXcZdAtpiD32";
-
-            olcAccess = [
-              /* custom access rules for userPassword attributes */
-              ''{0}to attrs=userPassword
-                by self write
-                by anonymous auth
-                by * none''
-
-              /* allow read on anything else */
-              ''{1}to *
-                 by dn.base="cn=portunus,dc=ifsr,dc=de" write
-                 by group.exact="cn=portunus-viewers,dc=ifsr,dc=de" read
-                 by self read
-                 by anonymous auth
-            ''
-            ];
-          };
-          children = {
-            "olcOverlay={2}memberof".attrs = {
-              objectClass = [ "olcOverlayConfig" "olcMemberOf" "top" ];
-              olcOverlay = "{2}memberof";
-              olcMemberOfRefInt = "TRUE";
-              olcMemberOfDangling = "ignore";
-              olcMemberOfGroupOC = "groupOfNames";
-              olcMemberOfMemberAD = "member";
-              olcMemberOfMemberOfAD = "memberOf";
-            };
-          };
-        };
-      };
+      # normally disables port 389 (but not with our patch), use 636 with tls
+      # `portunus.domain` resolves to localhost
+      tls = true;
     };
   };
 
-  systemd.services.openldap = {
-    wants = [ "acme-${domain}.service" ];
-    after = [ "acme-${domain}.service" ];
-  };
-  # security.acme.defaults.group = "certs";
-  # users.groups.certs.members = [ "openldap" ];
-  # certificate permissions
-  users.users.openldap.extraGroups = [ "nginx" ];
-
   security.pam.services.sshd.makeHomeDir = true;
 
   services.nginx = {
     enable = true;
-    virtualHosts."${domain}" = {
-      # locations = {
-      #   "/".proxyPass = "http://localhost:${toString config.services.portunus.port}";
-      # };
+    virtualHosts."${config.services.portunus.domain}" = {
+      locations = {
+        "/".proxyPass = "http://localhost:${toString config.services.portunus.port}";
+      };
     };
   };
-  networking.firewall = {
-    extraInputRules = ''
-      ip saddr { 141.30.86.192/26, 141.30.30.169, 10.88.0.1/16 } tcp dport 636 accept comment "Allow ldaps access from office nets and podman"
-    '';
-  };
 }
diff --git a/modules/mail/postfix.nix b/modules/mail/postfix.nix
index fb5887a..2ba240a 100644
--- a/modules/mail/postfix.nix
+++ b/modules/mail/postfix.nix
@@ -44,11 +44,9 @@ in
         # 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_use_tls = true;
-        # smtp_tls_security_level = "encrypt";
-        smtpd_use_tls = true;
-        # smtpd_tls_security_level = lib.mkForce "encrypt";
-        # smtpd_tls_auth_only = true;
+        smtp_tls_security_level = "may";
+        smtpd_tls_security_level = "may";
+        smtpd_tls_auth_only = true;
         smtpd_tls_protocols = [
           "!SSLv2"
           "!SSLv3"
diff --git a/modules/mail/rspamd.nix b/modules/mail/rspamd.nix
index 5cce802..cab3fd0 100644
--- a/modules/mail/rspamd.nix
+++ b/modules/mail/rspamd.nix
@@ -141,22 +141,26 @@ in
               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";
@@ -180,6 +184,7 @@ in
     redis = {
       vmOverCommit = true;
       servers.rspamd = {
+        port = 0;
         enable = true;
       };
     };
@@ -189,6 +194,11 @@ in
           "/" = {
             proxyPass = "http://127.0.0.1:11334";
             proxyWebsockets = true;
+            extraConfig = ''
+              allow 141.30.0.0/16;
+              allow 141.76.0.0/16;
+              deny all;
+            '';
           };
         };
       };
diff --git a/modules/matrix/default.nix b/modules/matrix/default.nix
index d4ca31f..03d58e1 100644
--- a/modules/matrix/default.nix
+++ b/modules/matrix/default.nix
@@ -27,6 +27,9 @@ in
     key = "portunus/search-password";
     owner = config.systemd.services.matrix-synapse.serviceConfig.User;
   };
+  nixpkgs.config.permittedInsecurePackages = [
+    "olm-3.2.16"
+  ];
 
   services = {
     postgresql = {
@@ -96,21 +99,22 @@ in
       extraConfigFiles = [
         (pkgs.writeTextFile {
           name = "matrix-synapse-extra-config.yml";
-          text = ''
-            modules:
-              - module: ldap_auth_provider.LdapAuthProviderModule
-                config:
-                  enabled: true
-                  uri: ldap://localhost
-                  base: ou=users,dc=ifsr,dc=de
-                  # taken from kaki config
-                  attributes:
-                    uid: uid
-                    mail: uid
-                    name: cn
-                  bind_dn: uid=search,ou=users,dc=ifsr,dc=de
-                  bind_password_file: ${config.sops.secrets.matrix_ldap_search.path}
-          '';
+          text = let portunus = config.services.portunus; in
+            ''
+              modules:
+                - module: ldap_auth_provider.LdapAuthProviderModule
+                  config:
+                    enabled: true
+                    uri: ldap://localhost
+                    base: ou=users,${portunus.ldap.suffix}
+                    # taken from kaki config
+                    attributes:
+                      uid: uid
+                      mail: uid
+                      name: cn
+                    bind_dn: uid=search,ou=users,${portunus.ldap.suffix}
+                    bind_password_file: ${config.sops.secrets.matrix_ldap_search.path}
+            '';
         })
       ];
     };
diff --git a/modules/monitoring.nix b/modules/monitoring/default.nix
similarity index 91%
rename from modules/monitoring.nix
rename to modules/monitoring/default.nix
index 3166ba4..4601db9 100644
--- a/modules/monitoring.nix
+++ b/modules/monitoring/default.nix
@@ -37,12 +37,8 @@ in
         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 = {
@@ -65,10 +61,6 @@ in
         enabledCollectors = [ "systemd" ];
         port = 9002;
       };
-      postfix = {
-        enable = true;
-        port = 9003;
-      };
     };
     scrapeConfigs = [
       {
@@ -78,13 +70,6 @@ in
         }];
         scrape_interval = "15s";
       }
-      {
-        job_name = "postfix";
-        static_configs = [{
-          targets = [ "127.0.0.1:${toString config.services.prometheus.exporters.postfix.port}" ];
-        }];
-        # scrape_interval = "60s";
-      }
       {
         job_name = "rspamd";
         static_configs = [{
@@ -92,6 +77,13 @@ in
         }];
         scrape_interval = "15s";
       }
+      {
+        job_name = "fabric";
+        static_configs = [{
+          targets = [ "127.0.0.1:25585" ];
+        }];
+        scrape_interval = "60s";
+      }
     ];
   };
 
diff --git a/modules/nextcloud.nix b/modules/nextcloud.nix
index ac11e63..b2557cc 100644
--- a/modules/nextcloud.nix
+++ b/modules/nextcloud.nix
@@ -15,7 +15,7 @@ in
     nextcloud = {
       enable = true;
       configureRedis = true;
-      package = pkgs.nextcloud29;
+      package = pkgs.nextcloud30;
       hostName = domain;
       https = true; # Use https for all urls
       phpExtraExtensions = all: [
@@ -59,7 +59,7 @@ in
       occ = lib.getExe config.services.nextcloud.occ;
       ldapConfig = rec {
         ldapAgentName = "uid=search,ou=users,${ldapBase}";
-        ldapBase = "dc=ifsr,dc=de";
+        ldapBase = config.services.portunus.ldap.suffix;
         ldapBaseGroups = "ou=groups,${ldapBase}";
         ldapBaseUsers = "ou=users,${ldapBase}";
         ldapConfigurationActive = "1";
diff --git a/modules/padlist.nix b/modules/padlist.nix
index 8a5f440..c7ea438 100644
--- a/modules/padlist.nix
+++ b/modules/padlist.nix
@@ -43,6 +43,7 @@ in
           '';
         };
         "/vendor".return = "403";
+        "/.git".return = "403";
       };
     };
   };
diff --git a/modules/stream.nix b/modules/stream.nix
index f76141a..5d36501 100644
--- a/modules/stream.nix
+++ b/modules/stream.nix
@@ -1,13 +1,12 @@
 { config, ... }:
+let cfg = config.services.owncast;
+in
 {
   services = {
     nginx = {
       virtualHosts = {
         "stream.${config.networking.domain}" = {
           locations."/" =
-            let
-              cfg = config.services.owncast;
-            in
             {
               proxyPass = "http://${toString cfg.listen}:${toString cfg.port}";
               proxyWebsockets = true;
@@ -19,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/unbound/default.nix b/modules/unbound/default.nix
new file mode 100644
index 0000000..01b2e60
--- /dev/null
+++ b/modules/unbound/default.nix
@@ -0,0 +1,14 @@
+{ ... }:
+{
+  services.resolved.extraConfig = ''
+    DNSStubListener=no
+  '';
+  services.unbound = {
+    enable = true;
+    settings = {
+      server = {
+        interface = [ "127.0.0.1" "::1" ];
+      };
+    };
+  };
+}
diff --git a/modules/web/default.nix b/modules/web/default.nix
index 262ea0b..cdd0729 100644
--- a/modules/web/default.nix
+++ b/modules/web/default.nix
@@ -1,6 +1,7 @@
 { ... }:
 {
   imports = [
+    ./ifsrdenew.nix
     ./ifsrde.nix
     ./ese.nix
     ./infoscreen.nix
@@ -11,5 +12,7 @@
     ./sharepic.nix
     ./userdir.nix
     ./ftp.nix
+    ./hyperilo.nix
+    ./notenrechner.nix
   ];
 }
diff --git a/modules/web/ese.nix b/modules/web/ese.nix
index 93fc356..3929671 100644
--- a/modules/web/ese.nix
+++ b/modules/web/ese.nix
@@ -1,80 +1,33 @@
 { config, pkgs, ... }:
 let
   domain = "ese.${config.networking.domain}";
-  cms-domain = "directus-ese.${config.networking.domain}";
+  webRoot = "/srv/web/ese";
 in
 {
-  sops.secrets."directus_env" = { };
-  environment.systemPackages = [ pkgs.nodejs_22 ];
-  virtualisation.oci-containers = {
-    containers.directus-ese = {
-      image = "directus/directus:latest";
-      volumes = [
-        "/srv/web/directus-ese/uploads:/directus/uploads"
-        "/srv/web/directus-ese/database:/directus/database"
-      ];
-      extraOptions = [ "--network=host" ];
-      environment = {
-        "DB_CLIENT" = "pg";
-        "DB_HOST" = "localhost";
-        "DB_PORT" = "5432";
-        "DB_DATABASE" = "directus_ese";
-        "DB_USER" = "directus_ese";
-        "PUBLIC_URL" = "https://directus-ese.ifsr.de";
-        "AUTH_PROVIDERS" = "keycloak";
-        "AUTH_KEYCLOAK_DRIVER" = "openid";
-        "AUTH_KEYCLOAK_CLIENT_ID" = "directus-ese";
-        "AUTH_KEYCLOAK_ISSUER_URL" = "https://sso.ifsr.de/realms/internal/.well-known/openid-configuration";
-        "AUTH_KEYCLOAK_IDENTIFIER_KEY" = "email";
-        "AUTH_KEYCLOAK_ALLOW_PUBLIC_REGISTRATION" = "true";
-        "AUTH_KEYCLOAK_DEFAULT_ROLE_ID" = "a6b7a1b6-a6fa-442c-87fd-e37c2a16424b";
-      };
-      environmentFiles = [
-        config.sops.secrets."directus_env".path
-      ];
-
-    };
-  };
-  services.postgresql = {
-    enable = true;
-    ensureUsers = [
-      {
-        name = "directus_ese";
-        ensureDBOwnership = true;
-      }
-    ];
-    ensureDatabases = [ "directus_ese" ];
-  };
-
   services.nginx = {
-    virtualHosts."${cms-domain}" = {
-      locations."/" = {
-        extraConfig = ''
-          if ($request_method = 'OPTIONS') {
-            add_header 'Access-Control-Allow-Origin' '*';
-            add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
-            add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
-            add_header 'Access-Control-Max-Age' 1728000;
-            add_header 'Content-Type' 'text/plain; charset=utf-8';
-            add_header 'Content-Length' 0;
-            return 204;
-          }
-
-          add_header 'Access-Control-Allow-Origin' '*';
-          add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
-          add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
-        '';
-        proxyPass = "http://127.0.0.1:8055";
-      };
-    };
     virtualHosts."${domain}" = {
       locations."= /" = {
-        return = "301 /2024/";
+        return = "302 /2025/";
       };
       locations."/" = {
-        root = "/srv/web/ese/served";
+        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/ftp.nix b/modules/web/ftp.nix
index c816885..7529169 100644
--- a/modules/web/ftp.nix
+++ b/modules/web/ftp.nix
@@ -11,6 +11,7 @@ in
       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;
@@ -22,15 +23,137 @@ in
     '';
     locations."=/403.html" = {
       root = pkgs.writeTextDir "403.html" ''
+        <!DOCTYPE html>
         <html>
-          <head>
-            <title>403 Forbidden</title>
-          </head>
-          <body>
-            <center><h1>403 Forbidden</h1></center>
-            <center>Dieser Ordner ist nur aus dem Uni-Netz zug&aumlnglich.</center>
-            <center>This directory is only accessible from the TUD network.</center>
-          </body>
+        <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/web/ifsrde.nix b/modules/web/ifsrde.nix
index 694abc7..84c4ad1 100644
--- a/modules/web/ifsrde.nix
+++ b/modules/web/ifsrde.nix
@@ -60,6 +60,7 @@ 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";
diff --git a/modules/web/ifsrdenew.nix b/modules/web/ifsrdenew.nix
new file mode 100644
index 0000000..e2c7548
--- /dev/null
+++ b/modules/web/ifsrdenew.nix
@@ -0,0 +1,49 @@
+{ config, pkgs, lib, ... }:
+let
+  user = "fsr-web";
+  group = "fsr-web";
+  webRoot = "/srv/web/ifsrdenew";
+in
+{
+
+  users.users.${user} = {
+    group = group;
+    isSystemUser = true;
+  };
+  users.groups.${group} = { };
+  users.users.nginx = {
+    extraGroups = [ group ];
+  };
+  services.nginx = {
+
+    virtualHosts."test.${config.networking.domain}" = {
+      root = webRoot;
+      locations = {
+        "/" = {
+          tryFiles = "$uri $uri/ =404";
+        };
+        "~ ^/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
+        "~* /(system|vendor)/.*\.(txt|xml|md|html|json|yaml|yml|php|pl|py|cgi|twig|sh|bat)$".return = "403";
+        # deny running scripts inside user folder
+        "~* /user/.*\.(txt|md|json|yaml|yml|php|pl|py|cgi|twig|sh|bat)$".return = "403";
+        # deny access to specific files in the root folder
+        "~ /(LICENSE\.txt|composer\.lock|composer\.json|nginx\.conf|web\.config|htaccess\.txt|\.htaccess)".return = "403";
+        ## End - Security
+      };
+    };
+  };
+
+  users.users."ese-deploy" = {
+    isNormalUser = true;
+    openssh.authorizedKeys.keys = [
+      ''command="${pkgs.rrsync}/bin/rrsync ${webRoot}",restrict ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFRIojP9vBbxy0fCEJFMNKXgkTA7Sju9mn+i01mYzovU''
+    ];
+  };
+}
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
index 0c5a51b..6c9e597 100644
--- a/modules/web/sharepic.nix
+++ b/modules/web/sharepic.nix
@@ -1,60 +1,14 @@
-{ pkgs, config, lib, ... }:
+{ pkgs, config, ... }:
 let
   domain = "sharepic.${config.networking.domain}";
-  user = "sharepic";
-  group = "sharepic";
 in
 {
-  users.users.${user} = {
-    group = group;
-    isSystemUser = true;
-  };
-  users.groups.${group} = { };
-
-  services.phpfpm.pools.sharepic = {
-    user = "sharepic";
-    group = "sharepic";
-    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;
-
-    virtualHosts."${domain}" = {
-      root = "/srv/web/sharepic";
-      extraConfig = ''
-        index index.php index.html;
-      '';
-
-      locations = {
-        "/" = {
-          tryFiles = "$uri $uri/ =404";
-        };
-        "~ \.php$" = {
-          extraConfig = ''
-            try_files $uri =404;
-            fastcgi_pass unix:${config.services.phpfpm.pools.sharepic.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;
-          '';
-        };
-        "/data".return = "403";
-      };
+  services.nginx.virtualHosts."${domain}" = {
+    root = pkgs.fetchFromGitHub {
+      owner = "jannikmenzel";
+      repo = "iFSR-Sharepicgenerator";
+      rev = "ac721d5fff2dba1f046939a6d6532b1a8cfceba8";
+      hash = "sha256-of+N58TDt2BcbDVEriKn6rjQVl0GdV4ZMEblrdUutZk=";
     };
   };
 }
diff --git a/modules/wiki/ese.nix b/modules/wiki/ese.nix
index 7546517..4125198 100644
--- a/modules/wiki/ese.nix
+++ b/modules/wiki/ese.nix
@@ -6,6 +6,9 @@ let
 in
 {
 
+  system.activationScripts.hacky-mediawiki-convert = ''
+    cp ${pkgs.imagemagick}/bin/convert /srv/web/wiki.ese/convert
+  '';
   users.users.${user} = {
     group = group;
     isSystemUser = true;
diff --git a/modules/wiki/fsr.nix b/modules/wiki/fsr.nix
index 9f82869..ac16e41 100644
--- a/modules/wiki/fsr.nix
+++ b/modules/wiki/fsr.nix
@@ -38,6 +38,7 @@ in
       };
 
       extraConfig = ''
+        wfLoadSkin( 'MinervaNeue' );
         $wgSitename = "FSR Wiki";
         $wgArticlePath = '/$1';
 
@@ -57,6 +58,7 @@ in
         $wgUseAjax = true;
         $wgEnableMWSuggest = true;
         $wgDefaultSkin = 'timeless';
+        $wgDefaultMobileSkin = 'minerva';
 
         //TODO what about $wgUpgradeKey ?
 
@@ -64,7 +66,8 @@ in
         # https://www.mediawiki.org/wiki/Extension:PluggableAuth
         # https://www.mediawiki.org/wiki/Extension:OpenID_Connect
         $wgOpenIDConnect_MigrateUsersByEmail = true;
-        $wgPluggableAuth_EnableLocalLogin = true;
+        //$wgOpenIDConnect_MigrateUsersByUserName = true;
+        $wgPluggableAuth_EnableLocalLogin = false;
         $wgPluggableAuth_Config["iFSR Login"] = [
           "plugin" => "OpenIDConnect",
           "data" => [
@@ -74,23 +77,22 @@ in
           ],
         ];
       '';
-
       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://extdist.wmflabs.org/dist/extensions/PluggableAuth-REL1_40-3689731.tar.gz";
-          hash = "sha256-BMA0qV+x+iQt/P9tbl9csEUni9jiQcBtZeuwdjx2QPk=";
+          url = "https://extdist.wmflabs.org/dist/extensions/PluggableAuth-REL1_42-1da98f4.tar.gz";
+          hash = "sha256-5uBUy7lrr86ApASYPWgF6Wa09mxxP0o+lXLt1gVswlA=";
         };
         OpenIDConnect = pkgs.fetchzip {
-          url = "https://extdist.wmflabs.org/dist/extensions/OpenIDConnect-REL1_40-b354cdb.tar.gz";
-          hash = "sha256-gLHaveEzfmpqU9fWATZsUU377FJj2yq//raHZUR/VWk=";
-        };
-        VisualEditor = pkgs.fetchzip {
-          url = "https://extdist.wmflabs.org/dist/extensions/VisualEditor-REL1_40-8970b62.tar.gz";
-          hash = "sha256-G+qvKVuF6OCnwS5q2cKfij1/aH1I6lOw84K6fED980s=";
-        };
-        SyntaxHighlight = pkgs.fetchzip {
-          url = "https://extdist.wmflabs.org/dist/extensions/SyntaxHighlight_GeSHi-REL1_40-1170e8f.tar.gz";
-          hash = "sha256-75+wwTvHhwPBP1jVLK2fQWBi7vznOvPVgNpY3kzWJtg=";
+          url = "https://extdist.wmflabs.org/dist/extensions/OpenIDConnect-REL1_42-6c28c16.tar.gz";
+          hash = "sha256-X5kUuvxINbuXaLMKRcLOl2L3qbnMT72lg2NA3A9Daj8=";
         };
       };
     };
diff --git a/modules/zammad.nix b/modules/zammad.nix
index fed019b..cbff484 100644
--- a/modules/zammad.nix
+++ b/modules/zammad.nix
@@ -1,4 +1,4 @@
-{ config, ... }:
+{ config, lib, ... }:
 let
   domain = "tickets.${config.networking.domain}";
 in
@@ -9,11 +9,18 @@ in
       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 
diff --git a/overlays/default.nix b/overlays/default.nix
index 7240ef2..3d7b533 100644
--- a/overlays/default.nix
+++ b/overlays/default.nix
@@ -1,7 +1,8 @@
 _final: prev:
 let
   inherit (prev) fetchurl;
-  inherit (prev) fetchFromGitHub;
+  inherit (prev) fetchpatch;
+  inherit (prev) callPackage;
 in
 {
   # AGDSN is running an outdated version that we have to comply to
@@ -12,17 +13,32 @@ in
       sha256 = "sha256-3w+FJezbo4DnS1N8pxrfO3WWWT8CGJtZqw6//IXMyN4=";
     };
   }));
-  # (hopefully) fix systemd journal reading
-  prometheus-postfix-exporter = prev.prometheus-postfix-exporter.overrideAttrs (_old: {
-    patches = [
-      ./prometheus-postfix-exporter/0001-cleanup-also-catch-milter-reject.patch
-    ];
-    src = fetchFromGitHub {
-      owner = "adangel";
-      repo = "postfix_exporter";
-      rev = "414ac12ee63415eede46cb3084d755a6da6fba23";
-      hash = "sha256-m1kVaO3N7XC1vtnxXX9kMiEFPmZuoopRUYgA7gQzP8w=";
-    };
-  });
+  # 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/secrets/quitte.yaml b/secrets/quitte.yaml
index fe5cb67..41b7cbb 100644
--- a/secrets/quitte.yaml
+++ b/secrets/quitte.yaml
@@ -7,10 +7,15 @@ keycloak:
 portunus:
     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:UEJEPSQDGa4lewyqQ4fZH//li6KMfE9Jb/BzbLUM9o02qZuuAUDw17gTTTTPdl8WoBS02nN9r0s=,iv:2TFoMv0LAFTQDEf6ekjzS1Q1P+Z47V8kUnluQpTHWug=,tag:QOKDbVDZLmBymplJPHfrfQ==,type:str]
+rspamd-password: ENC[AES256_GCM,data:Dd6lTyDh3FFqOTeipY0o5uJz5/Mh6FsVahbI5M1njn5S690avzQ4+8YISrwkuA==,iv:OAuA+t2KzGDvURng2RWFAoMNfw+RNLtM1hLEniuzz9c=,tag:RBN41BmsrvgXKEOa8gCDfw==,type:str]
+authentik:
+    core: ENC[AES256_GCM,data:SlRm6l21ItHrAC/8G21CqsBqOu/tQcXLZkcLR4BO/Ovqp+23lG9RaULKb4OWyEiRepU2AWF5b4djTiCSMG2lQ8f/DPzJxiB4mtd2Wdw7yEbqmeg0yYxs9Ak24BjsWdTGiq/dHqf12KM=,iv:xCtQS7AyuFiQPOFX843qc42GN+eQWVY8fbRS0MjBFoE=,tag:NMAPn5BTWTbrZTSjEvpC0w==,type:str]
+    ldap: ENC[AES256_GCM,data:6nOHCmoTUEUjp2iZuXmCj9GxQZ2dgDBt+oKhpjVgp15NNPVy8g6WK1KdPp+evVKxKzTP8oS1NPN8homjTaThzj6GwwzNMQwIcF0mK4XZCJzniUemWOv6CdV/wQpBLq8lMdt2tdSH8hcuvElHJjf6s42ty4bvqmiG80is+uk4MgAUhHsplARoeWU=,iv:Y2mXYuIbD9oSK4kTzAy2jowjnLv35AcSOVrVgSePig4=,tag:9GNutBfGPX+BS+QADlvueA==,type:str]
 grafana:
     oidc_secret: ENC[AES256_GCM,data:oH+VCL4e4wve6RyVwlTXPSmirbf+STD5FxUj9OjGDLs=,iv:PhVVCy5JyRa+fOrYAsnjDL+97zYASmKcBzB8t9ZVWIU=,tag:JzGO/FeKem4vd7ApvZ2Zcg==,type:str]
 mediawiki:
@@ -19,173 +24,151 @@ mediawiki:
 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]
-directus_env: ENC[AES256_GCM,data:Q8mQYpwsMbv8NHIzTjxlbS528uZoFkzB0WDZITiYdbq6Y5a+12IEuXXRU+/v7vonpSWFH0ROqfrGy5yd3VhTR2eFvg8OsnlanFnnF4DYIDVMWLEOf4XoOoh/9tYPqoPYFtvwYnlCZFaEky4BKdcIFuqSuqrV9GSabBRuNJ1RbPyRXA6Nwr25uWYr70/1iIEb1tfffqR1YfycZ1JW4kL7OcjxNb6CwoPQ00Z/0t3YYG5Rc9rj7qTc6qw=,iv:yswA2oUhllYoAflK4BbxUMlCWaEfrFi/6g1r6wWZxHA=,tag:36xbdXho+lqKQt9ZaqS/Mw==,type:str]
-strukturbot_env: ENC[AES256_GCM,data:klTFgdNvdMYA++GsmqEHdhklZ5JUreP2Lh+5E0mj5iH7F8Run6/gAdHBJpCWEe2Q3o6RdZduy+kCXzJWznkLbEASxgJNcAWdFq2CU4ov0Z6rGS6i/X376Yc6I7oYLfQSd58r8Q/rhFl2qXkCiSGJYNvo6vGh6+b/TdTABwAnvj/k81n2SsSpoMOu9/1Pyop7QNVMuAtXaE/sca1KPtU/Yg3DrKczxKzKppReafIs7ICI/760N/H0Wwh6rtw51mfQxxOW9UpPXmnEFI8b+07pVsgNoSbzPCMaAoxf6LFnTnqtFRNS0N7rX3DrP6GSv2A8Bwm5of0sLhIm3gAAQ2iXp2di+BOi7uRqFVtNZ18XGPil8FVEkeIFdmhjCJAOJRyuANl3JsaqRk4lT1qMglyjHtCodP5rvVe+pALzpihNPIQPy0Tes2GOM4Q6ww4UxZrgevNHz7CnEMSEPU8Hjb63UkZTZbj2HxF8,iv:a2NyivM34Z/V/ir+NzsXNm73sp6uASYDiqDOG2ix2JE=,tag:buP1Hcvt3dEW249BWNBKkw==,type:str]
-decisions_env: ENC[AES256_GCM,data:fUoBTkceqbabZcR3Rmf2iSUd45T/oQ+6K4ReznhyJ/P3yzlgW17eG615o5v42PmLerpkABXZuVIkQSpkJsnn/Z2cSnv7vNvkeZcRambDWnEtz39Gu0uZR1um4Nl9hfJrp+otj3tTdzoh06MADQegWSbFLhJm6Qa71Fqh+dbGPZ8rbQAGDs0T6I2BFF1khND0COAQPO+5/gtRigngLaFgAJ/EClaRcUVF2BE7N7Za8ZMMDH7NOYSOSG/TTHZCownFeWbh3d7H89wG5Qw4jgXMz6Wd3y9QzEjjmhSubRi0hbSTZ+t4yiSjeODAVQLYlZ4DCjZECl+yvUndugdr1L1b5EpgjeFJTAsWjZtnu64=,iv:vcToub6JCQ9END3cuqCA7h0KC9drG0VIK52EyV8xQHM=,tag:PhzRofrNi67RFNP444GWBw==,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: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]
-course-management-phil:
-    secret-key: ENC[AES256_GCM,data:YxANlc3+BVkrDSRuaO1xtzJLnprK6vXpHD+o9dtTu4Q=,iv:FVnRAa7YEfHC7x4K4fkjIp4n4sCiI+OFwMIHu5KHRXQ=,tag:zneVoFMCK41ph1eRpWhdaQ==,type:str]
-    adminpass: ENC[AES256_GCM,data:akLU2/5wBHgbhy83Agfe5SNFUpfgCB19DV3SMSj8wORgTgSEhlZnrWKt,iv:9BInYkjKIsi+nPaSoOEkcKcoK/9bxACYpaKcaEd5Fd0=,tag:UxBUMj1xIL6xlXQpGrjHVA==,type:str]
 bacula:
     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+IFgyNTUxOSA2anFsRGRMaHNVaEhvUmsw
-            WnMzTU80Qyt2N0ZWaW5jTUVqM2owOXhLaWxJClMzQ0RPQktPRkFPWTYxTGY1T2xi
-            Z21McDNzdGxnSTl6RExBNitJTFlncDQKLS0tIEFsZkIwSDVhR0JuTlMycm93cEc3
-            c2lzVGV6WnVQT1pOTTVwRUxlMWZobWsK0CrDl2ELoYOTrMt3uN3mgBSyaYqOQY4I
-            vBK12PV9FR9GFpKN4kGB03PZ0gV0N1zlcCHpnPCUuHwbCvvF2+vCag==
+            YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBRdTBXa2wyZWIzQkMxQVg5
+            MjRLVWZwNzVXRGg0cU5lcjlWbllqdWtjdnpNCk1SYnlCcUNKampzYU12ZlE0MHRQ
+            WHYvcForelFWMGVXWXJaOEFmY3I3cUkKLS0tIGlJaHR0STMzRklDbVE2THVlczBr
+            MWM0M3FvbjUzL3p3ZU1zUG94ckV3ZTAKUOAkZ8nlvT36cyPy5USyDzoIG569N818
+            tMM5aQsEQ9vTOaUoK4gtBEXBva7VerMprdcTRYLcSJ/9L1vXdlVT/g==
             -----END AGE ENCRYPTED FILE-----
-    lastmodified: "2024-05-19T09:13:57Z"
-    mac: ENC[AES256_GCM,data:LqmR0jd8pD+l45o7cdxnuoDZUSBfPqL6o7AFtEsWeqEYi/Lbv+LLIBXIlUgG2BnOk2d78kmCFGqAl0F8Hi8qohG8Zki4FsHFDnrfXDlRZX+7J3TCvk/TIQ7NHqA1DjPf37WFuJWxUaW7oeeZVyOQ9KFgaenQMBt/eehiHpgBfW0=,iv:z5nD7ntEF3+Op9Dvg2h4jf2MPtfXsgRoH6B8MMi8Ius=,tag:4BmArd9jw1v/6HU7tat4VA==,type:str]
+    lastmodified: "2025-05-26T22:42:49Z"
+    mac: ENC[AES256_GCM,data:EboiWEeVxjSmS0XCbUeu/NMAsPfxVQM03U4Xz1fzXBrBqdvkMIvYnAwbkmvLs19ypVvRwy1blXdMb/1n8esmKSlK3WB95xrn1DK98wUOlkiW95g3Ydp6yrW4+cgj3VBfcho2GF/LqzaGo25CH6zBdTJWZXW2DaWCJyOWjn35H2Q=,iv:JMlv3H0VmvpfeVIovQevNSXVfbmMDkhrVE+1lAnqsiE=,tag:eN06XFCK/2hOY0AWFWigJg==,type:str]
     pgp:
-        - created_at: "2024-02-29T15:23:23Z"
+        - created_at: "2025-03-07T23:03:16Z"
           enc: |-
             -----BEGIN PGP MESSAGE-----
 
-            hF4DntlvaG5T7wcSAQdAq8HJ1VopKhShChQAnq9ETU308YAZqOojMA1Plpb2thMw
-            i/fon3Dt/odw8jj9sbIUVqImWy7K6FqEiAkIWN6Sq6F8raA1ohc+AmVHos1pHK5z
-            0lwBHKNB7pt1h8LIv3GYqPUaz+yPhYyCk5PQqVemJSvMlilOjfkaIHHDV7VNEsVx
-            kwZSaNij15cytLHY+iJGkChUqlTdwmxWRIW3Fa0FEe9OZzSj0fkS60m9TAug3w==
-            =EMPz
+            hF4DntlvaG5T7wcSAQdAoaGQG1W4yMq0MxUwvYIrxwdzKKBHJgv9hLCPvSyJwBEw
+            f177FgAT9PQW69wM2x3QZm4+HsozWqs3k7SlMRsRy0he0fAtdkGUr0EL0DCFi5ml
+            0lwBJxkI/R2CI08fRqkS0GuM+6HiV6BRypVy15C1oXEhxlE/MhnCYlP/0MeHbQ7H
+            MkStwmbVqvOm8aizboD0EePZpHC0M+61ULgMt9HnzWzQ/ue/YepI1dPfEWBQ1g==
+            =etd/
             -----END PGP MESSAGE-----
           fp: B8E1727497FC48AA14158BDF947F769D7B95EC2B
-        - created_at: "2024-02-29T15:23:23Z"
+        - created_at: "2025-03-07T23:03:16Z"
           enc: |-
             -----BEGIN PGP MESSAGE-----
 
-            hQIMA/YLzOYaRIJJAQ//RaAlgiR0nVZYWrN+aVgYIJsW8vz2ZMREcvONe++DF7XV
-            NOKkI7fua1jag6NWGQ9hzCEXQsoiQjL8CpaOSN9fJmnsmQoNv3vOLoeArLnPI6np
-            OBwOLQu2KgIcHCP48qfJ5idIV90UNoABro3MsbKDQAnXES+Eco7YflQVlesgEfau
-            oxKnZvGcOhxigQM3DobM1/keNnCuE3wyK5hVKV28VD/TNozZIj1MyoXpfzjzSgne
-            A80+/uLj1r5eg1l5nDAdEtOfvxf+eCCEom3oh2uRKK5HsRUySSE7tNRGNnn8QsWy
-            fUy+/95fOasyHRtr/cNfhZrly9VcavAhR4WYWU/LJ0pOYT1WtxMG3U4HGswiT2G2
-            4ElM1BvdV/TlTTd3G+XhzyLK182w7Lz0CpNfpI9tgA6iIDHFiWL+KjF9Cy/VbidB
-            B4h3bMOSw3YeESQFRG9y91QQreT8OOfkI58taiKM5J1yCNKGu7F3DEQxQm0/wHBP
-            xMNZGu3nZGL05QgqFw1lw4YeQlSQJC81bQZMSaBB+7KEcU2oXH8pKVHerKHwieZR
-            Kd7uD1ounFp5QD33Xc9Ebqin9dIOMyM8QtMsi6fD4ofwb7riRcBM82EF2slE8Q6o
-            u2VuTj0mlsG75EnLyrQ9AFLPAPtR+qS6R+/JaZqgRUxk0xswGet8GApA2oou38XS
-            XAE5sEQTEGD6SD34U0BHRNrPLbqKNcZXccwukZ9er90p2NzyE+hoxKfQFhocnpdv
-            j86MWn4nAcZYY75Vyg5AxaS60/R48WvzfEp0nVd6Zukd/05AV2kBMVJp9WZw
-            =Nzjo
+            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: "2024-02-29T15:23:23Z"
+        - created_at: "2025-03-07T23:03:16Z"
           enc: |-
             -----BEGIN PGP MESSAGE-----
 
-            hQIMA8uqUsBLHj6XARAAiGzDHGIs1rA3EMCBHY8ajWMJ5XQeLJBU6iZHlCijLqLf
-            nnGUwVj1fWXj0Gh+cQV28YMaQL7KPUbKBswhqRgYu4uCFdyC+ipQ2aC5OwUz1t+y
-            cbX3jMifiVpDVanYwukb/gC4O3F6aY99ezEo7RJNkwBO730DsPadUZ3S5i7IlC5+
-            5dd6eIzlt72fBC9QceSi2cAiiP57WmKC1bHLyCR7LXNN3719QzXQsjxVBvwYpD4d
-            dF5lgdDMNi4xgjQ03UawHjp3TbsqXIw6F+/Wx6CEXo6XSCcOLIdrOVJNirocw3sO
-            lg1tjfvqOnZbVNjh51Txl6IFfdffx37qgQjx9O1dxNod3ORa7aO74gAnd3oZyOUq
-            FngRxTqu2yvzonbGGRNT5gMk9QZNqCHOHjp4NudpwPuLFnwrvt+jw8XVrGrMYMkA
-            P/gOc3EXrCKr25yKE+dA8l3ikzd8wnPago2Adm/Xt34J4bIL27QKml3+g/gVnW6i
-            In59tWMQ5lxnD9nXA1jY8RccwEZHvI74+AkQNa3t9miA4bDER7n/KyF+XbxePI3G
-            qta9fCpb2a4EmsBm8JGtFnD7/Dek5UVcOjnrPxG2yMMAxwv7ZjFT/IeqoEb5yL+D
-            opB002q0UJP01RVBeqoetLNhMX1R8TN55oetpyWkPECuwCKi0Pbqdqe+qWR0UKHU
-            ZgEJAhBHH/fM4ZUeqi5Z9B7WvAFj+95g7Jlgtw8nqm9C7JsvDGQ7LxkQ7OJr691U
-            jhO60x3CF8SOR9E0A4Y/iVAVQQleVES5+xC0KpVY3YacRHi4HR39v7Lg5cPYIOCT
-            c3NiN6iiTg==
-            =Af2X
+            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: "2024-02-29T15:23:23Z"
+        - created_at: "2025-03-07T23:03:16Z"
           enc: |-
             -----BEGIN PGP MESSAGE-----
 
-            hQIMAzUXo8ZPJwGLAQ//QLCUOMTptOAa+Ol+lB6ijB/iRzQoDs6LW/GvH2mbeV17
-            PBpFge3SYObBAZRZfF6x2PAuBNIAptckJxQ+bw/BoxQWDUI6Bsl0lSxu0eC+e5Z+
-            Zx7GOIqUYDuRXZ9NmFA3VeD/3PV4SuveazM6bOwMmlbfxeh+EBiSa7F79cl10pb4
-            ZbpOuxqvHb52/jZ0Lsny+MVycgh5YyWyLSl1nO9Uc9zVIWDyygA//UevEgFkr/fp
-            kUagX164Rmvaxc37fcvER+TxBKTm1SUFc82MB2sC4VEWb6RylpWWrbyEquJOW0/K
-            Jhw6kFnQ42THB6wSCuZw2HuHzZuXOzQLzJqq8l5p43sTUxiyCGQxtNDE47V32YqR
-            2sEZLltwPLpgmzmPSflDE4GgYOP7rhOJ50bpqBk9yArzCziB3g27/QSLhGak9u6y
-            b39NdyKA48tXP7TQIWkPFBLWlHWnjWHps2tbNPLaq6CM/tA811Nq+XKBs0/eDeZV
-            +DkX4BFmdvV2k3gq2juqWkeHLcHRyIIC8cHDVTVEL9TBP8GyzCIxhU+q3Dnkffnd
-            g60olZanFfsVNpg8kFqAN5OeLIHtCxqXzj+qs8QgD5YMUIOuwvkngbyHpWv8vBhi
-            GSIYB+bIBRxJPIc9ofmXv3S4R128NGIKq06K/P4Odnt+8iy/Q00CNYW75zobg3TS
-            XAHD6cnvUQILTDtFLsTZw8rcmHI9ls8L+yHmTilEo7jrsK7DUUjgQlLULZlBE5Hc
-            vGQ3KEop1AdG+K4try/OSaQPY2dYtLH3mqGlmmaID7fJQiTnWJNpTIgIHynO
-            =5/0t
+            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: "2024-02-29T15:23:23Z"
+        - created_at: "2025-03-07T23:03:16Z"
           enc: |-
             -----BEGIN PGP MESSAGE-----
 
-            hQIMA30JDs8MiK29ARAAjlskdt3CZzDk9JflbhgpSgGd+cad6SkaG449uPV+TLfL
-            lcAG066sYMbMgzZn2D4AZCXb2mcU/gz9Kdo/MBVL8G3TN+M1yMxqOJm8xLQ02HHz
-            VMuSTiwLyj1G+dr161O+PEiNQMqq9YNxGg7Oi8b0T3hylcHFGKQ8Ji37hUmOlvfc
-            EuYjd2j6udarLYLDcq5gxYhvlfCJm9WMljrLYC5IatgWF/KgLkFVpy4aa2aYtlxP
-            mwbea4PqUCznhvUEsD7ucc8fOOPiYD8aJypEG7NxyJSaytdL6yjMTDQAOWRq5dGP
-            0NcF61r2P8gGDfxk/iCf9vJR/IzM9JgCnlwbiAWx0HKCbkScf/j87Lb7DK2kmY/W
-            fJsjwyVpr5X/OHcDEW1Bc8dx2mU5Dc27tpVSuigv/uXLk+H6RQjfMwgE6eViX4ve
-            rmMw3J+Pnd/eRO7ELQa1D8ujkwOLjSSl/KEwV+BjtQvo1E5NuVxn39o3dblvBY4Y
-            Lkj9wG5G6N8sUZQt2nQkGR9mIP89CxOFFaiXrCH9VNSO7hyuVQXIb7xED5kWA6KT
-            eJPvTnx3kzKb7XXW3hOXV3dB7c3Dgjpkp6TyqpWoXxLoMYzeJE1MpnlABxG++oyP
-            AydJ7hRSErm6+3PsslObohW3cuNO7fhurkobd6lhUkOtTyN80n2RqYUWYJcG7uXS
-            XAGy4OMGwQJSy4g+2bmNiwCHvnZPSuuF616G9g2+TyYtcL9v2BNYZyt7LAe6MabH
-            IPthrl605mLgmC1Af6hJAXQkLuAWVxN4XIq8PGm4ss3vTqgVLLGMF2ODsHqP
-            =zFMG
+            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: BF37903AE6FD294C4C674EE24472A20091BFA792
-        - created_at: "2024-02-29T15:23:23Z"
+        - created_at: "2025-03-07T23:03:16Z"
           enc: |-
             -----BEGIN PGP MESSAGE-----
 
-            hF4DNffZWjBmO5ASAQdA6hgR8rEHwDyrs8e89euwZL1J7Ul/1XKJVGSQHSgAAgEw
-            1shc8TrDXMt9NveygpzlbDJc9pYUg2bKn+rgpS853Cx+MqYQBeu5SMpxpiZrr8J6
-            1GYBCQIQeafELm9z2HZo0mwujkbcxPfrPX8vUzzE/EL6EDwfPGDL6HUAyHnQC7hc
-            dCK6qK5OZNkpFGYqwUuPeDQwwwmdd1qNSD1Fcg+SuJ64vw4BAlX5iT1JyVqgTRI4
-            Tz3Bpkdhphw=
-            =f1SN
+            hF4DNffZWjBmO5ASAQdA07imap27d/V8R6oVOlXUxEYL8QJcTaSiBICEnBnfJnww
+            i69xnBOhGKwnWAgFipvXwFeyiQcmr+LnLbdXB5YGTblDzG46PCEH8JyeMakHpHAI
+            0lwBHOOaak4roVNGoxmDedniFhdH0rmd8c9MtG00iT4Bc95fjM3eSFuTrESWPY9i
+            RUApyfYx9WJq5jZ+lFii3uOSQg/rMpVTIpjjXC3HyJTvfy+MnECBAI6+cAWsjQ==
+            =J26p
             -----END PGP MESSAGE-----
           fp: B1A16011B86BACB56ADB713DB712039D23133661
-        - created_at: "2024-02-29T15:23:23Z"
+        - created_at: "2025-03-07T23:03:16Z"
           enc: |-
             -----BEGIN PGP MESSAGE-----
 
-            hQIMA1tId/HHLgxAARAAkHey4n6G1FuZMQpZ6Jo/o27mzuqsRwj5+kS14+WVm8Xm
-            BZ+DEMIbV/mCBBsIZKHrkrM1ml+Mul3pUHkTfo92n0dHjKaRKNLplbhyXMAavQnY
-            MpcYv0OTR3/ZlbtQXsDT691rBGtAXoI5dwX9lRGUN7W0TzjZ91O+mLLloEYdmHuD
-            y2b36TsYmV0tq7e/T1xQn2cy7SAB0OUmAlL+W7/18P7NdJxMtBPoPDoRgcXZneZq
-            GxLRiTPgCeXaN6OzfsNxQrWv0kVh0ob95Grp1/J7CPqh+iEf6IROyHpD4/Yc41ih
-            FJLU3sameMn+PlelRqL2MhctYPjgqFnLB/2ILaG2yNMOM7MiMZY+WW+LjojYgAjS
-            /s0aBgxBVyR/r22fDsDISbwKkmSiubZcYStwP4WmlMCDYY6nLIxJC1Sel6IRqqGD
-            QzPiG0lwK4llx9vdDK5IKqFyu+Z89ctVHRntBtDBqWM1z8O/5R+HiDwy3VDkDfH/
-            tuF56xvhVsyEJSe85t89RUDpv9aTq0YxjWBKhCOcP1bWOLzwmaP/Lldo5QUZ3GnP
-            lDP3FjsRld3upKBQ4UntcChjDjjxWVTIbc07gIDbel4JnNvv/o4r+NpXhiisnXgl
-            siOLAtCdFudfRscSDxhmyBzRmmMe3sKbi+ezxOuepThXaXYvyUk2D8GZg0mLF1HS
-            XAGycxm5KP86z5gREvArW26HbIxNlfUpTIoIKgmX3nWhj62Og5VmBYLcMARxVzut
-            Fu8ukgtgmIQKx8O2OLnmiO3rLvY2xPS5DUOgmYjfPqcxFkI/q32L2OXH3ZJY
-            =KFx1
-            -----END PGP MESSAGE-----
-          fp: FB44F0746DF25F0B24A2EAE586C8A257C3EC82AB
-        - created_at: "2024-02-29T15:23:23Z"
-          enc: |-
-            -----BEGIN PGP MESSAGE-----
-
-            hF4Da5T//DC6DJkSAQdAyVvnKDj+KjtF0mEhf/QXb8dwIZPjPr5CAcvZvJhSThgw
-            J0bpP5IAu6LRp+D8C5SnMjaN1eNKX2McFcM3PVsGyCiAEihHKRD91J5xQ4Uc4Tea
-            0lwBkk7/c9S0KXiKM8pzqRMuimVOs9DMXqxbEKc2BvM7hmKJJfAYE/dvRxNayW4j
-            Qh7px98tsMsSJCeaj5zqa89aBl8UOQmBYdsjby3BRbOoHNE2ulKe4m4HcV3IpQ==
-            =Rdl0
+            hF4Da5T//DC6DJkSAQdAkFwtDJxwbCFZ8EyxkK1R4v5Ntfy0QIzLpKk1CxXklnkw
+            GKQPVXU2qFH4UwH/58fKENIGTVHlDRXZa6gALeW6EBr3uQFoJ3d/APHpD4nprqOy
+            0lwB45yH3YjdTG2YY4bI3eZKplK6R9mK/lzAVG7zV7nVs+glr6/1XpaeYJxiT1/K
+            p6aV8I+/FTg30d7Rfv2PpPaB31spmUxA3RDIbybzn2uygwOdKB0PQnnGLAOXBA==
+            =vuaK
             -----END PGP MESSAGE-----
           fp: FBBFAC260D9283D1EF2397DD3CA65E9DD6EB319D
     unencrypted_suffix: _unencrypted
-    version: 3.8.1
+    version: 3.10.2