zaphyra's git: nixfiles

zaphyra's nixfiles

commit d5814f32ec8c92cab339f1ede6dfdf538fb4d556
Author: Katja Ramona Sophie Kwast (zaphyra) <git@zaphyra.eu>
Date: Wed, 20 May 2026 12:29:16 +0200

initial commit
207 files changed, 15876 insertions(+), 0 deletions(-)
A
.gitignore
|
1
+
A
.sops.yaml
|
72
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
default.nix
|
69
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
flake.lock
|
5
+++++
A
flake.nix
|
1
+
A
lib/collectModules.nix
|
77
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
lib/default.nix
|
27
+++++++++++++++++++++++++++
A
lib/forAllSystems.nix
|
28
++++++++++++++++++++++++++++
A
lib/mergeAttrsRecursiveList.nix
|
31
+++++++++++++++++++++++++++++++
A
lib/toCamelCase.nix
|
35
+++++++++++++++++++++++++++++++++++
A
machines/cautus.nix
|
178
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
machines/clevai.nix
|
71
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
machines/cuvier.nix
|
200
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
machines/default.nix
|
58
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
machines/isodon.nix
|
103
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
machines/leucas.nix
|
71
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
machines/sorrah.nix
|
78
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
maidModules/dbus.nix
|
25
+++++++++++++++++++++++++
A
maidModules/environment.nix
|
36
++++++++++++++++++++++++++++++++++++
A
maidModules/programs/fish.nix
|
132
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
maidModules/programs/git.nix
|
36
++++++++++++++++++++++++++++++++++++
A
maidModules/programs/lazygit.nix
|
34
++++++++++++++++++++++++++++++++++
A
maidModules/programs/starship.nix
|
51
+++++++++++++++++++++++++++++++++++++++++++++++++++
A
maidModules/xdg.nix
|
121
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/common/configure/boot.nix
|
91
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/common/configure/dn42Router.nix
|
315
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/common/configure/fonts.nix
|
21
+++++++++++++++++++++
A
nixosModules/common/configure/locale.nix
|
34
++++++++++++++++++++++++++++++++++
A
nixosModules/common/configure/nix.nix
|
59
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/common/configure/persist.nix
|
167
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/common/configure/primaryNetworkInterface.nix
|
94
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/common/configure/rootDisk.nix
|
185
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/common/configure/sops.nix
|
22
++++++++++++++++++++++
A
nixosModules/common/hardware/audio.nix
|
21
+++++++++++++++++++++
A
nixosModules/common/hardware/bluetooth.nix
|
22
++++++++++++++++++++++
A
nixosModules/common/hardware/fprint.nix
|
23
+++++++++++++++++++++++
A
nixosModules/common/hardware/intelGraphics.nix
|
24
++++++++++++++++++++++++
A
nixosModules/common/hardware/quirks.nix
|
23
+++++++++++++++++++++++
A
nixosModules/common/hardware/smartcard.nix
|
19
+++++++++++++++++++
A
nixosModules/common/hardware/thunderbolt.nix
|
15
+++++++++++++++
A
nixosModules/common/profiles/amdCpu.nix
|
23
+++++++++++++++++++++++
A
nixosModules/common/profiles/base.nix
|
103
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/common/profiles/intelCpu.nix
|
17
+++++++++++++++++
A
nixosModules/common/profiles/minimal.nix
|
48
++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/common/profiles/netcup.nix
|
34
++++++++++++++++++++++++++++++++++
A
nixosModules/common/profiles/nvme.nix
|
18
++++++++++++++++++
A
nixosModules/common/programs/fish.nix
|
17
+++++++++++++++++
A
nixosModules/common/programs/networkUtilities.nix
|
25
+++++++++++++++++++++++++
A
nixosModules/common/programs/niri.nix
|
56
++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/common/programs/nixUtilities.nix
|
23
+++++++++++++++++++++++
A
nixosModules/common/programs/shellUtilities.nix
|
37
+++++++++++++++++++++++++++++++++++++
A
nixosModules/common/programs/systemUtilities.nix
|
24
++++++++++++++++++++++++
A
nixosModules/common/security/kernel.nix
|
77
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/common/security/networking.nix
|
38
++++++++++++++++++++++++++++++++++++++
A
nixosModules/common/security/nix.nix
|
19
+++++++++++++++++++
A
nixosModules/common/services/NetworkManager.nix
|
19
+++++++++++++++++++
A
nixosModules/common/services/gitolite.nix
|
241
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/common/services/gotosocial.nix
|
248
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/common/services/greetd.nix
|
74
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/common/services/gvfs.nix
|
56
++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/common/services/mautrixBridge.nix
|
150
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/common/services/openssh.nix
|
133
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/common/services/power-profiles-daemon.nix
|
22
++++++++++++++++++++++
A
nixosModules/common/services/prosody.nix
|
148
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/common/services/resticBackup.nix
|
178
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/common/services/upower.nix
|
20
++++++++++++++++++++
A
nixosModules/common/services/vnstat.nix
|
155
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/common/users/zaphyra.nix
|
36
++++++++++++++++++++++++++++++++++++
A
nixosModules/default.nix
|
24
++++++++++++++++++++++++
A
nixosModules/dns/dns.nix
|
53
+++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/toBeUpstreamed/gomuks.nix
|
199
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/configure/cccdaWifi.nix
|
43
+++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/configure/dn42Router.nix
|
208
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/configure/dnsServer.nix
|
156
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/configure/floraCtl.nix
|
92
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/configure/fonts.nix
|
36
++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/configure/mailServer.nix
|
266
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/configure/matrixBridges.nix
|
156
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/configure/netcupTunnel.nix
|
140
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/configure/networkManagerProfiles.nix
|
74
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/configure/syncthing.nix
|
226
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/configure/syncthingBackup.nix
|
48
++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/configure/xmppServer.nix
|
38
++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/profiles/dn42.nix
|
180
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/profiles/graphical.nix
|
136
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/profiles/nginx.nix
|
75
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/profiles/resticBackupTarget.nix
|
53
+++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/profiles/zaphyra.nix
|
103
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/programs/broot.nix
|
54
++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/programs/celluloid.nix
|
25
+++++++++++++++++++++++++
A
nixosModules/zpha/programs/chaosctl.nix
|
27
+++++++++++++++++++++++++++
A
nixosModules/zpha/programs/deploymentUtilities.nix
|
24
++++++++++++++++++++++++
A
nixosModules/zpha/programs/dino.nix
|
22
++++++++++++++++++++++
A
nixosModules/zpha/programs/firefox.nix
|
385
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/programs/fish.nix
|
21
+++++++++++++++++++++
A
nixosModules/zpha/programs/gajim.nix
|
23
+++++++++++++++++++++++
A
nixosModules/zpha/programs/ghostty.nix
|
45
+++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/programs/git.nix
|
82
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/programs/gnome-calendar.nix
|
19
+++++++++++++++++++
A
nixosModules/zpha/programs/gnome-online-accounts.nix
|
53
+++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/programs/gnome-text-editor.nix
|
30
++++++++++++++++++++++++++++++
A
nixosModules/zpha/programs/gpg.nix
|
194
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/programs/htop.nix
|
42
++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/programs/loupe.nix
|
44
++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/programs/micro.nix
|
216
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/programs/nautilus.nix
|
69
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/programs/niri/enable.nix
|
65
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/programs/niri/gtk.nix
|
58
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/programs/niri/packages.nix
|
17
+++++++++++++++++
A
nixosModules/zpha/programs/niri/rules.nix
|
121
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/programs/niri/settings.nix
|
220
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/programs/nixUtilities.nix
|
34
++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/programs/papers.nix
|
27
+++++++++++++++++++++++++++
A
nixosModules/zpha/programs/pdfarranger.nix
|
22
++++++++++++++++++++++
A
nixosModules/zpha/programs/shaderbg.nix
|
37
+++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/programs/shellUtilities.nix
|
94
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/programs/ssh.nix
|
37
+++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/programs/swaylock.nix
|
81
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/programs/thunderbird.nix
|
40
++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/programs/typst.nix
|
25
+++++++++++++++++++++++++
A
nixosModules/zpha/programs/vibepanel.nix
|
141
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/programs/vicinae.nix
|
45
+++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/programs/wpaperd.nix
|
75
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/programs/yt-dlp.nix
|
25
+++++++++++++++++++++++++
A
nixosModules/zpha/programs/yubikey.nix
|
21
+++++++++++++++++++++
A
nixosModules/zpha/services/avahi.nix
|
21
+++++++++++++++++++++
A
nixosModules/zpha/services/batsignal.nix
|
45
+++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/services/dssd.nix
|
37
+++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/services/keyd.nix
|
45
+++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/services/knot.nix
|
227
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/services/knotACME.nix
|
144
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/services/oniri.nix
|
26
++++++++++++++++++++++++++
A
nixosModules/zpha/services/pipewire.nix
|
76
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/services/wlsunset.nix
|
39
+++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/websites/bikemap.zaphyra.eu.nix
|
124
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/websites/continuwuity.zaphyra.eu.nix
|
122
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/websites/ctu.cx.nix
|
22
++++++++++++++++++++++
A
nixosModules/zpha/websites/dav.zaphyra.eu.nix
|
73
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/websites/ente.zaphyra.eu.nix
|
133
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/websites/fedi.ctu.cx.nix
|
187
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/websites/fedi.home.ctu.cx.nix
|
185
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/websites/flauschehorn.zaphyra.eu.nix
|
94
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/websites/git.zaphyra.eu.nix
|
273
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/websites/gomuks.zaphyra.eu.nix
|
46
++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/websites/gts.zaphyra.eu.nix
|
212
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/websites/hass.zaphyra.eu.nix
|
82
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/websites/ip.fc9f.de.nix
|
95
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/websites/links.zaphyra.eu.nix
|
55
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/websites/memories.zaphyra.eu.nix
|
82
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/websites/music.zaphyra.eu.nix
|
204
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/websites/notes.zaphyra.eu.nix
|
66
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/websites/oeffi.zaphyra.eu.nix
|
107
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/websites/things.zaphyra.eu.nix
|
73
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/websites/vault.zaphyra.eu.nix
|
109
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
nixosModules/zpha/websites/zaphyra.eu.nix
|
30
++++++++++++++++++++++++++++++
A
npins/default.nix
|
249
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
npins/sources.json
|
297
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
overlays/default.nix
|
27
+++++++++++++++++++++++++++
A
overlays/dnsNix/default.nix
|
8
++++++++
A
overlays/nixStd/default.nix
|
8
++++++++
A
overlays/nixpkgsUnstable/default.nix
|
11
+++++++++++
A
overlays/swaylock-plugin-fprintd/default.nix
|
18
++++++++++++++++++
A
overlays/swaylock-plugin-fprintd/fprintd-support.patch
|
577
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
overlays/tuigreet/default.nix
|
20
++++++++++++++++++++
A
overlays/waylogout/default.nix
|
13
+++++++++++++
A
overlays/zphaLib/default.nix
|
15
+++++++++++++++
A
overlays/zphaPackages/default.nix
|
30
++++++++++++++++++++++++++++++
A
packages/adwaita-colors-icon-theme/package.nix
|
69
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
packages/chaosctl/chaosctl.fish
|
65
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
packages/chaosctl/package.nix
|
5
+++++
A
packages/default.nix
|
6
++++++
A
packages/deployrsActivator/package.nix
|
53
+++++++++++++++++++++++++++++++++++++++++++++++++++++
A
packages/domsonic/package.nix
|
41
+++++++++++++++++++++++++++++++++++++++++
A
packages/dssd/package.nix
|
46
++++++++++++++++++++++++++++++++++++++++++++++
A
packages/echo-meter/package.nix
|
56
++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
packages/gotosocial-unstable/0001-disable-template-indentation.patch
|
40
++++++++++++++++++++++++++++++++++++++++
A
packages/gotosocial-unstable/package.nix
|
142
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
packages/mautrix-telegram/package.nix
|
32
++++++++++++++++++++++++++++++++
A
packages/memos/package.nix
|
80
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
packages/niri-screen-time/package.nix
|
29
+++++++++++++++++++++++++++++
A
packages/niri-taskbar/package.nix
|
47
+++++++++++++++++++++++++++++++++++++++++++++++
A
packages/nirilayout/package.nix
|
48
++++++++++++++++++++++++++++++++++++++++++++++++
A
packages/nirimap/package.nix
|
44
++++++++++++++++++++++++++++++++++++++++++++
A
packages/nix-cleanup/nix-cleanup.sh
|
13
+++++++++++++
A
packages/nix-cleanup/package.nix
|
25
+++++++++++++++++++++++++
A
packages/nocturne/package.nix
|
58
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
packages/oniri/package.nix
|
35
+++++++++++++++++++++++++++++++++++
A
packages/phanpy/package.nix
|
47
+++++++++++++++++++++++++++++++++++++++++++++++
A
packages/setupDisk/package.nix
|
108
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
packages/shaderbg/package.nix
|
47
+++++++++++++++++++++++++++++++++++++++++++++++
A
packages/vibepanel/package.nix
|
52
++++++++++++++++++++++++++++++++++++++++++++++++++++
A
packages/wleave/package.nix
|
77
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
packages/wlsbg/package.nix
|
60
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
packages/wooz/package.nix
|
47
+++++++++++++++++++++++++++++++++++++++++++++++
A
resources/default.nix
|
21
+++++++++++++++++++++
A
resources/patches/mqttwebui-florapatches-owo.patch
|
50
++++++++++++++++++++++++++++++++++++++++++++++++++
A
resources/shaders/background1.frag
|
79
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
secrets/cautus.yaml
|
67
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
secrets/clevai.yaml
|
29
+++++++++++++++++++++++++++++
A
secrets/common.yaml
|
80
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
secrets/cuvier.yaml
|
73
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
secrets/default.nix
|
21
+++++++++++++++++++++
A
secrets/isodon.yaml
|
42
++++++++++++++++++++++++++++++++++++++++++
A
secrets/leucas.yaml
|
32
++++++++++++++++++++++++++++++++
A
secrets/sorrah.yaml
|
67
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
secrets/zaphyra/floractl.yaml
|
28
++++++++++++++++++++++++++++
A
secrets/zaphyra/sieve.yaml
|
28
++++++++++++++++++++++++++++
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1 @@
+result
diff --git a/.sops.yaml b/.sops.yaml
@@ -0,0 +1,72 @@
+keys:
+    - &zaphyra BFE6386C8D66BCD4DAE14FC895F0FE7CD7E6A022
+    - &cautus age1yln5qratl82u6sqakh9gn2c0s88nzm62cw9muq5z4kzgp9wqpyzshhptq9
+    - &sorrah age102glvtt2nwx4ymgjh85449exlhmzk56cayy9hkkpl82u6wm64qzqjar7ju
+    - &isodon age10ujnqv3ttdw23f7jryjg4dtw83fj2wf275kv38f2qrtj5kk3hg4qf5uf3h
+    - &cuvier age1wztgmu7rm4yvdr0x8xucwfmnf0sruafwjx2ttl9cvg54epn0qysqqnr5n3
+    - &clevai age1q58tmhk5hxrfn86qymet3mznafdzuhwl47s8mt3nyn43pararc5sng7kag
+    - &leucas age1enkp0mlswl30s4h7z4qvyha4cmc2n2exs0v97276q5mx0jc86ggs7g2dyq
+
+creation_rules:
+    - path_regex: secrets/common\.yaml$
+      key_groups:
+          - age:
+                - *cautus
+                - *sorrah
+                - *isodon
+                - *cuvier
+                - *clevai
+                - *leucas
+            pgp:
+                - *zaphyra
+
+    - path_regex: secrets/cautus\.yaml$
+      key_groups:
+          - age:
+                - *cautus
+            pgp:
+                - *zaphyra
+
+    - path_regex: secrets/sorrah\.yaml$
+      key_groups:
+          - age:
+                - *sorrah
+            pgp:
+                - *zaphyra
+
+    - path_regex: secrets/isodon\.yaml$
+      key_groups:
+          - age:
+                - *isodon
+            pgp:
+                - *zaphyra
+    - path_regex: secrets/cuvier\.yaml$
+      key_groups:
+          - age:
+                - *cuvier
+            pgp:
+                - *zaphyra
+    - path_regex: secrets/clevai\.yaml$
+      key_groups:
+          - age:
+                - *clevai
+            pgp:
+                - *zaphyra
+    - path_regex: secrets/leucas\.yaml$
+      key_groups:
+          - age:
+                - *leucas
+            pgp:
+                - *zaphyra
+    - path_regex: secrets/zaphyra/sieve\.yaml$
+      key_groups:
+          - age:
+                - *cautus
+            pgp:
+                - *zaphyra
+    - path_regex: secrets/zaphyra/floractl\.yaml$
+      key_groups:
+          - age:
+                - *cuvier
+            pgp:
+                - *zaphyra
diff --git a/default.nix b/default.nix
@@ -0,0 +1,69 @@
+let
+  npins = import ./npins;
+  overlays = import ./overlays { inherit npins; };
+  nixosConfigurations = import ./machines { inherit lib npins; };
+  lib = (import "${npins.nixpkgs}/lib").extend (
+    final: prev: {
+      zpha = import ./lib {
+        inherit npins;
+        lib = prev;
+      };
+    }
+  );
+
+in
+{
+
+  inherit
+    overlays
+    nixosConfigurations
+    ;
+
+  nixosModules = import ./nixosModules { inherit lib; };
+
+  formatter = lib.zpha.forAllSystems {
+    inherit (npins) nixpkgs;
+    body = pkgs: pkgs.nixfmt-rfc-style;
+  };
+
+  packages = lib.zpha.forAllSystems {
+    overlays = overlays.default;
+    body =
+      pkgs:
+      lib.mergeAttrsList [
+        (import ./packages pkgs)
+        (lib.flip lib.mapAttrs' nixosConfigurations (
+          machineName: nixosConfiguration:
+          lib.nameValuePair "setupDisk-${machineName}" (
+            pkgs.zpha.setupDisk.override {
+              systemConfig = nixosConfiguration.config;
+            }
+          )
+        ))
+      ];
+  };
+
+  deploy = {
+    activationTimeout = 600;
+    confirmTimeout = 240;
+    nodes = lib.flip builtins.mapAttrs nixosConfigurations (
+      _: nixosConfiguration:
+      let
+        inherit (nixosConfiguration) config pkgs;
+      in
+      {
+        hostname = config.networking.fqdn;
+        sshUser = "root";
+        sshOpts = [
+          "-p"
+          (toString (lib.head config.services.openssh.ports))
+        ];
+        profiles.system = {
+          user = "root";
+          path = pkgs.zpha.deployrsActivator.override { systemConfig = config; };
+        };
+      }
+    );
+  };
+
+}
diff --git a/flake.lock b/flake.lock
@@ -0,0 +1,5 @@
+{
+  "nodes": { "root": {} },
+  "root": "root",
+  "version": 7
+}
diff --git a/flake.nix b/flake.nix
@@ -0,0 +1 @@
+{ outputs = _: import ./.; }
diff --git a/lib/collectModules.nix b/lib/collectModules.nix
@@ -0,0 +1,77 @@
+{ ... }:
+
+{
+
+  collectModules =
+    {
+      path,
+      scope ? { },
+      fileName ? "default.nix",
+    }:
+    (
+      let
+        #some helpers (mostly from nixpkgs or at least derived from that)
+        sublist =
+          start: count: list:
+          let
+            len = builtins.length list;
+          in
+          builtins.genList (n: builtins.elemAt list (n + start)) (
+            if start >= len then
+              0
+            else if start + count > len then
+              len - start
+            else
+              count
+          );
+        pipe = builtins.foldl' (x: f: f x);
+        flatten = x: if builtins.isList x then builtins.concatMap (y: flatten y) x else [ x ];
+        drop = count: list: sublist count (builtins.length list) list;
+        splitPath = path: builtins.filter builtins.isString (builtins.split "/" (builtins.toString path));
+
+        #actual logic starts here
+        fileNameLength = builtins.stringLength fileName;
+        basePathLength = builtins.length (splitPath ./.);
+
+        collectFiles =
+          path:
+          pipe (builtins.readDir path) [
+            (builtins.mapAttrs (
+              name: value:
+              if value == "directory" then (collectFiles (path + "/${name}")) else (path + "/${name}")
+            ))
+            builtins.attrValues
+            flatten
+            (builtins.filter (
+              elem:
+              let
+                pathLength = builtins.length (splitPath elem);
+              in
+              (
+                (
+                  let
+                    strLength = builtins.stringLength elem;
+                  in
+                  builtins.substring (strLength - fileNameLength) strLength elem
+                ) == fileName
+              )
+              && (pathLength - 1) != basePathLength
+            ))
+          ];
+
+      in
+      pipe (collectFiles path) [
+        (builtins.map (filePath: {
+          name = (
+            let
+              pathParts = drop basePathLength (splitPath filePath);
+            in
+            builtins.concatStringsSep "-" (sublist 0 ((builtins.length pathParts) - 1) pathParts)
+          );
+          value = import filePath scope;
+        }))
+        builtins.listToAttrs
+      ]
+    );
+
+}
diff --git a/lib/default.nix b/lib/default.nix
@@ -0,0 +1,27 @@
+{
+  lib,
+  npins,
+  ...
+}:
+
+(
+  let
+    # read the current directorys files and pipe the result through a list of functions
+    selfLib = lib.pipe (builtins.readDir ./.) [
+      # convert to a list containing just the attribute names
+      (builtins.attrNames)
+      # drop "default.nix" from the list
+      (builtins.filter (name: name != "default.nix"))
+      # convert each list-element containing its file-name to an element containing the file's content
+      (builtins.map (
+        name:
+        import ./${name} {
+          inherit npins lib selfLib;
+        }
+      ))
+      # merge list of attribute sets together
+      (lib.mergeAttrsList)
+    ];
+  in
+  selfLib
+)
diff --git a/lib/forAllSystems.nix b/lib/forAllSystems.nix
@@ -0,0 +1,28 @@
+{
+  npins,
+  lib,
+  ...
+}:
+
+{
+
+  forAllSystems =
+    {
+      body,
+      systems ? [
+        "x86_64-linux"
+        "aarch64-linux"
+      ],
+      nixpkgs ? npins.nixpkgs,
+      overlays ? [ ],
+    }:
+    lib.genAttrs systems (
+      system:
+      body (
+        import nixpkgs {
+          inherit system overlays;
+        }
+      )
+    );
+
+}
diff --git a/lib/mergeAttrsRecursiveList.nix b/lib/mergeAttrsRecursiveList.nix
@@ -0,0 +1,31 @@
+{
+  lib,
+  ...
+}:
+
+{
+
+  mergeAttrsRecursiveList =
+    list:
+    let
+      # `binaryMerge start end` merges the elements at indices `index` of `list` such that `start <= index < end`
+      # Type: Int -> Int -> AttrSet
+      binaryMerge =
+        start: end:
+        # assert start < end; # Invariant
+        if end - start >= 2 then
+          # If there's at least 2 elements, split the range in two, recurse on each part and merge the result
+          # The invariant is satisfied because each half will have at least 1 element
+          lib.recursiveUpdate (binaryMerge start (start + (end - start) / 2)) (
+            binaryMerge (start + (end - start) / 2) end
+          )
+        else
+          # Otherwise there will be exactly 1 element due to the invariant, in which case we just return it directly
+          lib.elemAt list start;
+    in
+    if list == [ ] then
+      # Calling binaryMerge as below would not satisfy its invariant
+      { }
+    else
+      binaryMerge 0 (builtins.length list);
+}
diff --git a/lib/toCamelCase.nix b/lib/toCamelCase.nix
@@ -0,0 +1,35 @@
+{
+  lib,
+  ...
+}:
+
+{
+
+  toCamelCase =
+    str:
+    lib.throwIfNot (lib.isString str)
+      "toCamelCase does only accepts string values, but got ${lib.typeOf str}"
+      (
+        let
+          separators = lib.splitStringBy (
+            prev: curr:
+            builtins.elem curr [
+              "-"
+              "_"
+              " "
+            ]
+          ) false str;
+
+          parts = lib.flatten (
+            map (lib.splitStringBy (
+              prev: curr: lib.match "[a-z]" prev != null && lib.match "[A-Z]" curr != null
+            ) true) separators
+          );
+
+          first = if lib.length parts > 0 then lib.toLower (lib.head parts) else "";
+          rest = if lib.length parts > 1 then builtins.map lib.toSentenceCase (lib.tail parts) else [ ];
+        in
+        lib.concatStrings (map (lib.addContextFrom str) ([ first ] ++ rest))
+      );
+
+}
diff --git a/machines/cautus.nix b/machines/cautus.nix
@@ -0,0 +1,178 @@
+{
+
+  system = "x86_64-linux";
+  nixpkgsStable = true;
+
+  id = 2;
+  domain = "fc9f.de";
+
+  sshPublicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFfZ86/2bpfOY6G2pcEWPlmIS7Mf47lG+s+lmaD/ZL0Z";
+  wgPublicKey = "pLPU3QizJjY7QkCItenm/zRKKx5chCj5dTHBLtWHFQs=";
+
+  hardware = {
+    cpuVendor = "intel";
+    allowHibernation = false;
+  };
+
+  networking = {
+    ip4IsPrivate = false;
+    ip4Address = "91.132.144.50";
+    ip4PrefixLength = 22;
+    defaultGateway4 = "91.132.144.1";
+
+    ip6IsPrivate = false;
+    ip6Address = "2a03:4000:37:65f::1";
+    ip6PrefixLength = 64;
+
+    dn42 = {
+      ip6Address = "fd6b:6174:6a61::2";
+      ip6PrefixLength = 128;
+    };
+  };
+
+  nixosConfiguration =
+    {
+      machines,
+      machineConfig,
+      config,
+      lib,
+      ...
+    }:
+    {
+      sops.secrets = {
+        wgPrivateKey = {
+          owner = "systemd-network";
+          group = "systemd-network";
+        };
+      };
+
+      common = {
+        profiles.netcup.enable = true;
+        configure = {
+          primaryNetworkInterface.enable = true;
+          rootDisk.swap = {
+            enable = true;
+            size = "2G";
+          };
+        };
+      };
+
+      zpha = {
+        configure = {
+          dnsServer.enable = true;
+          xmppServer.enable = true;
+          mailServer.enable = true;
+          matrixBridges.enable = true;
+        };
+        profiles = {
+          zaphyra.enable = true;
+          dn42 = {
+            enable = true;
+            addresses = [
+              "${machineConfig.networking.dn42.ip6Address}/${toString machineConfig.networking.dn42.ip6PrefixLength}"
+              "fd6b:6174:6a61:53::${toString machineConfig.id}/128"
+            ];
+          };
+        };
+
+        websites = {
+          "zaphyra.eu".enable = true;
+          "oeffi.zaphyra.eu".enable = true;
+          "notes.zaphyra.eu".enable = true;
+          "dav.zaphyra.eu".enable = true;
+          "continuwuity.zaphyra.eu".enable = true;
+          "vault.zaphyra.eu".enable = true;
+          "git.zaphyra.eu".enable = true;
+          "bikemap.zaphyra.eu".enable = true;
+          "gts.zaphyra.eu".enable = true;
+        };
+      };
+
+      services.babeld = {
+        enable = true;
+        interfaces = {
+          wg-sorrah = { };
+        };
+        interfaceDefaults = {
+          type = "tunnel";
+        };
+        extraConfig = ''
+          local-port-readwrite 33123
+
+          export-table 254 # main
+          import-table 255 # local
+
+          kernel-priority 1000
+
+          # allow own prefixes
+          in ip fd6b:6174:6a61::/48 allow
+
+          # allow external routes
+          in ip fd00::/8 allow
+
+          in deny
+
+          redistribute ip fd6b:6174:6a61::${toString machineConfig.id} eq 48 allow
+          redistribute local deny
+          redistribute deny
+        '';
+      };
+
+      networking.firewall = {
+        checkReversePath = "loose";
+        trustedInterfaces = [ "wg-sorrah" ];
+        allowedUDPPorts = [
+          config.systemd.network.netdevs."42-wg-sorrah".wireguardConfig.ListenPort
+        ];
+      };
+
+      systemd.network = {
+        config.networkConfig = {
+          IPv6Forwarding = true;
+        };
+
+        netdevs = {
+          "42-wg-sorrah" = {
+            netdevConfig = {
+              Kind = "wireguard";
+              Name = "wg-sorrah";
+              MTUBytes = 1280;
+            };
+
+            wireguardConfig = {
+              PrivateKeyFile = config.sops.secrets."wgPrivateKey".path;
+              ListenPort = 4200 + machineConfig.id;
+              FirewallMark = 4200 + machineConfig.id;
+            };
+
+            wireguardPeers = lib.singleton {
+              PublicKey = machines.sorrah.wgPublicKey;
+              AllowedIPs = lib.singleton "::/0";
+              Endpoint = "[${machines.sorrah.networking.ip6Address}]:${toString (4200 + machines.sorrah.id)}";
+              PersistentKeepalive = 10;
+            };
+          };
+        };
+
+        networks = {
+          "42-wg-sorrah" = {
+            matchConfig.Name = "wg-sorrah";
+            linkConfig = {
+              RequiredForOnline = false;
+              Multicast = true;
+            };
+            networkConfig = {
+              DHCP = false;
+              IPv6AcceptRA = false;
+            };
+            address = [
+              "fe80:fc9f::${toString machineConfig.id}/64"
+              "fd6b:6174:6a61:fc9f::${toString machineConfig.id}/48"
+            ];
+          };
+        };
+      };
+
+    };
+
+}
diff --git a/machines/clevai.nix b/machines/clevai.nix
@@ -0,0 +1,71 @@
+{
+
+  system = "x86_64-linux";
+  nixpkgsStable = true;
+
+  id = 5;
+  domain = "fc9f.de";
+
+  sshPublicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH+aZ4jTRDA9Bf9i8UBdGzucahtpDZbjtyaTfR5BE9j4";
+  wgPublicKey = "yP1kDJLP4YmHjDLKlOu7bwyd7wCkPHHAx4tY28b/Mms=";
+
+  hardware = {
+    cpuVendor = "intel";
+    allowHibernation = false;
+  };
+
+  networking = {
+    ip4IsPrivate = false;
+    ip4Address = "152.89.106.158";
+    ip4PrefixLength = 22;
+    defaultGateway4 = "152.89.104.1";
+
+    ip6IsPrivate = false;
+    ip6Address = "2a03:4000:39:e9a::1";
+    ip6PrefixLength = 64;
+    defaultGateway6 = "fe80::1";
+
+    dn42 = {
+      ip6Address = "fd6b:6174:6a61::5";
+      ip6PrefixLength = 128;
+    };
+  };
+
+  nixosConfiguration =
+    {
+      machineConfig,
+      ...
+    }:
+    {
+
+      sops.secrets = {
+        wgPrivateKey = {
+          owner = "systemd-network";
+          group = "systemd-network";
+        };
+      };
+
+      common = {
+        profiles.netcup.enable = true;
+        configure = {
+          primaryNetworkInterface.enable = true;
+          rootDisk.swap = {
+            enable = true;
+            size = "2G";
+          };
+        };
+      };
+
+      zpha.profiles = {
+        zaphyra.enable = true;
+        dn42 = {
+          enable = true;
+          addresses = [
+            "${machineConfig.networking.dn42.ip6Address}/${toString machineConfig.networking.dn42.ip6PrefixLength}"
+          ];
+        };
+      };
+
+    };
+
+}
diff --git a/machines/cuvier.nix b/machines/cuvier.nix
@@ -0,0 +1,200 @@
+{
+
+  system = "x86_64-linux";
+  nixpkgsStable = true;
+
+  id = 4;
+  domain = "fc9f.de";
+
+  sshPubKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIANWOi6NMsfZ8wXOj/DXc092yajzG3VjPfRE0M9pViGh";
+  wgPublicKey = "CdnaBQL7c7zX0ORKhYyXp3HZ1kjqoEIGW03/mCCwAgI=";
+  syncthingId = "JUIJQZE-AWVYZIZ-CR6E66M-TAJIRDH-NEPEPZW-FHO37MJ-QM2MV5Q-OFJTEQI";
+
+  hardware = {
+    cpuVendor = "amd";
+    allowHibernation = false;
+  };
+
+  networking = {
+    primaryInterface = "enp3s0f0";
+
+    ip4IsPrivate = true;
+    ip4Address = "192.168.2.110";
+    ip4PrefixLength = 24;
+    defaultGateway4 = "192.168.2.1";
+
+    ip6IsPrivate = false;
+    ip6Address = "2a03:4000:4d:5e:acab::4";
+    ip6PrefixLength = 128;
+
+    dn42 = {
+      ip6Address = "fd6b:6174:6a61::4";
+      ip6PrefixLength = 128;
+    };
+  };
+
+  nixosConfiguration =
+    { lib, pkgs, ... }:
+    {
+      boot.initrd.systemd.emergencyAccess = true;
+      boot.initrd.availableKernelModules = [
+        "ehci_pci"
+        "xhci_pci"
+        "ahci"
+        "usbhid"
+        "usb_storage"
+        "sd_mod"
+        "uas"
+      ];
+
+      boot.initrd.luks.devices = {
+        data.device = "/dev/disk/by-partlabel/cuvier-data";
+        backup.device = "/dev/disk/by-partlabel/cuvier-backup";
+      };
+
+      systemd.tmpfiles.settings."mounts" = {
+        "/mnt/music".d = {
+          mode = "0755";
+          user = "zaphyra";
+          group = "users";
+        };
+        "/mnt/syncthing".d = {
+          mode = "0750";
+          user = "zaphyra";
+          group = "users";
+        };
+        "/mnt/restic".d = {
+          mode = "0700";
+          user = "restic";
+          group = "restic";
+        };
+      };
+
+      fileSystems = {
+        "/mnt/music" = {
+          device = "/dev/mapper/data";
+          options = [
+            "subvol=music"
+            "compress=zstd"
+            "discard=async"
+          ];
+        };
+        "/mnt/syncthing" = {
+          device = "/dev/mapper/data";
+          options = [
+            "subvol=syncthing"
+            "compress=zstd"
+            "discard=async"
+          ];
+        };
+        "/mnt/restic" = {
+          device = "/dev/mapper/backup";
+          options = [
+            "subvol=restic"
+            "discard=async"
+          ];
+        };
+      };
+
+      services.syncthing.settings.folders.zaphyra-music-orig.path = "/mnt/music";
+
+      sops.secrets = {
+        wgPrivateKey = {
+          owner = "systemd-network";
+          group = "systemd-network";
+        };
+      };
+
+      systemd.services.docker.path = [ pkgs.nftables ];
+      security.lockKernelModules = lib.mkForce false;
+      networking.firewall.trustedInterfaces = [
+        "docker0"
+        "br-88669f4be391"
+      ];
+      virtualisation.docker = {
+        enable = true;
+        package = pkgs.docker_29;
+        storageDriver = "btrfs";
+        daemon.settings = {
+          firewall-backend = "nftables";
+          data-root = "/persist/system/var/lib/docker";
+        };
+      };
+
+      users.users.zaphyra.extraGroups = [ "docker" ];
+
+      services.auto-cpufreq.enable = true;
+
+      common = {
+        profiles = {
+          nvme.enable = true;
+        };
+        security = {
+          kernel.enable = false;
+        };
+        configure = {
+          primaryNetworkInterface = {
+            enable = true;
+            acceptRouterAdvertisements = true;
+            ip6Address = null;
+          };
+          rootDisk.swap = {
+            enable = true;
+            size = "2G";
+          };
+        };
+      };
+
+      zpha = {
+        websites = {
+          "ctu.cx".enable = true;
+          "fedi.ctu.cx".enable = true;
+          "hass.zaphyra.eu".enable = true;
+          "gomuks.zaphyra.eu".enable = true;
+          "things.zaphyra.eu".enable = true;
+          "music.zaphyra.eu".enable = true;
+          "memories.zaphyra.eu".enable = true;
+          "links.zaphyra.eu".enable = true;
+        };
+        configure = {
+          netcupTunnel.enable = true;
+          syncthing.enable = true;
+          floraCtl.enable = true;
+          syncthingBackup.enable = true;
+        };
+        profiles = {
+          zaphyra.enable = true;
+          dn42.enable = true;
+          resticBackupTarget = {
+            enable = true;
+            path = "/mnt/restic";
+            keys = {
+              cuvier-syncthing-zaphyra-zaphyra-db-richtlinien = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOuRCzv8FO3EaUY9R36cg7RNRaRsMNEdxbUAMrU8hXXX";
+              cuvier-syncthing-zaphyra-documents = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPu2w5POF//j3FWQJ/h5+Upal9hqe4ytDWUX6Nsoow+1";
+              cuvier-syncthing-zaphyra-pictures = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILZCB0e1RLyEJlu7gk45R7Y35Kal5VXOCGE+gYvVm1A+";
+              cuvier-syncthing-zaphyra-videos = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIPbM811C0WvdN+19JBm3ulyb0SMoYIhT+GLU2pXPiEA";
+              cuvier-syncthing-zaphyra-audiobooks = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE6+dtNnZuVqnP7Get7pQWTPRrgf4H+L/d4I69y9+M40";
+              cuvier-syncthing-zaphyra-music-orig = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJAAvOyzZz6YgbUuYRFZH1oHbboVUAXQM0hSTXJYFFwA";
+              cuvier-syncthing-zaphyra-media = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAjf9rztvIBVjtuJIMaSCo8nIZuLNjrlA5NoNH/0S+YK";
+              cuvier-navidrome = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIP0vCwn4H04RqiLFUVK06N1ZOhEvNgdBod1Eedu82LHP";
+              cuvier-things = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG9ME2e9a2BbFgTxVY5OSL0VupYxZ10SjcLeO27qBBIF";
+              cuvier-gotosocial = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOvl23IonsUnU5nDVMjNp0W56HcT1TTuXRvFrkOay0iM";
+              cuvier-immich = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA8a7B0GzYf1J1Of8iwSMFQUON6CWCwnJW94K3uX95ij";
+              isodon-gotosocial = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID85l2Im1ff0JGp8vH8IngnjKB3K/cFiur/grPxWJbUE";
+              isodon-navidrome = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMv54uqcNCmcbYWzA1mGPeKrh29E2+/sr08caX3jihQc";
+              cautus-gotosocial = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIADOmcKGelaxzS9oObFMOdfUdm/PWS6Og9IJFZlLrsvm";
+              cautus-memos = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDJZ4Y87FvE9ejTh2X01u73+iUYAbSxHz0SzgQ/oMW7W";
+              cautus-continuwuity = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICnwja11WL5lX7uUnuapINM5NydD9reJ1N6uIR8IrUnO";
+              cautus-radicale = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK98eqmXaUpbm6PrRi/n2WmQ+Oo0x8z/JQathF8OPzNU";
+              cautus-prosody = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDe5En9owlKsj15sgWoq0zOFuAfc7VX3ON5DJ8TPIXAP";
+              cautus-vaultwarden = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJrsALG/N1wAA9T4MzMkbdA8LKNSsi38I4AsrmKi3eN2";
+              cautus-gitolite = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKrxAUyM6a7Rw1vjK+OyOR+9bYrWfV3L7bu05w9IPG+h";
+              cautus-mailserver = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGsKAXf8yLKv+B8FIrIssxlryqRPtuajPLJ7hVRh0dbz";
+            };
+          };
+        };
+
+      };
+    };
+
+}
diff --git a/machines/default.nix b/machines/default.nix
@@ -0,0 +1,58 @@
+{
+  lib ? import "${(import ../npins).nixpkgs}/lib",
+  npins ? import ../npins,
+  ...
+}:
+
+(
+  let
+    # read the current directorys files and pipe the result through a list of functions
+    machines = builtins.listToAttrs (
+      lib.pipe (builtins.readDir ./.) [
+        # convert to a list containing just the attribute names
+        (builtins.attrNames)
+        # drop "default.nix" from the list
+        (builtins.filter (name: name != "default.nix"))
+        # convert each list-element containing its file-name to an element containing the file's content
+        (builtins.map (name: {
+          # remove '.nix' suffix
+          name = lib.removeSuffix ".nix" name;
+          value = import ./${name};
+        }))
+      ]
+    );
+
+    nixosModules = import ../nixosModules { inherit lib; };
+    overlays = import ../overlays { inherit npins; };
+
+    specialArgs = {
+      inherit npins machines nixosConfigurations;
+      resources = import ../resources { inherit npins lib; };
+      sopsSecrets = import ../secrets { inherit npins lib; };
+    };
+
+    nixosConfigurations = lib.flip builtins.mapAttrs machines (
+      machineName: machineConfig:
+      let
+        nixpkgs = if !machineConfig.nixpkgsStable then npins.nixpkgsUnstable else npins.nixpkgs;
+      in
+      import "${nixpkgs}/nixos/lib/eval-config.nix" {
+        system = machineConfig.system;
+
+        specialArgs = specialArgs // {
+          machineConfig = machineConfig // {
+            inherit machineName;
+          };
+        };
+
+        modules = lib.flatten [
+          { nixpkgs.overlays = overlays.default; }
+          nixosModules.default
+          machineConfig.nixosConfiguration
+        ];
+      }
+    );
+
+  in
+  nixosConfigurations
+)
diff --git a/machines/isodon.nix b/machines/isodon.nix
@@ -0,0 +1,103 @@
+{
+
+  system = "x86_64-linux";
+  nixpkgsStable = true;
+
+  id = 3;
+  domain = "fc9f.de";
+
+  sshPubKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG/7udhktYVZHHZ2RXQeKHt0ACfcG5dmTpyd5LMw4U0O root@nixos";
+  wgPublicKey = "nvyhYuWJl/dKyV/2+bDrUisvL3mi38PsNzfdIDDwSjY=";
+  syncthingId = "QI2EPUE-4VMZ3XV-LXX3GXP-RHCWTRY-AACLSGL-YG7MIYV-THST74N-KJGIBQ6";
+
+  hardware = {
+    cpuVendor = "intel";
+    allowHibernation = false;
+  };
+
+  networking = {
+    primaryInterface = "enp1s0";
+
+    ip4IsPrivate = true;
+    ip4Address = "192.168.2.111";
+    ip4PrefixLength = 24;
+    defaultGateway4 = "192.168.2.1";
+
+    ip6IsPrivate = false;
+    ip6Address = "2a03:4000:4d:5e:acab::3";
+    ip6PrefixLength = 128;
+
+    dn42 = {
+      ip6Address = "fd6b:6174:6a61::3";
+      ip6PrefixLength = 128;
+    };
+  };
+
+  nixosConfiguration = _: {
+    boot.initrd.systemd.emergencyAccess = true;
+    boot.kernel.sysctl."net.ipv6.conf.all.proxy_ndp" = true;
+
+    sops.secrets = {
+      wgPrivateKey = {
+        owner = "systemd-network";
+        group = "systemd-network";
+      };
+    };
+
+    common = {
+      profiles.nvme.enable = true;
+      configure = {
+        primaryNetworkInterface = {
+          enable = true;
+          acceptRouterAdvertisements = true;
+          ip6Address = null;
+        };
+        rootDisk.swap = {
+          enable = true;
+          size = "2G";
+        };
+      };
+    };
+
+    zpha = {
+      websites = {
+        "fedi.home.ctu.cx".enable = true;
+        "music.zaphyra.eu" = {
+          enable = true;
+          subdomain = "music2";
+        };
+      };
+
+      configure = {
+        syncthing.enable = true;
+        netcupTunnel.enable = true;
+      };
+
+      profiles = {
+        zaphyra.enable = true;
+        dn42.enable = true;
+        resticBackupTarget = {
+          enable = true;
+          path = "/persist/system/restic-backups";
+          keys = {
+            cuvier-navidrome = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIP0vCwn4H04RqiLFUVK06N1ZOhEvNgdBod1Eedu82LHP";
+            cuvier-things = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG9ME2e9a2BbFgTxVY5OSL0VupYxZ10SjcLeO27qBBIF";
+            cuvier-immich = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA8a7B0GzYf1J1Of8iwSMFQUON6CWCwnJW94K3uX95ij";
+            cuvier-gotosocial = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOvl23IonsUnU5nDVMjNp0W56HcT1TTuXRvFrkOay0iM";
+            isodon-gotosocial = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID85l2Im1ff0JGp8vH8IngnjKB3K/cFiur/grPxWJbUE";
+            isodon-navidrome = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMv54uqcNCmcbYWzA1mGPeKrh29E2+/sr08caX3jihQc";
+            cautus-gotosocial = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIADOmcKGelaxzS9oObFMOdfUdm/PWS6Og9IJFZlLrsvm";
+            cautus-memos = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDJZ4Y87FvE9ejTh2X01u73+iUYAbSxHz0SzgQ/oMW7W";
+            cautus-continuwuity = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICnwja11WL5lX7uUnuapINM5NydD9reJ1N6uIR8IrUnO";
+            cautus-radicale = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK98eqmXaUpbm6PrRi/n2WmQ+Oo0x8z/JQathF8OPzNU";
+            cautus-prosody = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDe5En9owlKsj15sgWoq0zOFuAfc7VX3ON5DJ8TPIXAP";
+            cautus-vaultwarden = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJrsALG/N1wAA9T4MzMkbdA8LKNSsi38I4AsrmKi3eN2";
+            cautus-gitolite = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKrxAUyM6a7Rw1vjK+OyOR+9bYrWfV3L7bu05w9IPG+h";
+            cautus-mailserver = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGsKAXf8yLKv+B8FIrIssxlryqRPtuajPLJ7hVRh0dbz";
+          };
+        };
+      };
+    };
+  };
+
+}
diff --git a/machines/leucas.nix b/machines/leucas.nix
@@ -0,0 +1,71 @@
+{
+
+  system = "x86_64-linux";
+  nixpkgsStable = true;
+
+  id = 6;
+  domain = "fc9f.de";
+
+  sshPublicKey = "";
+  wgPublicKey = "";
+
+  hardware = {
+    cpuVendor = "intel";
+    allowHibernation = false;
+  };
+
+  networking = {
+    dn42 = {
+      ip6Address = "fd6b:6174:6a61::6";
+      ip6PrefixLength = 128;
+    };
+  };
+
+  nixosConfiguration = _: {
+    sops.secrets = {
+      wgPrivateKey = {
+        owner = "systemd-network";
+        group = "systemd-network";
+      };
+    };
+
+    common = {
+      configure.rootDisk.swap = {
+        enable = true;
+        size = "16G";
+      };
+
+      profiles = {
+        nvme.enable = true;
+      };
+
+      programs = {
+        shellUtilities.enable = true;
+        nixUtilities.enable = true;
+      };
+
+      services = {
+        greetd.autoLogin = {
+          enable = true;
+          user = "zaphyra";
+        };
+      };
+    };
+
+    zpha = {
+      configure = {
+        cccdaWifi.enable = true;
+      };
+
+      profiles = {
+        zaphyra.enable = true;
+        graphical.enable = true;
+      };
+
+      services = {
+        keyd.enable = true;
+      };
+    };
+  };
+
+}
diff --git a/machines/sorrah.nix b/machines/sorrah.nix
@@ -0,0 +1,78 @@
+{
+
+  system = "x86_64-linux";
+  nixpkgsStable = true;
+
+  id = 1;
+  domain = "fc9f.de";
+
+  sshPublicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG/7udhktYVZHHZ2RXQeKHt0ACfcG5dmTpyd5LMw4U0O root@nixos";
+  wgPublicKey = "J+kRRNU65JGc0yk04v6P3tFwHSQOIfq8EkfD2gFupg4=";
+
+  hardware = {
+    cpuVendor = "intel";
+    allowHibernation = false;
+  };
+
+  networking = {
+    ip4IsPrivate = false;
+    ip4Address = "194.36.145.49";
+    ip4PrefixLength = 22;
+    defaultGateway4 = "194.36.144.1";
+
+    ip6IsPrivate = false;
+    ip6Address = "2a03:4000:4d:5e::1";
+    ip6PrefixLength = 64;
+
+    dn42 = {
+      wgPublicKey = "MRXPP//j+BDCiUyrYHdXtdULAsCZyfgumas8pxp6oiE=";
+      ip6Address = "fd6b:6174:6a61::1";
+      ip6PrefixLength = 48;
+    };
+  };
+
+  nixosConfiguration =
+    { lib, ... }:
+    {
+
+      boot.initrd.systemd.emergencyAccess = true;
+      boot.kernel.sysctl."net.ipv6.conf.all.proxy_ndp" = true;
+
+      sops.secrets = {
+        wgPrivateKey = {
+          owner = "systemd-network";
+          group = "systemd-network";
+        };
+      };
+
+      common = {
+        profiles.netcup.enable = true;
+        configure = {
+          primaryNetworkInterface.enable = true;
+          rootDisk.swap = {
+            enable = true;
+            size = "2G";
+          };
+        };
+      };
+
+      zpha = {
+        profiles.zaphyra.enable = true;
+
+        websites = {
+          "flauschehorn.zaphyra.eu".enable = true;
+          "ip.fc9f.de".enable = true;
+        };
+
+        configure = {
+          dnsServer.enable = true;
+          dn42Router.enable = true;
+          netcupTunnel = {
+            enable = true;
+            addresses = lib.singleton "2a03:4000:4d:5e:acab::1/112";
+          };
+        };
+      };
+    };
+
+}
diff --git a/maidModules/dbus.nix b/maidModules/dbus.nix
@@ -0,0 +1,25 @@
+{
+  config,
+  pkgs,
+  lib,
+  ...
+}:
+
+{
+
+  options.dbus.packages = lib.mkOption {
+    type = with lib.types; listOf package;
+    default = [ ];
+  };
+
+  config.file.xdg_data = lib.mkIf (config.dbus.packages != [ ]) {
+    "dbus-1/services" = {
+      source = pkgs.symlinkJoin {
+        name = "user-dbus-services";
+        paths = config.dbus.packages;
+        stripPrefix = "/share/dbus-1/services";
+      };
+    };
+  };
+
+}
diff --git a/maidModules/environment.nix b/maidModules/environment.nix
@@ -0,0 +1,36 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+let
+  inherit (lib) types;
+  cfg = config.environment;
+
+in
+{
+
+  options.environment = {
+    enable = lib.mkEnableOption "environment-related settings";
+    sessionVariables = lib.mkOption {
+      type = types.attrsOf types.str;
+      default = { };
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    file.home.".profile".text = ''
+      source <(${pkgs.systemd}/lib/systemd/user-environment-generators/30-systemd-environment-d-generator)
+    '';
+
+    file.xdg_config."environment.d/60-custom-session-vars.conf".text =
+      let
+        vars = cfg.sessionVariables;
+        varEntries = lib.attrsets.mapAttrsToList (k: v: "${k}=${v}") vars;
+        varFileContent = builtins.concatStringsSep "\n" varEntries;
+      in
+      varFileContent;
+  };
+
+}
diff --git a/maidModules/programs/fish.nix b/maidModules/programs/fish.nix
@@ -0,0 +1,132 @@
+{
+  lib,
+  pkgs,
+  config,
+  ...
+}:
+let
+  inherit (lib) types;
+  cfg = config.programs.fish;
+
+  toFishFunc =
+    body: funcName:
+    if (types.typeOf body) == "string" then
+      pkgs.writeFish "${funcName}.fish" ''
+        function ${funcName};
+        ${lib.concatMapStringsSep "\n" (line: "\t${line}") (lib.splitString "\n" body)}
+        end
+      ''
+    else if lib.isDerivation body then
+      body
+    else
+      throw "Input is of invalid type ${types.typeOf body}, expected `path` or `string`.";
+
+  isVendored =
+    plugin:
+    types.any (p: lib.pathExists "${plugin}/share/fish/${p}") [
+      "vendor_conf.d"
+      "vendor_completions.d"
+      "vendor_functions.d"
+    ];
+in
+{
+
+  options.programs.fish = {
+    enable = lib.mkEnableOption "fish";
+
+    config = lib.mkOption {
+      default = "";
+      type = types.lines;
+    };
+
+    functions = lib.mkOption {
+      default = { };
+      type =
+        with types;
+        attrsOf (oneOf [
+          lines
+          path
+        ]);
+    };
+
+    earlyConfigFiles = lib.mkOption {
+      default = { };
+      type = types.attrsOf types.lines;
+    };
+
+    abbrs = lib.mkOption {
+      default = { };
+      type = types.attrsOf types.lines;
+    };
+
+    aliases = lib.mkOption {
+      default = { };
+      type = types.attrsOf types.lines;
+    };
+
+    plugins = lib.mkOption {
+      type = types.attrsOf types.path;
+      default = { };
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    programs.fish.earlyConfigFiles = lib.mapAttrs' (
+      name: source:
+      lib.nameValuePair "plugin-${name}"
+        #  fish
+        ''
+          # Plugin ${name} -- ${source}
+          set -l src "${source}"
+
+          if test -d "$src/functions"
+              set fish_function_path $fish_function_path[1] "$src/functions" $fish_function_path[2..]
+          end
+
+          if test -d "$src/completions"
+              set fish_complete_path $fish_complete_path[1] "$src/completions" $fish_complete_path[2..]
+          end
+
+          for f in "$src/conf.d/"*
+              source "$f"
+          end
+
+          if test -f "$src/key_bindings.fish"
+              source "$src/key_bindings.fish"
+          end
+
+          if test -f "$src/init.fish"
+              source "$src/init.fish"
+          end
+        ''
+    ) (lib.filterAttrs (n: v: !(isVendored v)) cfg.plugins);
+
+    file.xdg_config = {
+      "fish/config.fish" = lib.mkIf (cfg.config != "") {
+        source = pkgs.writers.writeFish "config.fish" cfg.config;
+      };
+      "fish/conf.d/session-variables.fish" = lib.mkIf (config.environment.sessionVariables != { }) {
+        text = lib.concatMapAttrsStringSep "\n" (
+          name: value:
+          "set --global --export ${lib.escapeShellArg name} ${lib.escapeShellArg (toString value)}"
+        ) config.environment.sessionVariables;
+      };
+      "fish/conf.d/abbreviations.fish" = lib.mkIf (cfg.abbrs != { }) {
+        text = lib.concatMapAttrsStringSep "\n" (
+          name: value: "abbr --add -- ${lib.escapeShellArg name} ${lib.escapeShellArg (toString value)}"
+        ) cfg.abbrs;
+      };
+      "fish/conf.d/aliases.fish".text = lib.concatMapAttrsStringSep "\n" (
+        name: value: "alias -- ${lib.escapeShellArg name} ${lib.escapeShellArg (toString value)}"
+      ) cfg.aliases;
+    }
+    // (lib.mapAttrs' (
+      name: val: lib.nameValuePair "fish/functions/${name}.fish" { source = toFishFunc val name; }
+    ) cfg.functions)
+    // (lib.mapAttrs' (
+      name: val:
+      lib.nameValuePair "fish/conf.d/${name}.fish" { source = pkgs.writers.writeFish "${name}.fish" val; }
+    ) cfg.earlyConfigFiles);
+  };
+
+}
diff --git a/maidModules/programs/git.nix b/maidModules/programs/git.nix
@@ -0,0 +1,36 @@
+{
+  lib,
+  pkgs,
+  config,
+  ...
+}:
+let
+  inherit (lib) types;
+  cfg = config.programs.git;
+  settingsFormat = pkgs.formats.ini { };
+
+in
+{
+
+  options.programs.git = {
+    enable = lib.mkEnableOption "git";
+    ignores = lib.mkOption {
+      default = { };
+      type = with types; listOf str;
+    };
+    settings = lib.mkOption {
+      default = { };
+      type = settingsFormat.type;
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    packages = [ pkgs.git ];
+
+    file.xdg_config = {
+      "git/config".source = settingsFormat.generate "gitconfig.ini" cfg.settings;
+      "git/ignore".text = lib.concatStringsSep "\n" cfg.ignores;
+    };
+  };
+
+}
diff --git a/maidModules/programs/lazygit.nix b/maidModules/programs/lazygit.nix
@@ -0,0 +1,34 @@
+{
+  lib,
+  pkgs,
+  config,
+  ...
+}:
+let
+  inherit (lib) types;
+  cfg = config.programs.lazygit;
+  settingsFormat = pkgs.formats.yaml { };
+
+in
+{
+
+  options.programs.lazygit = {
+    enable = lib.mkEnableOption "lazygit";
+    defaultShell = lib.mkOption {
+      type = types.bool;
+      default = true;
+    };
+    settings = lib.mkOption {
+      default = { };
+      type = settingsFormat.type;
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    packages = [ pkgs.lazygit ];
+
+    file.xdg_config."lazygit/config.yml".source =
+      settingsFormat.generate "lazygit-config.yaml" cfg.settings;
+  };
+
+}
diff --git a/maidModules/programs/starship.nix b/maidModules/programs/starship.nix
@@ -0,0 +1,51 @@
+{
+  lib,
+  pkgs,
+  config,
+  ...
+}:
+let
+  inherit (lib) types;
+  cfg = config.programs.starship;
+  settingsFormat = pkgs.formats.toml { };
+
+in
+{
+
+  options.programs.starship = {
+    enable = lib.mkEnableOption "starship";
+    settings = lib.mkOption {
+      type = settingsFormat.type;
+      default = { };
+    };
+    enableFishIntegration = lib.mkOption {
+      type = types.bool;
+      default = config.programs.fish.enable;
+    };
+    enableTransience = lib.mkOption {
+      type = types.bool;
+      default = false;
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    packages = [ pkgs.starship ];
+
+    environment.sessionVariables.STARSHIP_CONFIG = lib.mkIf (
+      cfg.settings != { }
+    ) "${cfg.xdg.configDirectory}/starship/config.toml";
+
+    programs.fish.config = lib.mkIf cfg.enableFishIntegration (
+      lib.mkAfter ''
+        if test "$TERM" != "dumb"
+          starship init fish | source
+          ${lib.optionalString cfg.enableTransience "enable_transience"}
+        end
+      ''
+    );
+
+    file.xdg_config."starship/config.toml".source =
+      settingsFormat.generate "starship-config.toml" cfg.settings;
+  };
+
+}
diff --git a/maidModules/xdg.nix b/maidModules/xdg.nix
@@ -0,0 +1,121 @@
+{
+  options,
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+let
+  inherit (lib) types;
+  listOrSingletonOf =
+    type: with types; coercedTo (either (listOf type) type) lib.toList (listOf type);
+
+  cfg = config.xdg;
+  mimeappsFormat = pkgs.formats.ini { listToValue = lib.concatStringsSep ";"; };
+
+in
+{
+
+  options.xdg = {
+    enable = lib.mkEnableOption "xdg foo";
+    cacheDirectory = lib.mkOption {
+      type = types.str;
+      default = "~/.cache";
+    };
+    configDirectory = lib.mkOption {
+      type = types.str;
+      default = "~/.config";
+    };
+
+    dataDirectory = lib.mkOption {
+      type = types.str;
+      default = "~/.local/share";
+    };
+
+    stateDirectory = lib.mkOption {
+      type = types.str;
+      default = "~/.local/state";
+    };
+
+    mime-apps = {
+      addedAssociations = lib.mkOption {
+        type = with types; attrsOf (listOrSingletonOf str);
+        default = { };
+      };
+
+      removedAssociations = lib.mkOption {
+        type = with types; attrsOf (listOrSingletonOf str);
+        default = { };
+      };
+
+      defaultApplications = lib.mkOption {
+        type = with types; attrsOf (listOrSingletonOf str);
+        default = { };
+      };
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    environment.sessionVariables = {
+      XDG_CACHE_HOME = lib.mkIf (
+        cfg.cacheDirectory != options.xdg.cacheDirectory.default
+      ) cfg.cacheDirectory;
+      XDG_CONFIG_HOME = lib.mkIf (
+        cfg.configDirectory != options.xdg.configDirectory.default
+      ) cfg.configDirectory;
+      XDG_DATA_HOME = lib.mkIf (cfg.dataDirectory != options.xdg.dataDirectory.default) cfg.dataDirectory;
+      XDG_STATE_HOME = lib.mkIf (
+        cfg.stateDirectory != options.xdg.stateDirectory.default
+      ) cfg.stateDirectory;
+    };
+
+    file = {
+      xdg_config."mimeapps.list" =
+        let
+          nonDefault = {
+            added = cfg.mime-apps.addedAssociations != options.xdg.mime-apps.addedAssociations.default;
+            removed = cfg.mime-apps.removedAssociations != options.xdg.mime-apps.removedAssociations.default;
+            default = cfg.mime-apps.defaultApplications != options.xdg.mime-apps.defaultApplications.default;
+          };
+        in
+        lib.mkIf (lib.lists.any lib.trivial.id (lib.attrValues nonDefault)) {
+          source = mimeappsFormat.generate "mimeapps.list" {
+            "Added Associations" = cfg.mime-apps.addedAssociations;
+            "Removed Associations" = cfg.mime-apps.removedAssociations;
+            "Default Applications" = cfg.mime-apps.defaultApplications;
+          };
+        };
+      xdg_data."glib-2.0/schemas".source = pkgs.buildEnv {
+        name = "user-gsettings-schemas";
+        paths = config.packages;
+        pathsToLink = [ "/share/gsettings-schemas" ];
+        ignoreCollisions = true;
+        postBuild = ''
+          if [ -x $out/bin/glib-compile-schemas -a -w $out/share/glib-2.0/schemas ]; then
+              $out/bin/glib-compile-schemas $out/share/glib-2.0/schemas
+          fi
+
+          if [ -d  $out/share/gsettings-schemas/ ]; then
+            # symlink any schema files to the standard schema directory
+            for d in $out/share/gsettings-schemas/*; do
+              # Force symlink, in case there are duplicates
+              ln -fs $d/glib-2.0/schemas/*.xml $out
+            done
+
+            # and compile them
+            if [ -w $out ]; then
+              ${pkgs.glib.dev}/bin/glib-compile-schemas $out
+            fi
+          fi
+        '';
+      };
+
+      # pkgs.symlinkJoin {
+      #   name = "user-gsettings-schemas";
+      #   paths = config.packages;
+      #   stripPrefix = "/share/gsettings-schemas";
+      # };
+    };
+  };
+
+}
diff --git a/nixosModules/common/configure/boot.nix b/nixosModules/common/configure/boot.nix
@@ -0,0 +1,91 @@
+{
+  npins,
+  lib,
+  config,
+  pkgs,
+  ...
+}:
+let
+  inherit (lib) types;
+  cfg = config.common.configure.boot;
+  lanzaboote = import npins.lanzaboote {
+    inherit pkgs;
+  };
+
+in
+{
+
+  options.common.configure.boot = {
+    enable = lib.mkEnableOption "basic systemd-boot config";
+    secureboot = lib.mkEnableOption "secureboot support via lanzaboote";
+
+    configurationLimit = lib.mkOption {
+      type = types.number;
+      default = 10;
+    };
+    plymouth = {
+      enable = lib.mkEnableOption "graphical bootscreen";
+      theme = lib.mkOption {
+        type = types.str;
+        default = "bgrt";
+      };
+    };
+  };
+
+  imports =
+    (lib.mkIf cfg.enable [
+      lanzaboote.nixosModules.lanzaboote
+    ]).content;
+
+  config = lib.mkIf cfg.enable (
+    lib.mkMerge [
+      {
+        boot.initrd.systemd.enable = true;
+        boot.loader = {
+          grub.enable = false;
+          systemd-boot = {
+            enable = lib.mkDefault true;
+            inherit (cfg) configurationLimit;
+          };
+          efi = {
+            canTouchEfiVariables = true;
+            efiSysMountPoint = "/boot";
+          };
+        };
+      }
+
+      (lib.mkIf cfg.plymouth.enable {
+        boot = {
+          consoleLogLevel = 0;
+          initrd.verbose = false;
+          kernelParams = [
+            "quiet"
+            "udev.log_level=3"
+          ];
+          plymouth = {
+            enable = true;
+            inherit (cfg) theme;
+          };
+        };
+      })
+
+      (lib.mkIf cfg.secureboot {
+        environment.systemPackages = with pkgs; [ sbctl ];
+
+        common.configure.persist.system.dirs = [
+          "/var/lib/sbctl"
+        ];
+
+        boot = {
+          loader.systemd-boot.enable = lib.mkForce false;
+          lanzaboote = {
+            enable = true;
+            pkiBundle = "/var/lib/sbctl";
+            inherit (cfg) configurationLimit;
+          };
+        };
+      })
+    ]
+  );
+
+}
diff --git a/nixosModules/common/configure/dn42Router.nix b/nixosModules/common/configure/dn42Router.nix
@@ -0,0 +1,315 @@
+{
+  lib,
+  config,
+  pkgs,
+  ...
+}:
+
+let
+  inherit (lib) types;
+  cfg = config.common.configure.dn42Router;
+
+in
+{
+
+  options.common.configure.dn42Router = {
+    enable = lib.mkEnableOption "";
+    asn = lib.mkOption {
+      type = types.int;
+    };
+    routerId = lib.mkOption {
+      type = types.int;
+    };
+    address = lib.mkOption {
+      type = types.str;
+    };
+    range = lib.mkOption {
+      type = types.str;
+    };
+    peerings = lib.mkOption {
+      default = { };
+      type =
+        with types;
+        attrsOf (submodule {
+          options = {
+            asn = lib.mkOption { type = types.int; };
+            remoteLinkLocalAddress = lib.mkOption { type = types.str; };
+            localLinkLocalAddress = lib.mkOption {
+              type = types.str;
+              default = "fe80::6b61/64";
+            };
+            endpoint = lib.mkOption {
+              type = with types; nullOr str;
+              default = null;
+            };
+            listenPort = lib.mkOption { type = types.int; };
+            publicKey = lib.mkOption { type = types.str; };
+            hasPresharedKey = lib.mkOption {
+              type = types.bool;
+              default = false;
+            };
+          };
+        });
+    };
+    babel.enable = lib.mkOption {
+      type = types.bool;
+      default = false;
+    };
+    babel.peerings = lib.mkOption {
+      default = { };
+      type =
+        with types;
+        attrsOf (submodule {
+          options = {
+            type = lib.mkOption {
+              type = types.str;
+              default = "tunnel";
+            };
+          };
+        });
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    networking.firewall.allowedUDPPorts = lib.mapAttrsToList (
+      _name: peerConfig: peerConfig.listenPort
+    ) cfg.peerings;
+
+    sops.secrets = lib.pipe cfg.peerings [
+      (lib.mapAttrsToList (
+        name: peerConfig:
+        [
+          (lib.nameValuePair "dn42/peerings/${name}/wgPrivateKey" {
+            owner = "systemd-network";
+            group = "systemd-network";
+          })
+        ]
+        ++ lib.optionals peerConfig.hasPresharedKey [
+          (lib.nameValuePair "dn42/peerings/${name}/wgPresharedKey" {
+            owner = "systemd-network";
+            group = "systemd-network";
+          })
+        ]
+      ))
+      lib.lists.flatten
+      lib.listToAttrs
+    ];
+
+    systemd = {
+      network = {
+        netdevs = lib.mapAttrs' (
+          name: peerConfig:
+          lib.nameValuePair "20-dn42${name}" {
+            netdevConfig = {
+              Kind = "wireguard";
+              Name = "dn42${name}";
+            };
+            wireguardConfig = {
+              ListenPort = peerConfig.listenPort;
+              PrivateKeyFile = config.sops.secrets."dn42/peerings/${name}/wgPrivateKey".path;
+            }
+            // (lib.optionalAttrs peerConfig.hasPresharedKey {
+              PresharedKeyFile = config.sops.secrets."dn42/peerings/${name}/wgPresharedKey".path;
+            });
+            wireguardPeers = [
+              {
+                PersistentKeepalive = 30;
+                Endpoint = lib.mkIf (!builtins.isNull peerConfig.endpoint) peerConfig.endpoint;
+                PublicKey = peerConfig.publicKey;
+                AllowedIPs = [
+                  "fd00::/8"
+                  peerConfig.remoteLinkLocalAddress
+                ];
+              }
+            ];
+          }
+        ) cfg.peerings;
+
+        networks = lib.mapAttrs' (
+          name: peerConfig:
+          lib.nameValuePair "20-dn42${name}" {
+            matchConfig.Name = "dn42${name}";
+            linkConfig.RequiredForOnline = "no";
+
+            address = [ peerConfig.localLinkLocalAddress ];
+            routes = [ { Destination = "${peerConfig.remoteLinkLocalAddress}/128"; } ];
+
+            networkConfig = {
+              IPv6Forwarding = true;
+              IPv6AcceptRA = false;
+              DHCP = false;
+            };
+          }
+        ) cfg.peerings;
+      };
+      services = {
+        systemd-networkd.after = [ "sops-install-secrets.service" ];
+        stayrtr = {
+          wantedBy = [
+            "multi-user.target"
+            "bird.service"
+          ];
+          serviceConfig.DynamicUser = true;
+          serviceConfig.ExecStart = ''
+            ${lib.getExe pkgs.stayrtr} \
+              -bind [::1]:8282 \
+              -cache=https://dn42.burble.com/roa/dn42_roa_46.json \
+              -checktime=false
+          '';
+        };
+      };
+    };
+
+    services.bird = {
+      enable = true;
+      package = pkgs.bird3;
+      preCheckConfig = ''
+        # Remove roa files for checking, because they are only available at runtime
+        sed -i 's|include "/etc/bird/roa_dn42.conf";||' bird.conf
+
+        cat -n bird.conf # here for debugging purposes
+      '';
+      config = ''
+        log syslog { debug, trace, info, remote, warning, error, auth, fatal, bug };
+        log stderr all;
+
+        define OWNAS = ${toString cfg.asn};
+        define OWNNET = ${cfg.range};
+        define OWNNETSET = [ ${cfg.range} ];
+        define OWNIP = ${cfg.address};
+
+        router id ${toString cfg.routerId};
+        hostname "${config.networking.hostName}";
+
+        roa6 table dn42_roa;
+
+        function is_self_net() -> bool {
+          return net ~ OWNNETSET;
+        }
+
+        function is_valid_network() -> bool {
+          return net ~ [
+            fd00::/8{44,64}
+          ];
+        }
+
+        function import_filter() {
+          if (net.type != NET_IP6 || ! is_valid_network() || is_self_net()) then
+            reject;
+
+          if (roa_check(dn42_roa, net, bgp_path.last) != ROA_VALID) then {
+            # Reject when unknown or invalid according to ROA
+            print "[dn42] ROA check failed for ", net, " ASN ", bgp_path.last;
+            reject;
+          }
+
+          accept;
+        }
+
+        function export_filter() {
+          if ( ! is_valid_network() ) then
+            reject;
+
+          if (source !~ [RTS_STATIC, RTS_BGP]) then
+            reject;
+
+          accept;
+        }
+
+        protocol rpki {
+          roa6 { table dn42_roa; };
+          remote ::1;
+          port 8282;
+          refresh 600;
+          retry 300;
+          expire 7200;
+        }
+
+        protocol device {
+          scan time 10;
+        }
+
+        protocol static {
+          route OWNNET unreachable;
+
+          ipv6 {
+            import all;
+            export none;
+          };
+        }
+
+        protocol kernel {
+          scan time 20;
+
+          ipv6 {
+            import none;
+            export filter {
+              # dont export static routes
+              if source = RTS_STATIC then reject;
+              # preferred outgoing source address
+              krt_prefsrc = OWNIP;
+              accept;
+            };
+          };
+        }
+
+        template bgp dn42_peers {
+          local as OWNAS;
+          path metric 1;
+          advertise hostname on;
+          enforce first as on;
+          med metric on;
+
+          ipv6 {
+            import keep filtered;
+            import limit 9000 action block;
+            import where import_filter();
+            next hop self; # advertise this router as next hop
+
+            export where export_filter();
+          };
+        }
+      ''
+      + (lib.concatStringsSep "\n" (
+        lib.mapAttrsToList (name: peerConfig: ''
+          protocol bgp ${name} from dn42_peers {
+            neighbor ${peerConfig.remoteLinkLocalAddress}%dn42${name} as ${toString peerConfig.asn};
+            enable extended messages;
+          }
+        '') cfg.peerings
+      ))
+      + (lib.optionalString cfg.babel.enable ''
+        /* babel config */
+        protocol direct {
+          ipv6;
+
+          ${lib.concatStringsSep "\n" (
+            lib.mapAttrsToList (name: _peerConfig: ''
+              interface "${name}";
+            '') cfg.babel.peerings
+          )}
+        };
+
+        protocol babel {
+          randomize router id on;
+
+          ipv6 {
+            import where source != RTS_BGP && is_self_net();
+            export where source != RTS_BGP && is_self_net();
+          };
+
+          ${lib.concatStringsSep "\n" (
+            lib.mapAttrsToList (name: peerConfig: ''
+              interface "${name}" {
+                type ${peerConfig.type};
+                next hop prefer ipv6;
+              };
+            '') cfg.babel.peerings
+          )}
+        };
+        /* End of babel config */
+      '');
+    };
+  };
+
+}
diff --git a/nixosModules/common/configure/fonts.nix b/nixosModules/common/configure/fonts.nix
@@ -0,0 +1,21 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.common.configure.fonts.enable = lib.mkEnableOption "basic font config";
+
+  config = lib.mkIf config.common.configure.fonts.enable {
+    fonts = {
+      fontconfig.enable = true;
+      fontDir.enable = true;
+      enableGhostscriptFonts = true;
+      enableDefaultPackages = true;
+    };
+  };
+
+}
diff --git a/nixosModules/common/configure/locale.nix b/nixosModules/common/configure/locale.nix
@@ -0,0 +1,34 @@
+{
+  config,
+  lib,
+  ...
+}:
+
+{
+
+  options.common.configure.locale.enable = lib.mkEnableOption "locale configuration";
+
+  config = lib.mkIf config.common.configure.locale.enable {
+    time.timeZone = "Europe/Berlin";
+    i18n = {
+      defaultLocale = "en_GB.UTF-8";
+      supportedLocales = [
+        "en_GB.UTF-8/UTF-8"
+        "de_DE.UTF-8/UTF-8"
+      ];
+
+      extraLocaleSettings = {
+        LC_ADDRESS = "de_DE.UTF-8";
+        LC_IDENTIFICATION = "de_DE.UTF-8";
+        LC_MEASUREMENT = "de_DE.UTF-8";
+        LC_MONETARY = "de_DE.UTF-8";
+        LC_NAME = "de_DE.UTF-8";
+        LC_NUMERIC = "en_GB.UTF-8";
+        LC_PAPER = "de_DE.UTF-8";
+        LC_TELEPHONE = "de_DE.UTF-8";
+        LC_TIME = "en_GB.UTF-8";
+      };
+    };
+  };
+
+}
diff --git a/nixosModules/common/configure/nix.nix b/nixosModules/common/configure/nix.nix
@@ -0,0 +1,59 @@
+{
+  npins,
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.common.configure.nix.enable = lib.mkEnableOption "sane nix defaults";
+
+  imports =
+    (lib.mkIf config.common.nix.enable [
+      (import "${npins.lixModule}/module.nix" { lix = null; })
+    ]).content;
+
+  config = lib.mkIf config.common.configure.nix.enable (
+    let
+      NIXPKGS_PATH = lib.cleanSource npins.nixpkgs;
+      NIXPKGS_UNSTABLE_PATH = lib.cleanSource npins.nixpkgsUnstable;
+
+    in
+    {
+      nix = {
+        package = pkgs.lix;
+        nixPath = lib.mkForce [ "nixpkgs=${NIXPKGS_PATH}" ];
+        channel.enable = lib.mkForce false;
+
+        registry = {
+          nixpkgs.to = {
+            type = "path";
+            path = NIXPKGS_PATH;
+          };
+
+          nixpkgsUnstable.to = {
+            type = "path";
+            path = NIXPKGS_UNSTABLE_PATH;
+          };
+        };
+
+        settings = {
+          nix-path = config.nix.nixPath;
+          trusted-users = [ "@wheel" ];
+          use-xdg-base-directories = true;
+          experimental-features = [
+            "flakes"
+            "nix-command"
+          ];
+        };
+      };
+
+      environment.sessionVariables = {
+        inherit NIXPKGS_PATH NIXPKGS_UNSTABLE_PATH;
+      };
+    }
+  );
+
+}
diff --git a/nixosModules/common/configure/persist.nix b/nixosModules/common/configure/persist.nix
@@ -0,0 +1,167 @@
+{
+  npins,
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+let
+  inherit (lib) types;
+
+  cfg = config.common.configure.persist;
+
+  perms = {
+    user = lib.mkOption {
+      type = types.str;
+      default = "root";
+    };
+    group = lib.mkOption {
+      type = types.str;
+      default = "root";
+    };
+    mode = lib.mkOption {
+      type = with types; nullOr str;
+      default = null;
+    };
+  };
+
+in
+{
+
+  options.common.configure.persist = {
+    home.enable = lib.mkOption {
+      type = types.bool;
+      default = false;
+    };
+    system = {
+      enable = lib.mkOption {
+        type = types.bool;
+        default = false;
+      };
+      dirs = lib.mkOption {
+        default = [ ];
+        type =
+          with types;
+          listOf (oneOf [
+            str
+            (submodule {
+              options = {
+                directory = lib.mkOption { type = types.str; };
+              }
+              // perms;
+            })
+          ]);
+      };
+      files = lib.mkOption {
+        default = [ ];
+        type =
+          with types;
+          listOf (oneOf [
+            str
+            (submodule {
+              options = {
+                file = lib.mkOption { type = types.str; };
+                parentDirectory = lib.mkOption {
+                  type = with types; nullOr (submodule perms);
+                  default = null;
+                };
+              }
+              // perms;
+            })
+          ]);
+      };
+
+    };
+  };
+
+  imports =
+    (lib.mkIf cfg.enable [
+      "${npins.preservation}/module.nix"
+    ]).content;
+
+  config = lib.mkMerge [
+    (lib.mkIf cfg.home.enable {
+      common.configure.rootDisk.subVolumes.home = true;
+      programs.fuse.userAllowOther = lib.mkDefault true;
+      preservation.enable = true;
+      preservation.preserveAt."/persist" = { };
+    })
+    (lib.mkIf cfg.system.enable {
+      common.configure.rootDisk.subVolumes.system = true;
+
+      boot.initrd.systemd.services.defenestrate = {
+        description = "Defenestrate old btrfs-root";
+        wantedBy = [ "initrd.target" ];
+        after = [ "cryptsetup.target" ];
+        before = [ "sysroot.mount" ];
+        onFailure = [ "emergency.target" ];
+        unitConfig.DefaultDependencies = "no";
+        serviceConfig.Type = "oneshot";
+        script = ''
+          mkdir -p /btrfs_tmp
+          mount -o "subvol=/" /dev/mapper/root /btrfs_tmp
+
+          ${lib.getExe pkgs.btrfs-progs} subvolume delete --recursive /btrfs_tmp/nixos-root-4
+
+          mv /btrfs_tmp/nixos-root-3 /btrfs_tmp/nixos-root-4
+          mv /btrfs_tmp/nixos-root-2 /btrfs_tmp/nixos-root-3
+          mv /btrfs_tmp/nixos-root-1 /btrfs_tmp/nixos-root-2
+
+          ${lib.getExe pkgs.btrfs-progs} subvolume create /btrfs_tmp/nixos-root-1
+          ${lib.getExe pkgs.btrfs-progs} subvolume set-default /btrfs_tmp/nixos-root-1
+
+          umount /btrfs_tmp
+        '';
+      };
+
+      systemd.tmpfiles.rules = [
+        "d /var/lib/private 0700 root root"
+      ];
+      systemd.services = lib.genAttrs [ "sops-install-secrets" "sops-install-secrets-for-users" ] (_: {
+        unitConfig.RequiresMountsFor = [ "/persist/system" ];
+      });
+
+      systemd.tmpfiles.settings.preservation =
+        let
+          directories = map (
+            elem: if (lib.isAttrs elem) then elem.directory else elem
+          ) config.preservation.preserveAt."/persist/system".directories;
+        in
+        lib.genAttrs (lib.filter (elem: lib.hasPrefix "/var/lib/private/" elem) directories) (name: {
+          "d" = {
+            type = "!d";
+            mode = lib.mkForce "0700";
+          };
+        });
+
+      preservation.enable = true;
+      preservation.preserveAt."/persist/system" = {
+        directories = [
+          "/var/log"
+          "/var/lib/systemd/coredump"
+          "/var/lib/systemd/rfkill"
+          "/var/lib/systemd/timers"
+          {
+            directory = "/var/lib/nixos";
+            inInitrd = true;
+          }
+          # {
+          #   directory = "/var/lib/private";
+          #   mode = "0700";
+          # }
+        ]
+        ++ cfg.system.dirs;
+        files = [
+          {
+            file = "/var/lib/systemd/random-seed";
+            how = "symlink";
+            inInitrd = true;
+            configureParent = true;
+          }
+        ]
+        ++ cfg.system.files;
+      };
+    })
+  ];
+
+}
diff --git a/nixosModules/common/configure/primaryNetworkInterface.nix b/nixosModules/common/configure/primaryNetworkInterface.nix
@@ -0,0 +1,94 @@
+{
+  machineConfig,
+  config,
+  lib,
+  ...
+}:
+let
+  inherit (lib) types;
+  cfg = config.common.configure.primaryNetworkInterface;
+
+in
+{
+
+  options.common.configure.primaryNetworkInterface = {
+    enable = lib.mkEnableOption "configuration of the primary network interface";
+    interfaceName = lib.mkOption {
+      type = types.str;
+      default = machineConfig.networking.primaryInterface;
+    };
+    dnsServers = lib.mkOption {
+      type = with types; listOf str;
+      default = [
+        "1.1.1.1"
+        "8.8.8.8"
+        "9.9.9.9"
+      ];
+    };
+    useDHCP = lib.mkOption {
+      type = types.bool;
+      default = if (cfg.ip4Address != null) then false else true;
+    };
+    acceptRouterAdvertisements = lib.mkOption {
+      type = types.bool;
+      default = if (cfg.ip6Address != null) then false else true;
+    };
+    ip4Address = lib.mkOption {
+      type = with types; nullOr str;
+      default =
+        if (machineConfig.networking ? ip4Address && machineConfig.networking ? ip4PrefixLength) then
+          "${machineConfig.networking.ip4Address}/${toString machineConfig.networking.ip4PrefixLength}"
+        else
+          null;
+    };
+    ip4DefaultGateway = lib.mkOption {
+      type = with types; nullOr str;
+      default =
+        if machineConfig.networking ? defaultGateway4 then
+          machineConfig.networking.defaultGateway4
+        else
+          null;
+    };
+    ip6Address = lib.mkOption {
+      type = with types; nullOr str;
+      default =
+        if (machineConfig.networking ? ip6Address && machineConfig.networking ? ip6PrefixLength) then
+          "${machineConfig.networking.ip6Address}/${toString machineConfig.networking.ip6PrefixLength}"
+        else
+          null;
+    };
+    ip6DefaultGateway = lib.mkOption {
+      type = with types; nullOr str;
+      default =
+        if machineConfig.networking ? defaultGateway6 then
+          machineConfig.networking.defaultGateway6
+        else
+          null;
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    systemd.network = {
+      networks = {
+        "5-primaryInterface" = {
+          enable = true;
+          name = cfg.interfaceName;
+          networkConfig = {
+            DHCP = cfg.useDHCP;
+            IPv6AcceptRA = cfg.acceptRouterAdvertisements;
+          };
+          dns = cfg.dnsServers;
+          gateway = [
+            (lib.mkIf (cfg.ip4DefaultGateway != null) cfg.ip4DefaultGateway)
+            (lib.mkIf (cfg.ip6DefaultGateway != null) cfg.ip6DefaultGateway)
+          ];
+          address = [
+            (lib.mkIf (cfg.ip4Address != null) cfg.ip4Address)
+            (lib.mkIf (cfg.ip6Address != null) cfg.ip6Address)
+          ];
+        };
+      };
+    };
+  };
+
+}
diff --git a/nixosModules/common/configure/rootDisk.nix b/nixosModules/common/configure/rootDisk.nix
@@ -0,0 +1,185 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+let
+  inherit (lib) types;
+  cfg = config.common.configure.rootDisk;
+  users = lib.pipe config.common.users [
+    (lib.mapAttrsToList (name: value: if value.enable then name else null))
+    (lib.filter (element: !builtins.isNull element))
+  ];
+  partOpts = [
+    "compress=zstd"
+    "discard=async"
+  ];
+
+in
+{
+
+  options.common.configure.rootDisk = {
+    enable = lib.mkOption {
+      type = types.bool;
+      default = false;
+    };
+    subVolumes = {
+      nix = lib.mkOption {
+        type = types.bool;
+        default = true;
+      };
+      home = lib.mkOption {
+        type = types.bool;
+        default = false;
+      };
+      homePerUser = lib.mkOption {
+        type = types.bool;
+        default = cfg.subVolumes.home;
+      };
+      tmp = lib.mkOption {
+        type = types.bool;
+        default = false;
+      };
+      system = lib.mkOption {
+        type = types.bool;
+        default = false;
+      };
+    };
+    swap = {
+      enable = lib.mkOption {
+        type = types.bool;
+        default = false;
+      };
+      size = lib.mkOption {
+        type = types.strMatching "[0-9]+[KMGTP]";
+      };
+    };
+  };
+
+  config = lib.mkIf cfg.enable (
+    let
+      askPass = pkgs.writeShellScriptBin "cryptsetup-askpass" "systemctl default";
+    in
+    {
+      boot = {
+        supportedFilesystems = [ "btrfs" ];
+        initrd = {
+          luks.devices."root".device = "/dev/disk/by-partlabel/${config.networking.hostName}-root";
+
+          availableKernelModules = [
+            "btrfs"
+            "ipv6"
+          ];
+
+          systemd = {
+            enable = lib.mkDefault true;
+            network = lib.mkIf config.systemd.network.enable config.systemd.network;
+            storePaths = [ "${askPass}/bin/cryptsetup-askpass" ];
+            users.root.shell = "${askPass}/bin/cryptsetup-askpass";
+          };
+
+          network = {
+            enable = lib.mkDefault true;
+            ssh = {
+              enable = lib.mkDefault true;
+              port = 22;
+              hostKeys = lib.mkDefault (lib.map (element: element.path) config.services.openssh.hostKeys);
+              authorizedKeys =
+                with lib;
+                concatLists (
+                  mapAttrsToList (
+                    name: user: if elem "wheel" user.extraGroups then user.openssh.authorizedKeys.keys else [ ]
+                  ) config.users.users
+                );
+            };
+          };
+        };
+      };
+
+      services.btrfs.autoScrub.enable = true;
+
+      swapDevices = lib.singleton { device = lib.mkIf cfg.swap.enable "/swap/swapfile"; };
+
+      fileSystems = lib.mkMerge [
+        {
+          "/" = {
+            device = "/dev/mapper/root";
+            fsType = "btrfs";
+            options = partOpts;
+          };
+          "/boot" = {
+            device = "/dev/disk/by-partlabel/${config.networking.hostName}-boot";
+            fsType = "vfat";
+            options = [
+              "nofail"
+              "umask=0077"
+              "dmask=0077"
+            ];
+          };
+          "/tmp" = lib.mkIf cfg.subVolumes.tmp {
+            device = "/dev/mapper/root";
+            fsType = "btrfs";
+            options = [
+              "subvol=nix"
+              "nosuid"
+              "nodev"
+            ];
+          };
+          "/nix" = lib.mkIf cfg.subVolumes.nix {
+            device = "/dev/mapper/root";
+            fsType = "btrfs";
+            options = [
+              "subvol=nix"
+              "noatime"
+            ]
+            ++ partOpts;
+          };
+          "/persist/system" = lib.mkIf cfg.subVolumes.system {
+            device = "/dev/mapper/root";
+            fsType = "btrfs";
+            options = [
+              "subvol=system"
+              "noatime"
+            ]
+            ++ partOpts;
+          };
+          "/persist/home" = lib.mkIf (cfg.subVolumes.home && !cfg.subVolumes.homePerUser) {
+            device = "/dev/mapper/root";
+            fsType = "btrfs";
+            options = [
+              "subvol=home"
+            ]
+            ++ partOpts;
+          };
+          "/swap" = lib.mkIf cfg.swap.enable {
+            device = "/dev/mapper/root";
+            fsType = "btrfs";
+            options = [
+              "subvol=swap"
+              "noatime"
+              "discard=async"
+            ];
+          };
+        }
+        (lib.mkIf cfg.subVolumes.homePerUser (
+          lib.listToAttrs (
+            lib.map (user: {
+              name =
+                if config.common.configure.persist.home.enable then "/persist/home/${user}" else "/home/${user}";
+              value = {
+                device = "/dev/mapper/root";
+                fsType = "btrfs";
+                options = [
+                  "subvol=home-${user}"
+                ]
+                ++ partOpts;
+              };
+            }) users
+          )
+        ))
+      ];
+    }
+  );
+
+}
diff --git a/nixosModules/common/configure/sops.nix b/nixosModules/common/configure/sops.nix
@@ -0,0 +1,22 @@
+{
+  sopsSecrets,
+  npins,
+  config,
+  lib,
+  ...
+}:
+
+{
+
+  options.common.configure.sops.enable = lib.mkEnableOption "sops secrets";
+
+  imports =
+    (lib.mkIf config.common.configure.sops.enable [
+      "${npins.sopsNix}/modules/sops"
+    ]).content;
+
+  config = lib.mkIf config.common.configure.sops.enable {
+    sops.defaultSopsFile = sopsSecrets.${config.networking.hostName};
+  };
+
+}
diff --git a/nixosModules/common/hardware/audio.nix b/nixosModules/common/hardware/audio.nix
@@ -0,0 +1,21 @@
+{
+  lib,
+  config,
+  ...
+}:
+{
+
+  options.common.hardware.audio.enable = lib.mkEnableOption "basic pipewire config";
+
+  config = lib.mkIf config.common.hardware.audio.enable {
+    security.rtkit.enable = true;
+
+    services.pipewire = {
+      enable = true;
+      alsa.enable = true;
+      pulse.enable = true;
+      wireplumber.enable = true;
+    };
+  };
+
+}
diff --git a/nixosModules/common/hardware/bluetooth.nix b/nixosModules/common/hardware/bluetooth.nix
@@ -0,0 +1,22 @@
+{
+  config,
+  lib,
+  ...
+}:
+{
+
+  options.common.hardware.bluetooth.enable = lib.mkEnableOption "bluetooth foo";
+
+  config = lib.mkIf config.common.hardware.bluetooth.enable {
+    common.configure.persist.system.dirs = [
+      "/var/lib/bluetooth"
+    ];
+
+    hardware.bluetooth = {
+      enable = true;
+      powerOnBoot = lib.mkDefault false;
+      settings.General.Experimental = lib.mkDefault true;
+    };
+  };
+
+}
diff --git a/nixosModules/common/hardware/fprint.nix b/nixosModules/common/hardware/fprint.nix
@@ -0,0 +1,23 @@
+{
+  config,
+  lib,
+  ...
+}:
+let
+  inherit (lib) types;
+  cfg = config.common.hardware.fingerprint;
+
+in
+{
+
+  options.common.hardware.fingerprint.enable = lib.mkEnableOption "fingerprint sensor support";
+
+  config = lib.mkIf cfg.enable {
+    services.fprintd.enable = true;
+
+    common.configure.persist.system.dirs = [
+      "/var/lib/fprint"
+    ];
+  };
+
+}
diff --git a/nixosModules/common/hardware/intelGraphics.nix b/nixosModules/common/hardware/intelGraphics.nix
@@ -0,0 +1,24 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.common.hardware.intelGraphics.enable = lib.mkEnableOption "intel gpu drivers";
+
+  config = lib.mkIf config.common.hardware.intelGraphics.enable {
+    boot.initrd.kernelModules = [ "i915" ];
+
+    hardware.graphics = {
+      enable = true;
+      extraPackages = with pkgs; [
+        vpl-gpu-rt
+        intel-media-driver
+      ];
+    };
+  };
+
+}
diff --git a/nixosModules/common/hardware/quirks.nix b/nixosModules/common/hardware/quirks.nix
@@ -0,0 +1,23 @@
+{
+  config,
+  lib,
+  ...
+}:
+let
+  cfg = config.common.hardware.quirks;
+
+in
+{
+
+  options.common.hardware.quirks = {
+    thinkpad.enable = lib.mkEnableOption "thinkpad quirks";
+  };
+
+  config = lib.mkMerge [
+    (lib.mkIf cfg.thinkpad.enable {
+      boot.extraModprobeConfig = ''
+        options thinkpad_acpi fan_control=1
+      '';
+    })
+  ];
+}
diff --git a/nixosModules/common/hardware/smartcard.nix b/nixosModules/common/hardware/smartcard.nix
@@ -0,0 +1,19 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+{
+
+  options.common.hardware.smartcard.enable = lib.mkEnableOption "smartcard spport";
+
+  config = lib.mkIf config.common.hardware.smartcard.enable {
+    services = {
+      pcscd.enable = true;
+      udev.packages = with pkgs; [ libu2f-host ];
+      dbus.packages = with pkgs; [ gcr ];
+    };
+  };
+
+}
diff --git a/nixosModules/common/hardware/thunderbolt.nix b/nixosModules/common/hardware/thunderbolt.nix
@@ -0,0 +1,15 @@
+{
+  config,
+  lib,
+  ...
+}:
+{
+
+  options.common.hardware.thunderbolt.enable = lib.mkEnableOption "thundebolt support";
+
+  config = lib.mkIf config.common.hardware.thunderbolt.enable {
+    boot.initrd.availableKernelModules = [ "thunderbolt" ];
+    services.hardware.bolt.enable = true;
+  };
+
+}
diff --git a/nixosModules/common/profiles/amdCpu.nix b/nixosModules/common/profiles/amdCpu.nix
@@ -0,0 +1,23 @@
+{
+  config,
+  lib,
+  ...
+}:
+
+{
+
+  options.common.profiles.amdCpu.enable = lib.mkEnableOption "amd-cpu profile";
+
+  config = lib.mkIf config.common.profiles.amdCpu.enable {
+    hardware.cpu.amd.updateMicrocode = true;
+
+    boot = {
+      kernelParams = [ "amd_pstate=active" ];
+      kernelModules = [
+        "kvm-amd"
+        "amd-pstate"
+      ];
+    };
+  };
+
+}
diff --git a/nixosModules/common/profiles/base.nix b/nixosModules/common/profiles/base.nix
@@ -0,0 +1,103 @@
+{
+  pkgs,
+  lib,
+  config,
+  machineConfig,
+  ...
+}:
+
+{
+
+  options.common.profiles.base.enable = lib.mkEnableOption "base profile";
+
+  config = lib.mkIf config.common.profiles.base.enable {
+    boot.kernel.sysctl."kernel.sysrq" = lib.mkDefault 1;
+
+    # make things more declerative
+    services.userborn.enable = lib.mkDefault true;
+    #users.mutableUsers = lib.mkForce false;
+
+    networking = {
+      hostId = builtins.substring 0 8 (builtins.hashString "sha256" machineConfig.machineName);
+      hostName = machineConfig.machineName;
+      domain = lib.mkDefault machineConfig.domain;
+
+      useNetworkd = lib.mkDefault true;
+      useDHCP = lib.mkDefault false;
+
+      nftables.enable = lib.mkDefault true;
+      firewall.enable = lib.mkDefault true;
+    };
+
+    hardware.enableRedistributableFirmware = true;
+
+    common = {
+      profiles = {
+        amdCpu.enable = lib.mkDefault (machineConfig.hardware.cpuVendor == "amd");
+        intelCpu.enable = lib.mkDefault (machineConfig.hardware.cpuVendor == "intel");
+      };
+
+      configure = {
+        boot.enable = lib.mkDefault true;
+
+        locale.enable = lib.mkDefault true;
+        sops.enable = lib.mkDefault true;
+
+        nix.enable = true;
+      };
+
+      services = {
+        openssh.enable = true;
+      };
+
+      security = {
+        nix.enable = lib.mkDefault true;
+        kernel.enable = lib.mkDefault true;
+        networking.enable = lib.mkDefault true;
+      };
+
+      programs = {
+        shellUtilities.enable = lib.mkDefault true;
+        systemUtilities.enable = lib.mkDefault true;
+        networkUtilities.enable = lib.mkDefault true;
+
+        fish.enable = lib.mkDefault true;
+      };
+    };
+
+    programs = {
+      command-not-found.enable = false; # Not usable without channels; use nix-index instead.
+    };
+
+    services = {
+      dbus.implementation = "broker";
+    };
+
+    security.sudo.extraConfig = "Defaults lecture=\"never\""; # "We trust you have received the usual lecture from the local System Administrator."
+
+    system = {
+      stateVersion = lib.mkDefault "25.11";
+
+      # thanks piegames (https://git.darmstadt.ccc.de/piegames/home-config/-/blob/master/modules/generic.nix#L84)
+      activationScripts = {
+        diff = {
+          supportsDryActivation = true;
+          text = ''
+            ${pkgs.nvd}/bin/nvd --color=always --nix-bin-dir=${pkgs.nix}/bin diff "$(readlink /run/current-system)" "$systemConfig"
+            # Ignore "failures" because these tools have weird exit codes
+            ${pkgs.colordiff}/bin/colordiff --nobanner --fakeexitcode --color=always -ur -I '\/nix\/store' \
+              -- "$(readlink /run/current-system)/activate" "$systemConfig/activate" | ${pkgs.gnugrep}/bin/grep -v "^Binary files" || true
+            ${pkgs.colordiff}/bin/colordiff --nobanner --fakeexitcode --color=always -ur -I '\/nix\/store' \
+              -x "os-release" -x "issue" \
+              -- "$(readlink /run/current-system)/etc" "$systemConfig/etc" | ${pkgs.gnugrep}/bin/grep -v "^Binary files" || true
+            ${pkgs.colordiff}/bin/colordiff --nobanner --fakeexitcode --color=always -ur -I '\/nix\/store' \
+              -x "environment.d" \
+              -x "hwdb.d" \
+              -- "$(readlink /run/current-system)/systemd" "$systemConfig/systemd" | ${pkgs.gnugrep}/bin/grep -v "^Binary files" || true
+          '';
+        };
+      };
+    };
+  };
+
+}
diff --git a/nixosModules/common/profiles/intelCpu.nix b/nixosModules/common/profiles/intelCpu.nix
@@ -0,0 +1,17 @@
+{
+  config,
+  lib,
+  ...
+}:
+
+{
+
+  options.common.profiles.intelCpu.enable = lib.mkEnableOption "intel-cpu profile";
+
+  config = lib.mkIf config.common.profiles.intelCpu.enable {
+    boot.initrd.kernelModules = [ "kvm-intel" ];
+
+    hardware.cpu.intel.updateMicrocode = true;
+  };
+
+}
diff --git a/nixosModules/common/profiles/minimal.nix b/nixosModules/common/profiles/minimal.nix
@@ -0,0 +1,48 @@
+{
+  lib,
+  config,
+  ...
+}:
+
+{
+
+  options.common.profiles.minimal.enable = lib.mkEnableOption "minimal system profile";
+
+  config = lib.mkIf config.common.profiles.minimal.enable {
+    boot.enableContainers = lib.mkDefault false;
+
+    system.disableInstallerTools = lib.mkDefault true;
+
+    documentation = {
+      enable = lib.mkDefault false;
+      doc.enable = lib.mkDefault false;
+      info.enable = lib.mkDefault false;
+      man.enable = lib.mkDefault false;
+      nixos.enable = lib.mkDefault false;
+    };
+
+    environment = {
+      defaultPackages = lib.mkDefault [ ];
+      stub-ld.enable = lib.mkDefault false;
+    };
+
+    programs = {
+      less.lessopen = lib.mkDefault null;
+      command-not-found.enable = lib.mkDefault false;
+      fish.generateCompletions = lib.mkDefault false;
+    };
+
+    services = {
+      logrotate.enable = lib.mkDefault false;
+      udisks2.enable = lib.mkDefault false;
+    };
+
+    xdg = {
+      autostart.enable = lib.mkDefault false;
+      icons.enable = lib.mkDefault false;
+      mime.enable = lib.mkDefault false;
+      sounds.enable = lib.mkDefault false;
+    };
+  };
+
+}
diff --git a/nixosModules/common/profiles/netcup.nix b/nixosModules/common/profiles/netcup.nix
@@ -0,0 +1,34 @@
+{
+  config,
+  lib,
+  ...
+}:
+
+{
+
+  options.common.profiles.netcup.enable = lib.mkEnableOption "netcup vserver profile";
+
+  config = lib.mkIf config.common.profiles.netcup.enable {
+    boot = {
+      initrd.availableKernelModules = [
+        "ata_piix"
+        "uhci_hcd"
+        "virtio_pci"
+        "sr_mod"
+        "virtio_blk"
+      ];
+    };
+
+    common.configure.primaryNetworkInterface = {
+      interfaceName = "ens3";
+      ip6DefaultGateway = "fe80::1";
+      dnsServers = [
+        "46.38.225.230"
+        "46.38.252.230"
+        "2a03:4000:0:1::e1e6"
+        "2a03:4000:8000::fce6"
+      ];
+    };
+  };
+
+}
diff --git a/nixosModules/common/profiles/nvme.nix b/nixosModules/common/profiles/nvme.nix
@@ -0,0 +1,18 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.common.profiles.nvme.enable = lib.mkEnableOption "nvme profile";
+
+  config = lib.mkIf config.common.profiles.nvme.enable {
+    boot.initrd.availableKernelModules = [ "nvme" ];
+
+    environment.systemPackages = [ pkgs.nvme-cli ];
+  };
+
+}
diff --git a/nixosModules/common/programs/fish.nix b/nixosModules/common/programs/fish.nix
@@ -0,0 +1,17 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.common.programs.fish.enable = lib.mkEnableOption "fish shell";
+
+  config = lib.mkIf config.common.programs.fish.enable {
+    programs.fish.enable = true;
+    users.defaultUserShell = pkgs.fish;
+  };
+
+}
diff --git a/nixosModules/common/programs/networkUtilities.nix b/nixosModules/common/programs/networkUtilities.nix
@@ -0,0 +1,25 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.common.programs.networkUtilities.enable = lib.mkEnableOption "useful network-utilities";
+
+  config = lib.mkIf config.common.programs.networkUtilities.enable {
+    environment.systemPackages = with pkgs; [
+      dig
+      nmap
+      openssh
+      whois
+    ];
+
+    programs = {
+      mtr.enable = true;
+    };
+  };
+
+}
diff --git a/nixosModules/common/programs/niri.nix b/nixosModules/common/programs/niri.nix
@@ -0,0 +1,56 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+{
+
+  options.common.programs.niri.enable = lib.mkEnableOption "niri";
+  config = lib.mkIf config.common.programs.niri.enable {
+    programs.niri.enable = true;
+
+    systemd.user.services."niri" = {
+      # Niri ships with a `niri.service` unit, so the configuration here is
+      # actually a drop-in override. Environment variables here should be forced
+      # to be `{ }`, otherwise a default `PATH` for services is injected here,
+      # clearing all inherited paths and making the WM totally unuseable without
+      # `/run/current-system/sw/bin`, `q/etc/profiles/per-user/<username>/bin` and
+      # potentially other paths.
+      environment = lib.mkForce { };
+      bindsTo = [
+        "graphical-session.target"
+        "niri-session.target"
+      ];
+      before = [
+        "graphical-session.target"
+        "niri-session.target"
+      ];
+    };
+
+    systemd.user.targets."niri-session" = {
+      description = "Current Niri graphical user session";
+      documentation = [ "man:systemd.special(7)" ];
+      requires = [ "basic.target" ];
+      unitConfig.RefuseManualStart = true;
+      unitConfig.StopWhenUnneeded = true;
+    };
+
+    # `maid-activation.service` finishes before `graphical-session-pre.target`,
+    # so services dependent on a graphical environment won't run without another
+    # activation.
+    # See <https://github.com/viperML/nix-maid/issues/53>.
+    systemd.user.services."maid-graphical-session-activation" = {
+      wantedBy = [ "graphical-session.target" ];
+      after = [ "graphical-session.target" ];
+      serviceConfig.Type = "oneshot";
+      serviceConfig.ExecStart = "${lib.getExe' pkgs.systemd "systemctl"} --user restart maid-activation.service";
+      serviceConfig.RemainAfterExit = true;
+    };
+
+    # `nixos-fake-graphical-session.target` breaks some default systemd targets'
+    # semantics, such as `graphical-session.target`, when using most WMs.
+    # See <https://github.com/viperML/nix-maid/issues/56>
+    systemd.user.targets."nixos-fake-graphical-session".enable = false;
+  };
+}
diff --git a/nixosModules/common/programs/nixUtilities.nix b/nixosModules/common/programs/nixUtilities.nix
@@ -0,0 +1,23 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.common.programs.nixUtilities.enable = lib.mkEnableOption "useful nix utilities";
+
+  config = lib.mkIf config.common.programs.nixUtilities.enable {
+    environment.systemPackages = with pkgs; [
+      deadnix
+      nixfmt-rfc-style
+      nixfmt-tree
+      nixd
+      statix
+      nh
+    ];
+  };
+
+}
diff --git a/nixosModules/common/programs/shellUtilities.nix b/nixosModules/common/programs/shellUtilities.nix
@@ -0,0 +1,37 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.common.programs.shellUtilities.enable = lib.mkEnableOption "useful shell-utilities";
+
+  config = lib.mkIf config.common.programs.shellUtilities.enable {
+    environment = {
+      systemPackages = with pkgs; [
+        eza
+        micro
+        file
+        nmap
+        nvd
+        openssh
+        openssl
+        p7zip
+        unzip
+        progress
+        sops
+        rsync
+        zpha.nix-cleanup
+      ];
+    };
+
+    programs = {
+      bat.enable = true;
+      nano.enable = true;
+    };
+  };
+
+}
diff --git a/nixosModules/common/programs/systemUtilities.nix b/nixosModules/common/programs/systemUtilities.nix
@@ -0,0 +1,24 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.common.programs.systemUtilities.enable = lib.mkEnableOption "system utilities";
+
+  config = lib.mkIf config.common.programs.systemUtilities.enable {
+    environment.systemPackages = with pkgs; [
+      fastfetch
+      pciutils
+      usbutils
+    ];
+
+    programs = {
+      htop.enable = true;
+    };
+  };
+
+}
diff --git a/nixosModules/common/security/kernel.nix b/nixosModules/common/security/kernel.nix
@@ -0,0 +1,77 @@
+{
+  machineConfig,
+  config,
+  lib,
+  ...
+}:
+{
+
+  options.common.security.kernel.enable = lib.mkEnableOption "enhanced kernel security";
+
+  config = lib.mkIf config.common.security.kernel.enable {
+    environment = {
+      # memoryAllocator.provider = mkDefault "scudo"; # Breaks stuff
+      # variables.SCUDO_OPTIONS = mkDefault "ZeroContents=1"; # Breaks stuff
+    };
+
+    boot = {
+      blacklistedKernelModules = [
+        # Obscure network protocols
+        "ax25"
+        "netrom"
+        "rose"
+
+        # Old or rare or insufficiently audited filesystems
+        "adfs"
+        "affs"
+        "bfs"
+        "befs"
+        "cramfs"
+        "efs"
+        "erofs"
+        "exofs"
+        "freevxfs"
+        "f2fs"
+        "hfs"
+        "hpfs"
+        "jfs"
+        "minix"
+        "nilfs2"
+        "ntfs"
+        "omfs"
+        "qnx4"
+        "qnx6"
+        "sysv"
+        "ufs"
+      ];
+      kernel.sysctl = {
+        "kernel.yama.ptrace_scope" = lib.mkOverride 500 1;
+        "kernel.kptr_restrict" = lib.mkOverride 500 2;
+        "net.core.bpf_jit_enable" = lib.mkDefault false;
+        "kernel.ftrace_enabled" = lib.mkDefault false;
+      };
+      kernelParams = lib.mkMerge [
+        [
+          # Slab/slub sanity checks, redzoning, and poisoning
+          "slub_debug=FZP"
+
+          # Overwrite free'd memory
+          "page_poison=1"
+
+          # Enable page allocator randomization
+          "page_alloc.shuffle=1"
+        ]
+
+        # Disable hibernation (allows replacing the running kernel) unless requested
+        (lib.mkIf (!machineConfig.hardware.allowHibernation) [ "nohibernate" ])
+      ];
+    };
+
+    # Disable kernel module loading once the system is fully initialised.
+    # FIXME: Remove reverse dependencies
+    security.lockKernelModules = lib.mkDefault false;
+    # Prevent replacing the running kernel image w/o reboot
+    boot.kernel.sysctl."kernel.kexec_load_disabled" = lib.mkDefault true;
+  };
+
+}
diff --git a/nixosModules/common/security/networking.nix b/nixosModules/common/security/networking.nix
@@ -0,0 +1,38 @@
+{
+  config,
+  lib,
+  ...
+}:
+{
+
+  options.common.security.networking.enable = lib.mkEnableOption "enhanced networking security";
+
+  config = lib.mkIf config.common.security.networking.enable {
+    boot.kernel.sysctl = {
+      # Enable strict reverse path filtering (that is, do not attempt to route
+      # packets that "obviously" do not belong to the iface's network; dropped
+      # packets are logged as martians).
+      "net.ipv4.conf.all.log_martians" = lib.mkDefault true;
+      "net.ipv4.conf.all.rp_filter" = lib.mkDefault "1";
+      "net.ipv4.conf.default.log_martians" = lib.mkDefault true;
+      "net.ipv4.conf.default.rp_filter" = lib.mkDefault "1";
+
+      # Ignore broadcast ICMP (mitigate SMURF)
+      "net.ipv4.icmp_echo_ignore_broadcasts" = lib.mkDefault true;
+
+      # Ignore incoming ICMP redirects (note: default is needed to ensure that the
+      # setting is applied to interfaces added after the sysctls are set)
+      "net.ipv4.conf.all.accept_redirects" = lib.mkDefault false;
+      "net.ipv4.conf.all.secure_redirects" = lib.mkDefault false;
+      "net.ipv4.conf.default.accept_redirects" = lib.mkDefault false;
+      "net.ipv4.conf.default.secure_redirects" = lib.mkDefault false;
+      "net.ipv6.conf.all.accept_redirects" = lib.mkDefault false;
+      "net.ipv6.conf.default.accept_redirects" = lib.mkDefault false;
+
+      # Ignore outgoing ICMP redirects (this is ipv4 only)
+      "net.ipv4.conf.all.send_redirects" = lib.mkDefault false;
+      "net.ipv4.conf.default.send_redirects" = lib.mkDefault false;
+    };
+  };
+
+}
diff --git a/nixosModules/common/security/nix.nix b/nixosModules/common/security/nix.nix
@@ -0,0 +1,19 @@
+{
+  config,
+  lib,
+  ...
+}:
+{
+
+  options.common.security.nix.enable = lib.mkEnableOption "enhanced nix security";
+
+  config = lib.mkIf config.common.security.nix.enable {
+    users.groups.nix = { };
+
+    nix.settings.allowed-users = lib.mkForce [
+      "@users"
+      "@nix"
+    ];
+  };
+
+}
diff --git a/nixosModules/common/services/NetworkManager.nix b/nixosModules/common/services/NetworkManager.nix
@@ -0,0 +1,19 @@
+{
+  config,
+  lib,
+  ...
+}:
+{
+
+  options.common.services.NetworkManager.enable = lib.mkEnableOption "NetworkManager";
+
+  config = lib.mkIf config.common.services.NetworkManager.enable {
+    common.configure.persist.system.dirs = [
+      "/var/lib/NetworkManager"
+      "/etc/NetworkManager/system-connections"
+    ];
+
+    networking.networkmanager.enable = true;
+  };
+
+}
diff --git a/nixosModules/common/services/gitolite.nix b/nixosModules/common/services/gitolite.nix
@@ -0,0 +1,241 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+let
+  inherit (lib) types;
+  cfg = config.common.services.gitolite;
+
+in
+{
+
+  options.common.services.gitolite = {
+    enable = lib.mkEnableOption "gitolite git server";
+
+    dataDir = lib.mkOption {
+      type = types.str;
+      default = "/var/lib/gitolite";
+    };
+
+    adminPubkey = lib.mkOption {
+      type = types.str;
+    };
+
+    commonHooks = lib.mkOption {
+      type = types.attrsOf types.lines;
+      default = { };
+    };
+
+    triggers = lib.mkOption {
+      type = types.attrsOf types.lines;
+      default = { };
+    };
+
+    commands = lib.mkOption {
+      type = types.attrsOf types.lines;
+      default = { };
+    };
+
+    extraGitoliteRc = lib.mkOption {
+      type = types.lines;
+      default = "";
+    };
+
+    user = lib.mkOption {
+      type = types.str;
+      default = "gitolite";
+    };
+
+    group = lib.mkOption {
+      type = types.str;
+      default = "gitolite";
+    };
+  };
+
+  config = lib.mkIf cfg.enable (
+    let
+      # Use writeTextDir to not leak Nix store hash into file name
+      pubkeyFile = (pkgs.writeTextDir "gitolite-admin.pub" cfg.adminPubkey) + "/gitolite-admin.pub";
+      hooks = lib.mapAttrs (
+        name: script:
+        (pkgs.writeShellScript name (
+          if name == "post-receive" then
+            ''
+              read oldrev newrev ref
+              [ -t 0 ] || cat >/dev/null
+              [ -z "$GL_REPO" ] && die GL_REPO not set
+
+            ''
+            + script
+          else
+            script
+        ))
+      ) cfg.commonHooks;
+      triggers = lib.mapAttrs (name: script: (pkgs.writeShellScript name script)) cfg.triggers;
+      commands = lib.mapAttrs (name: script: (pkgs.writeShellScript name script)) cfg.commands;
+      manageGitoliteRc = cfg.extraGitoliteRc != "";
+      rcDir = pkgs.runCommand "gitolite-rc" {
+        preferLocalBuild = true;
+      } rcDirScript;
+      rcDirScript = ''
+        mkdir "$out"
+        export HOME=temp-home
+        mkdir -p "$HOME/.gitolite/logs" # gitolite can't run without it
+        '${pkgs.gitolite}'/bin/gitolite print-default-rc >>"$out/gitolite.rc.default"
+        cat <<END >>"$out/gitolite.rc"
+        # This file is managed by NixOS.
+        # Use services.gitolite options to control it.
+
+        END
+        cat "$out/gitolite.rc.default" >>"$out/gitolite.rc"
+      ''
+      + lib.optionalString (cfg.extraGitoliteRc != "") ''
+        echo -n ${lib.escapeShellArg ''
+
+          # Added by NixOS:
+          ${lib.removeSuffix "\n" cfg.extraGitoliteRc}
+
+          # per perl rules, this should be the last line in such a file:
+          1;
+        ''} >>"$out/gitolite.rc"
+      '';
+
+    in
+    {
+      common.configure.persist.system.dirs = [
+        {
+          directory = cfg.dataDir;
+          mode = "0755";
+          inherit (cfg) user;
+          inherit (cfg) group;
+        }
+      ];
+
+      common.services.gitolite.extraGitoliteRc = ''
+        $RC{LOCAL_CODE} = "$ENV{HOME}/.gitolite/local";
+      '';
+
+      users.users.${cfg.user} = {
+        home = cfg.dataDir;
+        uid = config.ids.uids.gitolite;
+        inherit (cfg) group;
+        extraGroups = [ "ssh" ];
+        useDefaultShell = true;
+        packages = [
+          pkgs.gitolite
+          pkgs.git
+          pkgs.perl
+        ];
+      };
+
+      users.groups.${cfg.group}.gid = config.ids.gids.gitolite;
+
+      systemd.services.gitolite-init = {
+        description = "Gitolite initialization";
+        wantedBy = [ "multi-user.target" ];
+        unitConfig.RequiresMountsFor = cfg.dataDir;
+
+        environment = {
+          GITOLITE_RC = ".gitolite.rc";
+          GITOLITE_RC_DEFAULT = "${rcDir}/gitolite.rc.default";
+        };
+
+        serviceConfig = lib.mkMerge [
+          (lib.mkIf (cfg.dataDir == "/var/lib/gitolite") {
+            StateDirectory = "gitolite gitolite/.gitolite gitolite/.gitolite/logs";
+            StateDirectoryMode = "0750";
+          })
+          {
+            Type = "oneshot";
+            User = cfg.user;
+            Group = cfg.group;
+            WorkingDirectory = "~";
+            RemainAfterExit = true;
+          }
+        ];
+
+        path = [
+          pkgs.gitolite
+          pkgs.git
+          pkgs.perl
+          pkgs.bash
+          pkgs.diffutils
+          config.programs.ssh.package
+        ];
+        script =
+          let
+            rcSetupScriptIfCustomFile =
+              if manageGitoliteRc then
+                ''
+                  cat <<END
+                  <3>ERROR: NixOS can't apply declarative configuration
+                  <3>to your .gitolite.rc file, because it seems to be
+                  <3>already customized manually.
+                  <3>See the services.gitolite.extraGitoliteRc option
+                  <3>in "man configuration.nix" for more information.
+                  END
+                  # Not sure if the line below addresses the issue directly or just
+                  # adds a delay, but without it our error message often doesn't
+                  # show up in `systemctl status gitolite-init`.
+                  journalctl --flush
+                  exit 1
+                ''
+              else
+                ''
+                  :
+                '';
+            rcSetupScriptIfDefaultFileOrStoreSymlink =
+              if manageGitoliteRc then
+                ''
+                  ln -sf "${rcDir}/gitolite.rc" "$GITOLITE_RC"
+                ''
+              else
+                ''
+                  [[ -L "$GITOLITE_RC" ]] && rm -f "$GITOLITE_RC"
+                '';
+          in
+          ''
+            if ( [[ ! -e "$GITOLITE_RC" ]] && [[ ! -L "$GITOLITE_RC" ]] ) ||
+               ( [[ -f "$GITOLITE_RC" ]] && diff -q "$GITOLITE_RC" "$GITOLITE_RC_DEFAULT" >/dev/null ) ||
+               ( [[ -L "$GITOLITE_RC" ]] && [[ "$(readlink "$GITOLITE_RC")" =~ ^/nix/store/ ]] )
+            then
+          ''
+          + rcSetupScriptIfDefaultFileOrStoreSymlink
+          + ''
+            else
+          ''
+          + rcSetupScriptIfCustomFile
+          + ''
+            fi
+
+            if [ ! -d repositories ]; then
+              gitolite setup -pk ${pubkeyFile}
+            fi
+
+            rm -rf   .gitolite/local/hooks/common/
+            mkdir -p .gitolite/local/hooks/common/
+            ${lib.concatStringsSep "\n" (
+              lib.mapAttrsToList (name: script: "ln -s ${script} .gitolite/local/hooks/common/${name}") hooks
+            )}
+
+            rm -rf .gitolite/local/triggers/
+            mkdir -p .gitolite/local/triggers/
+            ${lib.concatStringsSep "\n" (
+              lib.mapAttrsToList (name: script: "ln -s ${script} .gitolite/local/triggers/${name}") triggers
+            )}
+
+            rm -rf .gitolite/local/commands/
+            mkdir -p .gitolite/local/commands/
+            ${lib.concatStringsSep "\n" (
+              lib.mapAttrsToList (name: script: "ln -s ${script} .gitolite/local/commands/${name}") commands
+            )}
+
+            gitolite setup # Upgrade if needed
+          '';
+      };
+    }
+  );
+
+}
diff --git a/nixosModules/common/services/gotosocial.nix b/nixosModules/common/services/gotosocial.nix
@@ -0,0 +1,248 @@
+{
+  config,
+  pkgs,
+  lib,
+  ...
+}:
+let
+  inherit (lib) types;
+  cfg = config.common.services.gotosocial;
+
+  settingsFormat = pkgs.formats.yaml { };
+  defaultSettings = {
+    # Defaults
+    application-name = "gotosocial";
+    protocol = "https";
+
+    bind-address = "127.0.0.1";
+    port = 8080;
+
+    user = cfg.user;
+    group = cfg.group;
+
+    storage-local-base-path = cfg.stateDir;
+
+    db-type = "sqlite";
+    db-address = "${cfg.stateDir}/database.sqlite";
+  };
+
+in
+{
+
+  meta.maintainers = with lib.maintainers; [ zaphyra ];
+
+  options.common.services.gotosocial = {
+    enable = lib.mkEnableOption "ActivityPub social network server";
+
+    package = lib.mkPackageOption pkgs "gotosocial" { };
+
+    user = lib.mkOption {
+      type = types.str;
+      default = "gotosocial";
+      description = "The user gotosocial should run as";
+    };
+
+    group = lib.mkOption {
+      type = types.str;
+      default = "gotosocial";
+      description = "The group gotosocial should run as";
+    };
+
+    stateDir = lib.mkOption {
+      type = types.str;
+      default = "/var/lib/gotosocial";
+      readOnly = true;
+    };
+
+    setupPostgresqlDB = lib.mkOption {
+      type = lib.types.bool;
+      default = false;
+      description = ''
+        Whether to setup a local postgres database and populate the
+        `db-type` fields in `services.gotosocial.settings`.
+      '';
+    };
+
+    environmentFile = lib.mkOption {
+      type = with types; nullOr path;
+      default = null;
+      description = ''
+        File path containing environment variables for configuring the GoToSocial service
+        in the format of an EnvironmentFile as described by {manpage}`systemd.exec(5)`.
+
+        This option could be used to pass sensitive configuration to the GoToSocial daemon.
+
+        Please refer to the Environment Variables section in the [documentation](https://docs.gotosocial.org/en/latest/configuration/#environment-variables).
+      '';
+    };
+
+    settings = lib.mkOption {
+      default = defaultSettings;
+      description = ''
+        Contents of the GoToSocial YAML config.
+
+        Please refer to the
+        [documentation](https://docs.gotosocial.org/en/latest/configuration/)
+        and
+        [example config](https://github.com/superseriousbusiness/gotosocial/blob/main/example/config.yaml).
+
+        Please note that the `host` option cannot be changed later so it is important to configure this correctly before you start GoToSocial.
+      '';
+      type = types.submodule {
+        freeformType = settingsFormat.type;
+        options = {
+          host = lib.mkOption {
+            type = lib.types.nullOr lib.types.str;
+            default = null;
+            description = ''
+              Hostname that this server will be reachable at. Defaults to localhost for local testing,
+              but you should *definitely* change this when running for real, or your server won't work at all.
+              DO NOT change this after your server has already run once, or you will break things!
+            '';
+          };
+          port = lib.mkOption {
+            type = lib.types.port;
+            default = 8080;
+            description = ''
+              Int. Listen port for the GoToSocial webserver + API. If you're running behind a reverse proxy and/or in a docker,
+              container, just set this to whatever you like (or leave the default), and make sure it's forwarded properly.
+              If you are running with built-in letsencrypt enabled, and running GoToSocial directly on a host machine, you will
+              probably want to set this to 443 (standard https port), unless you have other services already using that port.
+              This *MUST NOT* be the same as the letsencrypt port specified below, unless letsencrypt is turned off.
+            '';
+          };
+        };
+      };
+    };
+  };
+
+  config = lib.mkIf cfg.enable (
+    let
+      configFile = settingsFormat.generate "gotosocial-config.yaml" cfg.settings;
+    in
+    {
+      assertions = [
+        {
+          assertion = cfg.settings.host or null != null;
+          message = "You have to define a hostname for GoToSocial (`common.services.gotosocial.settings.host`), it cannot be changed later without starting over!";
+        }
+      ];
+
+      common.services.gotosocial.settings =
+        (lib.mapAttrs (name: lib.mkDefault) defaultSettings)
+        // {
+          web-template-base-dir = "${cfg.package}/share/gotosocial/web/template/";
+          web-asset-base-dir = "${cfg.package}/share/gotosocal/web/assets/";
+        }
+        // (lib.optionalAttrs cfg.setupPostgresqlDB {
+          db-type = "postgres";
+          db-address = "/run/postgresql";
+          db-database = "gotosocial";
+          db-user = "gotosocial";
+        });
+
+      users = {
+        users."${cfg.user}" = {
+          home = cfg.stateDir;
+          group = cfg.group;
+          isSystemUser = true;
+        };
+        groups."${cfg.group}" = { };
+      };
+
+      environment.etc."gotosocial.yaml".source = configFile;
+
+      environment.systemPackages = [
+        (pkgs.writeShellScriptBin "gotosocial-admin" ''
+          exec systemd-run \
+            -u gotosocial-admin.service \
+            -p Group=${cfg.group} \
+            -p User=${cfg.user} \
+            -q -t -G --wait --service-type=exec \
+            ${lib.getExe cfg.package} --config-path ${configFile} admin "$@"
+        '')
+      ];
+
+      services.postgresql = lib.mkIf cfg.setupPostgresqlDB {
+        enable = true;
+        ensureDatabases = [ "gotosocial" ];
+        ensureUsers = [
+          {
+            name = "gotosocial";
+            ensureDBOwnership = true;
+          }
+        ];
+      };
+
+      systemd.services.gotosocial = {
+        description = "GoToSocial ActivityPub server";
+        restartTriggers = [ configFile ];
+
+        wantedBy = [ "multi-user.target" ];
+        requires = lib.optional cfg.setupPostgresqlDB "postgresql.service";
+        after = [
+          "network.target"
+          "sops-install-secrets.service"
+        ]
+        ++ lib.optional cfg.setupPostgresqlDB "postgresql.service";
+
+        environment = {
+          GTS_WAZERO_COMPILATION_CACHE = "${cfg.stateDir}/.cache";
+        };
+
+        serviceConfig = {
+          User = cfg.user;
+          Group = cfg.group;
+
+          Type = "exec";
+          WorkingDirectory = cfg.stateDir;
+          StateDirectory = lib.mkIf (
+            cfg.settings.storage-local-base-path != "/var/lib/gotosocial"
+          ) "gotosocial";
+          ReadOnlyPaths = [ cfg.package ];
+          ReadWritePaths = [ cfg.settings.storage-local-base-path ];
+          StateDirectoryMode = "750";
+
+          Restart = "on-failure";
+          RestartSec = 5;
+
+          EnvironmentFile = lib.mkIf (cfg.environmentFile != null) cfg.environmentFile;
+          ExecStart = "${lib.getExe cfg.package} --config-path ${configFile} server start";
+
+          # Security options:
+          # Based on https://github.com/superseriousbusiness/gotosocial/blob/v0.8.1/example/gotosocial.service
+          NoNewPrivileges = true;
+          PrivateTmp = true;
+          PrivateDevices = true;
+
+          RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6";
+          RestrictNamespaces = true;
+          RestrictRealtime = true;
+
+          DevicePolicy = "closed";
+          ProtectSystem = "full";
+          ProtectControlGroups = true;
+          ProtectKernelModules = true;
+          ProtectKernelTunables = true;
+          LockPersonality = true;
+
+          SystemCallFilter = "~@clock @debug @module @mount @obsolete @reboot @setuid @swap";
+
+          AmbientCapabilities = lib.optional (cfg.settings.port < 1024) "CAP_NET_BIND_SERVICE";
+          CapabilityBoundingSet = [
+            "~CAP_RAWIO CAP_MKNOD"
+            "~CAP_AUDIT_CONTROL CAP_AUDIT_READ CAP_AUDIT_WRITE"
+            "~CAP_SYS_BOOT CAP_SYS_TIME CAP_SYS_MODULE CAP_SYS_PACCT"
+            "~CAP_LEASE CAP_LINUX_IMMUTABLE CAP_IPC_LOCK"
+            "~CAP_BLOCK_SUSPEND CAP_WAKE_ALARM"
+            "~CAP_SYS_TTY_CONFIG"
+            "~CAP_MAC_ADMIN CAP_MAC_OVERRIDE"
+            "~CAP_NET_ADMIN CAP_NET_BROADCAST CAP_NET_RAW"
+            "~CAP_SYS_ADMIN CAP_SYS_PTRACE CAP_SYSLOG"
+          ];
+        };
+      };
+    }
+  );
+
+}
diff --git a/nixosModules/common/services/greetd.nix b/nixosModules/common/services/greetd.nix
@@ -0,0 +1,74 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+let
+  inherit (lib) types;
+  cfg = config.common.services.greetd;
+
+in
+{
+
+  options.common.services.greetd = {
+    enable = lib.mkEnableOption "greetd";
+    autoLogin = {
+      enable = lib.mkOption {
+        type = types.bool;
+        default = false;
+      };
+      user = lib.mkOption { type = types.str; };
+      command = lib.mkOption { type = types.str; };
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    security.pam.services.greetd.fprintAuth = false;
+
+    common.configure.persist.system.dirs = [
+      {
+        directory = "/var/cache/tuigreet";
+        mode = "755";
+        user = "greeter";
+        group = "greeter";
+      }
+    ];
+
+    systemd.tmpfiles.settings."greetd" = {
+      "/var/cache/tuigreet" = {
+        d = {
+          mode = "755";
+          user = "greeter";
+          group = "greeter";
+        };
+      };
+    };
+
+    services.greetd = {
+      enable = true;
+      restart = true;
+      useTextGreeter = true;
+      settings = {
+        initial_session = lib.mkIf cfg.autoLogin.enable { inherit (cfg.autoLogin) user command; };
+        default_session = {
+          user = "greeter";
+          command = lib.concatStringsSep " " [
+            (lib.getExe pkgs.tuigreet)
+            "--no-xsession-wrapper"
+            "--remember"
+            "--remember-session"
+            "--time"
+            "--user-menu"
+            "--asterisks"
+            "--power-shutdown"
+            "'systemctl poweroff'"
+            "--power-reboot"
+            "'systemctl reboot'"
+          ];
+        };
+      };
+    };
+  };
+
+}
diff --git a/nixosModules/common/services/gvfs.nix b/nixosModules/common/services/gvfs.nix
@@ -0,0 +1,56 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+let
+  inherit (lib) types;
+  cfg = config.common.services.gvfs;
+
+in
+{
+
+  options.common.services.gvfs = {
+    enable = lib.mkEnableOption "gvfs";
+    backends = {
+      afc = lib.mkOption {
+        type = types.bool;
+        default = false;
+      };
+      afp = lib.mkOption {
+        type = types.bool;
+        default = false;
+      };
+      mtp = lib.mkOption {
+        type = types.bool;
+        default = false;
+      };
+      gphoto2 = lib.mkOption {
+        type = types.bool;
+        default = false;
+      };
+      smb = lib.mkOption {
+        type = types.bool;
+        default = false;
+      };
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    services.gvfs = {
+      enable = true;
+      package =
+        (pkgs.gvfs.overrideAttrs (old: {
+          mesonFlags = (old.mesonFlags or [ ]) ++ [
+            "-Dafp=${lib.boolToString cfg.backends.afp}"
+            "-Dafc=${lib.boolToString cfg.backends.afc}"
+            "-Dmtp=${lib.boolToString cfg.backends.mtp}"
+            "-Dgphoto2=${lib.boolToString cfg.backends.gphoto2}"
+          ];
+        })).override
+          { samba = null; };
+    };
+  };
+
+}
diff --git a/nixosModules/common/services/mautrixBridge.nix b/nixosModules/common/services/mautrixBridge.nix
@@ -0,0 +1,150 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+let
+  inherit (lib) types;
+  cfg = config.common.services.mautrixBridge;
+  forEachInstance =
+    f: lib.flip lib.mapAttrs' cfg (name: cfg: lib.nameValuePair "mautrixBridge-${name}" (f name cfg));
+
+in
+{
+
+  # todo: add some documentation
+  options.common.services.mautrixBridge = lib.mkOption {
+    default = { };
+    type = types.attrsOf (
+      types.submodule {
+        options = {
+          enable = lib.mkOption {
+            type = types.bool;
+            default = false;
+          };
+          package = lib.mkOption {
+            type = types.package;
+          };
+          settings = lib.mkOption {
+            type = (pkgs.formats.json { }).type;
+            default = { };
+          };
+          environmentFile = lib.mkOption {
+            type = with types; nullOr path;
+            default = null;
+          };
+          serviceDependencies = lib.mkOption {
+            type = with types; listOf str;
+            default = [ ];
+          };
+        };
+      }
+    );
+  };
+
+  config = {
+    systemd.services = forEachInstance (
+      name: cfg:
+      let
+        dataDir = "/var/lib/mautrix-${name}";
+        registrationFile = "${dataDir}/registration.yaml";
+        settingsFile = "${dataDir}/config.yaml";
+        settingsFileUnsubstituted =
+          (pkgs.formats.json { }).generate "mautrix-${name}-config-unsubstituted.json"
+            cfg.settings;
+      in
+      {
+        enable = cfg.enable;
+        description = "mautrixBridge-${name}, a matrix puppeting bridge.";
+        restartTriggers = [ settingsFileUnsubstituted ];
+
+        wantedBy = [ "multi-user.target" ];
+        wants = [ "network-online.target" ] ++ cfg.serviceDependencies;
+        after = [ "network-online.target" ] ++ cfg.serviceDependencies;
+
+        path = [ pkgs.ffmpeg-headless ];
+
+        preStart = ''
+          # substitute the settings file by environment variables
+          # in this case read from EnvironmentFile
+          test -f '${settingsFile}' && rm -f '${settingsFile}'
+          old_umask=$(umask)
+          umask 0177
+          ${pkgs.envsubst}/bin/envsubst \
+            -o '${settingsFile}' \
+            -i '${settingsFileUnsubstituted}'
+          umask $old_umask
+
+          # generate the appservice's registration file if absent
+          if [ ! -f '${registrationFile}' ]; then
+            ${lib.getExe cfg.package} \
+              --generate-registration \
+              --config='${settingsFile}' \
+              --registration='${registrationFile}'
+          fi
+          chmod 640 ${registrationFile}
+
+          umask 0177
+          #todo: switch to `yq-go`
+          ${lib.getExe pkgs.yq} -s '
+              .[0].appservice.as_token = (.[0].appservice.as_token // .[1].as_token)
+            | .[0].appservice.hs_token = (.[0].appservice.hs_token // .[1].hs_token)
+          ${lib.optionalString (
+            name == "telegram"
+          ) "
+          | .[0].network.api_id = (.[0].network.api_id | tonumber)
+        "}
+            | .[0]' \
+            '${settingsFile}' '${registrationFile}' > '${settingsFile}.tmp'
+          mv '${settingsFile}.tmp' '${settingsFile}'
+          umask $old_umask
+        '';
+
+        serviceConfig = {
+          Type = "exec";
+
+          DynamicUser = true;
+          User = "mautrixBridge-${name}";
+          Group = "mautrixBridge-${name}";
+
+          EnvironmentFile = cfg.environmentFile;
+          StateDirectory = baseNameOf dataDir;
+          WorkingDirectory = dataDir;
+          UMask = 27;
+
+          ExecStart = "${lib.getExe cfg.package} --no-update --config='${settingsFile}'";
+
+          Restart = "on-failure";
+          RestartSec = "30s";
+
+          LockPersonality = true;
+          NoNewPrivileges = true;
+          MemoryDenyWriteExecute = lib.mkIf (name != "signal") true;
+
+          PrivateDevices = true;
+          PrivateTmp = true;
+          PrivateUsers = true;
+
+          ProtectSystem = "strict";
+          ProtectClock = true;
+          ProtectHome = true;
+          ProtectHostname = true;
+          ProtectKernelLogs = true;
+          ProtectKernelModules = true;
+          ProtectKernelTunables = true;
+          ProtectControlGroups = true;
+
+          RestrictRealtime = true;
+          RestrictSUIDSGID = true;
+
+          SystemCallArchitectures = "native";
+          SystemCallErrorNumber = "EPERM";
+          SystemCallFilter = [ "@system-service" ];
+        };
+      }
+    );
+  };
+
+}
diff --git a/nixosModules/common/services/openssh.nix b/nixosModules/common/services/openssh.nix
@@ -0,0 +1,133 @@
+{
+  config,
+  lib,
+  ...
+}:
+let
+  inherit (lib) types;
+  cfg = config.common.services.openssh;
+
+in
+{
+
+  options.common.services.openssh = {
+    enable = lib.mkEnableOption "openssh server";
+    enableRSASupport = lib.mkEnableOption "rsa support";
+    port = lib.mkOption {
+      type = types.port;
+      default = 22;
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    users.groups = {
+      ssh = {
+        gid = 200;
+      };
+      sftp = {
+        gid = 201;
+      };
+    };
+
+    # this is required because the secrets need to be decryped before the users get created
+    # but the impermanence bind-mounts get created _after_ the user creation...
+    sops.age.sshKeyPaths = [
+      (
+        if config.common.configure.persist.system.enable then
+          "/persist/system/var/lib/sshd/ssh_host_ed25519_key"
+        else
+          "/var/lib/sshd/ssh_host_ed25519_key"
+      )
+    ];
+
+    common.configure.persist.system.dirs = [ "/var/lib/sshd" ];
+
+    services.openssh = {
+      enable = true;
+
+      # Use socket activation via systemd
+      startWhenNeeded = true;
+
+      # Hostkeys
+      hostKeys = [
+        {
+          type = "ed25519";
+          path = "/persist/system/var/lib/sshd/ssh_host_ed25519_key";
+        }
+      ];
+
+      ports = [ cfg.port ];
+
+      # TODO: Find out why the heck this kills my gpg-agent
+      # extraConfig = "HostCertificate /run/secrets/hostcert";
+
+      settings = {
+
+        # Disable password authentication to enforce pubkey authentication
+        PasswordAuthentication = false;
+
+        # Disable keyboardinteractive authentication
+        KbdInteractiveAuthentication = false;
+
+        X11Forwarding = false;
+
+        # Only allow users of the ssh and sftp groups to connect
+        AllowGroups = [
+          "sftp"
+          "ssh"
+        ];
+
+        CASignatureAlgorithms = lib.concatStringsSep "," [
+          "ssh-ed25519"
+          "sk-ssh-ed25519@openssh.com"
+        ];
+
+        HostBasedAcceptedAlgorithms = lib.concatStringsSep "," [
+          "ssh-ed25519-cert-v01@openssh.com"
+          "sk-ssh-ed25519-cert-v01@openssh.com"
+          "ssh-ed25519"
+          "sk-ssh-ed25519@openssh.com"
+        ];
+
+        HostKeyAlgorithms = lib.concatStringsSep "," [
+          "ssh-ed25519-cert-v01@openssh.com"
+          "sk-ssh-ed25519-cert-v01@openssh.com"
+          "ssh-ed25519"
+          "sk-ssh-ed25519@openssh.com"
+        ];
+
+        PubKeyAcceptedAlgorithms = lib.concatStringsSep "," (
+          [
+            "ssh-ed25519-cert-v01@openssh.com"
+            "sk-ssh-ed25519-cert-v01@openssh.com"
+            "ssh-ed25519"
+            "sk-ssh-ed25519@openssh.com"
+          ]
+          ++ (lib.optionals cfg.enableRSASupport [ "rsa-sha2-512" ])
+        );
+
+        # Specifies the available KEX (Key Exchange) algorithms
+        KexAlgorithms = [
+          "mlkem768x25519-sha256"
+          "sntrup761x25519-sha512"
+          "sntrup761x25519-sha512@openssh.com"
+          "curve25519-sha256"
+          "curve25519-sha256@libssh.org"
+        ];
+
+        # Specifies the available MAC (message authentication code) algorithms
+        Macs = [
+          "hmac-sha2-512-etm@openssh.com"
+          "hmac-sha2-256-etm@openssh.com"
+        ];
+
+        Ciphers = [
+          "chacha20-poly1305@openssh.com"
+          "aes256-gcm@openssh.com"
+          "aes256-ctr"
+        ];
+      };
+    };
+  };
+
+}
diff --git a/nixosModules/common/services/power-profiles-daemon.nix b/nixosModules/common/services/power-profiles-daemon.nix
@@ -0,0 +1,22 @@
+{
+  config,
+  lib,
+  ...
+}:
+let
+  cfg = config.common.services.power-profiles-daemon;
+
+in
+{
+
+  options.common.services.power-profiles-daemon.enable = lib.mkEnableOption "power-profiles-daemon";
+
+  config = lib.mkIf cfg.enable {
+    common.configure.persist.system.dirs = [
+      "/var/lib/power-profiles-daemon"
+    ];
+
+    services.power-profiles-daemon.enable = true;
+  };
+
+}
diff --git a/nixosModules/common/services/prosody.nix b/nixosModules/common/services/prosody.nix
@@ -0,0 +1,148 @@
+{
+  config,
+  lib,
+  ...
+}:
+let
+  inherit (lib) types;
+  cfg = config.common.services.prosody;
+
+in
+{
+
+  options.common.services.prosody = {
+    enable = lib.mkEnableOption "prosody xmpp server with sane defaults";
+    domain = lib.mkOption {
+      type = types.str;
+    };
+    adminUsers = lib.mkOption {
+      type = with types; listOf str;
+      default = [ ];
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    dns.zones."${cfg.domain}" = {
+      SRV = [
+        {
+          proto = "tcp";
+          service = "xmpp-server";
+          priority = 0;
+          weight = 5;
+          port = 5269;
+          target = "xmpp.${cfg.domain}.";
+        }
+        {
+          proto = "tcp";
+          service = "xmpp-client";
+          priority = 0;
+          weight = 5;
+          port = 5222;
+          target = "xmpp.${cfg.domain}.";
+        }
+      ];
+      subdomains = {
+        "muc.xmpp".SRV = [
+          {
+            proto = "tcp";
+            service = "xmpp-server";
+            priority = 0;
+            weight = 5;
+            port = 5269;
+            target = "xmpp.${cfg.domain}.";
+          }
+        ];
+        "upload.xmpp".CNAME = [ "${config.networking.fqdn}." ];
+        xmpp.CNAME = [ "${config.networking.fqdn}." ];
+      };
+    };
+
+    networking.firewall.allowedTCPPorts = [
+      5269
+      5222
+    ]
+    ++ config.services.prosody.httpsPorts;
+
+    common.configure.persist.system.dirs = lib.singleton {
+      directory = config.services.prosody.dataDir;
+      mode = "755";
+      inherit (config.services.prosody) user;
+      inherit (config.services.prosody) group;
+    };
+
+    security.acme.certs."${cfg.domain}" = {
+      inherit (config.services.prosody) group;
+      reloadServices = [ "prosody.service" ];
+      extraDomainNames = [
+        "xmpp.${cfg.domain}"
+        "muc.xmpp.${cfg.domain}"
+        "upload.xmpp.${cfg.domain}"
+      ];
+    };
+
+    services.prosody = {
+      enable = true;
+
+      allowRegistration = false;
+      authentication = "internal_hashed";
+      admins = cfg.adminUsers;
+
+      # c2sRequireEncryption = true;
+      # s2sSecureAuth = true;
+
+      modules = {
+        disco = true; # Service discovery
+        roster = true; # Allow users to have a roster. Recommended ;)
+        saslauth = true; # Authentication for clients and servers. Recommended if you want to log in.
+        tls = true; # Add support for secure TLS on c2s/s2s connections
+        blocklist = true; # Allow users to block communications with other users
+        bookmarks = true; # Synchronise the list of open rooms between clients
+        carbons = true; # Keep multiple online clients in sync
+        dialback = true; # Support for verifying remote servers using DNS
+        limits = true; # Enable bandwidth limiting for XMPP connections
+        pep = true; # Allow users to store public and private data in their account
+        private = true; # Private XML storage (for room bookmarks, etc.)
+        smacks = true; # Allow a client to resume a disconnected session, and prevent message loss
+        vcard = true; # User profiles (stored in PEP)
+        vcard_legacy = true; # Conversion between legacy vCard and PEP Avatar, vcard
+        csi = true; # Allows clients to report their active/inactive state
+        ping = true; # Replies to XMPP pings with pongs
+        register = true; # Allow users to register on this server using a client and change passwords
+        time = true; # Let others know the time here on this server
+        uptime = true; # Report how long server has been running
+        version = true; # Replies to server version requests
+        mam = true; # Store recent messages to allow multi-device synchronization
+      };
+
+      extraModules = [
+        "admin_adhoc" # Allows administration via an XMPP client that supports ad-hoc commands
+        "admin_shell" # Allow secure administration via 'prosodyctl shell'
+        "invites" # Create and manage invites
+        "invites_adhoc" # Allow admins/users to create invitations via their client
+        "invites_register" # Allows invited users to create accounts
+      ];
+
+      httpsPorts = [ 5281 ];
+      httpFileShare.domain = "upload.xmpp.${cfg.domain}";
+
+      ssl.cert = "${config.security.acme.certs."${cfg.domain}".directory}/fullchain.pem";
+      ssl.key = "${config.security.acme.certs."${cfg.domain}".directory}/key.pem";
+
+      virtualHosts.zaphyra = {
+        enabled = true;
+        domain = cfg.domain;
+        ssl.cert = "${config.security.acme.certs."${cfg.domain}".directory}/fullchain.pem";
+        ssl.key = "${config.security.acme.certs."${cfg.domain}".directory}/key.pem";
+      };
+
+      muc = [
+        {
+          domain = "muc.xmpp.${cfg.domain}";
+          name = "zaphyra Chat";
+          restrictRoomCreation = "local";
+        }
+      ];
+    };
+  };
+
+}
diff --git a/nixosModules/common/services/resticBackup.nix b/nixosModules/common/services/resticBackup.nix
@@ -0,0 +1,178 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+let
+  inherit (lib) types;
+  cfg = config.common.services.resticBackup;
+
+in
+{
+
+  options.common.services.resticBackup = lib.mkOption {
+    default = { };
+    type = types.attrsOf (
+      types.submodule {
+        options = {
+          enable = lib.mkOption {
+            type = types.bool;
+            default = false;
+          };
+          user = lib.mkOption {
+            type = types.str;
+            default = "root";
+          };
+          environmentFile = lib.mkOption {
+            type = with types; nullOr str;
+            default = null;
+          };
+          passwordFile = lib.mkOption {
+            type = types.path;
+          };
+          targets = lib.mkOption {
+            type = with types; listOf str;
+          };
+          sshKeyFile = lib.mkOption {
+            type = types.path;
+          };
+          timerConfig = lib.mkOption {
+            type = types.attrs;
+            default = {
+              OnCalendar = "daily";
+              RandomizedDelaySec = 1200;
+            };
+          };
+          paths = lib.mkOption {
+            type = with types; listOf str;
+            default = [ ];
+          };
+          runBeforeBackup = lib.mkOption {
+            type = types.str;
+            default = "";
+          };
+          postgresDatabases = lib.mkOption {
+            type = with types; listOf str;
+            default = [ ];
+          };
+          sqliteDatabases = lib.mkOption {
+            type = with types; listOf str;
+            default = [ ];
+          };
+          influxBuckets = lib.mkOption {
+            type = with types; listOf str;
+            default = [ ];
+          };
+          exclude = lib.mkOption {
+            type = with types; listOf str;
+            default = [ ];
+          };
+        };
+      }
+    );
+  };
+
+  config = lib.mkIf (cfg != { }) {
+    systemd.services = lib.pipe cfg [
+      lib.attrsToList
+      (lib.map (
+        element:
+        (
+          if element.value.enable then
+            (lib.map (
+              target:
+              lib.nameValuePair "restic-backups-${element.name}-${target}" {
+                serviceConfig = {
+                  LoadCredential = [
+                    "sshPrivateKey:${element.value.sshKeyFile}"
+                    "repositoryPassword:${element.value.passwordFile}"
+                  ];
+                  EnvironmentFile = lib.optionals (element.value.environmentFile != null) [
+                    element.value.environmentFile
+                  ];
+                };
+              }
+            ) element.value.targets)
+          else
+            [ ]
+        )
+      ))
+      lib.flatten
+      lib.listToAttrs
+    ];
+
+    services.restic.backups = lib.pipe cfg [
+      lib.attrsToList
+      (lib.map (
+        element:
+        (
+          if element.value.enable then
+            (lib.map (
+              target:
+              lib.nameValuePair "${element.name}-${target}" {
+                initialize = true;
+                inherit (element.value) user timerConfig exclude;
+                passwordFile = "%d/repositoryPassword";
+                extraOptions = [
+                  "rclone.program=\"ssh -o StrictHostKeyChecking=no -i \${CREDENTIALS_DIRECTORY}/sshPrivateKey restic@${target}\""
+                ];
+                repository = "rclone:${element.name}";
+                paths = lib.mkMerge [
+                  element.value.paths
+                  (lib.mkIf (element.value.postgresDatabases != [ ]) (
+                    lib.map (element: "/tmp/postgresDatabases/${element}.sql.zst") element.value.postgresDatabases
+                  ))
+                  (lib.mkIf (element.value.sqliteDatabases != [ ]) (
+                    lib.map (
+                      element: "/tmp/sqliteDatabases/${builtins.baseNameOf element}.sqlite"
+                    ) element.value.sqliteDatabases
+                  ))
+                  (lib.mkIf (element.value.influxBuckets != [ ]) (
+                    lib.map (element: "/tmp/influxBuckets/${element}") element.value.influxBuckets
+                  ))
+                ];
+                backupPrepareCommand =
+                  element.value.runBeforeBackup
+                  +
+                    #dump postgresql databases
+                    (lib.optionalString (element.value.postgresDatabases != [ ]) (
+                      lib.concatMapStringsSep "\n" (db: ''
+                        echo "Dumping Postgres-database: ${db}"
+                        mkdir -p /tmp/postgresDatabases
+                        ${lib.getExe' pkgs.postgresql "pg_dump"} ${db} | ${lib.getExe pkgs.zstd} --rsyncable > /tmp/postgresDatabases/${db}.sql.zst
+                        [ $(du -b /tmp/postgresDatabases/${db}.sql.zst | cut -f1) -gt "50" ] || exit 1
+                      '') element.value.postgresDatabases
+                    ))
+                  +
+                    #dump sqlite databases
+                    (lib.optionalString (element.value.sqliteDatabases != [ ]) (
+                      lib.concatMapStringsSep "\n" (db: ''
+                        echo "Dumping sqlite-database: ${db}"
+                        mkdir -p /tmp/sqliteDatabases
+                        ${lib.getExe pkgs.sqlite} ${db} ".backup '/tmp/sqliteDatabases/${builtins.baseNameOf db}.sqlite'"
+                        [ $(du -b /tmp/sqliteDatabases/${builtins.baseNameOf db}.sqlite | cut -f1) -gt "50" ] || exit 1
+                      '') element.value.sqliteDatabases
+                    ))
+                  +
+                    #dump influx buckets
+                    (lib.optionalString (element.value.influxBuckets != [ ]) (
+                      lib.concatMapStringsSep "\n" (db: ''
+                        echo "Dumping influx-bucket: ${db}"
+                        mkdir -p /tmp/influxBuckets
+                        ${pkgs.influxdb2}/bin/influx backup --compression=none --bucket=${db} /tmp/influxBuckets/${db}
+                        [ $(du -b /tmp/influxBuckets/${db} | cut -f1) -gt "50" ] || exit 1
+                      '') element.value.influxBuckets
+                    ));
+              }
+            ) element.value.targets)
+          else
+            [ ]
+        )
+      ))
+      lib.flatten
+      lib.listToAttrs
+    ];
+  };
+
+}
diff --git a/nixosModules/common/services/upower.nix b/nixosModules/common/services/upower.nix
@@ -0,0 +1,20 @@
+{
+  config,
+  lib,
+  ...
+}:
+let
+  cfg = config.common.services.upower;
+
+in
+{
+
+  options.common.services.upower.enable = lib.mkEnableOption "upower";
+
+  config = lib.mkIf cfg.enable {
+    services.upower.enable = true;
+
+    common.configure.persist.system.dirs = [ "/var/lib/upower" ];
+  };
+
+}
diff --git a/nixosModules/common/services/vnstat.nix b/nixosModules/common/services/vnstat.nix
@@ -0,0 +1,155 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+let
+  inherit (lib) types;
+  cfg = config.common.services.vnstat;
+
+in
+{
+
+  options.common.services.vnstat = {
+    enable = lib.mkEnableOption "vnstat";
+    serveGraphs = lib.mkOption {
+      type = types.bool;
+      default = true;
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    services.vnstat.enable = true;
+
+    common.configure.persist.system.dirs = [ "/var/lib/vnstat" ];
+
+    services.nginx.virtualHosts."${config.networking.fqdn}" = lib.mkIf cfg.serveGraphs {
+      locations."/vnstat/" = {
+        alias = "/var/run/vnstati/";
+        index = "index.html";
+      };
+    };
+
+    systemd.services.vnstati = lib.mkIf cfg.serveGraphs {
+      wantedBy = [ "multi-user.target" ];
+      after = [ "vnstat.service" ];
+      startAt = "*-*-* *:0/10:00";
+
+      serviceConfig = {
+        User = "vnstatd";
+        Group = "vnstatd";
+        RuntimeDirectoryPreserve = true;
+        RuntimeDirectory = "vnstati";
+        RuntimeDirectoryMode = 775;
+        PrivateTmp = true;
+        ProtectHome = true;
+        ProtectSystem = "strict";
+      };
+
+      path = with pkgs; [
+        vnstat
+        jq
+        nix
+      ];
+      script = ''
+        set -x
+        ifaces=$(vnstat --json | jq -r .interfaces[].name | grep -v "^lo$")
+        echo $ifaces
+
+        cat > /var/run/vnstati/index.html << EOF
+        <!DOCTYPE html>
+        <html lang="en">
+          <head>
+            <title>${config.networking.hostName} - stats</title>
+            <meta charset="utf-8">
+            <meta name="robots" content="none">
+            <link rel="icon" href="static/favicon.ico" type="image/x-icon">
+            <style>
+        body {
+            padding: 0;
+            margin: 0;
+            background: #eee;
+            font-family: monospace;
+            min-height: 100vh;
+        }
+        img:not(:last-child) {
+            margin-bottom: 15px;
+        }
+        img {
+            flex-shrink: 0;
+        }
+        .ui.segment {
+            margin: 0 1em 10px 1em;
+            padding: 1em;
+            background-color: white;
+            box-shadow: 0 1px 2px 0 rgba(34,36,38,.15);
+            border-radius: .286rem;
+            border: 1px solid rgba(34,36,38,.15);
+            display: flex;
+            flex-direction: column;
+            align-items: center;
+            justify-content: center;
+
+        }
+        div.heading {
+            min-width: 100vw;
+            height: 70px;
+            display: flex;
+            flex-direction: column;
+            align-items: center;
+            justify-content: center;
+        }
+        div.table.vertical {
+            overflow-y: auto;
+            min-width: 100vw;
+            height: calc(100vh - 70px);
+            display: flex;
+            flex-direction: row;
+        }
+        div.iface {
+            display: flex;
+            flex-direction: column;
+            align-items: center;
+            flex-grow: 1;
+        }
+            </style>
+          </head>
+          <body>
+            <div class="heading">
+              <h1>${config.networking.hostName}</h1>
+            </div>
+            <div class="table vertical">            
+        EOF
+
+        for iface in $ifaces
+        do
+        cat >> /var/run/vnstati/index.html << EOF
+          <div class="iface">
+            <h3>''${iface}</h3>
+              <div class="ui segment"><img src="''${iface}-summary.png" alt="Summary"></div>
+              <div class="ui segment"><img src="''${iface}-hourly.png" alt="Hourly"></div>
+              <div class="ui segment"><img src="''${iface}-daily.png" alt="Daily"></div>
+              <div class="ui segment"><img src="''${iface}-monthly.png" alt="Monthly"></div>
+              <div class="ui segment"><img src="''${iface}-top10.png" alt="Top 10"></div>
+          </div>
+        EOF
+
+          vnstati -s -nh -i $iface -o /var/run/vnstati/$iface-summary.png
+          vnstati -h -nh -i $iface -o /var/run/vnstati/$iface-hourly.png
+          vnstati -d -nh -i $iface -o /var/run/vnstati/$iface-daily.png
+          vnstati -m -nh -i $iface -o /var/run/vnstati/$iface-monthly.png
+          vnstati -t -nh -i $iface -o /var/run/vnstati/$iface-top10.png
+        done
+
+        cat >> /var/run/vnstati/index.html << EOF
+            </div>
+          </body>
+        </html>
+        EOF
+
+      '';
+    };
+  };
+
+}
diff --git a/nixosModules/common/users/zaphyra.nix b/nixosModules/common/users/zaphyra.nix
@@ -0,0 +1,36 @@
+{
+  sopsSecrets,
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.common.users.zaphyra.enable = lib.mkEnableOption "zaphyra user";
+
+  config = lib.mkIf config.common.users.zaphyra.enable {
+    environment.systemPackages = [ pkgs.ghostty.terminfo ];
+
+    sops.secrets.zaphyraPassword = {
+      neededForUsers = true;
+      sopsFile = sopsSecrets.common;
+    };
+
+    users.users.zaphyra = {
+      uid = 1001;
+      description = "Katja";
+      hashedPasswordFile = config.sops.secrets.zaphyraPassword.path;
+      isNormalUser = true;
+      extraGroups = [
+        "ssh"
+        "wheel"
+      ];
+      openssh.authorizedKeys.keys = [
+        (builtins.readFile "${pkgs.zpha.website}/ssh_pubkey.asc")
+      ];
+    };
+  };
+
+}
diff --git a/nixosModules/default.nix b/nixosModules/default.nix
@@ -0,0 +1,24 @@
+{
+  lib ? import "${(import ../npins).nixpkgs}/lib",
+}:
+
+let
+  # read the current directorys files and pipe the result through a list of functions
+  modules = builtins.foldl' (x: f: f x) (builtins.readDir ./.) [
+    # convert to a list containing just the attribute names
+    (builtins.attrNames)
+    # drop "default.nix" from the list
+    (builtins.filter (name: name != "default.nix"))
+    (builtins.map (name: {
+      inherit name;
+      value = lib.filesystem.listFilesRecursive ./${name};
+    }))
+    # convert the resulting list to an attribute set
+    (builtins.listToAttrs)
+  ];
+
+in
+modules
+// {
+  default = lib.flatten (builtins.attrValues modules);
+}
diff --git a/nixosModules/dns/dns.nix b/nixosModules/dns/dns.nix
@@ -0,0 +1,53 @@
+{
+  nixosConfigurations,
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+let
+  inherit (lib) types;
+
+in
+{
+
+  options.dns = {
+    enable = lib.mkEnableOption "dns zones via nix";
+
+    # contains dns entries defined on the local host
+    zones = lib.mkOption {
+      type = types.attrsOf pkgs.dnsNix.types.subzone;
+      default = { };
+    };
+
+    # contains dns entries defined on the local host and on remote hosts, merged together
+    allZones = lib.mkOption {
+      type = types.attrsOf pkgs.dnsNix.types.zone;
+      default = { };
+    };
+
+    zoneFiles = lib.mkOption {
+      type = types.attrsOf types.path;
+      readOnly = true;
+      default = lib.mapAttrs (
+        name: zone:
+        toString (
+          pkgs.writeTextFile {
+            name = "${name}.zone";
+            text = pkgs.dnsNix.types.zoneToString name (pkgs.dnsNix.evalZone name zone);
+          }
+        )
+      ) config.dns.allZones;
+    };
+  };
+
+  config = lib.mkIf config.dns.enable {
+    # serve records defined in all host configs
+    dns.allZones = lib.mkMerge (
+      lib.flip lib.mapAttrsToList nixosConfigurations (
+        _hostName: machineConfig: machineConfig.config.dns.zones
+      )
+    );
+  };
+
+}
diff --git a/nixosModules/toBeUpstreamed/gomuks.nix b/nixosModules/toBeUpstreamed/gomuks.nix
@@ -0,0 +1,199 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+let
+  inherit (lib) types;
+
+  settingsFormat = pkgs.formats.yaml { };
+
+in
+{
+
+  options.services.gomuks = lib.mkOption {
+    description = "Configuration for gomuks instances.";
+    default = { };
+    type = types.attrsOf (
+      types.submodule {
+        options = {
+          enable = (lib.mkEnableOption "gomuks, a Matrix client written in Go.") // {
+            default = true;
+          };
+
+          package = lib.mkPackageOption pkgs "gomuks" { };
+
+          settings = lib.mkOption {
+            default = { };
+            description = ''
+              Contents of the gomuks YAML config.
+            '';
+            type = types.submodule {
+              freeformType = settingsFormat.type;
+              options = {
+                web = {
+                  listen_address = lib.mkOption {
+                    type = types.str;
+                    default = "localhost:29325";
+                    description = ''
+                      The address and port that gomuks will listen on.
+                    '';
+                  };
+
+                  username = lib.mkOption {
+                    type = types.str;
+                    description = ''
+                      Username for basic http authentication.
+                    '';
+                  };
+
+                  password_hash = lib.mkOption {
+                    type = types.str;
+                    description = ''
+                      `bcrypt`ed password for basic http authentication.
+                    '';
+                  };
+
+                  DisableAuthBecauseIWantMyAccountToBeHacked = lib.mkOption {
+                    type = with types; nullOr bool;
+                    visible = false;
+                    default = null;
+                  };
+                };
+
+                logging.writers = lib.mkOption {
+                  type = with types; listOf attrs;
+                  visible = false;
+                  default = [
+                    {
+                      type = "stdout";
+                      format = "pretty-colored";
+                    }
+                  ];
+                };
+              };
+            };
+          };
+        };
+      }
+    );
+  };
+
+  config = {
+    assertions = lib.flatten (
+      lib.flip lib.mapAttrsToList config.services.gomuks (
+        name: cfg: [
+          {
+            assertion = (
+              (cfg.settings.web.DisableAuthBecauseIWantMyAccountToBeHacked != true)
+              && ((cfg.settings.web ? username) || (cfg.settings.web ? password_hash))
+            );
+            message = ''
+              You need to set at least `services.gomuks.${name}.settings.web.username` and
+              `services.gomuks.${name}.settings.web.password_hash` in the settings.
+            '';
+          }
+        ]
+      )
+    );
+
+    systemd.services = lib.flip lib.mapAttrs' config.services.gomuks (
+      name: cfg:
+      (lib.nameValuePair "gomuks-${name}" (
+        let
+          settingsFile = settingsFormat.generate "gomuks-${name}.yaml" (
+            lib.filterAttrsRecursive (name: value: value != null) cfg.settings
+          );
+
+        in
+        {
+          enable = cfg.enable;
+          description = "A Matrix client written in Go";
+
+          restartTriggers = [ settingsFile ];
+
+          wantedBy = [ "multi-user.target" ];
+
+          environment = {
+            HOME = lib.mkDefault "%S/%N";
+            GOMUKS_CONFIG_HOME = lib.mkDefault "%S/%N";
+            GOMUKS_DATA_HOME = lib.mkDefault "%S/%N";
+            GOMUKS_LOGS_HOME = lib.mkDefault "%S/%N";
+            GOMUKS_CACHE_HOME = lib.mkDefault "%C/%N";
+            GOMUKS_TMPDIR = lib.mkDefault "%C/%N";
+          };
+
+          preStart = ''
+            test -f "$GOMUKS_CONFIG_HOME/config-nixos.yaml" && rm -f "$GOMUKS_CONFIG_HOME/config-nixos.yaml"
+
+            ${lib.getExe pkgs.systemd-credsubst} \
+              --copy-if-no-creds \
+              --input "${settingsFile}" \
+              --output "$GOMUKS_CONFIG_HOME/config-nixos.yaml"
+
+            if [ -f "$GOMUKS_CONFIG_HOME/config.yaml" ];
+            then
+              ${lib.getExe pkgs.yq-go} -i ". *= load(\"$GOMUKS_CONFIG_HOME/config-nixos.yaml\")" "$GOMUKS_CONFIG_HOME/config.yaml"
+            else
+              cp "$GOMUKS_CONFIG_HOME/config-nixos.yaml" "$GOMUKS_CONFIG_HOME/config.yaml"
+            fi
+          '';
+
+          serviceConfig = {
+            DynamicUser = true;
+
+            StateDirectory = "gomuks-${name}";
+            CacheDirectory = "gomuks-${name}";
+
+            ExecStart = lib.getExe cfg.package;
+
+            ProtectSystem = "full";
+            ProtectControlGroups = true;
+            ProtectKernelModules = true;
+            ProtectKernelTunables = true;
+            LockPersonality = true;
+
+            DevicePolicy = "strict";
+            DeviceAllow = [ "/dev/stdin r" ];
+
+            SystemCallArchitectures = "native";
+            MemoryDenyWriteExecute = true;
+
+            NoNewPrivileges = true;
+            PrivateTmp = true;
+            PrivateDevices = true;
+            UMask = "077";
+
+            RestrictAddressFamilies = "AF_INET AF_INET6";
+            RestrictNamespaces = true;
+            RestrictRealtime = true;
+            RestrictSUIDSGID = true;
+
+            RemoveIPC = true;
+            PrivateIPC = true;
+
+            SystemCallFilter = [
+              "@system-service"
+              "~@resources"
+              "~@mount"
+              "~@setuid"
+              "~@cpu-emulation"
+              "~@debug"
+              "~@keyring"
+              "~@obsolete"
+              "~@pkey"
+              "setrlimit"
+            ];
+          };
+        }
+      ))
+    );
+  };
+
+  meta.maintainers = with lib.maintainers; [
+    zaphyra
+  ];
+
+}
diff --git a/nixosModules/zpha/configure/cccdaWifi.nix b/nixosModules/zpha/configure/cccdaWifi.nix
@@ -0,0 +1,43 @@
+{
+  lib,
+  config,
+  pkgs,
+  ...
+}:
+{
+
+  options.zpha.configure.cccdaWifi.enable =
+    lib.mkEnableOption "configure cccda wifi NetworkManager profile";
+
+  config = lib.mkIf config.zpha.configure.cccdaWifi.enable {
+    networking.networkmanager.ensureProfiles = {
+      profiles.ccc-da-wifi = {
+        connection = {
+          id = "darmstadt.ccc.de";
+          type = "wifi";
+          uuid = "945c40f1-a800-4619-8276-1002a718a9f2";
+          autoconnect = "true";
+        };
+        "wifi" = {
+          ssid = "darmstadt.ccc.de";
+          mode = "infrastructure";
+        };
+        "wifi-security" = {
+          "auth-alg" = "open";
+          "key-mgmt" = "wpa-eap";
+        };
+        "802-1x" = {
+          "eap" = "ttls";
+          "phase2-auth" = "pap";
+          "ca-cert" = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
+          "altsubject-matches" = "DNS:radius.cccda.de";
+          "identity" = config.networking.fqdn;
+          "password" = config.networking.fqdn + (toString config.networking.hostId);
+        };
+        ipv4.method = "auto";
+        ipv6.method = "auto";
+      };
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/configure/dn42Router.nix b/nixosModules/zpha/configure/dn42Router.nix
@@ -0,0 +1,208 @@
+{
+  machineConfig,
+  config,
+  lib,
+  ...
+}:
+
+{
+
+  options.zpha.configure.dn42Router.enable = lib.mkEnableOption "";
+
+  config = lib.mkIf config.zpha.configure.dn42Router.enable {
+    dns.zones."zaphyra.eu".subdomains."router-a.dn42".AAAA = [
+      machineConfig.networking.ip6Address
+    ];
+
+    sops.secrets."dn42/wgPrivateKey" = {
+      owner = "systemd-network";
+      group = "systemd-network";
+    };
+
+    networking.firewall = {
+      checkReversePath = "loose";
+      allowedUDPPorts = [
+        config.systemd.network.netdevs."20-dn42".wireguardConfig.ListenPort
+      ];
+      trustedInterfaces = [
+        "dn42"
+        "wg-cautus"
+      ];
+    };
+
+    systemd.network = {
+      config.networkConfig = {
+        IPv6Forwarding = true;
+      };
+    };
+
+    common.configure.dn42Router = {
+      enable = true;
+      routerId = 42171801;
+      asn = 4242421718;
+      address = "fd6b:6174:6a61::1";
+      range = "fd6b:6174:6a61::/48";
+      peerings = {
+        kioubit = {
+          asn = 4242423914;
+          remoteLinkLocalAddress = "fe80::ade0";
+          endpoint = "de2.g-load.eu:21718";
+          publicKey = "B1xSG/XTJRLd+GrWDsB06BqnIq8Xud93YVh/LYYYtUY=";
+          listenPort = 51823;
+        };
+        pleiades = {
+          asn = 4242420069;
+          remoteLinkLocalAddress = "fe80::706c:6569:6164:6573";
+          endpoint = "central.net.nojus.org:21718";
+          publicKey = "1YAga5Bhreysf/XmhOnDGh3FmbN3Mp2jZjMSAQb/TEM=";
+          listenPort = 51824;
+        };
+        echonet = {
+          asn = 4242420714;
+          remoteLinkLocalAddress = "fe80::718";
+          publicKey = "NxYj58YhWf0JXC+pQAHfh3saUkQSII0lBTDvYGe5kw4=";
+          listenPort = 51825;
+        };
+        tbspace = {
+          asn = 76190;
+          remoteLinkLocalAddress = "fe80::1299:e";
+          endpoint = "dn42.tbspace.de:49158";
+          publicKey = "NW8IeEmAXmwYMuMlvrb9Zpkcko6bzotDlYtGePtgzQE=";
+          listenPort = 51826;
+        };
+        antibldg = {
+          asn = 4242421403;
+          remoteLinkLocalAddress = "fe80::1234:9320";
+          endpoint = "zaphyra.dn42.antibuild.ing:15569";
+          publicKey = "vambITMGGpA7kxCRGFlY1X36bevxXYELT/ORNgZ72ms=";
+          listenPort = 51827;
+        };
+        dahlabandon = {
+          asn = 4242420814;
+          remoteLinkLocalAddress = "fe80::1718";
+          endpoint = "helios-fallen.iron-bear.de:1718";
+          publicKey = "+tg4bDDwfyQZSw0x8x9Ye2tDWPZ/VAf+KTAE1QLaKEI=";
+          listenPort = 51828;
+        };
+        pentane = {
+          asn = 4242423253;
+          remoteLinkLocalAddress = "fe80::43:59:43";
+          endpoint = "imp.aidoskyneen.eu:49507";
+          publicKey = "W+h0FMrxsAP7RppqFFMrfDHuu5CMW5aTW9E1MZXFf1w=";
+          listenPort = 51829;
+        };
+        # e1mo = {
+        #   asn = 4242420565;
+        #   remoteLinkLocalAddress = "";
+        #   endpoint = "";
+        #   publicKey = "";
+        #   listenPort = 51830;
+        # };
+        clerie = {
+          asn = 4242422574;
+          remoteLinkLocalAddress = "fe80::2574";
+          endpoint = "dn42-il-gw1.net.clerie.de:51718";
+          publicKey = "yJmr6lQzibmZV6/6VItXsXbcq4UKMyWFwJJt4lAkvCs=";
+          listenPort = 51831;
+        };
+        etwas = {
+          asn = 4242422264;
+          remoteLinkLocalAddress = "fe80::acab";
+          endpoint = "ncvps.dn42.etwas.me:22266";
+          publicKey = "7ZLtBmXN+zOYJ52jtUdZO0HiEZZrxnIO/LLejFcFnnk=";
+          listenPort = 51832;
+        };
+        pilz = {
+          asn = 4242420663;
+          remoteLinkLocalAddress = "fe80::acab";
+          publicKey = "NxHkdwZPVL+3HdrHTFOslUpUckTf0dzEG9qpZ0FTBnA=";
+          listenPort = 51833;
+        };
+        # c4tg1rl5 = {
+        #   asn = "4242421411";
+        #   remoteLinkLocalAddress = "";
+        #   hasPresharedKey = true;
+        #   publicKey = "";
+        #   listenPort = 51834;
+        # };
+        lgcl = {
+          asn = 4242421825;
+          remoteLinkLocalAddress = "fe80::4d:6172:6379";
+          publicKey = "7AoJ23hNMLzVM4jjusBlrGDEdwAkSdsEl3Vw/diVlns=";
+          listenPort = 51835;
+        };
+        mira = {
+          asn = 4242420161;
+          endpoint = "dn42-router-1.svc.nesaia.net:21718";
+          remoteLinkLocalAddress = "fe80::161";
+          publicKey = "AAcJ/NA68NtxKd27+HlbEZXFo+u0NKt+ksUGYJBc/AQ=";
+          listenPort = 51836;
+        };
+        lfm = {
+          asn = 4242420632;
+          endpoint = "gw-de1.dn42.infra.linfan.moe:41718";
+          remoteLinkLocalAddress = "fe80::632";
+          publicKey = "WfQGnN2WX5KTi3SGIde2kuVfhgDQ+ASaZfU+ORb9ukA=";
+          listenPort = 51837;
+        };
+        ember = {
+          asn = 4242420197;
+          endpoint = "chaldene.dn42.n0emis.eu:21718";
+          remoteLinkLocalAddress = "fe80::42:42:1";
+          publicKey = "UgGpP9j881KeHOL5qElEZ/fXbucBUgTcihbv3yGlxHo=";
+          listenPort = 51838;
+        };
+      };
+    };
+
+    zpha.profiles.dn42 = {
+      enable = true;
+      wgPublicKey = machineConfig.networking.dn42.wgPublicKey;
+      wgPrivateKey = config.sops.secrets."dn42/wgPrivateKey".path;
+      addresses = [
+        "${machineConfig.networking.dn42.ip6Address}/${toString machineConfig.networking.dn42.ip6PrefixLength}"
+        "fd6b:6174:6a61:53::${toString machineConfig.id}/128"
+      ];
+    };
+
+    services.kresd = {
+      enable = true;
+      listenPlain = [ "[fd6b:6174:6a61::1]:53" ];
+      extraConfig = ''
+        modules = {
+          'hints > iterate',  -- Allow loading /etc/hosts or custom root hints
+          'stats',            -- Track internal statistics
+          'predict',          -- Prefetch expiring/frequent records
+        }
+
+        log_level('info')
+
+        -- Cache size
+        cache.size = 100 * MB
+
+        dn42 = {
+          'dn42.',
+          '20.172.in-addr.arpa.',
+          '21.172.in-addr.arpa.',
+          '22.172.in-addr.arpa.',
+          '23.172.in-addr.arpa.',
+          '10.in-addr.arpa.',
+          'd.f.in-addr.arpa.',
+        }
+
+        -- NXDOMAINs that could sometimes happen due to aggressive DNSSEC caching.
+        policy.add(policy.suffix(policy.FLAGS({'NO_EDNS'}), dn42))
+
+        policy.add(policy.suffix(policy.STUB({'fd42:d42:d42:54::1', 'fd42:d42:d42:53::1'}), policy.todnames(dn42)))
+        -- policy.add(policy.FORWARD({'1.1.1.1'}))
+
+        -- trust_anchors.remove('.')
+        trust_anchors.set_insecure(dn42) -- Disable DNSSEC for these domains
+
+        modules.load('nsid')
+        nsid.name(hostname() .. ':' .. os.getenv("SYSTEMD_INSTANCE"))
+      '';
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/configure/dnsServer.nix b/nixosModules/zpha/configure/dnsServer.nix
@@ -0,0 +1,156 @@
+{
+  machines,
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+let
+  inherit (lib) types;
+  cfg = config.zpha.configure.dnsServer;
+
+in
+{
+
+  options.zpha.configure.dnsServer = {
+    enable = lib.mkEnableOption "";
+    isPrimary = lib.mkOption {
+      type = types.bool;
+      default = config.networking.hostName == "cautus";
+    };
+  };
+
+  config = lib.mkIf cfg.enable (
+    let
+      allZones =
+        with pkgs.dnsNix.combinators;
+        let
+          TXT = [ "openpgp4fpr:BFE6386C8D66BCD4DAE14FC895F0FE7CD7E6A022" ];
+          CAA = [
+            {
+              issuerCritical = false;
+              tag = "issue";
+              value = "letsencrypt.org";
+            }
+          ];
+          NS = [
+            "ns1.fc9f.de."
+            "ns2.fc9f.de."
+          ];
+          SOA = {
+            nameServer = "ns1.fc9f.de.";
+            adminEmail = "dns@fc9f.de"; # Email address with a real `@`!
+            serial = 0;
+          };
+
+        in
+        {
+          "fc9f.de" = {
+            inherit NS SOA;
+            subdomains = {
+              acme.NS = [ "ns1" ];
+              ns1 = host machines.cautus.networking.ip4Address machines.cautus.networking.ip6Address;
+              ns2 = host machines.sorrah.networking.ip4Address machines.sorrah.networking.ip6Address;
+            };
+          };
+
+          "zaphyra.dn42" = {
+            NS = [ "ns1.zaphyra.dn42." ];
+            SOA = {
+              nameServer = "ns1.zaphyra.dn42.";
+              adminEmail = "dns@zaphyra.eu"; # Email address with a real `@`!
+              serial = 0;
+            };
+            subdomains = {
+              ns1.AAAA = [ "fd6b:6174:6a61:53::1" ];
+              ns2.AAAA = [ "fd6b:6174:6a61:53::2" ];
+            };
+          };
+
+          "zaphyra.eu" = {
+            inherit
+              SOA
+              NS
+              CAA
+              ;
+          };
+
+          "zaphyra.de" = {
+            inherit
+              SOA
+              NS
+              CAA
+              TXT
+              ;
+          };
+
+          "zpha.de" = {
+            inherit
+              SOA
+              NS
+              CAA
+              TXT
+              ;
+          };
+
+          "katja.wtf" = {
+            inherit SOA NS CAA;
+          };
+
+          "ctucx.de" = {
+            inherit SOA NS CAA;
+          };
+
+          "thein.ovh" = {
+            inherit SOA NS CAA;
+          };
+
+          "ctu.cx" = {
+            inherit SOA NS CAA;
+            subdomains = {
+              _atproto.TXT = [ "did=did:plc:zaeuok3fmh2pcp4cjiicku4i" ];
+            };
+          };
+        };
+
+    in
+    {
+      systemd.services.knot.after = lib.mkIf cfg.isPrimary [ "sops-install-secrets.service" ];
+
+      sops.secrets.knotKeys = lib.mkIf cfg.isPrimary {
+        owner = "knot";
+        group = "knot";
+      };
+
+      dns = {
+        enable = true;
+        inherit allZones;
+      };
+
+      zpha.services = {
+        knot = {
+          enable = true;
+          primary = cfg.isPrimary;
+          zones = lib.mkIf cfg.isPrimary (
+            lib.mapAttrs (_name: value: {
+              file = value;
+              journal-content = "all";
+              zonefile-sync = -1;
+              zonefile-load = "difference-no-serial";
+            }) config.dns.zoneFiles
+          );
+        };
+        knotACME = lib.mkIf cfg.isPrimary {
+          enable = true;
+          zone = "acme.fc9f.de";
+          zones = lib.attrNames allZones;
+          nameServers = [
+            "ns1.fc9f.de."
+          ];
+          keyFile = config.sops.secrets.knotKeys.path;
+        };
+      };
+    }
+  );
+
+}
diff --git a/nixosModules/zpha/configure/floraCtl.nix b/nixosModules/zpha/configure/floraCtl.nix
@@ -0,0 +1,92 @@
+{
+  config,
+  lib,
+  ...
+}:
+
+{
+
+  options.zpha.configure.floraCtl.enable = lib.mkEnableOption "";
+
+  config = lib.mkIf config.zpha.configure.floraCtl.enable {
+    sops.secrets."zigbee2mqttSecrets.yaml" = {
+      owner = "zigbee2mqtt";
+      key = "zigbee2mqttSecrets";
+    };
+
+    common.configure.persist.system.dirs = [
+      {
+        directory = "/var/lib/zigbee2mqtt";
+        mode = "0770";
+        user = "zigbee2mqtt";
+        group = "zigbee2mqtt";
+      }
+    ];
+
+    systemd.services.zigbee2mqtt = {
+      serviceConfig = {
+        Restart = lib.mkForce "always";
+        RuntimeMaxSec = "1d";
+      };
+    };
+
+    services = {
+      udev.extraRules = ''
+        SUBSYSTEM=="tty", ATTRS{idVendor}=="10c4", ATTRS{serial}=="00_12_4B_00_25_9B_C1_FC", SYMLINK+="zigbee0"
+        ATTR{idVendor}=="0451", ATTR{idProduct}=="16a8", ENV{ID_MM_DEVICE_IGNORE}="1"
+        SUBSYSTEM=="tty", ATTRS{idVendor}=="0451", ATTRS{idProduct}=="16a8", SYMLINK+="zigbee0"
+      '';
+
+      zigbee2mqtt = {
+        enable = true;
+        settings = {
+          homeassistant.enabled = true;
+          permit_join = false;
+
+          mqtt = {
+            base_topic = "zigbee2mqtt";
+            server = "mqtt://192.168.2.147:1883";
+            user = "!${config.sops.secrets."zigbee2mqttSecrets.yaml".path} mqttUser";
+            password = "!${config.sops.secrets."zigbee2mqttSecrets.yaml".path} mqttPassword";
+          };
+
+          serial = {
+            adapter = "zstack";
+            port = "/dev/zigbee0";
+            disable_led = true;
+          };
+
+          frontend = {
+            port = 8422;
+            host = "::1";
+          };
+
+          advanced = {
+            log_level = "info";
+            log_output = [ "console" ];
+            channel = 11;
+            pan_id = 55067;
+            ext_pan_id = [
+              116
+              73
+              103
+              101
+              20
+              101
+              160
+              9
+            ];
+            network_key = "!${config.sops.secrets."zigbee2mqttSecrets.yaml".path} network_key";
+          };
+
+          device_options.retain = true;
+          devices = {
+            "0x00124b0023ad17f1".friendly_name = "door_buzzer";
+          };
+        };
+      };
+    };
+
+  };
+
+}
diff --git a/nixosModules/zpha/configure/fonts.nix b/nixosModules/zpha/configure/fonts.nix
@@ -0,0 +1,36 @@
+{
+  lib,
+  config,
+  pkgs,
+  ...
+}:
+{
+
+  options.zpha.configure.fonts.enable = lib.mkEnableOption "configure fonts";
+
+  config = lib.mkIf config.zpha.configure.fonts.enable {
+    fonts = {
+      fontconfig = {
+        enable = true;
+        defaultFonts = {
+          serif = [ "Adwaita Sans" ];
+          sansSerif = [ "Adwaita Sans" ];
+          monospace = [ "Adwaita Mono" ];
+          emoji = [ "Noto Color Emoji" ];
+        };
+      };
+
+      packages = with pkgs; [
+        adwaita-fonts
+        liberation_ttf
+        ttf_bitstream_vera
+        noto-fonts
+        noto-fonts-color-emoji
+        nerd-fonts.symbols-only
+        fira-code
+        fira-mono
+      ];
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/configure/mailServer.nix b/nixosModules/zpha/configure/mailServer.nix
@@ -0,0 +1,266 @@
+{
+  npins,
+  sopsSecrets,
+  machineConfig,
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.zpha.configure.mailServer.enable = lib.mkEnableOption "";
+
+  imports =
+    (lib.mkIf config.zpha.configure.mailServer.enable [
+      (import npins.simpleNixosMailserver)
+    ]).content;
+
+  config = lib.mkIf config.zpha.configure.mailServer.enable (
+    let
+      hostName = config.networking.fqdn;
+      ip6Address = machineConfig.networking.ip6Address;
+      ip4Address = machineConfig.networking.ip4Address;
+      primaryDomain = "zaphyra.eu";
+      primaryDomainDkimKey = "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMuEtG24S6ksVx04avtjwIrfijZvQMxe44HrAXjW+Qe7ZbBHtS+q8alvL21zHbe4VgAOTNZ+fCnqSif4TFaOQnwuGwWke5SRBHV6RmWLaJUnN7krjFj+oNmKnl5M3GPI62shhk4OlMgAdDrH/JApd4XTqR3m0U/8rXqPumfbHhzwIDAQAB";
+      extraDomains = {
+        "katja.wtf" =
+          "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC56L/GDEY0hTvcZjjCpk3/3c4qAmGAQR06tVpgz7gHs0kMPiGhpg3gDv8kCOu4l3C96oT6eaQoyLcC+ZhJT4ribZaNSD+7lkXk23s4LecklBQxLAjvLrc0GQ9zYp8/Qg4g+Wo9fHR3Lum4tqyyFuT/P21knw+nDWxvz3d0Y4XNVwIDAQAB";
+        "ctu.cx" =
+          "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDOuE5vBNP0L4i3OxcFdFbTJ/c8o58CL+cMHBh8lAZej1nSYOPBdpfJRpWiHduWu8cLWNu62nDeY9IGGnE6g9o6+6sMT51NdoY7FFcNNjhm0EoZVDaB1Ffy74ycIDAwuNfp8kpKFsxWMSs1CFy6IRDIIzaQc9JAIoBUBbN5rP2DcwIDAQAB";
+        "ctucx.de" =
+          "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCg3pBG8oH8h0w5YrZ7Dpmtk+/XqE9HElWeF1SWMo86aVLkbsMKjY0WbfAq5YfEdSr/pQrILC+oAt/q6TuLzABYd7cLzK7KgdIX2SuYvujmHqzOOn1huAkzQU0wJMnMYx/0wCFMnVHXsWY9UF2zHDhYu8Jo9vuwMPwGG9u1qnfdCQIDAQAB";
+        "thein.ovh" =
+          "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDYogCqmPNomxG4KyZGsfpefFNPS5lY9aRm7TjiONKKPKQFb4oFfUBacurfL+cdGhX6CBnRr6IUXZ37e+ptOyWNFfG1e5R7dJeRdmCZvsn2DRbxCEeGA6gjl3hmRIjg3HUCWjWlzjRXV4Qke/7Q1y1lfivOrgU72bLw/V7BEi1OZQIDAQAB";
+      };
+
+      mailAutoConfig = ''
+        <?xml version="1.0" encoding="UTF-8"?>
+        <clientConfig version="1.1">
+         <emailProvider id="${primaryDomain}">
+           <domain>${primaryDomain}</domain>
+           <displayName>${hostName}</displayName>
+           <displayShortName>${hostName}</displayShortName>
+           <incomingServer type="imap">
+             <hostname>${hostName}</hostname>
+             <port>993</port>
+             <socketType>SSL</socketType>
+             <authentication>password-cleartext</authentication>
+             <username>%EMAILADDRESS%</username>
+           </incomingServer>
+           <outgoingServer type="smtp">
+             <hostname>${hostName}</hostname>
+             <port>465</port>
+             <socketType>SSL</socketType>
+             <authentication>password-cleartext</authentication>
+             <username>%EMAILADDRESS%</username>
+           </outgoingServer>
+         </emailProvider>
+        </clientConfig>
+      '';
+
+    in
+    {
+      dns.zones =
+        with pkgs.dnsNix.combinators;
+        let
+          TXT = [ "v=spf1 a mx ip4:${ip4Address} +ip6:${ip6Address} ~all" ];
+          DMARC = "v=DMARC1; p=none";
+          MX = with mx; [ (mx 10 "${hostName}.") ];
+        in
+        {
+          "${primaryDomain}" = {
+            inherit MX TXT;
+
+            SRV = [
+              {
+                proto = "tcp";
+                service = "imaps";
+                priority = 0;
+                weight = 1;
+                port = 993;
+                target = "${hostName}.";
+              }
+              {
+                proto = "tcp";
+                service = "imap";
+                priority = 0;
+                weight = 1;
+                port = 143;
+                target = "${hostName}.";
+              }
+              {
+                proto = "tcp";
+                service = "submission";
+                priority = 0;
+                weight = 1;
+                port = 587;
+                target = "${hostName}.";
+              }
+            ];
+
+            subdomains = {
+              autoconfig.CNAME = [ "${hostName}." ];
+              _dmarc.TXT = [ DMARC ];
+              "${config.mailserver.dkimSelector}._domainkey".TXT = [
+                primaryDomainDkimKey
+              ];
+            };
+          };
+        }
+        // (lib.mapAttrs (_domain: dkimKey: {
+          inherit MX TXT;
+
+          subdomains = {
+            _dmarc.TXT = [ DMARC ];
+            "${config.mailserver.dkimSelector}._domainkey".TXT = [ dkimKey ];
+          };
+        }) extraDomains);
+
+      sops.secrets = {
+        "restic/mailserver/repositoryPassword" = { };
+        "restic/mailserver/sshPrivateKey" = { };
+        "mailPasswords/katja@zaphyra.eu" = { };
+        "mailPasswords/gts@zaphyra.eu" = { };
+        "mailPasswords/vaultwarden@zaphyra.eu" = { };
+        "sieveScripts/katja@zaphyra.eu.sieve" = {
+          sopsFile = sopsSecrets.zaphyra.sieve;
+          key = "katja@zaphyra.eu";
+          restartUnits = [ "dovecot2.service" ];
+          owner = "virtualMail";
+          group = "virtualMail";
+          path = "/etc/dovecot/sieve/katja-zaphyra-eu.sieve";
+        };
+      };
+
+      systemd.services.dovecot.after = [ "sops-install-secrets.service" ];
+
+      security.acme.certs."${hostName}".reloadServices = [
+        "postfix.service"
+        "dovecot.service"
+      ];
+
+      common = {
+        configure.persist.system.dirs = [
+          "/var/lib/dhparams"
+          "/var/lib/dovecot"
+          "/var/lib/postfix"
+          {
+            directory = "/var/lib/dkimKeys";
+            mode = "0700";
+            user = "rspamd";
+            group = "rspamd";
+          }
+          {
+            directory = "/var/lib/mailboxes";
+            mode = "0700";
+            user = "virtualMail";
+            group = "virtualMail";
+          }
+          {
+            directory = "/var/lib/redis-rspamd";
+            mode = "0700";
+            user = "redis-rspamd";
+            group = "redis-rspamd";
+          }
+          {
+            directory = "/var/lib/rspamd";
+            mode = "0700";
+            user = "rspamd";
+            group = "rspamd";
+          }
+          {
+            directory = "/var/lib/sieve";
+            mode = "0770";
+            user = "virtualMail";
+            group = "virtualMail";
+          }
+        ];
+
+        services.resticBackup.mailserver = {
+          enable = true;
+          targets = [ "isodon.fc9f.de" ];
+          sshKeyFile = config.sops.secrets."restic/mailserver/sshPrivateKey".path;
+          passwordFile = config.sops.secrets."restic/mailserver/repositoryPassword".path;
+          paths = [
+            "/var/lib/mailboxes"
+            "/var/lib/dkimKeys"
+            "/var/lib/sieve"
+          ];
+        };
+      };
+
+      services = {
+        dovecot2 = {
+          pluginSettings.sieve_global = "/etc/dovecot/sieve";
+          sieve.extensions = [ "editheader" ];
+        };
+        nginx.virtualHosts."autoconfig.${primaryDomain}" = {
+          useACMEHost = "${config.networking.fqdn}";
+          forceSSL = true;
+          locations."= /mail/config-v1.1.xml".return = "200 '${mailAutoConfig}'";
+        };
+      };
+
+      mailserver = {
+        enable = true;
+        stateVersion = 3;
+
+        openFirewall = true;
+        localDnsResolver = false;
+        virusScanning = false;
+
+        certificateScheme = "acme";
+
+        enableManageSieve = true;
+        enableSubmission = true;
+        enableSubmissionSsl = true;
+        enableImap = true;
+        enableImapSsl = true;
+        enablePop3 = false;
+        enablePop3Ssl = false;
+
+        fullTextSearch.enable = true;
+        dmarcReporting.enable = true;
+
+        indexDir = "/var/lib/dovecot/indices";
+        mailDirectory = "/var/lib/mailboxes";
+        sieveDirectory = "/var/lib/sieve";
+        dkimKeyDirectory = "/var/lib/dkimKeys";
+
+        fqdn = hostName;
+        systemDomain = primaryDomain;
+        systemName = "zaphyra-mail";
+
+        domains = [ primaryDomain ] ++ (lib.attrNames extraDomains);
+
+        loginAccounts = {
+          "katja@zaphyra.eu" = {
+            hashedPasswordFile = config.sops.secrets."mailPasswords/katja@zaphyra.eu".path;
+            sieveScript = ''
+              require ["include"];
+              include :global "katja-zaphyra-eu";
+            '';
+            aliases = [
+              "@zaphyra.eu"
+              "@ctu.cx"
+              "@ctucx.de"
+              "@thein.ovh"
+              "@katja.wtf"
+            ];
+          };
+          "gts@zaphyra.eu" = {
+            hashedPasswordFile = config.sops.secrets."mailPasswords/gts@zaphyra.eu".path;
+            sendOnly = true;
+          };
+          "vaultwarden@zaphyra.eu" = {
+            hashedPasswordFile = config.sops.secrets."mailPasswords/vaultwarden@zaphyra.eu".path;
+            sendOnly = true;
+          };
+        };
+      };
+    }
+  );
+
+}
diff --git a/nixosModules/zpha/configure/matrixBridges.nix b/nixosModules/zpha/configure/matrixBridges.nix
@@ -0,0 +1,156 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.zpha.configure.matrixBridges.enable = lib.mkEnableOption "";
+
+  config = lib.mkIf config.zpha.configure.matrixBridges.enable {
+    assertions = [
+      {
+        assertion = config.zpha.websites."continuwuity.zaphyra.eu".enable;
+        message = "The option 'zpha.websites.\"continuwuity.zaphyra.eu\"' must be enabled in order to use this module.";
+      }
+    ];
+
+    sops.secrets = {
+      "environments/mautrixBridges/signal" = { };
+      "environments/mautrixBridges/telegram" = { };
+      "environments/mautrixBridges/whatsapp" = { };
+    };
+
+    common.configure.persist.system.dirs = [
+      "/var/lib/private/mautrix-signal"
+      "/var/lib/private/mautrix-telegram"
+      "/var/lib/private/mautrix-whatsapp"
+    ];
+
+    common.services.mautrixBridge =
+      let
+        commonSettings = rec {
+          database.type = "sqlite3-fk-wal";
+
+          homeserver.address = "http://[::1]:6167";
+          homeserver.domain = "zaphyra.eu";
+
+          encryption.allow = true;
+          encryption.default = true;
+          encryption.pickle_key = "$MAUTRIX_PICKLE_KEY";
+
+          relay.enabled = false;
+          backfill.enabled = true;
+
+          double_puppet.secrets."${homeserver.domain}" = "as_token:$MAUTRIX_DOUBLE_PUPPET_SHARED_SECRET";
+
+          bridge = {
+            personal_filtering_spaces = true;
+
+            permissions."${homeserver.domain}" = "admin";
+
+            cleanup_on_logout = {
+              enabled = true;
+            }
+            // (lib.genAttrs [ "manual" "bad_credentials" ] (_name: {
+              shared_has_users = "delete";
+              shared_no_users = "delete";
+              relayed = "delete";
+              private = "delete";
+            }));
+          };
+        };
+      in
+      {
+        signal = {
+          enable = true;
+          package = pkgs.mautrix-signal.override { withGoolm = true; };
+          environmentFile = config.sops.secrets."environments/mautrixBridges/signal".path;
+          serviceDependencies = [ "continuwuity.service" ];
+          settings = lib.recursiveUpdate commonSettings {
+            database.uri = "file:/var/lib/mautrix-signal/mautrix-signal.db?_txlock=immediate";
+
+            network = {
+              device_name = "Mautix-Signal (on zaphyra.eu)";
+              displayname_template = "{{or .ProfileName .PhoneNumber \"Unknown user\"}} (Signal)";
+            };
+
+            appservice = {
+              id = "signal";
+              address = "http://[::1]:29328/";
+              hostname = "[::1]";
+              port = 29328;
+              as_token = "$MAUTRIX_AS_TOKEN";
+              hs_token = "$MAUTRIX_HS_TOKEN";
+            };
+          };
+        };
+
+        telegram = {
+          enable = true;
+          package = pkgs.zpha.mautrix-telegram;
+          environmentFile = config.sops.secrets."environments/mautrixBridges/telegram".path;
+          serviceDependencies = [ "continuwuity.service" ];
+          settings = lib.recursiveUpdate commonSettings {
+            database.uri = "file:/var/lib/mautrix-telegram/mautrix-telegram.db?_txlock=immediate";
+
+            backfill.max_initial_messages = 400;
+
+            appservice = {
+              id = "telegram";
+              address = "http://[::1]:29317/";
+              hostname = "[::1]";
+              port = 29317;
+              as_token = "$MAUTRIX_AS_TOKEN";
+              hs_token = "$MAUTRIX_HS_TOKEN";
+            };
+
+            network = {
+              device_info.device_model = "Mautix-TelegramGO (on zaphyra.eu)";
+              api_id = "$TELEGRAM_API_ID";
+              api_hash = "$TELEGRAM_API_HASH";
+            };
+          };
+        };
+
+        whatsapp = {
+          enable = true;
+          package = pkgs.mautrix-whatsapp.override { withGoolm = true; };
+          environmentFile = config.sops.secrets."environments/mautrixBridges/whatsapp".path;
+          serviceDependencies = [ "continuwuity.service" ];
+          settings = lib.recursiveUpdate commonSettings {
+            database.uri = "file:/var/lib/mautrix-whatsapp/mautrix-whatsapp.db?_txlock=immediate";
+
+            backfill.max_initial_messages = 100;
+
+            appservice = {
+              id = "whatsapp";
+              address = "http://[::1]:29318/";
+              hostname = "[::1]";
+              port = 29318;
+              as_token = "$MAUTRIX_AS_TOKEN";
+              hs_token = "$MAUTRIX_HS_TOKEN";
+            };
+
+            network = {
+              displayname_template = "{{or .BusinessName .PushName .Phone}} (WA)";
+              os_name = "Mautrix-WhatsApp (on zaphyra.eu)";
+              identity_change_notices = true;
+              url_previews = true;
+              history_sync = {
+                request_full_sync = true;
+                full_sync_config = {
+                  days_limit = 1000;
+                  size_mb_limit = 1024;
+                  storage_quota_mb = 1024;
+                };
+              };
+            };
+          };
+        };
+      };
+  };
+
+}
diff --git a/nixosModules/zpha/configure/netcupTunnel.nix b/nixosModules/zpha/configure/netcupTunnel.nix
@@ -0,0 +1,140 @@
+{
+  nixosConfigurations,
+  machines,
+  machineConfig,
+  config,
+  lib,
+  ...
+}:
+let
+  inherit (lib) types;
+  cfg = config.zpha.configure.netcupTunnel;
+
+in
+{
+
+  options.zpha.configure.netcupTunnel = {
+    enable = lib.mkEnableOption "";
+
+    addresses = lib.mkOption {
+      type = types.listOf types.str;
+      default = [
+        "${machineConfig.networking.ip6Address}/${toString machineConfig.networking.ip6PrefixLength}"
+      ];
+    };
+
+    wgPrivateKey = lib.mkOption {
+      type = types.path;
+      default = config.sops.secrets."wgPrivateKey".path;
+    };
+
+    wgPublicKey = lib.mkOption {
+      type = types.str;
+      default = machineConfig.wgPublicKey;
+    };
+
+  };
+
+  config = lib.mkIf cfg.enable {
+    networking.firewall = {
+      trustedInterfaces = [
+        "wg-netcup"
+        "netcup"
+      ];
+      allowedUDPPorts = [
+        config.systemd.network.netdevs."15-wg-netcup".wireguardConfig.ListenPort
+      ];
+    };
+
+    systemd.network = {
+      config.networkConfig = {
+        IPv6Forwarding = true;
+      };
+
+      netdevs = {
+        "10-netcup" = lib.mkIf (config.networking.hostName != "sorrah") {
+          netdevConfig = {
+            Kind = "dummy";
+            Name = "netcup";
+          };
+        };
+        "15-wg-netcup" = {
+          netdevConfig = {
+            Kind = "wireguard";
+            Name = "wg-netcup";
+          };
+
+          wireguardConfig = {
+            PrivateKeyFile = cfg.wgPrivateKey;
+            ListenPort = 51820;
+            FirewallMark = 51820;
+          };
+
+          wireguardPeers =
+            if (config.networking.hostName == "sorrah") then
+              (lib.pipe nixosConfigurations [
+                (lib.filterAttrs (name: _: name != config.networking.hostName))
+                (lib.filterAttrs (_: value: value.config.zpha.configure.netcupTunnel.enable))
+                (lib.mapAttrsToList (
+                  name: value: {
+                    PublicKey = value.config.zpha.configure.netcupTunnel.wgPublicKey;
+                    AllowedIPs = value.config.zpha.configure.netcupTunnel.addresses;
+                    PersistentKeepalive = 10;
+                  }
+                ))
+              ])
+            else
+              [
+                {
+                  PublicKey = machines.sorrah.wgPublicKey;
+                  Endpoint = "${machines.sorrah.networking.ip4Address}:51820";
+                  AllowedIPs = [ "::/0" ];
+                  PersistentKeepalive = 10;
+                }
+              ];
+        };
+      };
+
+      networks = {
+        "5-primaryInterface" = lib.mkIf (config.networking.hostName == "sorrah") {
+          networkConfig = {
+            IPv6ProxyNDP = true;
+            IPv6ProxyNDPAddress = lib.pipe config.systemd.network.netdevs."15-wg-netcup".wireguardPeers [
+              (map (element: if element ? AllowedIPs then element.AllowedIPs else [ ]))
+              lib.flatten
+              (map (element: builtins.elemAt (lib.strings.splitString "/" element) 0))
+            ];
+          };
+        };
+        "10-netcup" = lib.mkIf (config.networking.hostName != "sorrah") {
+          enable = true;
+          name = "netcup";
+          address = cfg.addresses;
+          routingPolicyRules = lib.singleton {
+            From = "2a03:4000:4d:5e:acab::/112";
+            Table = 1234;
+            Priority = 2000;
+          };
+        };
+        "15-wg-netcup" = {
+          matchConfig.Name = "wg-netcup";
+          linkConfig.RequiredForOnline = false;
+        }
+        // (
+          if (config.networking.hostName == "sorrah") then
+            {
+              address = cfg.addresses;
+            }
+          else
+            {
+              routes = lib.singleton {
+                Destination = "::/0";
+                Table = "1234";
+              };
+            }
+        );
+      };
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/configure/networkManagerProfiles.nix b/nixosModules/zpha/configure/networkManagerProfiles.nix
@@ -0,0 +1,74 @@
+{
+  sopsSecrets,
+  config,
+  lib,
+  ...
+}:
+
+{
+
+  options.zpha.configure.networkManagerProfiles.enable = lib.mkEnableOption "NetworkManager profile";
+
+  config = lib.mkIf config.zpha.configure.networkManagerProfiles.enable {
+    sops.secrets = {
+      "environments/networkManagerProfiles/zaphyraPhone".sopsFile = sopsSecrets.common;
+      "environments/networkManagerProfiles/grogHome".sopsFile = sopsSecrets.common;
+    };
+
+    networking.networkmanager.ensureProfiles = {
+      environmentFiles = [
+        config.sops.secrets."environments/networkManagerProfiles/zaphyraPhone".path
+        config.sops.secrets."environments/networkManagerProfiles/grogHome".path
+      ];
+      profiles = {
+        "grogHome" = {
+          connection = {
+            id = "grogHome";
+            type = "wifi";
+            uuid = "1bbc0cce-148f-4afa-876e-fa6db67d884e";
+          };
+          ipv4 = {
+            method = "auto";
+          };
+          ipv6 = {
+            addr-gen-mode = "default";
+            method = "auto";
+          };
+          proxy = { };
+          wifi = {
+            mode = "infrastructure";
+            ssid = "$GROG_HOME_SSID";
+          };
+          wifi-security = {
+            key-mgmt = "wpa-psk";
+            psk = "$GROG_HOME_PASS";
+          };
+        };
+        "zaphyraPhone" = {
+          connection = {
+            id = "zaphyraPhone";
+            type = "wifi";
+            uuid = "2dc7bb4a-e016-427d-aeb6-28e7a6b91f41";
+          };
+          ipv4 = {
+            method = "auto";
+          };
+          ipv6 = {
+            addr-gen-mode = "default";
+            method = "auto";
+          };
+          proxy = { };
+          wifi = {
+            mode = "infrastructure";
+            ssid = "$ZAPHYRA_PHONE_SSID";
+          };
+          wifi-security = {
+            auth-alg = "open";
+            key-mgmt = "wpa-psk";
+            psk = "$ZAPHYRA_PHONE_PASS";
+          };
+        };
+      };
+    };
+  };
+}
diff --git a/nixosModules/zpha/configure/syncthing.nix b/nixosModules/zpha/configure/syncthing.nix
@@ -0,0 +1,226 @@
+{
+  nixosConfigurations,
+  machineConfig,
+  config,
+  lib,
+  ...
+}:
+let
+  inherit (lib) types;
+  cfg = config.zpha.configure.syncthing;
+
+in
+{
+  options.zpha.configure.syncthing = {
+    enable = lib.mkEnableOption "";
+
+    user = lib.mkOption {
+      type = types.str;
+      default = "zaphyra";
+    };
+
+    group = lib.mkOption {
+      type = types.str;
+      default = "users";
+    };
+
+    dataDir = lib.mkOption {
+      type = types.str;
+      default = "/persist/home/zaphyra";
+    };
+
+    configDir = lib.mkOption {
+      type = types.str;
+      default = "/persist/home/zaphyra/.config/syncthing";
+    };
+
+    id = lib.mkOption {
+      type = types.str;
+      default = machineConfig.syncthingId;
+    };
+
+    enabledShares = lib.mkOption {
+      type = with types; listOf str;
+      default = [
+        "zaphyra-audiobooks"
+        "zaphyra-documents"
+        "zaphyra-pictures"
+        "zaphyra-music"
+        "zaphyra-music-orig"
+        "zaphyra-videos"
+        "zaphyra-db-richtlinien"
+      ];
+    };
+  };
+
+  config = lib.mkIf cfg.enable (
+    let
+      syncthingDevices = {
+        #mobile devices
+        huntii.id = "6YZT5PZ-EKXZBMV-C2MJL75-OCQ36LC-L3QIRPW-VJ5EU3C-2ICZDDO-IZ7IGAO";
+        haueri.id = "YMIK4FX-2IVSTHP-CRZHAEM-2KNSZTP-QR5JTSD-MSSR3XV-SUCJF4F-T5SSDAN";
+        iphone.id = "3SM3LJV-XMHYW2D-MU5WQ3T-KGYUJOI-LXOL6YI-BSVZ2B5-QJ6GVXN-MPWMKQ7";
+
+        #servers
+        isodon.name = "isodon.fc9f.de";
+        isodon.id = "QI2EPUE-4VMZ3XV-LXX3GXP-RHCWTRY-AACLSGL-YG7MIYV-THST74N-KJGIBQ6";
+
+        cuvier.name = "cuvier.fc9f.de";
+        cuvier.id = "JUIJQZE-AWVYZIZ-CR6E66M-TAJIRDH-NEPEPZW-FHO37MJ-QM2MV5Q-OFJTEQI";
+
+        #others
+        #celestine.id = "K6DSDMU-PV56YYN-3XNIU2K-DKIFYT5-AL3PWNS-73M3V52-CYO5WLP-B655VAG";
+      };
+
+      defaultVersioning = {
+        type = "trashcan";
+        params = {
+          cleanoutDays = "3";
+        };
+      };
+
+      isCurrentHost = key: _value: key != config.networking.hostName;
+
+      deviceNames = lib.pipe syncthingDevices [
+        (lib.filterAttrs isCurrentHost)
+        builtins.attrNames
+      ];
+
+      shareDeviceNames =
+        share:
+        (lib.pipe syncthingDevices [
+          (lib.filterAttrs isCurrentHost)
+          (lib.filterAttrs (
+            name: _value:
+            let
+              syncthingCfg = nixosConfigurations."${name}".config.modules.presets.zaphyra.syncthing;
+            in
+            if (nixosConfigurations ? name) then
+              if syncthingCfg.enable then lib.elem share syncthingCfg.enabledShares else false
+            else
+              false
+          ))
+          builtins.attrNames
+        ]);
+
+      devices = lib.pipe syncthingDevices [
+        (lib.filterAttrs isCurrentHost)
+      ];
+
+      folders =
+        let
+          inherit (config.services.syncthing) dataDir;
+
+        in
+        {
+          zaphyra-audiobooks = rec {
+            enable = lib.mkDefault (lib.elem id cfg.enabledShares);
+            id = "zaphyra-audiobooks";
+            label = "Audiobooks";
+            path = lib.mkDefault "${dataDir}/Audiobooks";
+            devices = deviceNames;
+            versioning = lib.mkDefault defaultVersioning;
+          };
+
+          zaphyra-documents = rec {
+            enable = lib.mkDefault (lib.elem id cfg.enabledShares);
+            id = "zaphyra-documents";
+            label = "Documents";
+            path = lib.mkDefault "${dataDir}/Documents";
+            devices = deviceNames;
+            versioning = lib.mkDefault defaultVersioning;
+          };
+
+          zaphyra-music = rec {
+            enable = lib.mkDefault (lib.elem id cfg.enabledShares);
+            id = "zaphyra-music";
+            label = "Music";
+            path = lib.mkDefault "${dataDir}/Music";
+            devices = deviceNames;
+            versioning = lib.mkDefault defaultVersioning;
+          };
+
+          zaphyra-pictures = rec {
+            enable = lib.mkDefault (lib.elem id cfg.enabledShares);
+            id = "zaphyra-pictures";
+            label = "Pictures";
+            path = lib.mkDefault "${dataDir}/Pictures";
+            devices = deviceNames;
+            versioning = lib.mkDefault defaultVersioning;
+          };
+
+          zaphyra-videos = rec {
+            enable = lib.mkDefault (lib.elem id cfg.enabledShares);
+            id = "zaphyra-videos";
+            label = "Videos";
+            path = lib.mkDefault "${dataDir}/Videos";
+            devices = deviceNames;
+            versioning = lib.mkDefault defaultVersioning;
+          };
+
+          zaphyra-db-richtlinien = rec {
+            enable = lib.mkDefault (lib.elem id cfg.enabledShares);
+            id = "zaphyra-db-richtlinien";
+            label = "Bahn-Richtlinien";
+            path = lib.mkDefault "${dataDir}/Bahn-Richtlinien";
+            devices = deviceNames;
+            versioning = lib.mkDefault defaultVersioning;
+          };
+
+          zaphyra-music-orig = rec {
+            enable = lib.mkDefault (lib.elem id cfg.enabledShares);
+            id = "zaphyra-music-orig";
+            label = "Music (Originals)";
+            path = lib.mkDefault "${dataDir}/Music (Originals)";
+            devices = shareDeviceNames "${id}";
+            versioning = lib.mkDefault defaultVersioning;
+          };
+
+          zaphyra-media = rec {
+            enable = lib.mkDefault (lib.elem id cfg.enabledShares);
+            id = "zaphyra-media";
+            label = "Media (legacy)";
+            path = lib.mkDefault "${dataDir}/Media (legacy)";
+            devices = shareDeviceNames "${id}";
+            versioning = lib.mkDefault defaultVersioning;
+          };
+
+          zaphyra-music-inbox = rec {
+            enable = lib.mkDefault (lib.elem id cfg.enabledShares);
+            id = "zaphyra-music-inbox";
+            label = "Music (Inbox)";
+            path = lib.mkDefault "${dataDir}/Music (Inbox)";
+            devices = (shareDeviceNames "${id}");
+            versioning = lib.mkDefault defaultVersioning;
+          };
+
+        };
+
+    in
+    {
+      sops.secrets = lib.genAttrs [ "syncthing/cert" "syncthing/key" ] (_name: {
+        owner = cfg.user;
+        inherit (cfg) group;
+      });
+
+      services.syncthing = {
+        enable = true;
+        openDefaultPorts = true;
+        guiAddress = "[::1]:8384";
+
+        inherit (cfg) user;
+        inherit (cfg) group;
+
+        cert = config.sops.secrets."syncthing/cert".path;
+        key = config.sops.secrets."syncthing/key".path;
+
+        inherit (cfg) dataDir;
+        inherit (cfg) configDir;
+
+        settings.devices = devices;
+        settings.folders = folders;
+      };
+    }
+  );
+
+}
diff --git a/nixosModules/zpha/configure/syncthingBackup.nix b/nixosModules/zpha/configure/syncthingBackup.nix
@@ -0,0 +1,48 @@
+{
+  config,
+  lib,
+  ...
+}:
+
+{
+
+  options.zpha.configure.syncthingBackup.enable = lib.mkEnableOption "";
+
+  config = lib.mkIf config.zpha.configure.syncthingBackup.enable (
+    let
+      shares = lib.removeAttrs config.services.syncthing.settings.folders [ "zaphyra-music" ];
+    in
+    {
+      sops.secrets = lib.pipe shares [
+        (lib.filterAttrs (name: value: value.enable))
+        (lib.mapAttrsToList (name: value: value.id))
+        (map (element: [
+          (lib.nameValuePair "restic/syncthing/${element}/repositoryPassword" { })
+          (lib.nameValuePair "restic/syncthing/${element}/sshPrivateKey" { })
+        ]))
+        (lib.flatten)
+        (builtins.listToAttrs)
+      ];
+
+      common.services.resticBackup = lib.pipe shares [
+        (lib.filterAttrs (name: value: value.enable))
+        (lib.mapAttrs' (
+          name: value:
+          lib.nameValuePair "syncthing-${name}" {
+            inherit (config.services.syncthing) user;
+            enable = true;
+            targets = [ "restic-target.fc9f.de" ];
+            sshKeyFile = config.sops.secrets."restic/syncthing/${name}/sshPrivateKey".path;
+            passwordFile = config.sops.secrets."restic/syncthing/${name}/repositoryPassword".path;
+            paths = [ value.path ];
+            exclude = [
+              ".stfolder"
+              ".stversions"
+            ];
+          }
+        ))
+      ];
+    }
+  );
+
+}
diff --git a/nixosModules/zpha/configure/xmppServer.nix b/nixosModules/zpha/configure/xmppServer.nix
@@ -0,0 +1,38 @@
+{
+  config,
+  lib,
+  ...
+}:
+
+{
+
+  options.zpha.configure.xmppServer.enable = lib.mkEnableOption "";
+
+  config = lib.mkIf config.zpha.configure.xmppServer.enable {
+    sops.secrets = {
+      "restic/prosody/repositoryPassword" = { };
+      "restic/prosody/sshPrivateKey" = { };
+    };
+
+    common.services = {
+      resticBackup.prosody = {
+        inherit (config.services.prosody) user;
+        enable = true;
+        targets = [
+          "restic-target.fc9f.de"
+          "isodon.fc9f.de"
+        ];
+        sshKeyFile = config.sops.secrets."restic/prosody/sshPrivateKey".path;
+        passwordFile = config.sops.secrets."restic/prosody/repositoryPassword".path;
+        paths = [ config.services.prosody.dataDir ];
+      };
+
+      prosody = {
+        enable = true;
+        domain = "zaphyra.eu";
+        adminUsers = [ "katja@zaphyra.eu" ];
+      };
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/profiles/dn42.nix b/nixosModules/zpha/profiles/dn42.nix
@@ -0,0 +1,180 @@
+{
+  nixosConfigurations,
+  machines,
+  machineConfig,
+  config,
+  lib,
+  ...
+}:
+let
+  inherit (lib) types;
+  cfg = config.zpha.profiles.dn42;
+
+in
+{
+
+  options.zpha.profiles.dn42 = {
+    enable = lib.mkEnableOption "";
+
+    addresses = lib.mkOption {
+      type = types.listOf types.str;
+      default = [
+        "${machineConfig.networking.dn42.ip6Address}/${toString machineConfig.networking.dn42.ip6PrefixLength}"
+      ];
+    };
+
+    wgPrivateKey = lib.mkOption {
+      type = types.path;
+      default = config.sops.secrets."wgPrivateKey".path;
+    };
+
+    wgPublicKey = lib.mkOption {
+      type = types.str;
+      default = machineConfig.wgPublicKey;
+    };
+
+  };
+
+  config = lib.mkIf cfg.enable {
+    dns.zones."zaphyra.dn42".subdomains."${config.networking.hostName}".AAAA = [
+      ((lib.network.ipv6.fromString (lib.elemAt cfg.addresses 0)).address)
+    ];
+
+    security.acme.certs."${config.networking.hostName}.zaphyra.dn42" = {
+      server = "https://acme.burble.dn42/v1/dn42/acme/directory";
+      validMinDays = 20;
+      keyType = "ec384";
+      dnsProvider = null;
+    };
+
+    services.nginx.virtualHosts."${config.networking.hostName}.zaphyra.dn42" = {
+      enableACME = true;
+      forceSSL = true;
+      kTLS = true;
+    };
+
+    services.resolved = {
+      enable = true;
+      fallbackDns = [
+        "8.8.8.8"
+        "2001:4860:4860::8844"
+      ];
+    };
+
+    systemd.network = {
+      netdevs."20-dn42" = {
+        netdevConfig = {
+          Kind = "wireguard";
+          Name = "dn42";
+          MTUBytes = 1280;
+        };
+
+        wireguardConfig = {
+          PrivateKeyFile = cfg.wgPrivateKey;
+          ListenPort = 1718;
+          FirewallMark = 1718;
+        };
+
+        wireguardPeers =
+          if config.zpha.configure.dn42Router.enable then
+            (lib.pipe nixosConfigurations [
+              (lib.filterAttrs (name: _: name != config.networking.hostName))
+              (lib.filterAttrs (_: value: value.config.zpha.profiles.dn42.enable))
+              (lib.mapAttrsToList (
+                name: value: {
+                  PublicKey = value.config.zpha.profiles.dn42.wgPublicKey;
+                  AllowedIPs = value.config.zpha.profiles.dn42.addresses;
+                  PersistentKeepalive = 10;
+                }
+              ))
+            ])
+            ++ [
+              {
+                # zaphyraThinkPad
+                PublicKey = "7drlp9TmHgSgqSR1PynfAzf8BIH4LWVuFDtPqGs88EY=";
+                AllowedIPs = [ "fd6b:6174:6a61::20/128" ];
+                PersistentKeepalive = 10;
+              }
+              {
+                # zaphyraApplePhone
+                PublicKey = "3rp8iD+Nk9DsyM/JCvrV7bBnEzioG30SDqOQhNWwsVs=";
+                AllowedIPs = [ "fd6b:6174:6a61::21/128" ];
+                PersistentKeepalive = 10;
+              }
+              {
+                # zaphyraPixel
+                PublicKey = "ski1Uya2PSCZsrBblcgoM9WL5h+1KAd61uZD2sfRDjE=";
+                AllowedIPs = [ "fd6b:6174:6a61::22/128" ];
+                PersistentKeepalive = 10;
+              }
+              {
+                # zaphyraFramework
+                PublicKey = "YdseqpjpKGV7JWWDEJOAtqB3tzk7vI/gPFiqmCyeVTM=";
+                AllowedIPs = [ "fd6b:6174:6a61::23/128" ];
+                PersistentKeepalive = 10;
+              }
+            ]
+          else
+            [
+              {
+                PublicKey = machines.sorrah.networking.dn42.wgPublicKey;
+                Endpoint = "[${machines.sorrah.networking.ip6Address}]:1718";
+                AllowedIPs = [ "fd00::/8" ];
+                PersistentKeepalive = 10;
+              }
+            ];
+      };
+
+      networks."20-dn42" = {
+        matchConfig.Name = "dn42";
+        linkConfig.RequiredForOnline = false;
+        address = cfg.addresses;
+      }
+      // (
+        if config.zpha.configure.dn42Router.enable then
+          { }
+        else
+          {
+            routes = [ { Destination = "fd00::/8"; } ];
+            networkConfig = {
+              DNSDefaultRoute = false;
+              DNS = [ "fd6b:6174:6a61::1" ];
+              Domains = [
+                "~dn42"
+                "d.f.ip6.arpa"
+              ];
+            };
+          }
+      );
+    };
+
+    #dn42 root ca
+    security.pki.certificates = lib.singleton ''
+      -----BEGIN CERTIFICATE-----
+      MIID8DCCAtigAwIBAgIFIBYBAAAwDQYJKoZIhvcNAQELBQAwYjELMAkGA1UEBhMC
+      WEQxDTALBgNVBAoMBGRuNDIxIzAhBgNVBAsMGmRuNDIgQ2VydGlmaWNhdGUgQXV0
+      aG9yaXR5MR8wHQYDVQQDDBZkbjQyIFJvb3QgQXV0aG9yaXR5IENBMCAXDTE2MDEx
+      NjAwMTIwNFoYDzIwMzAxMjMxMjM1OTU5WjBiMQswCQYDVQQGEwJYRDENMAsGA1UE
+      CgwEZG40MjEjMCEGA1UECwwaZG40MiBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHzAd
+      BgNVBAMMFmRuNDIgUm9vdCBBdXRob3JpdHkgQ0EwggEiMA0GCSqGSIb3DQEBAQUA
+      A4IBDwAwggEKAoIBAQDBGRDeAYYR8YIMsNTl/5rI46r0AAiCwM9/BXohl8G1i6PR
+      VO76BA931VyYS9mIGMEXEJLlJPrvYetdexHlvrqJ8mDJO4IFOnRUYCNmGtjNKHvx
+      6lUlmowEoP+dSFRMnbwtoN9xrmRHDed1BfTFAirSDL6jY1RiK60p62oIpF6o6/FS
+      FE7RXUEv0xm65II2etGj8oT2B7L2DDDb23bu6RQFx491tz/V1TVW0JJE3yYeAPqu
+      y3rJUGddafj5/SWnHdtAsUK8RVfhyRxCummAHuolmRKfbyOj0i5KzRXkfEn50cDw
+      GQwVUM6mUbuqFrKC7PRhRIwc3WVgBHewTZlnF/sJAgMBAAGjgaowgacwDgYDVR0P
+      AQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFFR2iLLAtTDQ/E/J
+      bTv5jFURrBUVMB8GA1UdIwQYMBaAFFR2iLLAtTDQ/E/JbTv5jFURrBUVMEQGA1Ud
+      HgQ9MDugOTAHggUuZG40MjAKhwisFAAA//wAADAihyD9QgAAAAAAAAAAAAAAAAAA
+      //8AAAAAAAAAAAAAAAAAADANBgkqhkiG9w0BAQsFAAOCAQEAXKQ7QaCBaeJxmU11
+      S1ogDSrZ7Oq8jU+wbPMuQRqgdfPefjrgp7nbzfUW5GrL58wqj+5/FAqltflmSIHl
+      aB4MpqM8pyvjlc/jYxUNFglj2WYxO0IufBrlKI5ePZ4omUjpR4YR4gQpYCuWlZmu
+      P6v/P0WrfgdFTk0LGEA9OwKcTqkPpcI/SjB3rmZcs42yQWvimAF94GtScE09uKlI
+      9QLS2UBmtl5EJRFVrDEC12dyamq8dDRfddyaT4MoQOAq3D9BQ1pHByu3pz/QFaJC
+      1zAi8vbktPY7OMprTOc8pHDL3q8KFP8jJcoEzZ5Jw0vkCrULhLXvtFtjB0djzVxQ
+      C0IKqQ==
+      -----END CERTIFICATE-----
+    '';
+  };
+
+}
diff --git a/nixosModules/zpha/profiles/graphical.nix b/nixosModules/zpha/profiles/graphical.nix
@@ -0,0 +1,136 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+{
+
+  options.zpha.profiles.graphical.enable = lib.mkEnableOption "graphical preset";
+
+  config = lib.mkIf config.zpha.profiles.graphical.enable {
+    preservation.preserveAt."/persist".users.zaphyra = {
+      commonMountOptions = [
+        "x-gdu.hide"
+        "x-gvfs-hide"
+      ];
+
+      directories = [
+        "Downloads"
+        "Documents"
+        "Pictures"
+        "Videos"
+        "Music"
+
+        "Audiobooks"
+        "Bahn-Richtlinien"
+
+        "proj"
+
+        {
+          directory = ".local/share/Trash";
+          # Trash should be accessed via a symlink. Nautilus is incompatible
+          # with a bind-mounted trash.
+          how = "symlink";
+        }
+      ];
+    };
+
+    services.udisks2.enable = true;
+    systemd.network.wait-online.enable = false;
+
+    common = {
+      configure = {
+        fonts.enable = true;
+      };
+
+      hardware = {
+        intelGraphics.enable = true;
+        bluetooth.enable = true;
+        audio.enable = true;
+        smartcard.enable = true;
+      };
+
+      services = {
+        upower.enable = true;
+        power-profiles-daemon.enable = true;
+
+        NetworkManager.enable = true;
+
+        gvfs.enable = true;
+
+        greetd = {
+          enable = true;
+          autoLogin.command = "niri-session -l";
+        };
+      };
+    };
+
+    zpha = {
+      configure = {
+        fonts.enable = true;
+        networkManagerProfiles.enable = true;
+      };
+
+      services = {
+        dssd.enable = true;
+        avahi.enable = true;
+        pipewire.enable = true;
+        batsignal.enable = true;
+
+        oniri.enable = true;
+      };
+
+      programs = {
+        shellUtilities.enable = true;
+        nixUtilities.enable = true;
+        deploymentUtilities.enable = true;
+        chaosctl.enable = true;
+
+        broot.enable = true;
+        micro.enable = true;
+
+        git.enable = true;
+        gpg.enable = true;
+
+        niri.enable = true;
+        swaylock.enable = true;
+        vibepanel.enable = true;
+        vicinae.enable = true;
+
+        wpaperd.enable = true;
+        shaderbg.enable = true;
+
+        ghostty.enable = true;
+
+        nautilus.enable = true;
+        gnome-text-editor.enable = true;
+        celluloid.enable = true;
+        loupe.enable = true;
+        papers.enable = true;
+        pdfarranger.enable = true;
+        yt-dlp.enable = true;
+        typst.enable = true;
+
+        firefox.enable = true;
+        thunderbird.enable = true;
+        gajim.enable = true;
+        dino.enable = true;
+        gnome-calendar.enable = true;
+      };
+    };
+
+    users.users.zaphyra = {
+      extraGroups = [ "networkmanager" ];
+      maid = {
+        xdg.enable = true;
+
+        packages = with pkgs; [
+          adw-bluetooth
+          zpha.nirilayout
+        ];
+      };
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/profiles/nginx.nix b/nixosModules/zpha/profiles/nginx.nix
@@ -0,0 +1,75 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.zpha.profiles.nginx.enable = lib.mkEnableOption "nginx";
+
+  config = lib.mkIf config.zpha.profiles.nginx.enable {
+    common.configure.persist.system.dirs = [ "/var/lib/acme" ];
+
+    sops.secrets.acmeTSIGKey = { };
+
+    security.acme = {
+      acceptTerms = true;
+      defaults = {
+        email = "letsencrypt@zaphyra.eu";
+        keyType = "ec384";
+        dnsProvider = "rfc2136";
+        environmentFile = pkgs.writeText "acme-dns-env" ''
+          RFC2136_NAMESERVER=ns1.fc9f.de
+          RFC2136_TSIG_KEY=acme-nix-${config.networking.hostName}
+          RFC2136_TSIG_ALGORITHM=hmac-sha384
+        '';
+        credentialFiles = {
+          RFC2136_TSIG_SECRET_FILE = config.sops.secrets.acmeTSIGKey.path;
+        };
+      };
+      certs."${config.networking.fqdn}" = {
+        group = lib.mkIf config.services.nginx.enable "nginx";
+        extraDomainNames = lib.pipe config.services.nginx.virtualHosts [
+          (lib.mapAttrsToList (
+            key: config: [
+              (config.serverAliases or [ ])
+              key
+            ]
+          ))
+          lib.flatten
+          (lib.remove config.networking.fqdn)
+          (lib.filter (domain: !(lib.hasSuffix "dn42" domain)))
+        ];
+      };
+    };
+
+    services.nginx = {
+      enable = lib.mkDefault true;
+      recommendedGzipSettings = true;
+      recommendedOptimisation = true;
+      recommendedProxySettings = true;
+      recommendedTlsSettings = true;
+      commonHttpConfig = ''
+        server_names_hash_bucket_size 64;
+        charset utf-8;
+
+        access_log off;
+      '';
+
+      virtualHosts."${config.networking.fqdn}" = {
+        useACMEHost = "${config.networking.fqdn}";
+        forceSSL = true;
+        kTLS = true;
+        default = true;
+      };
+    };
+
+    networking.firewall.allowedTCPPorts = [
+      80
+      443
+    ];
+  };
+
+}
diff --git a/nixosModules/zpha/profiles/resticBackupTarget.nix b/nixosModules/zpha/profiles/resticBackupTarget.nix
@@ -0,0 +1,53 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+let
+  inherit (lib) types;
+  cfg = config.zpha.profiles.resticBackupTarget;
+
+in
+{
+
+  options.zpha.profiles.resticBackupTarget = {
+    enable = lib.mkEnableOption "this machine to be used as restic backup target";
+    path = lib.mkOption {
+      type = types.path;
+    };
+    keys = lib.mkOption {
+      type = with types; attrsOf str;
+      default = { };
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    dns.zones."fc9f.de".subdomains."restic-target".CNAME = lib.mkIf (
+      config.networking.hostName == "cuvier"
+    ) [ "${config.networking.fqdn}." ];
+
+    users = {
+      groups.restic = { };
+      users.restic = {
+        uid = 1002;
+        isNormalUser = true;
+        group = "restic";
+        extraGroups = [
+          "ssh"
+          "nix"
+        ];
+        home = cfg.path;
+        maid.packages = with pkgs; [
+          restic
+          rclone
+        ];
+        openssh.authorizedKeys.keys = lib.mapAttrsToList (
+          repo: key:
+          ''restrict,command="${lib.getExe pkgs.rclone} serve restic --stdio --append-only --verbose ${cfg.path}/${repo}" ${key}''
+        ) cfg.keys;
+      };
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/profiles/zaphyra.nix b/nixosModules/zpha/profiles/zaphyra.nix
@@ -0,0 +1,103 @@
+{
+  npins,
+  pkgs,
+  lib,
+  config,
+  machineConfig,
+  ...
+}:
+
+let
+  cfg = config.zpha.profiles.zaphyra;
+
+in
+{
+
+  options.zpha.profiles.zaphyra.enable = lib.mkEnableOption "zaphyra's defaults";
+
+  imports =
+    (lib.mkIf cfg.enable [
+      "${npins.nixMaid}/src/nixos"
+    ]).content;
+
+  config = lib.mkIf cfg.enable {
+    dns.zones."fc9f.de".subdomains."${config.networking.hostName}" =
+      lib.mkIf (machineConfig ? networking)
+        (
+          let
+            networkCfg = machineConfig.networking;
+          in
+          {
+            AAAA = lib.mkIf ((networkCfg ? ip6Address) && !networkCfg.ip6IsPrivate) [
+              networkCfg.ip6Address
+            ];
+            A = lib.mkIf ((networkCfg ? ip4Address) && !networkCfg.ip4IsPrivate) [
+              networkCfg.ip4Address
+            ];
+          }
+        );
+
+    maid.sharedModules = [
+      ../../../maidModules/environment.nix
+      ../../../maidModules/xdg.nix
+      ../../../maidModules/dbus.nix
+      ../../../maidModules/programs/fish.nix
+      ../../../maidModules/programs/starship.nix
+      ../../../maidModules/programs/git.nix
+      ../../../maidModules/programs/lazygit.nix
+    ];
+
+    users.users = {
+      root = {
+        extraGroups = [ "ssh" ];
+        openssh.authorizedKeys.keys = [
+          (builtins.readFile "${pkgs.zpha.website}/ssh_pubkey.asc")
+        ];
+      };
+      zaphyra.maid = {
+        programs = {
+          starship.enable = true;
+        };
+      };
+    };
+
+    zpha = {
+      profiles.nginx.enable = true;
+      programs = {
+        fish.enable = true;
+        htop.enable = true;
+        ssh.enable = true;
+      };
+    };
+
+    common = {
+      configure = {
+        rootDisk.enable = lib.mkDefault true;
+        persist.system.enable = lib.mkDefault true;
+        persist.home.enable = lib.mkDefault true;
+      };
+
+      profiles = {
+        base.enable = true;
+        minimal.enable = lib.mkDefault true;
+      };
+
+      services.vnstat.enable = true;
+
+      users.zaphyra.enable = true;
+    };
+
+    services = {
+      timesyncd.enable = lib.mkDefault true;
+      fstrim.enable = lib.mkDefault true;
+
+      speechd.enable = false;
+
+      journald.extraConfig = "SystemMaxUse=2.5G";
+
+      # logind.settings.Login.KillUserProcesses = lib.mkDefault true;
+      nginx.appendHttpConfig = ''add_header X-Clacks-Overhead "GNU memdmp" always;'';
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/programs/broot.nix b/nixosModules/zpha/programs/broot.nix
@@ -0,0 +1,54 @@
+{
+  lib,
+  pkgs,
+  config,
+  ...
+}:
+
+{
+
+  options.zpha.programs.broot.enable = lib.mkEnableOption "broot";
+
+  config = lib.mkIf config.zpha.programs.broot.enable {
+    users.users.zaphyra.maid = {
+      packages = [ pkgs.broot ];
+
+      file.xdg_config = {
+        "broot/conf.hjson".text = builtins.toJSON {
+          verbs = [
+            {
+              invocation = "edit";
+              shortcut = "e";
+              key = "enter";
+              apply_to = "text_file";
+              external = "$EDITOR {file:space-separated}";
+              leave_broot = false;
+            }
+            {
+              invocation = "create {subpath}";
+              execution = "$EDITOR {directory}/{subpath}";
+              leave_broot = false;
+            }
+            {
+              invocation = "git_diff";
+              shortcut = "gd";
+              leave_broot = false;
+              execution = "git difftool -y {file}";
+            }
+            {
+              key = "alt-pagedown";
+              internal = "page_down";
+              impacted_panel = "right";
+            }
+            {
+              key = "alt-pageup";
+              internal = "page_up";
+              impacted_panel = "right";
+            }
+          ];
+        };
+      };
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/programs/celluloid.nix b/nixosModules/zpha/programs/celluloid.nix
@@ -0,0 +1,25 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.zpha.programs.celluloid.enable = lib.mkEnableOption "celluloid";
+
+  config = lib.mkIf config.zpha.programs.celluloid.enable {
+    users.users.zaphyra.maid = {
+      packages = [ pkgs.celluloid ];
+      gsettings.settings = {
+        io.github.celluloid-player.celluloid = {
+          always-append-to-playlist = true;
+          draggable-video-area-enable = true;
+          always-autohide-cursor = true;
+        };
+      };
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/programs/chaosctl.nix b/nixosModules/zpha/programs/chaosctl.nix
@@ -0,0 +1,27 @@
+{
+  config,
+  pkgs,
+  lib,
+  ...
+}:
+
+{
+
+  options.zpha.programs.chaosctl.enable = lib.mkEnableOption "chaos-darmstadt related stuff";
+
+  config = lib.mkIf config.zpha.programs.chaosctl.enable {
+    users.users.zaphyra.maid.packages = [
+      pkgs.zpha.chaosctl
+      (pkgs.writeShellScriptBin "cda-door-buzzer" ''
+        export GNUPGHOME=/home/zaphyra/.config/gnupg
+        export SSH_AUTH_SOCK=$(${lib.getExe' pkgs.gnupg "gpgconf"} --list-dirs agent-ssh-socket)
+        ${lib.getExe' pkgs.libnotify "notify-send"} -e "CCCDA-Door" "$(${lib.getExe pkgs.zpha.chaosctl} door buzzer)"
+      '')
+    ];
+
+    zpha.programs.niri.settings.binds = {
+      "Mod+Shift+D".spawn = lib.singleton "cda-door-buzzer";
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/programs/deploymentUtilities.nix b/nixosModules/zpha/programs/deploymentUtilities.nix
@@ -0,0 +1,24 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.zpha.programs.deploymentUtilities.enable = lib.mkEnableOption "deployment utilities";
+
+  config = lib.mkIf config.zpha.programs.deploymentUtilities.enable {
+    users.users.zaphyra.maid.packages = with pkgs; [
+      age
+      ssh-to-age
+
+      sops
+
+      #deploy-rs
+      nh
+    ];
+  };
+
+}
diff --git a/nixosModules/zpha/programs/dino.nix b/nixosModules/zpha/programs/dino.nix
@@ -0,0 +1,22 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.zpha.programs.dino.enable = lib.mkEnableOption "gajim";
+
+  config = lib.mkIf config.zpha.programs.dino.enable {
+    preservation.preserveAt."/persist".users.zaphyra.directories = [
+      ".local/share/dino"
+    ];
+
+    users.users.zaphyra.maid = {
+      packages = [ pkgs.dino ];
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/programs/firefox.nix b/nixosModules/zpha/programs/firefox.nix
@@ -0,0 +1,385 @@
+{
+  config,
+  lib,
+  pkgs,
+  npins,
+  ...
+}:
+
+{
+
+  options.zpha.programs.firefox.enable = lib.mkEnableOption "firefox";
+
+  config = lib.mkIf config.zpha.programs.firefox.enable {
+    preservation.preserveAt."/persist".users.zaphyra.directories = [
+      ".config/mozilla/firefox"
+      ".cache/mozilla/firefox"
+    ];
+
+    users.users.zaphyra.maid = {
+      environment.sessionVariables.BROWSER = "firefox";
+
+      file.xdg_config = {
+        "mozilla/firefox/gnome-theme".source = npins.firefoxGnomeTheme;
+        "mozilla/firefox/zaphyra/chrome/userChrome.css".text = ''
+          @import "../../gnome-theme/theme/gnome-theme.css";
+        '';
+        "mozilla/firefox/zaphyra/chrome/userContent.css".text = ''
+          @import "../../gnome-theme/userContent.css";
+        '';
+        "mozilla/firefox/profiles.ini".text = ''
+          [Profile0]
+          Name=zaphyra
+          IsRelative=1
+          Path=zaphyra
+          Default=1
+
+          [General]
+          StartWithLastProfile=1
+          Version=2
+        '';
+      };
+
+      xdg.mime-apps.defaultApplications = {
+        "x-scheme-handler/http" = "firefox.desktop";
+        "x-scheme-handler/https" = "firefox.desktop";
+        "x-scheme-handler/chrome" = "firefox.desktop";
+
+        "image/svg+xml" = "firefox.desktop";
+
+        "text/html" = "firefox.desktop";
+        "text/xml" = "firefox.desktop";
+      };
+
+      packages = lib.singleton (
+        pkgs.firefox.override (old: {
+          extraPrefsFiles = [
+            (pkgs.writeText "firefox-autoconfig.js" (
+              let
+                uiState = {
+                  currentVersion = 23;
+                  newElementCount = 0;
+                  seen = [ ];
+                  dirtyAreaCache = [ ];
+                  placements = {
+                    widget-overflow-fixed-list = [ ];
+                    toolbar-menubar = [ "menubar-items" ];
+                    vertical-tabs = [
+                      "tabbrowser-tabs"
+                    ];
+                    PersonalToolbar = [ ];
+                    unified-extensions-area = [
+                      "containerise_kinte_sh-browser-action"
+                      "_e9090647-32ff-48e4-9c3c-1361e8fd270e_-browser-action"
+                      "_contain-facebook-browser-action"
+                      "_12cf650b-1822-40aa-bff0-996df6948878_-browser-action"
+                      "_testpilot-containers-browser-action"
+                      "sponsorblocker_ajay_app-browser-action"
+                      "_ublacklist-browser-action"
+                    ];
+                    nav-bar = [
+                      "sidebar-button"
+                      "back-button"
+                      "forward-button"
+                      "stop-reload-button"
+                      "urlbar-container"
+                      "downloads-button"
+                      "_446900e4-71c2-419f-a6a7-df9c091e268b_-browser-action"
+                      "ublock0_raymondhill_net-browser-action"
+                      "unified-extensions-button"
+                    ];
+                    TabsToolbar = [ ];
+                  };
+                };
+              in
+              ''
+                // required for firefox-gnome-theme
+                lockPref("browser.toolbars.bookmarks.visibility", "never");
+                lockPref("browser.tabs.drawInTitlebar", true);
+                lockPref("browser.theme.dark-private-windows", false);
+                lockPref("browser.uidensity", 0);
+                lockPref("svg.context-properties.content.enabled", true);
+                lockPref("toolkit.legacyUserProfileCustomizations.stylesheets", true);
+
+                // enable vertical tabs
+                lockPref("sidebar.verticalTabs", true);
+                lockPref("sidebar.visibility", "expand-on-hover");
+                lockPref("sidebar.main.tools", "");
+                lockPref("sidebar.verticalTabs.dragToPinPromo.dismissed", true);
+
+                pref("browser.uiCustomization.state", "${lib.escape [ "\"" ] (builtins.toJSON uiState)}");
+              ''
+            ))
+          ];
+
+          nativeMessagingHosts = [ ];
+
+          extraPolicies = {
+            SearchEngines = {
+              Default = "Kagi";
+              Add = [
+                {
+                  Name = "Kagi";
+                  URLTemplate = "https://kagi.com/search?q={searchTerms}";
+                  Method = "GET";
+                  IconURL = "https://kagi.com/favicon.png";
+                  Alias = "kagi";
+                  Description = "Search with kagi";
+                  SuggestURLTemplate = "https://kagisuggest.com/api/autosuggest?q={searchTerms}";
+                }
+
+                {
+                  Name = "Noogle";
+                  URLTemplate = "https://noogle.dev/q?term={searchTerms}";
+                  Method = "GET";
+                  IconURL = "https://noogle.dev/favicon.png";
+                  Alias = "noogle";
+                  Description = "Search with noogle";
+                }
+
+                {
+                  Name = "NixOS Options";
+                  URLTemplate = "https://search.nixos.org/options?query={searchTerms}";
+                  Method = "GET";
+                  IconURL = "https://search.nixos.org/favicon.png";
+                  Alias = "nixos";
+                  Description = "Search in NixOS options";
+                }
+
+                {
+                  Name = "Nix Packages";
+                  URLTemplate = "https://search.nixos.org/packages?query={searchTerms}";
+                  Method = "GET";
+                  IconURL = "https://search.nixos.org/favicon.png";
+                  Alias = "nixpkgs";
+                  Description = "Search in nixpkgs";
+                }
+              ];
+            };
+
+            DisableSetDesktopBackground = true;
+
+            # disable tracking bullshit
+            DisableTelemetry = true;
+            DisablePocket = true;
+            DisableFirefoxStudies = true;
+            DisableFeedbackCommands = true;
+
+            # disable password manager
+            PasswordManagerEnabled = false;
+            OfferToSaveLogins = false;
+            PrimaryPassword = false;
+            AutofillCreditCardEnabled = false;
+
+            # disable bookmark-toolbar
+            NoDefaultBookmarks = true;
+            DisplayBookmarksToolbar = "never";
+            DisplayMenuBar = "default-off";
+
+            Homepage.StartPage = "previous-session";
+            Homepage.Locked = true;
+
+            # disable DoH
+            DNSOverHTTPS.Enabled = false;
+            DNSOverHTTPS.Locked = true;
+
+            # enable tracking protection
+            EnableTrackingProtection = {
+              Cryptomining = true;
+              Fingerprinting = true;
+              Value = true;
+              Locked = true;
+            };
+
+            UserMessaging = {
+              ExtensionRecommendations = false;
+              FeatureRecommendations = false;
+              UrlbarInterventions = false;
+              SkipOnboarding = true;
+              MoreFromMozilla = false;
+              FirefoxLabs = false;
+              Locked = true;
+            };
+
+            Handlers.schemes =
+              let
+                handler = {
+                  action = "useHelperApp";
+                  ask = true;
+                  handlers = [
+                    {
+                      name = "VDV PKPass DB Navigator Hook";
+                      path = pkgs.writeShellScript "db-hook" ''
+                        #!/usr/bin/env bash
+
+                        if [[ "$1" == "dbnav:"* ]]; then
+                          url=$(echo -n $1 | base64)
+                          xdg-open "https://vdv-pkpass.magicalcodewit.ch/account/db_login/callback?url=$url"
+                        elif [[ "$1" == "bahnbonus:"* ]]; then
+                          url=$(echo -n $1 | base64)
+                          xdg-open "https://vdv-pkpass.magicalcodewit.ch/account/bahnbonus_login/callback?url=$url"
+                        else
+                          xdg-open "$1"
+                        fi
+                      '';
+                    }
+                  ];
+                };
+              in
+              {
+                dbnav = handler;
+                bahnbonus = handler;
+              };
+
+            Preferences =
+              lib.mapAttrs
+                (_name: value: {
+                  Status = "locked";
+                  Value = value;
+                })
+                {
+                  "browser.uitour.enabled" = false;
+                  # "pinch-to-zoom" zoom with alt+mousewheel
+                  "mousewheel.with_alt.action" = 5;
+
+                  # disable some password-manager/autofill features
+                  "signon.generation.enabled" = false;
+                  "signon.autofillForms" = false;
+                  "signon.firefoxRelay.feature" = "disabled";
+                  "signon.management.page.breach-alerts.enabled" = false;
+
+                  # ask websites to not share or sell data
+                  "privacy.globalprivacycontrol.enabled" = true;
+
+                  # disable AI bullshit
+                  "browser.ml.smartAssist.enabled" = false;
+                  "browser.ml.pageAssist.enabled" = false;
+                  "browser.ml.chat.sidebar" = false;
+                  "browser.ml.chat.shortcuts" = false;
+                  "browser.ml.enable" = false;
+                  "browser.ml.chat.enabled" = false;
+                  "browser.ml.linkPreview.enabled" = false;
+                  "browser.tabs.groups.smart.enabled" = false;
+                  "browser.tabs.groups.smart.userEnabled" = false;
+                  "extensions.ml.enabled" = false;
+
+                  # disable warning on `abou:config`
+                  "browser.aboutConfig.showWarning" = false;
+
+                  # disable telemetry stuff
+                  "browser.ping-centre.telemetry" = false;
+                  "browser.topsites.contile.enabled" = false;
+                  "browser.crashReports.unsubmittedCheck.autoSubmit2" = false;
+
+                  # just open previous session
+                  "browser.startup.page" = 3;
+
+                  # disable trending search suggestions
+                  "browser.urlbar.suggest.trending" = false;
+
+                  # disable 'fancy' newtab page bullshit with tons of tracking and ads
+                  "browser.startup.homepage" = "chrome://browser/content/blanktab.html";
+                  "browser.newtabpage.enabled" = false;
+                  "browser.newtabpage.activity-stream.telemetry" = false;
+                  "browser.newtabpage.activity-stream.showSearch" = false;
+                  "browser.newtabpage.activity-stream.showSponsored" = false;
+                  "browser.newtabpage.activity-stream.showSponsoredTopSites" = false;
+                  "browser.newtabpage.activity-stream.feeds.telemetry" = false;
+                  "browser.newtabpage.activity-stream.feeds.topsites" = false;
+                  "browser.newtabpage.activity-stream.feeds.snippets" = false;
+                  "browser.newtabpage.activity-stream.feeds.system.topsites" = false;
+                  "browser.newtabpage.activity-stream.feeds.section.highlights" = false;
+                  "browser.newtabpage.activity-stream.section.highlights.includePocket" = false;
+                  "browser.newtabpage.activity-stream.section.highlights.includeBookmarks" = false;
+                  "browser.newtabpage.activity-stream.section.highlights.includeDownloads" = false;
+                  "browser.newtabpage.activity-stream.section.highlights.includeVisited" = false;
+                };
+
+            ExtensionSettings =
+              lib.pipe
+                [
+                  # Bitwarden Password Manager
+                  [
+                    "navbar"
+                    "{446900e4-71c2-419f-a6a7-df9c091e268b}"
+                  ]
+                  # uBlock Origin
+                  [
+                    "navbar"
+                    "uBlock0@raymondhill.net"
+                  ]
+                  # uBlacklist
+                  [
+                    "menupanel"
+                    "@ublacklist"
+                  ]
+                  # Facebook Container
+                  [
+                    "menupanel"
+                    "@contain-facebook"
+                  ]
+                  # Google Container
+                  [
+                    "menupanel"
+                    "@contain-google"
+                  ]
+                  # Containerise
+                  [
+                    "menupanel"
+                    "containerise@kinte.sh"
+                  ]
+                  # cookies.txt
+                  [
+                    "menupanel"
+                    "{12cf650b-1822-40aa-bff0-996df6948878}"
+                  ]
+                  # Firefox Multi-Account Containers
+                  [
+                    "menupanel"
+                    "@testpilot-containers"
+                  ]
+                  # IndicateTLS
+                  [
+                    "menupanel"
+                    "{252ee273-8c8d-4609-b54d-62ae345be0a1}"
+                  ]
+                  # IPvFoo
+                  [
+                    "menupanel"
+                    "ipvfoo@pmarks.net"
+                  ]
+                  # Modern for Wikipedia
+                  [
+                    "menupanel"
+                    "{e9090647-32ff-48e4-9c3c-1361e8fd270e}"
+                  ]
+                  # SponsorBlock for YouTube - Skip Sponsorships
+                  [
+                    "menupanel"
+                    "sponsorBlocker@ajay.app"
+                  ]
+                  # German Dictionary
+                  [
+                    "menupanel"
+                    "de-DE@dictionaries.addons.mozilla.org"
+                  ]
+                ]
+                [
+                  (lib.map (config: {
+                    name = lib.elemAt config 1;
+                    value = {
+                      installation_mode = "normal_installed";
+                      default_area = lib.elemAt config 0;
+                      install_url = "https://addons.mozilla.org/firefox/downloads/latest/${lib.elemAt config 1}/latest.xpi";
+                    };
+                  }))
+                  lib.listToAttrs
+                ];
+
+          };
+        })
+      );
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/programs/fish.nix b/nixosModules/zpha/programs/fish.nix
@@ -0,0 +1,21 @@
+{
+  config,
+  lib,
+  ...
+}:
+
+{
+
+  options.zpha.programs.fish.enable = lib.mkEnableOption "fish";
+
+  config = lib.mkIf config.zpha.programs.fish.enable {
+    preservation.preserveAt."/persist".users.zaphyra.files = [
+      ".local/share/fish/fish_history"
+    ];
+
+    users.users.zaphyra.maid = {
+      programs.fish.enable = true;
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/programs/gajim.nix b/nixosModules/zpha/programs/gajim.nix
@@ -0,0 +1,23 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.zpha.programs.gajim.enable = lib.mkEnableOption "gajim";
+
+  config = lib.mkIf config.zpha.programs.gajim.enable {
+    preservation.preserveAt."/persist".users.zaphyra.directories = [
+      ".local/share/gajim"
+      ".config/gajim"
+    ];
+
+    users.users.zaphyra.maid = {
+      packages = [ pkgs.gajim ];
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/programs/ghostty.nix b/nixosModules/zpha/programs/ghostty.nix
@@ -0,0 +1,45 @@
+{
+  lib,
+  pkgs,
+  config,
+  ...
+}:
+let
+  cfg = config.zpha.programs.ghostty;
+  settingsFormat = pkgs.formats.keyValue {
+    listsAsDuplicateKeys = true;
+    mkKeyValue = lib.generators.mkKeyValueDefault { } " = ";
+  };
+
+in
+{
+
+  options.zpha.programs.ghostty = {
+    enable = lib.mkEnableOption "ghostty";
+    settings = lib.mkOption {
+      default = { };
+      type = settingsFormat.type;
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    zpha.programs.ghostty.settings = {
+      background-opacity = 0.8;
+      background-blur = true;
+
+      clipboard-trim-trailing-spaces = true;
+      clipboard-paste-protection = false;
+    };
+
+    users.users.zaphyra.maid = {
+      environment.sessionVariables.TERMINAL = "ghostty";
+
+      packages = [ pkgs.ghostty ];
+
+      file.xdg_config = {
+        "ghostty/config".source = settingsFormat.generate "ghostty.ini" cfg.settings;
+      };
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/programs/git.nix b/nixosModules/zpha/programs/git.nix
@@ -0,0 +1,82 @@
+{
+  lib,
+  config,
+  ...
+}:
+
+{
+
+  options.zpha.programs.git.enable = lib.mkEnableOption "zaphyras git config";
+
+  config = lib.mkIf config.zpha.programs.git.enable {
+    users.users.zaphyra.maid = {
+      programs = {
+        git = {
+          enable = true;
+
+          ignores = [
+            ".DS_Store"
+            "*.swp"
+          ];
+
+          settings = {
+            user = {
+              name = "Katja Ramona Sophie Kwast (zaphyra)";
+              email = "git@zaphyra.eu";
+            };
+
+            aliases = {
+              plog = "log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(dim white)- %an%C(reset)%C(bold yellow)%d%C(reset)' --all";
+              log-gpg = "log --show-signature";
+              fixup = "commit --fixup";
+              fuck = "reset HEAD~1";
+              pfusch = "push --force-with-lease --force-if-includes";
+              yikes = "commit --amend --no-edit";
+            };
+
+            init.defaultBranch = "main";
+
+            diff.algorithm = "histogram";
+
+            commit.verbose = true;
+
+            rerere.enabled = true;
+
+            merge.conflictstyle = "zdiff3";
+
+            rebase = {
+              autosquash = true;
+              autostash = true;
+            };
+
+            push = {
+              default = "current";
+              autoSetupRemote = true;
+            };
+
+            pull.rebase = true;
+
+            transfer.fsckobjects = true;
+            fetch.fsckobjects = true;
+            receive.fsckObjects = true;
+          };
+        };
+
+        lazygit = {
+          enable = true;
+          settings = {
+            disableStartupPopups = true;
+            gui.nerdFontsVersion = "3";
+            git.overrideGpg = true;
+            os.editPreset = "micro";
+          };
+        };
+
+        fish.aliases = {
+          reinitgit = "rm -rf .git && git init && git add -A && git commit -m 'init'";
+        };
+      };
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/programs/gnome-calendar.nix b/nixosModules/zpha/programs/gnome-calendar.nix
@@ -0,0 +1,19 @@
+{
+  config,
+  systemConfig,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.zpha.programs.gnome-calendar.enable = lib.mkEnableOption "gnome-calendar";
+
+  config = lib.mkIf config.zpha.programs.gnome-calendar.enable {
+    zpha.programs.gnome-online-accounts.enable = true;
+
+    users.users.zaphyra.maid.packages = with pkgs; [ gnome-calendar ];
+  };
+
+}
diff --git a/nixosModules/zpha/programs/gnome-online-accounts.nix b/nixosModules/zpha/programs/gnome-online-accounts.nix
@@ -0,0 +1,53 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+  options.zpha.programs.gnome-online-accounts.enable =
+    lib.mkEnableOption "Enable gnome-online-accounts and evolution-data-server";
+
+  config = lib.mkIf config.zpha.programs.gnome-online-accounts.enable (
+    let
+      gnome-online-accounts = pkgs.gnome-online-accounts.overrideAttrs (prevAttrs: {
+        mesonFlags = prevAttrs.mesonFlags ++ [
+          "-Dexchange=false"
+          "-Dgoogle=false"
+          "-Dkerberos=false"
+          "-Downcloud=false"
+          "-Dms_graph=false"
+        ];
+      });
+
+    in
+    {
+      preservation.preserveAt."/persist".users.zaphyra.directories = [
+        ".local/share/evolution"
+        ".config/goa-1.0"
+      ];
+
+      users.users.zaphyra.maid = {
+        dbus.packages = [ gnome-online-accounts ];
+        packages = with pkgs; [
+          gnome-online-accounts-gtk
+          (evolution-data-server-gtk4.override {
+            withGtk4 = true;
+            enableOAuth2 = false;
+          })
+        ];
+        systemd.services.gnome-online-accounts = {
+          unitConfig.Description = "Gnome Online Accounts";
+          serviceConfig = {
+            Type = "dbus";
+            ExecStart = "${gnome-online-accounts}/libexec/goa-daemon";
+            BusName = "org.gnome.OnlineAccounts";
+          };
+        };
+      };
+
+    }
+  );
+
+}
diff --git a/nixosModules/zpha/programs/gnome-text-editor.nix b/nixosModules/zpha/programs/gnome-text-editor.nix
@@ -0,0 +1,30 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.zpha.programs.gnome-text-editor.enable = lib.mkEnableOption "gnome-text-editor";
+
+  config = lib.mkIf config.zpha.programs.gnome-text-editor.enable {
+    users.users.zaphyra.maid = {
+      packages = [ pkgs.gnome-text-editor ];
+
+      # dconf.settings = {
+      #   "org/gnome/TextEditor" = {
+      #     highlight-current-line = true;
+      #     wrap-text = true;
+      #     show-line-numbers = true;
+      #     show-map = true;
+      #     show-right-margin = true;
+      #     indent-style = "space";
+      #     indent-width = 4;
+      #   };
+      # };
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/programs/gpg.nix b/nixosModules/zpha/programs/gpg.nix
@@ -0,0 +1,194 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.zpha.programs.gpg.enable = lib.mkEnableOption "gpg";
+
+  config = lib.mkIf config.zpha.programs.gpg.enable {
+    preservation.preserveAt."/persist".users.zaphyra.directories = lib.singleton {
+      mode = "0700";
+      directory = ".config/gnupg";
+    };
+
+    users.users.zaphyra.maid = {
+      packages = with pkgs; [
+        pcsc-tools
+        gnupg
+      ];
+
+      environment.sessionVariables = {
+        GNUPGHOME = "/home/zaphyra/.config/gnupg";
+      };
+
+      programs.fish.config = ''
+        set -e SSH_AGENT_PID
+
+        begin
+          set -l gnupg_val 0
+          if set -q gnupg_SSH_AUTH_SOCK_by
+            set gnupg_val $gnupg_SSH_AUTH_SOCK_by
+          end
+
+          if test $gnupg_val -ne %self
+            set -x SSH_AUTH_SOCK (${lib.getExe' pkgs.gnupg "gpgconf"} --list-dirs agent-ssh-socket)
+          end
+        end
+      '';
+
+      programs.git.settings = {
+        user.signingKey = "BFE6386C8D66BCD4DAE14FC895F0FE7CD7E6A022";
+        commit.gpgSign = true;
+      };
+
+      file.xdg_config = {
+        "gnupg/gpg.conf".text = ''
+          keyserver "hkps://keyserver.ubuntu.com:443"
+        '';
+        "gnupg/scdaemon.conf".text = ''
+          disable-ccid
+        '';
+        "gnupg/gpg-agent.conf".text = ''
+          enable-ssh-support
+
+          default-cache-ttl 600
+          default-cache-ttl-ssh 600
+
+          pinentry-program "${lib.getExe pkgs.pinentry-gnome3}"
+        '';
+        "gnupg/sshcontrol".text = ''
+          595A90924C65444EF00F0890BA85DE2A57DD79B2
+        '';
+      };
+
+      systemd = {
+        services.gpg-agent = {
+          unitConfig = {
+            Description = "GnuPG cryptographic agent and passphrase cache";
+            Documentation = "man:gpg-agent(1)";
+            Requires = "gpg-agent.socket";
+            After = "gpg-agent.socket";
+            # This is a socket-activated service:
+            RefuseManualStart = true;
+          };
+
+          serviceConfig = {
+            ExecStart = "${lib.getExe' pkgs.gnupg "gpg-agent"} --supervised";
+            ExecReload = "${lib.getExe' pkgs.gnupg "gpgconf"} --reload gpg-agent";
+            Environment = [ "GNUPGHOME=~/.config/gnupg" ];
+          };
+        };
+
+        sockets =
+          let
+            hexStringToBase32 =
+              let
+                mod = a: b: a - a / b * b;
+                pow2 = lib.elemAt [
+                  1
+                  2
+                  4
+                  8
+                  16
+                  32
+                  64
+                  128
+                  256
+                ];
+
+                base32Alphabet = lib.stringToCharacters "ybndrfg8ejkmcpqxot1uwisza345h769";
+                hexToIntTable = lib.listToAttrs (
+                  lib.genList (x: {
+                    name = lib.toLower (lib.toHexString x);
+                    value = x;
+                  }) 16
+                );
+
+                initState = {
+                  ret = "";
+                  buf = 0;
+                  bufBits = 0;
+                };
+                go =
+                  {
+                    ret,
+                    buf,
+                    bufBits,
+                  }:
+                  hex:
+                  let
+                    buf' = buf * pow2 4 + hexToIntTable.${hex};
+                    bufBits' = bufBits + 4;
+                    extraBits = bufBits' - 5;
+                  in
+                  if bufBits >= 5 then
+                    {
+                      ret = ret + lib.elemAt base32Alphabet (buf' / pow2 extraBits);
+                      buf = mod buf' (pow2 extraBits);
+                      bufBits = bufBits' - 5;
+                    }
+                  else
+                    {
+                      inherit ret;
+                      buf = buf';
+                      bufBits = bufBits';
+                    };
+              in
+              hexString: (lib.foldl' go initState (lib.stringToCharacters hexString)).ret;
+
+            gpgconf =
+              dir:
+              let
+                hash = lib.substring 0 24 (
+                  hexStringToBase32 (builtins.hashString "sha1" "/home/zaphyra/.config/gnupg")
+                );
+                subdir = "d.${hash}/${dir}";
+              in
+              "%t/gnupg/${subdir}";
+            mkSocket =
+              {
+                desc,
+                docs,
+                stream,
+                fdName,
+              }:
+              {
+                wantedBy = [ "sockets.target" ];
+                unitConfig = {
+                  Description = desc;
+                  Documentation = docs;
+                };
+                socketConfig = {
+                  ListenStream = gpgconf "${stream}";
+                  FileDescriptorName = fdName;
+                  Service = "gpg-agent.service";
+                  SocketMode = "0600";
+                  DirectoryMode = "0700";
+                };
+              };
+          in
+          {
+            gpg-agent = mkSocket {
+              desc = "GnuPG cryptographic agent and passphrase cache";
+              docs = "man:gpg-agent(1)";
+              stream = "S.gpg-agent";
+              fdName = "std";
+            };
+
+            gpg-agent-ssh = mkSocket {
+              desc = "GnuPG cryptographic agent (ssh-agent emulation)";
+              docs = "man:gpg-agent(1) man:ssh-add(1) man:ssh-agent(1) man:ssh(1)";
+              stream = "S.gpg-agent.ssh";
+              fdName = "ssh";
+            };
+          };
+      };
+    };
+
+  };
+
+}
diff --git a/nixosModules/zpha/programs/htop.nix b/nixosModules/zpha/programs/htop.nix
@@ -0,0 +1,42 @@
+{
+  lib,
+  pkgs,
+  config,
+  ...
+}:
+let
+  cfg = config.zpha.programs.htop;
+  settingsFormat = pkgs.formats.keyValue {
+    listsAsDuplicateKeys = true;
+    mkKeyValue = lib.generators.mkKeyValueDefault { } "=";
+  };
+
+in
+{
+
+  options.zpha.programs.htop = {
+    enable = lib.mkEnableOption "htop";
+    settings = lib.mkOption {
+      default = { };
+      type = settingsFormat.type;
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    zpha.programs.htop.settings = {
+      hide_userland_threads = 1;
+      tree_view = 1;
+      show_program_path = 0;
+      show_cpu_frequency = 1;
+    };
+
+    users.users.zaphyra.maid = {
+      packages = [ (pkgs.htop.override { sensorsSupport = true; }) ];
+
+      file.xdg_config = {
+        "htop/htoprc".source = settingsFormat.generate "htoprc" cfg.settings;
+      };
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/programs/loupe.nix b/nixosModules/zpha/programs/loupe.nix
@@ -0,0 +1,44 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+{
+
+  options.zpha.programs.loupe.enable = lib.mkEnableOption "loupe image viewer";
+
+  config = lib.mkIf config.zpha.programs.loupe.enable {
+    users.users.zaphyra.maid = {
+      packages = [ pkgs.loupe ];
+
+      xdg.mime-apps = {
+        defaultApplications = lib.genAttrs [
+          "image/jpeg"
+          "image/png"
+          "image/gif"
+          "image/webp"
+          "image/tiff"
+          "mage/bmp"
+          "image/svg+xml"
+          "image/svg+xml-compressed"
+          "image/avif"
+          "image/heic"
+        ] (_name: [ "org.gnome.Loupe.desktop" ]);
+        addedAssociations = lib.genAttrs [
+          "image/jpeg"
+          "image/png"
+          "image/gif"
+          "image/webp"
+          "image/tiff"
+          "mage/bmp"
+          "image/svg+xml"
+          "image/svg+xml-compressed"
+          "image/avif"
+          "image/heic"
+        ] (_name: [ "org.gnome.Loupe.desktop" ]);
+      };
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/programs/micro.nix b/nixosModules/zpha/programs/micro.nix
@@ -0,0 +1,216 @@
+{
+  lib,
+  pkgs,
+  config,
+  ...
+}:
+let
+  inherit (lib) types;
+  cfg = config.zpha.programs.micro;
+
+in
+{
+
+  options.zpha.programs.micro = {
+    enable = lib.mkEnableOption "micro editor";
+    lsp-servers = lib.mkOption {
+      default = { };
+      type = types.attrsOf (
+        types.submodule {
+          options = {
+            command = lib.mkOption { type = types.str; };
+            options = lib.mkOption {
+              inherit ((pkgs.formats.json { })) type;
+              default = { };
+            };
+          };
+        }
+      );
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+
+    users.users.zaphyra.maid = {
+      environment.sessionVariables.EDITOR = "micro";
+
+      packages = [ pkgs.micro ];
+
+      file.xdg_config = {
+        # main config
+        "micro/settings.json".text = builtins.toJSON {
+          "*.nix".tabstospaces = true;
+          "*.nix".tabmovement = true;
+          "*.nix".tabsize = 2;
+
+          "*.nim".tabstospaces = true;
+          "*.nim".tabmovement = true;
+          "*.nim".tabsize = 2;
+
+          clipboard = "terminal";
+
+          scrollbar = true;
+          eofnewline = false;
+          wordwrap = true;
+          softwrap = true;
+
+          ignorecase = true;
+          savehistory = false;
+
+          "lsp.formatOnSave" = true;
+          "lsp.tabcompletion" = true;
+          "lsp.autocompleteDetails" = false;
+          "lsp.server" = lib.concatMapAttrsStringSep "," (
+            name: value:
+            lib.concatStringsSep "=" (
+              [
+                name
+                value.command
+              ]
+              ++ (lib.optionals (lib.hasAttr "options" value) [
+                (builtins.toJSON value.options)
+              ])
+            )
+          ) cfg.lsp-servers;
+        };
+
+        # keybindings
+        "micro/bindings.json".text = builtins.toJSON {
+          "Alt-," = "PreviousTab";
+          "Alt-." = "NextTab";
+          "Alt-Backspace" = "DeleteWordLeft";
+          "Alt-CtrlH" = "DeleteWordLeft";
+          "Alt-F" = "FindLiteral";
+          "Alt-[" = "DiffPrevious|CursorStart";
+          "Alt-]" = "DiffNext|CursorEnd";
+          "Alt-a" = "StartOfLine";
+          "Alt-b" = "WordLeft";
+          "Alt-c" = "RemoveAllMultiCursors";
+          "Alt-e" = "EndOfLine";
+          "Alt-f" = "WordRight";
+          "Alt-g" = "ToggleKeyMenu";
+          "Alt-m" = "SpawnMultiCursorSelect";
+          "Alt-n" = "SpawnMultiCursor";
+          "Alt-p" = "RemoveMultiCursor";
+          "Alt-x" = "SkipMultiCursor";
+          "Alt-{" = "ParagraphPrevious";
+          "Alt-}" = "ParagraphNext";
+          "AltDown" = "MoveLinesDown";
+          "AltLeft" = "StartOfTextToggle";
+          "AltRight" = "EndOfLine";
+          "AltShiftDown" = "SpawnMultiCursorDown";
+          "AltShiftLeft" = "SelectToStartOfTextToggle";
+          "AltShiftRight" = "SelectToEndOfLine";
+          "AltShiftUp" = "SpawnMultiCursorUp";
+          "AltUp" = "MoveLinesUp";
+          "Backspace" = "Backspace";
+          "Backtab" = "OutdentSelection|OutdentLine";
+          "Ctrl-MouseLeft" = "MouseMultiCursor";
+          "Ctrl-a" = "SelectAll";
+          "Ctrl-b" = "ShellMode";
+          "Ctrl-c" = "Copy|CopyLine";
+          "Ctrl-d" = "DuplicateLine";
+          "Ctrl-e" = "CommandMode";
+          "Ctrl-f" = "Find";
+          "Ctrl-g" = "ToggleHelp";
+          "Ctrl-h" = "Backspace";
+          "Ctrl-j" = "PlayMacro";
+          "Ctrl-k" = "CutLine";
+          "Ctrl-l" = "command-edit:goto ";
+          "Ctrl-n" = "FindNext";
+          "Ctrl-o" = "OpenFile";
+          "Ctrl-p" = "FindPrevious";
+          "Ctrl-q" = "Quit";
+          "Ctrl-r" = "ToggleRuler";
+          "Ctrl-s" = "Save";
+          "Ctrl-t" = "AddTab";
+          "Ctrl-u" = "ToggleMacro";
+          "Ctrl-v" = "Paste";
+          "Ctrl-w" = "NextSplit";
+          "Ctrl-x" = "Cut|CutLine";
+          "Ctrl-y" = "Redo";
+          "Ctrl-z" = "Undo";
+          "CtrlDown" = "CursorEnd";
+          "CtrlEnd" = "CursorEnd";
+          "CtrlHome" = "CursorStart";
+          "CtrlPageDown" = "NextTab";
+          "CtrlPageUp" = "PreviousTab";
+          "AltPageDown" = "NextTab";
+          "AltPageUp" = "PreviousTab";
+          "CtrlShiftDown" = "SelectToEnd";
+          "CtrlShiftLeft" = "SelectWordLeft";
+          "CtrlShiftRight" = "SelectWordRight";
+          "CtrlShiftUp" = "SelectToStart";
+          "CtrlUp" = "CursorStart";
+          "Delete" = "Delete";
+          "Down" = "CursorDown";
+          "End" = "EndOfLine";
+          "Enter" = "InsertNewline";
+          "Esc" = "Escape";
+          "F10" = "Quit";
+          "F2" = "Save";
+          "F3" = "Find";
+          "F4" = "Quit";
+          "F7" = "Find";
+          "Home" = "StartOfText";
+          "Insert" = "ToggleOverwriteMode";
+          "Left" = "CursorLeft";
+          "MouseLeft" = "MousePress";
+          "MouseLeftDrag" = "MouseDrag";
+          "MouseLeftRelease" = "MouseRelease";
+          "MouseMiddle" = "PastePrimary";
+          "MouseWheelDown" = "ScrollDown";
+          "MouseWheelUp" = "ScrollUp";
+          "PageDown" = "CursorPageDown";
+          "PageUp" = "CursorPageUp";
+          "Right" = "CursorRight";
+          "ShiftDown" = "SelectDown";
+          "ShiftEnd" = "SelectToEndOfLine";
+          "ShiftHome" = "SelectToStartOfTextToggle";
+          "ShiftLeft" = "SelectLeft";
+          "ShiftPageDown" = "SelectPageDown";
+          "ShiftPageUp" = "SelectPageUp";
+          "ShiftRight" = "SelectRight";
+          "ShiftUp" = "SelectUp";
+          "Tab" = "Autocomplete|IndentSelection|InsertTab";
+          "Up" = "CursorUp";
+
+          # plugins
+          "Alt-/" = "lua:comment.comment";
+          "Alt-d" = "command:definition";
+          #      "Alt-f" = "command:format";
+          "Alt-k" = "command:hover";
+          "Alt-r" = "command:references";
+          "CtrlSpace" = "command:lspcompletion";
+          "CtrlUnderscore" = "lua:comment.comment";
+          "F1" = "command:cheat";
+          #      "F4" = "command:jumptag";
+          "F5" = "lua:wc.wordCount";
+        };
+
+        # plugins
+        "micro/plug/lsp".source = pkgs.fetchFromGitHub {
+          owner = "AndCake";
+          repo = "micro-plugin-lsp";
+          rev = "v0.6.3";
+          sha256 = "sha256-rZ9Vw9WPGNaJBGHKU40F6cBIYQ1JFtSKPDrheazKkPY=";
+        };
+
+        "micro/plug/detectindent".source = pkgs.fetchFromGitHub {
+          owner = "dmaluka";
+          repo = "micro-detectindent";
+          rev = "v1.1.0";
+          sha256 = "sha256-5bKEkOnhz0pyBR2UNw5vvYiTtpd96fBPTYW9jnETvq4=";
+        };
+
+        "micro/plug/wc".source = pkgs.fetchFromGitHub {
+          owner = "adamnpeace";
+          repo = "micro-wc-plugin";
+          rev = "b2c9957e521770eadc1ecae9d54c0a30f40a0a3d";
+          sha256 = "sha256-Z6MC2cet8+7XHv41G+SlAZViCqlh/9dk0CSt7HklnTg=";
+        };
+      };
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/programs/nautilus.nix b/nixosModules/zpha/programs/nautilus.nix
@@ -0,0 +1,69 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.zpha.programs.nautilus.enable = lib.mkEnableOption "nautilus";
+
+  config = lib.mkIf config.zpha.programs.nautilus.enable {
+
+    users.users.zaphyra.maid = {
+      packages = with pkgs; [
+        nautilus
+        sushi # quick-preview for nautilus
+      ];
+
+      environment.sessionVariables = {
+        NAUTILUS_4_EXTENSION_DIR = "/etc/profiles/per-user/zaphyra/lib/nautilus/extensions-4";
+      };
+
+      gsettings.settings = {
+        org.gnome.nautilus = {
+          preferences = {
+            date-time-format = "detailed";
+            default-folder-viewer = "list-view";
+            show-delete-permanently = true;
+          };
+          list-view = {
+            use-tree-view = true;
+            default-zoom-level = "small";
+          };
+        };
+      };
+
+      xdg.mime-apps.defaultApplications = lib.genAttrs [
+        "inode/directory"
+        "application/x-7z-compressed"
+        "application/x-7z-compressed-tar"
+        "application/x-bzip"
+        "application/x-bzip-compressed-tar"
+        "application/x-compress"
+        "application/x-compressed-tar"
+        "application/x-cpio"
+        "application/x-gzip"
+        "application/x-lha"
+        "application/x-lzip"
+        "application/x-lzip-compressed-tar"
+        "application/x-lzma"
+        "application/x-lzma-compressed-tar"
+        "application/x-tar"
+        "application/x-tarz"
+        "application/x-xar"
+        "application/x-xz"
+        "application/x-xz-compressed-tar"
+        "application/zip"
+        "application/gzip"
+        "application/bzip2"
+        "application/x-bzip2-compressed-tar"
+        "application/vnd.rar;application/zstd"
+        "application/x-zstd-compressed-tar"
+      ] (_name: [ "org.gnome.Nautilus.desktop" ]);
+    };
+
+  };
+
+}
diff --git a/nixosModules/zpha/programs/niri/enable.nix b/nixosModules/zpha/programs/niri/enable.nix
@@ -0,0 +1,65 @@
+{
+  lib,
+  pkgs,
+  config,
+  npins,
+  ...
+}:
+let
+  inherit (lib) types;
+  cfg = config.zpha.programs.niri;
+
+  niri-nix = import "${npins.niri-nix}/lib" {
+    self = npins.niri-nix;
+    inherit lib;
+    nixpkgs.legacyPackages."${pkgs.stdenv.hostPlatform.system}" = pkgs;
+  };
+in
+{
+
+  options.zpha.programs.niri = {
+    enable = lib.mkEnableOption "niri";
+    settings = lib.mkOption {
+      type =
+        with lib.types;
+        let
+          valueType =
+            nullOr (oneOf [
+              bool
+              int
+              float
+              str
+              path
+              (attrsOf valueType)
+              (listOf valueType)
+            ])
+            // {
+              description = "Niri configuration value";
+            };
+        in
+        types.submodule {
+          freeformType = valueType;
+        };
+      default = { };
+    };
+
+    extraConfig = lib.mkOption {
+      type = types.lines;
+      default = "";
+    };
+
+    finalConfig = lib.mkOption {
+      type = types.lines;
+      default = (niri-nix.mkNiriKDL cfg.settings) + "\n" + cfg.extraConfig;
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    common.programs.niri.enable = true;
+    users.users.zaphyra.maid = {
+      file.xdg_config."niri/config.kdl".source =
+        niri-nix.validatedConfigFor config.programs.niri.package cfg.finalConfig;
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/programs/niri/gtk.nix b/nixosModules/zpha/programs/niri/gtk.nix
@@ -0,0 +1,58 @@
+{
+  lib,
+  config,
+  pkgs,
+  ...
+}:
+{
+
+  config = lib.mkIf config.zpha.programs.niri.enable {
+    users.users.zaphyra.maid = {
+      packages = with pkgs; [
+        adwaita-icon-theme
+        zpha.adwaita-colors-icon-theme
+      ];
+
+      environment.sessionVariables = {
+        GTK_IM_MODULE = "simple";
+      };
+
+      gsettings.settings = {
+        org.gnome.desktop = {
+          interface = {
+            color-scheme = "prefer-dark";
+            font-name = "Adwaita Sans 11";
+            accent-color = "green";
+            gtk-theme = "Adwaita";
+            icon-theme = "Adwaita-green";
+            cursor-theme = "Adwaita";
+          };
+          wm.preferences = {
+            button-layout = "";
+          };
+        };
+        org.gtk.Settings.Debug = {
+          enable-inspector-keybinding = true;
+        };
+      };
+
+      file.xdg_config."gtk-3.0/settings.ini".text = lib.generators.toINI { } {
+        Settings =
+          let
+            gnomeSettings = config.users.users.zaphyra.maid.gsettings.settings.org.gnome.desktop.interface;
+          in
+          {
+            gtk-application-prefer-dark-theme = gnomeSettings.color-scheme == "prefer-dark";
+            gtk-theme-name = gnomeSettings.gtk-theme;
+            gtk-icon-theme-name = gnomeSettings.icon-theme;
+            gtk-cursor-theme-name = gnomeSettings.cursor-theme;
+            gtk-font-name = gnomeSettings.font-name;
+          };
+      };
+    };
+
+    zpha.programs.niri.settings.cursor.xcursor-theme =
+      config.users.users.zaphyra.maid.gsettings.settings.org.gnome.desktop.interface.cursor-theme;
+  };
+
+}
diff --git a/nixosModules/zpha/programs/niri/packages.nix b/nixosModules/zpha/programs/niri/packages.nix
@@ -0,0 +1,17 @@
+{
+  lib,
+  config,
+  pkgs,
+  ...
+}:
+{
+
+  config = lib.mkIf config.zpha.programs.niri.enable {
+    users.users.zaphyra.maid.packages = with pkgs; [
+      xdg-utils
+      wl-clipboard-rs
+      adw-gtk3
+    ];
+  };
+
+}
diff --git a/nixosModules/zpha/programs/niri/rules.nix b/nixosModules/zpha/programs/niri/rules.nix
@@ -0,0 +1,121 @@
+{ config, lib, ... }:
+
+{
+
+  config = lib.mkIf config.zpha.programs.niri.enable {
+    zpha.programs.niri.settings = {
+      layer-rule = [
+        {
+          match = [
+            {
+              _props = {
+                namespace = "shaderbg";
+              };
+            }
+          ];
+          place-within-backdrop = true;
+        }
+      ];
+
+      window-rule = [
+        {
+          clip-to-geometry = true;
+          geometry-corner-radius = 6.0;
+        }
+        {
+          match = [
+            {
+              _props = {
+                is-floating = true;
+              };
+            }
+          ];
+          shadow.on = [ ];
+        }
+        {
+          match = [
+            {
+              _props = {
+                at-startup = true;
+                app-id = "^org.gnome.Fractal$";
+              };
+            }
+            {
+              _props = {
+                at-startup = true;
+                app-id = "^im.dino.Dino$";
+              };
+            }
+            {
+              _props = {
+                at-startup = true;
+                app-id = "^org.gajim.Gajim$";
+              };
+            }
+          ];
+
+          default-column-display = "tabbed";
+          open-on-workspace = "chat";
+        }
+        {
+          match = [
+            {
+              _props = {
+                app-id = "firefox$";
+              };
+            }
+            {
+              _props = {
+                app-id = "thunderbird$";
+              };
+            }
+          ];
+
+          open-maximized = true;
+        }
+        {
+          match = [
+            {
+              _props = {
+                app-id = "^org.gnome.NautilusPreviewer$";
+              };
+            }
+          ];
+
+          open-floating = true;
+        }
+        {
+          match = [
+            {
+              _props = {
+                app-id = "^thunderbird$";
+              };
+            }
+          ];
+
+          open-on-workspace = "mail";
+        }
+        {
+          match = [
+            {
+              _props = {
+                app-id = "firefox$";
+                title = "^Picture-in-Picture$";
+              };
+            }
+          ];
+
+          open-floating = true;
+          default-column-width.fixed = 480;
+          default-window-height.fixed = 270;
+          default-floating-position._props = {
+            x = 32;
+            y = 32;
+            relative-to = "bottom-right";
+          };
+        }
+      ];
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/programs/niri/settings.nix b/nixosModules/zpha/programs/niri/settings.nix
@@ -0,0 +1,220 @@
+{
+  lib,
+  config,
+  pkgs,
+  ...
+}:
+{
+
+  config = lib.mkIf config.zpha.programs.niri.enable {
+    zpha.programs.niri.settings = {
+      environment = {
+        QT_QPA_PLATFORM = "wayland";
+        QT_WAYLAND_FORCE_DPI = "physical";
+        QT_WAYLAND_DISABLE_WINDOWDECORATION = "1";
+      };
+
+      workspace = [
+        { _args = [ "browser" ]; }
+        { _args = [ "mail" ]; }
+        { _args = [ "terminal" ]; }
+        { _args = [ "chat" ]; }
+      ];
+
+      prefer-no-csd = [ ];
+
+      gestures.hot-corners = [ ];
+
+      layout = {
+        always-center-single-column = true;
+        gaps = 15;
+        background-color = "transparent";
+        focus-ring = {
+          width = 2;
+          inactive-color = "rgba(59, 135, 86, .9)";
+          active-color = "rgba(74, 169, 108, .9)";
+        };
+
+        tab-indicator = {
+          gaps-between-tabs = 10;
+          position = "top";
+        };
+      };
+
+      input = {
+        workspace-auto-back-and-forth = [ ];
+
+        focus-follows-mouse._props = {
+          max-scroll-amount = "0%";
+        };
+
+        touchpad = {
+          natural-scroll = [ ];
+          click-method = "clickfinger";
+        };
+
+        keyboard.xkb = {
+          layout = "us,de,ru";
+          variant = "mac,qwerty,mac";
+          options = "grp:alt_shift_toggle";
+        };
+      };
+
+      binds =
+        (lib.mergeAttrsList (
+          lib.map
+            (num: {
+              "Mod+${toString num}" = lib.mkDefault { focus-workspace = num; };
+              "Mod+Shift+${toString num}" = lib.mkDefault {
+                move-column-to-workspace = num;
+              };
+            })
+            [
+              1
+              2
+              3
+              4
+              5
+              6
+              7
+              8
+              9
+            ]
+        ))
+        // {
+          "Mod+1".focus-workspace = "browser";
+          "Mod+Shift+1".move-column-to-workspace = "browser";
+          "Mod+2".focus-workspace = "mail";
+          "Mod+Shift+2".move-column-to-workspace = "mail";
+          "Mod+3".focus-workspace = "terminal";
+          "Mod+Shift+3".move-column-to-workspace = "terminal";
+          "Mod+4".focus-workspace = "chat";
+          "Mod+Shift+4".move-column-to-workspace = "chat";
+
+          "Mod+Shift+E".quit = [ ];
+          "Mod+Z".power-off-monitors = [ ];
+
+          "Mod+Space".spawn = lib.mkDefault [ (lib.getExe pkgs.fuzzel) ];
+          "Mod+Return".spawn = [
+            (lib.getExe pkgs.unstable.app2unit)
+            "-T"
+          ];
+          "Mod+L".spawn = [
+            (lib.getExe' pkgs.systemd "loginctl")
+            "lock-session"
+          ];
+
+          "Mod+W" = {
+            _props.repeat = false;
+            close-window = [ ];
+          };
+
+          "XF86AudioMute" = {
+            _props.allow-when-locked = true;
+            spawn = lib.mkDefault [
+              "wpctl"
+              "set-mute"
+              "@DEFAULT_AUDIO_SINK@"
+              "toggle"
+            ];
+          };
+          "XF86AudioRaiseVolume" = {
+            _props.allow-when-locked = true;
+            spawn = lib.mkDefault [
+              "wpctl"
+              "set-volume"
+              "@DEFAULT_AUDIO_SINK@"
+              ".5%+"
+            ];
+          };
+          "XF86AudioLowerVolume" = {
+            _props.allow-when-locked = true;
+            spawn = lib.mkDefault [
+              "wpctl"
+              "set-volume"
+              "@DEFAULT_AUDIO_SINK@"
+              ".5%-"
+            ];
+          };
+
+          "XF86MonBrightnessUp" = {
+            _props.allow-when-locked = true;
+            spawn = lib.mkDefault [
+              (lib.getExe pkgs.brightnessctl)
+              "s"
+              "+2%"
+            ];
+          };
+          "XF86MonBrightnessDown" = {
+            _props.allow-when-locked = true;
+            spawn = lib.mkDefault [
+              (lib.getExe pkgs.brightnessctl)
+              "s"
+              "2%-"
+            ];
+          };
+
+          "Mod+Tab".toggle-overview = [ ];
+
+          "Mod+Prior".focus-workspace-up = [ ];
+          "Mod+Next".focus-workspace-down = [ ];
+          "Mod+WheelScrollDown" = {
+            _props.cooldown-ms = 150;
+            focus-workspace-down = [ ];
+          };
+          "Mod+WheelScrollUp" = {
+            _props.cooldown-ms = 150;
+            focus-workspace-up = [ ];
+          };
+          "Mod+WheelScrollRight".focus-column-right = [ ];
+          "Mod+WheelScrollLeft".focus-column-left = [ ];
+
+          "Mod+Shift+Prior".move-window-up-or-to-workspace-up = [ ];
+          "Mod+Shift+Next".move-window-down-or-to-workspace-down = [ ];
+
+          "Mod+Ctrl+Left".focus-monitor-left = [ ];
+          "Mod+Ctrl+Right".focus-monitor-right = [ ];
+
+          "Mod+Left".focus-column-left = [ ];
+          "Mod+Right".focus-column-right = [ ];
+          "Mod+Up".focus-window-or-workspace-up = [ ];
+          "Mod+Down".focus-window-or-workspace-down = [ ];
+
+          "Mod+Shift+Left".move-column-left = [ ];
+          "Mod+Shift+Right".move-column-right = [ ];
+          "Mod+Shift+Up".move-window-up = [ ];
+          "Mod+Shift+Down".move-window-down = [ ];
+
+          "Mod+R".switch-preset-column-width = [ ];
+          "Mod+Shift+R".switch-preset-window-height = [ ];
+          "Mod+Ctrl+R".reset-window-height = [ ];
+
+          "Mod+C".center-window = [ ];
+
+          "Mod+F".maximize-column = [ ];
+          "Mod+Ctrl+F".expand-column-to-available-width = [ ];
+          "Mod+Shift+F".fullscreen-window = [ ];
+          "Mod+Alt+F".set-column-width = "50%";
+
+          "Mod+Minus".set-column-width = "-10%";
+          "Mod+Equal".set-column-width = "+10%";
+          "Mod+Shift+Minus".set-window-height = "-10%";
+          "Mod+Shift+Equal".set-window-height = "+10%";
+
+          "Mod+Shift+Space".toggle-window-floating = [ ];
+
+          "Mod+T".toggle-column-tabbed-display = [ ];
+
+          "Mod+Print".spawn = [
+            (lib.getExe pkgs.hyprpicker)
+            "--lowercase-hex"
+            "--autocopy"
+          ];
+
+          "Print".screenshot = [ ];
+          "Shift+Print".screenshot-window = [ ];
+        };
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/programs/nixUtilities.nix b/nixosModules/zpha/programs/nixUtilities.nix
@@ -0,0 +1,34 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.zpha.programs.nixUtilities.enable = lib.mkEnableOption "useful nix utilities";
+
+  config = lib.mkIf config.zpha.programs.nixUtilities.enable {
+    users.users.zaphyra.maid.packages = with pkgs; [
+      unstable.npins
+      nixfmt-rfc-style
+      nixfmt-tree
+      nixd
+      statix
+      deadnix
+      nix-output-monitor
+    ];
+
+    zpha.programs.micro.lsp-servers.nix = {
+      command = lib.getExe pkgs.nixd;
+      options = {
+        config = builtins.toJSON {
+          formatting.command = [ (lib.getExe pkgs.nixfmt-rfc-style) ];
+        };
+      };
+    };
+
+  };
+
+}
diff --git a/nixosModules/zpha/programs/papers.nix b/nixosModules/zpha/programs/papers.nix
@@ -0,0 +1,27 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.zpha.programs.papers.enable = lib.mkEnableOption "papers pdf viewer";
+
+  config = lib.mkIf config.zpha.programs.papers.enable {
+    users.users.zaphyra.maid = {
+      packages = [ pkgs.papers ];
+
+      xdg.mime-apps = {
+        defaultApplications = {
+          "application/pdf" = [ "org.gnome.Papers.desktop" ];
+        };
+        addedAssociations = {
+          "application/pdf" = [ "org.gnome.Papers.desktop" ];
+        };
+      };
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/programs/pdfarranger.nix b/nixosModules/zpha/programs/pdfarranger.nix
@@ -0,0 +1,22 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.zpha.programs.pdfarranger.enable = lib.mkEnableOption "pdfarranger";
+
+  config = lib.mkIf config.zpha.programs.pdfarranger.enable {
+    users.users.zaphyra.maid = {
+      packages = [ pkgs.pdfarranger ];
+
+      xdg.mime-apps.addedAssociations = {
+        "application/pdf" = [ "com.github.jeromerobert.pdfarranger.desktop" ];
+      };
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/programs/shaderbg.nix b/nixosModules/zpha/programs/shaderbg.nix
@@ -0,0 +1,37 @@
+{
+  resources,
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.zpha.programs.shaderbg.enable = lib.mkEnableOption "shaderbg";
+
+  config = lib.mkIf config.zpha.programs.shaderbg.enable {
+    users.users.zaphyra.maid = {
+      packages = [ pkgs.shaderbg ];
+
+      systemd.services.shaderbg = {
+        description = "A live wallpaper program for Sway and other compositors with wlr-layer-shell support.";
+        after = [ "niri-session.target" ];
+        partOf = [ "niri-session.target" ];
+        wantedBy = [ "niri-session.target" ];
+
+        unitConfig.ConditionEnvironment = "WAYLAND_DISPLAY";
+
+        serviceConfig = {
+          Restart = "on-failure";
+          ExecStart = lib.escapeShellArgs [
+            (lib.getExe pkgs.zpha.shaderbg)
+            "*"
+            resources.shaders.background1
+          ];
+        };
+      };
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/programs/shellUtilities.nix b/nixosModules/zpha/programs/shellUtilities.nix
@@ -0,0 +1,94 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.zpha.programs.shellUtilities.enable = lib.mkEnableOption "useful shell-utilities";
+
+  config = lib.mkIf config.zpha.programs.shellUtilities.enable {
+    preservation.preserveAt."/persist".users.zaphyra.directories = [
+      ".local/share/zoxide"
+      ".local/share/mcfly"
+    ];
+
+    users.users.zaphyra.maid = {
+      packages = with pkgs; [
+        zoxide
+        eza
+        bat
+        ripgrep
+        fd
+
+        pwgen
+
+        (pkgs.writeShellScriptBin "use" ''
+          declare -a all
+          for p in "$@"; do
+            all+=("''${NIXPKGS_PATH}#$p")
+          done
+          eval nix shell ''${all[@]}
+        '')
+
+        mcfly
+        mcfly-fzf
+        fzf
+
+        wget
+        curl
+        rsync
+
+        fx
+        jq
+        bc
+        file
+
+        trash-cli
+
+        zpha.nix-cleanup
+      ];
+
+      programs.fish = {
+        config = ''
+          zoxide init fish | source
+          mcfly init fish | source
+          mcfly-fzf init fish | source
+        '';
+
+        aliases = {
+          ".." = "cd ..";
+          "..." = "cd ../..";
+
+          cd = "z";
+          cdi = "zi";
+
+          ls = "eza";
+          ll = "eza -l";
+          la = "eza -a";
+          lla = "eza -la";
+          tree = "eza --tree -a";
+
+          cat = "bat -pp";
+          less = "bat";
+          rm = "trash-put";
+
+          diff = "diff --color";
+          ip = "ip --color=auto";
+
+          killall = "pkill";
+
+          grep = "rg";
+          find = "fd";
+
+          wget = "wget --hsts-file=\"~/.local/state/wget-hsts\"";
+
+          zzz = "sleep 1 && systemctl suspend";
+        };
+      };
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/programs/ssh.nix b/nixosModules/zpha/programs/ssh.nix
@@ -0,0 +1,37 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.zpha.programs.ssh.enable = lib.mkEnableOption "ssh";
+
+  config = lib.mkIf config.zpha.programs.ssh.enable {
+    preservation.preserveAt."/persist".users.zaphyra.directories = [
+      {
+        directory = ".ssh";
+        mode = "0700";
+      }
+    ];
+
+    users.users.zaphyra.maid = {
+      packages = [ pkgs.openssh ];
+
+      file.home.".ssh/config".text = ''
+        Host *
+          Compression yes
+          ControlMaster yes
+          ControlPersist 2m
+          ServerAliveInterval 30
+
+        Host zaphyra-git
+          User git
+          Hostname git.zaphyra.eu
+      '';
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/programs/swaylock.nix b/nixosModules/zpha/programs/swaylock.nix
@@ -0,0 +1,81 @@
+{
+  resources,
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+let
+  cfg = config.zpha.programs.swaylock;
+
+in
+{
+
+  options.zpha.programs.swaylock = {
+    enable = lib.mkEnableOption "swaylock";
+    settings = lib.mkOption {
+      default = { };
+      type =
+        with lib.types;
+        attrsOf (oneOf [
+          bool
+          float
+          int
+          path
+          str
+        ]);
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    zpha.programs.swaylock.settings = {
+      # clock = true;
+
+      indicator = true;
+      ring-color = "4aa96c";
+      show-failed-attempts = true;
+      fingerprint = false;
+
+      command = "${lib.getExe pkgs.zpha.shaderbg} '*' --fps 10 ${resources.shaders.background1}";
+
+      # image = "/home/zaphyra/Pictures/Backgrounds/lock.png";
+      # scaling = "fit";
+      # color = "ffffaf";
+    };
+
+    security.pam.services.swaylock = { };
+    security.pam.services.swaylock-plugin = { };
+
+    services.systemd-lock-handler.enable = true;
+
+    users.users.zaphyra.maid = {
+      packages = [ pkgs.pkgs.swaylock-plugin-fprintd ];
+
+      file.xdg_config = {
+        "swaylock/config".text = lib.concatStrings (
+          lib.flip lib.mapAttrsToList cfg.settings (
+            n: v:
+            if v == false then
+              ""
+            else
+              (if v == true then n else n + "=" + (if builtins.isPath v then "${v}" else toString v)) + "\n"
+          )
+        );
+      };
+
+      systemd.services.swaylock = {
+        wantedBy = [ "lock.target" ];
+        partOf = [ "lock.target" ];
+        after = [ "lock.target" ];
+        environment = lib.mkForce { };
+        unitConfig.OnSuccess = [ "unlock.target" ];
+        serviceConfig = {
+          ExecStart = lib.getExe pkgs.swaylock-plugin-fprintd;
+          Restart = "on-failure";
+          RestartSec = 0;
+        };
+      };
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/programs/thunderbird.nix b/nixosModules/zpha/programs/thunderbird.nix
@@ -0,0 +1,40 @@
+{
+  config,
+  lib,
+  pkgs,
+  npins,
+  ...
+}:
+
+{
+
+  options.zpha.programs.thunderbird.enable = lib.mkEnableOption "thunderbird";
+
+  config = lib.mkIf config.zpha.programs.thunderbird.enable {
+    preservation.preserveAt."/persist".users.zaphyra.directories = [
+      ".thunderbird"
+      ".cache/thunderbird"
+    ];
+
+    users.users.zaphyra.maid = {
+      packages = lib.singleton (
+        pkgs.thunderbird.override (old: {
+          extraPrefsFiles = [
+            (pkgs.writeText "firefox-autoconfig.js" ''
+
+            '')
+          ];
+
+          nativeMessagingHosts = [ ];
+
+          extraPolicies = {
+            # disable tracking bullshit
+            DisableTelemetry = true;
+          };
+        })
+      );
+
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/programs/typst.nix b/nixosModules/zpha/programs/typst.nix
@@ -0,0 +1,25 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.zpha.programs.typst.enable = lib.mkEnableOption "typst (and utilities)";
+
+  config = lib.mkIf config.zpha.programs.typst.enable {
+    users.users.zaphyra.maid.packages = with pkgs; [
+      typst
+      typst-live
+      typesetter
+      # typewriter
+    ];
+
+    zpha.programs.micro.lsp-servers = {
+      typ.command = lib.getExe pkgs.unstable.tinymist;
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/programs/vibepanel.nix b/nixosModules/zpha/programs/vibepanel.nix
@@ -0,0 +1,141 @@
+{
+  lib,
+  pkgs,
+  config,
+  ...
+}:
+
+{
+
+  options.zpha.programs.vibepanel.enable = lib.mkEnableOption "vibepanel";
+
+  config = lib.mkIf config.zpha.programs.vibepanel.enable {
+    users.users.zaphyra.maid = {
+      packages = [ pkgs.zpha.vibepanel ];
+
+      file.xdg_config = {
+        "vibepanel/config.toml".source = pkgs.writers.writeTOML "vibepanel.toml" {
+          advanced = {
+            compositor = "niri";
+          };
+          bar = {
+            position = "top";
+            size = 32;
+            border_radius = 0;
+            background_color = "rgb(30, 40, 30)";
+            background_opacity = 0.9;
+          };
+          osd = {
+            enabled = true;
+            position = "top";
+          };
+          theme = {
+            mode = "dark";
+            accent = "#4aa96c";
+            ripple = false;
+            blur = true;
+            icons = {
+              theme = "gtk";
+              weight = 400;
+            };
+          };
+          widgets = {
+            background_opacity = 1;
+            border_radius = 40;
+            left = [
+              {
+                group = [
+                  "workspaces"
+                  "taskbar"
+                ];
+              }
+            ];
+            center = [
+              "clock"
+              "notifications"
+            ];
+            right = [
+              "media"
+              "tray"
+              {
+                group = [
+                  "quick_settings"
+                  "battery"
+                ];
+              }
+            ];
+            quick_settings.updates = false;
+            taskbar.show_workspace_separator = false;
+            clock = {
+              format = "%d. %b %H:%M";
+              on_click_right =
+                (lib.mkIf config.zpha.programs.gnome-calendar.enable (lib.getExe pkgs.gnome-calendar)).content;
+            };
+          };
+        };
+        "vibepanel/style.css".text = ''
+          :root {
+          }
+
+          .bar {
+            border-bottom: 2px solid rgba(59, 135, 86, 0.5);
+          }
+
+          .widget,
+          .widget-item {
+            background-color: unset;
+          }
+
+          .clock {
+            margin-right: -22px;
+          }
+        '';
+      };
+
+      systemd.services.vibepanel = {
+        wantedBy = [ "niri-session.target" ];
+        partOf = [ "niri-session.target" ];
+        after = [ "niri-session.target" ];
+        environment = lib.mkForce { };
+        serviceConfig = {
+          ExecStart = lib.getExe pkgs.zpha.vibepanel;
+          Restart = "on-failure";
+        };
+      };
+    };
+
+    zpha.programs.niri.settings.binds = {
+      "XF86AudioMute".spawn = [
+        "vibepanel"
+        "volume"
+        "toggle-mute"
+      ];
+      "XF86AudioLowerVolume".spawn = [
+        "vibepanel"
+        "volume"
+        "dec"
+        "2"
+      ];
+      "XF86AudioRaiseVolume".spawn = [
+        "vibepanel"
+        "volume"
+        "inc"
+        "2"
+      ];
+      "XF86MonBrightnessDown".spawn = [
+        "vibepanel"
+        "brightness"
+        "dec"
+        "2"
+      ];
+      "XF86MonBrightnessUp".spawn = [
+        "vibepanel"
+        "brightness"
+        "inc"
+        "2"
+      ];
+    };
+
+  };
+
+}
diff --git a/nixosModules/zpha/programs/vicinae.nix b/nixosModules/zpha/programs/vicinae.nix
@@ -0,0 +1,45 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.zpha.programs.vicinae.enable = lib.mkEnableOption "vicinae";
+
+  config = lib.mkIf config.zpha.programs.vicinae.enable {
+    preservation.preserveAt."/persist".users.zaphyra.directories = [
+      ".config/vicinae"
+      ".local/share/vicinae"
+    ];
+
+    zpha.programs.niri.settings.binds = {
+      "Mod+Space".spawn = [
+        (lib.getExe pkgs.vicinae)
+        "toggle"
+      ];
+    };
+
+    users.users.zaphyra.maid = {
+      packages = [ pkgs.vicinae ];
+
+      systemd.services.vicinae = {
+        after = [ "niri-session.target" ];
+        partOf = [ "niri-session.target" ];
+        wantedBy = [ "niri-session.target" ];
+
+        environment = lib.mkForce { };
+
+        unitConfig.ConditionEnvironment = "WAYLAND_DISPLAY";
+
+        serviceConfig = {
+          Restart = "on-failure";
+          ExecStart = "${lib.getExe pkgs.vicinae} server";
+        };
+      };
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/programs/wpaperd.nix b/nixosModules/zpha/programs/wpaperd.nix
@@ -0,0 +1,75 @@
+{
+  lib,
+  pkgs,
+  config,
+  ...
+}:
+
+{
+
+  options.zpha.programs.wpaperd.enable = lib.mkEnableOption "wpaperd";
+
+  config = lib.mkIf config.zpha.programs.wpaperd.enable {
+    users.users.zaphyra.maid = {
+      packages = [ pkgs.wpaperd ];
+
+      file.xdg_config = {
+        "wpaperd/wallpaper.toml".source = pkgs.writers.writeTOML "wpaperd-config.toml" {
+          default = {
+            duration = "30m";
+            mode = "center";
+            sorting = "ascending";
+            transition.bounce = { };
+          };
+
+          any.path = pkgs.symlinkJoin {
+            name = "backgrounds";
+            paths = with pkgs.nixos-artwork.wallpapers; [
+              binary-black
+              binary-blue
+              binary-red
+
+              nineish
+              nineish-solarized-dark
+              nineish-dark-gray
+              nineish-catppuccin-mocha-alt
+              nineish-catppuccin-mocha
+              nineish-catppuccin-frappe-alt
+              nineish-catppuccin-frappe
+              nineish-catppuccin-macchiato-alt
+              nineish-catppuccin-macchiato
+
+              simple-red
+              simple-dark-gray
+              simple-blue
+
+              dracula
+              gradient-grey
+            ];
+            stripPrefix = "/share/backgrounds/nixos";
+          };
+        };
+      };
+
+      systemd.services.wpaperd = {
+        wantedBy = [ "niri-session.target" ];
+        partOf = [ "niri-session.target" ];
+        after = [ "niri-session.target" ];
+        environment = lib.mkForce { };
+        unitConfig.ConditionEnvironment = "WAYLAND_DISPLAY";
+        serviceConfig = {
+          ExecStart = lib.getExe pkgs.wpaperd;
+          Restart = "on-failure";
+        };
+      };
+    };
+
+    zpha.programs.niri.settings.binds = {
+      "Mod+Home".spawn = [
+        (lib.getExe' pkgs.wpaperd "wpaperctl")
+        "next"
+      ];
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/programs/yt-dlp.nix b/nixosModules/zpha/programs/yt-dlp.nix
@@ -0,0 +1,25 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.zpha.programs.yt-dlp.enable = lib.mkEnableOption "yt-dlp ";
+
+  config = lib.mkIf config.zpha.programs.yt-dlp.enable {
+    users.users.zaphyra.maid = {
+      packages = with pkgs; [
+        yt-dlp
+        (pkgs.writeShellScriptBin "youtube-dl" ''
+          exec ${pkgs.yt-dlp}/bin/yt-dlp --compat-options youtube-dl "$@"
+        '')
+      ];
+
+      programs.fish.aliases.yt-dlp-audio = "yt-dlp --format bestaudio -x --audio-format opus --add-metadata --embed-thumbnail";
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/programs/yubikey.nix b/nixosModules/zpha/programs/yubikey.nix
@@ -0,0 +1,21 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.zpha.programs.yubikey.enable = lib.mkEnableOption "yubikey support";
+
+  config = lib.mkIf config.zpha.programs.yubikey.enable {
+    programs.yubikey-touch-detector.enable = true;
+
+    users.users.zaphyra.maid.packages = with pkgs; [
+      yubikey-manager
+      yubioath-flutter
+    ];
+  };
+
+}
diff --git a/nixosModules/zpha/services/avahi.nix b/nixosModules/zpha/services/avahi.nix
@@ -0,0 +1,21 @@
+{
+  config,
+  lib,
+  ...
+}:
+
+{
+
+  options.zpha.services.avahi.enable = lib.mkEnableOption "avahi";
+
+  config = lib.mkIf config.zpha.services.avahi.enable {
+    services.avahi = {
+      enable = true;
+      nssmdns4 = true;
+      publish.enable = true;
+      publish.userServices = true;
+    };
+
+  };
+
+}
diff --git a/nixosModules/zpha/services/batsignal.nix b/nixosModules/zpha/services/batsignal.nix
@@ -0,0 +1,45 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.zpha.services.batsignal.enable = lib.mkEnableOption "batsignal";
+
+  config = lib.mkIf config.zpha.services.batsignal.enable {
+    users.users.zaphyra.maid = {
+      systemd.services.batsignal = {
+        description = "batsignal - battery monitor daemon";
+        after = [ "niri-session.target" ];
+        partOf = [ "niri-session.target" ];
+        wantedBy = [ "niri-session.target" ];
+
+        serviceConfig = {
+          Restart = "on-failure";
+          ExecStart =
+            let
+              args = [
+                # expire notifications
+                "-e"
+                # show message when battery begins charging/discharging
+                "-p"
+                # minimum number of SECONDS to wait between battery checks
+                "-m10"
+                # bat levels
+                "-w20"
+                "-c10"
+                "-d5"
+                "-D"
+                "systemctl suspend"
+              ];
+            in
+            "${lib.getExe pkgs.batsignal} ${lib.escapeShellArgs args}";
+        };
+      };
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/services/dssd.nix b/nixosModules/zpha/services/dssd.nix
@@ -0,0 +1,37 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.zpha.services.dssd.enable = lib.mkEnableOption "dssd";
+
+  config = lib.mkIf config.zpha.services.dssd.enable {
+    preservation.preserveAt."/persist".users.zaphyra.directories = [
+      ".local/state/dssd"
+    ];
+
+    services.gnome.gnome-keyring.enable = lib.mkForce false;
+
+    users.users.zaphyra.maid = {
+      dbus.packages = [ pkgs.zpha.dssd ];
+
+      systemd.services.dssd = {
+        unitConfig = {
+          Description = "Dead Simple Secret Daemon";
+          Documentation = "https://github.com/ylxdzsw/dssd";
+        };
+
+        serviceConfig = {
+          Type = "dbus";
+          ExecStart = lib.getExe pkgs.zpha.dssd;
+          BusName = "org.freedesktop.secrets";
+        };
+      };
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/services/keyd.nix b/nixosModules/zpha/services/keyd.nix
@@ -0,0 +1,45 @@
+{
+  config,
+  lib,
+  ...
+}:
+
+{
+
+  options.zpha.services.keyd.enable = lib.mkEnableOption "keyd";
+
+  config = lib.mkIf config.zpha.services.keyd.enable {
+    users.groups.keyd = { };
+
+    systemd.services.keyd.serviceConfig = {
+      CapabilityBoundingSet = [ "CAP_SETGID" ];
+    };
+
+    environment.etc."libinput/local-overrides.quirks".text = ''
+      [Serial Keyboards]
+      MatchUdevType=keyboard
+      MatchName=keyd virtual keyboard
+      AttrKeyboardIntegration=internal
+    '';
+
+    services.keyd = {
+      enable = true;
+      keyboards = {
+        any = {
+          ids = [
+            "*"
+          ];
+          settings.main = {
+            "leftcontrol" = "leftalt";
+            "leftalt" = "leftcontrol";
+            "s+d" = "oneshot(shift)";
+            "f+j" = "space";
+            "j+k" = "backspace";
+            "k+l" = "enter";
+          };
+        };
+      };
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/services/knot.nix b/nixosModules/zpha/services/knot.nix
@@ -0,0 +1,227 @@
+{
+  machines,
+  nixosConfigurations,
+  machineConfig,
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+let
+  inherit (lib) types;
+  cfg = config.zpha.services.knot;
+
+  dnsServerAddresses =
+    isPrimary:
+    (lib.pipe nixosConfigurations [
+      (lib.filterAttrs (
+        _hostName: machineConfig:
+        let
+          cfgModule = machineConfig.config.zpha.services.knot;
+        in
+        cfgModule.enable && cfgModule.primary == isPrimary
+      ))
+      (lib.mapAttrsToList (
+        hostName: _machineConfig: [
+          (lib.mkIf (machines."${hostName}".networking.ip6Address != "")
+            machines."${hostName}".networking.ip6Address
+          )
+          (lib.mkIf (machines."${hostName}".networking.ip4Address != "")
+            machines."${hostName}".networking.ip4Address
+          )
+        ]
+      ))
+      lib.flatten
+    ]);
+
+  dnsServerSecondaries = lib.pipe nixosConfigurations [
+    (lib.filterAttrs (
+      _hostName: machineConfig:
+      let
+        cfgModule = machineConfig.config.zpha.services.knot;
+      in
+      cfgModule.enable && !cfgModule.primary
+    ))
+    (lib.mapAttrs (
+      hostName: _machineConfig: {
+        address = [
+          (lib.mkIf (machines."${hostName}".networking.ip6Address != "")
+            machines."${hostName}".networking.ip6Address
+          )
+          (lib.mkIf (machines."${hostName}".networking.ip4Address != "")
+            machines."${hostName}".networking.ip4Address
+          )
+        ];
+      }
+    ))
+  ];
+
+in
+{
+
+  options.zpha.services.knot = {
+    enable = lib.mkEnableOption "knot dns server";
+
+    primary = lib.mkOption {
+      type = types.bool;
+      default = false;
+    };
+
+    dataDir = lib.mkOption {
+      type = types.str;
+      default = "/var/lib/knot";
+    };
+
+    keyFiles = lib.mkOption {
+      type = types.listOf types.path;
+      default = [ ];
+    };
+
+    zones = lib.mkOption {
+      inherit ((pkgs.formats.yaml { })) type;
+      default = { };
+    };
+
+    extraACL = lib.mkOption {
+      inherit ((pkgs.formats.yaml { })) type;
+      default = { };
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    networking.firewall.allowedTCPPorts = [ 53 ];
+    networking.firewall.allowedUDPPorts = [ 53 ];
+
+    users = {
+      users.knot.uid = 553;
+      groups.knot.gid = 553;
+    };
+
+    common.configure.persist.system.dirs = [
+      {
+        directory = cfg.dataDir;
+        mode = "770";
+        user = "knot";
+        group = "knot";
+      }
+    ];
+
+    systemd.tmpfiles.settings = {
+      knotDataDir."${cfg.dataDir}".d = {
+        group = "knot";
+        user = "knot";
+        mode = "770";
+        age = "-";
+      };
+    };
+
+    services.knot =
+      let
+        primaryAddresses = dnsServerAddresses true;
+        secondaryAddresses = dnsServerAddresses false;
+        secondaries = dnsServerSecondaries;
+
+      in
+      {
+        enable = true;
+        keyFiles = lib.mkIf (cfg.keyFiles != [ ]) cfg.keyFiles;
+        settings = {
+          log.syslog.any = "debug";
+
+          server.listen = [
+            (lib.mkIf (machineConfig.networking.ip6Address != "") "${machineConfig.networking.ip6Address}@53")
+            (lib.mkIf (machineConfig.networking.ip4Address != "") "${machineConfig.networking.ip4Address}@53")
+            "::1@53"
+            "127.0.0.2@53"
+          ];
+
+          mod-rrl.default.rate-limit = 200;
+          mod-rrl.default.slip = 2;
+
+          remote = {
+            primary.address = primaryAddresses;
+          }
+          // secondaries;
+
+          acl = {
+            allowTransfer = lib.mkIf (secondaryAddresses != [ ]) {
+              address = secondaryAddresses;
+              action = "transfer";
+            };
+
+            allowNotify.address = primaryAddresses;
+            allowNotify.action = "notify";
+          }
+          // cfg.extraACL;
+
+          template =
+            let
+              notify = {
+                acl = lib.mkIf (config.services.knot.settings.acl ? allowTransfer) "allowTransfer";
+                notify = lib.mkIf (config.services.knot.settings.acl ? allowTransfer) (
+                  builtins.attrNames secondaries
+                );
+              };
+
+              catalog = {
+                catalog-role = "member";
+                catalog-zone = "catalog.";
+              };
+
+            in
+            {
+
+              default = {
+                semantic-checks = true;
+                global-module = "mod-rrl/default";
+              };
+
+              notifyZone = notify;
+              nixZone = notify // catalog;
+
+              secondaryZone = {
+                master = "primary";
+                acl = "allowNotify";
+
+                journal-content = "all";
+
+                zonefile-sync = -1;
+                zonefile-load = "none";
+              };
+            };
+
+          zone =
+            (
+              if !cfg.primary then
+                {
+                  "catalog.".catalog-role = "interpret";
+                  "catalog.".catalog-template = "secondaryZone";
+                  "catalog.".template = "secondaryZone";
+                }
+              else
+                {
+                  "catalog.".catalog-role = "generate";
+                  "catalog.".template = "notifyZone";
+                }
+            )
+            // (lib.mapAttrs (
+              _name: zone:
+              zone
+              // {
+                template = "nixZone";
+                acl = lib.mkIf (zone ? acl) (
+                  if (config.services.knot.settings.acl ? allowTransfer) then
+                    lib.flatten [
+                      [ "allowTransfer" ]
+                      zone.acl
+                    ]
+                  else
+                    zone.acl
+                );
+              }
+            ) cfg.zones);
+        };
+      };
+  };
+
+}
diff --git a/nixosModules/zpha/services/knotACME.nix b/nixosModules/zpha/services/knotACME.nix
@@ -0,0 +1,144 @@
+{
+  nixosConfigurations,
+  pkgs,
+  lib,
+  config,
+  ...
+}:
+let
+  inherit (lib) types;
+  cfg = config.zpha.services.knotACME;
+
+in
+{
+
+  options.zpha.services.knotACME = {
+    enable = lib.mkEnableOption "";
+    nameServers = lib.mkOption {
+      type = types.listOf types.str;
+    };
+    zone = lib.mkOption {
+      type = types.str;
+    };
+    zones = lib.mkOption {
+      type = types.listOf types.str;
+    };
+    keyFile = lib.mkOption {
+      type = types.str;
+    };
+  };
+
+  config = lib.mkIf cfg.enable (
+    let
+      generateACMERecord = recordName: ((builtins.hashString "sha1" recordName) + ".${cfg.zone}.");
+
+      machinesWithACMERecords = lib.flip lib.filterAttrs nixosConfigurations (
+        _machineName: machineConfig: machineConfig.config.security.acme.certs != { }
+      );
+
+      getAllDomainsPerMachine =
+        machineName:
+        (lib.pipe nixosConfigurations."${machineName}".config.security.acme.certs [
+          (lib.mapAttrsToList (domain: cfg: [ domain ] ++ cfg.extraDomainNames))
+          lib.flatten
+        ]);
+
+      getACMERecordsPerMachine =
+        machineName:
+        lib.pipe machineName [
+          getAllDomainsPerMachine
+          (builtins.map (recordName: (generateACMERecord recordName)))
+        ];
+
+      generateACMERecordsPerZone =
+        zoneName:
+        (lib.pipe machinesWithACMERecords [
+          (lib.mapAttrsToList (hostName: _: (getAllDomainsPerMachine hostName)))
+          lib.flatten
+          (builtins.filter (lib.hasSuffix zoneName))
+          (builtins.map (recordName: {
+            name = "_acme-challenge${
+              if zoneName != recordName then "." else ""
+            }${lib.removeSuffix "${if zoneName != recordName then "." else ""}${zoneName}" recordName}";
+            value = {
+              CNAME = [ (generateACMERecord recordName) ];
+            };
+          }))
+          builtins.listToAttrs
+        ]);
+
+    in
+    {
+      dns.allZones = lib.pipe cfg.zones [
+        (lib.map (element: lib.nameValuePair element { subdomains = generateACMERecordsPerZone element; }))
+        lib.listToAttrs
+      ];
+
+      systemd.services.knot =
+        let
+          acmeZoneFile = pkgs.writeTextFile {
+            name = "${cfg.zone}.zone";
+            text = pkgs.dnsNix.types.zoneToString cfg.zone (
+              pkgs.dnsNix.evalZone cfg.zone {
+                NS = cfg.nameServers;
+                SOA = {
+                  nameServer = lib.elemAt cfg.nameServers 0;
+                  adminEmail = "dns@${cfg.zone}"; # Email address with a real `@`!
+                  serial = 0;
+                };
+              }
+            );
+          };
+        in
+        {
+          reloadTriggers = [ "${config.zpha.services.knot.dataDir}/acme.zone" ];
+          serviceConfig = {
+            ExecStartPre = [
+              (pkgs.writeShellScript "knot-acmeZone-preStart" ''
+                set -eou pipefail
+                cp --dereference ${acmeZoneFile} ${config.zpha.services.knot.dataDir}/acme.zone
+                chmod -R 770 ${config.zpha.services.knot.dataDir}/acme.zone
+              '')
+            ];
+            ExecReload = lib.mkForce (
+              pkgs.writeShellScript "knot-reload" ''
+                set -eou pipefail
+                cp --dereference ${acmeZoneFile} ${config.zpha.services.knot.dataDir}/acme.zone
+                chmod -R 770 ${config.zpha.services.knot.dataDir}/acme.zone
+                ${config.services.knot.package}/bin/knotc reload
+              ''
+            );
+          };
+        };
+
+      zpha.services.knot = {
+        keyFiles = [ cfg.keyFile ];
+        zones = {
+          "${cfg.zone}" = {
+            file = "${config.zpha.services.knot.dataDir}/acme.zone";
+
+            zonefile-sync = 0;
+            zonefile-load = "difference-no-serial";
+
+            journal-content = "all";
+
+            acl = lib.mkIf ((lib.attrNames machinesWithACMERecords) != [ ]) (
+              lib.mapAttrsToList (hostName: _: "acme-nix-${hostName}") machinesWithACMERecords
+            );
+          };
+        };
+        extraACL = lib.mapAttrs' (hostName: _: {
+          name = "acme-nix-${hostName}";
+          value = {
+            key = [ "acme-nix-${hostName}" ];
+            action = "update";
+            update-owner = "name";
+            update-owner-match = "equal";
+            update-owner-name = getACMERecordsPerMachine hostName;
+          };
+        }) machinesWithACMERecords;
+      };
+    }
+  );
+
+}
diff --git a/nixosModules/zpha/services/oniri.nix b/nixosModules/zpha/services/oniri.nix
@@ -0,0 +1,26 @@
+{
+  lib,
+  pkgs,
+  config,
+  ...
+}:
+
+{
+
+  options.zpha.services.oniri.enable = lib.mkEnableOption "oniri";
+
+  config = lib.mkIf config.zpha.services.oniri.enable {
+    users.users.zaphyra.maid = {
+      systemd.services.oniri = {
+        wantedBy = [ "niri-session.target" ];
+        partOf = [ "niri-session.target" ];
+        after = [ "niri-session.target" ];
+        serviceConfig = {
+          ExecStart = lib.getExe pkgs.zpha.oniri;
+          Restart = "on-failure";
+        };
+      };
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/services/pipewire.nix b/nixosModules/zpha/services/pipewire.nix
@@ -0,0 +1,76 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+let
+  inherit (lib) types;
+  cfg = config.zpha.services.pipewire;
+
+in
+{
+
+  options.zpha.services.pipewire.enable = lib.mkEnableOption "pipewire";
+
+  config = lib.mkIf cfg.enable {
+    users.users.zaphyra.maid = {
+      packages = with pkgs; [
+        pwvucontrol
+        unstable.crosspipe
+        #pipemixer
+        unstable.wiremix
+      ];
+
+      file.xdg_config."pipewire/pipewire.conf.d/10-chloe-raop.conf".text =
+        let
+          raopSink = ipAddr: name: {
+            name = "libpipewire-module-raop-sink";
+            args = {
+              "raop.ip" = ipAddr;
+              "raop.port" = 7000;
+              "raop.name" = name;
+              "raop.encryption.type" = "auth_setup";
+            };
+          };
+
+        in
+        builtins.toJSON {
+          "context.modules" = [
+            { name = "libpipewire-module-zeroconf-discover"; }
+            {
+              name = "libpipewire-module-raop-discover";
+              args."roap.discover-local" = true;
+            }
+            # (raopSink "192.168.2.63" "Living Room")
+            # (raopSink "192.168.2.57" "Bed Room")
+            # (raopSink "192.168.2.54" "Datacenter")
+            # (raopSink "192.168.2.80" "Kitchen")
+            # {
+            #   name = "libpipewire-module-combine-stream";
+            #   args = {
+            #     "node.name" = "raop_multi_room";
+            #     "node.description" = "Multi-Room";
+            #     "combine.mode" = "sink";
+            #     "combine.latency-compensate" = false;
+            #     "combine.on-demand-streams" = true;
+            #     "stream.rules" = [
+            #       {
+            #         actions."create-stream" = { };
+            #         matches = [
+            #           {
+            #             "media.class" = "Audio/Sink";
+            #             "node.name" = "~raop_sink.*";
+            #           }
+            #         ];
+            #       }
+            #     ];
+            #   };
+            # }
+          ];
+        };
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/services/wlsunset.nix b/nixosModules/zpha/services/wlsunset.nix
@@ -0,0 +1,39 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.zpha.services.wlsunset.enable = lib.mkEnableOption "batsignal";
+
+  config = lib.mkIf config.zpha.services.wlsunset.enable {
+    users.users.zaphyra.maid = {
+      systemd.services.batsignal = {
+        description = "Day/night gamma adjustments for Wayland compositors.";
+        after = [ "niri-session.target" ];
+        partOf = [ "niri-session.target" ];
+        wantedBy = [ "niri-session.target" ];
+
+        unitConfig.ConditionEnvironment = "WAYLAND_DISPLAY";
+
+        serviceConfig = {
+          Restart = "on-failure";
+          ExecStart =
+            let
+              args = lib.cli.toCommandLineShellGNU { } {
+                t = 3000;
+                T = 4500;
+                l = "8.26";
+                L = "49.01";
+              };
+            in
+            "${lib.getExe pkgs.wlsunset} ${args}";
+        };
+      };
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/websites/bikemap.zaphyra.eu.nix b/nixosModules/zpha/websites/bikemap.zaphyra.eu.nix
@@ -0,0 +1,124 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.zpha.websites."bikemap.zaphyra.eu".enable = lib.mkEnableOption "";
+
+  config = lib.mkIf config.zpha.websites."bikemap.zaphyra.eu".enable (
+    let
+      deployScript = pkgs.writeShellScript "deployBikemap" ''
+        systemctl start deployBikemap;
+        systemctl status deployBikemap;
+      '';
+
+    in
+    {
+      assertions = lib.singleton {
+        assertion = config.common.services.gitolite.enable;
+        message = "The option 'common.services.gitolite.enable' must be enabled in order to use this module.";
+      };
+
+      dns.zones."zaphyra.eu".subdomains."bikemap".CNAME = lib.singleton "${config.networking.fqdn}.";
+
+      common.configure.persist.system.dirs = lib.singleton {
+        directory = "/var/lib/bikemap";
+        mode = "0755";
+        user = "bikemap";
+        inherit (config.common.services.gitolite) group;
+      };
+
+      users.users."bikemap" = {
+        isSystemUser = true;
+        inherit (config.common.services.gitolite) group;
+        createHome = true;
+        homeMode = "755";
+        home = "/var/lib/bikemap";
+      };
+
+      security.sudo.extraRules = [
+        {
+          users = [ "git" ];
+          commands = [
+            {
+              command = "${deployScript}";
+              options = [
+                "SETENV"
+                "NOPASSWD"
+              ];
+            }
+          ];
+        }
+      ];
+
+      systemd.services.deployBikemap = {
+        script = ''
+          # strict mode
+          set -euo pipefail
+          IFS=$'\n\t'
+
+          TMP_DIR=$(mktemp -d)
+          trap "{ rm -rf "$TMP_DIR"; }" SIGINT SIGTERM ERR EXIT
+
+          ${pkgs.git}/bin/git config --global --add safe.directory ${config.common.services.gitolite.dataDir}/repositories/biketracks.git
+          ${pkgs.git}/bin/git clone ${config.common.services.gitolite.dataDir}/repositories/biketracks.git $TMP_DIR/tracks
+
+          mkdir $TMP_DIR/tiles
+
+          ${pkgs.zpha.generateTilesFromGPX}/bin/generateTilesFromGPX $TMP_DIR/tracks $TMP_DIR/tiles
+
+          rm -rf ~/*;
+
+          ln -sf ${pkgs.zpha.gpx-map}/index.html ~/index.html
+          ln -sf ${pkgs.zpha.gpx-map}/bundle.js  ~/bundle.js
+          mv     $TMP_DIR/tiles             ~/tiles;
+          echo "{\"lastUpdated\":\"$(date +"%Y-%m-%d %H:%M")\"}" > ~/lastUpdated.json
+        '';
+
+        serviceConfig = {
+          Type = "oneshot";
+
+          User = "bikemap";
+          Group = config.common.services.gitolite.group;
+
+          WorkingDirectory = "~";
+          StateDirectory = "bikemap";
+          StateDirectoryMode = "755";
+
+          NoNewPrivileges = true;
+          PrivateTmp = true;
+          PrivateDevices = true;
+
+          RestrictAddressFamilies = "none";
+          RestrictNamespaces = true;
+          RestrictRealtime = true;
+
+          ProtectSystem = "full";
+          ProtectControlGroups = true;
+          ProtectKernelModules = true;
+          ProtectKernelTunables = true;
+
+          DevicePolicy = "closed";
+          LockPersonality = true;
+        };
+      };
+
+      common.services.gitolite.commonHooks.post-receive = ''
+        #deploy bikemap
+        [ "$GL_REPO" == "biketracks" ] && sudo ${deployScript}
+      '';
+
+      services.nginx.virtualHosts."bikemap.zaphyra.eu" = {
+        useACMEHost = "${config.networking.fqdn}";
+        forceSSL = true;
+        kTLS = true;
+        root = "/var/lib/bikemap/";
+      };
+    }
+  );
+
+}
diff --git a/nixosModules/zpha/websites/continuwuity.zaphyra.eu.nix b/nixosModules/zpha/websites/continuwuity.zaphyra.eu.nix
@@ -0,0 +1,122 @@
+{
+  npins,
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.zpha.websites."continuwuity.zaphyra.eu".enable = lib.mkEnableOption "";
+
+  config = lib.mkIf config.zpha.websites."continuwuity.zaphyra.eu".enable {
+    dns.zones."zaphyra.eu".subdomains."continuwuity".CNAME = [ "${config.networking.fqdn}." ];
+
+    sops.secrets = {
+      "restic/continuwuity/repositoryPassword" = { };
+      "restic/continuwuity/sshPrivateKey" = { };
+    };
+
+    common = {
+      configure.persist.system.dirs = [
+        "/var/lib/private/continuwuity"
+      ];
+
+      services.resticBackup.continuwuity = {
+        enable = true;
+        targets = [
+          "restic-target.fc9f.de"
+          "isodon.fc9f.de"
+        ];
+        sshKeyFile = config.sops.secrets."restic/continuwuity/sshPrivateKey".path;
+        passwordFile = config.sops.secrets."restic/continuwuity/repositoryPassword".path;
+        paths = [ "/var/lib/private/continuwuity" ];
+        runBeforeBackup = ''
+          systemctl stop continuwuity
+          ${lib.getExe pkgs.rsync} -a --exclude /var/lib/private/continuwuity/media /var/lib/private/continuwuity /tmp/continuwuity-database
+          systemctl start continuwuity
+        '';
+      };
+    };
+
+    services.matrix-continuwuity = {
+      enable = true;
+      package =
+        # (npins.continuwuity).outputs.packages.${pkgs.stdenv.hostPlatform.system}.default.overrideAttrs
+        pkgs.unstable.matrix-continuwuity;
+      settings = {
+        global = {
+          address = [ "::1" ];
+          trusted_servers = [
+            "matrix.org"
+            "tchncs.de"
+          ];
+          server_name = "zaphyra.eu";
+          allow_registration = false;
+          log_to_journald = true;
+        };
+      };
+    };
+
+    services.nginx.virtualHosts =
+      let
+        matrixServerConfig = {
+          "m.server" = "continuwuity.zaphyra.eu:443";
+        };
+        matrixClientConfig = {
+          "m.homeserver".base_url = "https://continuwuity.zaphyra.eu/";
+          "org.matrix.msc4143.rtc_foci" = [
+            {
+              type = "livekit";
+              livekit_service_url = "https://livekit.rtc.matrix.nwex.de";
+            }
+          ];
+        };
+      in
+      {
+        "${config.services.matrix-continuwuity.settings.global.server_name}" = {
+          locations = {
+            "= /.well-known/matrix/server".extraConfig = ''
+              add_header Content-Type application/json;
+              add_header "Access-Control-Allow-Origin" "*";
+              add_header "Access-Control-Allow-Methods" "GET, POST, PUT, DELETE, OPTIONS";
+              add_header "Access-Control-Allow-Headers" "Origin, X-Requested-With, Content-Type, Accept, Authorization";
+              return 200 '${builtins.toJSON matrixServerConfig}';
+            '';
+            "= /.well-known/matrix/client".extraConfig = ''
+              add_header Content-Type application/json;
+              add_header "Access-Control-Allow-Origin" "*";
+              add_header "Access-Control-Allow-Methods" "GET, POST, PUT, DELETE, OPTIONS";
+              add_header "Access-Control-Allow-Headers" "Origin, X-Requested-With, Content-Type, Accept, Authorization";
+              return 200 '${builtins.toJSON matrixClientConfig}';
+            '';
+            "/_matrix" = {
+              proxyPass = "http://[::1]:${toString config.services.matrix-continuwuity.settings.global.port}";
+              proxyWebsockets = true;
+            };
+          };
+        };
+        "continuwuity.zaphyra.eu" = {
+          useACMEHost = "${config.networking.fqdn}";
+          forceSSL = true;
+          kTLS = true;
+          locations = {
+            "/_matrix" = {
+              proxyPass = "http://[::1]:${toString config.services.matrix-continuwuity.settings.global.port}";
+              proxyWebsockets = true;
+            };
+            "/".root = pkgs.cinny.override {
+              conf = {
+                defaultHomeserver = 0;
+                homeserverList = [ config.services.matrix-continuwuity.settings.global.server_name ];
+                hashRouter.enabled = true;
+                allowCustomHomesevrers = false;
+              };
+            };
+          };
+        };
+      };
+  };
+
+}
diff --git a/nixosModules/zpha/websites/ctu.cx.nix b/nixosModules/zpha/websites/ctu.cx.nix
@@ -0,0 +1,22 @@
+{
+  machineConfig,
+  config,
+  lib,
+  ...
+}:
+
+{
+
+  options.zpha.websites."ctu.cx".enable = lib.mkEnableOption "";
+
+  config = lib.mkIf config.zpha.websites."ctu.cx".enable {
+    dns.zones."ctu.cx".AAAA = lib.singleton machineConfig.networking.ip6Address;
+
+    services.nginx.virtualHosts."ctu.cx" = {
+      useACMEHost = "${config.networking.fqdn}";
+      forceSSL = true;
+      kTLS = true;
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/websites/dav.zaphyra.eu.nix b/nixosModules/zpha/websites/dav.zaphyra.eu.nix
@@ -0,0 +1,73 @@
+{
+  config,
+  lib,
+  ...
+}:
+
+{
+
+  options.zpha.websites."dav.zaphyra.eu".enable = lib.mkEnableOption "";
+
+  config = lib.mkIf config.zpha.websites."dav.zaphyra.eu".enable {
+    dns.zones."zaphyra.eu".subdomains."dav".CNAME = [ "${config.networking.fqdn}." ];
+
+    sops.secrets = {
+      "restic/radicale/repositoryPassword" = { };
+      "restic/radicale/sshPrivateKey" = { };
+      radicaleUsers = {
+        owner = config.systemd.services.radicale.serviceConfig.User;
+        restartUnits = [ "radicale.service" ];
+      };
+    };
+
+    users = {
+      users.radicale.uid = 234;
+      groups.radicale.gid = 234;
+    };
+
+    common = {
+      configure.persist.system.dirs = lib.singleton {
+        mode = "700";
+        user = config.systemd.services.radicale.serviceConfig.User;
+        group = config.systemd.services.radicale.serviceConfig.Group;
+        directory = config.services.radicale.settings.storage.filesystem_folder;
+      };
+
+      services.resticBackup.radicale = {
+        user = config.systemd.services.radicale.serviceConfig.User;
+        enable = true;
+        targets = [
+          "restic-target.fc9f.de"
+          "isodon.fc9f.de"
+        ];
+        sshKeyFile = config.sops.secrets."restic/radicale/sshPrivateKey".path;
+        passwordFile = config.sops.secrets."restic/radicale/repositoryPassword".path;
+        paths = [ config.services.radicale.settings.storage.filesystem_folder ];
+      };
+    };
+
+    services = {
+      radicale = {
+        enable = true;
+        settings = {
+          server.hosts = [ "[::1]:5232" ];
+          web.type = "internal";
+          storage.filesystem_folder = "/var/lib/radicale";
+          headers.Access-Control-Allow-Origin = "*";
+          auth = {
+            type = "htpasswd";
+            htpasswd_filename = config.sops.secrets.radicaleUsers.path;
+            htpasswd_encryption = "plain";
+          };
+        };
+      };
+      nginx.virtualHosts."dav.zaphyra.eu" = {
+        useACMEHost = config.networking.fqdn;
+        forceSSL = true;
+        kTLS = true;
+        locations."/".proxyPass = "http://${lib.elemAt config.services.radicale.settings.server.hosts 0}/";
+      };
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/websites/ente.zaphyra.eu.nix b/nixosModules/zpha/websites/ente.zaphyra.eu.nix
@@ -0,0 +1,133 @@
+{
+  machineConfig,
+  config,
+  lib,
+  ...
+}:
+
+let
+  subdomains = [
+    "accounts"
+    "api"
+    "albums"
+    "cast"
+    "photos"
+  ];
+
+in
+{
+
+  options.zpha.websites."ente.zaphyra.eu".enable = lib.mkEnableOption "";
+
+  config = lib.mkIf config.zpha.websites."ente.zaphyra.eu".enable {
+    dns.zones = {
+      "zaphyra.eu".subdomains =
+        (lib.genAttrs' subdomains (
+          name:
+          lib.nameValuePair "${name}.ente" {
+            CNAME = [ "ente.zaphyra.eu." ];
+          }
+        ))
+        // {
+          "ente".AAAA = [ machineConfig.networking.ip6Address ];
+        };
+      "fc9f.de".subdomains."s3.${config.networking.hostName}".CNAME = [
+        "${config.networking.fqdn}."
+      ];
+    };
+
+    sops.secrets = {
+      "environments/ente" = {
+        owner = "ente";
+        group = "ente";
+      };
+      "environments/minio" = {
+        owner = "minio";
+        group = "minio";
+      };
+    };
+
+    common.configure.persist.system.dirs = [
+      {
+        user = "minio";
+        group = "minio";
+        directory = "/var/lib/minio";
+        mode = "0700";
+      }
+      {
+        user = "postgres";
+        group = "postgres";
+        directory = "/var/lib/postgresql";
+        mode = "0700";
+      }
+      {
+        inherit (config.services.ente.api) user group;
+        directory = "/var/lib/ente";
+        mode = "0700";
+      }
+    ];
+
+    systemd.services.ente.serviceConfig.EnvironmentFile = config.sops.secrets."environments/ente".path;
+
+    services = {
+      postgresql.enable = true;
+
+      minio = {
+        enable = true;
+        rootCredentialsFile = config.sops.secrets."environments/minio".path;
+        browser = false;
+        listenAddress = "[::1]:9000";
+      };
+
+      ente = {
+        web = {
+          enable = true;
+          domains = lib.genAttrs subdomains (name: "${name}.ente.zaphyra.eu");
+        };
+        api = {
+          enable = true;
+          enableLocalDB = true;
+          nginx.enable = true;
+          domain = "api.ente.zaphyra.eu";
+          settings = {
+            internal.admin = 1580559962386438;
+            s3.b2-eu-cen = {
+              use_path_style_urls = true;
+              are_local_buckets = true;
+              endpoint = "https://s3.${config.networking.fqdn}/";
+              region = "us-east-1";
+              bucket = "ente";
+            };
+          };
+        };
+      };
+
+      nginx.virtualHosts =
+        (lib.genAttrs' (lib.filter (name: name != "albums") subdomains) (
+          name:
+          lib.nameValuePair "${name}.ente.zaphyra.eu" {
+            serverAliases = lib.mkIf (name == "photos") [
+              "albums.ente.zaphyra.eu"
+            ];
+            useACMEHost = config.networking.fqdn;
+            forceSSL = true;
+            kTLS = true;
+          }
+        ))
+        // {
+          "s3.${config.networking.fqdn}" = {
+            useACMEHost = config.networking.fqdn;
+            forceSSL = true;
+            kTLS = true;
+            locations."/" = {
+              proxyPass = "http://[::1]:9000";
+              extraConfig = ''
+                client_max_body_size 100m;
+              '';
+            };
+          };
+        };
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/websites/fedi.ctu.cx.nix b/nixosModules/zpha/websites/fedi.ctu.cx.nix
@@ -0,0 +1,187 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.zpha.websites."fedi.ctu.cx".enable = lib.mkEnableOption "";
+
+  config = lib.mkIf config.zpha.websites."fedi.ctu.cx".enable {
+    dns.zones."ctu.cx".subdomains."fedi".CNAME = [
+      "${config.networking.fqdn}."
+    ];
+
+    sops.secrets = {
+      "restic/gotosocial/repositoryPassword" = { };
+      "restic/gotosocial/sshPrivateKey" = { };
+    };
+
+    systemd.tmpfiles.settings.gotosocial = {
+      "/var/lib/gotosocial/storage".d = {
+        inherit (config.common.services.gotosocial) user;
+        inherit (config.common.services.gotosocial) group;
+        mode = "750";
+        age = "-";
+      };
+    };
+
+    common = {
+      configure.persist.system.dirs = [
+        {
+          inherit (config.common.services.gotosocial) user group;
+          mode = "0755";
+          directory = config.common.services.gotosocial.stateDir;
+        }
+      ];
+
+      services.resticBackup.gotosocial = {
+        enable = true;
+        inherit (config.common.services.gotosocial) user;
+        targets = [
+          "restic-target.fc9f.de"
+          "isodon.fc9f.de"
+        ];
+        sshKeyFile = config.sops.secrets."restic/gotosocial/sshPrivateKey".path;
+        passwordFile = config.sops.secrets."restic/gotosocial/repositoryPassword".path;
+        sqliteDatabases = [
+          (lib.mkIf (
+            config.common.services.gotosocial.settings.db-type == "sqlite"
+          ) config.common.services.gotosocial.settings.db-address)
+        ];
+        paths = [
+          (lib.mkIf (
+            config.common.services.gotosocial.settings.storage-backend == "local"
+          ) config.common.services.gotosocial.settings.storage-local-base-path)
+          "${config.common.services.gotosocial.stateDir}/backup.json"
+        ];
+        runBeforeBackup = ''
+          ${config.common.services.gotosocial.package}/bin/gotosocial --config-path /etc/gotosocial.yaml admin export --path ${config.common.services.gotosocial.stateDir}/backup.json
+        '';
+      };
+    };
+
+    common.services.gotosocial = {
+      enable = true;
+      package = pkgs.zpha.gotosocial-unstable;
+      inherit (config.services.nginx) group;
+      settings = {
+        protocol = "https";
+
+        bind-address = "::1";
+        port = 8085;
+
+        trusted-proxies = [
+          "::1/128"
+          "172.17.0.0/24"
+        ];
+
+        storage-backend = lib.mkDefault "local";
+        storage-local-base-path = "${config.common.services.gotosocial.stateDir}/storage";
+
+        host = "fedi.ctu.cx";
+        account-domain = "ctu.cx";
+
+        landing-page-user = "katja";
+
+        accounts-allow-custom-css = true;
+        accounts-registration-open = false;
+
+        instance-expose-peers = true;
+        instance-expose-suspended = true;
+        instance-expose-suspended-web = true;
+
+        instance-languages = [
+          "de"
+          "en-us"
+        ];
+
+        media-local-max-size = "50MiB";
+        media-remote-max-size = "50MiB";
+
+        media-remote-cache-duration = "3 days";
+        media-cleanup-cron = "30 23 * * *";
+
+        statuses-cleanup-remote-older-than = "1 year";
+      };
+    };
+
+    services.nginx = {
+      appendHttpConfig = ''
+        proxy_cache_path /var/cache/nginx keys_zone=gotosocial_ap_public_responses:10m inactive=1w;
+      '';
+      virtualHosts = {
+        "${config.common.services.gotosocial.settings.host}" = {
+          useACMEHost = lib.mkDefault config.networking.fqdn;
+          forceSSL = lib.mkDefault true;
+          kTLS = lib.mkDefault true;
+          locations = {
+            "/" = {
+              proxyPass = "http://[${toString config.common.services.gotosocial.settings.bind-address}]:${toString config.common.services.gotosocial.settings.port}";
+              proxyWebsockets = true;
+            };
+
+            "~ /.well-known/(webfinger|host-meta)$" = {
+              proxyPass = "http://[${toString config.common.services.gotosocial.settings.bind-address}]:${toString config.common.services.gotosocial.settings.port}";
+              extraConfig = ''
+                proxy_cache gotosocial_ap_public_responses;
+                proxy_cache_background_update on;
+                proxy_cache_key $scheme://$host$uri$is_args$query_string;
+                proxy_cache_valid 200 10m;
+                proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504 http_429;
+                proxy_cache_lock on;
+                add_header X-Cache-Status $upstream_cache_status;
+              '';
+            };
+
+            "~ ^\/users\/(?:[a-z0-9_\.]+)\/main-key$" = {
+              proxyPass = "http://[${toString config.common.services.gotosocial.settings.bind-address}]:${toString config.common.services.gotosocial.settings.port}";
+              extraConfig = ''
+                proxy_cache gotosocial_ap_public_responses;
+                proxy_cache_background_update on;
+                proxy_cache_key $scheme://$host$uri;
+                proxy_cache_valid 200 604800s;
+                proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504 http_429;
+                proxy_cache_lock on;
+
+                add_header X-Cache-Status $upstream_cache_status;
+              '';
+            };
+
+            "/assets/".extraConfig = ''
+              alias ${config.common.services.gotosocial.package}/share/gotosocial/web/assets/;
+              autoindex off;
+              expires max;
+              add_header Cache-Control "public, immutable";
+            '';
+          };
+        };
+      }
+      // (
+        if
+          (
+            config.common.services.gotosocial.settings.account-domain
+            != config.common.services.gotosocial.settings.host
+          )
+        then
+          {
+            "${config.common.services.gotosocial.settings.account-domain}" = {
+              locations = {
+                "= /.well-known/host-meta".extraConfig =
+                  "return 301 https://${config.common.services.gotosocial.settings.host}$request_uri;";
+                "= /.well-known/webfinger".extraConfig =
+                  "return 301 https://${config.common.services.gotosocial.settings.host}$request_uri;";
+                "= /.well-known/nodeinfo".extraConfig =
+                  "return 301 https://${config.common.services.gotosocial.settings.host}$request_uri;";
+              };
+            };
+          }
+        else
+          { }
+      );
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/websites/fedi.home.ctu.cx.nix b/nixosModules/zpha/websites/fedi.home.ctu.cx.nix
@@ -0,0 +1,185 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.zpha.websites."fedi.home.ctu.cx".enable = lib.mkEnableOption "";
+
+  config = lib.mkIf config.zpha.websites."fedi.home.ctu.cx".enable {
+    dns.zones."ctu.cx".subdomains."fedi.home".CNAME = [
+      "${config.networking.fqdn}."
+    ];
+
+    sops.secrets = {
+      "restic/gotosocial/repositoryPassword" = { };
+      "restic/gotosocial/sshPrivateKey" = { };
+    };
+
+    systemd.tmpfiles.settings.gotosocial = {
+      "/var/lib/gotosocial/storage".d = {
+        inherit (config.common.services.gotosocial) user;
+        inherit (config.common.services.gotosocial) group;
+        mode = "750";
+        age = "-";
+      };
+    };
+
+    common = {
+      configure.persist.system.dirs = lib.singleton {
+        inherit (config.common.services.gotosocial) user group;
+        directory = config.common.services.gotosocial.stateDir;
+        mode = "0755";
+      };
+
+      services.resticBackup.gotosocial = {
+        enable = true;
+        inherit (config.common.services.gotosocial) user;
+        targets = [
+          "restic-target.fc9f.de"
+          "isodon.fc9f.de"
+        ];
+        sshKeyFile = config.sops.secrets."restic/gotosocial/sshPrivateKey".path;
+        passwordFile = config.sops.secrets."restic/gotosocial/repositoryPassword".path;
+        sqliteDatabases = [
+          (lib.mkIf (
+            config.common.services.gotosocial.settings.db-type == "sqlite"
+          ) config.common.services.gotosocial.settings.db-address)
+        ];
+        paths = [
+          (lib.mkIf (
+            config.common.services.gotosocial.settings.storage-backend == "local"
+          ) config.common.services.gotosocial.settings.storage-local-base-path)
+          "${config.common.services.gotosocial.stateDir}/backup.json"
+        ];
+        runBeforeBackup = ''
+          ${config.common.services.gotosocial.package}/bin/gotosocial --config-path /etc/gotosocial.yaml admin export --path ${config.common.services.gotosocial.stateDir}/backup.json
+        '';
+      };
+    };
+
+    common.services.gotosocial = {
+      enable = true;
+      package = pkgs.zpha.gotosocial-unstable;
+      inherit (config.services.nginx) group;
+      settings = {
+        protocol = "https";
+
+        bind-address = "::1";
+        port = 8085;
+
+        trusted-proxies = [
+          "::1/128"
+          "172.17.0.0/24"
+        ];
+
+        storage-backend = lib.mkDefault "local";
+        storage-local-base-path = "${config.common.services.gotosocial.stateDir}/storage";
+
+        host = "fedi.home.ctu.cx";
+        account-domain = "fedi.home.ctu.cx";
+
+        landing-page-user = "leah";
+
+        accounts-allow-custom-css = true;
+        accounts-registration-open = false;
+
+        instance-expose-peers = true;
+        instance-expose-suspended = true;
+        instance-expose-suspended-web = true;
+
+        instance-languages = [
+          "de"
+          "en-us"
+        ];
+
+        media-local-max-size = "50MiB";
+        media-remote-max-size = "50MiB";
+
+        media-remote-cache-duration = "3 days";
+        media-cleanup-cron = "30 23 * * *";
+
+        statuses-cleanup-remote-older-than = "1 year";
+      };
+    };
+
+    services.nginx = {
+      appendHttpConfig = ''
+        proxy_cache_path /var/cache/nginx keys_zone=gotosocial_ap_public_responses:10m inactive=1w;
+      '';
+      virtualHosts = {
+        "${config.common.services.gotosocial.settings.host}" = {
+          useACMEHost = lib.mkDefault config.networking.fqdn;
+          forceSSL = lib.mkDefault true;
+          kTLS = lib.mkDefault true;
+          locations = {
+            "/" = {
+              proxyPass = "http://[${toString config.common.services.gotosocial.settings.bind-address}]:${toString config.common.services.gotosocial.settings.port}";
+              proxyWebsockets = true;
+            };
+
+            "~ /.well-known/(webfinger|host-meta)$" = {
+              proxyPass = "http://[${toString config.common.services.gotosocial.settings.bind-address}]:${toString config.common.services.gotosocial.settings.port}";
+              extraConfig = ''
+                proxy_cache gotosocial_ap_public_responses;
+                proxy_cache_background_update on;
+                proxy_cache_key $scheme://$host$uri$is_args$query_string;
+                proxy_cache_valid 200 10m;
+                proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504 http_429;
+                proxy_cache_lock on;
+                add_header X-Cache-Status $upstream_cache_status;
+              '';
+            };
+
+            "~ ^\/users\/(?:[a-z0-9_\.]+)\/main-key$" = {
+              proxyPass = "http://[${toString config.common.services.gotosocial.settings.bind-address}]:${toString config.common.services.gotosocial.settings.port}";
+              extraConfig = ''
+                proxy_cache gotosocial_ap_public_responses;
+                proxy_cache_background_update on;
+                proxy_cache_key $scheme://$host$uri;
+                proxy_cache_valid 200 604800s;
+                proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504 http_429;
+                proxy_cache_lock on;
+
+                add_header X-Cache-Status $upstream_cache_status;
+              '';
+            };
+
+            "/assets/".extraConfig = ''
+              alias ${config.common.services.gotosocial.package}/share/gotosocial/web/assets/;
+              autoindex off;
+              expires max;
+              add_header Cache-Control "public, immutable";
+            '';
+          };
+        };
+      }
+      // (
+        if
+          (
+            config.common.services.gotosocial.settings.account-domain
+            != config.common.services.gotosocial.settings.host
+          )
+        then
+          {
+            "${config.common.services.gotosocial.settings.account-domain}" = {
+              locations = {
+                "= /.well-known/host-meta".extraConfig =
+                  "return 301 https://${config.common.services.gotosocial.settings.host}$request_uri;";
+                "= /.well-known/webfinger".extraConfig =
+                  "return 301 https://${config.common.services.gotosocial.settings.host}$request_uri;";
+                "= /.well-known/nodeinfo".extraConfig =
+                  "return 301 https://${config.common.services.gotosocial.settings.host}$request_uri;";
+              };
+            };
+          }
+        else
+          { }
+      );
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/websites/flauschehorn.zaphyra.eu.nix b/nixosModules/zpha/websites/flauschehorn.zaphyra.eu.nix
@@ -0,0 +1,94 @@
+{
+  config,
+  pkgs,
+  lib,
+  ...
+}:
+
+{
+
+  options.zpha.websites."flauschehorn.zaphyra.eu".enable = lib.mkEnableOption "";
+
+  config = lib.mkIf config.zpha.websites."flauschehorn.zaphyra.eu".enable {
+    dns.zones."zaphyra.eu".subdomains."flauschehorn".CNAME = [
+      "${config.networking.fqdn}."
+    ];
+
+    # required because this subdomain is still set in the flauschehorn.sexy-zone
+    dns.zones."ctu.cx".subdomains."63bc37c61bda3c1f4fa1f270f8890c7f89c24353.acme".CNAME = [
+      "63bc37c61bda3c1f4fa1f270f8890c7f89c24353.acme.fc9f.de."
+    ];
+
+    common.configure.persist.system.dirs = [
+      "/var/lib/private/flauschehorn"
+    ];
+
+    systemd.services.flauschehornFetcher = {
+      environment.DB_PATH = "/var/lib/flauschehorn/db.sqlite";
+      startAt = "*-*-* 3:00:00";
+      wants = [ "network-online.target" ];
+      after = [ "network-online.target" ];
+      serviceConfig = {
+        Type = "oneshot";
+
+        ExecStart = "${pkgs.zpha.flauschehorn-sexy}/bin/mastofetch";
+
+        DynamicUser = true;
+        User = "flauschehorn";
+        Group = "flauschehorn";
+
+        StateDirectory = "flauschehorn";
+        StateDirectoryMode = "755";
+        UMask = "022";
+
+        NoNewPrivileges = true;
+        PrivateTmp = true;
+        PrivateDevices = true;
+
+        RestrictAddressFamilies = "AF_INET AF_INET6";
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+
+        ProtectSystem = "full";
+        ProtectControlGroups = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+
+        DevicePolicy = "closed";
+        LockPersonality = true;
+      };
+    };
+
+    systemd.services.fcgiwrap-flauschehorn.serviceConfig = {
+      DynamicUser = true;
+      User = "flauschehorn";
+      Group = "flauschehorn";
+
+      StateDirectory = "flauschehorn";
+      StateDirectoryMode = "555";
+    };
+
+    services.fcgiwrap.instances.flauschehorn = {
+      socket.user = config.services.nginx.user;
+      socket.group = config.services.nginx.group;
+    };
+
+    services.nginx = {
+      enable = true;
+      virtualHosts."flauschehorn.zaphyra.eu" = {
+        serverAliases = [ "flauschehorn.sexy" ];
+        useACMEHost = "${config.networking.fqdn}";
+        forceSSL = true;
+        kTLS = true;
+        locations."/".extraConfig = ''
+          include "${pkgs.nginx}/conf/fastcgi_params";
+          fastcgi_param SCRIPT_FILENAME "${pkgs.zpha.flauschehorn-sexy}/bin/website";
+          fastcgi_param DB_PATH         "${config.systemd.services.flauschehornFetcher.environment.DB_PATH}";
+          fastcgi_param QUERY_STRING    $args;
+          fastcgi_pass  unix:${config.services.fcgiwrap.instances.flauschehorn.socket.address};
+        '';
+      };
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/websites/git.zaphyra.eu.nix b/nixosModules/zpha/websites/git.zaphyra.eu.nix
@@ -0,0 +1,273 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.zpha.websites."git.zaphyra.eu".enable = lib.mkEnableOption "";
+
+  config = lib.mkIf config.zpha.websites."git.zaphyra.eu".enable (
+    let
+      adminPubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILTOkSPzd70xNTgPOylix/lb5JKYdt+URoewlmGRP3oZ";
+      title = "zaphyra's git";
+      mail = "git@zaphyra.eu";
+      categories = [
+        "nix"
+        "etc"
+        "javascript"
+        "golang"
+        "nimlang"
+        "nimlang libraries"
+        "archive"
+      ];
+
+      stagitFunctions = pkgs.writeShellScript "stagitFunctions" ''
+        is_public_and_listed() {
+          if [ ! -f "$1/git-daemon-export-ok" ]; then
+            return 1
+          fi
+          return 0
+        }
+
+        is_forced_update() {
+          test "$oldrev" = "0000000000000000000000000000000000000000" && return 1
+          test "$newrev" = "0000000000000000000000000000000000000000" && return 1
+
+          hasrevs="$(${pkgs.git}/bin/git rev-list "$oldrev" "^$newrev" | ${pkgs.gnused}/bin/sed 1q)"
+          if test -n "$hasrevs"; then
+            return 0
+          fi
+          return 1
+        }
+
+        build_stagit_repo() {
+          reponame="$(basename "$1" ".git")"
+          printf "[%s] Generate stagit HTML pages... " "$reponame"
+
+          mkdir -p "${config.common.services.gitolite.dataDir}/stagit-cache"
+          mkdir -p "/var/lib/stagit/$reponame"
+
+          cd "/var/lib/stagit/$reponame" || return 1
+
+          # build repo pages
+          ${lib.getExe pkgs.zpha.stagit} -c "${config.common.services.gitolite.dataDir}/stagit-cache/$reponame" -n "${title}" -h 'https://git.zaphyra.eu/' -s 'git@git.zaphyra.eu:' "$1"
+
+          # set correct permissions
+          chown git:git -R /var/lib/stagit/$reponame;
+          chmod 755 -R /var/lib/stagit/$reponame;
+
+          echo "done"
+        }
+
+        build_stagit_index() {
+          printf "Generating stagit index... "
+
+          # set assets if not already there
+          ln -sf "${pkgs.zpha.stagit}/share/doc/stagit/highlight.min.js" "/var/lib/stagit/highlight.min.js" 2> /dev/null
+          ln -sf "${pkgs.zpha.stagit}/share/doc/stagit/style.css"        "/var/lib/stagit/style.css" 2> /dev/null
+
+          # generate index arguments
+          args="-n \"${title}\" -e '${mail}'"
+
+          for category in ${lib.escapeShellArgs categories}; do
+            args="$args -c '$category'"
+            for repo in "$HOME/repositories/"*.git/; do
+              repo="''${repo%/}"
+              is_public_and_listed "$repo" || continue
+
+              [ "$(${pkgs.gawk}/bin/awk -F '=' '/category/ {print $2}' $repo/config | ${pkgs.gnused}/bin/sed -e 's/^[[:space:]]*//')" = "$category" ] && args="$args $repo"
+            done
+          done
+
+          # build index
+          echo "$args" | xargs ${pkgs.zpha.stagit}/bin/stagit-index > /var/lib/stagit/index.html
+
+          # set correct permissions
+          chown git:git /var/lib/stagit/index.html;
+          chmod 755 /var/lib/stagit/index.html;
+
+          echo "done"
+        }
+
+
+        update_stagit_repo() {
+          repo="$(pwd)"
+          reponame="$(basename "$repo" ".git")"
+
+          cd "$repo" || return 1
+          is_public_and_listed "$repo" || return 0
+
+          # if forced update, remove directory and cache file
+          is_forced_update && printf "[%s] Forced update, trigger complete regeneration of stagit-pages... \n" "$reponame" && rm -rf "/var/lib/stagit/$reponame" "/var/lib/gitolite/stagit-cache/$reponame"
+
+          build_stagit_repo "$repo"
+          build_stagit_index
+        }
+
+      '';
+
+      rebuildWebdir = ''
+        source ${stagitFunctions}
+
+        # clear webdir
+        rm -rf /var/lib/stagit/*
+
+        # clear cache
+        rm -rf ${config.common.services.gitolite.dataDir}/stagit-cache/*
+
+        # generate pages per repo
+        for repo in "$HOME/repositories/"*.git/; do
+          repo="''${repo%/}"
+          is_public_and_listed "$repo" || continue
+
+          build_stagit_repo "$repo"
+        done
+
+        # generate index page
+        build_stagit_index
+      '';
+
+    in
+    {
+      dns.zones = {
+        "zaphyra.eu".subdomains."git".CNAME = lib.singleton "${config.networking.fqdn}.";
+        "katja.wtf".subdomains."git".CNAME = lib.singleton "${config.networking.fqdn}.";
+        "ctu.cx".subdomains."git".CNAME = lib.singleton "${config.networking.fqdn}.";
+      };
+
+      sops.secrets = {
+        "restic/gitolite/repositoryPassword" = { };
+        "restic/gitolite/sshPrivateKey" = { };
+      };
+
+      common = {
+        configure.persist.system.dirs = [
+          {
+            directory = "/var/lib/stagit";
+            mode = "0755";
+            inherit (config.common.services.gitolite) user group;
+          }
+        ];
+
+        services.resticBackup.gitolite = {
+          inherit (config.common.services.gitolite) user;
+          enable = true;
+          targets = [ "isodon.fc9f.de" ];
+          sshKeyFile = config.sops.secrets."restic/gitolite/sshPrivateKey".path;
+          passwordFile = config.sops.secrets."restic/gitolite/repositoryPassword".path;
+          paths = [ config.common.services.gitolite.dataDir ];
+        };
+
+        services.gitolite = {
+          enable = true;
+          user = "git";
+          group = "git";
+          inherit adminPubkey;
+
+          extraGitoliteRc = ''
+            $RC{GIT_CONFIG_KEYS} = ".*";
+            $RC{UMASK}           = 0027;
+
+            push(@{$RC{ENABLE}}, 'cgit');
+            push(@{$RC{ENABLE}}, 'symbolic-ref');
+            push(@{$RC{ENABLE}}, 'rebuild-webdir');
+            push(@{$RC{ENABLE}}, 'rebuild-webdir');
+
+            $RC{NON_CORE} = "rebuild-webdir-trigger POST_COMPILE rebuild-stagit";
+          '';
+
+          triggers.rebuild-webdir = rebuildWebdir;
+          commands.rebuild-webdir = rebuildWebdir;
+          commonHooks.post-receive = ''
+            # update stagit pages
+            source ${stagitFunctions}
+            update_stagit_repo "$1"
+          '';
+        };
+      };
+
+      services = {
+        fcgiwrap = {
+          instances.git = {
+            process.user = "git";
+            process.group = "git";
+            socket.user = "nginx";
+            socket.group = "nginx";
+          };
+        };
+
+        nginx = {
+          enable = true;
+          virtualHosts = {
+            "git.ctu.cx" = {
+              serverAliases = [ "git.katja.wtf" ];
+              useACMEHost = config.networking.fqdn;
+              forceSSL = true;
+              kTLS = true;
+              locations."/".return = "307 https://git.zaphyra.eu$request_uri";
+            };
+
+            "git.zaphyra.eu" = {
+              useACMEHost = config.networking.fqdn;
+              forceSSL = true;
+              kTLS = true;
+              root = "/var/lib/stagit";
+              locations = {
+                "@redir".return = "307 ../log.html";
+                "~ '^/([a-zA-Z0-9_.]+)/commit/.*$'".extraConfig = "error_page 404 = @redir;";
+
+                "~* \.html$".extraConfig = ''
+                  add_header Last-Modified $date_gmt;
+                  add_header Cache-Control 'private no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
+                  if_modified_since off;
+                  expires off;
+                  etag off;
+                '';
+
+                "~ '^/[a-zA-Z0-9._-]+/raw'".extraConfig = ''
+                  types {
+                    application/json                                 json;
+
+                    application/wasm                                 wasm;
+                    font/woff                                        woff;
+                    font/woff2                                       woff2;
+
+                    application/pdf                                  pdf;
+
+                    image/gif                                        gif;
+                    image/jpeg                                       jpeg jpg;
+                    image/png                                        png;
+                    image/svg+xml                                    svg svgz;
+                    image/webp                                       webp;
+                    image/x-icon                                     ico;
+                  }
+
+                  default_type   text/plain;
+                  try_files $uri =404;
+                '';
+
+                "~ '^/[a-zA-Z0-9._-]+/(git-(receive|upload)-pack|HEAD|info/refs|objects/(info/(http-)?alternates|packs)|[0-9a-f]{2}/[0-9a-f]{38}|pack/pack-[0-9a-f]{40}\.(pack|idx))$'".extraConfig =
+                  ''
+                    if ($query_string = service=git-receive-pack) {
+                      return 403;
+                    }
+
+                    include "${pkgs.nginx}/conf/fastcgi_params";
+                    fastcgi_param SCRIPT_FILENAME  "${pkgs.git}/libexec/git-core/git-http-backend";
+                    fastcgi_param GIT_PROJECT_ROOT /var/lib/gitolite/repositories;
+                    fastcgi_param PATH_INFO        $uri;
+                    fastcgi_pass  unix:${config.services.fcgiwrap.instances.git.socket.address};
+                  '';
+              };
+            };
+          };
+        };
+
+      };
+    }
+  );
+
+}
diff --git a/nixosModules/zpha/websites/gomuks.zaphyra.eu.nix b/nixosModules/zpha/websites/gomuks.zaphyra.eu.nix
@@ -0,0 +1,46 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.zpha.websites."gomuks.zaphyra.eu".enable = lib.mkEnableOption "";
+
+  config = lib.mkIf config.zpha.websites."gomuks.zaphyra.eu".enable {
+    dns.zones."zaphyra.eu".subdomains."gomuks".CNAME = [ "${config.networking.fqdn}." ];
+
+    common.configure.persist.system.dirs = [ "/var/lib/private/gomuks-zaphyra" ];
+
+    sops.secrets."gomuks/username" = { };
+    sops.secrets."gomuks/password" = { };
+
+    systemd.services.gomuks-zaphyra.serviceConfig.LoadCredential = [
+      "WEB_USERNAME:${config.sops.secrets."gomuks/username".path}"
+      "WEB_PASSWORD:${config.sops.secrets."gomuks/password".path}"
+    ];
+
+    services = {
+      gomuks.zaphyra = {
+        package = pkgs.unstable.gomuks-web;
+        settings = {
+          web.listen_address = "[::1]:29325";
+          web.username = "\${WEB_USERNAME}";
+          web.password_hash = "\${WEB_PASSWORD}";
+        };
+      };
+      nginx.virtualHosts."gomuks.zaphyra.eu" = {
+        useACMEHost = config.networking.fqdn;
+        forceSSL = true;
+        kTLS = true;
+        locations."/" = {
+          proxyPass = "http://${config.services.gomuks.zaphyra.settings.web.listen_address}";
+          proxyWebsockets = true;
+        };
+      };
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/websites/gts.zaphyra.eu.nix b/nixosModules/zpha/websites/gts.zaphyra.eu.nix
@@ -0,0 +1,212 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.zpha.websites."gts.zaphyra.eu".enable = lib.mkEnableOption "";
+
+  config = lib.mkIf config.zpha.websites."gts.zaphyra.eu".enable {
+    dns.zones."zaphyra.eu".subdomains."gts".CNAME = [ "${config.networking.fqdn}." ];
+
+    users = {
+      users.gotosocial.uid = 818;
+      groups.gotosocial.gid = 818;
+    };
+
+    sops.secrets = {
+      "restic/gotosocial/repositoryPassword" = { };
+      "restic/gotosocial/sshPrivateKey" = { };
+      "environments/gotosocial" = {
+        restartUnits = [ "gotosocial.service" ];
+      };
+    };
+
+    systemd.tmpfiles.settings.gotosocial = {
+      "/var/lib/gotosocial/storage".d = {
+        inherit (config.common.services.gotosocial) user group;
+        mode = "750";
+        age = "-";
+      };
+    };
+
+    common = {
+      configure.persist.system.dirs = lib.singleton {
+        inherit (config.common.services.gotosocial) user group;
+        directory = config.common.services.gotosocial.stateDir;
+        mode = "0755";
+      };
+
+      services.resticBackup.gotosocial = {
+        enable = true;
+        inherit (config.common.services.gotosocial) user;
+        targets = [
+          "restic-target.fc9f.de"
+          "isodon.fc9f.de"
+        ];
+        sshKeyFile = config.sops.secrets."restic/gotosocial/sshPrivateKey".path;
+        passwordFile = config.sops.secrets."restic/gotosocial/repositoryPassword".path;
+        sqliteDatabases = [
+          (lib.mkIf (
+            config.common.services.gotosocial.settings.db-type == "sqlite"
+          ) config.common.services.gotosocial.settings.db-address)
+        ];
+        paths = [
+          (lib.mkIf (
+            config.common.services.gotosocial.settings.storage-backend == "local"
+          ) config.common.services.gotosocial.settings.storage-local-base-path)
+          "${config.common.services.gotosocial.stateDir}/backup.json"
+        ];
+        runBeforeBackup = ''
+          ${config.common.services.gotosocial.package}/bin/gotosocial --config-path /etc/gotosocial.yaml admin export --path ${config.common.services.gotosocial.stateDir}/backup.json
+        '';
+      };
+    };
+
+    common.services.gotosocial = {
+      enable = true;
+      package = pkgs.zpha.gotosocial-unstable;
+      inherit (config.services.nginx) group;
+      environmentFile = config.sops.secrets."environments/gotosocial".path;
+      settings = {
+        protocol = "https";
+
+        bind-address = "::1";
+        port = 8085;
+
+        trusted-proxies = [
+          "::1/128"
+          "172.17.0.0/24"
+        ];
+
+        db-type = "sqlite";
+        db-address = "${config.common.services.gotosocial.stateDir}/database.sqlite";
+
+        storage-backend = "local";
+        storage-local-base-path = "${config.common.services.gotosocial.stateDir}/storage";
+
+        host = "gts.zaphyra.eu";
+        account-domain = "zaphyra.eu";
+
+        landing-page-user = "katja";
+
+        accounts-allow-custom-css = true;
+        accounts-registration-open = false;
+
+        instance-expose-peers = true;
+        instance-expose-suspended = true;
+        instance-expose-suspended-web = true;
+
+        instance-languages = [
+          "en"
+          "de"
+        ];
+
+        media-emoji-local-max-size = "100KiB";
+
+        media-local-max-size = "50MiB";
+        media-remote-max-size = "50MiB";
+
+        media-remote-cache-duration = "3 days";
+        media-cleanup-cron = "30 23 * * *";
+
+        smtp-host = "morio.infra.zaphyra.eu";
+        smtp-port = 587;
+        smtp-username = "gts@zaphyra.eu";
+        smtp-from = "gts@zaphyra.eu";
+
+        advanced-rate-limit-requests = 3000;
+      };
+    };
+
+    services.nginx = {
+      appendHttpConfig = ''
+        proxy_cache_path /var/cache/nginx keys_zone=gotosocial_ap_public_responses:10m inactive=1w;
+      '';
+      virtualHosts = {
+        "${config.common.services.gotosocial.settings.host}" = {
+          useACMEHost = lib.mkDefault config.networking.fqdn;
+          forceSSL = lib.mkDefault true;
+          kTLS = lib.mkDefault true;
+          locations = {
+            "/" = {
+              proxyPass = "http://[${toString config.common.services.gotosocial.settings.bind-address}]:${toString config.common.services.gotosocial.settings.port}";
+              proxyWebsockets = true;
+            };
+
+            "/client/" = {
+              index = "index.html";
+              alias = "${
+                pkgs.zpha.phanpy.override {
+                  clientName = "zaphyra's fedi";
+                  website = "https://gts.zaphyra.eu/client/";
+                  defaultInstance = "gts.zaphyra.eu";
+                }
+              }/";
+            };
+
+            "~ /.well-known/(webfinger|host-meta)$" = {
+              proxyPass = "http://[${toString config.common.services.gotosocial.settings.bind-address}]:${toString config.common.services.gotosocial.settings.port}";
+              extraConfig = ''
+                proxy_cache gotosocial_ap_public_responses;
+                proxy_cache_background_update on;
+                proxy_cache_key $scheme://$host$uri$is_args$query_string;
+                proxy_cache_valid 200 10m;
+                proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504 http_429;
+                proxy_cache_lock on;
+                add_header X-Cache-Status $upstream_cache_status;
+              '';
+            };
+
+            "~ ^\/users\/(?:[a-z0-9_\.]+)\/main-key$" = {
+              proxyPass = "http://[${toString config.common.services.gotosocial.settings.bind-address}]:${toString config.common.services.gotosocial.settings.port}";
+              extraConfig = ''
+                proxy_cache gotosocial_ap_public_responses;
+                proxy_cache_background_update on;
+                proxy_cache_key $scheme://$host$uri;
+                proxy_cache_valid 200 604800s;
+                proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504 http_429;
+                proxy_cache_lock on;
+
+                add_header X-Cache-Status $upstream_cache_status;
+              '';
+            };
+
+            "/assets/".extraConfig = ''
+              alias ${config.common.services.gotosocial.package}/share/gotosocial/web/assets/;
+              autoindex off;
+              expires max;
+              add_header Cache-Control "public, immutable";
+            '';
+          };
+        };
+      }
+      // (
+        if
+          (
+            config.common.services.gotosocial.settings.account-domain
+            != config.common.services.gotosocial.settings.host
+          )
+        then
+          {
+            "${config.common.services.gotosocial.settings.account-domain}" = {
+              locations = {
+                "= /.well-known/host-meta".extraConfig =
+                  "return 301 https://${config.common.services.gotosocial.settings.host}$request_uri;";
+                "= /.well-known/webfinger".extraConfig =
+                  "return 301 https://${config.common.services.gotosocial.settings.host}$request_uri;";
+                "= /.well-known/nodeinfo".extraConfig =
+                  "return 301 https://${config.common.services.gotosocial.settings.host}$request_uri;";
+              };
+            };
+          }
+        else
+          { }
+      );
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/websites/hass.zaphyra.eu.nix b/nixosModules/zpha/websites/hass.zaphyra.eu.nix
@@ -0,0 +1,82 @@
+{
+  machineConfig,
+  sopsSecrets,
+  resources,
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.zpha.websites."hass.zaphyra.eu".enable = lib.mkEnableOption "";
+
+  config = lib.mkIf config.zpha.websites."hass.zaphyra.eu".enable {
+    dns.zones = {
+      "zaphyra.eu".subdomains."hass".AAAA = lib.singleton machineConfig.networking.dn42.ip6Address;
+      "zaphyra.dn42".subdomains."hass".AAAA = lib.singleton machineConfig.networking.dn42.ip6Address;
+      "fc9f.de".subdomains."floractl".A = lib.singleton machineConfig.networking.ip4Address;
+    };
+
+    sops.secrets."floractl/config.json" = {
+      sopsFile = sopsSecrets.zaphyra.floractl;
+      key = "config";
+      owner = "nginx";
+      group = "nginx";
+    };
+
+    security.acme.certs."hass.zaphyra.dn42" = {
+      server = "https://acme.burble.dn42/v1/dn42/acme/directory";
+      validMinDays = 20;
+      keyType = "ec384";
+      dnsProvider = null;
+    };
+
+    services.nginx.virtualHosts =
+      let
+        vHost = {
+          forceSSL = true;
+          kTLS = true;
+          locations."/".extraConfig = ''
+            allow fd6b:6174:6a61::/48;
+            allow fd42:ccc:da::/48;
+            deny all;
+
+            proxy_pass http://192.168.2.147:8123;
+            proxy_http_version 1.1;
+            proxy_set_header Host 192.168.2.147;
+            proxy_set_header Upgrade $http_upgrade;
+            proxy_set_header Connection "upgrade";
+          '';
+        };
+
+      in
+      {
+        "hass.zaphyra.dn42" = vHost // {
+          enableACME = true;
+        };
+        "hass.zaphyra.eu" = vHost // {
+          useACMEHost = config.networking.fqdn;
+        };
+        "floractl.fc9f.de" = {
+          forceSSL = true;
+          useACMEHost = config.networking.fqdn;
+          kTLS = true;
+          root = pkgs.zpha.mqtt-webui.override {
+            patches = [
+              resources.patches.mqttwebui-florapatches-owo
+            ];
+          };
+          locations = {
+            "= /config.json".alias = config.sops.secrets."floractl/config.json".path;
+            "/mqtt" = {
+              proxyPass = "http://192.168.2.147:1884/";
+              proxyWebsockets = true;
+            };
+          };
+        };
+      };
+  };
+
+}
diff --git a/nixosModules/zpha/websites/ip.fc9f.de.nix b/nixosModules/zpha/websites/ip.fc9f.de.nix
@@ -0,0 +1,95 @@
+{
+  machineConfig,
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.zpha.websites."ip.fc9f.de".enable = lib.mkEnableOption "";
+
+  config = lib.mkIf config.zpha.websites."ip.fc9f.de".enable {
+    dns.zones."fc9f.de".subdomains = {
+      "ip" =
+        pkgs.dnsNix.combinators.host machineConfig.networking.ip4Address machineConfig.networking.ip6Address;
+      "ip4".A = [ machineConfig.networking.ip4Address ];
+      "ip6".AAAA = [ machineConfig.networking.ip6Address ];
+    };
+
+    services.nginx.virtualHosts = {
+      "ip.fc9f.de" = {
+        useACMEHost = config.networking.fqdn;
+        forceSSL = true;
+        kTLS = true;
+        locations."/" = {
+          extraConfig = "types { } default_type 'text/html; charset=utf-8';";
+          return = ''
+            200 '
+              <!DOCTYPE html>
+              <html>
+                <head>
+                  <title>ip.fc9f.de</title>
+                </head>
+                <body>
+                  <h1>ip.fc9f.de</h1>
+                  <ul>
+                    <li><span style="user-select: none;"><b>IPv6:</b> </span><span id="ip6">Loading...</span></li>
+                    <li><span style="user-select: none;"><b>IPv4:</b> </span><span id="ip4">Loading...</span></li>
+                  </ul>
+                  <p>Use bash and curl: <code>curl ip{4,6}.zaphyra.eu</code></p>
+                  <p><small>Because any other "Whats my IP?"-tool sucks. <a href="https://git.clerie.de/clerie/ip.clerie.de">Host yourself :3</a></small></p>
+
+                  <script>
+                    window.addEventListener("DOMContentLoaded", (event) => {
+                      [ "ip6", "ip4" ].forEach(async (ipVersion) => {
+                        try {
+                          const url      = "https://" + ipVersion + ".fc9f.de/";
+                          const response = await fetch(url + ((/\?/).test(url) ? "&" : "?") + (new Date()).getTime());
+                          if(response.status != 200) {
+                            document.getElementById(ipVersion).innerText = "Error!";
+                          } else {
+                            document.getElementById(ipVersion).innerText = await response.text();
+                          }
+                        } catch {
+                          document.getElementById(ipVersion).innerText = "Error!";
+                        }
+                      });
+                    });
+                  </script>
+                </body>
+              </html>'
+          '';
+        };
+      };
+
+      "ip4.fc9f.de" = {
+        useACMEHost = config.networking.fqdn;
+        forceSSL = true;
+        kTLS = true;
+        locations."/" = {
+          return = "200 '$remote_addr\n'";
+          extraConfig = ''
+            types { } default_type "text/plain; charset=utf-8";
+            add_header Access-Control-Allow-Origin *;
+          '';
+        };
+      };
+
+      "ip6.fc9f.de" = {
+        useACMEHost = config.networking.fqdn;
+        forceSSL = true;
+        kTLS = true;
+        locations."/" = {
+          return = "200 '$remote_addr\n'";
+          extraConfig = ''
+            types { } default_type "text/plain; charset=utf-8";
+            add_header Access-Control-Allow-Origin *;
+          '';
+        };
+      };
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/websites/links.zaphyra.eu.nix b/nixosModules/zpha/websites/links.zaphyra.eu.nix
@@ -0,0 +1,55 @@
+{
+  config,
+  lib,
+  ...
+}:
+
+{
+
+  options.zpha.websites."links.zaphyra.eu".enable = lib.mkEnableOption "";
+
+  config = lib.mkIf config.zpha.websites."links.zaphyra.eu".enable {
+    dns.zones."zaphyra.eu".subdomains."links".CNAME = [ "${config.networking.fqdn}." ];
+
+    # sops.secrets = {
+    #   "restic/memos/repositoryPassword" = { };
+    #   "restic/memos/sshPrivateKey" = { };
+    # };
+
+    common = {
+      configure.persist.system.dirs = [ "/var/lib/private/shiori" ];
+
+      # services.resticBackup.memos = {
+      #   inherit (config.services.memos) user;
+      #   enable = true;
+      #   targets = [
+      #     "restic-target.fc9f.de"
+      #     "isodon.fc9f.de"
+      #   ];
+      #   sshKeyFile = config.sops.secrets."restic/memos/sshPrivateKey".path;
+      #   passwordFile = config.sops.secrets."restic/memos/repositoryPassword".path;
+      #   sqliteDatabases = [ "${config.services.memos.dataDir}/memos_prod.db" ];
+      #   paths = [ "${config.services.memos.dataDir}/uploads" ];
+      # };
+    };
+
+    services = {
+      postgresql.enable = true;
+      shiori = {
+        enable = true;
+        address = "[::1]";
+        port = 7523;
+      };
+      nginx.virtualHosts."links.zaphyra.eu" = {
+        useACMEHost = config.networking.fqdn;
+        forceSSL = true;
+        kTLS = true;
+        locations."/" = {
+          proxyPass = "http://${toString config.services.shiori.address}:${toString config.services.shiori.port}";
+          proxyWebsockets = true;
+        };
+      };
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/websites/memories.zaphyra.eu.nix b/nixosModules/zpha/websites/memories.zaphyra.eu.nix
@@ -0,0 +1,82 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.zpha.websites."memories.zaphyra.eu".enable = lib.mkEnableOption "";
+
+  config = lib.mkIf config.zpha.websites."memories.zaphyra.eu".enable {
+    dns.zones."zaphyra.eu".subdomains."memories".CNAME = [ "${config.networking.fqdn}." ];
+
+    sops.secrets = {
+      "restic/immich/repositoryPassword" = { };
+      "restic/immich/sshPrivateKey" = { };
+    };
+
+    common = {
+      configure.persist.system.dirs = [
+        "/var/lib/postgresql"
+        {
+          inherit (config.services.immich) user group;
+          directory = config.services.immich.mediaLocation;
+          mode = "0750";
+        }
+      ];
+
+      services.resticBackup.immich = {
+        inherit (config.services.immich) user;
+        enable = true;
+        targets = [
+          "restic-target.fc9f.de"
+          "isodon.fc9f.de"
+        ];
+        sshKeyFile = config.sops.secrets."restic/immich/sshPrivateKey".path;
+        passwordFile = config.sops.secrets."restic/immich/repositoryPassword".path;
+        postgresDatabases = [ config.services.immich.database.name ];
+        paths = [ config.services.immich.mediaLocation ];
+      };
+    };
+
+    services = {
+      immich = {
+        enable = true;
+        package = pkgs.unstable.immich;
+        host = "::1";
+        redis.enable = true;
+        machine-learning.enable = true;
+        settings = {
+          server.externalDomain = "https://memories.zaphyra.eu";
+          backup.database.enabled = false;
+          metadata.faces."import" = true;
+          storageTemplate = {
+            enabled = false;
+            hashVerificationEnabled = true;
+            template = "{{y}}/{{y}}{{MM}}/{{dd}}-{{filename}}";
+          };
+        };
+      };
+
+      nginx.virtualHosts."memories.zaphyra.eu" = {
+        useACMEHost = "${config.networking.fqdn}";
+        forceSSL = true;
+        kTLS = true;
+        locations."/" = {
+          proxyPass = "http://[${config.services.immich.host}]:${toString config.services.immich.port}";
+          proxyWebsockets = true;
+          recommendedProxySettings = true;
+          extraConfig = ''
+            client_max_body_size 50000M;
+            proxy_read_timeout   600s;
+            proxy_send_timeout   600s;
+            send_timeout         600s;
+          '';
+        };
+      };
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/websites/music.zaphyra.eu.nix b/nixosModules/zpha/websites/music.zaphyra.eu.nix
@@ -0,0 +1,204 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+let
+  cfg = config.zpha.websites."music.zaphyra.eu";
+
+in
+{
+
+  options.zpha.websites."music.zaphyra.eu" = {
+    enable = lib.mkEnableOption "";
+    subdomain = lib.mkOption {
+      type = lib.types.str;
+      default = "music";
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    dns.zones."zaphyra.eu".subdomains."${cfg.subdomain}".CNAME = [ "${config.networking.fqdn}." ];
+
+    sops.secrets."environments/navidrome" = {
+      restartUnits = [ "navidrome.service" ];
+    };
+
+    users = {
+      users = {
+        navidrome.uid = config.services.navidrome.settings.Port;
+        piegames = {
+          isNormalUser = true;
+          createHome = true;
+          home = "/mnt/music/piegames";
+          group = "navidrome";
+          extraGroups = [ "ssh" ];
+          openssh.authorizedKeys.keys = config.users.users.zaphyra.openssh.authorizedKeys.keys ++ [
+            "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC0+vFmtDUgIg18r6a2ezNBRMrZVpAB6dhj37JDhYUu7CSr7o1pR8Sp/UUe/yAEjeyo0R/GfvKIKugCBDCd+6VsDuTkcWX9bwVdrelG3X7pIIt3tWdbqVVGZHFJ/qgdngvIYu3pY++ci6zFlBl8Z8SIXYXpDnVFYTvvBhDIOZJzvyyqL7B/Wm19fTf/HPLTtbuPFMg2gQxo8o5GW3Ow2keJRR1daSGYmGSA1y/F3UumoLuK85Zm0+Kt+yYTO2pMAUBN7Axv1yvnNGbdX9l+3eRHdKatfhCscs720cwR+HtMAfqvN1G5FAWnXdxPa8XIzyZIyrWRoEGPPuK1w2GlWUnDdYLVDMJOmLDz4TvYx2eodfWMRIdppLJYwWpv+Eg8JirO5bnbOnKzVM4C5ESYU8b1mRBI23ncqV+BAno+zehdzLPYg0q3N9wNDF6jqyw08YyiTDwlua4aZJzHDrDt7OjcFaC6t8W1/VimtE0bylhAzVeenJ8C+o3q2jPBvuNLIxk= piegames"
+          ];
+        };
+        music = {
+          isNormalUser = true;
+          home = "/mnt/music/inbox";
+          group = "users";
+          extraGroups = [ "ssh" ];
+          openssh.authorizedKeys.keys = config.users.users.zaphyra.openssh.authorizedKeys.keys ++ [
+            # yuka
+            "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGbzUmOJuuAYn/3ODyw3WKjz7SnKjMq4iHE+mEpwVVmw"
+            # rootile
+            "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINkk3bjlkJWq5y4S9sMfW2Wzo5jQFodVqm/Vn226etjW"
+            # ember
+            "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFsEf6O4VWZFtIholRze7bUi/emzpwTQL4wRJaoPSzrG"
+            "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJWEf+o3fw937guIiiZs8hYiBwrhkrxhul3xr0eRUI2RVGz9OHVOdz6WC5W0+lvQZAIeCmEyCF6DwX7aUANXqUg= misc@secretive.Ember’s-MacBook-Pro.local"
+            # nyaaa (nia)
+            "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMyMojaOLRaEb8+aMrdUZVfTN2V/VoUW8NeyQyNGOedu fly@kinkpad"
+          ];
+        };
+      };
+      groups.navidrome.gid = config.services.navidrome.settings.Port;
+    };
+
+    sops.secrets = {
+      "restic/navidrome/repositoryPassword" = { };
+      "restic/navidrome/sshPrivateKey" = { };
+    };
+
+    fileSystems."/mnt/music-ro" = {
+      device = config.services.syncthing.settings.folders.zaphyra-music-orig.path;
+      fsType = "fuse.bindfs";
+      options = [
+        "ro"
+        "perms=0000:a+rX"
+      ];
+    };
+
+    common = {
+      configure.persist.system.dirs = [
+        {
+          inherit (config.services.navidrome) user group;
+          directory = "/var/lib/navidrome";
+          mode = "0700";
+        }
+        {
+          inherit (config.services.navidrome) user group;
+          directory = "/var/cache/navidrome";
+          mode = "0700";
+        }
+      ];
+
+      services.openssh.enableRSASupport = true;
+      services.resticBackup.navidrome = {
+        inherit (config.services.navidrome) user;
+        enable = true;
+        targets = [
+          "restic-target.fc9f.de"
+          "isodon.fc9f.de"
+        ];
+        sshKeyFile = config.sops.secrets."restic/navidrome/sshPrivateKey".path;
+        passwordFile = config.sops.secrets."restic/navidrome/repositoryPassword".path;
+        postgresDatabases = [ "audiomuse" ];
+        sqliteDatabases = [ "${config.services.navidrome.settings.DataFolder}/navidrome.db" ];
+        runBeforeBackup = ''
+          ${config.systemd.services.navidrome.serviceConfig.ExecStart} backup create
+          ${config.systemd.services.navidrome.serviceConfig.ExecStart} backup prune
+        '';
+        paths = [
+          config.services.navidrome.settings.Backup.Path
+        ];
+      };
+    };
+
+    systemd.services.navidrome.serviceConfig = {
+      MemoryDenyWriteExecute = lib.mkForce false;
+      BindReadOnlyPaths = [ "/mnt/music-ro" ];
+    };
+
+    networking.firewall.allowedTCPPorts = [ config.services.postgresql.settings.port ];
+
+    services = {
+      openssh.extraConfig = ''
+        Match User "music"
+           # AllowTcpForwarding no
+           X11Forwarding no
+      '';
+
+      postgresql = {
+        enable = true;
+        settings = {
+          ssl = true;
+          password_encryption = "scram-sha-256";
+        };
+        enableTCPIP = true;
+        authentication = lib.mkOverride 10 ''
+          #type    database   DBuser    origin-address auth-method
+          local    all        all       peer
+          # ipv4
+          host     audiomuse  audiomuse 172.0.0.0/8    scram-sha-256
+          hostssl  audiomuse  audiomuse ::/0           scram-sha-256
+        '';
+        ensureDatabases = [ "audiomuse" ];
+        ensureUsers = [
+          {
+            name = "audiomuse";
+            ensureDBOwnership = true;
+            ensureClauses.login = true;
+          }
+        ];
+      };
+
+      navidrome = {
+        enable = true;
+        environmentFile = config.sops.secrets."environments/navidrome".path;
+        package = pkgs.unstable.navidrome.overrideAttrs {
+          CGO_CFLAGS_ALLOW = ".*--define-prefix.*";
+        };
+        settings = {
+          Address = "0.0.0.0";
+          BaseUrl = "https://${cfg.subdomain}.zaphyra.eu";
+          MusicFolder = "/mnt/music-ro/defaultLibrary";
+          Deezer.Enabled = true;
+          LastFM.Enabled = true;
+          EnableInsightsCollector = false;
+          EnableExternalServices = true;
+          EnableSharing = true;
+          EnableArtworkPrecache = true;
+          FFmpegPath = lib.getExe pkgs.ffmpeg;
+          DataFolder = "/var/lib/navidrome/state";
+          CacheFolder = "/var/cache/navidrome";
+          ImageCacheSize = "2GB";
+          TranscodingCacheSize = "10GB";
+          UIWelcomeMessage = "sailing on pcm waves";
+          Agents = "audiomuseai,lastfm,spotify,deezer";
+          Backup = {
+            Path = "/var/lib/navidrome/backup";
+            Count = 2;
+            Schedule = "0 0 * * *";
+          };
+          Plugins = {
+            Enabled = true;
+            Folder = "/var/lib/navidrome/plugins"; # Optional: custom plugins folder
+            AutoReload = true; # Useful during development/testing
+            LogLevel = "debug"; # Enable detailed plugin logging
+            CacheSize = "200MB";
+          };
+        };
+      };
+
+      nginx.virtualHosts."${cfg.subdomain}.zaphyra.eu" = {
+        useACMEHost = config.networking.fqdn;
+        forceSSL = true;
+        kTLS = true;
+        root = pkgs.zpha.domsonic;
+        locations = {
+          "/".tryFiles = "$uri $uri/ /index.html";
+        }
+        // (lib.genAttrs [ "/rest" "/auth" "/api" "/app" "/share" ] (name: {
+          proxyPass = "http://127.0.0.1:${toString config.services.navidrome.settings.Port}";
+        }));
+      };
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/websites/notes.zaphyra.eu.nix b/nixosModules/zpha/websites/notes.zaphyra.eu.nix
@@ -0,0 +1,66 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.zpha.websites."notes.zaphyra.eu".enable = lib.mkEnableOption "";
+
+  config = lib.mkIf config.zpha.websites."notes.zaphyra.eu".enable {
+    dns.zones."zaphyra.eu".subdomains."notes".CNAME = [ "${config.networking.fqdn}." ];
+
+    sops.secrets = {
+      "restic/memos/repositoryPassword" = { };
+      "restic/memos/sshPrivateKey" = { };
+    };
+
+    common = {
+      configure.persist.system.dirs = lib.singleton {
+        directory = "/var/lib/memos";
+        mode = "700";
+        inherit (config.services.memos) user group;
+      };
+
+      services.resticBackup.memos = {
+        inherit (config.services.memos) user;
+        enable = true;
+        targets = [
+          "restic-target.fc9f.de"
+          "isodon.fc9f.de"
+        ];
+        sshKeyFile = config.sops.secrets."restic/memos/sshPrivateKey".path;
+        passwordFile = config.sops.secrets."restic/memos/repositoryPassword".path;
+        sqliteDatabases = [ "${config.services.memos.dataDir}/memos_prod.db" ];
+        paths = [ "${config.services.memos.dataDir}/uploads" ];
+      };
+    };
+
+    services = {
+      memos = {
+        enable = true;
+        package = pkgs.zpha.memos;
+        settings = {
+          MEMOS_MODE = "prod";
+          MEMOS_DATA = config.services.memos.dataDir;
+          MEMOS_DRIVER = "sqlite";
+          MEMOS_ADDR = "[::1]";
+          MEMOS_PORT = "5230";
+          MEMOS_INSTANCE_URL = "https://notes.zaphyra.eu";
+        };
+      };
+      nginx.virtualHosts."notes.zaphyra.eu" = {
+        useACMEHost = config.networking.fqdn;
+        forceSSL = true;
+        kTLS = true;
+        locations."/" = {
+          proxyPass = "http://${toString config.services.memos.settings.MEMOS_ADDR}:${toString config.services.memos.settings.MEMOS_PORT}";
+          proxyWebsockets = true;
+        };
+      };
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/websites/oeffi.zaphyra.eu.nix b/nixosModules/zpha/websites/oeffi.zaphyra.eu.nix
@@ -0,0 +1,107 @@
+{
+  config,
+  pkgs,
+  lib,
+  ...
+}:
+
+{
+
+  options.zpha.websites."oeffi.zaphyra.eu".enable = lib.mkEnableOption "";
+
+  config = lib.mkIf config.zpha.websites."oeffi.zaphyra.eu".enable {
+    dns.zones = {
+      "zaphyra.eu".subdomains."oeffi".CNAME = [ "${config.networking.fqdn}." ];
+      "katja.wtf".subdomains."oeffi".CNAME = [ "${config.networking.fqdn}." ];
+    };
+
+    services.nginx = {
+      appendHttpConfig = ''
+        resolver 1.1.1.1 valid=300s;
+
+        map $vendotarget $vendopath {
+          default    no;
+          locations  '/mob/location/search';
+          location   '/mob/location/details';
+          journeys   '/mob/angebote/fahrplan';
+          journey    '/mob/angebote/recon';
+          departures '/mob/bahnhofstafel/abfahrt';
+          trip       '/mob/zuglauf';
+        }
+
+        map $hafastarget $hafasurl {
+          default no;
+          nahsh   nah.sh.hafas.de;
+          rmv     www.rmv.de;
+          bvg     bvg-apps-ext.hafas.de;
+          oebb    fahrplan.oebb.at;
+        }
+
+        map $hafastarget $hafaspath {
+          default no;
+          nahsh   '/bin/mgate.exe';
+          rmv     '/auskunft/bin/jp/mgate.exe';
+          bvg     '/bin/mgate.exe';
+          oebb    '/bin/mgate.exe';
+        }
+      '';
+
+      virtualHosts."oeffi.zaphyra.eu" = {
+        serverAliases = [ "oeffi.katja.wtf" ];
+        useACMEHost = config.networking.fqdn;
+        forceSSL = true;
+        kTLS = true;
+        root = pkgs.zpha.oeffisearch;
+        extraConfig = ''
+          merge_slashes off;
+          large_client_header_buffers 4 16k;
+        '';
+
+        locations = {
+          "/db/vehicle-sequence".extraConfig = ''
+            proxy_ssl_server_name on;
+            proxy_ssl_name        www.bahn.de;
+            proxy_set_header      Host www.bahn.de;
+            proxy_hide_header    'set-cookie';
+            proxy_pass            https://www.bahn.de/web/api/reisebegleitung/wagenreihung/vehicle-sequence$is_args$args;
+          '';
+
+          "~ ^/db/vendo/(?<vendotarget>[a-z]+)(/([^\\r\\n].*))?$".extraConfig = ''
+            if ($vendopath = no) {
+              return 400;
+            }
+
+            if ($vendotarget = 'trip') {
+              set $vendopath '$vendopath$2';
+            }
+
+            if ($vendotarget = 'location') {
+              set $vendopath '$vendopath$2';
+            }
+
+            set $vendodomain 'app.services-bahn.de';
+
+            proxy_ssl_server_name on;
+            proxy_ssl_name        $vendodomain;
+            proxy_set_header      Host $vendodomain;
+            proxy_hide_header    'set-cookie';
+            proxy_pass            https://$vendodomain$vendopath;
+          '';
+
+          "~ ^/hafas/(?<hafastarget>.*)$".extraConfig = ''
+            if ($hafasurl = no) {
+              return 400;
+            }
+
+            proxy_ssl_server_name on;
+            proxy_ssl_name        $hafasurl;
+            proxy_set_header      Host $hafasurl;
+            proxy_hide_header    'set-cookie';
+            proxy_pass            https://$hafasurl$hafaspath;
+          '';
+        };
+      };
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/websites/things.zaphyra.eu.nix b/nixosModules/zpha/websites/things.zaphyra.eu.nix
@@ -0,0 +1,73 @@
+{
+  npins,
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.zpha.websites."things.zaphyra.eu".enable = lib.mkEnableOption "";
+
+  imports =
+    (lib.mkIf config.zpha.websites."things.zaphyra.eu".enable [
+      "${npins.things}/nixosModule.nix"
+    ]).content;
+
+  config = lib.mkIf config.zpha.websites."things.zaphyra.eu".enable {
+    dns.zones."zaphyra.eu".subdomains."things".CNAME = [
+      "${config.networking.fqdn}."
+    ];
+
+    users = {
+      users.things.uid = 512;
+      groups.things.gid = 512;
+    };
+
+    sops.secrets = {
+      "restic/things/repositoryPassword" = { };
+      "restic/things/sshPrivateKey" = { };
+    };
+
+    common = {
+      configure.persist.system.dirs = [
+        {
+          directory = config.services.things.storagePath;
+          mode = "0700";
+          user = "things";
+          group = "things";
+        }
+      ];
+
+      services.resticBackup.things = {
+        inherit (config.services.things) user;
+        enable = true;
+        targets = [
+          "restic-target.fc9f.de"
+          "isodon.fc9f.de"
+        ];
+        sshKeyFile = config.sops.secrets."restic/things/sshPrivateKey".path;
+        passwordFile = config.sops.secrets."restic/things/repositoryPassword".path;
+        paths = [ config.services.things.storagePath ];
+      };
+    };
+
+    services = {
+      things = {
+        enable = true;
+        package = pkgs.zpha.things;
+        storagePath = "/var/lib/things";
+        nginx.enable = true;
+        nginx.domain = "things.zaphyra.eu";
+      };
+
+      nginx.virtualHosts."things.zaphyra.eu" = {
+        useACMEHost = "${config.networking.fqdn}";
+        forceSSL = true;
+        kTLS = true;
+      };
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/websites/vault.zaphyra.eu.nix b/nixosModules/zpha/websites/vault.zaphyra.eu.nix
@@ -0,0 +1,109 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.zpha.websites."vault.zaphyra.eu".enable = lib.mkEnableOption "";
+
+  config = lib.mkIf config.zpha.websites."vault.zaphyra.eu".enable {
+    dns.zones."zaphyra.eu".subdomains."vault".CNAME = [
+      "${config.networking.fqdn}."
+    ];
+
+    users = {
+      users.vaultwarden.uid = 523;
+      groups.vaultwarden.gid = 523;
+    };
+
+    sops.secrets = {
+      "restic/vaultwarden/repositoryPassword" = { };
+      "restic/vaultwarden/sshPrivateKey" = { };
+      "environments/vaultwarden" = {
+        owner = config.systemd.services.vaultwarden.serviceConfig.User;
+        group = config.systemd.services.vaultwarden.serviceConfig.Group;
+        restartUnits = [ "vaultwarden.service" ];
+      };
+    };
+
+    systemd = {
+      services.vaultwarden.after = [ "sops-install-secrets.service" ];
+      tmpfiles.settings.vaultwarden = {
+        "${config.services.vaultwarden.backupDir}".d = {
+          user = config.systemd.services.vaultwarden.serviceConfig.User;
+          group = config.systemd.services.vaultwarden.serviceConfig.Group;
+          mode = "750";
+          age = "-";
+        };
+      };
+    };
+
+    common = {
+      configure.persist.system.dirs = [
+        {
+          directory = "/var/lib/vaultwarden";
+          mode = "0700";
+          user = config.systemd.services.vaultwarden.serviceConfig.User;
+          group = config.systemd.services.vaultwarden.serviceConfig.Group;
+        }
+        {
+          directory = config.services.vaultwarden.backupDir;
+          mode = "0700";
+          user = config.systemd.services.vaultwarden.serviceConfig.User;
+          group = config.systemd.services.vaultwarden.serviceConfig.Group;
+        }
+      ];
+      services.resticBackup.vaultwarden = {
+        enable = true;
+        targets = [
+          "restic-target.fc9f.de"
+          "isodon.fc9f.de"
+        ];
+        sshKeyFile = config.sops.secrets."restic/vaultwarden/sshPrivateKey".path;
+        passwordFile = config.sops.secrets."restic/vaultwarden/repositoryPassword".path;
+        paths = [ config.services.vaultwarden.backupDir ];
+        runBeforeBackup = ''
+          ${lib.getExe' pkgs.systemd "systemctl"} start --wait backup-vaultwarden.service
+        '';
+      };
+    };
+
+    services = {
+      vaultwarden = {
+        enable = true;
+        domain = "vault.zaphyra.eu";
+        dbBackend = "sqlite";
+        backupDir = "/var/backups/vaultwarden";
+        environmentFile = config.sops.secrets."environments/vaultwarden".path;
+        config = {
+          ROCKET_ADDRESS = "::1";
+          ROCKET_PORT = 8582;
+
+          DOMAIN = "https://vault.zaphyra.eu";
+          SIGNUPS_ALLOWED = false;
+
+          PUSH_ENABLED = true;
+
+          SMTP_HOST = "morio.infra.zaphyra.eu";
+          SMTP_FROM = "vaultwarden@zaphyra.eu";
+          SMTP_USERNAME = "vaultwarden@zaphyra.eu";
+          SMTP_PORT = 465;
+          SMTP_SECURITY = "force_tls";
+        };
+      };
+      nginx.virtualHosts."vault.zaphyra.eu" = {
+        useACMEHost = "${config.networking.fqdn}";
+        forceSSL = true;
+        kTLS = true;
+        locations."/" = {
+          proxyPass = "http://[${config.services.vaultwarden.config.ROCKET_ADDRESS}]:${toString config.services.vaultwarden.config.ROCKET_PORT}/";
+          proxyWebsockets = true;
+        };
+      };
+    };
+  };
+
+}
diff --git a/nixosModules/zpha/websites/zaphyra.eu.nix b/nixosModules/zpha/websites/zaphyra.eu.nix
@@ -0,0 +1,30 @@
+{
+  machineConfig,
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+
+  options.zpha.websites."zaphyra.eu".enable = lib.mkEnableOption "";
+
+  config = lib.mkIf config.zpha.websites."zaphyra.eu".enable {
+    dns.zones."zaphyra.eu" =
+      pkgs.dnsNix.combinators.host machineConfig.networking.ip4Address machineConfig.networking.ip6Address;
+
+    services.nginx.virtualHosts."zaphyra.eu" = {
+      useACMEHost = "${config.networking.fqdn}";
+      forceSSL = true;
+      kTLS = true;
+      root = pkgs.zpha.website;
+      extraConfig = ''
+        location /.well-known/openpgpkey {
+            add_header Access-Control-Allow-Origin * always;
+        }
+      '';
+    };
+  };
+
+}
diff --git a/npins/default.nix b/npins/default.nix
@@ -0,0 +1,249 @@
+/*
+  This file is provided under the MIT licence:
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+# Generated by npins. Do not modify; will be overwritten regularly
+let
+  # Backwards-compatibly make something that previously didn't take any arguments take some
+  # The function must return an attrset, and will unfortunately be eagerly evaluated
+  # Same thing, but it catches eval errors on the default argument so that one may still call it with other arguments
+  mkFunctor =
+    fn:
+    let
+      e = builtins.tryEval (fn { });
+    in
+    (if e.success then e.value else { error = fn { }; }) // { __functor = _self: fn; };
+
+  # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/lists.nix#L295
+  range =
+    first: last: if first > last then [ ] else builtins.genList (n: first + n) (last - first + 1);
+
+  # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L257
+  stringToCharacters = s: map (p: builtins.substring p 1 s) (range 0 (builtins.stringLength s - 1));
+
+  # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L269
+  stringAsChars = f: s: concatStrings (map f (stringToCharacters s));
+  concatStrings = builtins.concatStringsSep "";
+
+  # If the environment variable NPINS_OVERRIDE_${name} is set, then use
+  # the path directly as opposed to the fetched source.
+  # (Taken from Niv for compatibility)
+  mayOverride =
+    name: path:
+    let
+      envVarName = "NPINS_OVERRIDE_${saneName}";
+      saneName = stringAsChars (c: if (builtins.match "[a-zA-Z0-9]" c) == null then "_" else c) name;
+      ersatz = builtins.getEnv envVarName;
+    in
+    if ersatz == "" then
+      path
+    else
+      # this turns the string into an actual Nix path (for both absolute and
+      # relative paths)
+      builtins.trace "Overriding path of \"${name}\" with \"${ersatz}\" due to set \"${envVarName}\"" (
+        if builtins.substring 0 1 ersatz == "/" then
+          /. + ersatz
+        else
+          /. + builtins.getEnv "PWD" + "/${ersatz}"
+      );
+
+  mkSource =
+    name: spec:
+    {
+      pkgs ? null,
+    }:
+    assert spec ? type;
+    let
+      # Unify across builtin and pkgs fetchers.
+      # `fetchGit` requires a wrapper because of slight API differences.
+      fetchers =
+        if pkgs == null then
+          {
+            inherit (builtins) fetchTarball fetchurl;
+            # For some fucking reason, fetchGit has a different signature than the other builtin fetchers …
+            fetchGit = args: (builtins.fetchGit args).outPath;
+          }
+        else
+          {
+            fetchTarball =
+              {
+                url,
+                sha256,
+              }:
+              pkgs.fetchzip {
+                inherit url sha256;
+                extension = "tar";
+              };
+            inherit (pkgs) fetchurl;
+            fetchGit =
+              {
+                url,
+                submodules,
+                rev,
+                name,
+                narHash,
+              }:
+              pkgs.fetchgit {
+                inherit url rev name;
+                fetchSubmodules = submodules;
+                hash = narHash;
+              };
+          };
+
+      # Dispatch to the correct code path based on the type
+      path =
+        if spec.type == "Git" then
+          mkGitSource fetchers spec
+        else if spec.type == "GitRelease" then
+          mkGitSource fetchers spec
+        else if spec.type == "PyPi" then
+          mkPyPiSource fetchers spec
+        else if spec.type == "Channel" then
+          mkChannelSource fetchers spec
+        else if spec.type == "Tarball" then
+          mkTarballSource fetchers spec
+        else if spec.type == "Container" then
+          mkContainerSource pkgs spec
+        else
+          builtins.throw "Unknown source type ${spec.type}";
+    in
+    spec // { outPath = mayOverride name path; };
+
+  mkGitSource =
+    {
+      fetchTarball,
+      fetchGit,
+      ...
+    }:
+    {
+      repository,
+      revision,
+      url ? null,
+      submodules,
+      hash,
+      ...
+    }:
+    assert repository ? type;
+    # At the moment, either it is a plain git repository (which has an url), or it is a GitHub/GitLab repository
+    # In the latter case, there we will always be an url to the tarball
+    if url != null && !submodules then
+      fetchTarball {
+        inherit url;
+        sha256 = hash;
+      }
+    else
+      let
+        url =
+          if repository.type == "Git" then
+            repository.url
+          else if repository.type == "GitHub" then
+            "https://github.com/${repository.owner}/${repository.repo}.git"
+          else if repository.type == "GitLab" then
+            "${repository.server}/${repository.repo_path}.git"
+          else if repository.type == "Forgejo" then
+            "${repository.server}/${repository.owner}/${repository.repo}.git"
+          else
+            throw "Unrecognized repository type ${repository.type}";
+        urlToName =
+          url: rev:
+          let
+            matched = builtins.match "^.*/([^/]*)(\\.git)?$" url;
+
+            short = builtins.substring 0 7 rev;
+
+            appendShort = if (builtins.match "[a-f0-9]*" rev) != null then "-${short}" else "";
+          in
+          "${if matched == null then "source" else builtins.head matched}${appendShort}";
+        name = urlToName url revision;
+      in
+      fetchGit {
+        rev = revision;
+        narHash = hash;
+
+        inherit name submodules url;
+      };
+
+  mkPyPiSource =
+    { fetchurl, ... }:
+    {
+      url,
+      hash,
+      ...
+    }:
+    fetchurl {
+      inherit url;
+      sha256 = hash;
+    };
+
+  mkChannelSource =
+    { fetchTarball, ... }:
+    {
+      url,
+      hash,
+      ...
+    }:
+    fetchTarball {
+      inherit url;
+      sha256 = hash;
+    };
+
+  mkTarballSource =
+    { fetchTarball, ... }:
+    {
+      url,
+      locked_url ? url,
+      hash,
+      ...
+    }:
+    fetchTarball {
+      url = locked_url;
+      sha256 = hash;
+    };
+
+  mkContainerSource =
+    pkgs:
+    {
+      image_name,
+      image_tag,
+      image_digest,
+      ...
+    }:
+    if pkgs == null then
+      builtins.throw "container sources require passing in a Nixpkgs value: https://github.com/andir/npins/blob/master/README.md#using-the-nixpkgs-fetchers"
+    else
+      pkgs.dockerTools.pullImage {
+        imageName = image_name;
+        imageDigest = image_digest;
+        finalImageTag = image_tag;
+      };
+in
+mkFunctor (
+  {
+    input ? ./sources.json,
+  }:
+  let
+    data =
+      if builtins.isPath input then
+        # while `readFile` will throw an error anyways if the path doesn't exist,
+        # we still need to check beforehand because *our* error can be caught but not the one from the builtin
+        # *piegames sighs*
+        if builtins.pathExists input then
+          builtins.fromJSON (builtins.readFile input)
+        else
+          throw "Input path ${toString input} does not exist"
+      else if builtins.isAttrs input then
+        input
+      else
+        throw "Unsupported input type ${builtins.typeOf input}, must be a path or an attrset";
+    version = data.version;
+  in
+  if version == 7 then
+    builtins.mapAttrs (name: spec: mkFunctor (mkSource name spec)) data.pins
+  else
+    throw "Unsupported format version ${toString version} in sources.json. Try running `npins upgrade`"
+)
diff --git a/npins/sources.json b/npins/sources.json
@@ -0,0 +1,297 @@
+{
+  "pins": {
+    "continuwuity": {
+      "type": "GitRelease",
+      "repository": {
+        "type": "Forgejo",
+        "server": "https://forgejo.ellis.link/",
+        "owner": "continuwuation",
+        "repo": "continuwuity"
+      },
+      "pre_releases": false,
+      "version_upper_bound": null,
+      "release_prefix": null,
+      "submodules": false,
+      "version": "v0.5.9",
+      "revision": "f668b4a64be61373d2f7d2be96917457558b962c",
+      "url": "https://forgejo.ellis.link/api/v1/repos/continuwuation/continuwuity/archive/v0.5.9.tar.gz",
+      "hash": "sha256-4zs26kTqwkJV7x+Sm12LnR02bbyH0f6Itbz7bDKUyts="
+    },
+    "dnsNix": {
+      "type": "Git",
+      "repository": {
+        "type": "Git",
+        "url": "https://git.zaphyra.eu/dns.nix"
+      },
+      "branch": "master",
+      "submodules": false,
+      "revision": "190f11f966723e89922c2c7cef7e060211814afd",
+      "url": null,
+      "hash": "sha256-GgFgW4wbSVhzc+ZSb0Nu7aJGs3iy0wGgJmIDbegcrQE="
+    },
+    "firefoxGnomeTheme": {
+      "type": "GitRelease",
+      "repository": {
+        "type": "GitHub",
+        "owner": "rafaelmardojai",
+        "repo": "firefox-gnome-theme"
+      },
+      "pre_releases": false,
+      "version_upper_bound": null,
+      "release_prefix": null,
+      "submodules": false,
+      "version": "v149.1",
+      "revision": "59464fcbe0ffbb15ef2114d4941c71b51005a585",
+      "url": "https://api.github.com/repos/rafaelmardojai/firefox-gnome-theme/tarball/refs/tags/v149.1",
+      "hash": "sha256-QFY6Eu0kmaWl8W76bXs5K2BVtTh+Md+1rGba1WiTYxU="
+    },
+    "flauschehornSexy": {
+      "type": "Git",
+      "repository": {
+        "type": "Git",
+        "url": "https://git.zaphyra.eu/flauschehorn.sexy"
+      },
+      "branch": "master",
+      "submodules": false,
+      "revision": "93dfb56c9fdc45b4947b3717efdb621f7feeceaf",
+      "url": null,
+      "hash": "sha256-A1faTJ/x7SDIAPLGG2Qy/WTVqVdfvM7fB2pEnoplqTs="
+    },
+    "gpxMap": {
+      "type": "Git",
+      "repository": {
+        "type": "Git",
+        "url": "https://git.zaphyra.eu/gpx-map"
+      },
+      "branch": "master",
+      "submodules": false,
+      "revision": "8177e5ba1b85173a1aa75f0e265b54abb0050f62",
+      "url": null,
+      "hash": "sha256-twTkdTPSVNcpquvQgx3LkXd7lwBP4/sW2W98oO9/C+g="
+    },
+    "haumea": {
+      "type": "Git",
+      "repository": {
+        "type": "Git",
+        "url": "https://git.zaphyra.eu/haumea"
+      },
+      "branch": "main",
+      "submodules": false,
+      "revision": "d8d558fc6dadc561381e06b0411b5a57a08927df",
+      "url": null,
+      "hash": "sha256-cKGfYcBhYShBioa1yjE5OPQtwKpCjEAOhtowhUzsrgk="
+    },
+    "lanzaboote": {
+      "type": "GitRelease",
+      "repository": {
+        "type": "GitHub",
+        "owner": "nix-community",
+        "repo": "lanzaboote"
+      },
+      "pre_releases": false,
+      "version_upper_bound": null,
+      "release_prefix": null,
+      "submodules": false,
+      "version": "v1.0.0",
+      "revision": "2fe211d9c0e2320ce23dc995a3f93666ca149d9a",
+      "url": "https://api.github.com/repos/nix-community/lanzaboote/tarball/v1.0.0",
+      "hash": "sha256-RJmgVDzjRI18BWVogG6wpsl1UCuV6ui8qr4DJ1LfWZ8="
+    },
+    "lixModule": {
+      "type": "GitRelease",
+      "repository": {
+        "type": "Forgejo",
+        "server": "https://git.lix.systems/",
+        "owner": "lix-project",
+        "repo": "nixos-module"
+      },
+      "pre_releases": false,
+      "version_upper_bound": null,
+      "release_prefix": null,
+      "submodules": false,
+      "version": "2.93.1",
+      "revision": "c3c78a32273e89d28367d8605a4c880f0b6607e3",
+      "url": "https://git.lix.systems/api/v1/repos/lix-project/nixos-module/archive/2.93.1.tar.gz",
+      "hash": "sha256-EfA5K5EZAnspmraJrXQlziffVpaT+QDBiE6yKmuaNNQ="
+    },
+    "mqttWebUI": {
+      "type": "Git",
+      "repository": {
+        "type": "Git",
+        "url": "http://git.zaphyra.eu/mqtt-webui"
+      },
+      "branch": "main",
+      "submodules": false,
+      "revision": "e881d1c17d7dd55f5e0b3d15225a80ae6efd6ce8",
+      "url": null,
+      "hash": "sha256-lcDxUxYXMp/zV3K9EzS727s5kdx0PQY7/gcmbehz/Fs="
+    },
+    "niri-nix": {
+      "type": "Git",
+      "repository": {
+        "type": "Forgejo",
+        "server": "https://codeberg.org/",
+        "owner": "BANanaD3V",
+        "repo": "niri-nix"
+      },
+      "branch": "main",
+      "submodules": false,
+      "revision": "0a5220500f2764422278f07f29576731e85b1d6c",
+      "url": "https://codeberg.org/BANanaD3V/niri-nix/archive/0a5220500f2764422278f07f29576731e85b1d6c.tar.gz",
+      "hash": "sha256-VACAF2i/81+d6NPO/WYpAgv0aD+hhctm1uYRsioALgc="
+    },
+    "nixMaid": {
+      "type": "Git",
+      "repository": {
+        "type": "GitHub",
+        "owner": "viperML",
+        "repo": "nix-maid"
+      },
+      "branch": "master",
+      "submodules": false,
+      "revision": "993d95a0a5b2f4b06faa022b32112b699ca3bbfd",
+      "url": "https://github.com/viperML/nix-maid/archive/993d95a0a5b2f4b06faa022b32112b699ca3bbfd.tar.gz",
+      "hash": "sha256-pLCCqY/0JC7QdWqyEyDEvGl63f9aS8zrFLVJW7gUMKI="
+    },
+    "nixStd": {
+      "type": "Git",
+      "repository": {
+        "type": "GitHub",
+        "owner": "chessai",
+        "repo": "nix-std"
+      },
+      "branch": "master",
+      "submodules": false,
+      "revision": "31bbc925750cc9d8f828fe55cee1a2bd985e0c00",
+      "url": "https://github.com/chessai/nix-std/archive/31bbc925750cc9d8f828fe55cee1a2bd985e0c00.tar.gz",
+      "hash": "sha256-e+7MJF2gsgTBuOWv4mCimSP0D9+naeFSw9a7N3yEmv4="
+    },
+    "nixosHardware": {
+      "type": "Git",
+      "repository": {
+        "type": "GitHub",
+        "owner": "NixOS",
+        "repo": "nixos-hardware"
+      },
+      "branch": "master",
+      "submodules": false,
+      "revision": "8792fab9d4a6454a9201675f01326f827ce35ead",
+      "url": "https://github.com/NixOS/nixos-hardware/archive/8792fab9d4a6454a9201675f01326f827ce35ead.tar.gz",
+      "hash": "sha256-u73aVD/lUmmT3JV+kPDztl7zPwQKd0eobD1AbJltaGs="
+    },
+    "nixpkgs": {
+      "type": "Git",
+      "repository": {
+        "type": "GitHub",
+        "owner": "NixOS",
+        "repo": "nixpkgs"
+      },
+      "branch": "nixos-25.11",
+      "submodules": false,
+      "revision": "d7a713c0b7e47c908258e71cba7a2d77cc8d71d5",
+      "url": "https://github.com/NixOS/nixpkgs/archive/d7a713c0b7e47c908258e71cba7a2d77cc8d71d5.tar.gz",
+      "hash": "sha256-6xWoytx8jFW4PF1GjRm/i/53trbpKGfz6zjzQGBr4cI="
+    },
+    "nixpkgsUnstable": {
+      "type": "Git",
+      "repository": {
+        "type": "GitHub",
+        "owner": "NixOS",
+        "repo": "nixpkgs"
+      },
+      "branch": "master",
+      "submodules": false,
+      "revision": "f04d3629e8772518f1005264b1a43a755fbcaaee",
+      "url": "https://github.com/NixOS/nixpkgs/archive/f04d3629e8772518f1005264b1a43a755fbcaaee.tar.gz",
+      "hash": "sha256-4q5WptkoeNJDsSWbzWxShJdSgL/QgZBu//+PqEsnm9A="
+    },
+    "oeffisearch": {
+      "type": "Git",
+      "repository": {
+        "type": "Git",
+        "url": "https://git.zaphyra.eu/oeffisearch"
+      },
+      "branch": "main",
+      "submodules": false,
+      "revision": "8b2131d7c60846394b735c28b968536236283709",
+      "url": null,
+      "hash": "sha256-aV01KT7uhiZ4oYzOZ4BROEvlw30isFZ5ujJSppQUOTM="
+    },
+    "preservation": {
+      "type": "Git",
+      "repository": {
+        "type": "GitHub",
+        "owner": "nix-community",
+        "repo": "preservation"
+      },
+      "branch": "main",
+      "submodules": false,
+      "revision": "93416f4614ad2dfed5b0dcf12f27e57d27a5ab11",
+      "url": "https://github.com/nix-community/preservation/archive/93416f4614ad2dfed5b0dcf12f27e57d27a5ab11.tar.gz",
+      "hash": "sha256-mMI9IanU+Xw+pVogD2oT0I2kTmvz2Un/Apc5+CwUpEY="
+    },
+    "simpleNixosMailserver": {
+      "type": "Git",
+      "repository": {
+        "type": "GitLab",
+        "repo_path": "simple-nixos-mailserver/nixos-mailserver",
+        "server": "https://gitlab.com/"
+      },
+      "branch": "nixos-25.11",
+      "submodules": false,
+      "revision": "25e6dbb8fca3b6e779c5a46fd03bd760b2165bb5",
+      "url": "https://gitlab.com/api/v4/projects/simple-nixos-mailserver%2Fnixos-mailserver/repository/archive.tar.gz?sha=25e6dbb8fca3b6e779c5a46fd03bd760b2165bb5",
+      "hash": "sha256-QHzRqq6gh+t3F/QU9DkP7X63dDDcuIQmaDz12p7ANTg="
+    },
+    "sopsNix": {
+      "type": "Git",
+      "repository": {
+        "type": "GitHub",
+        "owner": "Mic92",
+        "repo": "sops-nix"
+      },
+      "branch": "master",
+      "submodules": false,
+      "revision": "c591bf665727040c6cc5cb409079acb22dcce33c",
+      "url": "https://github.com/Mic92/sops-nix/archive/c591bf665727040c6cc5cb409079acb22dcce33c.tar.gz",
+      "hash": "sha256-VfGRo1qTBKOe3s2gOv8LSoA6Fk19PvBlwQ1ECN0Evn8="
+    },
+    "stagit": {
+      "type": "Git",
+      "repository": {
+        "type": "Git",
+        "url": "https://git.zaphyra.eu/stagit"
+      },
+      "branch": "main",
+      "submodules": false,
+      "revision": "e9369c19db7b6fd3d51b2dc938ad40cb5e2a1e7c",
+      "url": null,
+      "hash": "sha256-nZrulc0irdVDkZqTUl9EnuER92aE1apMIiE4aPWEUYs="
+    },
+    "things": {
+      "type": "Git",
+      "repository": {
+        "type": "Git",
+        "url": "https://git.zaphyra.eu/things"
+      },
+      "branch": "main",
+      "submodules": false,
+      "revision": "c0a6f8ca7d4275cc96d1ce9723b8732f5fb4f0e4",
+      "url": null,
+      "hash": "sha256-4z5ARVj7I7KQ4HyThcBeGccuIfK3eRybjz4ctiKnu14="
+    },
+    "zaphyraWebsite": {
+      "type": "Git",
+      "repository": {
+        "type": "Git",
+        "url": "https://git.zaphyra.eu/website"
+      },
+      "branch": "main",
+      "submodules": false,
+      "revision": "9808abbc409eac0f0f02cc3980a57c9207795665",
+      "url": null,
+      "hash": "sha256-81zdkclz9/h0df1bL81gFwWep1TFJSn+wYVSm6LFcoM="
+    }
+  },
+  "version": 7
+}
diff --git a/overlays/default.nix b/overlays/default.nix
@@ -0,0 +1,27 @@
+{
+  npins ? import ../npins,
+  lib ? import "${(import ../npins).nixpkgs}/lib",
+  ...
+}:
+
+let
+  # read the current directorys files and pipe the result through a list of functions
+  overlays = builtins.foldl' (x: f: f x) (builtins.readDir ./.) [
+    # convert to a list containing just the attribute names
+    (builtins.attrNames)
+    # drop "default.nix" from the list
+    (builtins.filter (name: name != "default.nix"))
+    # map the list to a name-value pair with a name that has the last  4 chars (".nix") stripped, and a value that is the contents of that given file
+    (builtins.map (name: {
+      inherit name;
+      value = import ./${name} { inherit npins; };
+    }))
+    # convert the resulting list to an attribute set
+    (builtins.listToAttrs)
+  ];
+
+in
+overlays
+// {
+  default = lib.flatten (builtins.attrValues overlays);
+}
diff --git a/overlays/dnsNix/default.nix b/overlays/dnsNix/default.nix
@@ -0,0 +1,8 @@
+{
+  npins ? import ../../npins,
+  ...
+}:
+
+final: prev: {
+  dnsNix = import npins.dnsNix { inherit (prev) lib; };
+}
diff --git a/overlays/nixStd/default.nix b/overlays/nixStd/default.nix
@@ -0,0 +1,8 @@
+{
+  npins ? import ../../npins,
+  ...
+}:
+
+final: prev: {
+  nixStd = import npins.nixStd;
+}
diff --git a/overlays/nixpkgsUnstable/default.nix b/overlays/nixpkgsUnstable/default.nix
@@ -0,0 +1,11 @@
+{
+  npins ? import ../../npins,
+  ...
+}:
+
+final: prev: {
+  unstable = import npins.nixpkgsUnstable {
+    inherit (prev.stdenv.hostPlatform) system;
+    overlays = import "${npins.nixpkgsUnstable}/pkgs/top-level/impure-overlays.nix";
+  };
+}
diff --git a/overlays/swaylock-plugin-fprintd/default.nix b/overlays/swaylock-plugin-fprintd/default.nix
@@ -0,0 +1,18 @@
+{ ... }:
+
+final: prev: {
+  swaylock-plugin-fprintd = prev.swaylock-plugin.overrideAttrs (prevAttrs: {
+    src = prev.applyPatches {
+      src = prevAttrs.src;
+      patches = [ ./fprintd-support.patch ];
+      postPatch = ''
+        substituteInPlace ./fingerprint/meson.build \
+          --replace-fail "/usr" "${prev.fprintd}"
+      '';
+    };
+
+    buildInputs = prevAttrs.buildInputs ++ [
+      prev.dbus
+    ];
+  });
+}
diff --git a/overlays/swaylock-plugin-fprintd/fprintd-support.patch b/overlays/swaylock-plugin-fprintd/fprintd-support.patch
@@ -0,0 +1,577 @@
+From 81ae315c1f85f45a374444ed8a35a58ce1b7d50b Mon Sep 17 00:00:00 2001
+From: "Katja Ramona Sophie Kwast (zaphyra)" <git@zaphyra.eu>
+Date: Tue, 26 Aug 2025 11:14:52 +0200
+Subject: [PATCH] 
+ `https://github.com/swaywm/swaylock/compare/master...SL-RU:swaylock-fprintd:fprintd
+ adapted` for swaylock-plugin
+
+---
+ completions/bash/swaylock      |   2 +
+ completions/fish/swaylock.fish |   1 +
+ completions/zsh/_swaylock      |   1 +
+ fingerprint/fingerprint.c      | 251 +++++++++++++++++++++++++++++++++
+ fingerprint/fingerprint.h      |  43 ++++++
+ fingerprint/meson.build        |  33 +++++
+ include/swaylock.h             |   3 +
+ main.c                         |  32 ++++-
+ meson.build                    |   2 +
+ render.c                       |   2 +
+ 10 files changed, 369 insertions(+), 1 deletion(-)
+ create mode 100644 fingerprint/fingerprint.c
+ create mode 100644 fingerprint/fingerprint.h
+ create mode 100644 fingerprint/meson.build
+
+diff --git a/completions/bash/swaylock b/completions/bash/swaylock
+index f8f411f..d13c679 100644
+--- a/completions/bash/swaylock
++++ b/completions/bash/swaylock
+@@ -14,6 +14,7 @@ _swaylock-plugin()
+     -F
+     -h
+     -i
++    -p
+     -k
+     -K
+     -L
+@@ -41,6 +42,7 @@ _swaylock-plugin()
+     --hide-keyboard-layout
+     --ignore-empty-password
+     --image
++    --fingerprint
+     --indicator-caps-lock
+     --indicator-idle-visible
+     --indicator-radius
+diff --git a/completions/fish/swaylock.fish b/completions/fish/swaylock.fish
+index 320ed6b..4ed4e62 100644
+--- a/completions/fish/swaylock.fish
++++ b/completions/fish/swaylock.fish
+@@ -14,6 +14,7 @@ complete -c swaylock-plugin -l help                   -s h --description "Show h
+ complete -c swaylock-plugin -l hide-keyboard-layout   -s K --description "Hide the current xkb layout while typing."
+ complete -c swaylock-plugin -l ignore-empty-password  -s e --description "When an empty password is provided, do not validate it."
+ complete -c swaylock-plugin -l image                  -s i --description "Display the given image, optionally only on the given output."
++complete -c swaylock-plugin -l fingerprint            -s p --description "Enable fingerprint scanning. Fprint is required."
+ complete -c swaylock-plugin -l indicator-caps-lock    -s l --description "Show the current Caps Lock state also on the indicator."
+ complete -c swaylock-plugin -l indicator-idle-visible      --description "Sets the indicator to show even if idle."
+ complete -c swaylock-plugin -l indicator-radius            --description "Sets the indicator radius."
+diff --git a/completions/zsh/_swaylock b/completions/zsh/_swaylock
+index 3854ced..1a659a0 100644
+--- a/completions/zsh/_swaylock
++++ b/completions/zsh/_swaylock
+@@ -18,6 +18,7 @@ _arguments -s \
+ 	'(--hide-keyboard-layout -K)'{--hide-keyboard-layout,-K}'[Hide the current xkb layout while typing]' \
+ 	'(--ignore-empty-password -e)'{--ignore-empty-password,-e}'[When an empty password is provided, do not validate it]' \
+ 	'(--image -i)'{--image,-i}'[Display the given image, optionally only on the given output]:filename:_files' \
++	'(--fingerprint -p)'{--fingerprint,-p}'[Enable fingerprint scanning. Fprint is required]' \
+ 	'(--indicator-caps-lock -l)'{--indicator-caps-lock,-l}'[Show the current Caps Lock state also on the indicator]' \
+ 	'(--indicator-idle-visible)'--indicator-idle-visible'[Sets the indicator to show even if idle]' \
+ 	'(--indicator-radius)'--indicator-radius'[Sets the indicator radius]:radius:' \
+diff --git a/fingerprint/fingerprint.c b/fingerprint/fingerprint.c
+new file mode 100644
+index 0000000..4e9477a
+--- /dev/null
++++ b/fingerprint/fingerprint.c
+@@ -0,0 +1,251 @@
++/*
++ * Based on fprintd util to verify a fingerprint
++ * Copyright (C) 2008 Daniel Drake <dsd@gentoo.org>
++ * Copyright (C) 2020 Marco Trevisan <marco.trevisan@canonical.com>
++ * Copyright (C) 2023 Alexandr Lutsai <s.lyra@ya.ru>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License along
++ * with this program; if not, write to the Free Software Foundation, Inc.,
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
++ */
++
++#include <stdbool.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <locale.h>
++#include <gio/gio.h>
++
++#include "fingerprint.h"
++#include "log.h"
++
++static void display_message(struct FingerprintState *state, const char *fmt, ...) {
++	va_list(args);
++	va_start(args, fmt);
++	vsnprintf(state->status, sizeof(state->status), fmt, args);
++	va_end(args);
++
++	state->sw_state->auth_state = AUTH_STATE_FINGERPRINT;
++	state->sw_state->fingerprint_msg = state->status;
++	damage_state(state->sw_state);
++	schedule_auth_idle(state->sw_state);
++}
++
++static void create_manager(struct FingerprintState *state) {
++	g_autoptr(GError) error = NULL;
++	state->connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
++	if (state->connection == NULL) {
++		swaylock_log(LOG_ERROR, "Failed to connect to session bus: %s", error->message);
++		display_message(state, "Fingerprint error");
++		return;
++	}
++
++	state->manager = fprint_dbus_manager_proxy_new_sync(
++		state->connection,
++		G_DBUS_PROXY_FLAGS_NONE,
++		"net.reactivated.Fprint",
++		"/net/reactivated/Fprint/Manager",
++		NULL, &error);
++	if (state->manager == NULL) {
++		swaylock_log(LOG_ERROR, "Failed to get Fprintd manager: %s", error->message);
++		display_message(state, "Fingerprint error");
++		return;
++	}
++
++	swaylock_log(LOG_DEBUG, "FPrint manager created");
++}
++
++static void open_device(struct FingerprintState *state) {
++	state->device = NULL;
++	g_autoptr(FprintDBusDevice) dev = NULL;
++	g_autoptr(GError) error = NULL;
++	g_autofree char *path = NULL;
++	if (!fprint_dbus_manager_call_get_default_device_sync(state->manager, &path, NULL, &error)) {
++		swaylock_log(LOG_ERROR, "Impossible to verify: %s", error->message);
++		display_message(state, "Fingerprint error");
++		return;
++	}
++
++	swaylock_log(LOG_DEBUG, "Fingerprint: using device %s", path);
++	dev = fprint_dbus_device_proxy_new_sync(
++		state->connection,
++		G_DBUS_PROXY_FLAGS_NONE,
++		"net.reactivated.Fprint",
++		path, NULL, &error);
++	if (error) {
++		swaylock_log(LOG_ERROR, "failed to connect to device: %s", error->message);
++		display_message(state, "Fingerprint error");
++		return;
++	}
++
++	if (!fprint_dbus_device_call_claim_sync(dev, "", NULL, &error)) {
++		// we need to wait while device can be claimed
++		swaylock_log(LOG_DEBUG, "failed to claim the device: %s(%d)", error->message, error->code);
++		return;
++	}
++
++	swaylock_log(LOG_DEBUG, "FPrint device opened %s", path);
++	state->device = g_steal_pointer (&dev);
++}
++
++static void verify_result(GObject *object, const char *result, gboolean done, void *user_data) {
++	struct FingerprintState *state = user_data;
++	swaylock_log(LOG_INFO, "Verify result: %s (%s)", result, done ? "done" : "not done");
++	state->match = g_str_equal (result, "verify-match");
++	if (g_str_equal (result, "verify-retry-scan")) {
++		display_message(state, "Retry");
++		return;
++	} else if(g_str_equal (result, "verify-swipe-too-short")) {
++		display_message(state, "Retry, too short");
++		return;
++	} else if(g_str_equal (result, "verify-finger-not-centered")) {
++		display_message(state, "Retry, not centered");
++		return;
++	} else if(g_str_equal (result, "verify-remove-and-retry")) {
++		display_message(state, "Remove and retry");
++		return;
++	}
++
++	if(state->match) {
++		display_message(state, "Fingerprint OK");
++	} else {
++		display_message(state, "Retry");
++	}
++
++	state->completed = TRUE;
++	g_autoptr(GError) error = NULL;
++	if (!fprint_dbus_device_call_verify_stop_sync(state->device, NULL, &error)) {
++		swaylock_log(LOG_ERROR, "VerifyStop failed: %s", error->message);
++		display_message(state, "Fingerprint error");
++		return;
++	}
++}
++
++static void verify_started_cb(GObject *obj, GAsyncResult *res, gpointer user_data) {
++	struct FingerprintState *state = user_data;
++	if (!fprint_dbus_device_call_verify_start_finish(FPRINT_DBUS_DEVICE(obj), res, &state->error)) {
++		return;
++	}
++
++	swaylock_log(LOG_DEBUG, "Verify started!");
++	state->started = TRUE;
++}
++
++static void proxy_signal_cb(GDBusProxy	*proxy,
++							const gchar *sender_name,
++							const gchar *signal_name,
++							GVariant	*parameters,
++							gpointer	 user_data)
++{
++	struct FingerprintState *state = user_data;
++	if (!state->started) {
++		return;
++	}
++
++	if (!g_str_equal(signal_name, "VerifyStatus")) {
++		return;
++	}
++
++	const gchar *result;
++	gboolean done;
++	g_variant_get(parameters, "(&sb)", &result, &done);
++	verify_result(G_OBJECT (proxy), result, done, user_data);
++}
++
++static void start_verify(struct FingerprintState *state) {
++	/* This one is funny. We connect to the signal immediately to avoid
++	 * race conditions. However, we must ignore any authentication results
++	 * that happen before our start call returns.
++	 * This is because the verify call itself may internally try to verify
++	 * against fprintd (possibly using a separate account).
++	 *
++	 * To do so, we *must* use the async version of the verify call, as the
++	 * sync version would cause the signals to be queued and only processed
++	 * after it returns.
++	 */
++	fprint_dbus_device_call_verify_start(state->device, "any", NULL,
++										 verify_started_cb,
++										 state);
++
++	/* Wait for verify start while discarding any VerifyStatus signals */
++	while (!state->started && !state->error) {
++		g_main_context_iteration(NULL, TRUE);
++	}
++
++	if (state->error) {
++		swaylock_log(LOG_ERROR, "VerifyStart failed: %s", state->error->message);
++		display_message(state, "Fingerprint error");
++		g_clear_error(&state->error);
++		return;
++	}
++}
++
++static void release_callback(GObject *source_object, GAsyncResult *res,
++							 gpointer user_data) {
++}
++
++void fingerprint_init(struct FingerprintState *fingerprint_state,
++					  struct swaylock_state *swaylock_state) {
++	memset(fingerprint_state, 0, sizeof(struct FingerprintState));
++	fingerprint_state->sw_state = swaylock_state;
++	create_manager(fingerprint_state);
++	if(fingerprint_state->manager == NULL || fingerprint_state->connection == NULL) {
++		return;
++	}
++}
++
++int fingerprint_verify(struct FingerprintState *fingerprint_state) {
++	/* VerifyStatus signals are processing, do not wait for completion. */
++	g_main_context_iteration (NULL, FALSE);
++	if (fingerprint_state->manager == NULL ||
++		fingerprint_state->connection == NULL) {
++		return false;
++	}
++
++	if (fingerprint_state->device == NULL) {
++		open_device(fingerprint_state);
++		if (fingerprint_state->device == NULL) {
++			return false;
++		}
++
++		g_signal_connect (fingerprint_state->device, "g-signal", G_CALLBACK (proxy_signal_cb),
++						  fingerprint_state);
++		start_verify(fingerprint_state);
++	}
++
++
++
++	if (!fingerprint_state->completed) {
++		return false;
++	}
++
++	if (!fingerprint_state->match) {
++		fingerprint_state->completed = 0;
++		fingerprint_state->match = 0;
++		start_verify(fingerprint_state);
++		return false;
++	}
++
++	return true;
++}
++
++void fingerprint_deinit(struct FingerprintState *fingerprint_state) {
++	if (!fingerprint_state->device) {
++		return;
++	}
++
++	g_signal_handlers_disconnect_by_func(fingerprint_state->device, proxy_signal_cb,
++										 fingerprint_state);
++	fprint_dbus_device_call_release(fingerprint_state->device, NULL, release_callback, NULL);
++	fingerprint_state->device = NULL;
++}
+diff --git a/fingerprint/fingerprint.h b/fingerprint/fingerprint.h
+new file mode 100644
+index 0000000..d030a43
+--- /dev/null
++++ b/fingerprint/fingerprint.h
+@@ -0,0 +1,43 @@
++/*
++ * Copyright (C) 2023 Alexandr Lutsai <s.lyra@ya.ru>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License along
++ * with this program; if not, write to the Free Software Foundation, Inc.,
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
++ */
++
++#ifndef _FINGERPRINT_H
++#define _FINGERPRINT_H
++
++#include "swaylock.h"
++#include "fingerprint/fprintd-dbus.h"
++
++struct FingerprintState {
++	GError	*error;
++	gboolean started;
++	gboolean completed;
++	gboolean match;
++
++	char status[128];
++
++	FprintDBusManager *manager;
++	GDBusConnection *connection;
++	FprintDBusDevice *device;
++	struct swaylock_state *sw_state;
++};
++
++void fingerprint_init(struct FingerprintState *fingerprint_state, struct swaylock_state *state);
++int fingerprint_verify(struct FingerprintState *fingerprint_state);
++void fingerprint_deinit(struct FingerprintState *fingerprint_state);
++
++#endif
+\ No newline at end of file
+diff --git a/fingerprint/meson.build b/fingerprint/meson.build
+new file mode 100644
+index 0000000..2239952
+--- /dev/null
++++ b/fingerprint/meson.build
+@@ -0,0 +1,33 @@
++gnome = import('gnome')
++
++dbus = dependency('dbus-1')
++glib = dependency('glib-2.0', version: '>=2.64.0')
++gio_dep = dependency('gio-2.0')
++
++fprintd_dbus_interfaces = files(
++    '/usr/share/dbus-1/interfaces/net.reactivated.Fprint.Manager.xml',
++    '/usr/share/dbus-1/interfaces/net.reactivated.Fprint.Device.xml',
++)
++
++fprintd_dbus_sources = gnome.gdbus_codegen('fprintd-dbus',
++    sources: fprintd_dbus_interfaces,
++    autocleanup: 'all',
++    interface_prefix: 'net.reactivated.Fprint.',
++    namespace: 'FprintDBus',
++    object_manager: true,
++)
++
++fingerprint = declare_dependency(
++    include_directories: [
++        include_directories('..'),
++    ],
++    sources: [
++        fprintd_dbus_sources,
++        'fingerprint.c' ,
++    ],
++    dependencies: [
++        gio_dep,
++        glib,
++        dbus
++    ],
++)
+\ No newline at end of file
+diff --git a/include/swaylock.h b/include/swaylock.h
+index 233c663..6d7bf6d 100644
+--- a/include/swaylock.h
++++ b/include/swaylock.h
+@@ -18,6 +18,7 @@ enum auth_state {
+ 	AUTH_STATE_IDLE, // nothing happening
+ 	AUTH_STATE_VALIDATING, // currently validating password
+ 	AUTH_STATE_INVALID, // displaying message: password was wrong
++	AUTH_STATE_FINGERPRINT,
+ };
+ 
+ // Indicator state: status of password buffer / typing letters
+@@ -74,6 +75,7 @@ struct swaylock_args {
+ 	bool daemonize;
+ 	int ready_fd;
+ 	bool indicator_idle_visible;
++	bool fingerprint;
+ 	char *plugin_command;
+ 	bool plugin_per_output;
+ 	/* negative values = no grace; unit: seconds */
+@@ -304,6 +306,7 @@ struct swaylock_state {
+ 	bool run_display, locked;
+ 	struct ext_session_lock_manager_v1 *ext_session_lock_manager_v1;
+ 	struct ext_session_lock_v1 *ext_session_lock_v1;
++	char *fingerprint_msg;
+ 	struct zxdg_output_manager_v1 *zxdg_output_manager;
+ 	struct forward_state forward;
+ 	struct swaylock_bg_server server;
+diff --git a/main.c b/main.c
+index 1300fc8..6cc33a5 100644
+--- a/main.c
++++ b/main.c
+@@ -29,6 +29,7 @@
+ #include "seat.h"
+ #include "swaylock.h"
+ #include "ext-session-lock-v1-client-protocol.h"
++#include "fingerprint/fingerprint.h"
+ #include "xdg-output-unstable-v1-client-protocol.h"
+ #include "xdg-output-unstable-v1-server-protocol.h"
+ #include "fullscreen-shell-unstable-v1-client-protocol.h"
+@@ -876,6 +877,7 @@ static int parse_options(int argc, char **argv, struct swaylock_state *state,
+ 		{"disable-caps-lock-text", no_argument, NULL, 'L'},
+ 		{"indicator-caps-lock", no_argument, NULL, 'l'},
+ 		{"line-uses-inside", no_argument, NULL, 'n'},
++		{"fingerprint", no_argument, NULL, 'p'},
+ 		{"line-uses-ring", no_argument, NULL, 'r'},
+ 		{"scaling", required_argument, NULL, 's'},
+ 		{"tiling", no_argument, NULL, 't'},
+@@ -955,6 +957,8 @@ static int parse_options(int argc, char **argv, struct swaylock_state *state,
+ 			"Disable the Caps Lock text.\n"
+ 		"  -l, --indicator-caps-lock        "
+ 			"Show the current Caps Lock state also on the indicator.\n"
++		"  -p, --fingerprint				"
++			"Enable fingerprint scanning. Fprint is required.\n"
+ 		"  -s, --scaling <mode>             "
+ 			"Image scaling mode: stretch, fill, fit, center, tile, solid_color.\n"
+ 		"  -t, --tiling                     "
+@@ -1060,7 +1064,7 @@ static int parse_options(int argc, char **argv, struct swaylock_state *state,
+ 	optind = 1;
+ 	while (1) {
+ 		int opt_idx = 0;
+-		c = getopt_long(argc, argv, "c:deFfhi:kKLlnrs:tuvC:R:", long_options,
++		c = getopt_long(argc, argv, "c:deFfhi:kKLlnprs:tuvC:R:", long_options,
+ 				&opt_idx);
+ 		if (c == -1) {
+ 			break;
+@@ -1104,6 +1108,11 @@ static int parse_options(int argc, char **argv, struct swaylock_state *state,
+ 				load_image(optarg, state);
+ 			}
+ 			break;
++		case 'p':
++			if(state) {
++				state->args.fingerprint = true;
++			}
++			break;
+ 		case 'k':
+ 			if (state) {
+ 				state->args.show_keyboard_layout = true;
+@@ -2257,6 +2266,17 @@ void log_init(int argc, char **argv) {
+ 	swaylock_log_init(LOG_ERROR);
+ }
+ 
++static void check_fingerprint(void *d) {
++	struct FingerprintState *fingerprint_state = d;
++	if (fingerprint_verify(fingerprint_state)) {
++		do_sigusr(1);
++	} else {
++		(void)write(sigusr_fds[1], NULL, 0);
++	}
++
++	loop_add_timer(state.eventloop, 300, check_fingerprint, fingerprint_state);
++}
++
+ int main(int argc, char **argv) {
+ 	/* Initially ignore SIGUSR1 + SIGUSR2 to prevent stray signals (like
+ 	 * `killall -SIGUSR2`) from affecting the pw backend subprocess */
+@@ -2291,6 +2311,7 @@ int main(int argc, char **argv) {
+ 		.hide_keyboard_layout = false,
+ 		.show_failed_attempts = false,
+ 		.indicator_idle_visible = false,
++		.fingerprint = false,
+ 		.ready_fd = -1,
+ 		.plugin_command = NULL,
+ 		.grace_time = 0.0f,
+@@ -2608,6 +2629,12 @@ int main(int argc, char **argv) {
+ 	sa2.sa_flags = 0;
+ 	sigaction(SIGCHLD, &sa2, NULL);
+ 
++	struct FingerprintState fingerprint_state;
++	if(state.args.fingerprint) {
++		fingerprint_init(&fingerprint_state, &state);
++		loop_add_timer(state.eventloop, 100, check_fingerprint, &fingerprint_state);
++	}
++
+ 	state.run_display = true;
+ 	while (state.run_display) {
+ 		errno = 0;
+@@ -2624,6 +2651,9 @@ int main(int argc, char **argv) {
+ 	ext_session_lock_v1_unlock_and_destroy(state.ext_session_lock_v1);
+ 	wl_display_roundtrip(state.display);
+ 
++	if(state.args.fingerprint) {
++		fingerprint_deinit(&fingerprint_state);
++	}
+ 	free(state.args.font);
+ 	cairo_destroy(state.test_cairo);
+ 	cairo_surface_destroy(state.test_surface);
+diff --git a/meson.build b/meson.build
+index e72aff7..6babe5c 100644
+--- a/meson.build
++++ b/meson.build
+@@ -116,6 +116,7 @@ if logind.found()
+ endif
+ 
+ subdir('include')
++subdir('fingerprint')
+ 
+ dependencies = [
+ 	cairo,
+@@ -125,6 +126,7 @@ dependencies = [
+ 	xkbcommon,
+ 	wayland_client,
+ 	wayland_server,
++	fingerprint,
+ ]
+ 
+ sources = [
+diff --git a/render.c b/render.c
+index 5d48017..123847b 100644
+--- a/render.c
++++ b/render.c
+@@ -109,6 +109,8 @@ static bool render_frame(struct swaylock_surface *surface) {
+ 		if (state->input_state == INPUT_STATE_CLEAR) {
+ 			// This message has highest priority
+ 			text = "Cleared";
++		} else if (state->auth_state == AUTH_STATE_FINGERPRINT) {
++			text = state->fingerprint_msg;
+ 		} else if (state->auth_state == AUTH_STATE_VALIDATING) {
+ 			text = "Verifying";
+ 		} else if (state->auth_state == AUTH_STATE_INVALID) {
+-- 
+2.50.1
+
diff --git a/overlays/tuigreet/default.nix b/overlays/tuigreet/default.nix
@@ -0,0 +1,20 @@
+{ ... }:
+
+final: prev: {
+  tuigreet = prev.tuigreet.overrideAttrs rec {
+    version = "0.10.0-unstable-2024-11-12";
+    useFetchCargoVendor = true;
+    # for some stupid reason `cargoHash` doesn't work in `overrideAttrs`, so we have to do it this way
+    cargoDeps = final.rustPlatform.fetchCargoVendor {
+      inherit src;
+      hash = "sha256-FrWDRsYhfq46wBm7F0Tifiw5oGXzSgwZC05ndNXcg8k=";
+    };
+    src = prev.fetchFromGitHub {
+      owner = "apognu";
+      repo = "tuigreet";
+      rev = "2aeca1b63dec977fc4e2ac6f97432386bedbc546";
+      sha256 = "sha256-6hVTU575tP+bPAUZlGhDwvBTVISyORC0wqljC7guZdA=";
+    };
+    doCheck = false;
+  };
+}
diff --git a/overlays/waylogout/default.nix b/overlays/waylogout/default.nix
@@ -0,0 +1,13 @@
+{ ... }:
+
+final: prev: {
+  waylogout = prev.waylogout.overrideAttrs rec {
+    version = "0.3";
+    src = prev.fetchFromGitHub {
+      owner = "loserMcloser";
+      repo = "waylogout";
+      rev = "v${version}";
+      hash = "sha256-dsuuTjmZm3IpqXU68LsAz86HNbMFvKhWPYOMG/5Z4jE=";
+    };
+  };
+}
diff --git a/overlays/zphaLib/default.nix b/overlays/zphaLib/default.nix
@@ -0,0 +1,15 @@
+{
+  npins ? import ../../npins,
+  ...
+}:
+
+final: prev: {
+  lib = prev.lib.extend (
+    _: _: {
+      zpha = import ../../lib {
+        inherit npins;
+        lib = prev.lib;
+      };
+    }
+  );
+}
diff --git a/overlays/zphaPackages/default.nix b/overlays/zphaPackages/default.nix
@@ -0,0 +1,30 @@
+{
+  npins ? import ../../npins,
+  ...
+}:
+
+final: prev: {
+  zpha = prev.lib.mergeAttrsList [
+    (import ../../packages prev)
+    {
+      website = prev.callPackage "${npins.zaphyraWebsite}/package.nix" { };
+
+      flauschehorn-sexy = prev.callPackage "${npins.flauschehornSexy}/package.nix" { };
+      things = prev.callPackage "${npins.things}/package.nix" { };
+      stagit = prev.callPackage "${npins.stagit}/package.nix" { };
+      oeffisearch = prev.callPackage "${npins.oeffisearch}/package.nix" {
+        version = builtins.substring 0 6 npins.oeffisearch.revision;
+        commit = npins.oeffisearch.revision;
+      };
+
+      mqtt-webui = prev.callPackage "${npins.mqttWebUI}/nix/package.nix" { };
+
+      # gpxMap
+      datamaps = prev.callPackage "${npins.gpxMap}/nix/packages/datamaps/package.nix" { };
+      gpx-map = prev.callPackage "${npins.gpxMap}/nix/packages/gpx-map/package.nix" { };
+      generateTilesFromGPX =
+        prev.callPackage "${npins.gpxMap}/nix/packages/generateTilesFromGPX/package.nix"
+          { };
+    }
+  ];
+}
diff --git a/packages/adwaita-colors-icon-theme/package.nix b/packages/adwaita-colors-icon-theme/package.nix
@@ -0,0 +1,69 @@
+{
+  lib,
+  stdenvNoCC,
+  fetchFromGitHub,
+  gtk3,
+  hicolor-icon-theme,
+  adwaita-icon-theme,
+  ...
+}:
+
+stdenvNoCC.mkDerivation (finalAttrs: {
+  pname = "adwaita-colors-icon-theme";
+  version = "2.4.1";
+  rev = "v${finalAttrs.version}";
+  srcHash = "sha256-M5dFb759sXfpD9/gQVF3sngyW4WdSgy4usInds9VIWk=";
+
+  src = fetchFromGitHub {
+    owner = "dpejoh";
+    repo = "Adwaita-colors";
+    hash = finalAttrs.srcHash;
+    inherit (finalAttrs) rev;
+  };
+
+  nativeBuildInputs = [
+    gtk3
+  ];
+
+  propagatedBuildInputs = [
+    hicolor-icon-theme
+    adwaita-icon-theme
+  ];
+
+  patchPhase = ''
+    # for some reason there are fucking broken symlinks?!
+    rm ./Adwaita-blue/scalable/places/com.bitwig.BitwigStudio.application-bitwig-project-folder-legacy.svg
+
+    find ./ -type d -name "Adwaita-*" | while read -r dir; do
+      # Construct the full path to the "index.theme" file
+      file="$dir/index.theme"
+      # Check if the file exists and contains the target text
+      if [ -f "$file" ]; then
+        substituteInPlace $file \
+          --replace-fail "Hidden=true" "" \
+          --replace-fail "AdwaitaLegacy," ""
+      fi
+    done
+  '';
+
+  installPhase = ''
+    runHook preInstall
+
+    install -d $out/share/icons
+    cp -r Adwaita-* $out/share/icons/
+
+    gtk-update-icon-cache -f -t $out/share/icons/Adwaita*
+
+    runHook postInstall
+  '';
+
+  dontDropIconThemeCache = true;
+
+  meta = {
+    description = "Adwaita Colors customizes Adwaita icons to match your GNOME theme's accent color, providing a cohesive, personalized look.";
+    homepage = "https://github.com/dpejoh/Adwaita-colors";
+    license = with lib.licenses; [ gpl3Only ];
+    platforms = lib.platforms.linux;
+    maintainers = with lib.maintainers; [ zaphyra ];
+  };
+})
diff --git a/packages/chaosctl/chaosctl.fish b/packages/chaosctl/chaosctl.fish
@@ -0,0 +1,65 @@
+# GLOBALS
+set CMD "$argv[1]"
+set CMDS beamer door help tv
+
+function error
+    echo $argv[2..] 1>&2
+    return $argv[1]
+end
+
+set _descr_beamer "Control the beamer in the lounge"
+set _usage_beamer "<on|off>"
+function cmd_beamer
+    switch $argv[1]
+        case on
+            ssh chaos@lounge.cccda.de beamer-on
+        case off
+            ssh chaos@lounge.cccda.de beamer-off
+        case "*"
+            error 1 "$(cmd_help beamer)"
+    end
+end
+
+set _descr_door "Control the door"
+function cmd_door
+    ssh door@door.cccda.de $argv
+end
+
+set _descr_help "Display this message"
+function cmd_help
+    set cmd "$argv[1]"
+    if [ "$cmd" != "" ]
+        echo "\
+$(eval echo \$_descr_$cmd)
+
+Usage:
+    chaosctl $cmd $(eval echo \$_usage_$cmd)"
+    else
+        echo "\
+The chaosctl utility
+
+Usage:
+    chaosctl <command>
+
+COMMANDS"
+        for cmd in $CMDS
+            echo -e "    $cmd\t$(eval echo \$_descr_$cmd)"
+        end
+    end
+end
+
+set _descr_tv "\tControl the TV in the kitchen"
+function cmd_tv
+    ssh chaos@kitchen.cccda.de tv $argv
+end
+
+if [ "cmd_$CMD" = cmd_ ]
+    error 1 "$(cmd_help)"
+    exit $status
+end
+
+if type -q "cmd_$CMD"
+    cmd_$CMD $argv[2..]
+else
+    error 127 "No such subcommand '$CMD'"
+end
diff --git a/packages/chaosctl/package.nix b/packages/chaosctl/package.nix
@@ -0,0 +1,5 @@
+{
+  writers,
+  ...
+}:
+writers.writeFishBin "chaosctl" ./chaosctl.fish
diff --git a/packages/default.nix b/packages/default.nix
@@ -0,0 +1,6 @@
+pkgs:
+
+builtins.removeAttrs (pkgs.lib.packagesFromDirectoryRecursive {
+  directory = ./.;
+  callPackage = pkgs.callPackage;
+}) [ "default" ]
diff --git a/packages/deployrsActivator/package.nix b/packages/deployrsActivator/package.nix
@@ -0,0 +1,53 @@
+{
+  systemConfig ? null,
+  lib,
+  buildEnv,
+  writeTextFile,
+  writeScriptBin,
+  runtimeShell,
+  deploy-rs,
+}:
+
+if systemConfig != null then
+  buildEnv {
+    name = "activatable-${systemConfig.system.build.toplevel.name}";
+    paths = [
+      systemConfig.system.build.toplevel
+      (writeTextFile {
+        name = "${systemConfig.system.build.toplevel.name}-activate-path";
+        text = ''
+          #!${runtimeShell}
+          set -euo pipefail
+
+          if [[ "''${DRY_ACTIVATE:-}" == "1" ]]
+          then
+              $PROFILE/bin/switch-to-configuration dry-activate
+          elif [[ "''${BOOT:-}" == "1" ]]
+          then
+              $PROFILE/bin/switch-to-configuration boot
+          else
+            # work around https://github.com/NixOS/nixpkgs/issues/73404
+            cd /tmp
+
+            $PROFILE/bin/switch-to-configuration switch
+
+            # https://github.com/serokell/deploy-rs/issues/31
+            ${lib.optionalString systemConfig.boot.loader.systemd-boot.enable "sed -i '/^default /d' ${systemConfig.boot.loader.efi.efiSysMountPoint}/loader/loader.conf"}
+          fi
+        '';
+        executable = true;
+        destination = "/deploy-rs-activate";
+      })
+      (writeTextFile {
+        name = "${systemConfig.system.build.toplevel.name}-activate-rs";
+        destination = "/activate-rs";
+        executable = true;
+        text = ''
+          #!${runtimeShell}
+          exec ${deploy-rs}/bin/activate "$@"
+        '';
+      })
+    ];
+  }
+else
+  writeScriptBin "activate" "echo 'No system config given!'"
diff --git a/packages/domsonic/package.nix b/packages/domsonic/package.nix
@@ -0,0 +1,41 @@
+{
+  lib,
+  stdenv,
+  fetchgit,
+  fetchYarnDeps,
+  yarnConfigHook,
+  yarnBuildHook,
+  yarnInstallHook,
+  nodejs,
+}:
+
+stdenv.mkDerivation (finalAttrs: {
+  pname = "domsonic";
+  version = builtins.substring 0 6 finalAttrs.rev;
+  rev = "1a49015b466af151e47bf1bf3dec971a03429ea4";
+
+  src = fetchgit {
+    url = "https://git.zaphyra.eu/domsonic";
+    hash = "sha256-lsrVnTYsltaBPAvfvPrqJYX8+ky9ce0KPBgOM/c5lc4=";
+    inherit (finalAttrs) rev;
+  };
+
+  yarnOfflineCache = fetchYarnDeps {
+    yarnLock = finalAttrs.src + "/yarn.lock";
+    hash = "sha256-A3zfYLa/UMIUR1xig3t7xQbx5nmx8Fwdqq2EFHCgSbM=";
+  };
+
+  nativeBuildInputs = [
+    nodejs
+    yarnConfigHook
+    yarnBuildHook
+    yarnInstallHook
+  ];
+
+  installPhase = ''
+    cp -r ./dist $out
+  '';
+
+  meta.maintainers = with lib.maintainers; [ zaphyra ];
+
+})
diff --git a/packages/dssd/package.nix b/packages/dssd/package.nix
@@ -0,0 +1,46 @@
+{
+  lib,
+  rustPlatform,
+  fetchFromGitHub,
+  pkg-config,
+  dbus,
+  ...
+}:
+
+rustPlatform.buildRustPackage (finalAttrs: {
+  pname = "dssd";
+  version = "0.3.3";
+  rev = "v${finalAttrs.version}";
+  srcHash = "sha256-gAV4gwrfvYfc2f1tDY/cNOFMrQzrzHSmEFsKg7ke/6c=";
+  cargoHash = "sha256-yX2/2TW3FNbqwzR6+5yP26E2Eps0bTJgJJrDIQG2KQU=";
+
+  src = fetchFromGitHub {
+    owner = "ylxdzsw";
+    repo = finalAttrs.pname;
+    hash = finalAttrs.srcHash;
+    inherit (finalAttrs) rev;
+  };
+
+  nativeBuildInputs = [
+    pkg-config
+  ];
+
+  buildInputs = [
+    dbus
+  ];
+
+  postInstall = ''
+    mkdir -p $out/share/dbus-1/services
+
+    cp ./org.freedesktop.secrets.service $out/share/dbus-1/services/
+  '';
+
+  meta = {
+    mainProgram = "dssd";
+    description = "Dead Simple Secret Daemon";
+    homepage = "https://github.com/ylxdzsw/dssd";
+    license = [ lib.licenses.mit ];
+    platforms = lib.platforms.linux;
+    maintainers = with lib.maintainers; [ zaphyra ];
+  };
+})
diff --git a/packages/echo-meter/package.nix b/packages/echo-meter/package.nix
@@ -0,0 +1,56 @@
+{
+  lib,
+  fetchFromGitHub,
+  stdenv,
+  pkg-config,
+  gtk4,
+  gtk4-layer-shell,
+  json-glib,
+  ...
+}:
+
+stdenv.mkDerivation (finalAttrs: {
+  pname = "echo-meter";
+  version = "0.0.0-unstable-2025-08-26";
+  rev = "e6e5a70600e85edcfca3aa44153a26614b6fe55c";
+  srcHash = "sha256-QPQ3crm7i0L0PivlhIsPLrv1TNVwF/c+WQDRK4EtEV8=";
+
+  src = fetchFromGitHub {
+    owner = "Nithin-3";
+    repo = finalAttrs.pname;
+    hash = finalAttrs.srcHash;
+    inherit (finalAttrs) rev;
+  };
+
+  postPatch = ''
+    substituteInPlace src/echo-meter.c \
+      --replace-fail "gtk-layer-shell/gtk-layer-shell.h" "gtk4-layer-shell/gtk4-layer-shell.h"
+  '';
+
+  nativeBuildInputs = [
+    pkg-config
+  ];
+
+  buildInputs = [
+    gtk4
+    gtk4-layer-shell
+    json-glib
+  ];
+
+  installPhase = ''
+    mkdir -p $out/bin
+
+    cp ./echo-meter $out/bin
+    cp -r ./assets $out/share
+  '';
+
+  meta = {
+    description = "A lightweight GTK4 layer-shell application that displays always-on-top real-time audio, brightness, and microphone level indicators.";
+    homepage = "https://github.com/Nithin-3/echo-meter";
+    license = lib.licenses.mit;
+    maintainers = with lib.maintainers; [ zaphyra ];
+    platforms = lib.platforms.linux;
+    mainProgram = "echo-meter";
+  };
+
+})
diff --git a/packages/gotosocial-unstable/0001-disable-template-indentation.patch b/packages/gotosocial-unstable/0001-disable-template-indentation.patch
@@ -0,0 +1,40 @@
+From 319ef126ac07b7dc4d63ab749b2dac7fe155a905 Mon Sep 17 00:00:00 2001
+From: "Katja Ramona Sophie Kwast (zaphyra)" <git@zaphyra.eu>
+Date: Fri, 22 Aug 2025 09:46:11 +0200
+Subject: [PATCH] disable template indentation
+
+---
+ internal/router/template.go | 12 ++----------
+ 1 file changed, 2 insertions(+), 10 deletions(-)
+
+diff --git a/internal/router/template.go b/internal/router/template.go
+index 860f9aad5..e4d0d99f5 100644
+--- a/internal/router/template.go
++++ b/internal/router/template.go
+@@ -323,21 +323,13 @@ func subtract(n1 int, n2 int) int {
+ // indent appropriately indents the given html
+ // by prepending each line with the indentStr.
+ func indent(n int, html template.HTML) template.HTML {
+-	out := indentRegex.ReplaceAllString(
+-		string(html),
+-		indents[:n*indentStrLen],
+-	)
+-	return noescape(out)
++	return html
+ }
+ 
+ // indentAttr appropriately indents the given html
+ // attribute by prepending each line with the indentStr.
+ func indentAttr(n int, html template.HTMLAttr) template.HTMLAttr {
+-	out := indentRegex.ReplaceAllString(
+-		string(html),
+-		indents[:n*indentStrLen],
+-	)
+-	return noescapeAttr(out)
++	return html
+ }
+ 
+ // outdentPreformatted outdents all preformatted text in
+-- 
+2.50.1
+
diff --git a/packages/gotosocial-unstable/package.nix b/packages/gotosocial-unstable/package.nix
@@ -0,0 +1,142 @@
+{
+  unstable,
+  lib,
+  fetchFromGitea,
+  applyPatches,
+  mkYarnPackage,
+  makeWrapper,
+  installShellFiles,
+  nixosTests,
+  ...
+}:
+
+unstable.buildGoModule (finalAttrs: {
+  pname = "gotosocial";
+  version = "0.22.0-unstable-2026-05-20-${builtins.substring 0 6 finalAttrs.rev}";
+  rev = "7161b5c7db457353f09a41612c4d3be3775c2636";
+  srcHash = "sha256-CJfov3aMCgijlqNN8BOO8RO9IqPoyruMQQw2654pBX0=";
+
+  src = applyPatches {
+    src = fetchFromGitea {
+      domain = "codeberg.org";
+      owner = "superseriousbusiness";
+      repo = "gotosocial";
+      hash = finalAttrs.srcHash;
+      inherit (finalAttrs) rev;
+    };
+    patches = [
+      ./0001-disable-template-indentation.patch
+    ];
+  };
+
+  frontend-assets = mkYarnPackage {
+    name = "${finalAttrs.pname}-${finalAttrs.version}-frontendAssets";
+    inherit (finalAttrs) src version;
+
+    packageJSON = "${finalAttrs.src}/web/source/package.json";
+    yarnLock = "${finalAttrs.src}/web/source/yarn.lock";
+
+    configurePhase = ''
+      runHook preConfigure
+
+      cp -r $node_modules node_modules
+      chmod +w -R node_modules
+
+      runHook postConfigure
+    '';
+
+    buildPhase = ''
+      runHook preBuild
+
+      mkdir -p ./web/source ./web/assets
+
+      cp -r $src/web/source/. ./web/source
+      cp -r $src/web/assets/. ./web/assets
+
+      export NODE_OPTIONS=--openssl-legacy-provider
+      node ./node_modules/ts-patch/bin/ts-patch.js install
+      yarn --offline --cwd ./web/source build
+
+      runHook postBuild
+    '';
+
+    distPhase = "true";
+    installPhase = "cp -r ./web/assets $out";
+
+    meta.license = lib.licenses.agpl3Only;
+  };
+
+  ldflags = [
+    "-s"
+    "-w"
+    "-X 'main.Commit=${finalAttrs.rev}'"
+    "-X 'main.Version=${finalAttrs.version}'"
+  ];
+  tags = [
+    "netgo"
+    "osusergo"
+    "static_build"
+    "kvformat"
+  ];
+
+  env.CGO_ENABLED = 0;
+
+  vendorHash = null;
+
+  nativeBuildInputs = [ installShellFiles ];
+  buildInputs = [ makeWrapper ];
+
+  # tests are working only on x86_64-linux
+  # doCheck = stdenv.hostPlatform.isLinux && stdenv.hostPlatform.isx86_64;
+  # checks are currently very unstable in our setup, so we should test manually for now
+  doCheck = false;
+
+  checkFlags =
+    let
+      # flaky / broken tests
+      skippedTests = [
+        # See: https://github.com/superseriousbusiness/gotosocial/issues/2651
+        "TestPage/minID,_maxID_and_limit_set"
+      ];
+    in
+    [ "-skip=^${builtins.concatStringsSep "$|^" skippedTests}$" ];
+
+  installCheckPhase = ''
+    runHook preCheck
+
+    $out/bin/gotosocial --help
+
+    runHook postCheck
+  '';
+
+  postInstall = ''
+    mkdir -p $out/share/${finalAttrs.pname}/web/{templates,assets}
+
+    cp -r  ./web/template/.                $out/share/${finalAttrs.pname}/web/template/
+    cp -rf ${finalAttrs.frontend-assets}/. $out/share/${finalAttrs.pname}/web/assets/
+
+    installShellCompletion --cmd gotosocial \
+      --bash <($out/bin/gotosocial completion bash) \
+      --fish <($out/bin/gotosocial completion fish) \
+      --zsh <($out/bin/gotosocial completion zsh)
+  '';
+
+  passthru.tests.gotosocial = nixosTests.gotosocial;
+
+  meta = with lib; {
+    mainProgram = "gotosocial";
+    maintainers = with lib.maintainers; [ zaphyra ];
+    license = licenses.agpl3Only;
+    homepage = "https://gotosocial.org";
+    changelog = "https://codeberg.org/superseriousbusiness/gotosocial/releases";
+    description = "Fast, fun, ActivityPub server, powered by Go";
+    longDescription = ''
+      ActivityPub social network server, written in Golang.
+      You can keep in touch with your friends, post, read, and
+      share images and articles. All without being tracked or
+      advertised to! A light-weight alternative to Mastodon
+      and Pleroma, with support for clients!
+    '';
+  };
+
+})
diff --git a/packages/mautrix-telegram/package.nix b/packages/mautrix-telegram/package.nix
@@ -0,0 +1,32 @@
+{
+  lib,
+  buildGoModule,
+  fetchFromGitHub,
+}:
+
+buildGoModule (finalAttrs: {
+  pname = "mautrix-telegramgo";
+  version = "0.2605.0";
+
+  srcHash = "sha256-9TCXyGvFCZAv8xIUW3oiVRv5EBdObrLuALfME/oAWBE=";
+  vendorHash = "sha256-xcBbBIsFXQ90WyQ8OY+CCVIiBepIlOD/o+ZjabNvM0Q=";
+
+  src = fetchFromGitHub {
+    owner = "mautrix";
+    repo = "telegram";
+    rev = "v${finalAttrs.version}";
+    hash = finalAttrs.srcHash;
+  };
+
+  tags = [ "goolm" ];
+
+  doCheck = false;
+
+  meta = {
+    homepage = "https://github.com/mautrix/telegram";
+    description = "A Matrix-Telegram puppeting bridge";
+    license = lib.licenses.agpl3Plus;
+    maintainers = with lib.maintainers; [ zaphyra ];
+    mainProgram = "mautrix-telegram";
+  };
+})
diff --git a/packages/memos/package.nix b/packages/memos/package.nix
@@ -0,0 +1,80 @@
+{
+  fetchFromGitHub,
+  buildGoModule,
+  stdenvNoCC,
+  nix-update-script,
+  nodejs,
+  lib,
+  fetchPnpmDeps,
+  pnpmConfigHook,
+  pnpm,
+}:
+let
+  version = "0.26.0";
+  src = fetchFromGitHub {
+    owner = "usememos";
+    repo = "memos";
+    rev = "v${version}";
+    hash = "sha256-3Q+acA1MjPlWMpRHpkNsg7CVc2DrBosfg4JC115OXMQ=";
+  };
+
+  memos-web = stdenvNoCC.mkDerivation (finalAttrs: {
+    pname = "memos-web";
+    inherit version src;
+    pnpmDeps = fetchPnpmDeps {
+      inherit (finalAttrs) pname version src;
+      sourceRoot = "${finalAttrs.src.name}/web";
+      fetcherVersion = 1;
+      hash = "sha256-6lrXpblVY7Q7UMMU0obVAwACodW+WJm/INiSMPzEgEI=";
+    };
+    pnpmRoot = "web";
+    nativeBuildInputs = [
+      nodejs
+      pnpmConfigHook
+      pnpm
+    ];
+    buildPhase = ''
+      runHook preBuild
+      pnpm -C web build
+      runHook postBuild
+    '';
+    installPhase = ''
+      runHook preInstall
+      cp -r web/dist $out
+      runHook postInstall
+    '';
+  });
+in
+buildGoModule {
+  pname = "memos";
+  inherit
+    version
+    src
+    memos-web
+    ;
+
+  vendorHash = "sha256-J/qsybKA+RIKQKaHmAtTcHt6z5VnFpWqFT0dF7AceWQ=";
+
+  doCheck = false;
+
+  preBuild = ''
+    rm -rf server/router/frontend/dist
+    cp -r ${memos-web} server/router/frontend/dist
+  '';
+
+  passthru.updateScript = nix-update-script {
+    extraArgs = [
+      "--subpackage"
+      "memos-web"
+    ];
+  };
+
+  meta = {
+    homepage = "https://usememos.com";
+    description = "Lightweight, self-hosted memo hub";
+    changelog = "https://github.com/usememos/memos/releases/tag/${src.rev}";
+    license = lib.licenses.mit;
+    mainProgram = "memos";
+    maintainers = with lib.maintainers; [ zaphyra ];
+  };
+}
diff --git a/packages/niri-screen-time/package.nix b/packages/niri-screen-time/package.nix
@@ -0,0 +1,29 @@
+{
+  lib,
+  buildGoModule,
+  fetchFromGitHub,
+}:
+
+buildGoModule (finalAttrs: {
+  pname = "niri-screen-time";
+  version = "0.0.0-${builtins.substring 0 6 finalAttrs.rev}";
+  rev = "dbe4a93c9a163427b0f7bed96fcfb338e274499f";
+  srcHash = "sha256-BxLV4cUKLb3uGLlj3Pn0PF6gAxwimHTK0WZyOYWf/L8=";
+
+  src = fetchFromGitHub {
+    owner = "probeldev";
+    repo = "niri-screen-time";
+    hash = finalAttrs.srcHash;
+    inherit (finalAttrs) rev;
+  };
+
+  vendorHash = "sha256-9y1F2ZrmpiQJ9ZTq9SoRE2PxR65DDNCeBKf4M0HUQC4=";
+
+  meta = {
+    homepage = "https://github.com/probeldev/niri-screen-time";
+    description = "Niri Screen Time";
+    license = lib.licenses.mit;
+    maintainers = with lib.maintainers; [ zaphyra ];
+    mainProgram = "niri-screen-time";
+  };
+})
diff --git a/packages/niri-taskbar/package.nix b/packages/niri-taskbar/package.nix
@@ -0,0 +1,47 @@
+{
+  lib,
+  unstable,
+  fetchFromGitHub,
+  pkg-config,
+  glib,
+  atkmm,
+  gdk-pixbuf,
+  cairo,
+  gtk3,
+  ...
+}:
+
+unstable.rustPlatform.buildRustPackage (finalAttrs: {
+  pname = "niri-taskbar";
+  version = "0.4.0+niri.25.11";
+  rev = "v${finalAttrs.version}";
+  srcHash = "sha256-aE5v94AA6bC0CP8pv/SPBxGKpkH+GxR/p7hTKXlvk3E=s";
+  cargoHash = "sha256-WRc1+ZVhiIfmLHaczAPq21XudI08CgVhlIhVcf0rmSw=";
+
+  src = fetchFromGitHub {
+    owner = "LawnGnome";
+    repo = finalAttrs.pname;
+    hash = finalAttrs.srcHash;
+    inherit (finalAttrs) rev;
+  };
+
+  nativeBuildInputs = [
+    pkg-config
+  ];
+
+  buildInputs = [
+    glib
+    atkmm
+    gdk-pixbuf
+    cairo
+    gtk3
+  ];
+
+  meta = {
+    description = "Niri taskbar module for Waybar";
+    homepage = "https://github.com/LawnGnome/niri-taskbar";
+    license = [ lib.licenses.mit ];
+    platforms = lib.platforms.linux;
+    maintainers = with lib.maintainers; [ zaphyra ];
+  };
+})
diff --git a/packages/nirilayout/package.nix b/packages/nirilayout/package.nix
@@ -0,0 +1,48 @@
+{
+  lib,
+  buildGoModule,
+  fetchFromGitHub,
+  pkg-config,
+  gtk4,
+  gtk4-layer-shell,
+  gobject-introspection,
+  ...
+}:
+
+buildGoModule (finalAttrs: {
+  pname = "nirilayout";
+  version = "0.2.0";
+
+  src = fetchFromGitHub {
+    owner = "calico32";
+    repo = finalAttrs.pname;
+    hash = "sha256-rlW8n+1gVPFBgv5UbVS43bI0NBVHfOPjJ6R0kMytrcQ=";
+    rev = "v${finalAttrs.version}";
+  };
+
+  vendorHash = "sha256-o6/ZH70j3e4kU/ZxP41/88kXakrWAu6QdHtOY+EceCs=";
+
+  nativeBuildInputs = [
+    pkg-config
+  ];
+
+  buildInputs = [
+    gtk4
+    gtk4-layer-shell
+    gobject-introspection
+  ];
+
+  meta = with lib; {
+    maintainers = with lib.maintainers; [ zaphyra ];
+    license = licenses.mit;
+    homepage = "https://github.com/calico32/nirilayout";
+    changelog = "https://github.com/calico32/nirilayout/releases/tag/v${finalAttrs.version}";
+    description = "niri monitor layout switcher";
+    longDescription = ''
+      nirilayout is a simple tool to quickly switch your niri output configuration
+      between different layouts. Especially useful for laptop users who move between
+      different setups frequently.
+    '';
+  };
+
+})
diff --git a/packages/nirimap/package.nix b/packages/nirimap/package.nix
@@ -0,0 +1,44 @@
+{
+  lib,
+  rustPlatform,
+  fetchFromGitHub,
+  pkg-config,
+  wrapGAppsHook4,
+  gtk4,
+  gtk4-layer-shell,
+  ...
+}:
+
+rustPlatform.buildRustPackage (finalAttrs: {
+  pname = "nirimap";
+  version = "0.2.0";
+  rev = "v${finalAttrs.version}";
+  srcHash = "sha256-4HnmIc9FDXgPfbJdhjuVenc2R/wZ9ULTi6QaTskO1/s=";
+  cargoHash = "sha256-EI79WewUTAOFivRsR2ZjywEAYZ9Lq6YnfwPml071CqU=";
+
+  src = fetchFromGitHub {
+    owner = "alexandergknoll";
+    repo = finalAttrs.pname;
+    hash = finalAttrs.srcHash;
+    inherit (finalAttrs) rev;
+  };
+
+  nativeBuildInputs = [
+    pkg-config
+    wrapGAppsHook4
+  ];
+
+  buildInputs = [
+    gtk4
+    gtk4-layer-shell
+  ];
+
+  meta = {
+    description = "A tool that automatically maximizes the only window of a niri workspace";
+    homepage = "https://github.com/Antiz96/oniri";
+    license = lib.licenses.gpl3;
+    mainProgram = "oniri";
+    maintainers = with lib.maintainers; [ zaphyra ];
+    platforms = lib.platforms.linux;
+  };
+})
diff --git a/packages/nix-cleanup/nix-cleanup.sh b/packages/nix-cleanup/nix-cleanup.sh
@@ -0,0 +1,13 @@
+#!/usr/bin/env bash
+set -eu
+
+# Delete everything from this profile that isn't currently needed
+nix-env --delete-generations old
+
+# Delete generations older than a week
+nix-collect-garbage
+nix-collect-garbage --delete-older-than 7d
+
+# Optimize
+nix-store --gc --print-dead
+nix-store --optimise
diff --git a/packages/nix-cleanup/package.nix b/packages/nix-cleanup/package.nix
@@ -0,0 +1,25 @@
+{
+  lib,
+  stdenvNoCC,
+  ...
+}:
+
+stdenvNoCC.mkDerivation (finalAttrs: {
+  pname = "nix-cleanup";
+  version = "0.0.0.1";
+
+  dontUnpack = true;
+  src = ./nix-cleanup.sh;
+
+  postInstall = ''
+    mkdir -p $out/bin
+    install --mode +x $src $out/bin/nix-cleanup
+  '';
+
+  meta = {
+    description = "Just a tiny helper to clean up your nix store!";
+    license = [ lib.licenses.mit ];
+    platforms = lib.platforms.linux;
+    maintainers = with lib.maintainers; [ zaphyra ];
+  };
+})
diff --git a/packages/nocturne/package.nix b/packages/nocturne/package.nix
@@ -0,0 +1,58 @@
+{
+  lib,
+  fetchFromGitHub,
+  python3Packages,
+  libadwaita,
+  wrapGAppsHook4,
+  meson,
+  ninja,
+  pkg-config,
+  libsecret,
+  gtk4,
+  blueprint-compiler,
+  desktop-file-utils,
+}:
+
+python3Packages.buildPythonApplication rec {
+  pname = "nocturne";
+  version = "1.0.1";
+
+  pyproject = false;
+
+  src = fetchFromGitHub {
+    owner = "Jeffser";
+    repo = "Nocturne";
+    tag = version;
+    hash = "sha256-nW8DCziEERN6xamT+eS6eGTnoNWG6OTdgLb4E2FIzXQ=";
+  };
+
+  nativeBuildInputs = [
+    wrapGAppsHook4
+    meson
+    ninja
+    pkg-config
+    gtk4
+    blueprint-compiler
+    desktop-file-utils
+  ];
+
+  buildInputs = [
+    libadwaita
+    libsecret
+  ];
+
+  dontWrapGApps = true;
+
+  preFixup = ''
+    makeWrapperArgs+=("''${gappsWrapperArgs[@]}")
+  '';
+
+  meta = {
+    description = " An Adwaita Music Player / Library Manager ";
+    changelog = "https://github.com/Jeffser/Nocturne/releases/tag/${version}";
+    homepage = "https://github.com/Jeffser/Nocturne";
+    license = lib.licenses.gpl3;
+    mainProgram = "nocturne";
+    maintainers = with lib.maintainers; [ zaphyra ];
+  };
+}
diff --git a/packages/oniri/package.nix b/packages/oniri/package.nix
@@ -0,0 +1,35 @@
+{
+  lib,
+  rustPlatform,
+  fetchFromGitHub,
+  pkg-config,
+  ...
+}:
+
+rustPlatform.buildRustPackage (finalAttrs: {
+  pname = "oniri";
+  version = "1.2.0";
+  rev = "v${finalAttrs.version}";
+  srcHash = "sha256-54OA+N2qCbtfbYAEh18D1rUjxvmarZHPaH33maQa4uc=";
+  cargoHash = "sha256-k1hnveoLOld7ec/FRmdUt8Ayoz45FO1KBkKVlA8E/ss=";
+
+  src = fetchFromGitHub {
+    owner = "Antiz96";
+    repo = finalAttrs.pname;
+    hash = finalAttrs.srcHash;
+    inherit (finalAttrs) rev;
+  };
+
+  nativeBuildInputs = [
+    pkg-config
+  ];
+
+  meta = {
+    description = "A tool that automatically maximizes the only window of a niri workspace";
+    homepage = "https://github.com/Antiz96/oniri";
+    license = lib.licenses.gpl3;
+    mainProgram = "oniri";
+    platforms = lib.platforms.linux;
+    maintainers = with lib.maintainers; [ zaphyra ];
+  };
+})
diff --git a/packages/phanpy/package.nix b/packages/phanpy/package.nix
@@ -0,0 +1,47 @@
+{
+  lib,
+  fetchFromGitHub,
+  buildNpmPackage,
+  clientName ? "Phanpy",
+  website ? "https://phanpy.social",
+  defaultInstance ? "",
+  defaultInstanceRegistrationUrl ? "",
+  defaultLang ? "en",
+  ...
+}:
+
+buildNpmPackage (finalAttrs: {
+  pname = "phanpy";
+  version = "2025.07.18.3f4b1a6";
+
+  rev = "b74005d30721d55832b14a8f2e07bd918fdeb5c4";
+  srcHash = "sha256-0OkH/XojM0W2oun797sNJqFrxNqFau1P+NECxCrib20=";
+  npmDepsHash = "sha256-2a+5G0ENpjOvw+TuxEJrkabAB3uoQnaBQc7Nek7a/dw=";
+
+  src = fetchFromGitHub {
+    owner = "cheeaun";
+    repo = "phanpy";
+    hash = finalAttrs.srcHash;
+    inherit (finalAttrs) rev;
+  };
+
+  env = {
+    NODE_OPTIONS = "--openssl-legacy-provider";
+    PHANPY_CLIENT_NAME = clientName;
+    PHANPY_WEBSITE = website;
+    PHANPY_DEFAULT_INSTANCE = defaultInstance;
+    PHANPY_DEFAULT_INSTANCE_REGISTRATION_URL = defaultInstanceRegistrationUrl;
+    PHANPY_DEFAULT_LANG = defaultLang;
+  };
+
+  installPhase = ''
+    cp -r ./dist $out
+  '';
+
+  meta = {
+    description = "A minimalistic opinionated Mastodon web client ";
+    homepage = "https://github.com/cheeaun/phanpy";
+    license = lib.licenses.mit;
+    maintainers = with lib.maintainers; [ zaphyra ];
+  };
+})
diff --git a/packages/setupDisk/package.nix b/packages/setupDisk/package.nix
@@ -0,0 +1,108 @@
+{
+  systemConfig ? null,
+  lib,
+  writeShellApplication,
+  writeScriptBin,
+  btrfs-progs,
+  parted,
+  openssh,
+}:
+
+if systemConfig != null then
+  writeShellApplication {
+    name = "setup-disk-${systemConfig.networking.hostName}";
+    runtimeInputs = [
+      btrfs-progs
+      parted
+      openssh
+    ];
+    text =
+      let
+        inherit (systemConfig.networking) hostName;
+        bootDisk = "/dev/disk/by-partlabel/${hostName}-boot";
+        rootDisk = "/dev/disk/by-partlabel/${hostName}-root";
+        subvolumes = lib.pipe systemConfig.fileSystems [
+          builtins.attrValues
+          (builtins.filter (element: element.device == "/dev/mapper/root"))
+          (builtins.map (element: element.options))
+          lib.flatten
+          (builtins.filter (value: (builtins.substring 0 6 value) == "subvol"))
+          (builtins.map (element: builtins.substring 7 (builtins.builtins.stringLength element) element))
+        ];
+      in
+      ''
+        set -euo pipefail
+
+        read -rp 'Disk: ' disk
+        read -srp "LUKS passphrase: " pass1
+        echo ""
+        read -srp 'LUKS passphrase (repeat): ' pass2
+        echo ""
+
+        if [[ "$pass1" != "$pass2" ]]
+        then
+          echo "Passphrases don't match"
+          exit
+        fi
+
+        parted --script --align optimal --fix "$disk" -- mklabel gpt \
+          mkpart ${hostName}-boot fat32 1M    1024M \
+          mkpart ${hostName}-root btrfs 1025M 100% \
+          set 1 esp on \
+          type 2 C12A7328-F81F-11D2-BA4B-00A0C93EC93B \
+          type 2 6523f8ae-3eb1-4e2a-a05a-18b695ae656f
+
+        echo "$pass1" | cryptsetup -q luksFormat "${rootDisk}"
+        echo "$pass1" | cryptsetup -q luksOpen "${rootDisk}" root
+
+        mkfs.vfat "${bootDisk}"
+        mkfs.btrfs /dev/mapper/root
+
+        mount --verbose /dev/mapper/root /mnt
+
+        ${lib.optionalString systemConfig.common.configure.persist.system.enable ''
+          btrfs subvolume create /mnt/nixos-root-1
+          btrfs subvolume create /mnt/nixos-root-2
+          btrfs subvolume create /mnt/nixos-root-3
+          btrfs subvolume create /mnt/nixos-root-4
+          btrfs subvolume set-default /mnt/nixos-root-1
+        ''}
+        ${lib.optionalString (systemConfig.common.configure.persist.system.enable == false) ''
+          btrfs subvolume create /mnt/nixos-root
+          btrfs subvolume set-default /mnt/nixos-root
+        ''}
+        ${lib.optionalString (builtins.elem "swap" subvolumes) ''
+          btrfs subvolume create /mnt/swap
+          btrfs filesystem mkswapfile --size ${systemConfig.common.configure.rootDisk.swap.size} --uuid clear /mnt/swap/swapfile
+        ''}
+        ${lib.pipe subvolumes [
+          (lib.remove "nixos-root-1")
+          (lib.remove "nixos-root")
+          (lib.remove "swap")
+          (builtins.map (element: "btrfs subvolume create /mnt/${element}"))
+          (builtins.concatStringsSep "\n")
+        ]}
+
+        umount --verbose /mnt
+
+        ${lib.pipe systemConfig.fileSystems [
+          (lib.filterAttrs (name: value: value.device == "/dev/mapper/root"))
+          (lib.concatMapAttrsStringSep "\n" (
+            name: value:
+            "mount --verbose --mkdir --options ${
+              lib.pipe value.options [
+                (builtins.filter (value: value != "x-initrd.mount"))
+                (builtins.concatStringsSep ",")
+              ]
+            } ${value.device} ${lib.strings.normalizePath "/mnt${lib.removeSuffix "/" name}"}"
+          ))
+        ]}
+
+        # generate ssh hostkey
+        mkdir -p /mnt${lib.removeSuffix "ssh_host_ed25519_key" (lib.last systemConfig.sops.age.sshKeyPaths)}
+        ssh-keygen -t ed25519 -f /mnt${lib.last systemConfig.sops.age.sshKeyPaths}
+        cat /mnt${lib.last systemConfig.sops.age.sshKeyPaths}.pub
+      '';
+  }
+else
+  writeScriptBin "setup-disk-none" "echo 'No system config given!'"
diff --git a/packages/shaderbg/package.nix b/packages/shaderbg/package.nix
@@ -0,0 +1,47 @@
+{
+  lib,
+  fetchFromSourcehut,
+  stdenv,
+  meson,
+  cmake,
+  ninja,
+  pkg-config,
+  libGL,
+  wayland,
+  wayland-scanner,
+  ...
+}:
+
+stdenv.mkDerivation (finalAttrs: {
+  pname = "shaderbg";
+  version = "0.0.0-unstable-2023-03-16";
+  rev = "027d4f87fd542c79d4276b521e39025477b6d03e";
+  srcHash = "sha256-/HtbS+vn69oEDVP4HDBvnmpkGRLz62j8lCZx+plrUeI=";
+
+  buildInputs = [
+    meson
+    cmake
+    ninja
+    pkg-config
+    libGL
+    wayland
+    wayland-scanner
+  ];
+
+  src = fetchFromSourcehut {
+    owner = "~mstoeckl";
+    repo = finalAttrs.pname;
+    hash = finalAttrs.srcHash;
+    inherit (finalAttrs) rev;
+  };
+
+  meta = {
+    description = "A live wallpaper program for Sway and other compositors with wlr-layer-shell support";
+    homepage = "https://git.sr.ht/~mstoeckl/shaderbg";
+    license = lib.licenses.gpl3;
+    maintainers = with lib.maintainers; [ zaphyra ];
+    platforms = lib.platforms.linux;
+    mainProgram = "shaderbg";
+  };
+
+})
diff --git a/packages/vibepanel/package.nix b/packages/vibepanel/package.nix
@@ -0,0 +1,52 @@
+{
+  lib,
+  rustPlatform,
+  fetchFromGitHub,
+  pkg-config,
+  wrapGAppsHook4,
+  gtk4,
+  gtk4-layer-shell,
+  libpulseaudio,
+  udev,
+  dbus,
+  wayland,
+  ...
+}:
+
+rustPlatform.buildRustPackage (finalAttrs: {
+  pname = "vibepanel";
+  version = "0.14.1";
+  rev = "v${finalAttrs.version}";
+  srcHash = "sha256-QsYSfNzaAtUTJGuGxikproRgMmmcKO4ZCkyrbruh4Z4=";
+  cargoHash = "sha256-PPwMNvhJCvjoMwCBu0aeONXBNlCecgYUnwm9PnjHI0c=";
+
+  src = fetchFromGitHub {
+    owner = "prankstr";
+    repo = finalAttrs.pname;
+    hash = finalAttrs.srcHash;
+    inherit (finalAttrs) rev;
+  };
+
+  nativeBuildInputs = [
+    pkg-config
+    wrapGAppsHook4
+  ];
+
+  buildInputs = [
+    gtk4
+    gtk4-layer-shell
+    libpulseaudio
+    udev
+    dbus
+    wayland
+  ];
+
+  meta = {
+    description = "A GTK4 panel for Wayland with integrated notifications, OSD, and quick settings";
+    homepage = "https://github.com/prankstr/vibepanel";
+    license = lib.licenses.mit;
+    mainProgram = "vibepanel";
+    platforms = lib.platforms.linux;
+    maintainers = with lib.maintainers; [ zaphyra ];
+  };
+})
diff --git a/packages/wleave/package.nix b/packages/wleave/package.nix
@@ -0,0 +1,77 @@
+{
+  lib,
+  fetchFromGitHub,
+  rustPlatform,
+  installShellFiles,
+  pkg-config,
+  scdoc,
+  wrapGAppsHook4,
+  at-spi2-atk,
+  glib,
+  gtk4,
+  gtk4-layer-shell,
+  libadwaita,
+  libxml2,
+}:
+rustPlatform.buildRustPackage rec {
+  pname = "wleave";
+  version = "0.6.2";
+
+  src = fetchFromGitHub {
+    owner = "AMNatty";
+    repo = "wleave";
+    rev = version;
+    hash = "sha256-+0EKnaxRaHRxRvhASuvfpUijEZJFimR4zSzOyC3FOkQ=";
+  };
+
+  cargoHash = "sha256-MRVWiQNzETFbWeKwYeoXSUY9gncRCsYdPEZhpOKcTvA=";
+
+  nativeBuildInputs = [
+    installShellFiles
+    pkg-config
+    scdoc
+    wrapGAppsHook4
+  ];
+
+  buildInputs = [
+    at-spi2-atk
+    glib
+    gtk4
+    gtk4-layer-shell
+    libadwaita
+    libxml2
+  ];
+
+  postPatch = ''
+    substituteInPlace layout.json \
+      --replace-fail "/usr/share/wleave" "$out/share/${pname}"
+
+    substituteInPlace src/config.rs \
+      --replace-fail "/etc/wleave" "$out/etc/${pname}"
+  '';
+
+  postInstall = ''
+    install -Dm644 -t "$out/etc/wleave" {"style.css","layout.json"}
+    install -Dm644 -t "$out/share/wleave/icons" icons/*
+
+    for f in man/*.scd; do
+      local page="man/$(basename "$f" .scd)"
+      scdoc < "$f" > "$page"
+      installManPage "$page"
+    done
+
+    installShellCompletion --cmd wleave \
+      --bash <(cat completions/wleave.bash) \
+      --fish <(cat completions/wleave.fish) \
+      --zsh <(cat completions/_wleave)
+  '';
+
+  meta = with lib; {
+    description = "Wayland-native logout script written in GTK4";
+    homepage = "https://github.com/AMNatty/wleave";
+    license = licenses.mit;
+    mainProgram = "wleave";
+    maintainers = with lib.maintainers; [ zaphyra ];
+    platforms = platforms.linux;
+  };
+}
diff --git a/packages/wlsbg/package.nix b/packages/wlsbg/package.nix
@@ -0,0 +1,60 @@
+{
+  lib,
+  fetchFromGitHub,
+  stdenv,
+  meson,
+  cmake,
+  ninja,
+  scdoc,
+  git,
+  pkg-config,
+  wayland,
+  wayland-protocols,
+  wayland-scanner,
+  libGL,
+  mpv,
+  fftwFloat,
+  ...
+}:
+
+stdenv.mkDerivation (finalAttrs: {
+  pname = "wlsbg";
+  version = "3.3.7";
+  rev = "v${finalAttrs.version}";
+  srcHash = "sha256-e0eOLsfU4wrz8Ck/iWwSnimTSYICmN/3vKeObmSE6zA=";
+
+  nativeBuildInputs = [
+    meson
+    ninja
+    pkg-config
+    cmake
+    scdoc
+    git
+  ];
+
+  buildInputs = [
+    wayland
+    wayland-protocols
+    wayland-scanner
+    libGL
+    mpv
+    fftwFloat
+  ];
+
+  src = fetchFromGitHub {
+    owner = "Sublimeful";
+    repo = finalAttrs.pname;
+    hash = finalAttrs.srcHash;
+    inherit (finalAttrs) rev;
+  };
+
+  meta = {
+    description = "Wallpaper tool with shader support for Wayland compositors";
+    homepage = "https://github.com/Sublimeful/wlsbg";
+    license = lib.licenses.mit;
+    maintainers = with lib.maintainers; [ zaphyra ];
+    platforms = lib.platforms.linux;
+    mainProgram = "wlsbg";
+  };
+
+})
diff --git a/packages/wooz/package.nix b/packages/wooz/package.nix
@@ -0,0 +1,47 @@
+{
+  lib,
+  fetchFromGitHub,
+  stdenv,
+  meson,
+  ninja,
+  cmake,
+  pkg-config,
+  wayland,
+  wayland-protocols,
+  wayland-scanner,
+  ...
+}:
+
+stdenv.mkDerivation (finalAttrs: {
+  pname = "wooz";
+  version = "0.1.0";
+  rev = "v${finalAttrs.version}";
+  srcHash = "sha256-mOrxRjvvcPTauBwau5Za/e3kqyN5ZJtU9ZTqmDyj/YY=";
+
+  buildInputs = [
+    meson
+    ninja
+    cmake
+    pkg-config
+    wayland
+    wayland-protocols
+    wayland-scanner
+  ];
+
+  src = fetchFromGitHub {
+    owner = "negrel";
+    repo = finalAttrs.pname;
+    hash = finalAttrs.srcHash;
+    inherit (finalAttrs) rev;
+  };
+
+  meta = {
+    description = "🔍 A zoom / magnifier utility for wayland compositors.";
+    homepage = "https://github.com/negrel/wooz";
+    license = lib.licenses.mit;
+    maintainers = with lib.maintainers; [ zaphyra ];
+    platforms = lib.platforms.linux;
+    mainProgram = "wooz";
+  };
+
+})
diff --git a/resources/default.nix b/resources/default.nix
@@ -0,0 +1,21 @@
+{
+  npins ? import ../npins,
+  lib ? import "${(import ../npins).nixpkgs}/lib",
+  ...
+}:
+
+let
+  haumea = import npins.haumea { inherit lib; };
+
+in
+(haumea.load {
+  src = ./.;
+  transformer =
+    name: value:
+    (
+      if name == [ ] then value else (if (builtins.hasAttr "default" value) then value.default else value)
+    );
+  loader = [
+    (haumea.matchers.always haumea.loaders.path)
+  ];
+})
diff --git a/resources/patches/mqttwebui-florapatches-owo.patch b/resources/patches/mqttwebui-florapatches-owo.patch
@@ -0,0 +1,50 @@
+From 4cdecf84adac2671e1506d640d88c5070cbe23ad Mon Sep 17 00:00:00 2001
+From: "Katja Ramona Sophie Kwast (zaphyra)" <git@zaphyra.eu>
+Date: Wed, 20 May 2026 11:33:54 +0200
+Subject: [PATCH] florapatches owo
+
+---
+ src/webui.js | 13 +++----------
+ 1 file changed, 3 insertions(+), 10 deletions(-)
+
+diff --git a/src/webui.js b/src/webui.js
+index 3286982..76938c3 100644
+--- a/src/webui.js
++++ b/src/webui.js
+@@ -334,7 +334,7 @@ const renderPage = (pageConfig) => {
+ 			<nav>
+ 				<header>
+ 					${backButton}
+-					<span>${image} ${pageConfig.title}</span>
++					<span>${image} ${unsafeHTML(pageConfig.title)}</span>
+ 					<button class="icon connectionStatus" @click="${() => window.location.reload(true)}">${unsafeSVG(disconnectedSvg)}</button>
+ 				</header>
+ 			</nav>
+@@ -383,11 +383,8 @@ window.addEventListener('DOMContentLoaded', async (event) => {
+ 	goToPage();
+ 
+ 	if (!config.disableAuth) {
+-		mqttOptions.username = localStorage.getItem('username') ?? prompt("username:");
+-		mqttOptions.password = localStorage.getItem('password') ?? prompt("password:");
+-
+-		localStorage.setItem('username',   mqttOptions.username);
+-		localStorage.setItem('password',   mqttOptions.password);
++		mqttOptions.username = config.username;
++		mqttOptions.password = config.password;
+ 	}
+ 	
+ 	if (navigator.serviceWorker) {
+@@ -427,10 +424,6 @@ window.addEventListener('DOMContentLoaded', async (event) => {
+ 	client.on('error', (error) => {
+ 		if (error.message === 'Connection refused: Not authorized') {
+ 			alert('Authentication failed!');
+-			if (!config.disableAuth) {
+-				localStorage.setItem('username', prompt('username:') ?? localStorage.getItem('username'));
+-				localStorage.setItem('password', prompt('password:') ?? localStorage.getItem('password'));
+-			}
+ 		}
+ 
+ 		location.reload()
+-- 
+2.51.2
+
diff --git a/resources/shaders/background1.frag b/resources/shaders/background1.frag
@@ -0,0 +1,78 @@
+/*** Uses noise / fbm apparatus from Clouds by iq on shadertoy ***/
+
+float pi = 3.14159;
+
+
+mat3 m = mat3( 0.0,  0.8,  0.60,
+              -0.80,  0.36, -0.48,
+              -0.60, -0.48,  0.64 );
+
+float hash( float n )
+{
+    return fract(sin(n)*43758.5453);
+}
+
+float noise( in vec3 x )
+{
+    vec3 p = floor(x);
+    vec3 f = fract(x);
+
+    f = f*f*(3.0-2.0*f);
+    float n = p.x + p.y*57.0 + 113.0*p.z;
+
+    float res = mix(mix(mix( hash(n+  0.0), hash(n+  1.0),f.x),
+                        mix( hash(n+ 57.0), hash(n+ 58.0),f.x),f.y),
+                    mix(mix( hash(n+113.0), hash(n+114.0),f.x),
+                        mix( hash(n+170.0), hash(n+171.0),f.x),f.y),f.z);
+    return 1.0 - sqrt(res);
+}
+
+float fbm( vec3 p )
+{
+    float f;
+    f  = 0.5000*noise( p ); p = m*p*2.02;
+    f += 0.2500*noise( p ); p = m*p*2.0130;
+    f += 0.1250*noise( p ); p = m*p*2.0370;
+	// enable smallest component for more wiggliness
+	// f += 0.0625*noise( p );
+	// f /= 0.9375
+    f /= 0.875;
+    return f;
+}
+
+float nat(in vec2 q, in float z, in float mx) {
+   return mx * noise(vec3(q, z)) + (1.0 - mx) * fbm(vec3(q, z));
+}
+
+void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
+  vec2 mouse = vec2(1.0 - iMouse.x / iResolution.x, 1.0 - iMouse.y / iResolution.y);
+  vec3 col = vec3 (0., 0., 0.);
+  
+  float s = 1.0 / ((1.0 + mouse.y) * 45.0);
+  float fbmz = 1.0 + 0.01 * iTime;
+  vec2 p = (fragCoord.xy * s);
+
+  float as = (4.0 * (1.0 - mouse.y) + (3.0 * (1.0 - mouse.x))) + 7.5;
+  float pdir = nat(p, fbmz, mouse.x) * 2.0 * pi * as;
+  float d = as * 1.0 / length(iResolution);
+  vec2 dp = vec2(d*sin(pdir), d * cos(pdir));
+  vec2 q = p + dp;
+  vec2 q2 = p - dp;
+  float qdir = nat(q, fbmz, mouse.x) * 2.0 * pi * as;
+  pdir = nat(q2, fbmz, mouse.x) * 2.0 * pi * as;
+  vec2 c = (q2 + q) / 2.0;
+  float pql = length(q - q2);
+
+  float mdir = (pdir + qdir)/2.0;
+  float ddir = mod((qdir - pdir)/2.0, pi * 2.0);
+  float tdd = tan(ddir);
+  vec2 co = vec2(pql * sin(mdir) / tdd, pql * cos(mdir) / tdd);
+  float ro = length(q2 - c + co);
+
+  float rf = .25 + (0.2 * (1.0 - mouse.x));
+  if (ro < rf) {
+    col += normalize(vec3(cos(ro/ddir),  2.5 - sin((ro/rf) * pi/2.0), 0.9 - ro/rf/ddir));
+    col *= (1.0 - (ro/rf));
+  } 
+  fragColor = vec4(col, 1.0);
+}+
\ No newline at end of file
diff --git a/secrets/cautus.yaml b/secrets/cautus.yaml
@@ -0,0 +1,67 @@
+acmeTSIGKey: ENC[AES256_GCM,data:YUFV6XU3Uw6homOoxy/gDYgnT/coLzA7n6POEainl/WaoiBN5u6T1HKTYo8r0df057e71G9noe2tH6IGBzO4aw==,iv:1BYfNw5U5aXu397eJ32zFpNnHiP5uayc2Py4SDcMmOk=,tag:xoAZbUYRHR/mBp3gzHvLWA==,type:str]
+wgPrivateKey: ENC[AES256_GCM,data:cyEd54xKsPxKTebdtKDJKWlByGY8+30NrjsnvcoAjZbsYbymtdwM1mGE1wc=,iv:1jzfXezrW9WDZGujvtAL4KGT6Mk/W7sLfk6Dq+oUumY=,tag:XKBj35fHvtqbksjkXbhtNQ==,type:str]
+knotKeys: ENC[AES256_GCM,data:emLfdkZnykMmwPNlWbohMmcqpePtgU6Nynj9Am+LZbVaRRM9WWDMEtWoMFdTiAQpVpethgcPbzHQocruB8ZfRAOqCG1dVuvlh+AfWKwK9VVxWMHDDAmCmxAGsLPUaeY1C54RzT2GzJyJNRFitFKzfg9NcgMsBwaXllg4TnSQeCT5ipecmDcyT73jjYZOfjPCphVJ0jn3ELRsLQlHYxmn/amaKWr25xpWHv33tu2uzyt4ZP2jfLAuc4HaTY5WhGLPjOUUPsi/TfU0K9iN+HSIeuezIF2NENRlpGSO44LZ63pW/aQo7OyCePFdhgFtqPiAASKDlA7UpygpcOHS7sq8S2eBYTIZ1psyMxADOtWrx3ku9YEcDWZtq1odipfGdpk566RUXJ1PyKHucXynGxQfTzRXv+aj5uhr+hSmyLhhBupUU55JS78UN+EzgP8VyGEDd/+HKCA/rfr7XHXZDD5xENIJp+DETvA7zoF+U1AKX97ZDRJ3VCK5pq27YBZ4r9qOqb8+LPadHJ/BVZU2yMvH8AZh+0We5GQ7iFS/C4c77f+Rng27dBuheAEkabQjiSZTnO1s+bRU+nhgxvsXlvDjpHfeZRnq56hjfnNzd9OKki9V4pY1SorOXIWtD42X9NwizbX22zcowKof1li0AKLBrP9oGfZO7vEQxCx/Kp8Ln1Zugh3hvWYgZp7Bcg4We6gzRc38alUyvqsoKf/iXA+eGACcideDGtXyi/Moreupwnr/4mhiARro80OhlE0wEqrJj694wC4dcRC4rtPU8RS09sa+GGSW8t25WiS3Y9xLHyZ+fjE5CNnHh1FZzSelWYTDxHf9kdFftLc6yNjNZjwnwLjbTp7dlT3bFuZCwEeb6ctiie8nvh61KID08B1IrOy3+55TvJqWjzpi+5ARPBFvCMnzpnkEsOPSc8e7HYMTHrGTfVBfhYyEC0uY6yq1YdYUcxaBLrF6zzpB2OD+XA27FuL8YSJHOpDj5jF+Zc4iwptY8ybGnDLBT/NDrzWSk1oxq/u79Q==,iv:zag9eUOJFjI7tZl2HimsEa9V7649xUOI2w/Oh2lNfZY=,tag:ipGKyJwgUgzBXMJSbR4vYA==,type:str]
+radicaleUsers: ENC[AES256_GCM,data:HbF45OTsp6AQCKi5DkOkU66y47unaE9EqW1MdbZPb9I5Jk4hk/V3ARIBdXFM4o5FUDgcwVs2sDtDRMCynI5LTLwXfOBxFpYPzziOTN1LX78A1v3nuzgbcJNT5Ycl5sx5+NVgaCwxE/H1mgvFkLHNwiXof5jI4V6mT1dWdKeQDLKeJjmpanv7+x4//rD3JxfS/mS/zYphN4ewbIc5DDHgfK9cW/EqFneQwhXV5lsPyOiQTr0Vq/c3GYBcW6HTpWQYhHy+hagvOcAwQo3bosk=,iv:FSKQz1ZQHKOoEX0NnhHwez8RBZri2Wo00ii7Ps8o3GY=,tag:XGMlfhH4e46oZGg9qM4oPA==,type:str]
+mailPasswords:
+    katja@zaphyra.eu: ENC[AES256_GCM,data:Z73DRPcdrk/XINsZ7w+F2RuXIZ6NvBKtzeWk93pwSAvJu5SOkCFKEYpp6wM+PRYOHXRyW0nk6X3gb3ux,iv:gGiZW4eZhVsiun7wW/d1g6T+gKGVWGW1GG1g6hkt1WA=,tag:eMzsqXwesLDAEGo21FC6Eg==,type:str]
+    gts@zaphyra.eu: ENC[AES256_GCM,data:y7aHceJr8+joFhQSuTlU9KtfH3Xw6VooSpnuSve5TLP0UPXW5Z9NN7lALXfHd8b42JTuEL54XBWP+ryd,iv:3S78h/QH8Qve8zJBdQqmgALatLISEx7p9Qm+t3mSL4U=,tag:1mDc5pvVc90I/v7SPowynA==,type:str]
+    vaultwarden@zaphyra.eu: ENC[AES256_GCM,data:skwSweCy4NgK/r+4qx0JsVXdPm8ZvA1Gu+wluusxlUZOMs+YGLKj83fxt3ZwZ9owqPMV9zsSiDfgS0ax,iv:DRqdmk4ZQibGCwsc/0h4F/x+RPqRxExGiT+ll+YopsE=,tag:wk7daIn5W1iSYLhmXV7avg==,type:str]
+environments:
+    vaultwarden: ENC[AES256_GCM,data:SmSotLiEvFuAKaddbsGlkEDMJ+ru+QH+7L3er/DBS+IvUyUMXd9BcULkXN6nMw7eW258S3MaUQq+AMOVTg+ofD5IMOs1KiH9sYoWU/Z1nxTJ40MDK0kGDrL+fZQduoAsAf4RgQGAIT+kqINzn28/kxmMJtgdCbQvaCsFKt4gDtVb+ztUmFhI6jdw/X18bx1AK9zvOJ9qt1XbV/m8HEclsNzWj6ZmZ2s2G1A/cYTQBJsJd9tNxBim5Mgi6G3AZAsGBFkd4wIuLl4dbIfo9jq6DKxcLT9Jmm5YMupXbdsarvHoKrpxHhL741TcQkNNncbuSKo21rgLZQkqB8ont8Jib1+G5cgnZq6Wz2plZMD+H9KKsdKV,iv:ELSX/EAHwsvLcqSy/pLAqFKJO+EypIBGNNVBthtNOZw=,tag:hESMXdqXMhCkEfHEUxB1CQ==,type:str]
+    gotosocial: ENC[AES256_GCM,data:x4uEb0wOvvF166TB6Q91m+4mDrOO2fXAuWj1FlInOfmEY1Z7Jdk/JxELUhifAm3BkOZi2Hw=,iv:3okHmE6kAsCoFWNcXwpLgbFvRz9ApV+cUcb0sDzHSyY=,tag:UNKEABN3UQKnRJMwM/GbDw==,type:str]
+    mautrixBridges:
+        signal: ENC[AES256_GCM,data:j1RtojUJauGsMVUbJuMqEL8QFRxiF93OAszAPHqtF9+KFlo1FrEsuKyNXDqVNAgjcl0MiAvyI8kkJ1d/VD+Z5H6vXoPwO2Ykj8aDJskGacBxgWgXSJohDiIonNKuacVugBoihGjsB4C1MdNLd4rxImxWTeq6o9mVVboEuyYMv5slv56hqD0NzWv/Bl0vpNYjoOecsle4gFkk2r8bg/dpmm8KQjwPCOAcREswf5wsZHkHrCLCL+j5a483qQQ/gi0IR2HTLyORXA+8DCX9yDyLkNPfbL1YXs8MXi+NzFjBf52E3v3F54/kxSg4uuThrFPuEW3Zi8+sJaaXR+/mCek0NPR278prXdUZcPiayge/wrr7rzqghv48SKQ/z+rIsaNy9krCgRNDvHHR2my3o48fNVZy/QdjqsKwtX0obiTmkjCzQluJrP8V//KH/FWjNijK+d4lnTxzsBLYTmvC/uPL0e7fEPNNscikbr4L4K2kXtbWhxFNiqPKRuk+315hFxsw+0IOEvLocqdg2sQCTfx53ujvKPqviICgKJ1ddLCm7rtV,iv:MsFvmu/YT6AukMbLmQBYM9bB3CGYoxOcUAMeVqPtf3Q=,tag:kwWg5CZej48GJ+UaqJLvKQ==,type:str]
+        whatsapp: ENC[AES256_GCM,data:RjlY5duOUwzm0qBZRfQ6yas6NNPGUVwKqj+27rYGhz9xRSmK6KDAYysmaNttUgM/V1bXn8yAs3wxKMnyaC/Nr4kdx5oNqSBCsKAtWpQipJkWPE229VnQ1GJc9plmHaRd5NONp9bV1S2npzuMUxG53rcA9nzdjygHVeYkeaOH5xmoiIikJU7pZV8hfZ25G2FA7xU3IyVtUrqEUr1jSJhyXq/FGskDRd2T5RaMTGN/ymOTWtaOD6almBlHIz7Nz9e38+OkdkHK8pj9OCwtcENzUWSNrmlztlLGHYpDOOtGJrjQjVCYyb121v0sP7sWMVpuB7N4cWp0RENgkP/oZvPG0Wgnwc25uJW7nJAjIMQulvetuVqeDsrDH1upjGZMzXhc+VSFdCb0h8mck3w2JDHSwRUntbjKbwqY9qUGu2f2MVw3LQxoAoTZiEdx22AwO4oGHM4BWTQzEqq3i4PgeWg9LjRMNRYTN3M0g0FzKFg10oleN7Byfxifm9OqeRR/pq61DpyyhVjg5DBCBlFa/6I5rde/uzMGTImF04TxdwEPjmwO,iv:jF2hvwVdyevUU6jr4j73FKP1unSk+Q3BT3mHg7XG5ws=,tag:5emCyANfl+q8Ei2qspgdDA==,type:str]
+        telegram: ENC[AES256_GCM,data:KheGPAOJYfaaD0dGg/R1aJQHVE6GesmiCTWTN+aZvOBmkAm2cusYLguOFGfmKFa8I8yOBxaTu2/7HYAabJOHORlSw7XbTna67aWRmo3pskLz/3Bb3ugTpEtpNeCL90ZdR9IVSIihW1VTVp7UKZ35yVuNSV5Jfs1uuFGsiPKp5cJ8Kftz3bhDOgYPCyqojqkqjuXoELn/sL1NnxV0Ctj2TFgupQlk/u/gAkT05Zj0YEU97JzLyjAOypOR2J5ZtcEDZkcrOPr/Z9cfh52lVL6JaIkZAoDLpxqp8RL9XvKFS0pnzDhAZIqe1hrx+5BLRpjx2wPcLPSlN3yUyqTnv/F+QXJQi21VGWaCFl+laHuepLaDVVoBwVA5bEjynU1/aFJbYQsA9/jaI4Xo1s/VzvcRsEtXNApC2Nz5TYXeOvnTJIKKz6fhBB1ULpLklXK+9m16QlajH3B1F0ndhXz9O4vOFUXF4IZWBbwlDVAiQzcgVge7hoem00gzkLXeLGkGBq1GysX+Kpz462/YRT3Qa+MhstSVcJSdPNYM7HM4QPHfViaIZHEY0ArTAETJad8hZRs/OXyq5mxcD2KMBrVlOQHYENr015xi4zaXYQlr2lcG+fQX7vBfw8EmnyImI35ZLbobS1u6pIhE4yUE2HMsERBy,iv:6fCTzfCnpjNcGNv/9b1KwIoGxv958mU5Xdd4ZOv8ifc=,tag:OytLQfY1RXz0zwlz+Vd1QA==,type:str]
+restic:
+    memos:
+        repositoryPassword: ENC[AES256_GCM,data:5a8lHWztE/QJoSu+EDT7tTVvuG2FoDJZYspKatPSsNiIlDXaYs7Hn6Kd3v9Tmpnn/A4FoKNlUvkhdqJmy+KAhQ==,iv:Vuxzv37simZtd48yrO32f8Kk4ddEzeS3yLzfmd0cjTw=,tag:b5XNowySIt4vhokuBQG3kw==,type:str]
+        sshPrivateKey: ENC[AES256_GCM,data:K+ikDKGEOua1kO5iDS18WMSxGwgH3yW+VxyEJLnEivlUZdVVIsD6zGPqYwixaqgMjRifTFMVLzng0gOWlXMJJF8PdWHV+W0IMPXW99DQCwz6quMmGv3p9wjOOWDEiNpsaA3AhsHwgWE2OZnWiB20QEJiP6+cyvmoe8rft8oJp1/OTxF5t9zyWr0IIpgzV+CocDI90yk7nzCzPcSFEc5RS9H8Z3YbfxAOmhXn3I4y1D8PA1EbOjjx6ZhuhqvWdHcQZg8aGkTl+v5KQOGTF0UvMvrq9jar7XsRKmAz0mdT7JRA30/o6QDT13kOHNTW0FbTyVsmApmMHimxycEvPsNHXr+iaD8ZPArEfhAc+j4DWaFinLpwvR+BGeNZkAxQ0OvE0HhVKI9n0J6uCoDu9CEPBOCE5PEZsA3hyuqhgA09clagoIm353JEVinhRX4/mHFo534JgW8W3X9hFPGqBnWyFpOw61Xwk3hJ6JIYfkXo+qooolRXD2dFgpgPh9Kc7k5KSlPJ,iv:VSJdUlylc0riTl7KKzfFZUj0UQUG8FBfvXFTHSQEYvE=,tag:glVT0wlbF2FBGXl3AKUb/Q==,type:str]
+    continuwuity:
+        repositoryPassword: ENC[AES256_GCM,data:CEI/UhOidO2woYhdmhaTALU9vzya09Y6NH8ay7/+VSRTdTOkKeoA6nvSZ4+BApGrRh7wmZ1/o2A657JAgQKaiQ==,iv:vzlp+q+qT/lFVmGq6h3HaXlk6GneWCPKQ7kMfOLFlNg=,tag:hLsc/7Mv5H43lsSXL3wFlQ==,type:str]
+        sshPrivateKey: ENC[AES256_GCM,data:gf+HMn8+uuhqP1yrTtFzQ7bcWiPk1scIILRkdk1q7QJt7z8U9L/MwK1gvTkahH8gBG+2cbsyX4Ya1sY+AZud647+/TsYnkkvH5pDFp+DPfg9MkgmWhUszG1ZywMwuJWmUg3PbDz8dX76gQFRllboYTYHmY8QJf++bhyEdixzghRHBl7XOxdbWWOdDrNvY8Lqz3mCwbfVAbfqkkKwaQISxySv5zj0WVEbT/L59tTCxeRL6a7S3GB1szBKivcxUZZlVovm4Lx8NNP0ZT+fHBGOccUe4FVh9AqvVua4RQxy+fJ+js7aJXmQg6I0cZpR1iYPxDBskZkocNznF/qFBBjloAuf0JenGhu7Es1X0TMRL/35ZQy4jmtNLQWlLQDjlnA9mxLeH5nhAC7Qte5V/Tw907RZb1+tGAj0haiC/xn5Lqz8KUq1Dc/7DgUidvbhQ8w1f47rp8ImaoKvR2yFbvXnuJFVWZCpNpLt3TgJ1Nz1CmWgTIIzpj9j4AP7NF5PNblqOlyU,iv:/F2NicUt02A9TQBavthp0sskvGJFND+TmoDX78FPvtg=,tag:OW/uBHgcaFeVAXKBiKykmg==,type:str]
+    radicale:
+        repositoryPassword: ENC[AES256_GCM,data:aBfXkH7/BzFANMaz3pRrc8ei8k2sVBLRSZcBXpQg/5sbGtC8nXSlo3LPuhxeUD1mO3Ez37n4GnCsaPIMZf5guA==,iv:/5sFfRT4sCwebeP6BLbHhecuXAZfa+snQH+a8KAI43o=,tag:ASoEUEONm+k8byGv+UK3og==,type:str]
+        sshPrivateKey: ENC[AES256_GCM,data:SRjmTTQ406g6dMKWIVRKg4fpR8axgIJJc0Fft4OhKMNtA/XoQUK/ucRr+wG1qRWP3Vj7M8aEAPlQcEk8LUSxLf0O+MHM+5Dd78tjFdxsFVH5MuMVIFTyPToGXELJZmjnRjzbrknt2ZKVgEACyGB9zRo1kd8m1eV7Fx5oE8t02KQQjAQ3tbF7eUSD9Qq/uomOqpXi8wF0tIagnx/RLyUVsAydW3YN5A/5/XPFApwbTcUQmZLhgBJjKwvArx1wT1X7PWSJ+cDOJ9ERX0xtjpUeJ5B9J2uCFcWcP+Biz7v0xZlHX/T/+Do5k8UTVcv4pT7Tan5bM9h6QCLSeqsmuAe+KjXNHUVyoMELK0C6PSz41jUo/1WxhP6XbApICNli9LUIKA/u1F82hnYAEsfAu09xReQAjq8/sGCX3mwnt8OL2DL1+vcsjyTL4jOIA9ljc5ajphtl8bFDJTzSvMZbJkHaNzpXrAzl5vNqzGQPhpyVKA7Iz4cwGa45EmhEo2zEXA5fGIkI22MWNqeIbAwcaLy+,iv:GhauOD22NaQGZ0P5iH/wFX2StDcUxTiJnuXe4S90FRY=,tag:1WoTcwHP+6c85XtGWFzlVg==,type:str]
+    prosody:
+        repositoryPassword: ENC[AES256_GCM,data:KH8+bvVpCJ5DovQcDzkZNFg4tivPqk0cqrSxyAwp/Hef7KGST3PEbUpKOVDlM+cWXud0HnmEUDW+scIjcBqQRQ==,iv:a0LcEn5k58IvOpX5tvbhdVMDR0USL9s0jLRucQ/r9OY=,tag:iKc4MkCJmV7H8+2Uz87EAw==,type:str]
+        sshPrivateKey: ENC[AES256_GCM,data:aOEhQJ+zrMWHPxiqPuksjR1R0/uM+rdl7JB+lgpH/3W/6SMAKC0yqMOgcThqTnARsqDiC6+bsH1ZaxDctlcrE5okt8nzLz3XuE7NrF2oZOughsAUF29cYtlAjYSQdGYDt0dxSCvozDn3rpnilaOBHpCOKRSrHbt/s8E7bJ49eEmyyJ/axUAnl3l1keACacHlLK8K+pQUZS3/yxHwFfhzLw/r6X+ykrO2tI8Cg1hhwJgq71Jy9LGCgY+k2tGNl6UH0wKqJL/M5SSDFNUA9Tfz+VXyDdAhal0sHeQbfQrYO0nu5CrNlMSfyMho6AifAqeX+KPQ4TSRN1qhX2zJaNdvMh496+iY75sbxo+m3jLLa88hbKEgaDm74svbJtpafgkak9Y+qyogJXQFvEcWaKBxXkeilPrxkE2b5meqjfG9mXKS2bIgl/u6+2JL0gJxJAgF1NfqSXUG6klYJxNQ4Hh9dsdbcRW6yIgyliBW0WVVcxoo/nxa8spwi0WyaGJdWKwZJxLOvA5Te8ArR3FecV4i,iv:XokatEWgo6SaimnLLKZkBZrBhGs5B6bkXnRGtkbKW9Y=,tag:B8J6QCXkUpcNKJORSCzr+w==,type:str]
+    vaultwarden:
+        repositoryPassword: ENC[AES256_GCM,data:YwPxvt5svhhfc6CQo7XJiFUEqRnBZABWEDBkGnT3MDvkZFIG5DlGUh6MbKYSmfmbYDulek9BNtvuwOn5FkbjuA==,iv:ftServQF3pQwDD3LsYggophCQH9f8xQsuVuLVbgjDTk=,tag:RiMeDvxdQHaL3Vr6FSxBgw==,type:str]
+        sshPrivateKey: ENC[AES256_GCM,data:KriiyX5+HotPEvhDKS0Eyfla0BCOdZZGurk79Y6tuVum0E1RD4UvX1oqSoUGdIO7UYGoz1hkMwKyRyrx/FmirqQXHwWYCThkuufGTcS7vCFB/GlEIuZ6rnqeKlFREZ5wnUccUITS9U2L/hX9IoaOixo3mlIIXTQE96qNWqwlT4MJe0IPQWEwE8fesMJd1kD77CfYvf1yPDplZFawNqIZAWrENmc47M93s9GRbr3oZs0Md+JnYOjEphqFLHaa4QkZq3Au2YvYjV665Ln55mDtXR0i/Rw+mRXHSXd4CIC7SNuSQAnV9iFb8dYRLwtsVCf8m/aSDgi6b/eCz5JjS1oBKCwq2fjfNRAhcnWkUFa4f91gPcteDj5t/QNlIDYwAtkEODtBylZ8mQerOv8dAOjYCuHgpSDAS1Wo8t2AxFcilVOOwMwG6T8vsJdZZcYmvdZV/vniIOjnv2dE8ZObrdRkYS6eQZMhfTUtLPOB8G/cDzm5wmqmrsrt29pJ5/DEjPORG+pDTlh5lqihMCdExJzE,iv:PpdROtF8Dr9aChJ7srXRI1iaeMMJilis/d+ksbTfcmk=,tag:5FQgU1nvoXwekHtUZwYWfA==,type:str]
+    gitolite:
+        repositoryPassword: ENC[AES256_GCM,data:2+tOHmVa6QsdWsj2+zSaBvav+aLlFooWOpO6Yor0/t2v9c+/RJ9l42RtTB3beMNyVjvEHi8fk8yhhuNk9M8QTA==,iv:wuX9+V7EewRyPI5pQE3fC22rlP2zNzTloeuv/G6CpBs=,tag:6GDTzSfPY12vvb1JALF34w==,type:str]
+        sshPrivateKey: ENC[AES256_GCM,data:+kOIrvezJXYhNdiv3szxi+m5QIkB2wUpRO8Q4JYNjV+6OQsVWwLzSGbv1QGcByczIHFzRMwxUBBCheJERzpcpOL9wtfa+GnmXmdpsvqF4TQEy3SlHNcekLKua2p03ScuyOHJPIZK3HKtrNoWwRicqjjYkNni7aHgFfamklhpWkIF5D94MBn8p/nTiJs5hNGeOWbrvu2Mu0wVkada7PBRNS5lgGE2eb5qVmFFDteZQU4AzRDLdhDTCtOyv/W20Fg/8Mh9aTVeyUXcqqbGTml6xpy66Er8WsgoAmKBj35pYDhcnjoZicoDXtqcsk13swHtU4ybbqKQ72/i4BO89+TkMALmJw33wCRDD2kD9DUM/69+osfgdBQl6RArEq6X2AySVa8RguMA9lawV+vrhcm+UOH0/z/urWZ2P46iJocF688lHoBr0D+aeMIkodr3MbjJE6CSTMf4R/wEJdKqhRn4zNOY/ogRd7EEbF8XfT8+aDc09F43H2rehJBknrkq7d/4mh0Dey5FnA6L9gtfqlmJ,iv:OeVbjl4O8aLSy4gkhabcWAWjHpt2HmTQw/VnTs9DZqg=,tag:wIYFrCkr2lnLscP793Yxmw==,type:str]
+    gotosocial:
+        repositoryPassword: ENC[AES256_GCM,data:koDQEnK6L266YQJ8aqJlgosljmO8/QN3Bn5HyGDcB59BLr2qCsqMmIsBA4eMihKlb9FH3sn4zTJrtC7ncZiTAA==,iv:bPYv1AYjj2t+g8NSiuSmJfBI6e6L2smbxykEQZtnIOI=,tag:3rdAniOVvWUqCD7+Lrq/ww==,type:str]
+        sshPrivateKey: ENC[AES256_GCM,data:5GxdmhaST2D1CBzdcr6Xyvi/BJDTDiTAN2s71ErHg4WPNzGjr3kzunQ9RN/V+lS6m/KNYsOIrWtxhgQh4gj8u3UmP4fyU52PJ+Uu30zNFRTHsMhaZgOqAYIFD64a3sNZu/iM1KDHsTgnGkuT134ehYzkemEe++77ECqetep8teC2q087HT2Q8GzcdU+VL/Crqgk68cr7qiaDBA1IxPu0cTuVBcRKZPdK45bapYIl+ms/0aGMAoVjozfF+Za1DCsu8Rnyjo+DrTijQBFZapLQg2ApjTvGXTWcN9gWpxH26ebL9AmKjn2m4sdMFRGM6bsDv82RlHfI/P3FFx9ma2lDrgN75XterYMTAvsjg32KBYMrQa46Zn6mUnmxtwLiTCl26jmjq4V3Xm7WlSntwHi1qakmXSP4028YtYNYO+lUfozG9fHNpD7lEzjXBFNeGUmRKgCByd26e9ug9HkATEJhAnxYf3m/haQh2t3txmjriqQSHC/l9e8Ob87ZlXpbzOCq0kIqMsRv8dNVxWMWSAo=,iv:M8Tzhet/9ofccRJGHPLnSjY6ZSCUhBoFzGXsgH/K6rk=,tag:2nN4WvlTjW1unrmG1Ql1zg==,type:str]
+    mailserver:
+        repositoryPassword: ENC[AES256_GCM,data:zFLJ2zPha+EbnWz5cBMFqQH1rTZq/kwAA7XGK+c4s6ubdPfB8NTmRtvN1giSK6IwnkDBeeF/yi3CiQXlCFXnwg==,iv:sTXfDaPk+1S0IF5VG3oSdfYmtikvNggqF0z4zvFWV1s=,tag:W5QRnjfkxZLWOAiBl5LZKA==,type:str]
+        sshPrivateKey: ENC[AES256_GCM,data:oEVwgsHWiQqHPwAZwyTE/sgUaRxLye6SHL4jwQSsEO9CKl0ulEi1A4D1gmZhTDsPryysplKWT5Vo20LIO5UTqJ/fY9vLPkqKsAlNLQ7P/AFqRnyvbcvdwOyTczBgf8hdSoqw4OxIDHwx1gZNJ5QeGORz+iN/rzXxA8NmXxnCqcUvHVsx+P+sDdiWau28o8xZ+RrFnwfaVkQ+HW1USebweau/blNvTND9CnTS64f5KIvcpzrxq/iLrzcoI+OoXF/L38FVzN8FidXTQRsQZ7nSkjA/jafUIg/dGJMbjuBPFKoj5Tb2SdMLZbFgrfNX/URNj+fM2V6zmjHhD5UwUZZVRuK+20uFFfBuc/5RYq+h4Wlr/7GAKujxc45lD41Dl1E6yiKEXf+Np2xlWCIx8vMgKPGvSWx/m+BxueIdDsMO9HvYbIX/SV2r1y+9egg7r/zgvzPTmufJJu/pjUo1fKaFeJ26p4YZNf5H0gRRsgNeJQ2bdVyVc6DNxbmcJcWqBLmhqtgnLJ2MYsKeqfcAgpo=,iv:wNDzZzUxo7RKOeuD8QQ2qPFDVXGOmJ1jTOBJscEPayI=,tag:6P6D0Z8pQdKa6rB1zCTkJg==,type:str]
+sops:
+    age:
+        - recipient: age1yln5qratl82u6sqakh9gn2c0s88nzm62cw9muq5z4kzgp9wqpyzshhptq9
+          enc: |
+            -----BEGIN AGE ENCRYPTED FILE-----
+            YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB2ZGZuanFjekpVQ2s4bUNP
+            aXVyVmJGRXdxUzBPUGVoWE4valNYZFEwbjBrCmJKczI2TkVhb0VCclRSYTR0TGFP
+            QlIzbXF1NWdjaHRxbkxQdnVNaDZ0Y0UKLS0tIDBnUnB6Smc5eVFTbUVrVGQrcGcv
+            dDRUNkxscGdMa09JV3BSam42VkUxTEkKe6TtN8smT8dQ6Rg0JqNDXAeVXfooaVBv
+            qlBEsTO7KAJNvHL4cGUob/a3oKhOqHNMc8Tx65y4+1Pnl4oE4qQn5g==
+            -----END AGE ENCRYPTED FILE-----
+    lastmodified: "2026-05-05T13:43:05Z"
+    mac: ENC[AES256_GCM,data:uEKoFAdps4vMsxUPWyvisC4EQxdS6FDvai95PyGvrcFzkjMOP6VA6jZ4klrwwMT3AEY3kTyeQA1h5sCZsvMeDfE1Jtd8deR9/59iR8EwX+ro98NqQyPW6BgLdQQKY/hpXqQMuUj33IZ7LOD5/1vUCSKp8557drMXTHLeQ4SscYE=,iv:cdJBn+mVhilA7c7xoqEItdmyyRmIhpvph9fWpoZfNA0=,tag:4mw6TdYW9LmW+wH+vaggoA==,type:str]
+    pgp:
+        - created_at: "2026-02-21T16:41:10Z"
+          enc: |-
+            -----BEGIN PGP MESSAGE-----
+
+            hF4DfdBfTP3jZzQSAQdAOt/SXhsmCct4DMVVeHSdh3dyCuolIb2x8G/vG4/Wckow
+            QTURCDjjqHZlf69zlKtzF3QLr+MqQ1GCLR0i5QyDOg8s3jafYac9s0BNkO1Ia3+x
+            0l4BcCGXxF5kdS7hlSasezrjEGRlgyMir+8wLyIIu+rZh+7tgxyH4A6EGYErObQy
+            Idt9vyI900uktntFLCu0RuXyChII1RG8aydWKADgFs369StEL22u3bDs9xkbHL+k
+            =w2fZ
+            -----END PGP MESSAGE-----
+          fp: BFE6386C8D66BCD4DAE14FC895F0FE7CD7E6A022
+    unencrypted_suffix: _unencrypted
+    version: 3.12.1
diff --git a/secrets/clevai.yaml b/secrets/clevai.yaml
@@ -0,0 +1,29 @@
+wgPrivateKey: ENC[AES256_GCM,data:tiT8sQEMH64xdfEZUuX/2n5dnnCsfh+905CbDkkvXhmv4wKS7I2fEKHP3bQ=,iv:x/gCId+VCn/EGvlOv4GWMq3y1UbtBZexgTDec/7Ii3c=,tag:+BSfqAiDTluAcXmhmSbVBg==,type:str]
+acmeTSIGKey: ENC[AES256_GCM,data:TtWvxIQrs5S9Xy9tWVBstwFgtMoqYX4CU134dxJ8dS7HH/qDNPlPZu052rjTerCSQ5dnUaTKx5wFRxFkZd7y+g==,iv:l8GaGgg2flBW+toGLfgCt+JeemS3I5v9eFCOJpfRYvI=,tag:GLLwm/GABJ5u8Ni56MPF1A==,type:str]
+sops:
+    age:
+        - recipient: age1q58tmhk5hxrfn86qymet3mznafdzuhwl47s8mt3nyn43pararc5sng7kag
+          enc: |
+            -----BEGIN AGE ENCRYPTED FILE-----
+            YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBXbXAyWm5PV0ErK1RjYTdV
+            WXU4YWMrZWNXRjZDNmNNbkRFSU9TTFJRSGtrCjdSSDlrZ3Z2Mkx0T0tYdTFUSDZ1
+            V25RS1R5MmVZbk9RRk03RmxocXZTR2cKLS0tIFhvenZ0cjlGYkw1K28rOFE3a29k
+            a05oalNCV0IzNWd4NHJPelVCODhrajgKomqxILIfKYLgwuNQhBGX5bxb4Iqvw6B8
+            yiqMxHyPCxPJra1epCqVWlPEhf4VgUnrDCtn3YEyrZmWMm5tWY9Pxw==
+            -----END AGE ENCRYPTED FILE-----
+    lastmodified: "2026-03-04T21:21:00Z"
+    mac: ENC[AES256_GCM,data:ettbV3coxarLZQK/mOY5RVvIifmjS+PgQrkuT8CggEoajmUVi96q1awfMSC+zThPAOdj/Q9XjxTGpquiScJj46rr6dWLCjzIfYdBVbaBgYoyQXHMYt0/4NGOgcCj0wQVYjmAQ5ZEf5kYvhlvlx0dZFsZUYorxUWJPn3Z6POY59g=,iv:AQl1lj2jWr7R6aVKgo8cQyNxU2vIr7qNNoSxpRN9I88=,tag:8q1E8GbgepFjUCLN2WpBiA==,type:str]
+    pgp:
+        - created_at: "2026-03-04T22:03:42Z"
+          enc: |-
+            -----BEGIN PGP MESSAGE-----
+
+            hF4DfdBfTP3jZzQSAQdATuw+gnT2NdgMxMJxBYJvLVVtGAtk5xNkXLDFGDJ8Qyow
+            yIV9Y6id+EwJNI4uQok9LznDJ3TVjVKOaXkGZxb9sn2oaZsQGX/oUUOWcPVVi2Io
+            0l4B1rIZry6jlJeT0u6Bjf4vqYffwXrmygv9d5fN12JHOFVL220pJQV3mxvrTrpp
+            ty1IaqzO63Qgqbs7SpCkVEVKg1VDnKuvFJ463EzOJ7XLA2TPwnGwOk9Ixy8k6I4T
+            =U6G8
+            -----END PGP MESSAGE-----
+          fp: BFE6386C8D66BCD4DAE14FC895F0FE7CD7E6A022
+    unencrypted_suffix: _unencrypted
+    version: 3.11.0
diff --git a/secrets/common.yaml b/secrets/common.yaml
@@ -0,0 +1,80 @@
+zaphyraPassword: ENC[AES256_GCM,data:PejTikjFR8ilcVEJS9JI5Fh2I5d6qcxgxNg6BmgoDRhBqkEllF8LG96nnh8gpAlrumy2ntsU+q/sVrzE4Q/hpoYVRKIRt4i7Qw==,iv:zc5XnVy9fTfFNdnfiFwM60mz7RDh/3Y3iB22AzwHrZQ=,tag:KrNlWECBrUZ2A731vXCsCg==,type:str]
+environments:
+    networkManagerProfiles:
+        voidPhone: ENC[AES256_GCM,data:7C9QJVQRSrw+P2jCJit3oT+gPxHW9pHx0XFj4uXuAOdR7Xk1cBzOJNiEGaXagycecw9OE+6A+pMuWw==,iv:UHNcKDeVPM3/K2cLEUa7X96dZ+10vTAjXDyNlDRH+8A=,tag:vMyw8f3bYogrgnewZj07ew==,type:str]
+        voidHome: ENC[AES256_GCM,data:kxQ7Bn0dULnFQnSR5PozRUK4GUhw3y9ienTyFNwcW1rlZ0d4UpkRRvpG2XehMrEfRaxds0RVboQ8Li3z9Q==,iv:gbB6R0X8Xr+e9/dvSAXuQIIHX3mF9j3H63A+AXXq0hY=,tag:kY/Gs5VSWtwBH2VjDDNYwg==,type:str]
+        zaphyraPhone: ENC[AES256_GCM,data:WxGXvbz20SImLfNTUOpBcWtDq9Ia6bqEQFqkijpNYxgyiv1cloZAcy4ag7n4HOE1vVxIBDNlvCtAzHTWAnzJzw==,iv:pA1HYcOD58ivIbRp/aoiuG6ZK5ZSbxm9F8d0tCIQf/A=,tag:mqpYSMiM55HErXItBrQ59Q==,type:str]
+        zaphyraHome: ENC[AES256_GCM,data:xMwLPSh78GIuiVh+4z84QgVQKOU7QW7VGjPJZpBwdZhvgXVQt5cMOmN4DD/Yev+Bme/2C7SigEY8bFJcgpHnbZ6Fp1E=,iv:fJtciKFZ2J3j32xF2L3XHS0cVBhPlOmpUzP4q0A3WHg=,tag:wd2XA4Q5p9TYjp9kfWDjAQ==,type:str]
+        grogHome: ENC[AES256_GCM,data:ASt5/wqH9c1yq6Ig6PAFEHwPEKCsigIDc+svk+H00jwdaFHqtivk2CzZKPfGe5U/keAJIQ0YsEFkuh/637LUCTz0U31MP7glshB+Fc5SBpexfY06r8IeQNyFNGMX,iv:hThh6hJ43LUfzZfprMgu+AtE98YdKuzj2/9nCmGkQJ0=,tag:aomJeVLG0Z6WaYKqy/DOTg==,type:str]
+sops:
+    age:
+        - recipient: age1yln5qratl82u6sqakh9gn2c0s88nzm62cw9muq5z4kzgp9wqpyzshhptq9
+          enc: |
+            -----BEGIN AGE ENCRYPTED FILE-----
+            YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBGZnF2ZzBWbXlpMzR2REtG
+            QUVYU08zYXBSTHIxdUNUWlRkU0hacmx2NldvCjRzNHI1ckhFZXdraWJwdG5idlhk
+            VkJQYkp5T2ppQkYwRnEyckE2YWJYWUUKLS0tIGs2VHJRYUh3RVhoVzlyVWRPUVp5
+            anFvYW5rZTVBNFZXcU1oWDNHdHlaT1EKmvZCautaRbOXVVZjOFkigCyYrJUeg7DF
+            AdR33NoDm9UnmJ86eq2NdLGLFY1x5GU2CZbER+Qhwf2+05NuhtCdJw==
+            -----END AGE ENCRYPTED FILE-----
+        - recipient: age102glvtt2nwx4ymgjh85449exlhmzk56cayy9hkkpl82u6wm64qzqjar7ju
+          enc: |
+            -----BEGIN AGE ENCRYPTED FILE-----
+            YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBqR25WcUNXa0NJTmRxNy9u
+            RWJINFpGQ3lWOWdBRzZqWDdhVGZmVnFvSWtzCnZWcGdRMHFqM3MzM1h2RGM0ejg0
+            QlUrNVN3TFBDRDdSdjI5V3VQYnpCZVkKLS0tIC9ETkpKN1AwOE9Ma2pVMkFtOVND
+            RzdTaEw3WWt0ODBjWWFsQUtYNlV0Vm8KC86xa3uDY/BXgLKbqKQ62WwKPpd2hfbG
+            cdYy7G9q7immIaL5brTNwmJG2tU6oUw2mkUF6DICv1fCJkTz+kTDQg==
+            -----END AGE ENCRYPTED FILE-----
+        - recipient: age10ujnqv3ttdw23f7jryjg4dtw83fj2wf275kv38f2qrtj5kk3hg4qf5uf3h
+          enc: |
+            -----BEGIN AGE ENCRYPTED FILE-----
+            YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBjaUkyNmo0dCtKZEc3eDNo
+            UDlOQS9Ya3daQkZVR1ozUXlGYmRONHNCZlc4CjNzSThTT25sSXMrSVY0bHpSNWlX
+            ZjZTdzZ0cGJrYSsybEtaS1hhTWowYlEKLS0tIE1wVDJFd0ljUHBJQkJ4SDZEaWdo
+            M3VFdWFDM3J4ZWNUVDhadUZPWUxTZE0KKfBJGHgSv/kl5Bd0tK5YI1+jwnm7798y
+            7IaVpdjjqtyHOcyRpa5cO7IXE6sa0GhPRrMGAl8gm2Ea/R+kf59NfQ==
+            -----END AGE ENCRYPTED FILE-----
+        - recipient: age1wztgmu7rm4yvdr0x8xucwfmnf0sruafwjx2ttl9cvg54epn0qysqqnr5n3
+          enc: |
+            -----BEGIN AGE ENCRYPTED FILE-----
+            YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBZUTlNOE04SjJ3dUNZeW4x
+            c1hWc3VjWFZwSlR1by8xWTEvWFA1cVdyL244CldsOG40MWNuRzhOR090NThMdldQ
+            SFoyc0hBR3NRZXBLN0NXamUrOXdCYlUKLS0tIHFaZ2tNRU84aVN1OVBuWWRFWXky
+            WGlkcVpiNGlTb1NkdEF3dlpxemtlMFUKRIe8sg9ttBDLyk8M6oEhQeMsYiGq5oRY
+            hHbJhwRvRysYGwOGU4bKau0veNh5khL+bsTt5TYO2HhFn6cgNBP2Eg==
+            -----END AGE ENCRYPTED FILE-----
+        - recipient: age1q58tmhk5hxrfn86qymet3mznafdzuhwl47s8mt3nyn43pararc5sng7kag
+          enc: |
+            -----BEGIN AGE ENCRYPTED FILE-----
+            YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAvbEh2d0hNM2dJTU1qeGxm
+            c0FMa0ZSNUVUeVcvZjFaQlo1bTQ3SUNPSmpvCnByd0ZvelF1TCtnWEVzMkt1VjE1
+            RGF1U0w4NDZrMlovMWZKY29mSURKWUUKLS0tIGtpZWJJWWpCaVU1Vnp2dVN1OXFK
+            VHIvajJpUmVEVkdxOUFMd2wrQ3g5aDAKB1gSIedEvfIqDpuTxXsfamUrGSKRMEhL
+            oAEZbZ0y4rFXabwhN8EEgTeX5f7ghv6mhcgKLY23x3M+RZHtIAHYDg==
+            -----END AGE ENCRYPTED FILE-----
+        - recipient: age1enkp0mlswl30s4h7z4qvyha4cmc2n2exs0v97276q5mx0jc86ggs7g2dyq
+          enc: |
+            -----BEGIN AGE ENCRYPTED FILE-----
+            YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBnN212ck1wYkhsZEh1SU44
+            Mk05MmI4OGNhczZ4dWdGTlIxbjE3NHV2MTNFCmRobUhlMFhmeTN1VWIvS0FwTXdP
+            bERxZmo0Z1h5dVo2dWxKU3pDd01YdW8KLS0tIGJuQ2c2VlZZZ29JeXVmVUVvR0NK
+            T2NrKzZoS3dlaWRHaFdDV2Z3TS91MEUKyCG7yBRk/vTi2mr882ephNzSHHpWFwnx
+            jxIC2owE/nRf5+AWm8Gp3k7+Et656LHz84UsETjnlLTrflrN8ExGLg==
+            -----END AGE ENCRYPTED FILE-----
+    lastmodified: "2025-12-02T15:23:50Z"
+    mac: ENC[AES256_GCM,data:0r2K+xbmBQkb7BEB+IHwyZifpIJ/hvOe9JC+nPdYaZhodJUXFRNrZ4HGVYkMu15ozibV/03IAoIfoTqJRel/gx6tRU02VhgQGmkYRZyTCCf1sbtPvntxbPhaJnWR8Rbh3cVjamT6vYWyiardztLQpa04XOjvJUNpgccweBEuGEE=,iv:pRIS9DIse7icc60+AE/W9/7tPg8yUXO4cjUoqoRufIo=,tag:4gw0PHfVle5myD5PYXzdFw==,type:str]
+    pgp:
+        - created_at: "2026-05-04T16:48:10Z"
+          enc: |-
+            -----BEGIN PGP MESSAGE-----
+
+            hF4DfdBfTP3jZzQSAQdArPy+d6Mh6n4x8P6yk7wfmgNyXAbbpTGoB7cZzS9jemAw
+            jrBZTYMWRpf+GEpB7VVcS33i+zyO2Fzpj2Ot6UG7kV0F8Zlml41r1mxxcaJI5JRu
+            0l4BMMuJUAPS11qKH9BI7Ntga0Tf2EXtMpGoUwOy7gh4NfJk08PlT6cKNnpJ7otc
+            NENCJBI2B7qB+/WRf1N9oxaY1Q509aOF0PqU2Bt/i/z4TXZxfatT3dNC+svQI604
+            =9AUB
+            -----END PGP MESSAGE-----
+          fp: BFE6386C8D66BCD4DAE14FC895F0FE7CD7E6A022
+    unencrypted_suffix: _unencrypted
+    version: 3.11.0
diff --git a/secrets/cuvier.yaml b/secrets/cuvier.yaml
@@ -0,0 +1,73 @@
+wgPrivateKey: ENC[AES256_GCM,data:hudqyQRVINJL1hmCUJ5F8ssgzaPzPjoGPxjpo+ffTH3ecLqCV1zXFmVABXE=,iv:0ebWDEAkPYoZcYaDWSyX4rUZNoTSkZA0Ije85cmp74M=,tag:LQFtefPK82AQs4b5VLcaHw==,type:str]
+acmeTSIGKey: ENC[AES256_GCM,data:TgzksmWIFaL7UXcI8h2DpCqUP6bysFZ2Zau5aQkpbxv8rFRyrwLrFVAd9DswNmfRHsXCxjEX4I9fDAoB9Fz6vw==,iv:zXCkMj6RvO/jjc65F58WMruuTrkWTbWqlJUDb/EKPVs=,tag:pb4LOTp+ZPXfaSZWnuhi/g==,type:str]
+gomuks:
+    username: ENC[AES256_GCM,data:fPJFcKJ4sA==,iv:RRqvs4fJkKCcmsJUxFPC6iJttQWiUJs07McX23T6F2k=,tag:Wlpvv8EwGmWVKD1nkaTiPg==,type:str]
+    password: ENC[AES256_GCM,data:IhU5a1D3CbBb7o0WUGiabVBa9uRR+V7WscweU2MmDAZmWuzxyO2k2n8FElwIaxpJHk/u5Xik4EJldHc/,iv:qzOJQ6+oqfQA+jEyj0TTc675YggkTS6T+dcuD1FRJMU=,tag:va00l7E7hU8xGZcRN7YQQw==,type:str]
+environments:
+    navidrome: ENC[AES256_GCM,data:saoEfReSrwHkuhW27eEYMJ51aVn599G+5q8Kk1/XnzfAUvlS4bUI5+CGfpBX3WFR2JjF8JpgMQUa9/jMHJfRxBOTwD+NsdlxO4mUDe/7bFCTwXHsUx/7MCfYLG1ijwbNBPpfg9sSA+c=,iv:wgYqEP0YGDFrg96ZJEHQeBcKvnAhkxPKuMyAoafuVMQ=,tag:fNpVGrJzD7ZiOpXU03LK8g==,type:str]
+syncthing:
+    cert: ENC[AES256_GCM,data:tw1CytMGY8Xj0TXv1PU/N/F9ItIrx/K/Nw6LsZUe3TZoBBMQ9610mE7ZQ/9Kvt311IeX0Q2qgUoAze/3q8/ejM+cu+79G8YsjS5fzi+Te9U52i8Aeg3kG3XL4FfJ2tzie/z+oqEhXRzYppdJBzXZQLm0wGipompv4gPe/ImMSDoV62niJ1U+scJ/vndK1PTdI8qyQkYSESuS5Ctu+bY2b+t19uCCHGf5/SxhjdZYcCcSI/8m+ueoYRE4+mEWF16zMg0+P5TauCEJ4aHQCbrUPIDTfhgECCO5oC7vZlntf++5YjtkM1a/uLVcR+oNrfu6VZQW6Gc2IOUs3LUs+T8NKJ6jxieGtFgfqM95RFau4ATN/dD8NiSeaIy0FAhUe82Z1GwOq5TuhwANtHGhVgoUq7ebiaU8gWYlENeKazyLEkvwdmqehgZW8g16BFU7qnJYW/1YZanzTqZKi2jw/VnGiQtDx6l43lKOD2Sw8SeoaOjaNScBho3atBuT2/BsLgqT4jcjR5j2R5sBuyZKTB7kh5vLecRSnswOKx/dP+RdNhjGHAqMaJXIsb5qRbOebshWWEA2BckTYzl6xxr66Ku3me2DyZhBkljRJX0ph0EXW1s/CYBKGtsgMycCGT/TO4cLvxsUWmRdWOPemN/HiCD1X9L9Wp8NTX+TK2waNmC/5oVBg/pdA38oC6Stc+CzTUS4v3WgMuBhBTCkRInUM1R6pHjgVq50j2/RIten7aLruXNagrtOwzq7HgnbroKwzThDNB9V0mmOSRvzFt7PBqlUnzrSvSd+XDpedntHQ53FlQlgS1SgmG7PcFwdc65pn4gI8tcm788uvzjgcBFE8ErtEUHLN3aFpHvl1zb8tgckdCN4PtbommlqL6z+/oNcZ8cOhCOx8ZnDP79bFWo4aA1jBvDVBzLC7zdLF0nfjEHwpYuQ/xCBnbOTIDtLecfjJiU9uI5n9tlwBdVpKN5HQuD2hLialXhAVhXZJge0uRgC5WDNG22g1TfD2/M0ghwwfL9iiPmoV76ICjURYZJY7MA1r2bSlmbvZgU05ro=,iv:lpncsQyDe9GqOVkhErE/o5QhFocNFRxirYLEJVrJ4Q0=,tag:jo/dZukny02HDOO6XtAA4w==,type:str]
+    key: ENC[AES256_GCM,data:sLV8PbY1xaVYh7JJ+R5XkS3fw8D1xFI7trxrGR5Hbf3nMleSzMDbApOXCDZtbFl93pzFK4chg6cd32KmSX/lcOOAWEuZgRlbOloDXD5velbDaWVis9JJXI6xl7ge4m4o/OwfwESiBxYDRJdMVVlBrPJHmHZ+dBn2GgpBT3kAV7Aif8k/RyHvzh60x6qn2NdGWuLLq7T8a2o5/+KkVplqFi501Z9J2uR5U0UMt/dqcSIHJpT4m5+SMV3PL4Wc6SpCVIgIWlIzwL9nTAlJyIQP+PvXKQk+4VIWMATg1b2rUSye3D2SQjGg0CCIYHwOIl31kDddDbgPVVcl19tOYzBcy6pCDv1GrXhx2rXQIKDpdbOpjw6gDeuVI0sw7jn96C1/,iv:SYrZtNeafL9FxHyzqVwqXAGlnlnFeqo63GuWoa2FN9w=,tag:rlIStTgsu6oY2PR9j31H7w==,type:str]
+zigbee2mqttSecrets: ENC[AES256_GCM,data:kkFVenP3NrqWUTB5scHCRgKMsrT2BKvYQMJFPbCZudb2rntZxag1ZHut2e1kIvImw8q1bPD9llvnQMPQNdArn2zBGVVuvE3fAWQfXF4JM+J6D4M0Ff89k+VQmUF9L7wgPVk+YtK/hcxjxwaOpv8IiRNC7X4nc7KkLhYSxpkZw90ZqTEt4+YtrhJYK5nmKrrHP+uFoHUlr4X0Cp0t1TyNZbxVW6f+ZgPAUvb/5fbb5j7oE0CU2vSarx8RHcWA4iip6arvzq/YsHZxzWGLdAhUmEdHf9VGPWf+DQBqF5mgX3US1jlP5KwfN912z0VFWuKsV/jsG9LcniMrOzgdBrunxUMBZDpPZYL95yaMeg1TFa0n6Cb/yNBMXk5MzwDKVok5gVk04GqnyIyF6nNZK0GDLU6GjHPpE6dDonWx,iv:sJp6JLH3+VZ6BqEMZwYW6NQNNIk9FLe2ilLdzdeBo/k=,tag:UJzTtdNgSEHd9a+etTEgNg==,type:str]
+restic:
+    navidrome:
+        repositoryPassword: ENC[AES256_GCM,data:562X0rVly6F8RB7gmo08f0KwtIng+C/9gc6hUKGD4EYfA6NmyjxsEHKGQ6/RrZdHoZLRL0ur9zUFL+NapKMHKQ==,iv:w1iAvPo/fBo30/vn9TP9IHCHSPJehYjuB/jhbUADVmU=,tag:7PkOIOXKJY9W2O+KhqytHg==,type:str]
+        sshPrivateKey: ENC[AES256_GCM,data:11T91mpZpRztqii6FLLPIvrdDrzvdH8Jh2g5dFF3U42bXWnCJjnUxLsMpVoXRmsqIiPdCD0pPRwOyo0Oea3tw9UmddRb2ls8R+t97hQZxy9yeY/wKLYcxG5UXaD1FGYtFUawA/E+UoK9FwDm1ywj7l7JpgsCya7dAf77+E04kNP271znEwJHruv2mMBhfANRDiUD4T1Z6dNRvcGaPZ/0nvAg4S3E3aplBUUzrU6mFw4vBYecx9BN5Y2KvDAaBfIoq6E5Wv2XrSnWpQSujsxDnrTzMeVe9kLLPWwD/INq46BYOjy+mxV2NVY7eVKn0H/xqqxMcxRGy6PEOKpzPZiPLMOaTCmuvLrcORTY4G3wWxdfuLg5nvwPxBGwt7IGQCBA6ckfAFkP/RkGtzWCGY27oQO/ItB+P44nJe4jVckvSTknhN3TePNAKFzTZ+xCgz3v5BpOfGoNTa/NLMFw/GwvvLgcQtXjOg0YowvKv5IoHayPCoKSATdmR3H288EQRtjNLTuW,iv:jt7QznFjBqA9mSx8sk9PcMCfJuanNHycU+fdmaakt+I=,tag:qC/xxSTlsFsfimoK9AkkIA==,type:str]
+    gotosocial:
+        repositoryPassword: ENC[AES256_GCM,data:tIOqoQ8u5Kf+tsym4+qu74f/FTcZrkDqXlG1M+kqb05+xiwtXVu9stCwtSlWgDoEzwOzWJg8xBg8v+l45GiLQQ==,iv:XhQnOFRXnC1tgySeMU1Gug0oZMG+7hR25OOj2WVA8pc=,tag:cwcIptFoTkKp/4NUiUcLHg==,type:str]
+        sshPrivateKey: ENC[AES256_GCM,data:A/b+BroGLUDfmPzjDoEzyTLre9OQsP56FPy50XmWZ1VikEJDi2OhQhR0Yfgq1kp/kNaJC10IRyIRTZhqErK/WyIjugVwcdZW7R/YK7Tzu8ZFoLRTgq4XqYrTxLKufQMJ2fQpVtO0xKIZ11gNw+lIdbgVxZPVA6nrBFCzSZ6Le13uoT1Bu8aCOOF4U1wDoxVbKiEP9VdafL1rh+XCrjXSKmPECLuVUgo5xBMKNoC+3LWMzlNWz0FGO0Md1bDIC13R+cWTkqNFhgXknVuToX+QRdMvei6XrBu38Jv8WSAPHKGy/fUzsXlxJ4L1iQVeXbjzZ7+kXsBzcg8AfxxQW0ystyFu0IbdmIhypasPyj9XEDGASda5AiS15lctGXAV2EbhodkkeKtgtj4qYihZULE5lvhEPzVaOjDoitgtCgTghClEhkvtV+iJL70sHj8JtHfmOHqH6FUSXJX/BM7AyHBRBXnntgPncW4uWHtq+V7lf0WGWlFM+/MC0Q2ARhWz9pOVx2VA,iv:o0siialrL6gjmTHkh/YiID+nJjLgar+YFuNbXapx7KQ=,tag:TPR7gEnQlP7TCE2QLqrlNA==,type:str]
+    things:
+        repositoryPassword: ENC[AES256_GCM,data:nZElU2HmcWnhQf1+7a+qcbzQ4IbPMunj5pMxrglYB+IfePKbxRlOKeIZCcHzb5taUnhpUpcfZMuDmO8MuXzv5g==,iv:lOcNMNi0kUeS+J9i8Q6/uoJlrhC62yRbSRDc4GKKZ8A=,tag:XXjcBRro17i9ppw/HzT8rg==,type:str]
+        sshPrivateKey: ENC[AES256_GCM,data:a7JsWKx7Z8ErRvWihxG5UbV4rP1fFavorLfoVVGUWWBXjrkiiZhdZy3Ft9sIggyac96MBsVArR74xPQgzjMyYGLmenVsPGxUmz0nmARoqAY8n6wBRpHC5uRC5f5qM5CdH27BkR1x6Eb0F5W8gyadfHfYmGyeBTXfWUdmWQ4ITOLp/Pto8Qd8YrXANJqq6yZvuwebuOWNckkM2JUMkKlg4tqXNYHOv+TFmcZ/SremxuVdCuXUHmdn7E9znEpEqYEjPsAjO+jc+9KoXniFd40OYyp6/ppDny8fAUsfBO6vqdNPC27aaZAUrbfTeUbmEIcscwAOg6oTAVU3MHZDgTw8OJhR73mmn5wgy0LtKVFldybXbKwGlFYrFGzLp2DDFFaJpvbv+SPvCGaFuTa7Om+/mRxMS8UK0wo4ahpxl60POffj65PlN4XQjfyhXH1jUkwf7Z+cBcCnN12fnr0g2IlrKrUl2mwjMs7yrlrjd9/+gPH5tolqBxoCDRg1ZtIAED9ax0T2bNDGsv9fsqNBPaTV,iv:isMFFD3eSpyomV3syJhi7rTcwsNRMrrGkGhbym6yP1I=,tag:rBkHMHUpYhPrMHPG6TPCAA==,type:str]
+    immich:
+        repositoryPassword: ENC[AES256_GCM,data:iF466mRAzG0k4lLFlKowIR6Hy2NjaFh6QJwsOeHZtHGJ7C0lxEIuPRPUiJ0Yrz7fYtGC/Em90TX3+t0f6IWANw==,iv:891vHDFzYz6/wI3LaAgfCKbLnKvoj929MSa/CvFP6TA=,tag:/JNiC/OVM29EPCS43tCfhg==,type:str]
+        sshPrivateKey: ENC[AES256_GCM,data:GvejdgJYWXm5Za+OhPpIoUujtDGHy+F6L86L8YNQUxP+38X0dMHnXAKWhnhOZZ+iO+ny6pjKYY+qhAZAEOAP1xmPHkVOHm4pcXIoL4TZuhmYUzf334uon4krgZ7kS2Cw4C0xMzQOyT4NV/0HmvsptiJt2bQNdgpFgzgDgi0EnqrmuPxvo/TUDviz+g3Z2zZhsupgBS83pdvAQxUPEWPcFR1+mIoEcmNXEY2qjmFNPi3IrXOTQ3Xo8RhouKqpnt0IvlpVSjMCDGVtYkIwdD+0pj3QNe7+eVMq9zG+FHptI59wj6UiJOUIsoSxvEqqN2DWIwq00pDnDJyp/NnAvZB4C7A+L7O4nxqwH4f2eYwlIWaouSs3Hz5PPfqduOL+Oi5whb1MmHslpGe+kK6AvHbowzy456ZYC3MnOFNuXNZMjPB0Vwdtk/BKNWZv86GWZbqTsBT+lxtHivG8le036EvMEANrBP+hDt93sZlntosxHqRNAmS2rm3RmToCkpGFs6QZaazyDyf2UZWTnYYDWnLE,iv:eBawBIa+Wdj9c/cUK66J5kQ2AzCoqlBXM8brzAHlD+s=,tag:egrihMdDqBpL/rIq2Yl2Hw==,type:str]
+    syncthing:
+        zaphyra-documents:
+            repositoryPassword: ENC[AES256_GCM,data:1Gh32TQG8KAYeyD0v7GJ7cRCRfeEqIwcN9yMOWqcrNi7ZnW1KHcnY/EiAeqimKLUjxslgwp4wchReJJ3/rRdCA==,iv:HJMmcpXzYgU8dukMXUkxvktjndGzGj4PaDNzMMUJJVo=,tag:jTSTrRK02VIKwmUHpxyonQ==,type:str]
+            sshPrivateKey: ENC[AES256_GCM,data:UKP6c9IVMlPrro/aW7y/RYCi0ORstDgh4McXutxsopTS6QcilFzxPe1GrBYrEJHAfgxowIvby2NdAtmcQhDAiG3q1tk8cYGkO1/M+UUU+e+UcY6KUBZWO5cJMMfunL6x80wzPJiY6XaXOPazaawSHWT3Lb8oKehdOdLi75vX+6RfawSnqQd74x91GORULjqQgDOXSIJ3r4nGUZzyh9QY8tPaOC5XaL+gTyNrkIlzhc3iOdS1tEPVpnS4TRi2sppzR+FTdEC0VX3pHSD2UPrQb+i5EXMLU54EIBsl7Shi7st1dZxyQgBQpy6b5pgKgfjamrR9QR7v5skTBYLFDP46cDjnYjEytBHbDROwOs8/Lbj1mQDUbS5xHh91wNmfVUJdwVrqUNiE1Y0n6LgxMnwjVmGjr2AamrFyZEuIRChxADlJifITW0UofhrLszVUQL/WYeIbkHA1HOlldnDe+tGHbKmzPHGcrshOLIPTe5B/9xQ6UJ/FRBeLu4JrQzIsbFzrIwIPMkQeqtmjOTBnlFN7,iv:BtA4ohklAuN/i3LcZMTGs5OplXfPaJffchVHsvt+FOQ=,tag:uxx+U3JwxqqHp+mCHmft3Q==,type:str]
+        zaphyra-db-richtlinien:
+            repositoryPassword: ENC[AES256_GCM,data:8JSHVHRSaUJwxqPAenKkk7bKJ9A7ya7XUBE04JF8ocFN9r7UIgw7+5tbYVGM3gK6RB8V1+uWZ0ByzKtKj1W9jQ==,iv:znXbWYY8t1nGKO20so8tQ60jIrEEFJ+cW6eitsbGoLs=,tag:pjyRPnQ3CpkwRpHK0Tp7EQ==,type:str]
+            sshPrivateKey: ENC[AES256_GCM,data:UxR5u+GmH7PaVrrtIl41PasID4jQX0r0zaxNAS+vrWuen+nLZc3hEhwmViNrRhr7nlVo442HxE0e9QVWJig7yKGOhPvnCLmmcuuhG+lc/uEpDb4slu+WdJsqMRIVGSAbgm25p/3r1ZpqPUDZGwXXgbSri0e9JsKDq5GVFi6+tp4zKSl09OoziNIO00MmXLtLV87UdEJuoA1yLKBR7cUcAUXXExar0kP+yWRiQ9hGmNEGvzpRCJi/OWN8zZVFPnugTUcN3qxj7ydSK7z6XDFSvOmIzxb62NZXyRCOOHVAwhYZYAQOtDrsrYh0iNYQrnnRmi465DgzKMmZsQUl6HH+3QUm9ovKYQjgHUZwA7wEpu7LK/h+N6EgBt1ZAHMSeL+jjjdvC+lMSB+BkxE5kvbGefZuSh99UjsTSBWGVnHx7Lv1mrd2ol+qlonJs1ZcTI3c956pmk9NgCy6VQSc9PN8bR2CYwlgaG9zvfJf3DI9Efpvi6u7D2hGQyloCpMEwlBlGWLjLVUHHN/5RYOUeei5,iv:QRIiPekSm58PCWBz9BkV93AuiNbxRQ6QQkicvNr86Is=,tag:S9SfpNfOQYx4YCWmT9wBjg==,type:str]
+        zaphyra-pictures:
+            repositoryPassword: ENC[AES256_GCM,data:naVYZxIpcKTyDPjouzhG4VZeqW3rRBRGWmFxN9nT72llYcZHq5d9R//KVg94G2GWaB6zvJ9cJLlBzqrBPG3Brg==,iv:QIM0Isfx8M9TO9ZI5lPQ/Afp7+qQ2H/7roL0ydZJ4Q4=,tag:vBP/97ELHaYwott8DzclyA==,type:str]
+            sshPrivateKey: ENC[AES256_GCM,data:V1oEnmEDQM6c3WWNSAnNpgUB0+Qzbqnln9oNU4RtcZwWDdpe22F0rNESjRwUU3oDLlP0tdPmIB4ByLxb8y7CcyfQZENMYhZGysWEdqhohoTNnSyR1QdH1ipIr8JCn5AH4irwmhFDWu0cbbVt44k2wJ7FX5PJF8JNFCau5SGXvazzqoBKx2zg/eoYA0zjz7bcXtAhpwS2JlhbQxlfQAuRypjF9w0TLd0ttZELx3D3MHi2Gdyy9B7xnVpVzFAvUjSwTb+v1l+HRioIplaWEcM3Or4wXmUOn8ob4GpeUsA5aInUo+M556j82zmrRE79aT7VNcwb3p3y4WBsEfIj0u/yf9yE0eLEiZzl4uQA1fPcXU0QGh8zctD2sEPUTyhN5YL0X9XSXYzykWWgtX+nex1QhfsKHVoljNd54c8Grgt7hkzQB34t1FNaOiyzln34mQxjOFuMLBdmWCd/h9ykxFuLfs/AocKRHJ2MWP/0hK5lJLfnhgBmQN/Abh7Vo2Eg8dfjIvsA+02qC/oW9oQsfU3a,iv:Umwx4QkBTJPW+SVTXI+PpscHgWiSDq7nRCbTZWddx44=,tag:cHN1RKEhCzWtiNq7xA8+cg==,type:str]
+        zaphyra-videos:
+            repositoryPassword: ENC[AES256_GCM,data:3fFva6HGbpibzSr2btU9en+maLsMjLnnelNpCGL4cFyK0KyGOTWWnWsPOHAfVbGQDy4KYNpyJK6I4i4LPyIezw==,iv:PErWSZAgw4C+136RyB7uUxI96AWen4M+0ieIJsVm96M=,tag:PqXegP8vfXjJ4FJb+UiWyQ==,type:str]
+            sshPrivateKey: ENC[AES256_GCM,data:xVewzNcWsXtTNJstwyMBGClUowqoZvBGB2CpcidarOBEHhi29pmWGXOvHDtYGGIp/b9KXPnxcEURiTxWjrwRVYxS9XC7qmGFn+cyt9MR9tcL3Ze29kWksUcPsNtj3Km8NDdNZsTufA0ZptPWZIsELXycZX1qk/Zz7PhgB95uCcphodVFCYBrDJegXls8OwE08xQn2moYN00UcHMxf0kAc+BtC5I/+MNFb4XJE+wZ0KYSNKfVjn5uk2Axe1t//CyvexE1Dw1whL7rz5eJwxUpbozEtl5ugJOMMWylVi7KdMu+aN4PNDl3Q6jBrhDlmHenjKsTk8eXFfEYH/ifRTLcuUfpyHWo/J68N91R4JqFlHjlRW3IYDagreBcDznbBf2TSIRYXq5zArxfQzCGmktIBW9uDh/uB/8QyVLF/HsJFLF3pYslAiWBhDl6sqKMGSJc1ZileRvJ5uO+nUsScdbEK7/L6AsFP/DBovRa5WTg1XI7gDttVZityqTC/oiscsYdyQtYqjhMmJFvqGPAmtSj,iv:nX/rqv7z7rLCA3K7sut9mlaiS0BVlXR650I5tbNZg6Y=,tag:Fz4Isl9L08UlobjYqYZ7rA==,type:str]
+        zaphyra-audiobooks:
+            repositoryPassword: ENC[AES256_GCM,data:b8zh/E/f5ynraUopZyuj7ldnQvcFsNvlsGS/g/vJrWz2PyzbZQ8Zh//0ReCXQ+Ulf/Bcv7YHUikb1bFA+sbpog==,iv:pLY9zjIpeuOMCaFM2yevpkFUxNc7fNNxsJF7Wvssab4=,tag:qW5ZqjKDfRBRdiQHDCrTqg==,type:str]
+            sshPrivateKey: ENC[AES256_GCM,data:a0973VDn5AmYbsT4Nvc33h1XNnTQ/aLYpRcXxPPB6GGMij5fjk1yaToOyWdtADz8z1sE/9bw++4l0m8QEdSO3BKjcEZn1mI7Jk9tWRfy4esahe8TFDVqjTRbrVCP3CpHfVAByGy/1qG7jXn16pwvFHVC2tf/jGvznir98XmVb60+hzAt2vv+cBfKtZjmFPJ7MRJgU08hKFseVTiCvPUM81XG7U7MiQENvkrrq5+GX7RETsEC00KtQpkd9hLcAk1/UPDGM5mi6bPrH3eZ2orb+81piIDl3SnVTmQCBZZeV5V6g6KWI/iv+bQwn25ZdhOge8k+UeP5fGJ17K2FDenPGph8PSuTkgcviLurujMgqX6ACZV7id2U0WSmj2QOyX4rHmRNZ+SzTV3H2MZrCBCTVF6iMlvqjg3z59WOcSnBTkvSxHHuq67IkHctjX+lZ9t20Jw7PZgMqE7qCJgDaZ9RUzNNeNQIA5AmMPPBxryGNPRhcSft0E+HWwxnYGY1+xvjudT1fG5OclDL6l6z+7wP,iv:urcCRSrjlCHmAsbdXf278PbyEqk1Jcvaoy964TR/m48=,tag:+CLoCSUcYD2w4zffDYnsfw==,type:str]
+        zaphyra-music-orig:
+            repositoryPassword: ENC[AES256_GCM,data:yNe+6uLTDhbPzJeuPCACmR1Xi5lzYIWANzW/TkVHfgXy+A50ShFiFDXSith7rouxwy8xoQMgskRIf04xUgnqfA==,iv:UUezW8XBYMWY3xCLigel3UO/Py7v3A8bbiiLyPoJknM=,tag:pk4aoVG1Uql2RN+7Mlrx5Q==,type:str]
+            sshPrivateKey: ENC[AES256_GCM,data:+TAvJSSMfhRWM5ENUZH6/Lo3WtrYnKd8WAwHbGFRmViyQuU1H/XKkrQ6ARPBGH2/UmrfQpiUxHKzLbl65pibrUJfYOfeZ90/NZxBJLT0cROAUJcQScqoISIUuurflK065ohz//rX99Ixr9vGTN8cMT7Z56ALRj6bd6xZ8iNSnc3AIgOP37LNTnfuB+rsPzHm4FukZWkRENtqmUVjz33ZGPdqn8dvF7SQBX1nRuPcSiVDwfB9Tk9K6fz0nmtCtUoVA26o3lumZvYUFvB8K616JCU+sEN0W1zWe59oUNTvDyg/y3snBI8TyeREVaWf7yuEuMZjlTmfu/hJJeTNQfxP5mQVnkwQx08v+yZ+MJFATAFTRZAaKjaVZ3Ojv6LyhliHJL+FllEWRaRZBO3IiebkmxDDCLhN1bmYUYydFWkXOUXtwkSB8LNZqA8zBH5kSSC+uVBcRcrIOvuKHxIRhnlS5aRigBiN3be+/5PBfXsspgFiQdqVliEHJoCML2dg1ELBqaeuDogugKuWi7JnTpN2,iv:/O2FNSdYrqa0x66zqeUenoA6h7FjT4tDZJkGdbWLLhc=,tag:bj4YfvJrD2Q3MTyz7WywMg==,type:str]
+        zaphyra-media:
+            repositoryPassword: ENC[AES256_GCM,data:JiFGGabPsgoJMNalqdNBS0orccNHME1LwW/70Or6cFVS/J6pMyWGIljZfJu9scGCWGrKia5Kwidy2qNZuqFN9g==,iv:+pW7pULpZMBw1HLpK7uJh6CkxGzBkwAvc5O89QkIdJA=,tag:ugFaggcJFck9cKiL6hAFFg==,type:str]
+            sshPrivateKey: ENC[AES256_GCM,data:8bZXSDMHfNN2+1/CGNArqA8RkzHbbG6MJhz7l4KG2PEqFxkDl0qyCfCSk7Joq8r19cvz3AJZd/UqkRv/ga2vQg5oxsi+u8dCzZmdUCczWPIErcd48DIHQvOYDSigN76dMCF9idyX9StEoqUPpfIxIfA50KLGOtVdlLQqOKDIXSq4Zu5UVfG2zOUHCexgoNsT6QHsBnSWTXTzbsgdG3dobRaeXIn8aEBqmHrZ18bBHpQcf/xhEFRr9kjzL5lkTxR2zrcja7wvXFQ9q8/zh07y7aXosFnpYcMWp/ysmSgk2xo+PTYm3aFQHLUjuWJswJDOXpnjWVvwlpJYaneYU2fo/RFHPpih51o3W5xL7FOYNXqfo587KIgATtkfFj6Sy0GZraN+PQZOPeIM73Qdzyu6PBx4Nyr4z/aG3jsLYbczziFI8zAoRiRBsfs+TPWflZWFTkKCOiHjT5jczI8JmETdXibBfKwMV3nbaJhjUhramWyMyLXd4v0c4tIBOND/tLRFSOOyZ6kJVOq4xN+2wKk=,iv:F1owr/igSXCS6QEUHLv9pVLcoc7kHghcvWpNu5cS2XA=,tag:d3VeiGO5DQnsTGNFx1Xb6w==,type:str]
+sops:
+    age:
+        - recipient: age1wztgmu7rm4yvdr0x8xucwfmnf0sruafwjx2ttl9cvg54epn0qysqqnr5n3
+          enc: |
+            -----BEGIN AGE ENCRYPTED FILE-----
+            YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAvUThET2FpV3BjcS9jdHZC
+            WjZWeDlZOGErb3FHK09TMXBuUENyQXRrTEVvClFINUo3dCttSGkrNnZMQnZNcEc2
+            NElwalF5VURkaVRNcXpXZlhER3A1UG8KLS0tIGNJL2VCUDRsanJNcjIzR21pMkRS
+            VnpNTCt3OUltYmRuVWtzN3lxdmhGdzQKhKCLyIW2Hb++BiBQiF3WaRUd5KmYkjea
+            ZdPTn4IM6RaZJrmIJ9sViciFqUZfJ4EwKDnPUjwB82leJ167OHfxxw==
+            -----END AGE ENCRYPTED FILE-----
+    lastmodified: "2026-05-16T17:25:04Z"
+    mac: ENC[AES256_GCM,data:SGvyoQcP/T9glulzRSWH75bpF/IKqB32pB4qdquk3ADdj9tqxbIGSBbabjxOAvVmARq3cRoDyAYYQ659r/VxvuL+u0NSiGw3O4T3w5k7Nl1uc64c4FI3ee7ePQceEld9mU+x7Wvoq7Rr9Y2ltHh5uje2VeAW3IyXdLaOxbLkzE4=,iv:df/Ox4pNObbVw2mAfhUSLVkhnA4aIUh1jzIIz1YYuCU=,tag:7hduOgc1ee3aDbPV9aOGRQ==,type:str]
+    pgp:
+        - created_at: "2026-02-11T11:23:03Z"
+          enc: |-
+            -----BEGIN PGP MESSAGE-----
+
+            hF4DfdBfTP3jZzQSAQdAH7W09cqt3rqNgveeY40tlaoNDEvrh3JhyBDuj9BHJ1Ew
+            U9u8mOX2EedMwUDuX9OUI0M3eirQc9mnbZSIPFYp2gi8zWyM03hQoXIg0JLsETre
+            0l4BYFe7wy+b96AFUYAUDNHyHyZAQzn+M4Qy1qA8qd6ykEP3m6ZowwG7AYXIQzWd
+            hyD6j3MTbC1g8E0YiXVJ+EAFmI/YThE/2RjeiRose/iQPE8bMn21JKpuGk0SXY2T
+            =KQFQ
+            -----END PGP MESSAGE-----
+          fp: BFE6386C8D66BCD4DAE14FC895F0FE7CD7E6A022
+    unencrypted_suffix: _unencrypted
+    version: 3.12.1
diff --git a/secrets/default.nix b/secrets/default.nix
@@ -0,0 +1,21 @@
+{
+  npins ? import ../npins,
+  lib ? import "${(import ../npins).nixpkgs}/lib",
+  ...
+}:
+
+let
+  haumea = import npins.haumea { inherit lib; };
+
+in
+(haumea.load {
+  src = ./.;
+  transformer =
+    name: value:
+    (
+      if name == [ ] then value else (if (builtins.hasAttr "default" value) then value.default else value)
+    );
+  loader = [
+    (haumea.matchers.always haumea.loaders.path)
+  ];
+})
diff --git a/secrets/isodon.yaml b/secrets/isodon.yaml
@@ -0,0 +1,42 @@
+acmeTSIGKey: ENC[AES256_GCM,data:qDk3XM2ZqPqdAbGTkLO1x/ZYK3JNcJxQReYSFz+2Mcpr4B8zeM5Clv2DYZds4g2MhRZngx3zCTabYK/NI3SXlA==,iv:oAP9NmLpI/5dbVjUT0Swyv5ARheY7SYi21+Bbhn6jLQ=,tag:lqjWTXma3Zs6BDu7iVaLSw==,type:str]
+wgPrivateKey: ENC[AES256_GCM,data:SYGGcLVnUQKFZYmpMSIggZGLek98pWmOOdtUO3VANDoIdYJgXJO4U6Ntucc=,iv:NSSUtB284+f0iMOv7ykak6xKJXoPIfoD/PjP7yWiVl4=,tag:lmW8oLaikrFr2vrh7GUFBg==,type:str]
+environments:
+    ente: ENC[AES256_GCM,data:UEc6HJey63DdcpFyVy8K8i7CCRs0EEQzq/DTUIFq56EDfr9Yt3kG7qhtxlSAcflD+jKCx2LUoFAoQMEgUOX9jXp4JsQdPFCfDqnbClM0A+BOOXcz+qIFVD4jCmzcVDe243c8AnUzhKl88qtvkzxnhVcdN/etB79RrbxP/eHZeozAf0LEr5VdFwhyvAVccL4Secegfh+Ug0oUlDDzfSV7+c+vHGtBfOj6HIwWV6F/LhAzfbBdid0yk0ChXVvkszowf/dDzR67NuDlh4a1IVozQNcbnDLCAxCpwa3+LNoE/WY+uCTXYrEY1FSZnZZkFjRcNjPfNOGBobKB/EPyxbDr3BPReVYBo68KCRhmnri7wdThG54MGj+MnvCoutNM+dEyNwcuBjq41c7KwjFrSM7Qwn9uLiH8FutU+uAWITz9j8MVUaA++UBlHom3BJ7l6EUVHjo9QXSP,iv:91B7EVvRJWWCls26i5+CIMN9GroGfASpnv2bf3F78MI=,tag:R0SpUtbylnIkmO7LPCUQuQ==,type:str]
+    minio: ENC[AES256_GCM,data:ldr2esRCB9j/FsexodAKBpW3geOKj3mum8GKI3JF+Hs+Um2uMFbHRIamE1Ag6NyNIqYzM2UWsF91baSnDZ927BWKsUxbh2iPLipUTPIkJq0RsSfj4EL1HtNIXsqYjIKR3vSmS8gQ,iv:eLGFJw0/3A2pCZYBg7n2A65f2PG1a19j/02STvTXEZU=,tag:7hPEC/mN4SJH06pxRLD3rw==,type:str]
+syncthing:
+    cert: ENC[AES256_GCM,data:qsaUGpfioRvylwpof3R20cbmIto4N9xQvIyIWpJsEwu11b/ikiFBSm0gp3EzzrKXCcoVkIlHTPQeYWfd42sdVoPRj5igQ2stgsR2oUD8Nml5mduu4mvREis6pd9RJkUGhQcze4M3l1zbwiksCMpjdmeO3wE0wTQuvOrbR/Umw/0PNkEv8nVt3eFwFoDTYEyJ4QiicLeF/pQk2akB5iBn+lK/0U+TjxxKfHzXrKpi/Coh8P7RuWb3+OOB5cq1dIB9egZO5vrdo/QbrskbZhv+Nx1XChxL0G5j/1v5Ijrux0jOSf5Shk0LMmQf4c+trT39X1LlOIA0eHQ0SySWSs9jVCR9gQ/xO63U9/DBvZJ/+YJkoGFOi+LYxm+OKOJLt62d2mCWdV9Wj62mBM3kPHyerDhE/Pi85uUp9ntPAhZooF9bUjuVBE8cmWH8LvLm6eE5aZFWV/Gnfi/FgXfUtLy2xgv7NGqr+5FfWRTH99ovkI/9R1+NZreMAUHHLG7blpyE6C4LNgLMoVtqXWAxykgsiocF1u01DUzaxXt08LltiBOZJ93YjWwf55WBtJYtw5WKYf2mEipG4krdxGSF3Nm5dDyA/5rOziUMLx43jHyu8v57zcf7ZW5hC5MeMnnVpRG0qVfm85wyzLJmyW6fMfgNeQL/i62T82x7SeR0AKNxOiI6TLXxKx5qo4L+jQURjjuYy2xugaqm2K7ermWAK1p2euyr03SDIJApxt5eTIBwjspTaoFtBZFb+4HPawAaTRqB3q9HBD0wQqUi+hSpL3JOy09WyH3GtKRFn8WECfKPCRImRdEG3UKB2YiOT/bvyLRks92YLBG1t55SqACO1k7y9f9d5dSsq9avS+a3tSgOIB+YtumFJUwEV6aYq6hFF3PwMbTJmZwPtCPgk2E9b/tN0syRbnHF4Hh9D7dglHM6GTzo1TmdlBwGECcV9tra820rab1KgcFGlvaf7yDCU1NNmdm2AJvKRgjL8NZ2ZPv8f0JHM2U40WnzPxfoVT3rqTT14ekvXQznJbzolO7GgWhtnyTlX3dm5+LQ81A=,iv:orep2lxLt+qUGyloMIwjr9C0CgARqJMoKJCO50DR5zs=,tag:sGlLh6nYKvhbStbLyLrH2Q==,type:str]
+    key: ENC[AES256_GCM,data:S7DXVxsZrTE28fnSK2kwFFe1gVOGBxO8y8z/MDzHT9x56RcWxpY49ol1b1gzwRSW/IKo+4BEcGpiIT2gGqxfHpe6wjnc9p61KVCZaImXLQvOTOweDFYpzsnwxPHFbM/GN9YoTsP9pCBF1OpngYM8H0XeCfVsH4i74ndnpp+raGqok3T+TWQKgOaDLR8E+EstJEAAvZr0B/uTV4n4jY/X9p0lRFLMSF+1BmKK7Edk3B6jzmbz8kP1HTKxzoI9t23TTVUC3CMD3KCn2tp5OhoCtPTI75UCrPKohjz/g2ZlfbrM8vpSV2h4RCBvABpMm7UayPNGoe0AAWcVS2xeBOq6wWh+PyYLNdu+l78LOCe7AyQrQbok4UlVJ1ql2uxVqZJD,iv:HBptgmPv5tS6G9qJaRsH6KtKpL3UyAZoQJqObGBpCxE=,tag:SEYYJgHRH2IIo45IgNS88g==,type:str]
+restic:
+    gotosocial:
+        repositoryPassword: ENC[AES256_GCM,data:9vknU77/Rjh6ScJZDqv5Gss+9Q6pEZ+plXyQKNLeqsQnnpvjEd15NyqtFpj5IArKieHSJ+rJO2zFaIPsrkhiCw==,iv:GnyOuY1daKR7Esca29Mt/JDQyoL95bpjA8fmorhjsvI=,tag:HMKeI5gdnBblUdTvdFB4jw==,type:str]
+        sshPrivateKey: ENC[AES256_GCM,data:vr/VGxEAa06DMoL/VA46wM7fbI2ayCIhkFODiW33mL7qekCXObLc8EMXZXcEWoQlgT3Mpy21bMAo1GV/D7FDuZS1t7ISs3pY8/8EptfEmABA4Rm2m5vPZlzfHICOIoIJG4COH7yujSG15TOtjCKr//z+dcRzY9LGTse/pjieHA932e0Uod39QA4unHzTgvhDYiiuj5nEwL9qAiRIgW0JTyOypxMlQoKa6Brx3NYgH9swbwudIH16uZpfj8vCzjuWPfvxAeEeW67aGl+GPz2H+cxO24Xv+HQYsgHz/A3JROw4lREOh/I+AcvxTyHSUTw9P8oA/+ffcfOFVlzAoHKmA2FbfrF7FqbAKehHBxxrPvKVREzfEChSnKJOFIQUqkfqK5/n9i/MM031OmEuDCzvO2ycOmrHzOWeBE90jt4whEH/oFdGCXchdOBVxn923W2bTeq1wS3M/NJA3Xo69YIW2NNB5jr7V1vNpXGuRaXLQo9qE5LKVqUN3TDR9xDDoKEbADToHBKpdg4ohGSFqaZe,iv:HAULIQwRnxL3DeRxTTblEMqxTmm9QzdpxpK9BjGZXRA=,tag:vCKGQsqLoMWcXzOcHgDPpQ==,type:str]
+    navidrome:
+        repositoryPassword: ENC[AES256_GCM,data:c4iZCvyMI/WSvUOfkqVs/MVnAx6BRup0c4yeOUhuH1GLWZln6/m78Fvbzf/5C6kHqoexx8ml/3m2ja6ZtPdwtA==,iv:c+HaRUzkqHuh911ygmf8O1qDc7h7sxbM6kXRh8P3R9Y=,tag:8PC4cxfj7+5n9FgrfU0v+Q==,type:str]
+        sshPrivateKey: ENC[AES256_GCM,data:IeBsZ8wXcm9Mw43DrREprw7pJoeY6AqnMjkTKZK27R9B7GHa+rTPrh7h+cDWycpXK+c0IgloH2LeZBnLxTmNxUwMYVFqq7OE3SMJ8LUEFvwvjgGOt+WvUqB2/AWxqReYdiGcydEfNRaCX2tpX4g3hXHl6b03/DUe6Gm7derAQSIpOvp4QJL72j8qAch/8vGBTuDuNhXzzG9AIWZMPPMajOeIOIT0EH7WVUwDNjSFEbR32wQ9ZKuKAtZpCbKxoo5bLB9QvO4tmAJBvcZgW9LWKZ/5IpNgqf7dqQABjqINUrdo1+K4B/Vs4JuTSA94ua2x0Y6NoxNSUJaSrn87/ERde9uWEBBH0m+TFemGAzEdoU/ILw4QwJnmbsw/cXe1KBkhuHNlUZ6+UwD2tjApgOb/NDEhXvwIL37JGIb1nxayM9223fGLdzi/e/futIAFGzbkjrMIvWuK52D3kyi1NxNpqv4v3IoFWLVPkgDDfzAk5rlBDFQqgYUKHeyfrmEAiXDZsfwumjRQ6/0huJHTGkY=,iv:mqLnoqIx4BelFPae63Ilf8AP9qu7zmrhkWcNKkSMWIs=,tag:KBgs6eynrPUM/qsRObOywg==,type:str]
+sops:
+    age:
+        - recipient: age10ujnqv3ttdw23f7jryjg4dtw83fj2wf275kv38f2qrtj5kk3hg4qf5uf3h
+          enc: |
+            -----BEGIN AGE ENCRYPTED FILE-----
+            YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA4QkhEM255Sk1nQ1RFd1Bq
+            R3hLcldmUGhOa1UyMCtFNmNVVDU0NU1HQkM4CnpZdWZtdXZ0bndReGpqckRWVW44
+            cGpXb2Z5SmF2Wk8xODFtaFM2VUVJQUkKLS0tIEtrZ1Y5eFduUXVyWVpVUDZ5dkN2
+            SjdmMEovakpNOTl1dzlhc2JFTzNTNlEK0ugZnzsj5hWD1SBMZYZk5rEPgvS7WlDy
+            UAPa/Y06FzG62Ku3rLLwZuF4vqFekx6dzo3Vo1F7hUb+Xz2asWfNWQ==
+            -----END AGE ENCRYPTED FILE-----
+    lastmodified: "2026-02-25T19:53:16Z"
+    mac: ENC[AES256_GCM,data:JuqlibBovsR42S/f1RLTrXWDTTSIlnf411YfefAxL1EE6sdt1aQbZldNlj4TthxILP++AqAhJYtI3bbdS6cGRb/1FLMqGUrGS5XlnIzFmG3Uly+tNE3iORfJvb57dp9jIiXc1Nkjn3w2mB5Q9RmsAcQDp0qhQHBKPRJGQIx5abo=,iv:EzrqXzFN4q1KO6bRsxiY5Vb5D7juUHuRNRdgvL0RLNs=,tag:AcWQFrHYlnEH+zPwFHe/Rg==,type:str]
+    pgp:
+        - created_at: "2026-02-04T22:47:37Z"
+          enc: |-
+            -----BEGIN PGP MESSAGE-----
+
+            hF4DfdBfTP3jZzQSAQdAYfJeJ669XAo/QnNKKRONlvpQ50P2zTSu+VkLj7oScDMw
+            M5Wf4gOHc++EkR02Wqk+FUFYfx027UbKtuPhnEiIFOm38O7iloJ2wCuJ2nCpUbql
+            0l4BO/4Zur6WWtzTO0Ui7iOAjKLFP6qAmNSAQs/JfH+EoQGU1CEWLv/u4+XlYQVg
+            H6iEmCB9FB+2Rq/2cKc1FKKYZPC4aGXRfzTRGLSzYK7wckZ6Xsk2SE/iBoeQKH7E
+            =1b+W
+            -----END PGP MESSAGE-----
+          fp: BFE6386C8D66BCD4DAE14FC895F0FE7CD7E6A022
+    unencrypted_suffix: _unencrypted
+    version: 3.11.0
diff --git a/secrets/leucas.yaml b/secrets/leucas.yaml
@@ -0,0 +1,32 @@
+wgPrivateKey: ENC[AES256_GCM,data:Hvp9TnEnuijffncHVU9LMNlNL5jsrdnDkFlDbEDPjpfGeZ6jCvx1O/BUGxQ=,iv:FWBUEtNfotGbZRoZj1xOvkiv3NsUouTRLRJy9F+v2nI=,tag:krdGEw5i7wysSD+i1ZMlVg==,type:str]
+acmeTSIGKey: ENC[AES256_GCM,data:9DaTH5dlV4aMIPI083ptjqcPUYpiGu6L47TsMFwyQtVviJcMvhdAQPSL3oMt19jGVSt3hyt4hwSnH2bFbQBvTA==,iv:F8UXkZFr53BjCxSfRmR2ja1AmBovjuF9Kh++atJxQNY=,tag:Ps3iVHcAi/Efe0hvTaLEmQ==,type:str]
+syncthing:
+    cert: ENC[AES256_GCM,data:8HLutfobxqw9AsICdcUzZpOWyBaUaKEbJWxWiR5pDGpnrhNPcsbdpm0sqLNrj0HdHhMdQP6kEnYdiC7GwgEXKQq986mwT//ETD4mfNf/LVHEJKmP6PhUfOSg3BqIOyj4Eh+euySv2anqbbtFn6zCJisVs/a4Jd6jw4ZCfzN9ksAqXmJSllhJe/QGX+7r12rq74vM9z0NzUCKjfJB1bnydKCAmK+PJQg1l+sbOQsjah1IF5Ekcae75rTVs/St1FI85edKuzEjy0Jpe1nTJZh1D9utfdnXh/6Pmwpw0Nb5Sudpp6+98/chn0pH3Ytka9LAuL2AMi3Ku4OtuWQ8tOKdf0l1atUmaW6q4FcxyGXfPnGLZUOKqYBQNBWPDfJV0vqcxLLPgqYIw7VaeIGXnl99qqNgYs65XNnzCWLL7G8r/vOS5iVEMIFzpcD4FxYP4QWnCW0SSWnaHHHy8JuELJdPPLuRjfCr5PJ9AMJJvg6gB/ZxSaz2hduUwC076hLnCkPxNc+OY5oRC7ksp4qVLJE865bWLeVg9PaLlEfDtBlCydxnv84iSAr7FyUZ8qVnhvWZyJcsxVK/putoiY2QPq2Joitvh+VafE4dCjaGfzaJ9y2gKUJOc//AZpbnN1x38puiX+sAuwo7MohOysIyjWLpqvN3E3UZXyC6CJbPX5NNYoHIGAWDVmArm88vv0l6vLkE7u4HE8atK+Q6/iaYoAIb0wxZuVDeV6lNarqMjjDrd63D2DfP9+eQd9NDT9+QPO3CUrZnvfWxDv3qdR2koX1hXjUoSSmRpCIjuMagBTxEnd+yrxG2D6yb0hA+Ph+3Oh/Vaej5+WT9+HP2F2r3m51wLRFwxWAWw+Ug08idMxykh4eX+XflWPtnqRUqt+HyYMUkadN33MTjKYGzqu9e0cRM6LgnoUsII7xdwquOc+mYAwclE2axF/maj6KmJesOUuWu/K+Gvni6TOVKwgPzhDgHthsekYVZd99iwCiugDmAmALCVV9xhtrg034R874dy58hfV36HHUlmB0BDJtDn+vqSbSsTHAqMB+uIh8=,iv:EleAtg+ncqhC8sZu41h8otk2SXNMDW59XtKx0CozbHA=,tag:MqEfsb5T9H7F8uJaqcw7yQ==,type:str]
+    key: ENC[AES256_GCM,data:XJNe9tsiItbqJ+GyTUm5lvWW+K0DYTt8gYySPcr6+O4vp2RGf3ApxW/ET0ULjVeKaBO2Io7E4Rd9JZh1T3lcHWR3NyWCTCQf+qpp9P2PT0fZfr8t5KoCh9NiSuWAm2iCOkbe4kWtZAnXJXX2snZD8TS31wozjL59SkU5x+o/MY2Peq0gAwkpCYBdgMkfdCldyF34baGzXFythxf22KUNKbvrizNbYyXGryLuqzs9m5GEFlDUQefZavfskKTfLEDz9ELGcqJfuhdHRO/gfVjXTnGeWA3mQgVCCUV074PADSSPcBLgG9LML7VawtLrCakkdXCUYCbrtGITjd28lyPZuOABm989R1y/RqYb5u6shFY+s/iBK2Mf+f7vng2DjydU,iv:WC11PUN5aiu2sPQGJFD5B1ZWWS3ivvIXlbqA67rhmjA=,tag:7pDKQhjQTGPo5LOlIvDSPQ==,type:str]
+sops:
+    age:
+        - recipient: age1enkp0mlswl30s4h7z4qvyha4cmc2n2exs0v97276q5mx0jc86ggs7g2dyq
+          enc: |
+            -----BEGIN AGE ENCRYPTED FILE-----
+            YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBzSlJ1ak55T0I1QWFwaE5p
+            T2hBWk0vZnJpcTYxUDYrMkxjaHlEQkE4Z0VJCkdLdlFLRVZxLzV0NkQvenVVd1Fj
+            ZUZhdnZ4ajJPZlJHeWE4YkhBUTg0RUUKLS0tIEh2emtQZ2QyUmY4TE14UWI5MlFQ
+            QXJ3NDVQeDVJd21uS3BuSU9qYlhyTmcKkzYPbErSC6wHKmu99a2/3TTvGeoXNBs0
+            yZeF6s+yy0CzUZaEXvMwGnXGBlngXLoFik0KIGD/TLiMR7DADsVu3Q==
+            -----END AGE ENCRYPTED FILE-----
+    lastmodified: "2026-05-18T18:09:07Z"
+    mac: ENC[AES256_GCM,data:BUJ/cI4aMYmnWt1KWaGpkxue9xP8USCuGyir/PCoZipGMHDlWIUKFNxC4BlOaH5qYIkXucpRoAsCHFBvHdSk92QSuL2H+N8cfMvSZ6PH6r8yGilj/18WUvbrrDwh/G5rNMZ8ykUkZbeKbSBcnDMGHeH1x8cY3We06j4jJzReIdU=,iv:WYnOSA/SEI/fMD7EWJHycQjSsC8Rw9L1WVST/DGxiF4=,tag:i88Kid9GVXwB4K49ulva3w==,type:str]
+    pgp:
+        - created_at: "2026-05-05T14:16:22Z"
+          enc: |-
+            -----BEGIN PGP MESSAGE-----
+
+            hF4DfdBfTP3jZzQSAQdAUBfCFV0nwrMVcwba2jkW+9lpbbSnoM91ZxOvDfrgfiMw
+            QCEWBYqoxbLUM8k3+qHhbj13hoypKdKZ/IU0cDUBAVXFMzO0vzkw0pCSL4ByT6e/
+            0l4BRlZlrVcvRvzgTzfJTowWTAMLGW3qD9FNTj6c9Tuupoe5csSLkBuwjlLL++pW
+            OvpCn824C33LksyV6GLT+0BNIHASBp5XnasJIABj7Mofuu+Wz4FvFDq7VdNoL/cq
+            =cpXM
+            -----END PGP MESSAGE-----
+          fp: BFE6386C8D66BCD4DAE14FC895F0FE7CD7E6A022
+    unencrypted_suffix: _unencrypted
+    version: 3.12.1
diff --git a/secrets/sorrah.yaml b/secrets/sorrah.yaml
@@ -0,0 +1,67 @@
+acmeTSIGKey: ENC[AES256_GCM,data:R3FFMuJBprWEqzVoUcK13NOW+InN86bEc0vjCeYKA5tea258Tj/EkNDtBnep19gf/XK+reZWVkvbgUUK3uxNng==,iv:YkaLjlAMNbjmrdD2TaHv+3Wppg4k2vSM+cbc0Y7uXls=,tag:nYky0Y8y7+cN4fpqJhH4CA==,type:str]
+wgPrivateKey: ENC[AES256_GCM,data:Iy9dmlJWbELbHHVZ9BSup/6OnoQvp6puHAA9qK5uyHGhbqbI/0Dj2yian/k=,iv:vduj39UMLkTa6Etzdmj/wPehSR/QQhjtqdrtBzh4uq4=,tag:UrYYNhiP3sR2hZ3G605tTA==,type:str]
+dn42:
+    wgPrivateKey: ENC[AES256_GCM,data:oviJ/7+nI+J7/WzXw0wO+ptxYl25uwLX4n+oWppXSLdHaUszaVlzq50tKj4=,iv:zxe8b49pRWNT9E0aTa2EgZaCoytPmA+pFDztuKn7nsY=,tag:RBAYBvT5oe55aSyMi+o63Q==,type:str]
+    peerings:
+        tech9:
+            wgPrivateKey: ENC[AES256_GCM,data:cUmMmVEC8USOc8BLSBfplx9UKATaRifmBJ+Df10f8O91h+XhXoGoxOD6PeE=,iv:g0m5TNPb6jqztxTFQ13GShDTO8Pnt08y7LwGEnHYqyo=,tag:Mk5VmjLCWkmU0tcP5cMlLw==,type:str]
+        kioubit:
+            wgPrivateKey: ENC[AES256_GCM,data:HyMZd3itnBA+voVRYeR9DDO+Unbr9E9QtGgam1y35sBlnZJQqo0ncydipmI=,iv:6V/nd3IWnCq40XZrf8hibzgPu8rYiAPivfbhjWmdrDo=,tag:p2eVNtQnrPzosqodtBObdA==,type:str]
+        pleiades:
+            wgPrivateKey: ENC[AES256_GCM,data:iKKi+kuUbMLOohMUKXd5T0ZF5Sy3qpc7WKarF+PTFEz3Cqg1SxR7TV8nLGs=,iv:wpKdAqE39a9kDUmSuFqXQ3OPvtw+2D+lLiJ2w63ngkE=,tag:ERAG9oKThCURk/gYceTRmA==,type:str]
+        echonet:
+            wgPrivateKey: ENC[AES256_GCM,data:bMdSdYWmpW1wOLqldpJ9UGaVfEdsgFI6pLXoNhAcxg5Kz1ET2NumuIDHrn0=,iv:lt9ROX/Nr2jZld4kKtG7Rb724jAcD9MyWYssT7t0DqU=,tag:l+ARToZrKuXv3H2CEDpsFg==,type:str]
+        tbspace:
+            wgPrivateKey: ENC[AES256_GCM,data:1Ykxn6gupvcAWQ+GqR6NdDjxZpEFTI34gIXd4l9+iSAgvkWNzMbpcWgi6Uc=,iv:5NY+jGcWW3KmI3VO+T9jxtIfAsfhLLxXYfCMZYQYW/U=,tag:RVzFJJWP+/DZXyW1qIIBQw==,type:str]
+        antibldg:
+            wgPrivateKey: ENC[AES256_GCM,data:LVy4QM7WP7+u1cYeBxuX+bwO80R/NZK/gQIApjVGjNARYcndd9ZzW6Xlha8=,iv:b3R3iw5fa/+PD8n+9J1H+mfGtLr6Rx8W0fAtzyzkECA=,tag:quqcr9BErwb6ViYjDcJUcA==,type:str]
+        dahlabandon:
+            wgPrivateKey: ENC[AES256_GCM,data:kg69gs1nX/KE5LEYIij8C+VE/INe0l8yNIG7uT1ruAkIVbcU+oE5tyaEO/k=,iv:XU1YcY5COxduz1Sj70hXjMfhWiub+0fSB5DI0aw5ze0=,tag:sGBE5X3dkwusvUBdfQR3rQ==,type:str]
+        pentane:
+            wgPrivateKey: ENC[AES256_GCM,data:XDT34TWLaBK7RrYkpdgccblWIWUSJ+6cMlyQ5uKjS0nKW9RGojjEpZU0MTw=,iv:DEdZS/WN5HwSxT33RcA14PhgtEZKS5s5QMNb6MgXIvU=,tag:J1fgIWyzbLyTdJ2xTmTYDA==,type:str]
+        e1mo:
+            wgPrivateKey: ENC[AES256_GCM,data:et7YXqEzxH6fg2SmVue4c9P++K1rvHv+tnUWT7STt5y9Vko8WKsnlF+FrEg=,iv:WeZ//v0X3cZ5K35DBCL54+ZFsQCs6BVkVlk/WIsERuo=,tag:iirs6o4hpyJF0zCfraV0OQ==,type:str]
+        clerie:
+            wgPrivateKey: ENC[AES256_GCM,data:6oxsE52y1cubCM6hebxGHM7Vq0yRKOpOvI/IAFg8j+O6+HQvH8OiMsQ95y4=,iv:O9Ua7lkYOVHO5KspnreSr1Sp4CGvODKMu8sYsrXFlWg=,tag:pK847w4CuwZIXwyLcu/L1g==,type:str]
+        etwas:
+            wgPrivateKey: ENC[AES256_GCM,data:HU/IET8vL2irRKNapdDIXPvTroLMSScn/mv4p8coQZZt/P1Ed88EOTs5xOI=,iv:7K3cLwc0gjeNhwmGS+gO7k14fN5xZ6tiiG/2bFxzcYE=,tag:42eOfWjkUzWzFQpaEfpKww==,type:str]
+        pilz:
+            wgPrivateKey: ENC[AES256_GCM,data:JRe0SZ+sStPoseuOUN5gSeMOyCJMA+455i/T+OsZUCe6dzujdZXzbV2ub7U=,iv:g78F4vDFeOxdEVckL2et/VjTTiHp60VDcY3Gz4j+VX8=,tag:7by6mA6Qo32sFj8GRTUaAA==,type:str]
+        c4tg1rl5:
+            wgPrivateKey: ENC[AES256_GCM,data:+ZI8DOp4TZE04W03EwOZDkv7zyutg9kRbS6KoQOLd4kbXNGOyU+1q13eYPU=,iv:ccseejEvOIVDNJDYJy34ya2sPegUTMhc343Cntq1oWA=,tag:8UsTBwduJiRYcBJxE6xtjA==,type:str]
+            wgPresharedKey: ENC[AES256_GCM,data:vz3GpI6RYim9WcqwDFmXHqMu9uM+KVWdFt9wT+2xZwHcZAlnsblQXIqiuQg=,iv:NZKR7NRQZSh7NE5RVKDs9wukPeeYUSRhCeYrRAbeK8s=,tag:3ETFdVP9DXqbgn+DifA+YQ==,type:str]
+        lgcl:
+            wgPrivateKey: ENC[AES256_GCM,data:g7VCZRpKMx2uhYYs7eItJvEVIfsm4kbFbC3OVkejmF1/IqW5Qzo1Ps+jnH4=,iv:NUZwrIV0W2nUjh7y0tOYodPIQpkr5LqrHVNvZ0lf8RM=,tag:FM/GQB6YJTeslMCt3xA3Hw==,type:str]
+        mira:
+            wgPrivateKey: ENC[AES256_GCM,data:xJBiuQI5F1zeMLbN2vOhD7iFC6KmWoc9iAk44IcaAgR8HL39Q2FZsgSGKCs=,iv:vfCv0U8tabDPwi9dnjrtkn3hWtjYslcIndVIhy6/qWs=,tag:yFiUM5P2cI65GAo2hDgWog==,type:str]
+        lfm:
+            wgPrivateKey: ENC[AES256_GCM,data:lTMtonR4XxE+xb2jlMvmb/MKnAnLWCW3xsQf4JsuxNkvUzyQ2aorWLLwxR0=,iv:13cFXvCsuznqCO7jTIMx77hTEPoeS5sQK7zUMVFN3aI=,tag:VnCcN0iBGkUEPmERvanoLg==,type:str]
+        ember:
+            wgPrivateKey: ENC[AES256_GCM,data:xKyTx57BobODn6l0QI2zx9gQSrIMFb9KftMdgFaKXn/sWQ7zdvNcmfAyeA4=,iv:WhMRcm70By4C+2aa1vl/wPjcZmr66wEsPArIQZmjZyI=,tag:fLAfgl2j1pXvdgciYefLyA==,type:str]
+sops:
+    age:
+        - recipient: age102glvtt2nwx4ymgjh85449exlhmzk56cayy9hkkpl82u6wm64qzqjar7ju
+          enc: |
+            -----BEGIN AGE ENCRYPTED FILE-----
+            YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBROXlVTkpCRTFOTnp6OG9G
+            VlFKaEUrMm1ydVdSR0dJVVZqUjVwc2pmRlc0CnA4Z2oyeE9mL25oRjVxbEtEOWFt
+            ZTJyOExPYnJTcmhKSjFmVXR5OUpZSEEKLS0tIHVybEx5TXVrZ3dqNHhDdWpqeEhK
+            N0NEdXg4Z0pKOG00dzZCVUVZN2Ewdm8K7xq9sLPppekHYkegD3jVMyLr5FjjKRuJ
+            Uk66Dd1gcbeON7wa8yfNINXBWO9WaJVht0hybi+hfNWDPm3KtKsqYg==
+            -----END AGE ENCRYPTED FILE-----
+    lastmodified: "2026-04-10T18:35:07Z"
+    mac: ENC[AES256_GCM,data:kSH+ZwdccC3BZIamHbBSQ3dQDmvxBwuuMO2cjRUyi1i3jd34YJHnjYXfsz4BVCo4n3n7dYwklIfXUMtZvFDqj31L5tjwsSYF6QdATpzHWWIISy8dTXb7PeFXWTFR9KZIxvKGGxCmWMUruGNeg54hhENza/1Vfz7OFY7mqpjMTDY=,iv:gbS8fy5FNl2JBW7pAfeYARyzZK9bm6PAFFc5IrVHjoc=,tag:J6ISYl6Eg9yuv2hiqImFGw==,type:str]
+    pgp:
+        - created_at: "2026-02-03T20:10:24Z"
+          enc: |-
+            -----BEGIN PGP MESSAGE-----
+
+            hF4DfdBfTP3jZzQSAQdAhJqdMbplMsk61HzXwYbIQHJdgZEyiYaaSOtorPcW9X0w
+            ZaeYVHuBPNwL/RK7AY7jQxOg2sUXJsBgP/gpj77GvO6A3JV0EnKPayDWIVKcKoYO
+            0l4BzUzUTO1NSC2yMZUn8DM+1u7qHcCqPeSpsCPM0JJBPpsGT0QEPtXdPBqv4l12
+            db8QP7wFrRDs3MGXwWHUM6eaw9zSCC/KLXTJ7K8jZmfdvm9enmFq1XQ+c9uvPI5X
+            =AzuY
+            -----END PGP MESSAGE-----
+          fp: BFE6386C8D66BCD4DAE14FC895F0FE7CD7E6A022
+    unencrypted_suffix: _unencrypted
+    version: 3.11.0
diff --git a/secrets/zaphyra/floractl.yaml b/secrets/zaphyra/floractl.yaml
@@ -0,0 +1,28 @@
+config: ENC[AES256_GCM,data:Nx+q69VQ8OdYGmii78D4sGF2U+fUUTJNxyU/fZXBWYG1QEnlrqK1ACAKVVbiigvI3uAVi88BeJFHH2mzsZ2Adb1DapyFdmU2e02dODoLbN1nNU6kSHBuZpNus9nCQKJ5oHL+JGkxHJIjl7Ls6GvkT8kTq9fByqykBGnwhwhH2cei4+1biytQugyR63hzJOk+rkeUbsohZGtraP+9H+LutarKaD5ymq+bn/s8DDR4AR66XjkS5UOVJ6FN4vPKjJmyXhTNuTCbxYM6zPYhXdhApeoY7+fAqqrUcJLc5qCBvBTrhDe3H5B84fcT8jgFSURLaOErqZiXE6LHASGoBKcHfdO9Pkgz+jCi0RQjS3OVaonMQC4WiXIu3yxEO1lvdKY23NLS6Pnc3bnDKrtflgPjaa/Y8e0qAd8nkj/ywIpWpUXOTplt876Ubi8nagQEAr4NvJZyTT0DI26PlflLTRsFdeXTPUcEuELGN0LKf2nFu3pyRgFum+f9dYESAnyusEBojDhbavA6+uLMa7QtX1bzcAqmda9AuWyuOM2HhaXzU4xZ7DEBZaJMLgcRUfjQUfoy1y6HIDR38k19+1gs1ePDwJhuOUjZ5QsuIUVyh3FRTM5vpaVZFnalsN7r3ZRG3hGWgorUxZ9HAcre9ekaoXRta3DJCLFgNQxiVWUM0XOMV+Q9uBsDkH+cDTnx3Jr0P/lIK0V1AdAMzRtCGw5Y3V6RqU16+kDZHdDZTahmZmsan6EiJ0tlPDB/JLfpCY8wU0GYfxushEEowxrZT5/3fWV7NW4y9PpxaiLxiiaGK7z6j4iFxfM/NmkiGsGiYj2j2tXH6ilcK3hCOqzsQvVhAHQPVjGYGAiXPuHFcdfQZutWyHNsuEpCglvuT1W06x04O79hRa3SzIEc/LidrP7a4uvObvqASE21WfxcwHuHmN5hpRk9Pl+XoYUWZyQq37kPrvDxY97HZpzz+xkly8kdAOXBTnvNd4jXxUFhfEUr0iYAVmOjDkXFpdqIJhMH0uYkYW4x24V0+wZCDaTb9Wbx7L2y8nx9GDb+uWlghcQ4vXtgIg0jaxxovoomTpHsBh9JoA7e6RkZxUTpO3aIncsw842hbUiX2CWdf3FC+HbcCIl0PKsuktb7jKc3Z16ZECmCqITICpjWOyNrOV4m5mpmVnvWwdstznRgK2cQGWLmGhSVrtlz7MKP8vNFBZL1rU3H0wi95MI7X9eeD6tH/M7/66ARA3eho9O9l34rSksyUy6WX7QT6e5Rldp+Fz1M9nTZxxtvMIW4DFAX8Gp0ltVa4xIw43dwM/UClkDq2/bxmeqLAOg0Rj0eh8Nc5zK8t4yTJd2+3Aqez8ormAkUDcxm0rcYHR47cDvKRGbR9XxZyIp5tExpfU2laQgz+9AsHRnry8S8m9Pnya5Me05tA1xhjdwhpTGLpzYgoZxzYvBLN6Thy8GjVzHU3eGA5DSp1TkGtI8c+5LC5F8Q8yvhmjOvqCFXepuJqVfLYOww7OoODjnyUPsPWVZfzGKLJN/xS/dowWyUY7/uaz32dDqmJ9AZ5IUn8b+xFmXLlk0Ri4jGCunP2dvRQJFTQUTP6Kq/mn/vI5dpsXaIOMRKgoy2UDHK0vJ8cIl+Q4MjzgT3ZronL9Z6koruVfUmMzIfnHOmS3zO7FBBF33iL0VoNIlazgDDLWvfIMXNAlpIroNrYaeipt1TBrqq5ynCo8+/yhqANq1k+W7mK7P87qcggcynHaFNREt38fvjMTaCVLRg3VDmKKStECFpLEl5BGBkEAVQZci6z/9rtnek163tX3Nl9tLVixOrSgKklofjJzskK7Jd4X4U8Qk/3msBAUs0y1MRxc3xn2vgggAqwcss3FeozHsXGpxfdPOn7AeaQPe4tYFz/RQpD4PR7nMCxIOqw1wJG6+Vanbk2+8/7nMjosCXj2JmxdjUnatJQQ/Nd/LH0of23IyneBcc0nDm0EYbc1hV3266nM/eZAX8duIRsjd6h4SEzDeFlMFWiSXnt7oTHX7PRNFtqk1c5GoZIFlSlTKpn6Ya8nmoM9FjX//Dx/EsVMAR0e1DgGwgzEuzGXeoj/0PBOq6MolJH2RO2kBOtwrk7CO9uP3F/bL3jofdeyLBMc0Kzc5jgJCwXVmPQlNsBV6cSi+IiTh4ZH+L7j0VJZjCvLU11oBQUTPjjpAPYLf2dMMkCkQifVxdjxYCHgvM7xgEOvOZUHcySuEwGJ9lEy1aXH6QCR9mAkirJAhHrwuJiPavcKmBQKtD/B4y95YEPKvHClrAFKtx+ES/weFLhi2IZDLwBvWZ2w6vMy767x5vuUOtHHXWyIz15R4Nwgf7FzYG1WEXGusLCbBHR/YtOpw4YQ/lC9jBQJnno66uGqT0kNVXH2LpBZxXGR06zbyuXbvsaMlxcjvAT4rshXVx5jtJ9onHrYCJOm4cU8eG4S3D/r4+oSV1jEbWm9cl8zdD3N7ORri7q9zEWtxEJSwEKw5I9JNNHzzpMPNZZWtbL90Y+LZ8dHi7DwvyojMI0O1cTgiIDChofq6/hDolCC6pkPYPevaUIpMtaxPeVejuBrmOvhC84LNe0O9vyZIHeQ6EPqAej50IdIA8fyp+j+BL3/CSIM3u2RVY5mTwO5EF1mdHWhb5q1/mnzupkKwa4sRWK/uZZ/lHTnjKfHWrgbGfpBhLPwC20BuPATmI289LoKp5HCRpCshgaf71euOQ+Wl+aWHqlls0Y2mWzz7YGxcDPOhM7Y74DIRR/BJ3owHZ1/vW1RCar6VIdqJkg9jhUkDqduCUdXx1vHzgXYUg97kt1BqvIhvbY2BQh+HnznJ+YHMTpSbwuxgKT4wTlrJ4nOz7jI2JrboFSD6sWhb+it6eEN1btB8UNG7FQawQo1SY6T/8W5WVlGdy0PJcUbKOST2kXw4aZT+76dZzM1z4w99iogKJ2xsx0uN+kPsoDRrHBQ0laCiDNw/uDwunXchYvzXh24ok+msKDQ7Xc8iqHjnlmKcVHrIestRbGreqf0mFhUyYkp0/Pcqo+M9/bBxT4jcP+79MLTiuzNGHLn0hAR0//OspCh0fSwgermG+jMeud3/u5VWAGw+BgE/WJM4F4KG8gI2JHtxYugJvdjUzkBcKwK7JSUwbywvAyQEMbMq50lTEIz8RcaUkSRXTEEc5VPoI1IoKsOcoAxvdHUVdKx8Eg4062yc1OqILRBCEVSLqMIquAsl5Qo4n7uLSi74apOUQpwoPrx+HNxNSz2KxTX7Qu+/eA0RaVG8hpI0N5T5XShAeqaaIDrzDTqbnNoH+mg3NamR1kk9oWJTn8tkz3Ku5dBGYExSP9r4lRI8OVCTQMit+5WIgSfIugzsqPLBfxm21Et1d1Ugn2T+sXLMAP6RTfOvU89D4JewFx86LtSgxSbVWTkWjKe8HC0Qq9Pmwsoi9TCiVBBtNj57g75lHqomNtDWbObAXmNI9LfiHlncSC6vEC7KeD2tFui9lk9S+1tYAWlhmcP6WvPWeOOu2/qIlutDrZ2XTuw4SqIZxJi9Ylsw5DKlCWRBtuoxXULulv/zhh64J77Nt5k+Pw4Sg+lzbOj16/GcZF24/vfReyx6MlcbWrreBaTKC+1qzN9Bzy1/mkIk/f6Pax/aovsxFz6KuT5ZlxVqNHI2ma4FTe8ePri0XyXgbxSnyWrIuFhp/CKVIKCGpsIe2+Q6taI96rNymHgVphAQsmAmZrf8fyTmhkc51TrahXWeUxKGjUvAYb/I3amT+QVpxLzWKm14RQ9GBY9UTSHYz4aWmaoM1VjqUBYfLxiXE1rC/eKBbvYTDxZsBJMr8ASXqmOf+t4+C9PgxqFln4LfltHDtQYSp68yvQAnjl+lGkOJ9KS/p0YmlrpF2uZYxwz+DlXXQO6AOq3+3R1QRy2RoWLILL3hGhQAIncpYJ5NUySDDcbMzm81EY5C0T7bGigZ1ysrgAqa6Q5lJGRQMpJBcdGMbANKjdM8O8VKV3JifNOSWgfRtYu87ry77oNCgIsUFnAWDLvPDrzCTCFJRuW1rZlY8xrTAYtR3W58YfEnxON7yZOZQQbL18IoZyqAX1sdyTeVsIljTykUVONW2FKPC3BsmaHKYJw4NoTcC8mv8F9Xw4bpDVE4Em/Nf/Q72PyH2t3pavA9VajJ+nAer9iiXe//9AhK/vl6sb4marEW1DmfbIG5Qdf90XXT/uxuUYGmHHUFiGBUe/FKJYEQM+YceiLTpmh4LKzr0mFO1GcYpc4iN91y4nlLNFQoBYBe/DDh2bgBia8uvNpPh7cj45JEDpcbz7X94x2U8WilgLugZ3QlTGKlepd4tDSjk9RD9yagbmhYkWbGVdhobRf7z6zMco2jDXA2fYJxBW8Pf+LsTwwlni114m4s0QLHH0bbcUt35lPnb5LxAYOg/qP0r1mRNPuy9zIh+cHXdfIeW00tqso8fwNPc8RY2theMew1NLVbCWjeZU9w5f6zt+kjFAA2yz27jG2SENjU8faygWEXPM/rwGlJySsZQBAsvGeUwagyNWkgippm2k5+82n0BOMQrZ1ma4Um2FMl2E3CqPXHOhmB3QbNRdcZDgHSwmwRyXO86+mXvDzDptSdrPc1ulm3EvM5ydcxJamthsKqOYr8px+hY0J6hpQDSBACoVyYuZ7EqYR6sbk7txVzHDpj70G0iK4oTMPx3r2FljwyRopQmiS6iadAAMRMTMPlxwKJ23/GqSeC5tsbb8YrQpjbc6D+P0//tM35NCFWSoL5MxGuFDXxsamYmQbAv7TMAGiThJWC3B4jJsrSb8yTbS4Hkr/JAwGljFsRjfhuCwfr0MyVjkl99/OLTi8J3z5s6DUlW6a1KzWQmOPcN5y1Lw6lZXQIv/UJK3gagfrXT3WSkqajiOA6cujY1MQVJG0Hx4yZG5G5LnVfgwpeCm3fpiqjT+Vj5grf3sNr4FNsWmDqCX73IdRiLUuXQup9SsiiSPSkdChzHXzfjVb9BVyrfYMfGAXl7vS9RyuV4hd/mFEAruxqnpCCPguzIdEdXy0/eZX2HpbpVmNte7JrTUcc5u+oxVKfASwX9vvLd+NNRu2GFqUwyv5x1FkQtzr44otssKBKsGQ/4qUA363Ek+ZrbI6oIPJPZJqIiYa/kl5cAh4LgXK3pKmz4QTFv7LddtNdG0Z+UgVE8m4H89xrbb0Hp3rZcMiR38dniilEKt1zEJxInceC8RaGUBpMSwlq9gACxZ/m9A61Pw7i/qUkDRkzeNeZOEUyTyCwdlILJof6kvbzxmTrh/2WqC2Wi4Dg3VCL/Y5dLk7WS+fkeK6fBeH0Z7S6uf34h1KLa8f0Kul1uAQK4U4nPbF8Q4pYXhxWylYyvB0DQjItsQxpLqP/A0rltTWVuc7kAcQayL+McuXIWFWIAVA8BXmosittp8oLa7ZU/FR4WOudAFwpK9YUOgLC8vg2qnpy9T2xaN6NBVuKz/DHbIvmzeax0xtt2l8TlNPAX3W0OUOtRjxMCJ++MU8JKkKsbiTD1CpYgAwvFb/0NlDzP6KcuOf1BLD9ntqquK/DJlcakP1ez7YWdN2QTkrTz07PWg5zonusGCnNq/KDVKAw2OK08SYHDvr0TDFRVpQUYhS9QAIEr+R85gWjArbaI6lfQN8r+yCDiQSmPxa2ooakTshvujjdsn4DDMYjbjsU3k0kfdIOQ2pOJgum6krWISR18u4zzux6Wc+9DbbcGaCs3hwg3D1pw70gzl+VAytscOYgW0XcAXY/PmkBz1X++h8YujKn8Y2I0HTn4YlssV7QzmZ0mFx/fweNVdgssh1pg9OaEfdoWpqowLa5pumEv1z6oI7Uvws5et/DnrY+qbl3xTfb0yJUoNc12jSNhy9+4K+EgcpttlD/E09UVlAVKcSckKbYxDDXBMQr9yYgG6W5AxKv3SlrrJIejYijPZJPEH7FA4K0I/zOxsPODkSVurCGRHB7WtppY87K1qp0b0EUrnnNA3Lk3edzZR3TlOgDq9UCnZoePUJOsP+Kw88gNxAaK+1Hk7GTLEsR+ATIbQG2x7T/oe/zzKxV7MRA8fPJx7/8NdvkC3uEByDuX6eNYIiYcpYZ8EKRbsfrvhwANnvRS+Vtg6+OOHkJqdQ/0/akNPlNpw+7ICmLNmmVpJqPX,iv:cR48sW0FzGfU/nNyuZ6eREbr+/viIkCoXZvV8gcArgc=,tag:MrEtauculvnuW0u5tLR3rw==,type:str]
+sops:
+    age:
+        - recipient: age1wztgmu7rm4yvdr0x8xucwfmnf0sruafwjx2ttl9cvg54epn0qysqqnr5n3
+          enc: |
+            -----BEGIN AGE ENCRYPTED FILE-----
+            YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBVaFJub09Kak53SDhsQXlU
+            akg3eGJZZlVDeGcyWG1RSXpMN09sY2IxdVQ4Cm1rejJEUEdtcHFwRldlTUxRMUMw
+            NmFVOFpTTnBsWVRLQSt1amIvZHVzcUEKLS0tIENDOHZYdzZlSE1sR0ozVjliNytx
+            QW03MktTTituUko2MU5zQlN4dWptc0EKjYAzjWpS3lxsjzBs826SUvzUhmHNuBeP
+            fwZ7Cagz2decze2sVWN/r283tvX1n1sALXqvrX8LzQqVwCVgQsXeWA==
+            -----END AGE ENCRYPTED FILE-----
+    lastmodified: "2026-05-20T09:28:22Z"
+    mac: ENC[AES256_GCM,data:tKxBymJbW/cqua2taY6pct4ndHDjzBAIn5ptc3YOlykyr606VTYPStmRyBKd+hx+98eq9v0fBJJxm/HVA+6VoibLTd7dazZWk4giWfs9kK2tQ5Y+v3GM5k+FGF4lqkcxXl/S8xLaBvTd0p4S+Lw480HaiszNdPrCS5Uxw8mIw1U=,iv:4QQQdear0lCHEutdhplF1LyHPBnjJeL6F6ynRU9Jxes=,tag:f//PisfFeFda6DIu8Fr59Q==,type:str]
+    pgp:
+        - created_at: "2026-05-20T09:27:49Z"
+          enc: |-
+            -----BEGIN PGP MESSAGE-----
+
+            hF4DfdBfTP3jZzQSAQdAdGYhFuC+8CgX6JRcJwDir4pEablD1O1H++teDzhvVgcw
+            +JhIXSeBfWJCSFoRxI8VlmpC1snWdc91Pgg6S5db1iFP4yY+svNE6ve4/K5tk4a5
+            0l4BKWXSe79bqnWMlTGU7/bQRUvtPlCtVrqcLyLBSccBaHJnoa8BzbVTuquY32DG
+            TUqq2nv/lYhEDtJT0tMSEqSI2CcgFuCq6nseIhKAzlurYdNPOWKIlB/uZR4QPJOV
+            =LRS9
+            -----END PGP MESSAGE-----
+          fp: BFE6386C8D66BCD4DAE14FC895F0FE7CD7E6A022
+    unencrypted_suffix: _unencrypted
+    version: 3.12.1
diff --git a/secrets/zaphyra/sieve.yaml b/secrets/zaphyra/sieve.yaml
@@ -0,0 +1,28 @@
+katja@zaphyra.eu: ENC[AES256_GCM,data:Dz3h+Cck4H0rZWd58CPy6oj2qt/hkfVxnH4TUV7BDphfSpSwCJXsbQOOexONHot8heCjNqXb/b6f0cUZgvnP9EmKa0fQBE8e4wHY5WRcLKdD/MBJ0jgUcGcw7en6vBptGxU4rdfAC4SI1bvivXn4hg1ST+we+LDzWs9lch7sVdVyX/FY0LtILUPUkls3zbr3eEVMPi6bY7xdWVLWzv5EguFZdPnUGFYpRYM7oZ29VClfFYoFuabE+1P2l0RFAIaKw046W+tvzQ+9eTluGBtCuj895iYDxHEQNUIyo8aj3QYDdsNDli4MnhFJrfMqmaFEzlTcWXZ73elp/1eF1b3EtfWx8dXLL22ThquNkifT03EqTtoekB1vXka8Aokezz7b99iZVPBkKUI30+wn4kkgj2kbgmlQ220Fr9xBUjud3g1lzXG26QIPb0KkFjLw5XgYX2bQ9FjFlllXcCjfOLYxvUc+/jbwL/wuqalxBic3chcnIXnLuDscDmY7UMctRjZjkcSQ3hE82BtLiDUbAejQOKwWeLqhuC7DcjYekhwdJs6ZmTFw5gaxayBl6c5QB6pa4DhHHRD+FhvsxyVSNyy2XT7xIQh/nBOG0gaE8mkKpQc4NVIQCUgPl+gNxB1zA3aAYRglrmwSz0pQJy6cQZ14gmEyDUZzlBXHLLmuigleuFwWWeG3ffnx4BQRV7bEcuGVbULeahwRwQGQOENdIw7qvTUObf6lzE/zm5z+68xAvgYFwQKXXl2+YHKRRa9xVoxawGkStYzHmA/fiGk+QiQekxX7zcBYqzlAih0vVvQRGUls9yjuuTpQzy/r7pCBJEVWqSvOZnYCZHZdLZBDYRVmf877yGWond95Z5jivzzOMfbiJCu8aIRLB67pCLImV9y5IoXq4s3/DeComSBgD2LpT86CoIqdmPEFJjoGw8hfKxb1j0Z0cpxI2P+TEqOap5rWDyF3kFHQilo69tmzayVhymA8kAGl+lna88j3W8/S5teq3h8DY5rixBA/v6D57jRNUzvnkCtoMBNBNDdatNwzPczr9bg0IRzsiwqP37e0DDUN4Y6btKvQv8jWbyw0mFGbkqRD8madZWr2D0gJm88RBYXj00ohjc+WktsczCV3KCvv4DXbNW9WkgymGOHqDhediRjktT2C4fGaNNB0ezIWGRaYB9HSciAwIRwd/ia9zYSKGcO7jENYvSLm2T4P9/1U7Gr7fO5LtdgK32UcYJ7Qx1nXK1tbZLfMHhjhuD4zsp7v158kVJfgKM07/6XZT2lQB5ozJ4C9oz2YEjIsG+AZJM/S+vUJ4JQa/1t6tlu5vcTAIn8Wi8G50fUN49jodWS3M/Qhvtyo2iheTsSCmldiWc+WCnD9J24XNnxvCdm7sdtqEGZ84vKbn4ASVRf0n8UBj4GzH3UaCeTB22VmU7oOcQTwUom4iU0oyj2DAL1tpzhuKoVcd57ZzVcqwVfDsDjfYiWiLWRww67fkzTOrNL6RfPUD82ciuqP4laTqgcYdL3WRVKZPs50lA8ind+7F9XGQrZBF2iS+L7yOlZ4WQIViWxhNwmOoU8FFlHO404PZxM5FfXvGwD1eQ1WhOSTWEvQJIWB9147vcF+JYGiBHnAr7MTtBTF7W6erVgelZN5pT622H6ETCUpKVB46jRJhGSxeYqKiCic60pLNgRCcT6K7qpKMrzhnoNMr155XYHIcbo1v6ma1tft+gTGauK8h6A1UMlWiKkRaDkh424wIcx96tqerbELOLYheTdpM6BkAQ9EcSd7NcSNXRJiyAtyz2ftLz5bqdhZUCJt2TE4gR7DaqK5Os6OEd5ke2BzsLpRoKJuEM+VFhpuAW2xA34uD6kD/XW7jsT9Gm8rLxzrQlxsfDWAuHqQSkpTf+7jvJs7Ycrjq+Vxh37CTg0Ug/lquYnzQjhJkDX5+RAo95RJuxLv4y5gTlnpQrbtekEAHOiwOo4FriIvbDAESWm0codoCXAhAiP/5dEXYwXtQmrnl6sx8Ea7jMvhN6YpCu7WMaApSmqGqHuyVLcjc1izmc7JeuAtPWzUUNqh5OsWgh3FDh1YfDLTEsaPWDh0cNVMBSl+HgtVhnsfLoydKbd0fiIXEr2Lm7peyYze+F3hOKd5z1DlAYAhm1DojsRu4R1YSvE5IyeYE3BugTDaPX9qysdLI+TtCtYghwkhc4YH8cI/vR/xt/RUF+4W3y6y562PjNLMcgGAtUDzVc/mqmPQFuVlsZ1C1bkw2E31OKoCKcDutyoLbcOcKq6oFToL1+VGqRbqy8Jc6WTSj+hJP72uOhCXM44G0Ph90kl6KkJoi49QhVYaqHcDce0EU9La5WjagP0uJPpbpLk63HE5yg1H4GLhTXsJRZv9jDEEtv0CmSiNIfgs/Sr7WCEve85udOCQYUgsagd4/m4mVw77j4LwgO4uQINZ6RB06pULW4tJbOhPZCG9AgckseTGuYEu1JlI7n6sOcddDhvTeIX4nmGIuyt2AcAP/KEd2ImTH+ujEfCnnlTbJPU2V5/04utKzTeGkaoeZY0VCaUeIoS8kpA1pN0LOk4uf9MxutbL0yuoo3vF75e5pLbA0nxH2uyNLFTcltbjsmZR794ZGzosdUnAoW2atdDitrGPo4ndNW6vIdeIiky6CV8uDJt1zwcdG/PE+YSh3Ytg0v5UI2G4XfuFD4GYeaXvylUOB4v6Jft+eYx64h0MH5Stue+fY5rak08i7GhtSKrN/9K9Tx5vNZ2l+SrbKaJjWsHakDZkOTPrcl/YGKT/var4CP4t4XMnoKPvU5tGxR6wccDyklhZLF99MNV52iJh1PBI0AL29YP1CxiJXztfmFW3S00o0kBpWMd85kVEH6k8YDxsCc1qedhQIyAXLO2AcX6ynV3Jeig1oqZxck0sOO8zwLStPLUZVBS6TMEBChR+60qTXFVr6DvUts6+rCIvbz7P/tFZFMVt+4Hxe1CqCjnhME+FxIH7PmTibfeeatmNNpNQRaM1FkVd5OE12WYOIDkUgqIk4WauAy194QbJ4WJ2+O8A6M4deniLFbN/JT2EEUK7tfrD0EMVGZhh7ltpJMc3uYB9HekCwZm2Ozv2A4ecsmvN+FSEpey77vrulF0Kxx9/lqkZyq2LEFKehjDe8LbtGHGU4GsqI8SrHIYL1zP8EwbvMPgsP6NMoA/XsPiu5GzoMjj2BmAME+VJqdTO9IyFsLfml0lmfXDpfIHYu4ZoJEXdD6FaFhTThiNSi4WH9GcOzDYREZdJESeK02zmc07CRun0Gllt3wX/KGAf0HHBxMVxJ+3BriKxFlYsWm/7tmWTsxnXI7W0sz8xaSrV48lMe06hez58IR8CQBpBCJiUeMNhTQ5XhlDjWOxS66BSmnevn7nHhIKdUZQk6ywyDjgqqSNeeAypOSqQTdybe9Et7rTUyeGlNvl8dAdVN6niE2e1gzpcy+iMDoXO7oi1JSgTJ1/k/9fr6iSOM1+ZkFhMqWv0dH2YuHNE80YwYb6P5RQJ9gXHTMaPBS6jh/dZ5rwJ8uRjda0yYbm2vUehoTDj7m/84JoyvRNcoYh/clYkNtSx3oFYN+5uH6xRfEE=,iv:FicnrntRYIa/LCgNuy6YkkTwDmBEaGw7XvZupWy9CJA=,tag:PDPiZt9IwpexWcVX8Qqk+g==,type:str]
+sops:
+    age:
+        - recipient: age1yln5qratl82u6sqakh9gn2c0s88nzm62cw9muq5z4kzgp9wqpyzshhptq9
+          enc: |
+            -----BEGIN AGE ENCRYPTED FILE-----
+            YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBPajN2bFVvMVhZa2tLTCs1
+            Z3lXajhENEhUZFV5RWQrNmE1R1ptRlFwOGtNClVtZlhvNCtmSmpON2ZJUVpaWUhi
+            Z0psNm5BQllIWk9VYzJwbVhyQ09jL1kKLS0tIDVkaHJCNDJQdTRVK01lZ2ZrWXFy
+            MUNKWHRNRDczbzl5bDJyd2tQb0Nld0EKIhBC+bX3t9mxRSW6WWGLANeQQjOohfod
+            D7q2Aeg0PtmVQBtcpw3B9sU7WkwBtVoRq2i7ihiHvmu2acoZHoccxA==
+            -----END AGE ENCRYPTED FILE-----
+    lastmodified: "2026-02-23T18:02:40Z"
+    mac: ENC[AES256_GCM,data:9B9Prk7mJndxj4zb3m/fzwRCp8WbWnQrw2tKnOzJ8IhGcFm2uFzTOEiAzPomOxl3hxpU9CtC0lnH04/KFL6ns9YpldJFrPYH8M5dA6pCh9Fbqcf8AggzEt8FigE0oLxPRZ0yG8evHklIKXumVD4BwpzaXduEx5P6YvzUTy6ODig=,iv:oOvScRu0qb1Pggsz/6Ic/oKthNLShagYiJ8AGAlaFz0=,tag:biplyLglpilD9NKYvao5Mw==,type:str]
+    pgp:
+        - created_at: "2026-02-23T18:02:22Z"
+          enc: |-
+            -----BEGIN PGP MESSAGE-----
+
+            hF4DfdBfTP3jZzQSAQdA9cFc1G+OVNQ09ygBsjgoP77us0T4LN6mpV/UCWQG30gw
+            CIN9RxivwgAJ3iV2uRaRVWk7Ww3FBgjE6HHa/ddV5ItVDPhraZpQA3X33XueU0ch
+            0lwBvexhVoQn0ySW6OFaoRavh14YYFAaDFow+dCQbjSN0OY2wjyRj4X7AKdvXwAM
+            19ZtJEZWtho4ZauvuB+EkRXm4CWsyui+gBbc/fTwDxGT5YG7HUEiAYv8t4U5aw==
+            =OXqK
+            -----END PGP MESSAGE-----
+          fp: BFE6386C8D66BCD4DAE14FC895F0FE7CD7E6A022
+    unencrypted_suffix: _unencrypted
+    version: 3.11.0