about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.vscode/settings.json3
-rw-r--r--Makefile3
-rw-r--r--README.md13
-rw-r--r--flake.lock223
-rw-r--r--flake.nix127
-rw-r--r--nix/hosts/caladan/aliases.nix1
-rw-r--r--nix/hosts/caladan/darwin-configuration.nix28
-rw-r--r--nix/hosts/caladan/emacs_config.el20
-rw-r--r--nix/hosts/caladan/home_emile.nix48
-rw-r--r--nix/hosts/caladan/nvim_plugins.nix21
-rw-r--r--nix/hosts/caladan/overlay.nix63
-rw-r--r--nix/hosts/corrino/configuration.nix223
-rw-r--r--nix/hosts/corrino/ports.nix13
-rw-r--r--nix/hosts/corrino/secrets/garage_admin_metrics_secret.age7
-rw-r--r--nix/hosts/corrino/secrets/garage_admin_token_secret.age7
-rw-r--r--nix/hosts/corrino/secrets/garage_env.agebin0 -> 602 bytes
-rw-r--r--nix/hosts/corrino/secrets/garage_rpc_secret.agebin387 -> 387 bytes
-rw-r--r--nix/hosts/corrino/secrets/goapp_oidc_client_secret.age7
-rw-r--r--nix/hosts/corrino/secrets/goapp_oidc_secret.agebin395 -> 395 bytes
-rw-r--r--nix/hosts/corrino/secrets/gotosocial_environment_file.agebin765 -> 765 bytes
-rw-r--r--nix/hosts/corrino/secrets/gotosocial_oidc_client_secret.age7
-rw-r--r--nix/hosts/corrino/secrets/grafana_env_vars.agebin431 -> 431 bytes
-rw-r--r--nix/hosts/corrino/secrets/grafana_oidc_client_secret.age7
-rw-r--r--nix/hosts/corrino/secrets/hedgedoc_environment_variables.agebin907 -> 911 bytes
-rw-r--r--nix/hosts/corrino/secrets/hedgedoc_oidc_client_secret.agebin0 -> 454 bytes
-rw-r--r--nix/hosts/corrino/secrets/immich_oidc_client_secret.agebin0 -> 454 bytes
-rw-r--r--nix/hosts/corrino/secrets/miniflux_admin_file.age7
-rw-r--r--nix/hosts/corrino/secrets/miniflux_oidc_client_secret.agebin0 -> 454 bytes
-rw-r--r--nix/hosts/corrino/secrets/miniflux_oidc_secret.age8
-rw-r--r--nix/hosts/corrino/secrets/tailscale-corrino-cert.agebin0 -> 3210 bytes
-rw-r--r--nix/hosts/corrino/secrets/tailscale-corrino-key.agebin0 -> 549 bytes
-rw-r--r--nix/hosts/corrino/www/ctf.emile.space.nix21
-rw-r--r--nix/hosts/corrino/www/git/cgit.nix6
-rw-r--r--nix/hosts/corrino/www/git/git.nix106
-rw-r--r--nix/hosts/corrino/www/goapp.emile.space.nix8
-rw-r--r--nix/hosts/corrino/www/grafana.emile.space.nix245
-rw-r--r--nix/hosts/corrino/www/hydra.emile.space.nix7
-rw-r--r--nix/hosts/corrino/www/irc.emile.space.nix345
-rw-r--r--nix/hosts/corrino/www/loki.emile.space.nix64
-rw-r--r--nix/hosts/corrino/www/mc.emile.space.nix153
-rw-r--r--nix/hosts/corrino/www/md.emile.space.nix49
-rw-r--r--nix/hosts/corrino/www/miniflux.emile.space.nix81
-rw-r--r--nix/hosts/corrino/www/photo/immich.nix39
-rw-r--r--nix/hosts/corrino/www/prometheus.emile.space.nix36
-rw-r--r--nix/hosts/corrino/www/promtail.emile.space.nix114
-rw-r--r--nix/hosts/corrino/www/s3.emile.space.nix105
-rw-r--r--nix/hosts/corrino/www/sb.emile.space.nix114
-rw-r--r--nix/hosts/corrino/www/social.emile.space.nix25
-rw-r--r--nix/hosts/corrino/www/sso.emile.space.nix29
-rw-r--r--nix/hosts/corrino/www/tickets.emile.space.nix2
-rw-r--r--nix/hosts/lampadas/configuration.nix76
-rw-r--r--nix/lib/flake-helper.nix41
-rw-r--r--nix/modules/libvirtnix/a.xml259
-rw-r--r--nix/modules/libvirtnix/b.xml183
-rw-r--r--nix/modules/libvirtnix/config.nix583
-rw-r--r--nix/modules/libvirtnix/domain.nix2056
-rw-r--r--nix/modules/libvirtnix/os.nix119
-rw-r--r--nix/modules/libvirtnix/secret.nix174
-rw-r--r--nix/modules/libvirtnix/test.nix10
-rw-r--r--nix/modules/libvirtnix/xml.nix58
-rw-r--r--nix/pkgs/aarch64-darwin.nix5
-rw-r--r--nix/pkgs/glibc-all-in-one/default.nix36
-rw-r--r--nix/pkgs/libc-database/default.nix2
-rw-r--r--nix/pkgs/r2wars-web/default.nix2
-rw-r--r--nix/pkgs/x86_64-linux.nix1
-rw-r--r--nix/templates/ctf/flake.lock141
-rw-r--r--nix/templates/ctf/flake.nix62
-rw-r--r--nix/templates/ctf/solve.py15
-rwxr-xr-xsecret_create.sh2
69 files changed, 4472 insertions, 1738 deletions
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..6418298
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+  "makefile.configureOnOpen": false
+}
\ No newline at end of file
diff --git a/Makefile b/Makefile
index 6708a52..5a10aec 100644
--- a/Makefile
+++ b/Makefile
@@ -17,14 +17,13 @@ update:
 	time nix flake update --commit-lock-file
 
 switch-caladan:
-	time nix run https://github.com/LnL7/nix-darwin/archive/master.tar.gz -- switch --flake .#caladan
+	time sudo nix run https://github.com/LnL7/nix-darwin/archive/master.tar.gz -- switch --flake .#caladan
 
 build-corrino:
 	time nix run nixpkgs#nix-output-monitor build .#nixosConfigurations.${HOSTNAME}.config.system.build.toplevel
 
 deploy: # build
 	time nix run -- nixpkgs#nixos-rebuild switch \
-		-vvv \
 		--fast \
 		--build-host root@${BUILDHOST} \
 		--target-host root@${HOSTNAME} \
diff --git a/README.md b/README.md
index a925db3..5ae3fda 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,14 @@
 # hefe
 
+## Buildhosts
+
+[hydra](https://hydra.emile.space) builds the packages, templates and hosts continously.
+
+- x86_64-linux: corrino (Hetzner AX41)
+- x86_64-darwin: kaitain (Mac Mini)
+- aarch64-linux: pi4
+- aarch64-darwin: caladan (m1 macbook air)
+
 ## Secrets
 
 - Managed using agenix
@@ -31,3 +40,7 @@ Print the generated secrets file as follows:
 ; make switch-caladan
 ```
 
+## Troubleshooting
+
+Weird `lock` issues? Try `sudo pkill -9 nix-daemon` on the build machine
+
diff --git a/flake.lock b/flake.lock
index 74f93e1..8462510 100644
--- a/flake.lock
+++ b/flake.lock
@@ -10,11 +10,11 @@
         "systems": "systems"
       },
       "locked": {
-        "lastModified": 1736955230,
-        "narHash": "sha256-uenf8fv2eG5bKM8C/UvFaiJMZ4IpUFaQxk9OH5t/1gA=",
+        "lastModified": 1750173260,
+        "narHash": "sha256-9P1FziAwl5+3edkfFcr5HeGtQUtrSdk/MksX39GieoA=",
         "ref": "refs/heads/main",
-        "rev": "e600439ec4c273cf11e06fe4d9d906fb98fa097c",
-        "revCount": 320,
+        "rev": "531beac616433bac6f9e2a19feb8e99a22a66baf",
+        "revCount": 331,
         "type": "git",
         "url": "https://github.com/ryantm/agenix"
       },
@@ -31,11 +31,11 @@
         ]
       },
       "locked": {
-        "lastModified": 1700795494,
-        "narHash": "sha256-gzGLZSiOhf155FW7262kdHo2YDeugp3VuIFb4/GGng0=",
+        "lastModified": 1744478979,
+        "narHash": "sha256-dyN+teG9G82G+m+PX/aSAagkC+vUv0SgUw3XkPhQodQ=",
         "owner": "lnl7",
         "repo": "nix-darwin",
-        "rev": "4b9b83d5a92e8c1fbfd8eb27eda375908c11ec4d",
+        "rev": "43975d782b418ebf4969e9ccba82466728c2851b",
         "type": "github"
       },
       "original": {
@@ -52,16 +52,16 @@
         ]
       },
       "locked": {
-        "lastModified": 1739302249,
-        "narHash": "sha256-C2vkThXQfsV7Ub0NP+rmm0iLLNjN9MvDjrbeZw2ZxCQ=",
-        "ref": "nix-darwin-24.11",
-        "rev": "f81c16138a6d047dcd257952688114898f5f7878",
-        "revCount": 1996,
+        "lastModified": 1749744770,
+        "narHash": "sha256-MEM9XXHgBF/Cyv1RES1t6gqAX7/tvayBC1r/KPyK1ls=",
+        "ref": "nix-darwin-25.05",
+        "rev": "536f951efb1ccda9b968e3c9dee39fbeb6d3fdeb",
+        "revCount": 2185,
         "type": "git",
         "url": "https://github.com/lnl7/nix-darwin"
       },
       "original": {
-        "ref": "nix-darwin-24.11",
+        "ref": "nix-darwin-25.05",
         "type": "git",
         "url": "https://github.com/lnl7/nix-darwin"
       }
@@ -75,11 +75,11 @@
         "utils": "utils"
       },
       "locked": {
-        "lastModified": 1727447169,
-        "narHash": "sha256-3KyjMPUKHkiWhwR91J1YchF6zb6gvckCAY1jOE+ne0U=",
+        "lastModified": 1749105467,
+        "narHash": "sha256-hXh76y/wDl15almBcqvjryB50B0BaiXJKk20f314RoE=",
         "ref": "master",
-        "rev": "aa07eb05537d4cd025e2310397a6adcedfe72c76",
-        "revCount": 328,
+        "rev": "6bc76b872374845ba9d645a2f012b764fecd765f",
+        "revCount": 340,
         "type": "git",
         "url": "https://github.com/serokell/deploy-rs"
       },
@@ -92,11 +92,11 @@
     "flake-compat": {
       "flake": false,
       "locked": {
-        "lastModified": 1696426674,
-        "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
+        "lastModified": 1733328505,
+        "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=",
         "owner": "edolstra",
         "repo": "flake-compat",
-        "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
+        "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec",
         "type": "github"
       },
       "original": {
@@ -123,6 +123,21 @@
         "url": "https://github.com/numtide/flake-utils"
       }
     },
+    "hefe-internal": {
+      "locked": {
+        "lastModified": 1750711567,
+        "narHash": "sha256-nQzCB2k8YQrU+nMpo6Vvea/EZQ6mAu8EmxqXtsiwNAg=",
+        "ref": "refs/heads/main",
+        "rev": "3b86571ad3fe8da0c6c3a48c38b04f11c1ab3d8c",
+        "revCount": 150,
+        "type": "git",
+        "url": "file:///Users/emile/hefe-internal"
+      },
+      "original": {
+        "type": "git",
+        "url": "file:///Users/emile/hefe-internal"
+      }
+    },
     "home-manager": {
       "inputs": {
         "nixpkgs": [
@@ -131,11 +146,11 @@
         ]
       },
       "locked": {
-        "lastModified": 1703113217,
-        "narHash": "sha256-7ulcXOk63TIT2lVDSExj7XzFx09LpdSAPtvgtM7yQPE=",
+        "lastModified": 1745494811,
+        "narHash": "sha256-YZCh2o9Ua1n9uCvrvi5pRxtuVNml8X2a03qIFfRKpFs=",
         "owner": "nix-community",
         "repo": "home-manager",
-        "rev": "3bfaacf46133c037bb356193bd2f1765d9dc82c1",
+        "rev": "abfad3d2958c9e6300a883bd443512c55dfeb1be",
         "type": "github"
       },
       "original": {
@@ -151,16 +166,16 @@
         ]
       },
       "locked": {
-        "lastModified": 1736373539,
-        "narHash": "sha256-dinzAqCjenWDxuy+MqUQq0I4zUSfaCvN9rzuCmgMZJY=",
-        "ref": "release-24.11",
-        "rev": "bd65bc3cde04c16755955630b344bc9e35272c56",
-        "revCount": 3879,
+        "lastModified": 1749154018,
+        "narHash": "sha256-gjN3j7joRvT3a8Zgcylnd4NFsnXeDBumqiu4HmY1RIg=",
+        "ref": "release-25.05",
+        "rev": "7aae0ee71a17b19708b93b3ed448a1a0952bf111",
+        "revCount": 4770,
         "type": "git",
         "url": "https://github.com/nix-community/home-manager"
       },
       "original": {
-        "ref": "release-24.11",
+        "ref": "release-25.05",
         "type": "git",
         "url": "https://github.com/nix-community/home-manager"
       }
@@ -172,11 +187,11 @@
         ]
       },
       "locked": {
-        "lastModified": 1736429655,
-        "narHash": "sha256-BwMekRuVlSB9C0QgwKMICiJ5EVbLGjfe4qyueyNQyGI=",
+        "lastModified": 1745925850,
+        "narHash": "sha256-cyAAMal0aPrlb1NgzMxZqeN1mAJ2pJseDhm2m6Um8T0=",
         "ref": "refs/heads/master",
-        "rev": "0621e47bd95542b8e1ce2ee2d65d6a1f887a13ce",
-        "revCount": 352,
+        "rev": "38bc60bbc157ae266d4a0c96671c6c742ee17a5f",
+        "revCount": 359,
         "type": "git",
         "url": "https://github.com/nix-community/naersk"
       },
@@ -187,16 +202,16 @@
     },
     "nixpkgs": {
       "locked": {
-        "lastModified": 1739206421,
-        "narHash": "sha256-PwQASeL2cGVmrtQYlrBur0U20Xy07uSWVnFup2PHnDs=",
-        "ref": "nixos-24.11",
-        "rev": "44534bc021b85c8d78e465021e21f33b856e2540",
+        "lastModified": 1752620740,
+        "narHash": "sha256-f3pO+9lg66mV7IMmmIqG4PL3223TYMlnlw+pnpelbss=",
+        "ref": "nixos-25.05",
+        "rev": "32a4e87942101f1c9f9865e04dc3ddb175f5f32e",
         "shallow": true,
         "type": "git",
         "url": "ssh://git@github.com/nixos/nixpkgs.git"
       },
       "original": {
-        "ref": "nixos-24.11",
+        "ref": "nixos-25.05",
         "shallow": true,
         "type": "git",
         "url": "ssh://git@github.com/nixos/nixpkgs.git"
@@ -204,11 +219,11 @@
     },
     "nixpkgs-unstable": {
       "locked": {
-        "lastModified": 1739138025,
-        "narHash": "sha256-M4ilIfGxzbBZuURokv24aqJTbdjPA9K+DtKUzrJaES4=",
+        "lastModified": 1748856973,
+        "narHash": "sha256-RlTsJUvvr8ErjPBsiwrGbbHYW8XbB/oek0Gi78XdWKg=",
         "ref": "nixpkgs-unstable",
-        "rev": "b2243f41e860ac85c0b446eadc6930359b294e79",
-        "revCount": 751383,
+        "rev": "e4b09e47ace7d87de083786b404bf232eb6c89d8",
+        "revCount": 809757,
         "type": "git",
         "url": "https://github.com/nixos/nixpkgs"
       },
@@ -218,21 +233,91 @@
         "url": "https://github.com/nixos/nixpkgs"
       }
     },
-    "nixpkgs2": {
+    "nixpkgs_2": {
       "locked": {
-        "lastModified": 1739923778,
-        "narHash": "sha256-BqUY8tz0AQ4to2Z4+uaKczh81zsGZSYxjgvtw+fvIfM=",
-        "ref": "nixos-24.11",
-        "rev": "36864ed72f234b9540da4cf7a0c49e351d30d3f1",
-        "shallow": true,
+        "lastModified": 1743259260,
+        "narHash": "sha256-ArWLUgRm1tKHiqlhnymyVqi5kLNCK5ghvm06mfCl4QY=",
+        "owner": "NixOS",
+        "repo": "nixpkgs",
+        "rev": "eb0e0f21f15c559d2ac7633dc81d079d1caf5f5f",
+        "type": "github"
+      },
+      "original": {
+        "owner": "NixOS",
+        "ref": "nixpkgs-unstable",
+        "repo": "nixpkgs",
+        "type": "github"
+      }
+    },
+    "pwndbg": {
+      "inputs": {
+        "nixpkgs": "nixpkgs_2",
+        "pyproject-build-systems": "pyproject-build-systems",
+        "pyproject-nix": "pyproject-nix",
+        "uv2nix": "uv2nix"
+      },
+      "locked": {
+        "lastModified": 1750271240,
+        "narHash": "sha256-mDT83XFhJaAQA7l1/t3TrjsiHzyW4BCWb5lXfe6Qlb8=",
+        "ref": "refs/heads/dev",
+        "rev": "1de6e8e2723f3d89952ca47fd4c8dab78af574b5",
+        "revCount": 2497,
         "type": "git",
-        "url": "ssh://git@github.com/nixos/nixpkgs.git"
+        "url": "ssh://git@github.com/pwndbg/pwndbg"
       },
       "original": {
-        "ref": "nixos-24.11",
-        "shallow": true,
         "type": "git",
-        "url": "ssh://git@github.com/nixos/nixpkgs.git"
+        "url": "ssh://git@github.com/pwndbg/pwndbg"
+      }
+    },
+    "pyproject-build-systems": {
+      "inputs": {
+        "nixpkgs": [
+          "pwndbg",
+          "nixpkgs"
+        ],
+        "pyproject-nix": [
+          "pwndbg",
+          "pyproject-nix"
+        ],
+        "uv2nix": [
+          "pwndbg",
+          "uv2nix"
+        ]
+      },
+      "locked": {
+        "lastModified": 1742689179,
+        "narHash": "sha256-kDXV6r6pQp6sxBKKxXqcTGPdiH63m8WA+IvzHhdZlEg=",
+        "owner": "pyproject-nix",
+        "repo": "build-system-pkgs",
+        "rev": "5c2a1faadc4015d50eb9919a8e20c112f3765fc2",
+        "type": "github"
+      },
+      "original": {
+        "owner": "pyproject-nix",
+        "repo": "build-system-pkgs",
+        "type": "github"
+      }
+    },
+    "pyproject-nix": {
+      "inputs": {
+        "nixpkgs": [
+          "pwndbg",
+          "nixpkgs"
+        ]
+      },
+      "locked": {
+        "lastModified": 1743085397,
+        "narHash": "sha256-mCJgxAltNx9uzYTpaSNr6yQtDMXnRykXL87L2bLmsPo=",
+        "owner": "pyproject-nix",
+        "repo": "pyproject.nix",
+        "rev": "af4c3ccf8cffcd49626b0455defb0f6b22cc1910",
+        "type": "github"
+      },
+      "original": {
+        "owner": "pyproject-nix",
+        "repo": "pyproject.nix",
+        "type": "github"
       }
     },
     "root": {
@@ -241,11 +326,12 @@
         "darwin": "darwin_2",
         "deploy-rs": "deploy-rs",
         "flake-utils": "flake-utils",
+        "hefe-internal": "hefe-internal",
         "home-manager": "home-manager_2",
         "naersk": "naersk",
         "nixpkgs": "nixpkgs",
         "nixpkgs-unstable": "nixpkgs-unstable",
-        "nixpkgs2": "nixpkgs2"
+        "pwndbg": "pwndbg"
       }
     },
     "systems": {
@@ -298,11 +384,11 @@
         "systems": "systems_2"
       },
       "locked": {
-        "lastModified": 1701680307,
-        "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
+        "lastModified": 1731533236,
+        "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
         "owner": "numtide",
         "repo": "flake-utils",
-        "rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
+        "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
         "type": "github"
       },
       "original": {
@@ -310,6 +396,31 @@
         "repo": "flake-utils",
         "type": "github"
       }
+    },
+    "uv2nix": {
+      "inputs": {
+        "nixpkgs": [
+          "pwndbg",
+          "nixpkgs"
+        ],
+        "pyproject-nix": [
+          "pwndbg",
+          "pyproject-nix"
+        ]
+      },
+      "locked": {
+        "lastModified": 1743267007,
+        "narHash": "sha256-A5lFzCjO3kBnpUewPaHoM1f6qgubDqw7bgIGSi5i0JE=",
+        "owner": "pyproject-nix",
+        "repo": "uv2nix",
+        "rev": "ede084fd69a0b656acb1ac20e6609385a3f967ba",
+        "type": "github"
+      },
+      "original": {
+        "owner": "pyproject-nix",
+        "repo": "uv2nix",
+        "type": "github"
+      }
     }
   },
   "root": "root",
diff --git a/flake.nix b/flake.nix
index 030fbe2..c755d26 100644
--- a/flake.nix
+++ b/flake.nix
@@ -1,14 +1,14 @@
 {
   inputs = {
-    nixpkgs.url = "git+ssh://git@github.com/nixos/nixpkgs.git?shallow=1&ref=nixos-24.11";
-    nixpkgs2.url = "git+ssh://git@github.com/nixos/nixpkgs.git?shallow=1&ref=nixos-24.11";
+    # nixpkgs.url = "git+ssh://git@github.com/nixos/nixpkgs.git?shallow=1&ref=nixos-24.11";
+    nixpkgs.url = "git+ssh://git@github.com/nixos/nixpkgs.git?shallow=1&ref=nixos-25.05";
     nixpkgs-unstable.url = "git+https://github.com/nixos/nixpkgs?ref=nixpkgs-unstable";
 
     # nix darwin version must match nixpkgs version:
     #   nixpkgs:    nixos-xx.yy
-    #   nix-darwin: nix-darwin-xx.yy
+    #   darwin:     nix-darwin-xx.yy
     # with xx.yy being the same
-    darwin.url = "git+https://github.com/lnl7/nix-darwin?ref=nix-darwin-24.11";
+    darwin.url = "git+https://github.com/lnl7/nix-darwin?ref=nix-darwin-25.05";
     darwin.inputs.nixpkgs.follows = "nixpkgs";
 
     deploy-rs.url = "git+https://github.com/serokell/deploy-rs?ref=master";
@@ -17,7 +17,7 @@
     agenix.url = "git+https://github.com/ryantm/agenix";
     agenix.inputs.nixpkgs.follows = "nixpkgs";
 
-    home-manager.url = "git+https://github.com/nix-community/home-manager?ref=release-24.11";
+    home-manager.url = "git+https://github.com/nix-community/home-manager?ref=release-25.05";
     home-manager.inputs.nixpkgs.follows = "nixpkgs";
 
     naersk.url = "git+https://github.com/nix-community/naersk";
@@ -25,7 +25,9 @@
 
     flake-utils.url = "git+https://github.com/numtide/flake-utils";
 
-    # hefe-internal.url = "git+file:///Users/emile/hefe-internal";
+    pwndbg.url = "git+ssh://git@github.com/pwndbg/pwndbg";
+
+    hefe-internal.url = "git+file:///Users/emile/hefe-internal";
     # hefe-internal.url = "git+ssh://git@git.emile.space/hefe-internal";
 
     # nix registry add flake:mylocalrepo git+file:///path/to/local/repo
@@ -37,7 +39,6 @@
     {
       self,
       nixpkgs, # packages
-      nixpkgs2, # packages2
       nixpkgs-unstable, # unstable branch
       darwin, # darwin related stuff
       deploy-rs, # deploy the hosts
@@ -45,47 +46,13 @@
       home-manager, # manage my home envs
       naersk, # build rust stuff
       flake-utils, # common flake utils
-      # hefe-internal,    # internal tooling
+      pwndbg, # fancy gdbinit
+      hefe-internal,    # internal tooling
       ...
     }@inputs:
     let
       lib = import ./nix/lib inputs;
       helper = lib.flake-helper;
-
-      # TODO(emile): move all these functions into the helper, keeping the flake.nix clean
-
-      # A function taking an attribute set of flake templates, importing their
-      # flake.nix/output/packages (if there are any) and returning an attribute set
-      # of their packages (if the template has one or more)
-      template-packages =
-        templ:
-        (builtins.mapAttrs (
-          name: value:
-          (((import ./nix/templates/${name}/flake.nix).outputs) {
-            inherit flake-utils;
-
-            # need to provide nixpkgs WITHOUT the overlay for the packages defined in the template applied
-            nixpkgs = nixpkgs;
-          }).packages or { }
-        ) templ);
-
-      # apply the above function to the templates
-      templates = template-packages self.templates;
-
-      # Merge template packages into root packages with template prefix
-      mergedTemplatePackages =
-        system:
-        let
-          lib = nixpkgs.lib;
-        in
-        lib.foldl (
-          acc: tplName:
-          let
-            tplPkgs = templates.${tplName}.${system} or { };
-            prefixed = lib.mapAttrs' (pkgName: pkg: lib.nameValuePair "${tplName}-${pkgName}" pkg) tplPkgs;
-          in
-          acc // prefixed
-        ) { } (builtins.attrNames templates);
     in
     {
       hosts = {
@@ -116,15 +83,13 @@
           ip = "corrino";
           description = "Hetzner AX41 dual 512GB NVME";
           modules = [
-            # hefe-internal.nixosModules.corrino
+            hefe-internal.nixosModules.corrino
             (
               { self, ... }:
               {
                 nixpkgs.overlays = [
                   (final: prev: {
-                    inherit (self.packages.x86_64-linux)
-                      goapp-frontend
-                      ;
+                    inherit (self.packages.x86_64-linux) goapp-frontend;
                   })
                 ];
               }
@@ -175,8 +140,16 @@
         #   description = "lankiveil bmc";
         # };
 
-        # kaitain = {};
-        # ecaz = {};
+        # kaitain = {
+        #   system = "x86_64-darwin";
+        #   description = "mac mini";
+        # };
+
+        # ecaz = {
+        #   system = "x86_64-linux";
+        #   description = "pi4";
+        # };
+
         # gamont = {};
 
         # futher names: https://neoencyclopedia.fandom.com/wiki/List_of_Dune_planets
@@ -218,12 +191,11 @@
 
         # no clue why, but when rebuilding corrino and this not being commented,
         # something in the hardware.bluetooth module breaks
-        #
         # unstable-darwin = final: prev: {
-        #   unstable-darwin = import nixpkgs-unstable {
-        #     system = "aarch64-darwin";
-        #     config.allowUnfree = true;
-        #   };
+        unstable-darwin = import nixpkgs-unstable {
+          system = "aarch64-darwin";
+          config.allowUnfree = true;
+        };
         # };
       };
 
@@ -243,12 +215,13 @@
                 inherit system;
                 overlays = [
                   (
-                    if system == "x86_64-linux" then
-                      self.overlays.x86_64-linux
-                    else if system == "aarch64-darwin" then
-                      self.overlays.aarch64-darwin
-                    else
-                      null
+                    self.overlays.${system}
+                    # if system == "x86_64-linux" then
+                    #   self.overlays.x86_64-linux
+                    # else if system == "aarch64-darwin" then
+                    #   self.overlays.aarch64-darwin
+                    # else
+                    #   null
                   )
                   # some arguments for packages
                   (_: _: { inherit naersk; })
@@ -257,18 +230,27 @@
             in
             # take all the packages exposed from templates and add them to
             # the packages exposed by this flake
-            mergedTemplatePackages system
-            // {
-              inherit (pkgs) vokobe r2wars-web remarvin;
+              
+            # TODO(emile): templates
+            #helper.merged-template-packages system
+            # //
+            {
+              inherit (pkgs)
+                vokobe
+                r2wars-web
+                # remarvin
+                # glibc-all-in-one
+                ;
             }
           );
 
       hydraJobs = {
         inherit (self) packages;
-        nixosConfigurations = helper.buildHosts self.nixosConfigurations;
-        templates = template-packages self.templates;
+        # nixosConfigurations = helper.buildHosts self.nixosConfigurations;
+        templates = helper.template-packages self.templates;
       };
 
+      # TODO(emile): templates
       templates = {
         # ; nix nix registry add hefe /Users/emile/Documents/hefe
         # ; nix flake init -t hefe#ctf
@@ -276,14 +258,11 @@
           description = "A basic ctf env with pwn, rev, ... tools";
           path = ./nix/templates/ctf;
           welcomeText = ''
-            # A basic CTF env
-            ## Intended usage
-            The intended usage of this flake is...
-
-            ## More info
-            - [Rust language](https://www.rust-lang.org/)
-            - [Rust on the NixOS Wiki](https://nixos.wiki/wiki/Rust)
-            - ...
+            # CTF flake template
+
+            Run `nix develop` to get a shell with pwntools, pwndbg, pycryptodome, ...
+
+            Add packages in the flake as you like.
           '';
         };
         goapp = {
@@ -291,7 +270,7 @@
           path = ./nix/templates/goapp;
           welcomeText = ''
             # A basic golang service
-             
+         
             - using gorilla/mux
           '';
         };
diff --git a/nix/hosts/caladan/aliases.nix b/nix/hosts/caladan/aliases.nix
index d65bc14..6738589 100644
--- a/nix/hosts/caladan/aliases.nix
+++ b/nix/hosts/caladan/aliases.nix
@@ -4,6 +4,7 @@
 
   # short forms
   tf = "terraform";
+  m = "multipass";
 
   r2help = ''r2 -qq -c "?*~..." --'';
   mosh = "mosh --no-init";
diff --git a/nix/hosts/caladan/darwin-configuration.nix b/nix/hosts/caladan/darwin-configuration.nix
index ef18642..e581cb8 100644
--- a/nix/hosts/caladan/darwin-configuration.nix
+++ b/nix/hosts/caladan/darwin-configuration.nix
@@ -1,7 +1,9 @@
 { pkgs, lib, ... }:
 
 {
-  imports = [ ./overlay.nix ];
+  imports = [
+    ./overlay.nix
+  ];
 
   system.stateVersion = 5;
 
@@ -27,9 +29,17 @@
   # users.users."_nixbld3".uid = 307;
   # users.users."_nixbld4".uid = 308;
   # users.users."_nixbld5".uid = 309;
+  
+  # virtualisation.multipass = {
+  #   enable = true;
+  #   package = pkgs.multipass;
+  #   logLevel = "debug";
+  # };
+
+  ids.gids.nixbld = 30000;
 
   nix = {
-    useDaemon = true;
+    # useDaemon = true;
     # package = pkgs.nixFlakes;
     extraOptions =
       ''
@@ -85,10 +95,10 @@
       {
         hostName = "corrino.emile.space";
         system = "x86_64-linux";
-        maxJobs = 16;
+        maxJobs = 10;
         speedFactor = 2;
 
-        # Feature	      | Derivations requiring it
+        # Feature	        | Derivations requiring it
         # ----------------|-----------------------------------------------------
         # kvm	            | Everything which builds inside a vm, like NixOS tests
         # nixos-test	    | Machine can run NixOS tests
@@ -110,14 +120,16 @@
     ];
   };
 
-  nixpkgs = {
-    config.allowUnfree = true;
+  nixpkgs.config = {
+    allowUnfree = true;
+    allowUnsupportedSystem = true;
   };
 
-  services.nix-daemon.enable = true;
+  # services.nix-daemon.enable = true;
 
   # <3
-  security.pam.enableSudoTouchIdAuth = true;
+  # security.pam.enableSudoTouchIdAuth = true;
+  security.pam.services.sudo_local.touchIdAuth = true;
 
   environment = {
     systemPackages = [ ]; # set via home-manager
diff --git a/nix/hosts/caladan/emacs_config.el b/nix/hosts/caladan/emacs_config.el
index 0ed5786..01cf5bd 100644
--- a/nix/hosts/caladan/emacs_config.el
+++ b/nix/hosts/caladan/emacs_config.el
@@ -17,10 +17,7 @@
   (unless (package-installed-p package)
     (package-install package)))
 
-(when (display-graphic-p)
-  (tool-bar-mode 0)
-  (scroll-bar-mode 'left))
-
+(scroll-bar-mode -1)
 (load-theme 'leuven) ;; light theme
 (setq pixel-scroll-precision-mode 1)
 (xterm-mouse-mode 1)
@@ -42,9 +39,6 @@
                (display-buffer-no-window)
                (allow-no-window . t)))
 
-(when (not (display-graphic-p))
-      (menu-bar-mode -1))
-
 ;; general purpose emacs settings
 (use-package emacs
   :init
@@ -148,7 +142,6 @@
               completion-category-overrides '((file (styles partial-completion)))))
               
 
-
 ;; markdown mode
 ;; https://jblevins.org/projects/markdown-mode/
 (defvar markdown-command)
@@ -181,7 +174,7 @@
   :ensure nil ; no need to install it as it is built-in, but needs to be activated
   :hook (after-init . delete-selection-mode))
 
-;; Configure the Lisp program for SLIME
+;; Configure the Lisp program for SLY
 (add-to-list 'exec-path "/Users/emile/.nix-profile/bin")
 (defvar inferior-lisp-program "sbcl")
 
@@ -206,14 +199,5 @@
 (use-package breadcrumb
      :ensure t)
 
-;(setq circe-network-options
-;  '(("Libera Chat"
-;     :tls t
-;     :tls-keylist (("/Users/emile/libera.crt"
-;                    "/Users/emile/libera.key"))
-;     :sasl-external t
-;     :nick "hanemile"
-;     :channels ("#test"))))
-
 (provide '.emacs)
 ;;; emacs_config.el ends here
diff --git a/nix/hosts/caladan/home_emile.nix b/nix/hosts/caladan/home_emile.nix
index 86d6965..704b73b 100644
--- a/nix/hosts/caladan/home_emile.nix
+++ b/nix/hosts/caladan/home_emile.nix
@@ -1,4 +1,4 @@
-{ pkgs, ... }:
+{ pkgs, lib, ... }:
 
 {
   home = {
@@ -50,7 +50,7 @@
         fi
       '';
 
-      initExtraBeforeCompInit = ''
+      initContent = lib.mkOrder 550 ''
         ${builtins.readFile ./session_variables.zsh}
         ${builtins.readFile ./functions.zsh}
 
@@ -60,6 +60,18 @@
       '';
     };
 
+    neovim = let
+      custom_plugins = pkgs.callPackage ./nvim_plugins.nix { };
+    in {
+      enable = true;
+      plugins = with pkgs.vimPlugins // custom_plugins; [
+        neovim-ayu
+        lisp.vlime
+      ];
+      extraConfig = ''
+      '';
+    };
+
     emacs = {
       enable = true;
       package = pkgs.emacs;
@@ -76,10 +88,8 @@
     kitty = {
       enable = true;
 
-      # package = pkgs.kitty;
-
       font = {
-        name = "Iosevka Nerd Font";
+        name = "Berkeley Mono";
         size = 13;
       };
 
@@ -92,9 +102,6 @@
         tab_bar_edge = "top";
         tab_bar_style = "slant";
         tab_bar_min_tabs = 1;
-
-        # tab_title_template = "{index}[{layout_name[0:2]}]: {title.replace('emile', 'e')[title.rfind('/')+1:]}";
-        # tab_title_template = "{index}[{layout_name[0:2]}]: {title.replace('emile', 'e')}";
         tab_title_template = "{index} {title.replace('emile', 'e')}";
 
         editor = "/Users/emile/.cargo/bin/hx";
@@ -182,9 +189,9 @@
     ## language server
     # nodePackages_latest.typescript-language-server # js / typescript
     nil # nix 
-    nodePackages.yaml-language-server # yaml
+    # nodePackages.yaml-language-server # yaml
     python312Packages.python-lsp-server # python
-    gopls # golang
+    # gopls # golang
 
     # binary foo
     radare2
@@ -199,7 +206,8 @@
 
     # go foo
     go
-    delve
+    # delve
+    # gotools
 
     # c foo
     cmake
@@ -247,9 +255,23 @@
 
     taskwarrior3
 
-    drawio
+    # drawio
+
+    # cargo rustup
+    cargo
+
+    utm
+
+    #nmap ffuf
+    #typst
+    #age
+    #ffmpeg
+    #exiftool
+
+    # custom
+    # libc-database
 
-    libc-database
+    # unstable.duckdb
 
     # blender
 
diff --git a/nix/hosts/caladan/nvim_plugins.nix b/nix/hosts/caladan/nvim_plugins.nix
new file mode 100644
index 0000000..7f07816
--- /dev/null
+++ b/nix/hosts/caladan/nvim_plugins.nix
@@ -0,0 +1,21 @@
+{ vimUtils, fetchgit, ... }:
+
+let
+  build = ({name, owner, rev, sha256}: vimUtils.buildVimPlugin {
+    inherit name;
+    src = fetchgit {
+      inherit rev sha256;
+      url = "https://github.com/${owner}/${name}";
+    };
+    dependencies = [];
+  });
+in {
+  lisp = {
+    vlime = build {
+      name = "vlime";
+      owner = "l04m33";
+      rev = "065b95f3ac7a455314c2bdefeb2b792f290034df";
+      sha256 = "1bmmskdwvbl6lvbnjp9lls86rz0vzmk73y644bjb9ix9ygmjbia4";
+    };
+  };
+}
diff --git a/nix/hosts/caladan/overlay.nix b/nix/hosts/caladan/overlay.nix
index c9bdd79..8295339 100644
--- a/nix/hosts/caladan/overlay.nix
+++ b/nix/hosts/caladan/overlay.nix
@@ -3,42 +3,37 @@
 {
   nixpkgs = {
     overlays = [
-      (self: super: {
-        kitty = super.kitty.overrideAttrs (old: {
-          preCheck = ''
-            # skip failing tests due to darwin sandbox
-            substituteInPlace kitty_tests/file_transmission.py \
-              --replace test_file_get dont_test_file_get \
-              --replace test_path_mapping_receive dont_test_path_mapping_receive \
-              --replace test_transfer_send dont_test_transfer_send
-            substituteInPlace kitty_tests/shell_integration.py \
-              --replace test_fish_integration dont_test_fish_integration
-            substituteInPlace kitty_tests/shell_integration.py \
-              --replace test_bash_integration dont_test_bash_integration
-            substituteInPlace kitty_tests/open_actions.py \
-              --replace test_parsing_of_open_actions dont_test_parsing_of_open_actions
-            substituteInPlace kitty_tests/ssh.py \
-              --replace test_ssh_connection_data dont_test_ssh_connection_data
-            substituteInPlace kitty_tests/fonts.py \
-              --replace 'class Rendering(BaseTest)' 'class Rendering'
+      #(self: super: {
+      #  kitty = super.kitty.overrideAttrs (old: {
+      #    preCheck = ''
+      #      # skip failing tests due to darwin sandbox
+      #      substituteInPlace kitty_tests/file_transmission.py \
+      #        --replace test_file_get dont_test_file_get \
+      #        --replace test_path_mapping_receive dont_test_path_mapping_receive \
+      #        --replace test_transfer_send dont_test_transfer_send
+      #      substituteInPlace kitty_tests/shell_integration.py \
+      #        --replace test_fish_integration dont_test_fish_integration
+      #      substituteInPlace kitty_tests/shell_integration.py \
+      #        --replace test_bash_integration dont_test_bash_integration
+      #      substituteInPlace kitty_tests/open_actions.py \
+      #        --replace test_parsing_of_open_actions dont_test_parsing_of_open_actions
+      #      substituteInPlace kitty_tests/ssh.py \
+      #        --replace test_ssh_connection_data dont_test_ssh_connection_data
+      #      substituteInPlace kitty_tests/fonts.py \
+      #        --replace 'class Rendering(BaseTest)' 'class Rendering'
 
-            # TODO(emile): figure out why this test is failing and activate it
-            # again.
-            substituteInPlace kittens/hyperlinked_grep/main_test.go \
-              --replace TestRgArgParsing DontTestRgArgParsing \
+      #      # TODO(emile): figure out why this test is failing and activate it
+      #      # again.
+      #      substituteInPlace kittens/hyperlinked_grep/main_test.go \
+      #        --replace TestRgArgParsing DontTestRgArgParsing \
 
-            # theme collection test starts an http server
-            rm tools/themes/collection_test.go
-            # passwd_test tries to exec /usr/bin/dscl
-            rm tools/utils/passwd_test.go
-          '';
-        });
-
-        # helix-2303 = self.callPackage ../../pkgs/helix-2303 { };
-        # r2 = self.callPackage ../../pkgs/radare2-5.8.4 { };
-        # ansel = self.callPackage ../../pkgs/ansel { };
-        # typst = self.callPackage ../pkgs/radare2-5.8.4 { };
-      })
+      #      # theme collection test starts an http server
+      #      rm tools/themes/collection_test.go
+      #      # passwd_test tries to exec /usr/bin/dscl
+      #      rm tools/utils/passwd_test.go
+      #    '';
+      #  });
+      #})
     ];
     config = {
       allowUnfree = true;
diff --git a/nix/hosts/corrino/configuration.nix b/nix/hosts/corrino/configuration.nix
index 52e9ecf..7683630 100644
--- a/nix/hosts/corrino/configuration.nix
+++ b/nix/hosts/corrino/configuration.nix
@@ -25,9 +25,11 @@ in
     # ./vm.nix
 
     ./www/git
-    ./www/nix-cache
+    #./www/nix-cache
 
-    ./www/goapp.emile.space.nix
+    # doesn't find the goapp-frontend package, some issue with the overlay being applied, at least
+    # thats what I think the problem is
+    # ./www/goapp.emile.space.nix
 
     # screego
 
@@ -35,36 +37,38 @@ in
     ./www/emile.space.nix
     ./www/tmp.emile.space.nix
     ./www/hydra.emile.space.nix
-    ./www/netbox.emile.space.nix
+    # ./www/netbox.emile.space.nix
     ./www/stats.emile.space.nix
-    # ./www/grafana.emile.space.nix
+    ./www/grafana.emile.space.nix
     # ./www/prometheus.emile.space.nix
-    # ./www/loki.emile.space.nix
-    # ./www/promtail.emile.space.nix
 
     ./www/photo
 
     ./www/tickets.emile.space.nix
     # ./www/talks.emile.space.nix
+    ./www/miniflux.emile.space.nix
     # ./www/stream.emile.space.nix
     ./www/md.emile.space.nix
     ./www/social.emile.space.nix
     ./www/sso.emile.space.nix
-    ./www/s3.emile.space.nix
+    # ./www/s3.emile.space.nix
     # ./www/cs.emile.space.nix
-    ./www/irc.emile.space.nix
+    # ./www/irc.emile.space.nix
+    # ./www/cl.emile.space.nix
     # ./www/db.emile.space.nix
 
-    # ./www/ctf.emile.space.nix
+    #./www/ctf.emile.space.nix
     # ./www/magic-hash.emile.space.nix
 
+    # ./www/mc.emile.space.nix
+
     # gemini
-    ./gemini/emile.space.nix
+    # ./gemini/emile.space.nix
 
     # general purpose modules
 
     # r2wars
-    ./www/r2wa.rs.nix
+    # ./www/r2wa.rs.nix
 
     # milliways
     # ./remarvin.nix
@@ -159,9 +163,7 @@ in
       '';
     };
 
-    supportedFilesystems = {
-      "cifs" = true;
-    };
+    supportedFilesystems = [ "cifs" ];
   };
 
   time.timeZone = "Europe/Berlin";
@@ -220,40 +222,40 @@ in
   };
 
   # create a oneshot job to authenticate to Tailscale
-  systemd.services.tailscale-autoconnect = {
-    description = "Automatic connection to Tailscale";
-
-    # make sure tailscale is running before trying to connect to tailscale
-    after = [
-      "network-pre.target"
-      "tailscale.service"
-    ];
-    wants = [
-      "network-pre.target"
-      "tailscale.service"
-    ];
-    wantedBy = [ "multi-user.target" ];
-
-    # set this service as a oneshot job
-    serviceConfig.Type = "oneshot";
-
-    # have the job run this shell script
-    script = with pkgs; ''
-      # wait for tailscaled to settle
-      sleep 2
-
-      # check if we are already authenticated to tailscale
-      status="$(${tailscale}/bin/tailscale status -json | ${jq}/bin/jq -r .BackendState)"
-      if [ $status = "Running" ]; then # if so, then do nothing
-        exit 0
-      fi
-
-      # otherwise authenticate with tailscale
-      ${tailscale}/bin/tailscale up \
-        --advertise-exit-node --exit-node
-    '';
-    # -authkey ${config.age.secrets.tailscale_authkey}
-  };
+  # systemd.services.tailscale-autoconnect = {
+  #   description = "Automatic connection to Tailscale";
+
+  #   # make sure tailscale is running before trying to connect to tailscale
+  #   after = [
+  #     "network-pre.target"
+  #     "tailscale.service"
+  #   ];
+  #   wants = [
+  #     "network-pre.target"
+  #     "tailscale.service"
+  #   ];
+  #   wantedBy = [ "multi-user.target" ];
+
+  #   # set this service as a oneshot job
+  #   serviceConfig.Type = "oneshot";
+
+  #   # have the job run this shell script
+  #   script = with pkgs; ''
+  #     # wait for tailscaled to settle
+  #     sleep 2
+
+  #     # check if we are already authenticated to tailscale
+  #     status="$(${tailscale}/bin/tailscale status -json | ${jq}/bin/jq -r .BackendState)"
+  #     if [ $status = "Running" ]; then # if so, then do nothing
+  #       exit 0
+  #     fi
+
+  #     # otherwise authenticate with tailscale
+  #     ${tailscale}/bin/tailscale up \
+  #       --advertise-exit-node --exit-node
+  #   '';
+  #   # -authkey ${config.age.secrets.tailscale_authkey}
+  # };
 
   networking = {
     hostName = "corrino";
@@ -314,6 +316,7 @@ in
         80
         443 # normal web
         config.emile.ports.gitDaemon
+        8085
       ];
       allowedUDPPorts = [
         # 51820 # wireguard
@@ -405,6 +408,9 @@ in
         "docker"
         "libvirtd"
       ];
+      packages = with pkgs; [
+        docker
+      ];
     };
 
     tmpuser1 = {
@@ -440,6 +446,27 @@ in
       # use corrino as a subnet router and an exit node
       useRoutingFeatures = "both";
     };
+
+    restic.backups."corrino" = {
+      repository = "/mnt/storagebox-bx11/corrino";
+      passwordFile = config.age.secrets.restic_password.path;
+      initialize = true;
+      pruneOpts = [
+        "--keep-daily 7"
+        "--keep-weekly 5"
+        "--keep-monthly 12"
+        "--keep-yearly 75"
+      ];
+    };
+    # restic.server = {
+    #   enable = true;
+    #   prometheus = true;
+    #   package = pkgs.restic-rest-server;
+    #   extraFlags = [ "--no-auth" ];
+    #   listenAddress = "127.0.0.1:${toString config.emile.ports.restic}";
+    #   dataDir = "/var/lib/restic";
+    #   appendOnly = true;
+    # };
   };
 
   nix = {
@@ -469,38 +496,38 @@ in
       allowed-uris = https://github.com/ https://git.emile.space/ git+https://github.com/
     '';
 
-    buildMachines = [
-      {
-        hostName = "localhost";
-        system = "x86_64-linux";
-        protocol = "ssh-ng";
-        maxJobs = 8;
-        supportedFeatures = [
-          "nixos-test"
-          "benchmark"
-          "big-parallel"
-          "kvm"
-        ];
-      }
-      {
-        hostName = "caladan.pinto-pike.ts.net";
-        sshUser = "hydra";
-        sshKey = "/var/lib/hydra/.ssh/id_ed25519";
-        system = "aarch64-darwin";
-        protocol = "ssh-ng";
-        maxJobs = 1;
-        speedFactor = 2;
-        supportedFeatures = [
-          "nixos-test"
-          "benchmark"
-          "big-parallel"
-          "kvm"
-        ];
-        mandatoryFeatures = [ ];
-      }
-    ];
+    # buildMachines = [
+    #   {
+    #     hostName = "localhost";
+    #     system = "x86_64-linux";
+    #     protocol = "ssh-ng";
+    #     maxJobs = 8;
+    #     supportedFeatures = [
+    #       "nixos-test"
+    #       "benchmark"
+    #       "big-parallel"
+    #       "kvm"
+    #     ];
+    #   }
+    #   {
+    #     hostName = "caladan.pinto-pike.ts.net";
+    #     sshUser = "hydra";
+    #     sshKey = "/var/lib/hydra/.ssh/id_ed25519";
+    #     system = "aarch64-darwin";
+    #     protocol = "ssh-ng";
+    #     maxJobs = 1;
+    #     speedFactor = 2;
+    #     supportedFeatures = [
+    #       "nixos-test"
+    #       "benchmark"
+    #       "big-parallel"
+    #       "kvm"
+    #     ];
+    #     mandatoryFeatures = [ ];
+    #   }
+    # ];
 
-    distributedBuilds = true;
+    # distributedBuilds = true;
   };
 
   nixpkgs.config = {
@@ -518,7 +545,7 @@ in
   };
 
   virtualisation = {
-    # docker.enable = true;
+    docker.enable = true;
     libvirtd = {
       enable = true;
       qemu = {
@@ -526,12 +553,14 @@ in
         runAsRoot = true;
         swtpm.enable = true;
         ovmf = {
-          enable = true;
-          packages = [
-            (pkgs.unstable.OVMF.override {
+        enable = true;
+        packages = [
+          (
+            pkgs.OVMF.override {
               secureBoot = true;
               tpmSupport = true;
-            }).fd
+            }
+          ).fd
           ];
         };
       };
@@ -556,13 +585,25 @@ in
     };
 
     "/mnt/storagebox-bx11" = {
-      device = "//u331921.your-storagebox.de/backup";
-      fsType = "cifs";
-      options =
-        let
-          automount_opts = "_netdev,x-systemd.automount,noauto,x-systemd.idle-timeout=60,x-systemd.device-timeout=5s,x-systemd.mount-timeout=5s";
-        in
-        [ "${automount_opts},credentials=${config.age.secrets.storage_box_bx11_password.path}" ];
+      device = "u331921@u331921.your-storagebox.de:/home/backup";
+      fsType = "sshfs";
+      options = [ # Filesystem options
+        "allow_other"          # for non-root access
+        "_netdev"              # this is a network fs
+
+        # We don't mount on demand, as that will cause services like navidrome to fail
+        # as the share doesn't yet exist.
+        #"x-systemd.automount" # mount on demand, rather than boot
+
+        #"debug"               # print debug logging
+                               # warning: this causes the one-shot service to never exit
+
+        # SSH options
+        "StrictHostKeyChecking=no"  # prevent the connection from failing if the host's key hasn't been trusted yet
+        "ServerAliveInterval=15" # keep connections alive
+        "Port=23"
+        "IdentityFile=/root/.ssh/id_ed25519"
+      ];
     };
   };
 
diff --git a/nix/hosts/corrino/ports.nix b/nix/hosts/corrino/ports.nix
index bb64934..483ab7f 100644
--- a/nix/hosts/corrino/ports.nix
+++ b/nix/hosts/corrino/ports.nix
@@ -5,16 +5,24 @@
     photo = {
       photoprism = 2342;
       immich = 2343;
+      immich-public-proxy = 2344;
     };
     git = 3000;
     hydra = 3001;
     grafana = 3002;
     md = 3003;
     gotosocial = 3004;
-    immich = 3005;
     monica = 3006;
+    miniflux = 3007;
     harmonia = 5000;
+    garage = {
+      s3 = 6001;
+      web = 6002;
+      rpc = 6003;
+      admin = 6004;
+    };
     irc = {
+      bouncer = 6666;
       clear = 6667;
       ssl = 6697;
     };
@@ -34,8 +42,6 @@
       s3 = 9000;
       web = 9001;
     };
-    promtail = 9033;
-    loki = 9034;
     authelia = 9091;
     gitDaemon = 9418;
     prometheus = {
@@ -45,6 +51,7 @@
         nginx = 9913;
         systemd = 9558;
         smartctl = 9633;
+        restic = 9634;
       };
     };
   };
diff --git a/nix/hosts/corrino/secrets/garage_admin_metrics_secret.age b/nix/hosts/corrino/secrets/garage_admin_metrics_secret.age
new file mode 100644
index 0000000..e1af7da
--- /dev/null
+++ b/nix/hosts/corrino/secrets/garage_admin_metrics_secret.age
@@ -0,0 +1,7 @@
+age-encryption.org/v1
+-> ssh-ed25519 gvwQ2Q 7QkcpYGeeMsbW0GcXzNGPTc0jUf4ydpMiTO6ZxEIKGY
+OOxq2hMORsmUzBuoqOIPNJeLqJB0seve9PhorS6PKNs
+-> ssh-ed25519 m8VklA pF7mWG6tviFC6qD88dxoQRnXGfR0AuanVyY+bh8XgV0
+mrk4HgEs3i8y5P+BSGM1psweXpY/xO+8vK/DsXyhyiY
+--- zqEl/ZN/3jEgMZ/IbPbyTHGZJDDENLOnoQezaACeoSs
+íóõÃ÷ÔþlÉé…”,±ÿ‚W`£…\6éyh†Ž.
ÏéÞoVÐ(€?äEmå¶,Ü;Á(@¬0dVƒ£A=4©v¡É
\ No newline at end of file
diff --git a/nix/hosts/corrino/secrets/garage_admin_token_secret.age b/nix/hosts/corrino/secrets/garage_admin_token_secret.age
new file mode 100644
index 0000000..2a18a6b
--- /dev/null
+++ b/nix/hosts/corrino/secrets/garage_admin_token_secret.age
@@ -0,0 +1,7 @@
+age-encryption.org/v1
+-> ssh-ed25519 gvwQ2Q hcMMVkZSsObrOFjetml2z4eH+EfnuSsna+GaXEeMUA4
+y6lFBj49cMhOGuJBpILHsykpBMpKDHZpFXR4E4zZEbg
+-> ssh-ed25519 m8VklA Z6zLilTWlGWG17Q6jBx13m3KYs3gE93TPLq0CidHeTA
+eqMN5mDMasi/Nw2y5Kgwy2COna+3zbbFTTUrD/O26ls
+--- QdVyqrTLmEcGSB37Ft3Ur0Ry9Jk9DyHFI6fo88tnsgI
+X`w•YšÙ,ç<ˆçùŸA›Á$XeêDGe´	;±àµ†¿Âô>ü1n¥s€ª’‰±äÁNr_Y\µ`¬Á„)ü„F#{çãß´<Œ
\ No newline at end of file
diff --git a/nix/hosts/corrino/secrets/garage_env.age b/nix/hosts/corrino/secrets/garage_env.age
new file mode 100644
index 0000000..becb511
--- /dev/null
+++ b/nix/hosts/corrino/secrets/garage_env.age
Binary files differdiff --git a/nix/hosts/corrino/secrets/garage_rpc_secret.age b/nix/hosts/corrino/secrets/garage_rpc_secret.age
index e228d0d..ce8a65a 100644
--- a/nix/hosts/corrino/secrets/garage_rpc_secret.age
+++ b/nix/hosts/corrino/secrets/garage_rpc_secret.age
Binary files differdiff --git a/nix/hosts/corrino/secrets/goapp_oidc_client_secret.age b/nix/hosts/corrino/secrets/goapp_oidc_client_secret.age
new file mode 100644
index 0000000..1477d56
--- /dev/null
+++ b/nix/hosts/corrino/secrets/goapp_oidc_client_secret.age
@@ -0,0 +1,7 @@
+age-encryption.org/v1
+-> ssh-ed25519 gvwQ2Q wi3/W2UnB/8kCTeJ8shGpBQ21uvSQlbtZBAbocbQ+zY
+6RQGQss6B6uvq4yFXGVQtbHgDKGzMVO2xDgUDlBV5Es
+-> ssh-ed25519 m8VklA oFBut6nQ5Er9YQWpCb+2j3JKsNIjBRjwuQ7ERVekVyM
+ccR/5J5g1D11iNieF/BXtzxcusF1Zaq04iifOFk6Q/U
+--- r2E/KdtXa9/j5ecWhjzLsztBG8W+if2MeOPya6KJDxY
+êCª7­p}ôÞÿVjíàüÍ4|³EWëá].kÊ~ìÒÎXŠe:ñ(r¯õ2:ù‹”$}®Ñ~wÒÎ0Æ»Pð`+ ¡X¨g@¸PFXÁ/iú%Š;˜!$Ï	ƒ"ªIÔúi¹ñ_Î#¬*Ó|Oà„Œ†d;ZÓò£twDº F»¶l–Ðd„¨F¦6дÎJøä©a4®ýq2ây
8¿ÞT
\ No newline at end of file
diff --git a/nix/hosts/corrino/secrets/goapp_oidc_secret.age b/nix/hosts/corrino/secrets/goapp_oidc_secret.age
index ca96981..4ca657b 100644
--- a/nix/hosts/corrino/secrets/goapp_oidc_secret.age
+++ b/nix/hosts/corrino/secrets/goapp_oidc_secret.age
Binary files differdiff --git a/nix/hosts/corrino/secrets/gotosocial_environment_file.age b/nix/hosts/corrino/secrets/gotosocial_environment_file.age
index 7211c12..d2b6b90 100644
--- a/nix/hosts/corrino/secrets/gotosocial_environment_file.age
+++ b/nix/hosts/corrino/secrets/gotosocial_environment_file.age
Binary files differdiff --git a/nix/hosts/corrino/secrets/gotosocial_oidc_client_secret.age b/nix/hosts/corrino/secrets/gotosocial_oidc_client_secret.age
new file mode 100644
index 0000000..bd6a14a
--- /dev/null
+++ b/nix/hosts/corrino/secrets/gotosocial_oidc_client_secret.age
@@ -0,0 +1,7 @@
+age-encryption.org/v1
+-> ssh-ed25519 gvwQ2Q PoCUbJ4Pg2ZCYy3HCI4w7J/vu5gh6d257Q/PlOBEEG4
+Hisdtymy8i3cN0LVuXpY8duCBMLlTT8QoOEYVmLy+n8
+-> ssh-ed25519 m8VklA i01UwKFor55UM6dL7glsMP6PQM9fcoa9EV3RRXPwRnE
+9fJ9nWhhwyEXclzaYAszUTLTNH/xkM3u9uw0BgY8FOg
+--- 8MUr5xLS5Rt1yXNPfIQwfF6ZGJwNOe/RVtlX0MaGMZ8
+ãYéòàì^쌛 ¯ôØhÅ#®‡üÿÂß2¿¢ñ·ÎN°‚tغ¢BêFÖX“Æ­oˆ“.eœ¼Cv%‘·1/•ýÌO%.‰GíÌþ×Ϥ%fÛÿ·¨šô]žä_2²ªeo~p ÇÜ{ýëbÝšý ðSH±Ó݆RÃnkôßcŽ‘jm‡º<?Ëk’%ÑnÙÈ:ˆFBJ#‰÷&
\ No newline at end of file
diff --git a/nix/hosts/corrino/secrets/grafana_env_vars.age b/nix/hosts/corrino/secrets/grafana_env_vars.age
index 0365676..7ad889e 100644
--- a/nix/hosts/corrino/secrets/grafana_env_vars.age
+++ b/nix/hosts/corrino/secrets/grafana_env_vars.age
Binary files differdiff --git a/nix/hosts/corrino/secrets/grafana_oidc_client_secret.age b/nix/hosts/corrino/secrets/grafana_oidc_client_secret.age
new file mode 100644
index 0000000..91d87e8
--- /dev/null
+++ b/nix/hosts/corrino/secrets/grafana_oidc_client_secret.age
@@ -0,0 +1,7 @@
+age-encryption.org/v1
+-> ssh-ed25519 gvwQ2Q YdFGe2zYgdQDdW9tZG/VPlV6ZnWCv1u2hIg3AiBVwU0
+7PXZNJR5HszD2IqVJX3Rw2VfI1Anf3fuOQTFNq58raU
+-> ssh-ed25519 m8VklA LP+D7xz1SbSPT+6lEfaRjxNPSsBfyN7pE2LAe7ErVCA
+wQeekB+chF01SCX8+BNcKk7o6jN8sT5uic+Oe/od9yc
+--- ZG36Lj4tUALxjMb1+86Y3tdH1KMEw+Teeks0UbB2Zvk
+¼.² „öií0èÓ.X®¬âúýðNÝâ)¡Â`ÁçuPˆ&4ÌÒÊôšpHTDöžþB‹¾­aCŽ—µ>}q…”Q’–QÐã“´¤˜ÅŠ“·‘>T¡È<„þÙ‡+'€zeÞ{©ÅÊÁS؃›
IØxîÆˆŸ$mO…Ö¨‹÷¹yÄPÛþvyAš´íUg¦f4$oW¾gýˆ³×
\ No newline at end of file
diff --git a/nix/hosts/corrino/secrets/hedgedoc_environment_variables.age b/nix/hosts/corrino/secrets/hedgedoc_environment_variables.age
index cf3cac2..a5fe67e 100644
--- a/nix/hosts/corrino/secrets/hedgedoc_environment_variables.age
+++ b/nix/hosts/corrino/secrets/hedgedoc_environment_variables.age
Binary files differdiff --git a/nix/hosts/corrino/secrets/hedgedoc_oidc_client_secret.age b/nix/hosts/corrino/secrets/hedgedoc_oidc_client_secret.age
new file mode 100644
index 0000000..46d39fd
--- /dev/null
+++ b/nix/hosts/corrino/secrets/hedgedoc_oidc_client_secret.age
Binary files differdiff --git a/nix/hosts/corrino/secrets/immich_oidc_client_secret.age b/nix/hosts/corrino/secrets/immich_oidc_client_secret.age
new file mode 100644
index 0000000..be7429d
--- /dev/null
+++ b/nix/hosts/corrino/secrets/immich_oidc_client_secret.age
Binary files differdiff --git a/nix/hosts/corrino/secrets/miniflux_admin_file.age b/nix/hosts/corrino/secrets/miniflux_admin_file.age
new file mode 100644
index 0000000..3e00b9b
--- /dev/null
+++ b/nix/hosts/corrino/secrets/miniflux_admin_file.age
@@ -0,0 +1,7 @@
+age-encryption.org/v1
+-> ssh-ed25519 gvwQ2Q OGds4NLmRiMmVjPTORP3jLe3iEkqrDyTqW4V7ceFfRk
+FFdZcsT9ZruNhpY5cb674qpQpK0qzHNwRPCfHvYaKcE
+-> ssh-ed25519 m8VklA 84XSPja8dzJEUVR5olwNONVzNn5QrsX+R+WeBHqxXDo
+5CVpnTDcO0EG3NsHdFsSABWNBIe3Xe16me13JIOlfos
+--- rlIUU/0gYwxIXmpRI5/3mmZXJ+JrG/tE/3IBtpo4uT4
+²Ý-9J¬N™ÌVŸëªG.l‚.eÈ?Á©&¾§rõ;V˜#®ahSJWjh‰ôèv˜Ñ³t;3è¸É‚ϫk Ž(^QU	‹ö*ºcz·ßF°bwPZ¼uqãšÄ‰Û
\ No newline at end of file
diff --git a/nix/hosts/corrino/secrets/miniflux_oidc_client_secret.age b/nix/hosts/corrino/secrets/miniflux_oidc_client_secret.age
new file mode 100644
index 0000000..85879a9
--- /dev/null
+++ b/nix/hosts/corrino/secrets/miniflux_oidc_client_secret.age
Binary files differdiff --git a/nix/hosts/corrino/secrets/miniflux_oidc_secret.age b/nix/hosts/corrino/secrets/miniflux_oidc_secret.age
new file mode 100644
index 0000000..668c429
--- /dev/null
+++ b/nix/hosts/corrino/secrets/miniflux_oidc_secret.age
@@ -0,0 +1,8 @@
+age-encryption.org/v1
+-> ssh-ed25519 gvwQ2Q Xjrf8ip0AwW6b77m4XJupjfszP3echG2XQULubcQO14
+A2RZ+dWnrbS83yw5krGr3QuHzdxUo8RLbqstO7PqdzY
+-> ssh-ed25519 m8VklA RHSeMlhwrnG433aw1Hggck8kaVlSL94TVvn68PIS3W8
+ymqenUy96rBZnCaLzrgcAW67s4WkHUv/xRPLw3Viy9g
+--- iJRJrTP0vx6h7/uHO09JlKmq5Z/IfcrYxlIe+ZGFFlI
+-SàJ¢%·îÄ1ÃdÙZŒÛ"ž5xˆZâ5gsŒ%ã{}uÄnôÅö/›­p¶Ûw¦Â€Jò(É3…!Å1nE2„QQMJ™Œs[Ao{t½®¾rð‰Ñëç/Ä
+‰vK`Šã¨rúœD»
\ No newline at end of file
diff --git a/nix/hosts/corrino/secrets/tailscale-corrino-cert.age b/nix/hosts/corrino/secrets/tailscale-corrino-cert.age
new file mode 100644
index 0000000..ecb9e6e
--- /dev/null
+++ b/nix/hosts/corrino/secrets/tailscale-corrino-cert.age
Binary files differdiff --git a/nix/hosts/corrino/secrets/tailscale-corrino-key.age b/nix/hosts/corrino/secrets/tailscale-corrino-key.age
new file mode 100644
index 0000000..5226883
--- /dev/null
+++ b/nix/hosts/corrino/secrets/tailscale-corrino-key.age
Binary files differdiff --git a/nix/hosts/corrino/www/ctf.emile.space.nix b/nix/hosts/corrino/www/ctf.emile.space.nix
index 28c9419..a6ebd05 100644
--- a/nix/hosts/corrino/www/ctf.emile.space.nix
+++ b/nix/hosts/corrino/www/ctf.emile.space.nix
@@ -7,18 +7,19 @@
 
     locations = {
       "/" = {
-        proxyPass = "http://127.0.0.1:${toString config.emile.ports.ctf}";
+        # proxyPass = "http://127.0.0.1:${toString config.emile.ports.ctf}";
+        proxyPass = "http://138.199.213.51";
       };
     };
   };
 
-  virtualisation.oci-containers = {
-    # backend = "docker";
-    containers = {
-      "ctfd" = {
-        image = "ctfd/ctfd";
-        ports = [ "${toString config.emile.ports.ctf}:8000" ];
-      };
-    };
-  };
+  # virtualisation.oci-containers = {
+  #   # backend = "docker";
+  #   containers = {
+  #     "ctfd" = {
+  #       image = "ctfd/ctfd";
+  #       ports = [ "${toString config.emile.ports.ctf}:8000" ];
+  #     };
+  #   };
+  # };
 }
diff --git a/nix/hosts/corrino/www/git/cgit.nix b/nix/hosts/corrino/www/git/cgit.nix
index 68304db..44f5996 100644
--- a/nix/hosts/corrino/www/git/cgit.nix
+++ b/nix/hosts/corrino/www/git/cgit.nix
@@ -630,7 +630,13 @@ in
     extraGroups = [ "gitea" ];
     home = "/var/lib/git";
     uid = lib.mkForce 127;
+    # shell = "${pkgs.git}/bin/git-shell";
+    #   openssh.authorizedKeys.keys = [
+    #     "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPZi43zHEsoWaQomLGaftPE5k0RqVrZyiTtGqZlpWsew emile@caladan
+    # "
+    # ];
   };
+  
   users.groups.git = {
     gid = lib.mkForce 127;
   };
diff --git a/nix/hosts/corrino/www/git/git.nix b/nix/hosts/corrino/www/git/git.nix
deleted file mode 100644
index 3a2b9da..0000000
--- a/nix/hosts/corrino/www/git/git.nix
+++ /dev/null
@@ -1,106 +0,0 @@
-{
-  lib,
-  pkgs,
-  config,
-  ...
-}:
-
-let
-  cfg = config.services.gitea;
-in
-{
-  services.nginx.virtualHosts."git.emile.space" = {
-    forceSSL = true;
-    enableACME = true;
-
-    # TODO(emile): figure out why this doesn't work when enabled, has to do with authelia
-    # extraConfig = authelia-location;
-
-    locations = {
-      "/" = {
-        # proxyPass = "http://127.0.0.1:3000";
-        proxyPass = "http://127.0.0.1:${toString config.services.gitea.settings.server.HTTP_PORT}";
-
-        # TODO(emile): figure out why this doesn't work when enabled, has to do with authelia
-        # extraConfig = authelia-authrequest;
-      };
-    };
-  };
-
-  # auth via authelia
-  services.authelia.instances.main.settings.identity_providers.oidc.clients = [
-    {
-      id = "git";
-
-      # ; nix run nixpkgs#authelia -- crypto hash generate pbkdf2 --variant sha512 --random --random.length 72 --random.charset rfc3986
-      secret = "$pbkdf2-sha512$310000$4bi9wRkfcqnjbdmgt7rU.g$pQ2mC6GW4.BQwanGKKFhFyIx6Y.WY80xd/YpmlYOPnlnGBWpp0dSOTv6a/2yqSA5D.EuRkGCyeexSE5FdCK2TA";
-      public = false;
-      authorization_policy = "two_factor";
-      redirect_uris = [ "https://git.emile.space/user/oauth2/authelia/callback" ];
-      scopes = [
-        "openid"
-        "email"
-        "profile"
-      ];
-    }
-  ];
-
-  services.gitea = rec {
-    enable = true;
-
-    appName = "git.emile.space";
-
-    # unstable in order to use the 1.20... version
-    #package = pkgs.forgejo;
-    package = pkgs.unstable.forgejo;
-
-    stateDir = "/var/lib/gitea";
-    repositoryRoot = "${stateDir}/repositories";
-
-    settings = {
-      service.DISABLE_REGISTRATION = true;
-
-      DEFAULT = {
-        WORK_PATH = "/var/lib/gitea";
-      };
-
-      server = {
-        DOMAIN = pkgs.lib.mkForce "git.emile.space";
-        ROOT_URL = pkgs.lib.mkForce "https://git.emile.space";
-        HTTP_PORT = config.emile.ports.git;
-
-        #START_SSH_SERVER = true;
-        BUILTIN_SSH_SERVER_USER = "git";
-        SSH_USER = "gitea";
-        SSH_DOMAIN = "git.emile.space";
-
-        REPO_INDEXER_ENABLED = true;
-      };
-
-      indexer = {
-        REPO_INDEXER_ENABLED = true;
-        ISSUE_INDEXER_PATH = "${stateDir}/indexers/issues.bleve";
-        REPO_INDEXER_PATH = "${stateDir}/indexers/repos.bleve";
-        MAX_FILE_SIZE = 1048576;
-        REPO_INDEXER_INCLUDE = "";
-        REPO_INDEXER_EXCLUDE = "resources/bin/**";
-      };
-
-      #federation = {
-      #  enable = true;
-      #  share_user_statistics = true;
-      #  max_size = 4;
-      #};
-    };
-  };
-
-  users.users.git = {
-    isSystemUser = true;
-    useDefaultShell = true;
-    group = "git";
-    extraGroups = [ "gitea" ];
-    home = cfg.stateDir;
-    uid = 127;
-  };
-  users.groups.git = { };
-}
diff --git a/nix/hosts/corrino/www/goapp.emile.space.nix b/nix/hosts/corrino/www/goapp.emile.space.nix
index 361e95a..e31079e 100644
--- a/nix/hosts/corrino/www/goapp.emile.space.nix
+++ b/nix/hosts/corrino/www/goapp.emile.space.nix
@@ -12,12 +12,16 @@
     };
   };
 
+  age.secrets.goapp_oidc_client_secret.owner = "authelia-main";
+  age.secrets.goapp_oidc_client_secret.group = "authelia-main";
+  
   services.authelia.instances.main.settings.identity_providers.oidc.clients = [
     {
-      id = "goapp";
+      client_id = "goapp";
 
       # ; nix run nixpkgs#authelia -- crypto hash generate pbkdf2 --variant sha512 --random --random.length 72 --random.charset rfc3986
-      secret = "$pbkdf2-sha512$310000$LPXJRoGR9RyTcaT6cADljg$FK8RV5CnKj5ano4fXmRzzvXcX/00F7k/G6nd67t.8iewpwyq8FntV4JgYZSV8AynYMxz1qnL4j3BzITLCM0KgQ";
+      client_secret = "{{ secret \"${config.age.secrets.goapp_oidc_client_secret.path}\" }}";
+
       public = false;
       authorization_policy = "two_factor";
       redirect_uris = [
diff --git a/nix/hosts/corrino/www/grafana.emile.space.nix b/nix/hosts/corrino/www/grafana.emile.space.nix
index 22b444f..2caa4d4 100644
--- a/nix/hosts/corrino/www/grafana.emile.space.nix
+++ b/nix/hosts/corrino/www/grafana.emile.space.nix
@@ -3,134 +3,143 @@
 {
   systemd.services.grafana.serviceConfig.EnvironmentFile = config.age.secrets.grafana_env_vars.path;
 
-  services = {
-    nginx.virtualHosts = {
-      "grafana.emile.space" = {
-        addSSL = true;
-        enableACME = true;
-        locations."/" = {
-          proxyPass = "http://${toString config.services.grafana.settings.server.http_addr}:${toString config.services.grafana.settings.server.http_port}/";
-          proxyWebsockets = true;
-        };
+  
+  services.nginx.virtualHosts = {
+    "grafana.emile.space" = {
+      addSSL = true;
+      enableACME = true;
+      locations."/" = {
+        proxyPass = "http://${toString config.services.grafana.settings.server.http_addr}:${toString config.services.grafana.settings.server.http_port}/";
+        proxyWebsockets = true;
       };
     };
+  };
 
-    authelia.instances.main.settings.identity_providers.oidc.clients = [
-      {
-        id = "Grafana";
-
-        # ; nix run nixpkgs#authelia -- crypto hash generate pbkdf2 --variant sha512 --random --random.length 72 --random.charset rfc3986
-        secret = "$pbkdf2-sha512$310000$S.RE0jcmr7Sn/tjJDNxV/A$1tsYhQ/YEcVfE4JyzszHemrcUqy.84Fb6xVSmz87if5C9N46Mz2lRWB5l8s4EIrLsiumPnt4HQMkYZ4MoovJzA";
-        public = false;
-        authorization_policy = "two_factor";
-        redirect_uris = [ "https://grafana.emile.space/login/generic_oauth" ];
-        scopes = [
-          "openid"
-          "email"
-          "profile"
-          "groups"
-        ];
-        grant_types = [
-          "refresh_token"
-          "authorization_code"
-        ];
-        response_types = [ "code" ];
-        response_modes = [
-          "form_post"
-          "query"
-          "fragment"
-        ];
-      }
-    ];
-
-    grafana = {
-      enable = true;
-      settings = {
-        server = {
-          http_addr = "127.0.0.1";
-          http_port = config.emile.ports.grafana;
-          domain = "grafana.emile.space";
-          root_url = "https://grafana.emile.space/";
-        };
+  age.secrets.grafana_oidc_client_secret.owner = "authelia-main";
+  age.secrets.grafana_oidc_client_secret.group = "authelia-main";
 
-        "auth.generic_oauth" =
-          let
-            sso = "https://sso.emile.space/api/oidc";
-          in
-          {
-            enabled = true;
-            client_id = "Grafana";
-
-            # [auth.generic_oauth]
-            # client_secret = ... 
-            #   set in env var as 
-            #   GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET 
-            client_secret = "set in env var this is just a placeholder";
-
-            use_refresh_token = true;
-            token_url = "${sso}/token";
-            auth_url = "${sso}/authorization";
-            api_url = "${sso}/userinfo";
-
-            scopes = [
-              "openid"
-              "email"
-              "profile"
-              "groups"
-            ];
-
-            email_attribute_path = "email";
-            login_attribute_path = "preferred_username";
-            name_attribute_path = "name";
-
-            role_attribute_path = "contains(groups[*], 'grafana_server_admin') && 'GrafanaAdmin' || contains(groups[*], 'grafana_admin') && 'Admin' || contains(groups[*], 'grafana_editor') && 'Editor' || 'Viewer'";
-
-          };
+  services.authelia.instances.main.settings.identity_providers.oidc.clients = [
+    {
+      client_id = "Grafana";
+
+      # ; nix run nixpkgs#authelia -- crypto hash generate pbkdf2 --variant sha512 --random --random.length 72 --random.charset rfc3986
+      client_secret = "{{ secret \"${config.age.secrets.grafana_oidc_client_secret.path}\" }}";
+
+      public = false;
+      authorization_policy = "two_factor";
+      redirect_uris = [ "https://grafana.emile.space/login/generic_oauth" ];
+      scopes = [
+        "openid"
+        "email"
+        "profile"
+        "groups"
+      ];
+      grant_types = [
+        "refresh_token"
+        "authorization_code"
+      ];
+      response_types = [ "code" ];
+      response_modes = [
+        "form_post"
+        "query"
+        "fragment"
+      ];
+    }
+  ];
+
+  services.grafana = {
+    enable = true;
+    settings = {
+      server = {
+        http_addr = "127.0.0.1";
+        http_port = config.emile.ports.grafana;
+        domain = "grafana.emile.space";
+        root_url = "https://grafana.emile.space/";
       };
 
-      provision = {
-        dashboards.settings = { };
-        datasources.settings = {
-          datasources = [
-            {
-              url = "http://localhost:${toString config.services.prometheus.port}";
-              type = "prometheus";
-              name = "Prometheus";
-              editable = false;
-              access = "proxy"; # server = "proxy", browser = "direct"
-            }
-            {
-              name = "loki";
-              url = "http://${config.services.loki.configuration.common.instance_addr}:${toString config.services.loki.configuration.server.http_listen_port}";
-              type = "loki";
-            }
+      "auth.generic_oauth" =
+        let
+          sso = "https://sso.emile.space/api/oidc";
+        in
+        {
+          enabled = true;
+          client_id = "Grafana";
+
+          # [auth.generic_oauth]
+          # client_secret = ... 
+          #   set in env var as 
+          #   GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET 
+          client_secret = "set in env var this is just a placeholder";
+
+          use_refresh_token = true;
+          token_url = "${sso}/token";
+          auth_url = "${sso}/authorization";
+          api_url = "${sso}/userinfo";
+
+          scopes = [
+            "openid"
+            "email"
+            "profile"
+            "groups"
           ];
+
+          email_attribute_path = "email";
+          login_attribute_path = "preferred_username";
+          name_attribute_path = "name";
+
+          role_attribute_path = "contains(groups[*], 'grafana_server_admin') && 'GrafanaAdmin' || contains(groups[*], 'grafana_admin') && 'Admin' || contains(groups[*], 'grafana_editor') && 'Editor' || 'Viewer'";
+
         };
+    };
 
-        # TODO(emile): finish setting up the grafana notifier filling out the settings section
-        # notifiers = [
-        #   {
-        #     uid = "2ad1c1d1-bcd9-4cb8-8897-c89c5820ffb1";
-        #     type = "email";
-        #     settings = {};
-        #     org_name = "Main Org.";
-        #     org_id = 1;
-        #     name = "email";
-        #     id_default = true;
-        #     frequency = "5m";
-        #     disable_resolve_message = false;
-        #   }
-        # ];
-
-        # TODO(emile): finish setting up the alerting stuff within here
-        # alerting = {
-        #   templates.settings = { };
-        #   rules.settings = {};
-        #   policies.settings = {};
-        #   muteTimings.settings = {};
-        #   contactPoints.settings = {};
-        # };
+    provision = {
+      dashboards.settings = { };
+      datasources.settings = {
+        deleteDatasources = [
+          { name = "Prometheus"; orgId = 1; }  
+          { name = "Lampadas"; orgId = 1; }  
+        ];
+        datasources = [
+          {
+            url = "http://localhost:${toString config.services.prometheus.port}";
+            type = "prometheus";
+            name = "Prometheus Corrino";
+            editable = false;
+            access = "proxy"; # server = "proxy", browser = "direct"
+          }
+          {
+            url = "http://lampadas:9009";
+            type = "prometheus";
+            name = "Prometheus Lampadas";
+            editable = false;
+            access = "proxy"; # server = "proxy", browser = "direct"
+          }
+        ];
       };
+
+      # TODO(emile): finish setting up the grafana notifier filling out the settings section
+      # notifiers = [
+      #   {
+      #     uid = "2ad1c1d1-bcd9-4cb8-8897-c89c5820ffb1";
+      #     type = "email";
+      #     settings = {};
+      #     org_name = "Main Org.";
+      #     org_id = 1;
+      #     name = "email";
+      #     id_default = true;
+      #     frequency = "5m";
+      #     disable_resolve_message = false;
+      #   }
+      # ];
+
+      # TODO(emile): finish setting up the alerting stuff within here
+      # alerting = {
+      #   templates.settings = { };
+      #   rules.settings = {};
+      #   policies.settings = {};
+      #   muteTimings.settings = {};
+      #   contactPoints.settings = {};
+      # };
     };
   };
 }
diff --git a/nix/hosts/corrino/www/hydra.emile.space.nix b/nix/hosts/corrino/www/hydra.emile.space.nix
index a5cdb53..fe70bf4 100644
--- a/nix/hosts/corrino/www/hydra.emile.space.nix
+++ b/nix/hosts/corrino/www/hydra.emile.space.nix
@@ -21,9 +21,10 @@
   services.hydra = {
     enable = true;
 
-    package = pkgs.hydra_unstable.overrideAttrs (old: {
-      patches = (if old ? patches then old.patches else [ ]) ++ [ ./hydra.patch ];
-    });
+    #package = pkgs.hydra_unstable.overrideAttrs (old: {
+    #  patches = (if old ? patches then old.patches else [ ]) ++ [ ./hydra.patch ];
+    #});
+    package = pkgs.hydra;
 
     listenHost = "*";
     port = config.emile.ports.hydra;
diff --git a/nix/hosts/corrino/www/irc.emile.space.nix b/nix/hosts/corrino/www/irc.emile.space.nix
index ac00445..7653cb4 100644
--- a/nix/hosts/corrino/www/irc.emile.space.nix
+++ b/nix/hosts/corrino/www/irc.emile.space.nix
@@ -1,155 +1,198 @@
-{ config, ... }:
+{ config, pkgs, ... }:
 
 {
+  ##############################################################################
+  # Client
+  ##############################################################################
+  # gamja web client
+  # https://codeberg.org/emersion/gamja
+  #
+  # nah, let's just use the commaond line "senpai" client, from the same person
+
+  ##############################################################################
+  # Bouncer
+  ##############################################################################
+  # soju bouncer
+  # https://soju.im
+
+  # services.soju = {
+  #   enable = true;
+  #   listen = [ "127.0.0.1:${toString config.emile.ports.irc.bouncer}" ];
+
+  #   hostName = "irc.emile.space";
+  #   httpOrigins = [ "127.0.0.1" "irc.emile.space" ];
+
+  #   # tlsCertificateKey = "/var/lib/acme/irc.emile.space/key.pem";
+  #   # tlsCertificate = "/var/lib/acme/irc.emile.space/cert.pem";
+  # };
+
+  # services.soju = {
+  #   enable = true;
+  #   package = pkgs.soju;
+  #   listen = [ "127.0.0.1:${toString config.emile.ports.irc.bouncer}" ];
+  #   adminSocket.enable = true;
+  #   # tlsCertificateKey = "/var/lib/acme/irc.emile.space/key.pem";
+  #   # tlsCertificate = "/var/lib/acme/irc.emile.space/cert.pem";
+  #   httpOrigins = [ "127.0.0.1" "irc.emile.space" ];
+  #   hostName = "irc.emile.space";
+  #   extraConfig = "";
+  #   enableMessageLogging = true;
+  #   acceptProxyIP = [];
+  # };
+
+  ##############################################################################
+  # Server
+  ##############################################################################
+
   # Create a tls cert for the irc server
-  security.acme.certs = {
-    "irc.emile.space" = {
-      webroot = "/var/lib/acme/acme-challenge/";
-      email = "acme@emile.space";
-      postRun = "cp fullchain.pem /home/ergo/ && cp key.pem /home/ergo && chown ergo:ergo /home/ergo/*.pem && systemctl reload ergo.service";
-    };
-  };
-
-  # Allow ergo to access the created cert
-  # The systemd server runs using a dynamic user, so the below inserts the .pem files
-  #   into "/run/credentials/ergochat.service/key.pem"
-  systemd.services.ergochat.serviceConfig = {
-    LoadCredential = [
-      "fullchain.pem:/var/lib/acme/irc.emile.space/fullchain.pem"
-      "key.pem:/var/lib/acme/irc.emile.space/key.pem"
-    ];
-  };
-
-  # allow connections to the port from the "outside"
-  networking.firewall.allowedTCPPorts = [ config.emile.ports.irc.ssl ];
-
-  services.ergochat = {
-    enable = true;
-
-    # https://raw.githubusercontent.com/ergochat/ergo/master/default.yaml
-    settings = {
-      accounts = {
-        authentication-enabled = true;
-        multiclient = {
-          allowed-by-default = true;
-          always-on = "opt-out";
-          auto-away = "opt-out";
-          enabled = true;
-        };
-        registration = {
-          enabled = true;
-          allow-before-connect = true;
-          bcrypt-cost = 4;
-          email-verification = {
-            enabled = false;
-          };
-          throttling = {
-            duration = "10m";
-            enabled = true;
-            max-attempts = 30;
-          };
-        };
-      };
-      channels = {
-        default-modes = "+ntC";
-        registration = {
-          enabled = true;
-        };
-      };
-      datastore = {
-        autoupgrade = true;
-        path = "/var/lib/ergo/ircd.db";
-      };
-      history = {
-        enabled = true;
-        autoreplay-on-join = 0;
-        autoresize-window = "3d";
-        channel-length = 2048;
-        chathistory-maxmessages = 100;
-        client-length = 256;
-        restrictions = {
-          expire-time = "1w";
-          grace-period = "1h";
-          query-cutoff = "none";
-        };
-        retention = {
-          allow-individual-delete = false;
-          enable-account-indexing = false;
-        };
-        tagmsg-storage = {
-          default = false;
-          whitelist = [
-            "+draft/react"
-            "+react"
-          ];
-        };
-        znc-maxmessages = 2048;
-      };
-      limits = {
-        awaylen = 390;
-        channellen = 64;
-        identlen = 20;
-        kicklen = 390;
-        nicklen = 32;
-        topiclen = 390;
-      };
-      network = {
-        name = "emilespace";
-      };
-      server = {
-        casemapping = "permissive";
-        check-ident = false;
-        enforce-utf = true;
-        forward-confirm-hostnames = false;
-        ip-cloaking = {
-          enabled = false;
-        };
-        ip-limits = {
-          count = false;
-          throttle = false;
-        };
-        listeners = {
-          # sts only port
-          ":6667".sts-only = true;
-
-          # loopback listeners
-          # "127.0.0.1:6668" = {};
-          # "[::]:6668" = {};
-
-          ":${toString config.emile.ports.irc.ssl}" = {
-            tls = {
-              cert = "/run/credentials/ergochat.service/fullchain.pem";
-              key = "/run/credentials/ergochat.service/key.pem";
-            };
-
-            # for cloud load balancers setting a PROXY header, NOT reverse proxies...
-            proxy = false;
-
-            min-tls-version = 1.2;
-          };
-        };
-        lookup-hostnames = false;
-        max-sendq = "1M";
-        name = "emile.space";
-        relaymsg = {
-          enabled = false;
-        };
-        sts = {
-          enabled = true; # redirect from plain to tls if supported
-
-          # how long clients should be forced to use TLS for.
-          # (Emile): no clue why, can I set something like \infty here?
-          duration = "12m";
-
-        };
-      };
-      logging = [
-        {
-          method = "stderr";
-          type = "* -userinput -useroutput";
-          level = "debug";
-        }
-      ];
-    };
-  };
+  # security.acme.certs = {
+  #   "irc.emile.space" = {
+  #     webroot = "/var/lib/acme/acme-challenge/";
+  #     email = "acme@emile.space";
+  #     # postRun = "cp fullchain.pem /home/ergo/ && cp key.pem /home/ergo && chown ergo:ergo /home/ergo/*.pem && systemctl reload ergo.service";
+  #   };
+  # };
+
+  # # Allow ergo to access the created cert
+  # # The systemd server runs using a dynamic user, so the below inserts the .pem files
+  # #   into "/run/credentials/ergochat.service/key.pem"
+  # systemd.services.ergochat.serviceConfig = {
+  #   LoadCredential = [
+  #     "fullchain.pem:/var/lib/acme/irc.emile.space/fullchain.pem"
+  #     "key.pem:/var/lib/acme/irc.emile.space/key.pem"
+  #   ];
+  # };
+
+  # # allow connections to the port from the "outside"
+  # networking.firewall.allowedTCPPorts = [ config.emile.ports.irc.ssl ];
+
+  # services.ergochat = {
+  #   enable = true;
+
+  #   # https://raw.githubusercontent.com/ergochat/ergo/master/default.yaml
+  #   settings = {
+  #     accounts = {
+  #       authentication-enabled = true;
+  #       multiclient = {
+  #         allowed-by-default = true;
+  #         always-on = "opt-out";
+  #         auto-away = "opt-out";
+  #         enabled = true;
+  #       };
+  #       registration = {
+  #         enabled = true;
+  #         allow-before-connect = true;
+  #         bcrypt-cost = 4;
+  #         email-verification = {
+  #           enabled = false;
+  #         };
+  #         throttling = {
+  #           duration = "10m";
+  #           enabled = true;
+  #           max-attempts = 30;
+  #         };
+  #       };
+  #     };
+  #     channels = {
+  #       default-modes = "+ntC";
+  #       registration = {
+  #         enabled = true;
+  #       };
+  #     };
+  #     datastore = {
+  #       autoupgrade = true;
+  #       path = "/var/lib/ergo/ircd.db";
+  #     };
+  #     history = {
+  #       enabled = true;
+  #       autoreplay-on-join = 0;
+  #       autoresize-window = "3d";
+  #       channel-length = 2048;
+  #       chathistory-maxmessages = 100;
+  #       client-length = 256;
+  #       restrictions = {
+  #         expire-time = "1w";
+  #         grace-period = "1h";
+  #         query-cutoff = "none";
+  #       };
+  #       retention = {
+  #         allow-individual-delete = false;
+  #         enable-account-indexing = false;
+  #       };
+  #       tagmsg-storage = {
+  #         default = false;
+  #         whitelist = [
+  #           "+draft/react"
+  #           "+react"
+  #         ];
+  #       };
+  #       znc-maxmessages = 2048;
+  #     };
+  #     limits = {
+  #       awaylen = 390;
+  #       channellen = 64;
+  #       identlen = 20;
+  #       kicklen = 390;
+  #       nicklen = 32;
+  #       topiclen = 390;
+  #     };
+  #     network = {
+  #       name = "emilespace";
+  #     };
+  #     server = {
+  #       casemapping = "permissive";
+  #       check-ident = false;
+  #       enforce-utf = true;
+  #       forward-confirm-hostnames = false;
+  #       ip-cloaking = {
+  #         enabled = false;
+  #       };
+  #       ip-limits = {
+  #         count = false;
+  #         throttle = false;
+  #       };
+  #       listeners = {
+  #         # sts only port
+  #         ":6667".sts-only = true;
+
+  #         # loopback listeners
+  #         # "127.0.0.1:6668" = {};
+  #         # "[::]:6668" = {};
+
+  #         ":${toString config.emile.ports.irc.ssl}" = {
+  #           tls = {
+  #             cert = "/run/credentials/ergochat.service/fullchain.pem";
+  #             key = "/run/credentials/ergochat.service/key.pem";
+  #           };
+
+  #           # for cloud load balancers setting a PROXY header, NOT reverse proxies...
+  #           proxy = false;
+
+  #           min-tls-version = 1.2;
+  #         };
+  #       };
+  #       lookup-hostnames = false;
+  #       max-sendq = "1M";
+  #       name = "emile.space";
+  #       relaymsg = {
+  #         enabled = false;
+  #       };
+  #       sts = {
+  #         enabled = true; # redirect from plain to tls if supported
+
+  #         # how long clients should be forced to use TLS for.
+  #         # (Emile): no clue why, can I set something like \infty here?
+  #         duration = "12m";
+
+  #       };
+  #     };
+  #     logging = [
+  #       {
+  #         method = "stderr";
+  #         type = "* -userinput -useroutput";
+  #         level = "debug";
+  #       }
+  #     ];
+  #   };
+  # };
 }
diff --git a/nix/hosts/corrino/www/loki.emile.space.nix b/nix/hosts/corrino/www/loki.emile.space.nix
deleted file mode 100644
index e5bfe24..0000000
--- a/nix/hosts/corrino/www/loki.emile.space.nix
+++ /dev/null
@@ -1,64 +0,0 @@
-{ config, ... }:
-
-{
-  services = {
-    loki = {
-      enable = false;
-      configuration = {
-        auth_enabled = false;
-        server = {
-          http_listen_port = config.emile.ports.loki;
-        };
-
-        limits_config = {
-          reject_old_samples = false;
-          reject_old_samples_max_age = "7d";
-          max_global_streams_per_user = 100000;
-          max_streams_per_user = 100000;
-
-          retention_period = "10m";
-        };
-
-        compactor = {
-          retention_enabled = true;
-          delete_request_store = "tsdb";
-        };
-
-        common = {
-          instance_addr = "127.0.0.1";
-          ring = {
-            instance_addr = "127.0.0.1";
-            kvstore.store = "inmemory";
-          };
-          replication_factor = 1;
-          path_prefix = "/tmp/loki";
-        };
-
-        # limits_config.allow_structured_metadata = false;
-
-        schema_config.configs = [
-          {
-            from = "2023-05-09";
-            store = "tsdb";
-            object_store = "filesystem";
-            schema = "v13";
-            index = {
-              prefix = "index_";
-              period = "24h";
-            };
-          }
-          {
-            from = "2024-10-18";
-            store = "tsdb";
-            object_store = "filesystem";
-            schema = "v13";
-            index = {
-              prefix = "index_";
-              period = "24h";
-            };
-          }
-        ];
-      };
-    };
-  };
-}
diff --git a/nix/hosts/corrino/www/mc.emile.space.nix b/nix/hosts/corrino/www/mc.emile.space.nix
new file mode 100644
index 0000000..1a081bc
--- /dev/null
+++ b/nix/hosts/corrino/www/mc.emile.space.nix
@@ -0,0 +1,153 @@
+{ config, pkgs, ... }:
+
+{
+  services.minecraft-server = {
+    package = pkgs.minecraft-server;
+    serverProperties = {
+      server-port = 43000;
+
+      # 0 peaceful
+      # 1 easy
+      # 2 normal
+      # 3 hard
+      difficulty = 1;
+
+      # 0 survival
+      # 1 creative
+      # 2 adventure
+      # 5 default
+      # "spectator" spectator
+      # gamemode = "survival";
+      gamemode = 0;
+
+      max-players = 10;
+      motd = "Neurodivergenter Hexenzirkel";
+      enable-rcon = true;
+      "rcon.password" = "hunter2";
+      enable-command-block = false;
+      enable-query = false;
+      spawn-protection = 0;
+
+      white-list = true;
+    };
+    openFirewall = true;
+
+    whitelist = {
+      "emileemail" = "a7614a53-b8b8-47b7-91cf-860e7c7f325f";
+      "dodonator23" = "f93506b6-76e8-437d-927d-dceeb833a33f";
+      "ChaosAyumi" = "223040ec-ca30-4238-8b58-c81597c30426";
+      "xerunala" = "962e41c8-1da8-4592-9a2f-e36cdb20d5a6";
+      "rappet" = "588377a5-362f-4ea1-8195-9cf97dd7a884";
+    };
+
+    jvmOpts = "-Xms4092M -Xmx4092M";
+    eula = true;
+    enable = true;
+    declarative = true;
+    dataDir = "/var/lib/minecraft";
+  };
+
+  services.nginx.virtualHosts."mc.emile.space" = {
+    forceSSL = true;
+    enableACME = true;
+  };
+
+  services.bluemap = {
+    enable = true;
+
+    enableNginx = true;
+    host = "mc.emile.space";
+
+    webappSettings = {
+      enabled = true;
+      webroot = config.services.bluemap.webRoot;
+    };
+
+    # webserverSettings = {};
+    webserverSettings.enabled = false; # using nginx;
+    webRoot = "/var/lib/bluemap/web";
+
+    # coreSettings = {};
+    coreSettings.data = "/var/lib/bluemap";
+    coreSettings.metrics = false; # don't send data to the devs
+
+    storage = {
+      "file" = {
+        root = "${config.services.bluemap.webRoot}/maps";
+      };
+    };
+    # storage.<name>.storage-type
+
+    maps = let
+      worldpath = "/var/lib/minecraft/world";
+    in {
+      "overworld" = {
+        world = "${worldpath}";
+        ambient-light = 0.1;
+        cave-detection-ocean-floor = -5;
+        dimension = "minecraft:overworld";
+      };
+
+      "nether" = {
+        world = "${worldpath}/DIM-1";
+        sorting = 100;
+        sky-color = "#290000";
+        void-color = "#150000";
+        ambient-light = 0.6;
+        world-sky-light = 0;
+        remove-caves-below-y = -10000;
+        cave-detection-ocean-floor = -5;
+        cave-detection-uses-block-light = true;
+        max-y = 90;
+        dimension = "minecraft:the_nether";
+      };
+
+      "end" = {
+        world = "${worldpath}/DIM1";
+        sorting = 200;
+        sky-color = "#080010";
+        void-color = "#080010";
+        ambient-light = 0.6;
+        world-sky-light = 0;
+        remove-caves-below-y = -10000;
+        cave-detection-ocean-floor = -5;
+        dimension = "minecraft:the_end";
+      };
+    };
+
+    # A set of resourcepacks, datapacks, and mods to extract resources from, loaded in alphabetical order.
+    packs = {};
+
+    # How often to trigger rendering the map, in the format of a systemd timer onCalendar configuration. See systemd.timer(5).
+    #
+    # This one means "every three hours":
+    # *-*-* */3:00:00
+    onCalendar = "*-*-* *:00:00";
+
+    eula = true;
+
+    enableRender = true;
+
+    # The world used by the default map ruleset. If you configure your own maps you do not need to set this.
+    # defaultWorld = "${config.services.minecraft.dataDir}/world";
+     
+    addons = {};
+  };
+
+  services.restic.backups."corrino" = {
+    paths = [ "/var/lib/minecraft" ];
+  };
+
+  services.restic.backups."minecraft" = {
+    repository = "/mnt/storagebox-bx11/minecraft";
+    paths = [ "/var/lib/minecraft" ];
+    passwordFile = config.age.secrets.restic_password.path;
+    initialize = true;
+    pruneOpts = [
+      "--keep-daily 7"
+      "--keep-weekly 5"
+      "--keep-monthly 12"
+      "--keep-yearly 75"
+    ];
+  };
+}
diff --git a/nix/hosts/corrino/www/md.emile.space.nix b/nix/hosts/corrino/www/md.emile.space.nix
index 6088ea0..1ee46fd 100644
--- a/nix/hosts/corrino/www/md.emile.space.nix
+++ b/nix/hosts/corrino/www/md.emile.space.nix
@@ -6,18 +6,21 @@
     enableACME = true;
     locations = {
       "/" = {
-        proxyPass = "http://127.0.0.1:${toString config.services.hedgedoc.settings.port}";
+        proxyPass = "http://[${config.services.hedgedoc.settings.host}]:${toString config.services.hedgedoc.settings.port}";
       };
     };
   };
 
+  age.secrets.hedgedoc_oidc_client_secret.owner = "authelia-main";
+  age.secrets.hedgedoc_oidc_client_secret.group = "authelia-main";
+  
   # auth via authelia
   services.authelia.instances.main.settings.identity_providers.oidc.clients = [
     {
-      id = "HedgeDoc";
+      client_id = "HedgeDoc";
 
       # ; nix run nixpkgs#authelia -- crypto hash generate pbkdf2 --variant sha512 --random --random.length 72 --random.charset rfc3986
-      secret = "$pbkdf2-sha512$310000$l4Kyec7Q9oY2GAhWA/xMig$P/MYFmulfgsDNyyiclUzd6le0oSiOvqCIvl4op5DkXtVTxLWlMA3ZwhJ6Z7u.OfIREuEM2htH6asxWPhBhkpNQ";
+      client_secret = "{{ secret \"${config.age.secrets.hedgedoc_oidc_client_secret.path}\" }}";
       public = false;
       authorization_policy = "two_factor";
       redirect_uris = [ "https://md.emile.space/auth/oauth2/callback" ];
@@ -47,7 +50,7 @@
     environmentFile = config.age.secrets.hedgedoc_environment_variables.path;
 
     settings = {
-      host = "127.0.0.1";
+      host = "::1";
       port = config.emile.ports.md;
 
       domain = "md.emile.space";
@@ -85,28 +88,20 @@
     };
   };
 
-  # backups
-  # services.restic.backups."hedgedoc" = {
-  #   user = "u331921";
-  #   timerConfig = {
-  #     OnCalendar = "daily";
-  #     Persistent = true;
-  #   };
-  #   # repository = "stfp:u331921@u331921.your-storagebox-de:23/restic";
-  #   repository = "/mnt/storagebox-bx11/backup/hedgedoc";
-  #   initialize = true; # initializes the repo, don't set if you want manual control
-  #   passwordFile = config.age.secrets.restic_password.path;
-  #   paths = [ "/var/lib/hedgedoc/" ];
-  #   pruneOpts = [
-  #     "--keep-daily 7"
-  #     "--keep-weekly 5"
-  #     "--keep-monthly 12"
-  #     "--keep-yearly 75"
-  #   ];
-
-  #   # extraOpts = [
-  #   #   "sftp.command='ssh backup@192.168.1.100 -i /home/user/.ssh/id_rsa -s sftp'"
-  #   # ];
-  # };
+  services.restic.backups."corrino" = {
+    paths = [ "/var/lib/hedgedoc" ];
+  };
 
+  services.restic.backups."hedgedoc" = {
+    repository = "/mnt/storagebox-bx11/hedgedoc";
+    paths = [ "/var/lib/hedgedoc" ];
+    passwordFile = config.age.secrets.restic_password.path;
+    initialize = true;
+    pruneOpts = [
+      "--keep-daily 7"
+      "--keep-weekly 5"
+      "--keep-monthly 12"
+      "--keep-yearly 75"
+    ];
+  };
 }
diff --git a/nix/hosts/corrino/www/miniflux.emile.space.nix b/nix/hosts/corrino/www/miniflux.emile.space.nix
new file mode 100644
index 0000000..90cb8f2
--- /dev/null
+++ b/nix/hosts/corrino/www/miniflux.emile.space.nix
@@ -0,0 +1,81 @@
+{ config, pkgs, ... }:
+
+{
+	services.nginx.virtualHosts."miniflux.emile.space" = {
+		forceSSL = true;
+		enableACME = true;
+		locations = {
+			"/" = {
+				proxyPass = "http://${config.services.miniflux.config.LISTEN_ADDR}";
+			};
+		};
+	};
+
+	# oidc not working and I can't bother to continue debugging it now
+	# 
+	# Apr 12 15:37:38 corrino authelia[3693799]: {"level":"error","method":"POST","msg":"Access Request failed with error: Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The request was determined to be using 'token_endpoint_auth_method' method 'none', however the OAuth 2.0 client registration does not allow this method. The registered client with id 'miniflux' is configured to only support 'token_endpoint_auth_method' method 'client_secret_basic'. Either the Authorization Server client registration will need to have the 'token_endpoint_auth_method' updated to 'none' or the Relying Party will need to be configured to use 'client_secret_basic'.
+	#
+  # age.secrets.miniflux_oidc_client_secret.owner = "authelia-main";
+  # age.secrets.miniflux_oidc_client_secret.group = "authelia-main";
+	# 
+  # auth via authelia
+  # services.authelia.instances.main.settings.identity_providers.oidc.clients = [
+  #   {
+  #     client_id = "miniflux";
+
+  #     # ; nix run nixpkgs#authelia -- crypto hash generate pbkdf2 --variant sha512 --random --random.length 72 --random.charset rfc3986
+	#     client_secret = "{{ secret \"${config.age.secrets.miniflux_oidc_client_secret.path}\" }}";
+  #     public = false;
+  #     authorization_policy = "two_factor";
+  #     redirect_uris = [ "https://miniflux.emile.space/oauth2/oidc/callback" ];
+  #     scopes = [
+  #       "openid"
+  #       "email"
+  #       "profile"
+  #     ];
+  #     # grant_types = [
+  #     #   "refresh_token"
+  #     #   "authorization_code"
+  #     # ];
+  #     # response_types = [ "code" ];
+  #     # response_modes = [
+  #     #   "form_post"
+  #     #   "query"
+  #     #   "fragment"
+  #     # ];
+  #     # token_endpoint_auth_method = "client_secret_post";
+  #     # token_endpoint_auth_method = "none";
+  #   }
+  # ];
+
+	services.miniflux = {
+		enable = true;
+		package = pkgs.miniflux;
+		config = {
+		  LISTEN_ADDR = "[::1]:${toString config.emile.ports.miniflux}";
+			BASE_URL = "https://miniflux.emile.space";
+
+			# Cleanup job frequency to remove old sessions and archive entries.
+		  CLEANUP_FREQUENCY = 48;
+
+			# Set to 1 to enable maintenance mode. Maintenance mode disables the web ui and show a text message to the users.
+			# MAINTENANCE_MODE = 1;
+			# MAINTENANCE_MESSAGE = "updating foo";
+			
+			# DISABLE_LOCAL_AUTH = "true";
+			# OAUTH2_CLIENT_ID = "miniflux";
+			# OAUTH2_USER_CREATION = 1;
+			# OAUTH2_CLIENT_SECRET_FILE = config.age.secrets.miniflux_oidc_secret.path;
+			# OAUTH2_OIDC_DISCOVERY_ENDPOINT = "https://sso.emile.space";
+			# OAUTH2_OIDC_PROVIDER_NAME = "authelia";
+			# OAUTH2_PROVIDER = "oidc";
+			# OAUTH2_REDIRECT_URL = "https://miniflux.emile.space/oauth2/oidc/callback";
+			
+			LOG_LEVEL = "debug";
+		};
+		createDatabaseLocally = true;
+
+		# File containing the ADMIN_USERNAME and ADMIN_PASSWORD (length >= 6) in the format of an EnvironmentFile=, as described by systemd.exec(5).
+		adminCredentialsFile = config.age.secrets.miniflux_admin_file.path;
+	};
+}
diff --git a/nix/hosts/corrino/www/photo/immich.nix b/nix/hosts/corrino/www/photo/immich.nix
index 92a3a64..3e1bf48 100644
--- a/nix/hosts/corrino/www/photo/immich.nix
+++ b/nix/hosts/corrino/www/photo/immich.nix
@@ -6,6 +6,15 @@
     forceSSL = true;
     enableACME = true;
     locations = {
+      # # immich private proxy
+      # "/share" = {
+      #   proxyPass = "http://${config.services.immich.host}:${toString config.services.immich-public-proxy.port}";
+      # };
+      # "/share/*" = {
+      #   proxyPass = "http://${config.services.immich.host}:${toString config.services.immich-public-proxy.port}";
+      # };
+
+      # immich
       "/" = {
         proxyPass = "http://${config.services.immich.host}:${toString config.services.immich.port}";
         proxyWebsockets = true;
@@ -13,13 +22,17 @@
     };
   };
 
+  age.secrets.immich_oidc_client_secret.owner = "authelia-main";
+  age.secrets.immich_oidc_client_secret.group = "authelia-main";
+
   # auth via authelia
   services.authelia.instances.main.settings.identity_providers.oidc.clients = [
     {
-      id = "Immich";
+      client_id = "Immich";
 
       # ; nix run nixpkgs#authelia -- crypto hash generate pbkdf2 --variant sha512 --random --random.length 72 --random.charset rfc3986
-      secret = "$pbkdf2-sha512$310000$iCgyAKjoYH9UKADProvbgw$LjrYkX1MjjtSXWDkxDjyp3NkLLuLVvKVwy3o8/Rw.8Z8b6yCkPWdBCothuCMlaGcgfG/zLWM6lRV4BrXVZpkig";
+      client_secret = "{{ secret \"${config.age.secrets.immich_oidc_client_secret.path}\" }}";
+
       public = false;
       authorization_policy = "two_factor";
       redirect_uris = [
@@ -43,15 +56,14 @@
       #  "fragment"
       #];
 
-      token_endpoint_auth_method = "client_secret_basic";
+      # token_endpoint_auth_method = "client_secret_basic";
 
       # might be needed since the upgrade to nixos-24.11 and the resulting
       # 4.37.5 -> 4.38.17 upgrade
-      # token_endpoint_auth_method = "client_secret_post";
+      token_endpoint_auth_method = "client_secret_post";
     }
   ];
 
-
   services.immich = {
     enable = true;
     package = pkgs.immich;
@@ -59,7 +71,7 @@
     secretsFile = config.age.secrets.immich_secrets_file.path;
 
     host = "127.0.0.1";
-    port = config.emile.ports.immich;
+    port = config.emile.ports.photo.immich;
 
     machine-learning = {
       enable = false;
@@ -68,4 +80,19 @@
       };
     };
   };
+
+  # services.immich-public-proxy = {
+  #   enable = true;
+  #   package = pkgs.immich-public-proxy;
+  #   settings = {
+  #     downloadOriginalPhoto = true;
+  #     showGalleryTitle = true;
+  #     allowDownloadAll = 1;
+  #     showHomePage = true;
+  #     showMetadata = true;
+  #   };
+  #   port = config.emile.ports.photo.immich-public-proxy;
+  #   openFirewall = false;
+  #   immichUrl = "photo.emile.space";
+  # };
 }
diff --git a/nix/hosts/corrino/www/prometheus.emile.space.nix b/nix/hosts/corrino/www/prometheus.emile.space.nix
index 898f3b2..ec13dfa 100644
--- a/nix/hosts/corrino/www/prometheus.emile.space.nix
+++ b/nix/hosts/corrino/www/prometheus.emile.space.nix
@@ -38,15 +38,43 @@
           enable = true;
           port = config.emile.ports.prometheus.exporter.nginx;
         };
+        restic = {
+          enable = true;
+          # repository = "sftp:u331921@u331921.your-storagebox.de:/home/backup";
+          repository = "/mnt/storagebox-bx11/corrino";
+          port = config.emile.ports.prometheus.exporter.restic;
+          passwordFile = config.age.secrets.restic_password.path;
+        };
       };
       scrapeConfigs = [
         {
           job_name = "corrino";
           static_configs = [
-            { targets = [ "127.0.0.1:${toString config.services.prometheus.exporters.node.port}" ]; }
-            { targets = [ "127.0.0.1:${toString config.services.prometheus.exporters.systemd.port}" ]; }
-            { targets = [ "127.0.0.1:${toString config.services.prometheus.exporters.smartctl.port}" ]; }
-            { targets = [ "127.0.0.1:${toString config.services.prometheus.exporters.nginx.port}" ]; }
+            {
+              targets = [
+                "127.0.0.1:${toString config.services.prometheus.exporters.node.port}"
+              ];
+            }
+            {
+              targets = [
+                "127.0.0.1:${toString config.services.prometheus.exporters.systemd.port}"
+              ];
+            }
+            {
+              targets = [
+                "127.0.0.1:${toString config.services.prometheus.exporters.smartctl.port}"
+              ];
+            }
+            {
+              targets = [
+                "127.0.0.1:${toString config.services.prometheus.exporters.nginx.port}"
+              ];
+            }
+            {
+              targets = [
+                "127.0.0.1:${toString config.services.prometheus.exporters.restic.port}"
+              ];
+            }
           ];
         }
         {
diff --git a/nix/hosts/corrino/www/promtail.emile.space.nix b/nix/hosts/corrino/www/promtail.emile.space.nix
deleted file mode 100644
index 654414c..0000000
--- a/nix/hosts/corrino/www/promtail.emile.space.nix
+++ /dev/null
@@ -1,114 +0,0 @@
-{ config, ... }:
-
-{
-  # allow the promtail user to read the nginx access files
-  users.users.promtail.extraGroups = [ "nginx" ];
-
-  services = {
-    promtail = {
-      enable = true;
-      configuration = {
-        server = {
-          http_listen_port = config.emile.ports.promtail;
-          grpc_listen_port = 0;
-        };
-        positions.filename = "/tmp/positions.yml";
-        clients = [{
-          url = "http://localhost:${toString config.services.loki.configuration.server.http_listen_port}/loki/api/v1/push";
-        }];
-        scrape_configs = [
-
-          # systemd
-          {
-            job_name = "journal";
-            journal = {
-              max_age = "12h";
-              labels = {
-                job = "systemd-journal";
-                host = config.networking.hostName;
-              };
-            };
-            relabel_configs = [
-              {
-                source_labels = [ "__journal__systemd_unit" ];
-                target_label = "unit";
-              }
-            ];
-          }
-
-          # nginx error log
-          {
-            job_name = "nginx-error-logs";
-            static_configs = [{
-              targets = [ "localhost" ];
-              labels = {
-                job = "nginx-error-logs";
-                host = "corrino";
-                __path__ = "/var/log/nginx/*error.log";
-              };
-            }];
-          }
-
-          # nginx
-          {
-            job_name = "nginx";
-            static_configs = [
-              {
-                targets = [ "localhost" ];
-                labels = {
-                  job = "nginx";
-                  host = "corrino";
-                  __path__ = "/var/log/nginx/*access.log";
-                };   
-              }
-            ];
-            pipeline_stages = [
-              # {
-              #   regex = {
-              #     expression = "(?:[0-9]{1,3}\.){3}([0-9]{1,3})";
-              #     replace = "***";
-              #   };
-              # }
-              {
-                regex = {
-                  expression = ''(?P<remote_addr>.+) - - \[(?P<time_local>.+)\] "(?P<method>.+) (?P<url>.+) (HTTP\/(?P<version>\d.\d))" (?P<status>\d{3}) (?P<body_bytes_sent>\d+) (["](?P<http_referer>(\-)|(.+))["]) (["](?P<http_user_agent>.+)["])'';
-                };
-              }
-              {
-                labels = {
-                  remote_addr = null;
-                  time_local = null;
-                  method = null;
-                  url = null;
-                  status = null;
-                  body_bytes_sent = null;
-                  http_referer = null;
-                  http_user_agent = null;
-                };
-              }
-              {
-                timestamp = {
-                  source = "time_local";
-                  format = "02/Jan/2006:15:04:05 -0700";
-                };
-              }
-              {
-                drop = {
-                  source = "url";
-                  expression = ''/(_matrix|.well-known|notifications|api|identity).*'';
-                };
-              }
-              {
-                drop = {
-                  source = "url";
-                  expression = ''grafana.*'';
-                };
-              }
-            ];
-          }
-
-        ];
-      };
-    };
-  };
-}
diff --git a/nix/hosts/corrino/www/s3.emile.space.nix b/nix/hosts/corrino/www/s3.emile.space.nix
index b4646ad..ae33542 100644
--- a/nix/hosts/corrino/www/s3.emile.space.nix
+++ b/nix/hosts/corrino/www/s3.emile.space.nix
@@ -1,12 +1,21 @@
-{ config, ... }:
+{ config, pkgs, ... }:
 
 {
+  security.acme.certs."s3.emile.space" = {
+    group = "nginx";
+    domain = "s3.emile.space";
+    extraDomainNames = [
+      "*.s3.emile.space"
+      "*.s3-web.emile.space"
+    ];
+  };
+
   services.nginx.virtualHosts."s3.emile.space" = {
     forceSSL = true;
     enableACME = true;
     locations = {
       "/" = {
-        proxyPass = "http://[::1]:${toString config.emile.ports.minio.s3}";
+        proxyPass = "http://[::1]:${toString config.emile.ports.garage.s3}";
       };
     };
   };
@@ -16,24 +25,96 @@
     enableACME = true;
     locations = {
       "/" = {
-        proxyPass = "http://[::1]:${toString config.emile.ports.minio.web}";
+        proxyPass = "http://[::1]:${toString config.emile.ports.garage.web}";
       };
     };
   };
 
-  services.minio = {
+  services.garage = {
     enable = true;
-    region = "eu-north-1-hel-1a"; # corrino is in the helsinki hetzner dc
+    package = pkgs.garage_1_x;
+    settings = {
+      data_dir = [
+        { capacity = "50G"; path = "/var/lib/garage/data"; }
+      ];
 
-    listenAddress = "[::1]:${toString config.emile.ports.minio.s3}";
+      db_engine = "sqlite";
+      replication_factor = 3;
 
-    browser = true;
-    consoleAddress = "[::1]:${toString config.emile.ports.minio.web}";
+      s3_api = {
+        s3_region = "garage";
+        api_bind_addr = "[::]:${toString config.emile.ports.garage.s3}";
+        root_domain = "s3.emile.space";
+      };
+      s3_web = {
+        bind_addr = "[::]:${toString config.emile.ports.garage.web}";
+        root_domain = "s3-web.emile.space";
+        index = "index.html";
+      };
+      admin = {
+        api_bind_addr = "[::]:${toString config.emile.ports.garage.admin}";
+        # metrics_token = config.age.secrets.garage_admin_metrics_secret.path;
+        # admin_token = config.age.secrets.garage_admin_token_secret.path;
+      };
 
-    dataDir = [ "/minio/data" ];
-    configDir = "/minio/config";
+      # rpc_secret_file = config.age.secrets.garage_rpc_secret.path;
+      rpc_bind_addr = "[::]:${toString config.emile.ports.garage.rpc}";
+      rpc_bind_outgoing = false;
+      rpc_public_addr = "[fc00:1::1]:${toString config.emile.ports.garage.rpc}";
+    };
 
-    rootCredentialsFile = config.age.secrets.minio_root_credz.path;
-    # accessKey
+    environmentFile = config.age.secrets.garage_env.path;
   };
+#         metrics_token = config.age.secrets.garage_admin_metrics_secret.path;
+#         admin_token = config.age.secrets.garage_admin_token_secret.path;
+#       rpc_secret_file = config.age.secrets.garage_rpc_secret.path;
+
+# nix/hosts/corrino/secrets/garage_admin_metrics_secret.age
+# nix/hosts/corrino/secrets/garage_admin_token_secret.age  
+# nix/hosts/corrino/secrets/garage_admin_token.age         
+# nix/hosts/corrino/secrets/garage_metrics_token.age       
+# nix/hosts/corrino/secrets/garage_rpc_secret.age
+  
+  # services.garage = {
+  #   enable = true;
+  #   package = pkgs.garage_1_x;
+  #   settings = {
+  #     db_engine = "sqlite";
+  #     replication_factor = 2;
+
+  #     data_dir = [
+  #       { capacity = "50G"; path = dataDir; }
+  #     ];
+
+  #     compression_level = 1;
+
+  #     rpc_secret_file = config.age.secrets.garage_rpc_secret.path;
+  #     rpc_bind_addr = "[::]:${toString config.emile.ports.garage.rpc}";
+  #     rpc_bind_outgoing = false;
+  #     rpc_public_addr = "[fc00:1::1]:${toString config.emile.ports.garage.rpc}";
+
+  #     allow_world_readable_secrets = false;
+
+  #     s3_api = {
+  #       api_bind_addr = "[::]:${toString config.emile.ports.garage.s3}";
+  #       s3_region = "garage";
+  #       root_domain = "s3.emile.space";
+  #     };
+
+  #     s3_web = {
+  #       bind_addr = "[::]:${toString config.emile.ports.garage.web}";
+  #       root_domain = "s3-web.emile.space";
+  #       add_host_to_metrics = true;
+  #     };
+
+  #     admin = {
+  #       api_bind_addr = "[::]:${toString config.emile.ports.garage.admin}";
+  #       metrics_token = config.age.secrets.garage_admin_metrics_secret.path;
+  #       admin_token = config.age.secrets.garage_admin_token_secret.path;
+  #       trace_sink = "http://localhost:4317";
+  #     };
+
+  #   };
+  #   logLevel = "trace"; # info
+  # };
 }
diff --git a/nix/hosts/corrino/www/sb.emile.space.nix b/nix/hosts/corrino/www/sb.emile.space.nix
deleted file mode 100644
index 1854f0e..0000000
--- a/nix/hosts/corrino/www/sb.emile.space.nix
+++ /dev/null
@@ -1,114 +0,0 @@
-{ pkgs, ... }:
-
-{
-  services.nginx.virtualHosts."sb.emile.space" = {
-    forceSSL = true;
-    enableACME = true;
-    locations = {
-      "/" = {
-        proxyPass = "http://${config.services.silverbullet.listenSddress}:${toString config.services.silverbullet.listenPort}";
-        extraConfig = ''
-          ## Send a subrequest to Authelia to verify if the user is authenticated and has permission to access the resource.
-          auth_request /internal/authelia/authz;
-
-          ## Save the upstream metadata response headers from Authelia to variables.
-          auth_request_set $user $upstream_http_remote_user;
-          auth_request_set $groups $upstream_http_remote_groups;
-          auth_request_set $name $upstream_http_remote_name;
-          auth_request_set $email $upstream_http_remote_email;
-
-          ## Inject the metadata response headers from the variables into the request made to the backend.
-          proxy_set_header Remote-User $user;
-          proxy_set_header Remote-Groups $groups;
-          proxy_set_header Remote-Email $email;
-          proxy_set_header Remote-Name $name;
-
-          ## Configure the redirection when the authz failure occurs. Lines starting with 'Modern Method' and 'Legacy Method'
-          ## should be commented / uncommented as pairs. The modern method uses the session cookies configuration's authelia_url
-          ## value to determine the redirection URL here. It's much simpler and compatible with the mutli-cookie domain easily.
-
-          ## Modern Method: Set the $redirection_url to the Location header of the response to the Authz endpoint.
-          auth_request_set $redirection_url $upstream_http_location;
-
-          ## Modern Method: When there is a 401 response code from the authz endpoint redirect to the $redirection_url.
-          error_page 401 =302 $redirection_url;
-
-          ## Legacy Method: Set $target_url to the original requested URL.
-          ## This requires http_set_misc module, replace 'set_escape_uri' with 'set' if you don't have this module.
-          # set $target_url $scheme://$http_host$request_uri;
-
-          ## Legacy Method: When there is a 401 response code from the authz endpoint redirect to the portal with the 'rd'
-          ## URL parameter set to $target_url. This requires users update 'auth.example.com/' with their external authelia URL.
-          # error_page 401 =302 https://sso.emile.space/?rd=$target_url;
-        '';
-      };
-      "/internal/authelia/authz" = {
-        extraConfig = ''
-          ## Essential Proxy Configuration
-          internal;
-          proxy_pass https://sso.emile.space/api/authz/auth-request;
-
-          ## Headers
-          ## The headers starting with X-* are required.
-          proxy_set_header X-Original-Method $request_method;
-          proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
-          proxy_set_header X-Forwarded-For $remote_addr;
-          proxy_set_header Content-Length "";
-          proxy_set_header Connection "";
-
-          ## Basic Proxy Configuration
-          proxy_pass_request_body off;
-          proxy_next_upstream error timeout invalid_header http_500 http_502 http_503; # Timeout if the real server is dead
-          proxy_redirect http:// $scheme://;
-          proxy_http_version 1.1;
-          proxy_cache_bypass $cookie_session;
-          proxy_no_cache $cookie_session;
-          proxy_buffers 4 32k;
-          client_body_buffer_size 128k;
-
-          ## Advanced Proxy Configuration
-          send_timeout 5m;
-          proxy_read_timeout 240;
-          proxy_send_timeout 240;
-          proxy_connect_timeout 240;
-        '';
-      };
-
-  };
-
-  # auth via authelia
-  # services.authelia.instances.main.settings.identity_providers.oidc.clients = [
-  #   {
-  #     id = "silverbullet";
-
-  #     # ; nix run nixpkgs#authelia -- crypto hash generate pbkdf2 --variant sha512 --random --random.length 72 --random.charset rfc3986
-  #     secret = "$pbkdf2-sha512$310000$mxk7uITQOZNYEqeinigQnw$wsF2S6RPL2zVRg1X0bAuINh8Lu5PuA/2/FYJSy3i/Ig5vtCzaIFb0xYEcus4jkqTIgyp3aBxtgSzAKjQKC.QKg";
-  #     public = false;
-  #     authorization_policy = "two_factor";
-  #     redirect_uris = [ "https://md.emile.space/auth/oauth2/callback" ];
-  #     scopes = [
-  #       "openid"
-  #       "email"
-  #       "profile"
-  #     ];
-  #     grant_types = [
-  #       "refresh_token"
-  #       "authorization_code"
-  #     ];
-  #     response_types = [ "code" ];
-  #     response_modes = [
-  #       "form_post"
-  #       "query"
-  #       "fragment"
-  #     ];
-  #     token_endpoint_auth_method = "client_secret_post";
-  #   }
-  # ];
-
-  services.silverbullet = {
-    enable = true;
-    spaceDir = "/var/lib/silverbullet";
-    listenPort = 3000;
-    listenAddress = "[::1]";
-  };
-}
diff --git a/nix/hosts/corrino/www/social.emile.space.nix b/nix/hosts/corrino/www/social.emile.space.nix
index 210f0be..d9d30f7 100644
--- a/nix/hosts/corrino/www/social.emile.space.nix
+++ b/nix/hosts/corrino/www/social.emile.space.nix
@@ -38,13 +38,17 @@
     };
   };
 
+  age.secrets.gotosocial_oidc_client_secret.owner = "authelia-main";
+  age.secrets.gotosocial_oidc_client_secret.group = "authelia-main";
+  
   # auth via authelia
   services.authelia.instances.main.settings.identity_providers.oidc.clients = [
     {
-      id = "gotosocial";
+      client_id = "gotosocial";
 
       # ; nix run nixpkgs#authelia -- crypto hash generate pbkdf2 --variant sha512 --random --random.length 72 --random.charset rfc3986
-      secret = "$pbkdf2-sha512$310000$oDpZ5FuO965TbjPoophJXw$dbkAwWFvLN1h1Zh9US2ZOE5ilPRdEHMdGF/x0uorou2UqURrXF0KQmXxsV38F2yYMS7u/ecramKlvfMwsqHOcg";
+      client_secret = "{{ secret \"${config.age.secrets.gotosocial_oidc_client_secret.path}\" }}";
+
       public = false;
       authorization_policy = "two_factor";
       redirect_uris = [ "https://social.emile.space/auth/callback" ];
@@ -93,4 +97,21 @@
       Restart = "on-failure";
     };
   };
+
+  services.restic.backups."corrino" = {
+    paths = [ "/var/lib/gotosocial" ];
+  };
+
+  services.restic.backups."gotosocial" = {
+    repository = "/mnt/storagebox-bx11/gotosocial";
+    paths = [ "/var/lib/gotosocial" ];
+    passwordFile = config.age.secrets.restic_password.path;
+    initialize = true;
+    pruneOpts = [
+      "--keep-daily 7"
+      "--keep-weekly 5"
+      "--keep-monthly 12"
+      "--keep-yearly 75"
+    ];
+  };
 }
diff --git a/nix/hosts/corrino/www/sso.emile.space.nix b/nix/hosts/corrino/www/sso.emile.space.nix
index 44e30bb..6ffff80 100644
--- a/nix/hosts/corrino/www/sso.emile.space.nix
+++ b/nix/hosts/corrino/www/sso.emile.space.nix
@@ -141,9 +141,22 @@ in
         storage.local.path = "/var/lib/authelia-main/db.sqlite";
 
         session = {
-          domain = "sso.emile.space";
-          expiration = 3600; # 1 hour
-          inactivity = 300; # 5 minutes
+          # domain = "sso.emile.space";
+          # expiration = 3600; # 1 hour
+          # inactivity = 300; # 5 minutes
+
+          cookies = [
+            {
+              domain = "emile.space";
+              authelia_url = "https://sso.emile.space";
+              # The period of time the user can be inactive for until the session is destroyed. Useful if you want long session timers but don’t want unused devices to be vulnerable.
+              inactivity = "1h";
+              # The period of time before the cookie expires and the session is destroyed. This is overridden by remember_me when the remember me box is checked.
+              expiration = "1d";
+              # The period of time before the cookie expires and the session is destroyed when the remember me box is checked. Setting this to -1 disables this feature entirely for this session cookie domain
+              remember_me = "3M";
+            }
+          ];
         };
 
         notifier = {
@@ -196,6 +209,16 @@ in
           default_policy = "deny";
           rules = [
             {
+              # silverbullet needs access to these without auth
+              domain = "sb.emile.space";
+              policy = "bypass";
+              resources = [
+                "/.client/manifest.json$"
+                "/.client/[a-zA-Z0-9_-]+.png$"
+                "/service_worker.js$"
+              ];
+            }
+            {
               domain = "*.emile.space";
               policy = "two_factor";
             }
diff --git a/nix/hosts/corrino/www/tickets.emile.space.nix b/nix/hosts/corrino/www/tickets.emile.space.nix
index fb12961..08f23d3 100644
--- a/nix/hosts/corrino/www/tickets.emile.space.nix
+++ b/nix/hosts/corrino/www/tickets.emile.space.nix
@@ -18,7 +18,7 @@
       enable = true;
       package = pkgs.pretix;
       plugins = with config.services.pretix.package.plugins; [
-        passbook
+        # passbook
         pages
       ];
       user = "pretix";
diff --git a/nix/hosts/lampadas/configuration.nix b/nix/hosts/lampadas/configuration.nix
index cc829d8..d2630a8 100644
--- a/nix/hosts/lampadas/configuration.nix
+++ b/nix/hosts/lampadas/configuration.nix
@@ -2,7 +2,7 @@
 # your system. Help is available in the configuration.nix(5) man page, on
 # https://search.nixos.org/options and in the NixOS manual (`nixos-help`).
 
-{ pkgs, lib, ... }:
+{ config, pkgs, lib, ... }:
 
 let
   emile_keys = [
@@ -17,12 +17,34 @@ in
     ./hardware-configuration.nix
   ];
 
+  hardware.fancontrol = {
+    enable = true;
+    config = ''
+      # Configuration file generated by pwmconfig, changes will be lost
+      INTERVAL=10
+      DEVPATH=hwmon0=devices/platform/coretemp.0 hwmon1=devices/platform/nct6775.672
+      DEVNAME=hwmon0=coretemp hwmon1=nct6798
+      FCTEMPS=hwmon1/pwm3=hwmon0/temp2_input hwmon1/pwm2=hwmon1/temp2_input
+      FCFANS=hwmon1/pwm3=hwmon1/fan3_input hwmon1/pwm2=hwmon1/fan2_input
+      MINTEMP=hwmon1/pwm3=35 hwmon1/pwm2=35
+      MAXTEMP=hwmon1/pwm3=75 hwmon1/pwm2=75
+      MINSTART=hwmon1/pwm3=255 hwmon1/pwm2=255
+      MINSTOP=hwmon1/pwm3=30 hwmon1/pwm2=30
+      MINPWM=hwmon1/pwm3=30 hwmon1/pwm2=30
+      MAXPWM=hwmon1/pwm3=255 hwmon1/pwm2=255
+    '';
+  };
+
   boot = {
     loader = {
       systemd-boot.enable = true;
       efi.canTouchEfiVariables = true;
     };
     kernelParams = [ "ip=dhcp" ];
+    kernelModules = [
+      # fan speed modules, detected using `sensors-detect`
+      "coretemp" "nct6775"
+    ];
     initrd = {
       availableKernelModules = [ "r8169" ];
       systemd.users.root.shell = "/bin/cryptsetup-askpass";
@@ -159,10 +181,41 @@ in
     };
 
     # metric exporters
-    prometheus.exporters = {
-      node.enable = true; # port 9100
-      systemd.enable = true; # port 9558
-      smartctl.enable = true; # port 9633
+    prometheus = {
+      enable = true;
+      port = 9090;
+      listenAddress = "100.87.209.97";
+      scrapeConfigs = [
+        {
+          job_name = "node";
+          static_configs = [{
+            targets = [
+              "localhost:${toString config.services.prometheus.exporters.node.port}"
+            ];
+          }];
+        }  
+        {
+          job_name = "systemd";
+          static_configs = [{
+            targets = [
+              "localhost:${toString config.services.prometheus.exporters.systemd.port}"
+            ];
+          }];
+        }  
+        {
+          job_name = "smartctl";
+          static_configs = [{
+            targets = [
+              "localhost:${toString config.services.prometheus.exporters.smartctl.port}"
+            ];
+          }];
+        }  
+      ];
+      exporters = {
+        node.enable = true; # port 9100
+        systemd.enable = true; # port 9558
+        smartctl.enable = true; # port 9633
+      };
     };
 
     # shares
@@ -177,7 +230,6 @@ in
       openFirewall = true;
       settings = {
         global = {
-
           ## Browsing/Identification ###
           "workgroup" = "Pacific";
           "server string" = "lampadas";
@@ -269,18 +321,6 @@ in
           "force user" = "emile";
           "guest ok" = "no";
           "read only" = "no";
-
-          # "fruit:aapl" = "yes";
-          # "fruit:copyfile" = "yes";
-          # "fruit:delete_empty_adfiles" = "yes";
-          # "fruit:metadata" = "stream";
-          # "fruit:posix_rename" = "yes";
-          # "fruit:time machine" = "yes";
-          # "fruit:veto_appledouble" = "no";
-          # "fruit:wipe_intentionally_left_blank_rfork" = "yes";
-          # "fruit:nfs_aces" = "no";
-          # "fruit:zero_file_id" = "yes";
-          # "fruit:encoding" = "native";
         };
 
         public = {
diff --git a/nix/lib/flake-helper.nix b/nix/lib/flake-helper.nix
index f841fa5..e58e8b1 100644
--- a/nix/lib/flake-helper.nix
+++ b/nix/lib/flake-helper.nix
@@ -6,6 +6,8 @@
   deploy-rs,
   home-manager,
   darwin,
+  flake-utils,
+  pwndbg,
   ...
 }@inputs:
 
@@ -65,7 +67,7 @@ rec {
             if system == "x86_64-linux" then
               self.nixosModules.x86_64-linux
             else if system == "aarch64-darwin" then
-              ({ })
+             { }
             else
               null
           )
@@ -88,6 +90,8 @@ rec {
                     self.overlays.x86_64-linux
                   else if system == "aarch64-darwin" then
                     self.overlays.aarch64-darwin
+                    # // self.overlays.unstable-darwin
+                    # {}
                   else
                     null
                 )
@@ -95,7 +99,9 @@ rec {
                 # no clue why, but when rebuilding corrino and this not being commented,
                 # something in the hardware.bluetooth module breaks
                 #
-                # (if system == "aarch64-darwin" then self.overlays.unstable-darwin else null)
+                # (if system == "aarch64-darwin"
+                # then self.overlays.unstable-darwin
+                # else null)
 
                 (_: _: { inherit (agenix.packages."x86_64-linux") agenix; })
 
@@ -240,4 +246,35 @@ rec {
 
       # don't build hosts that start with an underscore
       (nixpkgs.lib.filterAttrs (name: host: (builtins.substring 0 1 name) != "_") hosts);
+
+  # A function taking an attribute set of flake templates, importing their
+  # flake.nix/output/packages (if there are any) and returning an attribute set
+  # of their packages (if the template has one or more)
+  template-packages =
+    builtins.mapAttrs (name: value:
+      (((import ../../nix/templates/${name}/flake.nix).outputs)
+        { inherit nixpkgs flake-utils pwndbg; })
+        .packages or { }
+    );
+
+  # TODO(emile): templates
+  # apply the above function to the templates
+  # templates = template-packages self.templates;
+
+  # TODO(emile): templates
+  # Merge template packages into root packages with template prefix
+  # merged-template-packages =
+  #   system:
+  #   let
+  #     lib = nixpkgs.lib;
+  #   in
+  #   lib.foldl (
+  #     acc: tplName:
+  #     let
+  #       tplPkgs = templates.${tplName}.${system} or { };
+  #       prefixed = lib.mapAttrs' (pkgName: pkg: lib.nameValuePair "${tplName}-${pkgName}" pkg) tplPkgs;
+  #     in
+  #     acc // prefixed
+  #   ) { } (builtins.attrNames templates);
+  
 }
diff --git a/nix/modules/libvirtnix/a.xml b/nix/modules/libvirtnix/a.xml
new file mode 100644
index 0000000..1255ad4
--- /dev/null
+++ b/nix/modules/libvirtnix/a.xml
@@ -0,0 +1,259 @@
+<domain type='kvm' id='5'>
+  <name>fileserver2</name>
+  <uuid>d62e0276-d474-452b-80f4-c951c39bcf74</uuid>
+  <metadata>
+    <libosinfo:libosinfo xmlns:libosinfo='http://libosinfo.org/xmlns/libvirt/domain/1.0'>
+      <libosinfo:os id='http://nixos.org/nixos/unknown'/>
+    </libosinfo:libosinfo>
+  </metadata>
+  <memory unit='KiB'>2097152</memory>
+  <currentMemory unit='KiB'>2097152</currentMemory>
+  <vcpu placement='static'>3</vcpu>
+  <resource>
+    <partition>/machine</partition>
+  </resource>
+  <os>
+    <type arch='x86_64' machine='pc-q35-3.1'>hvm</type>
+    <loader readonly='yes' type='pflash'>/usr/share/OVMF/OVMF_CODE.fd</loader>
+    <nvram>/var/lib/libvirt/qemu/nvram/fileserver2_VARS.fd</nvram>
+    <boot dev='hd'/>
+  </os>
+  <features>
+    <acpi/>
+    <apic/>
+    <vmport state='off'/>
+  </features>
+  <cpu mode='host-passthrough' check='none' migratable='on'/>
+  <clock offset='utc'>
+    <timer name='rtc' tickpolicy='catchup'/>
+    <timer name='pit' tickpolicy='delay'/>
+    <timer name='hpet' present='no'/>
+  </clock>
+  <on_poweroff>destroy</on_poweroff>
+  <on_reboot>restart</on_reboot>
+  <on_crash>destroy</on_crash>
+  <pm>
+    <suspend-to-mem enabled='no'/>
+    <suspend-to-disk enabled='no'/>
+  </pm>
+  <devices>
+    <emulator>/usr/bin/qemu-system-x86_64</emulator>
+    <disk type='block' device='disk'>
+      <driver name='qemu' type='raw' cache='none' io='native' discard='unmap'/>
+      <source dev='/dev/vmstorage/fileserver2' index='3'/>
+      <backingStore/>
+      <target dev='vda' bus='virtio'/>
+      <alias name='virtio-disk0'/>
+      <address type='pci' domain='0x0000' bus='0x04' slot='0x00' function='0x0'/>
+    </disk>
+    <disk type='block' device='disk'>
+      <driver name='qemu' type='raw'/>
+      <source dev='/dev/mapper/storage1' index='2'/>
+      <backingStore/>
+      <target dev='vdb' bus='virtio'/>
+      <alias name='virtio-disk1'/>
+      <address type='pci' domain='0x0000' bus='0x08' slot='0x00' function='0x0'/>
+    </disk>
+    <disk type='block' device='disk'>
+      <driver name='qemu' type='raw'/>
+      <source dev='/dev/mapper/storage2' index='1'/>
+      <backingStore/>
+      <target dev='vdc' bus='virtio'/>
+      <alias name='virtio-disk2'/>
+      <address type='pci' domain='0x0000' bus='0x09' slot='0x00' function='0x0'/>
+    </disk>
+    <controller type='usb' index='0' model='qemu-xhci' ports='15'>
+      <alias name='usb'/>
+      <address type='pci' domain='0x0000' bus='0x02' slot='0x00' function='0x0'/>
+    </controller>
+    <controller type='pci' index='0' model='pcie-root'>
+      <alias name='pcie.0'/>
+    </controller>
+    <controller type='pci' index='1' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='1' port='0x10'/>
+      <alias name='pci.1'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0' multifunction='on'/>
+    </controller>
+    <controller type='pci' index='2' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='2' port='0x11'/>
+      <alias name='pci.2'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x1'/>
+    </controller>
+    <controller type='pci' index='3' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='3' port='0x12'/>
+      <alias name='pci.3'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x2'/>
+    </controller>
+    <controller type='pci' index='4' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='4' port='0x13'/>
+      <alias name='pci.4'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x3'/>
+    </controller>
+    <controller type='pci' index='5' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='5' port='0x14'/>
+      <alias name='pci.5'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x4'/>
+    </controller>
+    <controller type='pci' index='6' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='6' port='0x15'/>
+      <alias name='pci.6'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x5'/>
+    </controller>
+    <controller type='pci' index='7' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='7' port='0x16'/>
+      <alias name='pci.7'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x6'/>
+    </controller>
+    <controller type='pci' index='8' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='8' port='0x17'/>
+      <alias name='pci.8'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x7'/>
+    </controller>
+    <controller type='pci' index='9' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='9' port='0x18'/>
+      <alias name='pci.9'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0' multifunction='on'/>
+    </controller>
+    <controller type='pci' index='10' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='10' port='0x19'/>
+      <alias name='pci.10'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x1'/>
+    </controller>
+    <controller type='pci' index='11' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='11' port='0x1a'/>
+      <alias name='pci.11'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x2'/>
+    </controller>
+    <controller type='pci' index='12' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='12' port='0x1b'/>
+      <alias name='pci.12'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x3'/>
+    </controller>
+    <controller type='pci' index='13' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='13' port='0x1c'/>
+      <alias name='pci.13'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x4'/>
+    </controller>
+    <controller type='pci' index='14' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='14' port='0x1d'/>
+      <alias name='pci.14'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x5'/>
+    </controller>
+    <controller type='pci' index='15' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='15' port='0x1e'/>
+      <alias name='pci.15'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x6'/>
+    </controller>
+    <controller type='pci' index='16' model='pcie-to-pci-bridge'>
+      <model name='pcie-pci-bridge'/>
+      <alias name='pci.16'/>
+      <address type='pci' domain='0x0000' bus='0x07' slot='0x00' function='0x0'/>
+    </controller>
+    <controller type='sata' index='0'>
+      <alias name='ide'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x1f' function='0x2'/>
+    </controller>
+    <controller type='virtio-serial' index='0'>
+      <alias name='virtio-serial0'/>
+      <address type='pci' domain='0x0000' bus='0x03' slot='0x00' function='0x0'/>
+    </controller>
+    <controller type='scsi' index='0' model='lsilogic'>
+      <alias name='scsi0'/>
+      <address type='pci' domain='0x0000' bus='0x10' slot='0x01' function='0x0'/>
+    </controller>
+    <interface type='bridge'>
+      <mac address='52:54:00:bf:ba:88'/>
+      <source network='services' portid='6c3a3512-d823-4521-9043-3a91a65682f5' bridge='br7'/>
+      <target dev='vnet2'/>
+      <model type='virtio'/>
+      <alias name='net0'/>
+      <address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
+    </interface>
+    <serial type='pty'>
+      <source path='/dev/pts/3'/>
+      <target type='isa-serial' port='0'>
+        <model name='isa-serial'/>
+      </target>
+      <alias name='serial0'/>
+    </serial>
+    <console type='pty' tty='/dev/pts/3'>
+      <source path='/dev/pts/3'/>
+      <target type='serial' port='0'/>
+      <alias name='serial0'/>
+    </console>
+    <channel type='unix'>
+      <source mode='bind' path='/var/lib/libvirt/qemu/channel/target/domain-5-fileserver2/org.qemu.guest_agent.0'/>
+      <target type='virtio' name='org.qemu.guest_agent.0' state='disconnected'/>
+      <alias name='channel0'/>
+      <address type='virtio-serial' controller='0' bus='0' port='1'/>
+    </channel>
+    <channel type='spicevmc'>
+      <target type='virtio' name='com.redhat.spice.0' state='disconnected'/>
+      <alias name='channel1'/>
+      <address type='virtio-serial' controller='0' bus='0' port='2'/>
+    </channel>
+    <input type='tablet' bus='usb'>
+      <alias name='input0'/>
+      <address type='usb' bus='0' port='1'/>
+    </input>
+    <input type='mouse' bus='ps2'>
+      <alias name='input1'/>
+    </input>
+    <input type='keyboard' bus='ps2'>
+      <alias name='input2'/>
+    </input>
+    <graphics type='spice' port='5901' autoport='yes' listen='127.0.0.1'>
+      <listen type='address' address='127.0.0.1'/>
+    </graphics>
+    <sound model='ich9'>
+      <alias name='sound0'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x1b' function='0x0'/>
+    </sound>
+    <audio id='1' type='spice'/>
+    <video>
+      <model type='qxl' ram='65536' vram='65536' vgamem='16384' heads='1' primary='yes'/>
+      <alias name='video0'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x0'/>
+    </video>
+    <redirdev bus='usb' type='spicevmc'>
+      <alias name='redir0'/>
+      <address type='usb' bus='0' port='2'/>
+    </redirdev>
+    <redirdev bus='usb' type='spicevmc'>
+      <alias name='redir1'/>
+      <address type='usb' bus='0' port='3'/>
+    </redirdev>
+    <memballoon model='virtio'>
+      <alias name='balloon0'/>
+      <address type='pci' domain='0x0000' bus='0x05' slot='0x00' function='0x0'/>
+    </memballoon>
+    <rng model='virtio'>
+      <backend model='random'>/dev/urandom</backend>
+      <alias name='rng0'/>
+      <address type='pci' domain='0x0000' bus='0x06' slot='0x00' function='0x0'/>
+    </rng>
+  </devices>
+  <seclabel type='dynamic' model='apparmor' relabel='yes'>
+    <label>libvirt-d62e0276-d474-452b-80f4-c951c39bcf74</label>
+    <imagelabel>libvirt-d62e0276-d474-452b-80f4-c951c39bcf74</imagelabel>
+  </seclabel>
+  <seclabel type='dynamic' model='dac' relabel='yes'>
+    <label>+109:+115</label>
+    <imagelabel>+109:+115</imagelabel>
+  </seclabel>
+</domain>
diff --git a/nix/modules/libvirtnix/b.xml b/nix/modules/libvirtnix/b.xml
new file mode 100644
index 0000000..311e7d5
--- /dev/null
+++ b/nix/modules/libvirtnix/b.xml
@@ -0,0 +1,183 @@
+<domain type="kvm" id="1337">
+  <name>blub</name>
+  <uuid>cafebabe-d474-452b-80f4-c951c39bcf74</uuid>
+  <metadata>
+    <libosinfo:libosinfo xmlns:libosinfo="https://libosinfo.org/xmlns/libvirt/domain/1.0">
+      <libosinfo:os id="https://nixos.org/nixos/unknown"/>
+    </libosinfo:libosinfo>
+  </metadata>
+  <memory unit="KiB">2097152</memory>
+  <currentMemory unit="KiB">2097152</currentMemory>
+  <vcpu placement="static">3</vcpu>
+  <resource>
+    <partition>/machine</partition>
+  </resource>
+  <os>
+    <type arch="x86_64" machine="pc-q35-3.1">hvm</type>
+    <loader readonly="yes" pflash="pflash">/usr/share/OVMF/OVMF_CODE.fd</loader>
+    <nvram>/var/lib/libvirt/qemu/nvram/fileserver2_VARS.fd</nvram>
+    <type dev="hd"/>
+  </os>
+  <features>
+    <acpi/>
+    <apic/>
+    <vmport state="off"/>
+  </features>
+  <cpu mode="host-passthrough" check="none" migratable="on"/>
+  <clock offset="utc">
+    <timer name="rtc" tickpolicy="catchup"/>
+    <timer name="pit" tickpolicy="delay"/>
+    <timer name="hpet" present="no"/>
+  </clock>
+  <on_poweroff>destroy</on_poweroff>
+  <on_reboot>restart</on_reboot>
+  <on_crash>destroy</on_crash>
+  <pm>
+    <suspend-to-mem enabled="no"/>
+    <suspend-to-disk enabled="no"/>
+  </pm>
+  <devices>
+    <emulator>/usr/bin/qemu-system-x86_64</emulator>
+    <disk type="block" device="disk">block
+      <driver name="qemu" type="raw" cache="none" io="native" discard="unmap"/>
+      <source dev="/dev/vmstorage/fileserver2" index="3"/>
+      <backingStore/>
+      <target dev="vda" bus="virtio"/>
+      <alias name="virtio-disk0"/>
+      <address type="pci" domain="0x0000" bus="0x04" slot="0x00" function="0x0"/>
+    </disk>
+    <disk type="block" device="disk">block
+      <driver name="qemu" type="raw"/>
+      <source dev="/dev/mapper/storage1" index="2"/>
+      <backingStore/>
+      <target dev="vdb" bus="virtio"/>
+      <alias name="virtio-disk1"/>
+      <address type="pci" domain="0x0000" bus="0x08" slot="0x00" function="0x0"/>
+    </disk>
+    <disk type="block" device="disk">block
+      <driver name="qemu" type="raw"/>
+      <source dev="/dev/maper/storage2" index="1"/>
+      <backingStore/>
+      <target dev="vdc" bus="virtio"/>
+      <alias name="virtio-disk2"/>
+      <address type="pci" domain="0x0000" bus="0x09" slot="0x00" function="0x0"/>
+    </disk>
+    <controller type="usb" index="0" model="qemu-xhci" ports="15">
+      <model name="qemu-xhci"/>
+      <alias name="usb"/>
+      <address type="pci" domain="0x0000" bus="0x02" slot="0x00" function="0x0"/>
+    </controller>
+    <controller type="pci" index="0" model="pci-root">
+      <model name="pci-root"/>
+      <alias name="pcie.0"/>
+    </controller>
+    <controller type="pci" index="1" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <target chassis="1" port="0x10"/>
+      <alias name="pci.1"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x0"/>
+    </controller>
+    <controller type="pci" index="2" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <target chassis="2" port="0x11"/>
+      <alias name="pci.2"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x0"/>
+    </controller>
+    <controller type="pci" index="3" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <target chassis="3" port="0x12"/>
+      <alias name="pci.3"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x2"/>
+    </controller>
+    <controller type="pci" index="4" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <target chassis="4" port="0x13"/>
+      <alias name="pci.4"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x3"/>
+    </controller>
+    <controller type="pci" index="5" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <target chassis="5" port="0x14"/>
+      <alias name="pci.5"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x4"/>
+    </controller>
+    <controller type="pci" index="6" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <target chassis="6" port="0x15"/>
+      <alias name="pci.6"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x5"/>
+    </controller>
+    <controller type="pci" index="7" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <target chassis="7" port="0x16"/>
+      <alias name="pci.7"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x6"/>
+    </controller>
+    <controller type="pci" index="8" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <target chassis="8" port="0x17"/>
+      <alias name="pci.8"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x7"/>
+    </controller>
+    <controller type="pci" index="9" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <target chassis="9" port="0x18"/>
+      <alias name="pci.9"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x03" function="0x0"/>
+    </controller>
+    <controller type="pci" index="10" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <target chassis="10" port="0x19"/>
+      <alias name="pci.10"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x03" function="0x1"/>
+    </controller>
+    <controller type="pci" index="11" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <target chassis="11" port="0x1a"/>
+      <alias name="pci.11"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x03" function="0x2"/>
+    </controller>
+    <controller type="pci" index="12" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <target chassis="12" port="0x1b"/>
+      <alias name="pci.12"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x03" function="0x3"/>
+    </controller>
+    <controller type="pci" index="13" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <target chassis="13" port="0x1c"/>
+      <alias name="pci.13"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x03" function="0x4"/>
+    </controller>
+    <controller type="pci" index="14" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <target chassis="14" port="0x1d"/>
+      <alias name="pci.14"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x03" function="0x5"/>
+    </controller>
+    <controller type="pci" index="15" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <target chassis="15" port="0x1e"/>
+      <alias name="pci.15"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x03" function="0x6"/>
+    </controller>
+    <controller type="pci" index="16" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <alias name="pci.16"/>
+      <address type="pci" domain="0x0000" bus="0x07" slot="0x00" function="0x0"/>
+    </controller>
+    <controller type="sata" index="0">
+      <alias name="ide"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x1f" function="0x02"/>
+    </controller>
+    <controller type="virtio-serial" index="0">
+      <alias name="virtio-serial0"/>
+      <address type="pci" domain="0x0000" bus="0x03" slot="0x00" function="0x0"/>
+    </controller>
+    <controller type="scsi" index="0" model="lsilogic">
+      <model name="lsilogic"/>
+      <alias name="scsi0"/>
+      <address type="pci" domain="0x0000" bus="0x10" slot="0x01" function="0x0"/>
+    </controller>
+  </devices>
+</domain>
diff --git a/nix/modules/libvirtnix/config.nix b/nix/modules/libvirtnix/config.nix
new file mode 100644
index 0000000..5272ce2
--- /dev/null
+++ b/nix/modules/libvirtnix/config.nix
@@ -0,0 +1,583 @@
+{
+  services.emile.libvirtnix = {
+    enable = true;
+    vm = {
+      "alan" = {
+        vm_type = "kvm";
+        id = "1337";
+        name = "blub";
+        uuid = "cafebabe-d474-452b-80f4-c951c39bcf74";
+
+        metadata.libosinfo = "https://libosinfo.org/xmlns/libvirt/domain/1.0";
+        metadata.libosinfo_os = "https://nixos.org/nixos/unknown";
+
+        memory.unit = "KiB";
+        memory.value = 2097152;
+
+        currentMemory.unit = "KiB";
+        currentMemory.value = 2097152;
+
+        vcpu.placement = "static";
+        vcpu.count = 3;
+
+        resource.partition = "/machine";
+
+        os = {
+          type = {
+            arch = "x86_64";
+            machine = "pc-q35-3.1";
+            value = "hvm";
+          };
+
+          loader = {
+            readonly = "yes";
+            type = "pflash";
+            value = "/usr/share/OVMF/OVMF_CODE.fd";
+          };
+
+          nvram.value = "/var/lib/libvirt/qemu/nvram/fileserver2_VARS.fd";
+
+          boot.dev = "hd";
+        };
+
+        features = {
+          acpi = true;
+          apic = true;
+          vmport = {
+            state = "off";
+          };
+        };
+
+        cpu = {
+          mode = "host-passthrough";
+          check = "none";
+          migratable = "on";
+        };
+
+        clock = {
+          offset = "utc";
+          timer = [
+            {
+              name = "rtc";
+              tickpolicy = "catchup";
+            }
+            {
+              name = "pit";
+              tickpolicy = "delay";
+            }
+            {
+              name = "hpet";
+              present = "no";
+            }
+          ];
+        };
+
+        on_poweroff = "destroy";
+        on_reboot = "restart";
+        on_crash = "destroy";
+
+        pm = {
+          suspend-to-mem = "no";
+          suspend-to-disk = "no";
+        };
+
+        devices = {
+          emulators = [
+            {
+              value = "/usr/bin/qemu-system-x86_64";
+            }
+          ];
+
+          disks = [
+            {
+              type = "block";
+              device = "disk";
+              driver = {
+                name = "qemu";
+                type = "raw";
+                cache = "none";
+                io = "native";
+                discard = "unmap";
+              };
+              source = {
+                dev = "/dev/vmstorage/fileserver2";
+                index = 3;
+              };
+              backingStore = true;
+              target = {
+                dev = "vda";
+                bus = "virtio";
+              };
+              alias = {
+                name = "virtio-disk0";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x04";
+                slot = "0x00";
+                function = "0x0";
+              };
+            }
+            {
+              type = "block";
+              device = "disk";
+              driver = {
+                name = "qemu";
+                type = "raw";
+              };
+              source = {
+                dev = "/dev/mapper/storage1";
+                index = 2;
+              };
+              backingStore = true;
+              target = {
+                dev = "vdb";
+                bus = "virtio";
+              };
+              alias = {
+                name = "virtio-disk1";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x08";
+                slot = "0x00";
+                function = "0x0";
+              };
+            }
+            {
+              type = "block";
+              device = "disk";
+              driver = {
+                name = "qemu";
+                type = "raw";
+              };
+              source = {
+                dev = "/dev/maper/storage2";
+                index = 1;
+              };
+              backingStore = true;
+              target = {
+                dev = "vdc";
+                bus = "virtio";
+              };
+              alias = {
+                name = "virtio-disk2";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x09";
+                slot = "0x00";
+                function = "0x0";
+              };
+            }
+          ];
+
+          controllers = [
+            {
+              type = "usb";
+              index = 0;
+              model = "qemu-xhci";
+              ports = 15;
+              alias = {
+                name = "usb";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x02";
+                slot = "0x00";
+                function = "0x0";
+              };
+            }
+            {
+              type = "pci";
+              index = 0;
+              model = "pci-root";
+              alias = {
+                name = "pcie.0";
+              };
+            }
+            {
+              type = "pci";
+              index = 1;
+              model = "pcie-root-port";
+              target = {
+                chassis = 1;
+                port = "0x10";
+              };
+              alias = {
+                name = "pci.1";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x02";
+                function = "0x0";
+                multifunction = "on";
+              };
+            }
+            {
+              type = "pci";
+              index = 2;
+              model = "pcie-root-port";
+              target = {
+                chassis = 2;
+                port = "0x11";
+              };
+              alias = {
+                name = "pci.2";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x02";
+                function = "0x0";
+                multifunction = "on";
+              };
+            }
+            {
+              type = "pci";
+              index = 3;
+              model = "pcie-root-port";
+              target = {
+                chassis = 3;
+                port = "0x12";
+              };
+              alias = {
+                name = "pci.3";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x02";
+                function = "0x2";
+              };
+            }
+            {
+              type = "pci";
+              index = 4;
+              model = "pcie-root-port";
+              target = {
+                chassis = 4;
+                port = "0x13";
+              };
+              alias = {
+                name = "pci.4";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x02";
+                function = "0x3";
+              };
+            }
+            {
+              type = "pci";
+              index = 5;
+              model = "pcie-root-port";
+              target = {
+                chassis = 5;
+                port = "0x14";
+              };
+              alias = {
+                name = "pci.5";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x02";
+                function = "0x4";
+              };
+            }
+            {
+              type = "pci";
+              index = 6;
+              model = "pcie-root-port";
+              target = {
+                chassis = 6;
+                port = "0x15";
+              };
+              alias = {
+                name = "pci.6";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x02";
+                function = "0x5";
+              };
+            }
+            {
+              type = "pci";
+              index = 7;
+              model = "pcie-root-port";
+              target = {
+                chassis = 7;
+                port = "0x16";
+              };
+              alias = {
+                name = "pci.7";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x02";
+                function = "0x6";
+              };
+            }
+            {
+              type = "pci";
+              index = 8;
+              model = "pcie-root-port";
+              target = {
+                chassis = 8;
+                port = "0x17";
+              };
+              alias = {
+                name = "pci.8";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x02";
+                function = "0x7";
+              };
+            }
+            {
+              type = "pci";
+              index = 9;
+              model = "pcie-root-port";
+              target = {
+                chassis = 9;
+                port = "0x18";
+              };
+              alias = {
+                name = "pci.9";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x03";
+                function = "0x0";
+                multifunction = "on";
+              };
+            }
+            {
+              type = "pci";
+              index = 10;
+              model = "pcie-root-port";
+              target = {
+                chassis = 10;
+                port = "0x19";
+              };
+              alias = {
+                name = "pci.10";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x03";
+                function = "0x1";
+              };
+            }
+            {
+              type = "pci";
+              index = 11;
+              model = "pcie-root-port";
+              target = {
+                chassis = 11;
+                port = "0x1a";
+              };
+              alias = {
+                name = "pci.11";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x03";
+                function = "0x2";
+              };
+            }
+            {
+              type = "pci";
+              index = 12;
+              model = "pcie-root-port";
+              target = {
+                chassis = 12;
+                port = "0x1b";
+              };
+              alias = {
+                name = "pci.12";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x03";
+                function = "0x3";
+              };
+            }
+            {
+              type = "pci";
+              index = 13;
+              model = "pcie-root-port";
+              target = {
+                chassis = 13;
+                port = "0x1c";
+              };
+              alias = {
+                name = "pci.13";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x03";
+                function = "0x4";
+              };
+            }
+            {
+              type = "pci";
+              index = 14;
+              model = "pcie-root-port";
+              target = {
+                chassis = 14;
+                port = "0x1d";
+              };
+              alias = {
+                name = "pci.14";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x03";
+                function = "0x5";
+              };
+            }
+            {
+              type = "pci";
+              index = 15;
+              model = "pcie-root-port";
+              target = {
+                chassis = 15;
+                port = "0x1e";
+              };
+              alias = {
+                name = "pci.15";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x03";
+                function = "0x6";
+              };
+            }
+            {
+              type = "pci";
+              index = 16;
+              model = "pcie-root-port";
+              alias = {
+                name = "pci.16";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x07";
+                slot = "0x00";
+                function = "0x0";
+              };
+            }
+            {
+              type = "sata";
+              index = 0;
+              alias = {
+                name = "ide";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x1f";
+                function = "0x02";
+              };
+            }
+            {
+              type = "virtio-serial";
+              index = 0;
+              alias = {
+                name = "virtio-serial0";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x03";
+                slot = "0x00";
+                function = "0x0";
+              };
+            }
+            {
+              type = "scsi";
+              index = 0;
+              model = "lsilogic";
+              alias = {
+                name = "scsi0";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x10";
+                slot = "0x01";
+                function = "0x0";
+              };
+            }
+          ];
+          interfaces = [
+            {
+              type = "bridge";
+              mac = {
+                address = "52:54:00:bf:ba:88";
+              };
+              source = {
+                network = "services";
+                portid = "6c3a3512-d823-4521-9043-3a91a65682f5";
+                bridge = "br7";
+              };
+              target = {
+                dev = "vnet2";
+              };
+              model = {
+                type = "virtio";
+              };
+              alias = {
+                name = "net0";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x01";
+                slot = "0x00";
+                function = "0x0";
+              };
+            }
+          ];
+        };
+      };
+    };
+  };
+}
diff --git a/nix/modules/libvirtnix/domain.nix b/nix/modules/libvirtnix/domain.nix
index a028c7d..d1a5b9c 100644
--- a/nix/modules/libvirtnix/domain.nix
+++ b/nix/modules/libvirtnix/domain.nix
@@ -1,701 +1,1443 @@
-{ pkgs, lib, ... }:
-
-{
-  options = with lib; {
-
-    # meta, this allows defining the package used for each vm individually, defaults should be sane
-    packages = mkOption {
-      type = types.submodule {
-        options = {
-          libvirt = mkPackageOption pkgs "libvirt" { };
-          qemu = mkPackageOption pkgs "qemu" { };
-        };
-      };
+{ config, lib, ... }:
+
+let
+  mkTag = import ./xml.nix { inherit lib; };
+  pkgs = import <nixpkgs> { };
+
+  stringOption =
+    {
+      default ? "",
+    }:
+    lib.mkOption {
+      type = lib.types.str;
+      default = "${default}";
     };
 
-    # attributs for the root `<domain ...>` node
-    type = mkOption {
-      type = types.enum [
-        "xen"
-        "hvm"
-        "kvm"
-        "qemu"
-        "lxc"
-      ];
-      default = "kvm";
-      example = "qemu";
-      description = ''
-        				hypervisor used for running the domain
-
-        				allowed values are driver specific, if missing, plz send PR
-
-        				hvm since since 8.1.0 and QEMU 2.12
-        			'';
-    };
-
-    id = mkOption {
-      type = types.int;
-      default = 0;
-      example = 42;
-    };
-
-    # General metadata
-    name = mkOption {
-      type = types.str;
-      example = "MyGuest";
-    };
-
-    uuid = mkOption {
-      type = types.str;
-      example = "3e3fce45-4f53-4fa7-bb32-11f34168b82b";
-    };
-
-    genid = mkOption {
-      type = types.str;
-      example = "43dc0cf8-809b-4adb-9bea-a9abb5f3d90e";
-    };
-
-    title = mkOption {
-      type = types.str;
-      example = "A short description - title - of the domain";
+  # shorthand for an option with an enum, no default value
+  enumOpt =
+    options:
+    lib.mkOption {
+      type = lib.types.nullOr (lib.types.enum options);
+      default = null;
     };
+in
+{
+  options = {
+    services.emile.libvirtnix =
+      let
+
+        address = lib.mkOption {
+          default = null;
+          type = lib.types.nullOr (
+            lib.types.submodule {
+              options = {
+                type = enumOpt [ "pci" ];
+                domain = lib.mkOption {
+                  type = lib.types.str;
+                  example = "0x0000";
+                };
+                bus = lib.mkOption {
+                  type = lib.types.str;
+                  example = "0x4";
+                };
+                slot = lib.mkOption {
+                  type = lib.types.str;
+                  example = "0x00";
+                };
+                function = lib.mkOption {
+                  type = lib.types.str;
+                  example = "0x00";
+                };
+                multifunction = lib.mkOption {
+                  type = lib.types.enum [
+                    "on"
+                    "off"
+                  ];
+                  example = "on";
+                  default = "off";
+                };
+              };
+            }
+          );
+        };
 
-    description = mkOption {
-      type = types.str;
-      example = "Some human readable description";
-    };
+        interface = lib.mkOption {
+          type = lib.types.submodule {
+            options = {
+              type = enumOpt [ "bridge" ];
+              mac = lib.mkOption {
+                type = lib.types.submodule {
+                  options = {
+                    address = lib.types.mkOption {
+                      type = lib.types.str;
+                    };
+                  };
+                };
+              };
+              source = lib.mkOption {
+                type = lib.types.submodule {
+                  options = {
+                    network = lib.types.mkOption {
+                      type = lib.types.str;
+                    };
+                    portid = lib.types.mkOption {
+                      type = lib.types.str;
+                    };
+                    bridge = lib.types.mkOption {
+                      type = lib.types.str;
+                    };
+                  };
+                };
+              };
+              target = lib.mkOption {
+                type = lib.types.submodule {
+                  options = {
+                    dev = lib.types.mkOption {
+                      type = lib.types.str;
+                    };
+                  };
+                };
+              };
+              model = lib.mkOption {
+                type = lib.types.submodule {
+                  options = {
+                    type = lib.types.mkOption {
+                      type = enumOpt [ "virtio" ];
+                    };
+                  };
+                };
+              };
+              alias = lib.mkOption {
+                type = lib.types.submodule {
+                  options = {
+                    dev = lib.types.mkOption {
+                      name = lib.types.str;
+                    };
+                  };
+                };
+              };
+              inherit address;
+            };
+          };
+        };
 
-    metadata = mkOption {
-      type = types.str;
-      default = "";
-      example = ''
-        			  <metadata>
-        			    <app1:foo xmlns:app1="http://app1.org/app1/">..</app1:foo>
-        			    <app2:bar xmlns:app2="http://app1.org/app2/">..</app2:bar>
-        			  </metadata>
-        			'';
-    };
 
-    os = mkOption {
-      type = types.submodule {
-        options = {
-          firmware = mkOption {
-            type = types.enum [
-              "bios"
-              "efi"
+        controller = lib.types.submodule {
+          options = {
+            type = enumOpt [
+              "usb"
+              "pci"
+              "sata"
+              "virtio-serial"
+              "scsi"
             ];
-            default = "efi";
-            example = "bios";
+            index = lib.mkOption { type = lib.types.int; };
+            model = lib.mkOption {
+              type = lib.types.nullOr lib.types.str;
+              default = null;
+            };
+            ports = lib.mkOption {
+              type = lib.types.nullOr lib.types.int;
+              default = null;
+            };
+            alias = lib.mkOption {
+              type = lib.types.submodule {
+                options = {
+                  name = lib.mkOption { type = lib.types.str; };
+                };
+              };
+            };
+            target = lib.mkOption {
+              type = lib.types.nullOr (
+                lib.types.submodule {
+                  options = {
+                    chassis = lib.mkOption { type = lib.types.int; };
+                    port = lib.mkOption { type = lib.types.str; };
+                  };
+                }
+              );
+              default = null;
+            };
+            inherit address;
           };
-          type = mkOption {
-            type = types.enum [
-              "hvm"
-              "linux"
-            ];
-            default = "hvm";
-            example = "linux";
-          };
-          arch = mkOption { type = types.str; };
-          machine = mkOption { type = types.str; };
-
-          # TODO(emile): features
-          # Search for the following...
-          # > When using firmware auto-selection there are different features enabled in the firmwares
-          # ... here: https://libvirt.org/formatdomain.html
-          # can't bother to implement this now
-
-          # features = mkOption {
-          # 	type = types.submodule {
-          # 		options = {
-          # 			enabled = {
-          # 				type = types.enum [ "yes" "no" ];
-          # 			};
-          # 			name = {};
-          # 		};
-          # 	};
-          # };
-
-          # such as:
-          # <loader readonly='yes' secure='yes' type='pflash'>/usr/share/OVMF/OVMF_CODE.fd</loader>
-          loader = mkOption {
-            type = types.submodule {
-              options = {
-                readonly = mkOption {
-                  type = types.enum [
-                    "yes"
-                    "no"
-                  ];
+        };
+
+        emulator = lib.types.submodule {
+          options = {
+            value = lib.mkOption { type = lib.types.str; };
+          };
+        };
+
+        disk = lib.types.submodule {
+          options = {
+            type = enumOpt [ "block" ];
+            device = enumOpt [ "disk" ];
+            driver = lib.mkOption {
+              type = lib.types.submodule {
+                options = {
+                  name = lib.mkOption { type = lib.types.str; };
+                  type = enumOpt [ "raw" ];
+                  cache = enumOpt [ "none" ];
+                  io = enumOpt [ "native" ];
+                  discard = enumOpt [ "unmap" ];
                 };
-                type = mkOption {
-                  type = types.enum [
-                    "rom"
-                    "pflash"
-                  ];
+              };
+            };
+            source = lib.mkOption {
+              type = lib.types.submodule {
+                options = {
+                  dev = lib.mkOption { type = lib.types.str; };
+                  index = lib.mkOption { type = lib.types.int; };
+                };
+              };
+            };
+            backingStore = lib.mkOption { type = lib.types.bool; };
+            target = lib.mkOption {
+              type = lib.types.submodule {
+                options = {
+                  dev = lib.mkOption { type = lib.types.str; };
+                  bus = enumOpt [ "virtio" ];
+                };
+              };
+            };
+            alias = lib.mkOption {
+              type = lib.types.submodule {
+                options = {
+                  name = lib.mkOption { type = lib.types.str; };
                 };
-                secure = mkOption {
-                  type = types.enum [
+              };
+            };
+            inherit address;
+          };
+        };
+
+        devices = lib.mkOption {
+          type = lib.types.submodule {
+            options = {
+              emulators = lib.mkOption {
+                type = lib.types.listOf emulator;
+              };
+              disks = lib.mkOption {
+                type = lib.types.listOf disk;
+              };
+              controllers = lib.mkOption {
+                type = lib.types.listOf controller;
+              };
+              interfaces = lib.mkOption {
+                type = lib.types.listOf interface;
+              };
+            };
+          };
+        };
+
+        pm = lib.mkOption {
+          type = lib.types.submodule {
+            options = {
+              suspend-to-mem = lib.mkOption {
+                type = lib.types.nullOr (
+                  lib.types.enum [
                     "yes"
                     "no"
-                  ];
-                };
-                stateless = mkOption {
-                  type = types.enum [
+                  ]
+                );
+                default = null;
+                example = "yes";
+              };
+              suspend-to-disk = lib.mkOption {
+                type = lib.types.nullOr (
+                  lib.types.enum [
                     "yes"
                     "no"
-                  ];
-                };
-                format = mkOption {
-                  type = types.enum [
-                    "raw"
-                    "qcow2"
-                  ];
-                };
-                value = mkOption {
-                  type = types.str;
-                  example = "/usr/share/OVMF/OVMF_CODE.fd";
-                };
+                  ]
+                );
+                default = null;
+                example = "yes";
               };
             };
           };
+        };
 
-          # <nvram type='network'>
-          #   <source protocol='iscsi' name='iqn.2013-07.com.example:iscsi-nopool/0'>
-          #     <host name='example.com' port='6000'/>
-          #     <auth username='myname'>
-          #       <secret type='iscsi' usage='mycluster_myname'/>
-          #     </auth>
-          #   </source>
-          # </nvram>
-
-          # nvram = {
-          # 	type = "network";
-          # 	source = {
-          # 		protocol = "iscsi";
-          # 		name = "iqn.2013-07.com.example:iscsi-nopool/0";
-          # 	};
-          # };
-
-          nvram = mkOption {
-            type = types.submodule {
-              options = {
-                type = mkOption {
-                  type = types.enum [
-                    "file"
-                    "block"
-                    "dir"
-                    "network"
-                    "volume"
-                    "nvme"
-                    "vhostuser"
-                    "vhostvdpa"
-                  ];
-                  default = "";
-                };
+        on_handle = lib.mkOption {
+          type = lib.types.enum [
+            "destroy"
+            "restart"
+            "preserve"
+            "rename-restart"
+          ];
+        };
 
-                source = mkOption {
-                  type = types.submodule {
+        on_poweroff = on_handle;
+        on_reboot = on_handle;
+        on_crash = on_handle;
+
+        clock = lib.mkOption {
+          type = lib.types.submodule {
+            options = {
+              offset = lib.mkOption {
+                type = lib.types.str;
+                example = "utc";
+                default = "utc";
+              };
+              timer = lib.mkOption {
+                type = lib.types.listOf (
+                  lib.types.submodule {
                     options = {
-                      # TODO(emile): figure out how to conditionally allow setting the options below
-                      #              if the type defined above has been set
-
-                      # if type == file
-                      file = mkOption { type = types.str; };
-                      fdgroup = mkOption { type = types.str; };
-
-                      # if type == block
-                      dev = mkOption { type = types.str; };
-
-                      # if type == dir
-                      dir = mkOption { type = types.str; };
-
-                      # if type == network
-                      protocol = mkOption {
-                        type = types.enum [
-                          "nbd"
-                          "iscsi"
-                          "rbd"
-                          "sheepdog"
-                          "gluster"
-                          "vxhs"
-                          "nfs"
-                          "http"
-                          "https"
-                          "ftp"
-                          "ftps"
-                          "tftp"
-                          "ssh"
-                        ];
-                      };
-                      name = mkOption { type = types.str; };
-                      tls = mkOption {
-                        type = types.enum [
-                          "yes"
-                          "no"
+                      name = lib.mkOption {
+                        type = lib.types.enum [
+                          # "platform" (currently unsupported)
+                          "hpet" # xen, qemu, lxc
+                          "kvmclock" # qemu
+                          "pit" # qemu
+                          "rtc" # qemu, lxc
+                          "tsc" # xen, qemu - since 3.2.0
+
+                          # The hypervclock timer adds support for the reference time counter and the reference page for iTSC feature for guests running the Microsoft Windows operating system.
+                          "hypervclock" # qemu - since 1.2.2
+
+                          "armvtimer" # qemu - since 6.1.0
                         ];
+                        default = "";
+                        example = "";
                       };
-                      tlsHostname = mkOption { type = types.str; };
-                      query = mkOption { type = types.str; };
-
-                      # if type == volume
-                      pool = mkOption { type = types.str; };
-                      volume = mkOption { type = types.str; };
-                      mode = mkOption {
-                        type = types.enum [
-                          "direct"
-                          "host"
+                      track = lib.mkOption {
+                        # TODO(emile): Only valid for name="rtc" or name="platform".
+                        type = lib.types.enum [
+                          "boot"
+                          "guest"
+                          "wall"
+                          "realtime"
                         ];
-                        default = "host";
-                      };
-
-                      # if type == nvme
-                      type = mkOption {
-                        type = types.enum [ "pci" ];
-                        default = "pci";
-                        description = ''
-                          													When the type is `nvme`, only `pci` is supported
-                          													When the type is `vhostuser`, only `unix` is supported
-
-                          													(I've got not clue how to model this is nix, it's essentially an attribute that is overloaded based on another value)
-                          												'';
+                        default = "";
+                        example = "";
                       };
-                      managed = mkOption {
-                        type = types.enum [
-                          "yes"
-                          "no"
-                        ];
+                      tickpolicy = lib.mkOption {
+                        type = lib.types.nullOr (
+                          lib.types.enum [
+                            "catchup"
+                            "delay"
+                            "merge"
+                            "discard"
+                            "catchup"
+                          ]
+                        );
+                        default = null;
+                        example = "";
                       };
-                      namespace = mkOption {
-                        type = types.int;
-                        default = 0;
+                      present = lib.mkOption {
+                        type = lib.types.nullOr (
+                          lib.types.enum [
+                            "yes"
+                            "no"
+                          ]
+                        );
+                        default = null;
+                        example = "";
                       };
+                    };
+                  }
+                );
+                example = [
+                  {
+                    name = "rtc";
+                    tickpolicy = "catchup";
+                  }
+                  {
+                    name = "pit";
+                    tickpolicy = "delay";
+                  }
+                  {
+                    name = "hpet";
+                    present = "no";
+                  }
+                ];
+                default = [
+                  {
+                    name = "rtc";
+                    tickpolicy = "catchup";
+                  }
+                  {
+                    name = "pit";
+                    tickpolicy = "delay";
+                  }
+                  {
+                    name = "hpet";
+                    present = "no";
+                  }
+                ];
+              };
+            };
+          };
+        };
 
-                      # if type == vhostuser
-                      # type = see the type in the nvme section above
-                      path = mkOption { type = types.str; };
+        cpu = lib.mkOption {
+          type = lib.types.submodule {
+            options = {
+              mode = lib.mkOption {
+                type = lib.types.str;
+                example = "host-passthrough";
+                default = "";
+              };
+              check = lib.mkOption {
+                type = lib.types.str;
+                example = "none";
+                default = "";
+              };
+              migratable = lib.mkOption {
+                type = lib.types.str;
+                example = "on";
+                default = "";
+              };
+            };
+          };
+        };
 
-                      # if type == vhostvdpa
-                      # dev = (defined above in the "type == block" section)
+        features = lib.mkOption {
+          type = lib.types.submodule {
+            options = {
+              acpi = lib.mkEnableOption "enable acpi";
+              apic = lib.mkEnableOption "enable apic";
+
+              vmport = lib.mkOption {
+                type = lib.types.submodule {
+                  options = {
+                    state = lib.mkOption {
+                      type = lib.types.str;
+                      example = "off";
+                      default = "";
+                    };
+                  };
+                };
+              };
+            };
+          };
+        };
 
-                      # if type == file
-                      # if type == block
-                      # if type == volume
-                      seclabel = mkOption { type = types.str; };
+        os_boot = lib.mkOption {
+          type = lib.types.submodule {
+            options = {
+              dev = lib.mkOption {
+                type = lib.types.str;
+                example = "hd";
+                default = "";
+              };
+            };
+          };
+        };
 
-                      index = mkOption {
-                        type = types.int;
-                        default = 0;
-                      };
+        os_nvram = lib.mkOption {
+          type = lib.types.submodule {
+            options = {
+              value = lib.mkOption {
+                type = lib.types.str;
+                example = "/var/lib/libvirt/qemu/nvram/fileserver2_VARS.fd";
+                default = "";
+              };
+            };
+          };
+        };
 
-                      # TODO(emile): implement checks here
-                      # start here and scroll down a bit to the table
-                      # https://libvirt.org/formatdomain.html#hard-drives-floppy-disks-cdroms
-                      # if type == network
-                      host = mkOption {
-                        type = types.submodule {
-                          options = {
-                            name = mkOption { type = types.str; };
-                            port = mkOption {
-                              type = types.int;
-                              default = 0;
-                            };
-                            transport = mkOption { type = types.str; };
-                            socket = mkOption { type = types.str; };
-
-                          };
-                        };
-                      };
+        os_loader = lib.mkOption {
+          type = lib.types.submodule {
+            options = {
+              readonly = lib.mkOption {
+                type = lib.types.str;
+                example = "yes";
+                default = "";
+              };
+              type = lib.mkOption {
+                type = lib.types.str;
+                example = "pflash";
+                default = "";
+              };
+              value = lib.mkOption {
+                type = lib.types.str;
+                example = "/usr/share/OVMF/OVMF_CODE.fd";
+                default = "";
+              };
+            };
+          };
+        };
 
-                      snapshot = mkOption {
-                        type = types.submodule {
-                          options = {
-                            name = mkOption { type = types.str; };
-                          };
-                        };
-                      };
+        os_type = lib.mkOption {
+          type = lib.types.submodule {
+            options = {
+              arch = lib.mkOption {
+                type = lib.types.str;
+                example = "x86_64";
+                default = "";
+              };
+              machine = lib.mkOption {
+                type = lib.types.str;
+                example = "pc-q35-3.1";
+                default = "";
+              };
+              value = lib.mkOption {
+                type = lib.types.str;
+                example = "hvm";
+                default = "";
+              };
+            };
+          };
+        };
 
-                      config = mkOption {
-                        type = types.submodule {
-                          options = {
-                            file = mkOption { type = types.str; };
-                          };
-                        };
-                      };
+        os = lib.mkOption {
+          description = "os";
+          type = lib.types.submodule {
+            options = {
+              type = os_type;
+              loader = os_loader;
+              nvram = os_nvram;
+              boot = os_boot;
+            };
+          };
+        };
 
-                      # Since 3.9.0, the auth element is supported for a disk type "network" that is using a source element with the protocol attributes "rbd", "iscsi", or "ssh".
-                      auth = mkOption {
-                        type = types.submodule {
-                          options = {
-                            type = mkOption {
-                              # type = types.enum [ "chap" ];
-                              # freeform, yet "chap" is one of the allowed options, I don't know others (yet)
-                              type = types.str;
-                            };
-                            username = mkOption { type = types.str; };
-
-                            # https://libvirt.org/formatsecret.html
-                            secret = mkOption {
-                              type = types.submodule {
-                                options = {
-                                  ephemeral = mkOption {
-                                    type = types.enum [
-                                      "yes"
-                                      "no"
-                                    ];
-                                    default = "no";
-                                  };
-                                  private = mkOption {
-                                    type = types.enum [
-                                      "yes"
-                                      "no"
-                                    ];
-                                    default = "no";
-                                  };
-
-                                  uuid = types.submodule {
-                                    options = {
-                                      value = mkOption {
-                                        type = types.str;
-                                        default = "";
-                                      };
-                                    };
-                                  };
-                                  description = types.submodule {
-                                    options = {
-                                      value = mkOption {
-                                        type = types.str;
-                                        default = "";
-                                      };
-                                    };
-                                  };
-
-                                  usage = types.submodule {
-                                    options = {
-                                      type = mkOption {
-                                        type = types.enum [
-                                          "volume"
-                                          "ceph"
-                                          "iscsi"
-                                          "tls"
-                                          "vtpm"
-                                        ];
-                                        default = "";
-                                      };
-                                      value = mkOption {
-                                        type = types.str;
-                                        default = "";
-                                      };
-
-                                      name = mkOption {
-                                        type = types.submodule {
-                                          options = {
-                                            value = mkOption { type = types.str; };
-                                          };
-                                        };
-                                      };
-
-                                      volume = mkOption {
-                                        type = types.submodule {
-                                          options = {
-                                            value = mkOption { type = types.str; };
-                                          };
-                                        };
-                                      };
-
-                                      # when using the "iscsi" type
-                                      target = mkOption {
-                                        type = types.submodule {
-                                          options = {
-                                            value = mkOption { type = types.str; };
-                                          };
-                                        };
-                                      };
-
-                                    };
-                                  };
-
-                                };
-                              };
-                            }; # end of secret
-
-                          };
-                        };
-                      }; # end of auth
-
-                      # https://libvirt.org/formatstorageencryption.html
-                      encryption = mkOption {
-                        type = types.submodule {
-                          options = {
-                            type = mkOption { type = types.str; };
-
-                            # mandatory
-                            format = mkOption {
-                              type = types.enum [
-                                "default"
-                                "qcow"
-                                "luks"
-                                "luks2"
-                                "luks-any"
-                              ];
-                            };
-
-                            engine = mkOption {
-                              type = types.enum [
-                                "qemu"
-                                "librbd"
-                              ];
-                            };
-
-                            secrets = mkOption {
-                              type = types.listOf (mkOption {
-                                type = types.submodule {
-                                  options = {
-
-                                    # mandatory
-                                    type = mkOption { type = types.enum [ "volume" ]; };
-
-                                    # uuid or usage
-                                    uuid = mkOption { type = types.str; };
-                                    usage = mkOption { type = types.str; };
-
-                                  };
-                                };
-                              });
-                            }; # end of secrets
-
-                            cipher = mkOption {
-                              type = types.submodule {
-                                options = {
-                                  name = mkOption {
-                                    type = types.str;
-                                    example = "'aes', 'des', 'cast5', 'serpent', 'twofish', etc.";
-                                  };
-                                  size = mkOption {
-                                    type = types.str;
-                                    example = "'256', '192', '128', etc.";
-                                  };
-                                  mode = mkOption {
-                                    type = types.str;
-                                    example = "'cbc', 'xts', 'ecb', etc.";
-                                  };
-                                  hash = mkOption {
-                                    type = types.str;
-                                    example = "'md5', 'sha1', 'sha256', etc.";
-                                  };
-                                };
-                              };
-                            }; # end of cipher
-
-                            ivgen = mkOption {
-                              type = types.submodule {
-                                options = {
-                                  name = mkOption {
-                                    type = types.str;
-                                    example = "'plain', 'plain64', 'essiv', etc.";
-                                  };
-                                  hash = mkOption {
-                                    type = types.str;
-                                    example = "'md5', 'sha1', 'sha256'";
-                                  };
-                                };
-                              };
-                            }; # end of ivygen
-                          };
-                        };
-                      }; # end of encryption
-
-                      # TODO(emile): reservations
-                      # Looking at the following, it seems like this can use the source element recursively
-                      # Haven't looked into how to define recursive nix options yet...
-                      # https://github.com/virt-manager/virt-manager/blob/5ddd3456a0ca9836a98fc6ca4f0b2eaab268bf47/tests/data/cli/compare/virt-install-many-devices.xml#L398-L400
-
-                      # TODO(emile): initiator
-
-                      # Based on this here:
-                      # https://github.com/virt-manager/virt-manager/blob/5ddd3456a0ca9836a98fc6ca4f0b2eaab268bf47/tests/data/cli/compare/virt-install-many-devices.xml#L440
-                      address = mkOption {
-                        type = types.submodule {
-                          options = {
-                            domain = mkOption { type = types.int; };
-                            bus = mkOption { type = types.int; };
-                            slot = mkOption { type = types.int; };
-                            function = mkOption { type = types.int; };
-                          };
-                        };
-                      }; # end of address
-
-                      # TODO(emile): slices
-                      # Didn't find any usage of it, why is there documentation for it then?
-
-                      ssl = mkOption {
-                        type = types.submodule {
-                          options = {
-                            verify = mkOption {
-                              type = types.enum [
-                                "yes"
-                                "no"
-                              ];
-                            };
-                          };
-                        };
-                      }; # end of ssl
-
-                      # TODO(emile): cookies for http and https
-
-                      readahead = mkOption {
-                        type = types.submodule {
-                          options = {
-                            size = mkOption { type = types.int; };
-                          };
-                        };
-                      }; # end of readahead
-
-                      timeout = mkOption {
-                        type = types.submodule {
-                          options = {
-                            seconds = mkOption { type = types.int; };
-                          };
-                        };
-                      }; # end of timeout
-
-                      identity = mkOption {
-                        type = types.submodule {
-                          options = {
-                            user = mkOption { type = types.str; };
-                            group = mkOption { type = types.str; };
-
-                            # if ssh
-                            # required
-                            username = mkOption { type = types.str; };
-
-                            # one of these:
-                            agentsock = mkOption { type = types.str; };
-                            keyfile = mkOption { type = types.str; };
-                          };
-                        };
-                      }; # end of identity
-
-                      # disk type "vhostuser"
-                      reconnect = mkOption {
-                        type = types.submodule {
-                          options = {
-                            # mandatory
-                            enabled = mkOption {
-                              type = types.enum [
-                                "yes"
-                                "no"
-                              ];
-                            };
-                            timeout = mkOption {
-                              type = types.int;
-                              description = "seconds";
-                            };
-
-                            # optional for disk type network and protocol nbd
-                            delay = mkOption {
-                              type = types.int;
-                              default = 0;
-                              description = "seconds";
-                            };
-                          };
-                        };
-                      }; # end of reconnect
-
-                      # disk type "ssh"
-                      knownHosts = mkOption {
-                        type = types.submodule {
-                          options = {
-                            path = mkOption { type = types.str; };
-                          };
-                        };
-                      }; # end of knownHosts
-
-                      dataStore = mkOption {
-                        type = types.submodule {
-                          options = {
-                            # TODO(emile): can accept the same types as a `source` element
-                            type = mkOption { type = types.str; };
-
-                            # TODO(emile): can accept format and source subelements
-                            # this is (once again) recursive for the source, stil need to figure
-                            # out how to handle this, just not now
-                          };
-                        };
-                      }; # end of dataStore
-
-                      startupPolicy = mkOption {
-                        type = types.enum [
-                          "mandatory"
-                          "requisite"
-                          "optional"
-                        ];
-                        default = "mandatory";
-                      }; # end of startupPolicy
-
-                      backingStore = mkOption {
-                        type = types.submodule {
-                          options = {
-                            type = mkOption {
-                              # TODO(emile): can accept the same types as a `source` element
-                              type = types.str;
-                            };
-
-                            index = mkOption {
-                              # TODO(emile): figure out if this can just be an int
-                              type = types.str;
-                            };
-
-                            # TODO(emile): can use the following sub elements:
-                            # - format
-                            # - source
-                            # - backingStore
-                          };
-                        };
-                      }; # end of backingStore
-
-                      mirror = mkOption {
-                        type = types.submodule {
-                          options = {
-                            api = mkOption {
-                              type = types.enum [
-                                "copy"
-                                "active-commit"
-                              ];
-                            };
-                            ready = mkOption {
-                              type = types.enum [
-                                "yes"
-                                "abort"
-                                "pivot"
-                              ];
-                            };
-                            # TODO(emile): can use the following sub elements:
-                            # - type (disk types)
-                            # - format
-                            # - source
-                            # - file
-                          };
-                        };
-                      }; # end of mirror
+        resource = lib.mkOption {
+          description = "resource";
+          type = lib.types.submodule {
+            options = {
+              partition = lib.mkOption {
+                type = lib.types.str;
+                example = "/machine";
+                default = "";
+              };
+            };
+          };
+        };
 
-                    };
-                  };
-                }; # end of source
+        vcpu = lib.mkOption {
+          description = "vcpu";
+          type = lib.types.submodule {
+            options = {
+              placement = lib.mkOption {
+                # TODO(emile): fill up
+                type = lib.types.enum [ "static" ];
+                example = "static";
+                default = "static";
+              };
+              count = lib.mkOption {
+                type = lib.types.int;
+                example = 4;
+                default = 1;
+              };
+            };
+          };
+        };
+
+        mem = lib.mkOption {
+          description = "memory";
+          type = lib.types.submodule {
+            options = {
+              unit = lib.mkOption {
+                # TODO(emile): fill up
+                type = lib.types.enum [
+                  "KiB"
+                  "MiB"
+                  "GiB"
+                  "TiB"
+                ];
+                example = "KiB";
+                default = "KiB";
+              };
+              value = lib.mkOption {
+                type = lib.types.int;
+                example = 2097152;
+                default = 1000;
+              };
+            };
+          };
+        };
 
+        memory = mem;
+        currentMemory = mem;
+
+        metadata = lib.mkOption {
+          description = "metadata submodule";
+          type = lib.types.submodule {
+            options = {
+              libosinfo = lib.mkOption {
+                type = lib.types.str;
+                example = "https://libosinfo.org/xmlns/libvirt/domain/1.0";
+                default = "";
+              };
+              libosinfo_os = lib.mkOption {
+                type = lib.types.str;
+                example = "https://nixos.org/nixos/unknown";
+                default = "";
               };
             };
-          }; # end of nvram
+          };
+        };
 
+        vm = lib.types.submodule {
+          options = {
+            vm_type = stringOption { default = "kvm"; };
+            id = stringOption { };
+            name = stringOption { };
+            uuid = stringOption { };
+
+            inherit
+              metadata
+              memory
+              currentMemory
+              vcpu
+              resource
+              os
+              features
+              cpu
+              clock
+              on_poweroff
+              on_reboot
+              on_crash
+              pm
+              devices
+              ;
+          };
+        };
+      in
+      {
+        enable = lib.mkEnableOption "Enable r2wars-web";
+
+        # Temporary output we write the domain to, in order to inspect the correctness of
+        # the generated xml
+        output.domain = lib.mkOption { type = lib.types.path; };
+
+        # An attrset of VMs
+        vm = lib.mkOption {
+          description = "VMs to define";
+          type = lib.types.attrsOf vm;
         };
       };
-    };
+  };
 
-    memory = lib.mkOption {
-      type = lib.types.int;
-      default = 1024;
-      example = 2048;
-      description = ''
-        The amount of memory to provide to the VM
-      '';
-    };
+  config = lib.mkIf config.services.emile.libvirtnix.enable {
+    services.emile.libvirtnix =
+      let
+        vm = config.services.emile.libvirtnix.vm;
+
+        # try this in a nix repl
+        # nix-repl> mkTag = import ./xml.nix { lib = (import <nixpkgs> {}).lib; }
+        # nix-repl> :p let func = (x: mkTag {name=x.variant;}); case = {"emulator" = func; "disk" = func;}; in map (x: case."${x.variant}" x) [ {variant = "emulator"; value = "asd";} {variant = "disk"; type = "block"; } ]
+
+        emulator =
+          x:
+          mkTag {
+            name = "emulator";
+            value = x.value;
+          };
+
+        interface =
+          x:
+          mkTag {
+            name = "interface";
+            args = [
+              (
+                if x.type != null then
+                  {
+                    key = "type";
+                    val = x.type;
+                  }
+                else
+                  ""
+              )
+            ];
+            children = [
+              # (
+              #   if x.mac != null then
+              #     (mkTag {
+              #       name = "mac";
+              #       args = [
+              #         {
+              #           key = "address";
+              #           val = x.mac.address;
+              #         }
+              #       ];
+              #       closing = false;
+              #     })
+              #   else
+              #     ""
+              # )
+              #(
+              #  if x.source != null then
+              #    (mkTag {
+              #      name = "source";
+              #      args = [
+              #        {
+              #          key = "network";
+              #          val = x.source.network;
+              #        }
+              #        {
+              #          key = "portid";
+              #          val = x.source.portid;
+              #        }
+              #        {
+              #          key = "bridge";
+              #          val = x.source.bridge;
+              #        }
+              #      ];
+              #      closing = false;
+              #    })
+              #  else
+              #    ""
+              #)
+              #(
+              #  if x.target != null then
+              #    (mkTag {
+              #      name = "target";
+              #      args = [
+              #        {
+              #          key = "dev";
+              #          val = x.target.dev;
+              #        }
+              #      ];
+              #    })
+              #  else
+              #    ""
+              #)
+              #(
+              #  if x.model != null then
+              #    (mkTag {
+              #      name = "model";
+              #      args = [
+              #        {
+              #          key = "type";
+              #          val = x.model.type;
+              #        }
+              #      ];
+              #    })
+              #  else
+              #    ""
+              #)
+              #(
+              #  if x.alias!= null then
+              #    (mkTag {
+              #      name = "alias";
+              #      args = [
+              #        {
+              #          key = "name";
+              #          val = x.alias.name;
+              #        }
+              #      ];
+              #    })
+              #  else
+              #    ""
+              #)
+              #(
+              #  if x.address != null then
+              #    mkTag {
+              #      name = "address";
+              #      args = [
+              #        (
+              #          if x.address.type != null then
+              #            {
+              #              key = "type";
+              #              val = x.address.type;
+              #            }
+              #          else
+              #            ""
+              #        )
+              #        {
+              #          key = "domain";
+              #          val = x.address.domain;
+              #        }
+              #        {
+              #          key = "bus";
+              #          val = x.address.bus;
+              #        }
+              #        {
+              #          key = "slot";
+              #          val = x.address.slot;
+              #        }
+              #        {
+              #          key = "function";
+              #          val = x.address.function;
+              #        }
+              #      ];
+              #      closing = false;
+              #    }
+              #  else
+              #    ""
+              #)
+            ];
+          };
+
+        controller =
+          x:
+          mkTag {
+            name = "controller";
+            args = [
+              (
+                if x.type != null then
+                  {
+                    key = "type";
+                    val = x.type;
+                  }
+                else
+                  ""
+              )
+              (
+                if x.index != null then
+                  {
+                    key = "index";
+                    val = "${toString x.index}";
+                  }
+                else
+                  ""
+              )
+              (
+                if x.model != null then
+                  {
+                    key = "model";
+                    val = x.model;
+                  }
+                else
+                  ""
+              )
+              (
+                if x.ports != null then
+                  {
+                    key = "ports";
+                    val = "${toString x.ports}";
+                  }
+                else
+                  ""
+              )
+            ];
+            children = [
+              # TODO(emile): figure out if the arg in controller is really the same as the
+              #              one given in model. The configs I've got let it seem so
+              # <controller type="pci" index="1" model="pcie-root-port">
+              #   <model name="pcie-root-port"/>
+              (
+                if x.model != null then
+                  (mkTag {
+                    name = "model";
+                    args = [
+                      {
+                        key = "name";
+                        val = x.model;
+                      }
+                    ];
+                    closing = false;
+                  })
+                else
+                  ""
+              )
+              (
+                if x.target != null then
+                  (mkTag {
+                    name = "target";
+                    args = [
+                      {
+                        key = "chassis";
+                        val = "${toString x.target.chassis}";
+                      }
+                      {
+                        key = "port";
+                        val = x.target.port;
+                      }
+                    ];
+                  })
+                else
+                  ""
+              )
+              (mkTag {
+                name = "alias";
+                args = [
+                  (
+                    if x.alias.name != null then
+                      {
+                        key = "name";
+                        val = x.alias.name;
+                      }
+                    else
+                      ""
+                  )
+                ];
+                closing = false;
+              })
+
+              (
+                if x.address != null then
+                  mkTag {
+                    name = "address";
+                    args = [
+                      (
+                        if x.address.type != null then
+                          {
+                            key = "type";
+                            val = x.address.type;
+                          }
+                        else
+                          ""
+                      )
+                      {
+                        key = "domain";
+                        val = x.address.domain;
+                      }
+                      {
+                        key = "bus";
+                        val = x.address.bus;
+                      }
+                      {
+                        key = "slot";
+                        val = x.address.slot;
+                      }
+                      {
+                        key = "function";
+                        val = x.address.function;
+                      }
+                    ];
+                    closing = false;
+                  }
+                else
+                  ""
+              )
+            ];
+          };
+
+        disk =
+          x:
+          mkTag {
+            name = "disk";
+            args = [
+              {
+                key = "type";
+                val = x.type;
+              }
+              {
+                key = "device";
+                val = x.device;
+              }
+            ];
+            children = [
+              (mkTag {
+                name = "driver";
+                args = [
+                  (
+                    if x.driver.name != null then
+                      {
+                        key = "name";
+                        val = x.driver.name;
+                      }
+                    else
+                      ""
+                  )
+                  (
+                    if x.driver.type != null then
+                      {
+                        key = "type";
+                        val = x.driver.type;
+                      }
+                    else
+                      ""
+                  )
+                  (
+                    if x.driver.cache != null then
+                      {
+                        key = "cache";
+                        val = x.driver.cache;
+                      }
+                    else
+                      ""
+                  )
+                  (
+                    if x.driver.io != null then
+                      {
+                        key = "io";
+                        val = x.driver.io;
+                      }
+                    else
+                      ""
+                  )
+                  (
+                    if x.driver.discard != null then
+                      {
+                        key = "discard";
+                        val = x.driver.discard;
+                      }
+                    else
+                      ""
+                  )
+                ];
+                closing = false;
+              })
+              (mkTag {
+                name = "source";
+                args = [
+                  {
+                    key = "dev";
+                    val = x.source.dev;
+                  }
+                  {
+                    key = "index";
+                    val = "${toString x.source.index}";
+                  }
+                ];
+                closing = false;
+              })
+              (
+                if x.backingStore == true then
+                  mkTag {
+                    name = "backingStore";
+                    closing = false;
+                  }
+                else
+                  ""
+              )
+              (mkTag {
+                name = "target";
+                args = [
+                  {
+                    key = "dev";
+                    val = x.target.dev;
+                  }
+                  {
+                    key = "bus";
+                    val = x.target.bus;
+                  }
+                ];
+                closing = false;
+              })
+              (mkTag {
+                name = "alias";
+                args = [
+                  {
+                    key = "name";
+                    val = x.alias.name;
+                  }
+                ];
+                closing = false;
+              })
+              (mkTag {
+                name = "address";
+                args = [
+                  {
+                    key = "type";
+                    val = x.address.type;
+                  }
+                  {
+                    key = "domain";
+                    val = x.address.domain;
+                  }
+                  {
+                    key = "bus";
+                    val = x.address.bus;
+                  }
+                  {
+                    key = "slot";
+                    val = x.address.slot;
+                  }
+                  {
+                    key = "function";
+                    val = x.address.function;
+                  }
+                ];
+                closing = false;
+              })
+            ];
+            value = x.type;
+          };
+
+        devices =
+          vm_name:
+          mkTag {
+            name = "devices";
+            children =
+              map (x: emulator x) vm.${vm_name}.devices.emulators
+              ++ map (x: disk x) vm.${vm_name}.devices.disks
+              ++ map (x: controller x) vm.${vm_name}.devices.controllers
+              ++ map (x: interface x) vm.${vm_name}.devices.interfaces;
+          };
+
+        pm =
+          vm_name:
+          mkTag {
+            name = "pm";
+            children =
+              let
+                suspend-to-mem = mkTag {
+                  name = "suspend-to-mem";
+                  args = [
+                    {
+                      key = "enabled";
+                      val = vm.${vm_name}.pm.suspend-to-mem;
+                    }
+                  ];
+                  closing = false;
+                };
+
+                suspend-to-disk = mkTag {
+                  name = "suspend-to-disk";
+                  args = [
+                    {
+                      key = "enabled";
+                      val = vm.${vm_name}.pm.suspend-to-disk;
+                    }
+                  ];
+                  closing = false;
+                };
+              in
+              [
+                suspend-to-mem
+                suspend-to-disk
+              ];
+          };
+
+        on_handler =
+          { vm_name, tag }:
+          mkTag {
+            name = "${tag}";
+            value = vm.${vm_name}.${tag};
+          };
+
+        on_poweroff =
+          vm_name:
+          on_handler {
+            inherit vm_name;
+            tag = "on_poweroff";
+          };
+        on_reboot =
+          vm_name:
+          on_handler {
+            inherit vm_name;
+            tag = "on_reboot";
+          };
+        on_crash =
+          vm_name:
+          on_handler {
+            inherit vm_name;
+            tag = "on_crash";
+          };
+
+        clock =
+          vm_name:
+          mkTag {
+            name = "clock";
+            args = [
+              {
+                key = "offset";
+                val = vm.${vm_name}.clock.offset;
+              }
+            ];
+            children = map (
+              x:
+              mkTag {
+                name = "timer";
+                args =
+                  let
+                    tickpolicy =
+                      if x.tickpolicy != null then
+                        {
+                          key = "tickpolicy";
+                          val = x.tickpolicy;
+                        }
+                      else
+                        { };
+
+                    present =
+                      if x.present != null then
+                        {
+                          key = "present";
+                          val = x.present;
+                        }
+                      else
+                        { };
+                  in
+                  [
+                    {
+                      key = "name";
+                      val = x.name;
+                    }
+                    tickpolicy
+                    present
+                  ];
+              }
+            ) vm.${vm_name}.clock.timer;
+          };
+
+        cpu =
+          vm_name:
+          mkTag {
+            name = "cpu";
+            args =
+              let
+                mode = vm.${vm_name}.cpu.mode;
+                check = vm.${vm_name}.cpu.check;
+                migratable = vm.${vm_name}.cpu.migratable;
+              in
+              [
+                (
+                  if (mode != null) then
+                    {
+                      key = "mode";
+                      val = mode;
+                    }
+                  else
+                    ""
+                )
+                (
+                  if (check != null) then
+                    {
+                      key = "check";
+                      val = check;
+                    }
+                  else
+                    ""
+                )
+                (
+                  if (migratable != null) then
+                    {
+                      key = "migratable";
+                      val = migratable;
+                    }
+                  else
+                    ""
+                )
+              ];
+            closing = false;
+          };
+
+        features =
+          vm_name:
+          mkTag {
+            name = "features";
+            children =
+              let
+                acpi =
+                  vm_name:
+                  mkTag {
+                    name = "acpi";
+                    closing = false;
+                  };
+                apic =
+                  vm_name:
+                  mkTag {
+                    name = "apic";
+                    closing = false;
+                  };
+                vmport =
+                  vm_name:
+                  mkTag {
+                    name = "vmport";
+                    args = [
+                      {
+                        key = "state";
+                        val = vm.${vm_name}.features.vmport.state;
+                      }
+                    ];
+                    closing = false;
+                  };
+              in
+              [
+                (if (vm.${vm_name}.features.acpi != false) then (acpi vm_name) else "")
+                (if (vm.${vm_name}.features.apic != false) then (apic vm_name) else "")
+                (if (vm.${vm_name}.features.vmport != null) then (vmport vm_name) else "")
+              ];
+          };
+
+        os =
+          vm_name:
+          mkTag {
+            name = "os";
+            children =
+              let
+                os_type =
+                  vm_name:
+                  mkTag {
+                    name = "type";
+                    args = [
+                      {
+                        key = "arch";
+                        val = vm.${vm_name}.os.type.arch;
+                      }
+                      {
+                        key = "machine";
+                        val = vm.${vm_name}.os.type.machine;
+                      }
+                    ];
+                    value = vm.${vm_name}.os.type.value;
+                  };
+                os_loader =
+                  vm_name:
+                  mkTag {
+                    name = "loader";
+                    args = [
+                      {
+                        key = "readonly";
+                        val = vm.${vm_name}.os.loader.readonly;
+                      }
+                      {
+                        key = "pflash";
+                        val = vm.${vm_name}.os.loader.type;
+                      }
+                    ];
+                    value = vm.${vm_name}.os.loader.value;
+                  };
+                os_nvram =
+                  vm_name:
+                  mkTag {
+                    name = "nvram";
+                    value = vm.${vm_name}.os.nvram.value;
+                  };
+                os_boot =
+                  vm_name:
+                  mkTag {
+                    name = "type";
+                    args = [
+                      {
+                        key = "dev";
+                        val = vm.${vm_name}.os.boot.dev;
+                      }
+                    ];
+                    closing = false;
+                  };
+              in
+              [
+                (os_type vm_name)
+                (os_loader vm_name)
+                (os_nvram vm_name)
+                (os_boot vm_name)
+              ];
+          };
+
+        resource =
+          vm_name:
+          mkTag {
+            name = "resource";
+            children = [
+              (mkTag {
+                name = "partition";
+                value = "${toString vm.${vm_name}.resource.partition}";
+              })
+            ];
+          };
+
+        vcpu =
+          vm_name:
+          mkTag {
+            name = "vcpu";
+            args = [
+              {
+                key = "placement";
+                val = vm.${vm_name}.vcpu.placement;
+              }
+            ];
+            value = "${toString vm.${vm_name}.vcpu.count}";
+          };
+
+        currentMemory =
+          vm_name:
+          mkTag {
+            name = "currentMemory";
+            args = [
+              {
+                key = "unit";
+                val = vm.${vm_name}.currentMemory.unit;
+              }
+            ];
+            value = "${toString vm.${vm_name}.currentMemory.value}";
+          };
+        memory =
+          vm_name:
+          mkTag {
+            name = "memory";
+            args = [
+              {
+                key = "unit";
+                val = vm.${vm_name}.memory.unit;
+              }
+            ];
+            value = "${toString vm.${vm_name}.memory.value}";
+          };
+
+        libosinfo_os =
+          vm_name:
+          mkTag {
+            name = "libosinfo:os";
+            args = [
+              {
+                key = "id";
+                val = vm.${vm_name}.metadata.libosinfo_os;
+              }
+            ];
+            closing = false;
+          };
+
+        libosinfo_libosinfo =
+          vm_name:
+          mkTag {
+            name = "libosinfo:libosinfo";
+            args = [
+              {
+                key = "xmlns:libosinfo";
+                val = vm.${vm_name}.metadata.libosinfo;
+              }
+            ];
+            children = [
+              (libosinfo_os vm_name)
+            ];
+          };
+
+        metadata =
+          vm_name:
+          mkTag {
+            name = "metadata";
+            children = [ (libosinfo_libosinfo vm_name) ];
+          };
+
+        uuid =
+          vm_name:
+          mkTag {
+            name = "uuid";
+            value = vm.${vm_name}.uuid;
+          };
+
+        name =
+          vm_name:
+          mkTag {
+            name = "name";
+            value = vm.${vm_name}.name;
+          };
+
+        # the vm_name has to be passed in, as we want to accesses the value for the given vm when
+        # generating the config
+        domain =
+          vm_name:
+          mkTag {
+            name = "domain";
+            args = [
+              {
+                key = "type";
+                val = vm.${vm_name}.vm_type;
+              }
+              {
+                key = "id";
+                val = vm.${vm_name}.id;
+              }
+            ];
+            children = [
+              (name vm_name)
+              (uuid vm_name)
+              (metadata vm_name)
+              (memory vm_name)
+              (currentMemory vm_name)
+              (vcpu vm_name)
+              (resource vm_name)
+              (os vm_name)
+              (features vm_name)
+              (cpu vm_name)
+              (clock vm_name)
+              (on_poweroff vm_name)
+              (on_reboot vm_name)
+              (on_crash vm_name)
+              (pm vm_name)
+              (devices vm_name)
+            ];
+          };
+      in
+      {
+        output.domain = pkgs.writeText "libvirt-domain-config.xml" (domain "alan");
+      };
   };
 }
diff --git a/nix/modules/libvirtnix/os.nix b/nix/modules/libvirtnix/os.nix
new file mode 100644
index 0000000..41f36f3
--- /dev/null
+++ b/nix/modules/libvirtnix/os.nix
@@ -0,0 +1,119 @@
+{ lib, ... }:
+
+let
+  mkOption = lib.mkOption;
+  submodule = lib.types.submodule;
+  types = lib.types;
+
+  os = {
+    firmware = mkOption {
+      type = types.enum [
+        "bios"
+        "efi"
+      ];
+      default = "efi";
+      example = "bios";
+    };
+
+    type = mkOption {
+      type = types.enum [
+        "hvm"
+        "linux"
+      ];
+      default = "hvm";
+      example = "linux";
+    };
+
+    arch = mkOption { type = types.str; };
+    machine = mkOption { type = types.str; };
+
+    # TODO(emile): features
+    # Search for the following...
+    # > When using firmware auto-selection there are different features enabled in the firmwares
+    # ... here: https://libvirt.org/formatdomain.html
+    # can't bother to implement this now
+
+    # features = mkOption {
+    # 	type = types.submodule {
+    # 		options = {
+    # 			enabled = {
+    # 				type = types.enum [ "yes" "no" ];
+    # 			};
+    # 			name = {};
+    # 		};
+    # 	};
+    # };
+
+    # such as:
+    # <loader readonly='yes' secure='yes' type='pflash'>/usr/share/OVMF/OVMF_CODE.fd</loader>
+    loader = mkOption {
+      type = types.submodule {
+        options = {
+          readonly = mkOption {
+            type = types.enum [
+              "yes"
+              "no"
+            ];
+          };
+          type = mkOption {
+            type = types.enum [
+              "rom"
+              "pflash"
+            ];
+          };
+          secure = mkOption {
+            type = types.enum [
+              "yes"
+              "no"
+            ];
+          };
+          stateless = mkOption {
+            type = types.enum [
+              "yes"
+              "no"
+            ];
+          };
+          format = mkOption {
+            type = types.enum [
+              "raw"
+              "qcow2"
+            ];
+          };
+          value = mkOption {
+            type = types.str;
+            example = "/usr/share/OVMF/OVMF_CODE.fd";
+          };
+        };
+      };
+    };
+
+    # <nvram type='network'>
+    #   <source protocol='iscsi' name='iqn.2013-07.com.example:iscsi-nopool/0'>
+    #     <host name='example.com' port='6000'/>
+    #     <auth username='myname'>
+    #       <secret type='iscsi' usage='mycluster_myname'/>
+    #     </auth>
+    #   </source>
+    # </nvram>
+
+    # nvram = {
+    # 	type = "network";
+    # 	source = {
+    # 		protocol = "iscsi";
+    # 		name = "iqn.2013-07.com.example:iscsi-nopool/0";
+    # 	};
+    # };
+    
+  };
+in
+{
+  options = {
+    os = mkOption {
+      type = submodule {
+        options = {
+          inherit (os) firmware type arch machine loader;
+        };
+      };
+    };
+  };
+}
diff --git a/nix/modules/libvirtnix/secret.nix b/nix/modules/libvirtnix/secret.nix
new file mode 100644
index 0000000..b597174
--- /dev/null
+++ b/nix/modules/libvirtnix/secret.nix
@@ -0,0 +1,174 @@
+{ config, lib, ... }:
+
+# https://libvirt.org/formatsecret.html
+
+let
+  pkgs = import <nixpkgs> { };
+
+  mkOption = lib.mkOption;
+  submodule = lib.types.submodule;
+  types = lib.types;
+  enum = types.enum;
+  str = types.str;
+
+  yesNoOption = mkOption {
+    type = enum [
+      "yes"
+      "no"
+    ];
+    default = "no";
+  };
+  # YesnoOption = mkOption { type = enum [ "yes" "no" ]; default = "yes"; };
+
+  # takes a few args and creats a valid xml tag pair out of it
+  #
+  # testTag = mkTag {
+  #   name = "name";
+  #   args = [
+  #     {
+  #       key = "arg1";
+  #       val = "arg1val";
+  #     }
+  #     {
+  #       key = "arg2";
+  #       val = "arg2val";
+  #     }
+  #   ];
+  #   value = "qwe";
+  #   children = [
+  #     (mkTag { name = "nested"; args = []; value = "qwe"; children = [];})
+  #   ];
+  # };
+  #
+  # <name arg1=arg1val arg2=arg2val>
+  #   value
+  #   {children}
+  # </name>
+  mkTag =
+    {
+      name, # name of the tag to be used, such as `secret`, `description`, ...
+      args ? [ ], # args, [ { key="a"; val="b"; } { key="c"; val="d"; } ]
+      value ? "", # the value to place in the middle
+      children ? [ ], # the child elements
+    }:
+    let
+      args_str =
+        " " + lib.strings.concatStrings (lib.strings.intersperse " " (map (x: "${x.key}='${x.val}'") args));
+      child_evaled = lib.strings.concatStrings children;
+    in
+    "<${name}${lib.optionalString (args != [ ]) args_str}>${value}${child_evaled}</${name}>";
+
+  strOption =
+    {
+      default ? "",
+    }:
+    mkOption {
+      type = str;
+      default = "${default}";
+    };
+
+  usage = mkOption {
+    type = submodule {
+      options = {
+        type = mkOption {
+          type = enum [
+            "volume"
+            "ceph"
+            "iscsi"
+            "tls"
+            "vtpm"
+          ];
+          default = "";
+        };
+
+        value = strOption { };
+
+        name = strOption { };
+        volume = strOption { };
+        target = strOption { };
+      };
+    };
+  };
+
+  secret = {
+    inherit usage;
+
+    ephemeral = yesNoOption;
+    private = yesNoOption;
+
+    uuid = strOption { };
+    description = strOption { };
+  };
+
+in
+{
+  options = {
+    services.emile.libvirtnix = {
+      enable = lib.mkEnableOption "Enable r2wars-web";
+
+      secret = mkOption {
+        type = submodule {
+          options = {
+            inherit (secret)
+              ephemeral
+              private
+              uuid
+              description
+              usage
+              ;
+          };
+        };
+      };
+
+      # output = mkOption { type = types.path; };
+    };
+  };
+
+  config = lib.mkIf config.services.emile.libvirtnix.enable {
+    services.emile.libvirtnix =
+      let
+        secret = mkTag {
+          name = "secret";
+          args = [
+            {
+              key = "ephemeral";
+              val = config.services.emile.libvirtnix.secret.ephemeral;
+            }
+            {
+              key = "private";
+              val = config.services.emile.libvirtnix.secret.private;
+            }
+          ];
+          children = [
+            (mkTag {
+              name = "description";
+              value = "Super secret description";
+            })
+            (mkTag {
+              name = "uuid";
+              value = "0a81f5b2-8403-7b23-c8d6-21ccc2f80d6f";
+            })
+            (mkTag {
+              name = "usage";
+              args = [
+                {
+                  key = "type";
+                  val = "volume";
+                }
+              ];
+              children = [
+                (mkTag {
+                  name = "volume";
+                  value = "/var/lib/libvirt/images/kernel.img";
+                })
+              ];
+            })
+          ];
+        };
+
+      in
+      {
+        # output = pkgs.writeText "libvirt-secret-config.xml" secret;
+      };
+  };
+}
diff --git a/nix/modules/libvirtnix/test.nix b/nix/modules/libvirtnix/test.nix
new file mode 100644
index 0000000..8542b7e
--- /dev/null
+++ b/nix/modules/libvirtnix/test.nix
@@ -0,0 +1,10 @@
+let
+	pkgs = import <nixpkgs> {};
+in
+	pkgs.lib.evalModules {
+		modules = [
+			# ./secret.nix
+			./domain.nix
+			./config.nix
+		];
+	}
diff --git a/nix/modules/libvirtnix/xml.nix b/nix/modules/libvirtnix/xml.nix
new file mode 100644
index 0000000..10eefa3
--- /dev/null
+++ b/nix/modules/libvirtnix/xml.nix
@@ -0,0 +1,58 @@
+{ lib, ... }:
+
+# takes a few args and creats a valid xml tag pair out of it
+#
+# testTag = mkTag {
+#   name = "name";
+#   args = [
+#     {
+#       key = "arg1";
+#       val = "arg1val";
+#     }
+#     {
+#       key = "arg2";
+#       val = "arg2val";
+#     }
+#   ];
+#   value = "qwe";
+#   children = [
+#     (mkTag { name = "nested"; args = []; value = "qwe"; children = [];})
+#   ];
+# };
+#
+# <name arg1=arg1val arg2=arg2val>
+#   value
+#   {children}
+# </name>
+{
+  name, # name of the tag to be used, such as `secret`, `description`, ...
+  args ? [ ], # args, [ { key="a"; val="b"; } { key="c"; val="d"; } ]
+  value ? "", # the value to place in the middle
+  children ? [ ], # the child elements
+  closing ? true, # add a closing tag
+}:
+let
+
+  # filter out all values missing a key
+  # this allows passing empty args such as `{}` which then get discarded
+  filteredArgs = (builtins.filter (x: x ? key) args);
+
+  # convert the list of attr sets into a list of strings
+  mappedArgs = (map (x: "${x.key}='${x.val}'") filteredArgs);
+
+  # [ "A" "B" "C" ] => [ "A" " " "B" " " "C" ]
+  spacedArgs = lib.strings.intersperse "" mappedArgs;
+
+  # [ "A" " " "B" " " "C" ] => "A B C"
+  joinedArgs = lib.strings.concatStrings spacedArgs;
+
+  # prefix the args with a space when inserting
+  args_str = lib.optionalString (filteredArgs != [ ]) (" " + joinedArgs);
+
+  child_evaled = lib.strings.concatStrings children;
+
+  cond = condition: value: if condition then value else "";
+
+  closingTag = if closing == true then "</${name}>" else "";
+in
+"<${name}${args_str}${cond (closing == false) "/"}>${value}${child_evaled}${lib.optionalString (closing) closingTag}"
diff --git a/nix/pkgs/aarch64-darwin.nix b/nix/pkgs/aarch64-darwin.nix
index f5e8b60..78d5374 100644
--- a/nix/pkgs/aarch64-darwin.nix
+++ b/nix/pkgs/aarch64-darwin.nix
@@ -1,6 +1,7 @@
 final: prev: {
   vokobe = final.callPackage ./vokobe { inherit (final) naersk; };
   r2wars-web = final.callPackage ./r2wars-web { };
-  remarvin = final.callPackage ./remarvin { };
-  libc-database = final.callPackage ./libc-database {};
+  # remarvin = final.callPackage ./remarvin { };
+  # libc-database = final.callPackage ./libc-database {};
+  # glibc-all-in-one = final.callPackage ./glibc-all-in-one { };
 }
diff --git a/nix/pkgs/glibc-all-in-one/default.nix b/nix/pkgs/glibc-all-in-one/default.nix
new file mode 100644
index 0000000..896f91e
--- /dev/null
+++ b/nix/pkgs/glibc-all-in-one/default.nix
@@ -0,0 +1,36 @@
+{ pkgs ? import <nixpkgs> {}, lib, fetchFromGitHub }:
+
+let
+  python = pkgs.python311.withPackages ( ps: with ps; [ requests ] );
+in pkgs.stdenv.mkDerivation rec {
+  name = "glibc-all-in-one";
+  version = "master";
+
+  src = fetchFromGitHub {
+    owner = "fr0ster";
+    repo = "glibc-all-in-one";
+    rev = version;
+    sha256 = "sha256-vrh/ol56sNWTjGbwZ1Jrh+Lxz7aROvI6IbkJf/WliNE=";
+  };
+
+  buildPhase = '''';
+
+  installPhase = ''
+    mkdir -p $out/bin
+    cp build download extract $out/bin
+    cp update_list $out/bin/update_list_raw
+
+    echo "${python}/bin/python3 $out/bin/update_list_raw" > $out/bin/update_list
+    chmod +x $out/bin/update_list
+
+    mkdir -p $out/debs
+    mkdir -p $out/libs
+  '';
+
+  meta = {
+    description = "";
+    homepage = "https://github.com/fr0ster/glibc-all-in-one";
+    license = lib.licenses.mit;
+    maintainers = with lib.maintainers; [ hanemile ];
+  };
+ }
diff --git a/nix/pkgs/libc-database/default.nix b/nix/pkgs/libc-database/default.nix
index b13a167..0bc91b4 100644
--- a/nix/pkgs/libc-database/default.nix
+++ b/nix/pkgs/libc-database/default.nix
@@ -13,7 +13,7 @@ pkgs.stdenv.mkDerivation rec {
     owner = "niklasb";
     repo = "libc-database";
     rev = version;
-    sha256 = "Zysjhr76TenMarnoKo+M8DrTNbsnaXSoFZO1puPVoxU=";
+    sha256 = "sha256-Zysjhr76TenMarnoKo+M8DrTNbsnaXSoFZO1puPVoxU=";
   };
 
   # not building, we just want to download the repo
diff --git a/nix/pkgs/r2wars-web/default.nix b/nix/pkgs/r2wars-web/default.nix
index 1d55de3..01b4a2d 100644
--- a/nix/pkgs/r2wars-web/default.nix
+++ b/nix/pkgs/r2wars-web/default.nix
@@ -18,7 +18,7 @@ pkgs.buildGoModule rec {
 
   vendorHash = null;
 
-  CGO_ENABLED = 0;
+  env.CGO_ENABLED = 0;
   subPackages = [ "src" ];
 
   postInstall = ''
diff --git a/nix/pkgs/x86_64-linux.nix b/nix/pkgs/x86_64-linux.nix
index c186cc4..33f314b 100644
--- a/nix/pkgs/x86_64-linux.nix
+++ b/nix/pkgs/x86_64-linux.nix
@@ -2,5 +2,6 @@ final: prev: {
   vokobe = final.callPackage ./vokobe { inherit (final) naersk; };
   r2wars-web = final.callPackage ./r2wars-web { };
   remarvin = final.callPackage ./remarvin { };
+  # glibc-all-in-one = final.callPackage ./glibc-all-in-one { };
   # libc-database = final.callPackage ./libc-database {};
 }
diff --git a/nix/templates/ctf/flake.lock b/nix/templates/ctf/flake.lock
new file mode 100644
index 0000000..b756d8d
--- /dev/null
+++ b/nix/templates/ctf/flake.lock
@@ -0,0 +1,141 @@
+{
+  "nodes": {
+    "nixpkgs": {
+      "locked": {
+        "lastModified": 1740603184,
+        "narHash": "sha256-t+VaahjQAWyA+Ctn2idyo1yxRIYpaDxMgHkgCNiMJa4=",
+        "ref": "nixos-24.11",
+        "rev": "f44bd8ca21e026135061a0a57dcf3d0775b67a49",
+        "shallow": true,
+        "type": "git",
+        "url": "ssh://git@github.com/nixos/nixpkgs.git"
+      },
+      "original": {
+        "ref": "nixos-24.11",
+        "shallow": true,
+        "type": "git",
+        "url": "ssh://git@github.com/nixos/nixpkgs.git"
+      }
+    },
+    "nixpkgs_2": {
+      "locked": {
+        "lastModified": 1736241350,
+        "narHash": "sha256-CHd7yhaDigUuJyDeX0SADbTM9FXfiWaeNyY34FL1wQU=",
+        "owner": "NixOS",
+        "repo": "nixpkgs",
+        "rev": "8c9fd3e564728e90829ee7dbac6edc972971cd0f",
+        "type": "github"
+      },
+      "original": {
+        "owner": "NixOS",
+        "ref": "nixpkgs-unstable",
+        "repo": "nixpkgs",
+        "type": "github"
+      }
+    },
+    "pwndbg": {
+      "inputs": {
+        "nixpkgs": "nixpkgs_2",
+        "pyproject-build-systems": "pyproject-build-systems",
+        "pyproject-nix": "pyproject-nix",
+        "uv2nix": "uv2nix"
+      },
+      "locked": {
+        "lastModified": 1740333626,
+        "narHash": "sha256-OcwULIZcWOC1FNGa0SNGtyMyfbwTsBj17LBPpGOZL78=",
+        "ref": "refs/heads/dev",
+        "rev": "ef090ebf5eb75713b1f97c3d9aa3d7be636b0c3a",
+        "revCount": 2284,
+        "type": "git",
+        "url": "ssh://git@github.com/pwndbg/pwndbg"
+      },
+      "original": {
+        "type": "git",
+        "url": "ssh://git@github.com/pwndbg/pwndbg"
+      }
+    },
+    "pyproject-build-systems": {
+      "inputs": {
+        "nixpkgs": [
+          "pwndbg",
+          "nixpkgs"
+        ],
+        "pyproject-nix": [
+          "pwndbg",
+          "pyproject-nix"
+        ],
+        "uv2nix": [
+          "pwndbg",
+          "uv2nix"
+        ]
+      },
+      "locked": {
+        "lastModified": 1737338290,
+        "narHash": "sha256-gnXlfFEHA+/jMH7R+7y3JxrI3WfOjgBhzzJNuFW70UU=",
+        "owner": "pyproject-nix",
+        "repo": "build-system-pkgs",
+        "rev": "e1487e5cefda0c7990bdd2e660bee20971680e45",
+        "type": "github"
+      },
+      "original": {
+        "owner": "pyproject-nix",
+        "repo": "build-system-pkgs",
+        "type": "github"
+      }
+    },
+    "pyproject-nix": {
+      "inputs": {
+        "nixpkgs": [
+          "pwndbg",
+          "nixpkgs"
+        ]
+      },
+      "locked": {
+        "lastModified": 1738204167,
+        "narHash": "sha256-J5M2sj3x4ocM93shScT/3Z4XWHZhwwW1NyQK+C+8Mys=",
+        "owner": "pyproject-nix",
+        "repo": "pyproject.nix",
+        "rev": "0d9f4b90cee1b5c5d6c142ef22de1e246e003ccc",
+        "type": "github"
+      },
+      "original": {
+        "owner": "pyproject-nix",
+        "repo": "pyproject.nix",
+        "type": "github"
+      }
+    },
+    "root": {
+      "inputs": {
+        "nixpkgs": "nixpkgs",
+        "pwndbg": "pwndbg"
+      }
+    },
+    "uv2nix": {
+      "inputs": {
+        "nixpkgs": [
+          "pwndbg",
+          "nixpkgs"
+        ],
+        "pyproject-nix": [
+          "pwndbg",
+          "pyproject-nix"
+        ]
+      },
+      "locked": {
+        "lastModified": 1738653454,
+        "narHash": "sha256-tAFX8mPZtZ+zVE/+bwPC3U+u5MxjpNP0gG24DG26jVs=",
+        "owner": "pyproject-nix",
+        "repo": "uv2nix",
+        "rev": "05b0c148bc53aebc6a906b6d0ac41dde5954cd47",
+        "type": "github"
+      },
+      "original": {
+        "owner": "pyproject-nix",
+        "repo": "uv2nix",
+        "type": "github"
+      }
+    }
+  },
+  "root": "root",
+  "version": 7
+}
diff --git a/nix/templates/ctf/flake.nix b/nix/templates/ctf/flake.nix
index da21034..f185bb7 100644
--- a/nix/templates/ctf/flake.nix
+++ b/nix/templates/ctf/flake.nix
@@ -1,14 +1,20 @@
 {
-  description = "ctf";
+  description = ''
+    One Flake to rule them all^W^Wcommon CTF problems, namely broken infa.
+
+    Usage:
+    ; nix flake init -t git+https://github.com/hanemile/hefe\#ctf
+  '';
   nixConfig.bash-prompt = "\[ctf\]; ";
 
   inputs = {
-    nixpkgs.url = "git+https://github.com/NixOS/nixpkgs";
+    nixpkgs.url = "git+ssh://git@github.com/nixos/nixpkgs.git?shallow=1&ref=nixos-24.11";
+    pwndbg.url = "git+ssh://git@github.com/pwndbg/pwndbg";
   };
 
   # Flake outputs
   outputs =
-    { nixpkgs, ... }:
+    { nixpkgs, pwndbg, ... }@inputs:
     let
       # Systems supported
       allSystems = [
@@ -21,30 +27,46 @@
       # Helper to provide system-specific attributes
       nameValuePair = name: value: { inherit name value; };
       genAttrs = names: f: builtins.listToAttrs (map (n: nameValuePair n (f n)) names);
-      forAllSystems = f: genAttrs allSystems (system: f { pkgs = import nixpkgs { inherit system; }; });
+      forAllSystems = f: genAttrs allSystems (system: f {
+        pkgs = import nixpkgs { inherit system; };
+        pwndbg = inputs.pwndbg.packages.${system}.default;
+      });
     in
     {
       # Development environment output
       devShells = forAllSystems (
-        { pkgs }:
+        { pkgs, pwndbg }:
         {
           default =
-            let
-              python = pkgs.python311; # Use Python 3.11
-            in
             pkgs.mkShell {
-              packages =
-                with pkgs;
-                [ qemu ]
-                ++ [
-                  # Python plus helper tools
-                  (python.withPackages (
-                    ps: with ps; [
-                      pwntools
-                      pycryptodome
-                    ]
-                  ))
-                ];
+              shellHook = ''
+                cat << EOF > solve.py
+                from pwn import *
+
+                context.gdbinit="${pwndbg}/share/pwndbg/gdbinit.py"
+
+                # exe = ELF("./a.out")
+
+                p = remote("138.199.213.51", 31335)
+                #p = gdb.debug(exe.path, gdbscript=''''
+                #                break main
+                #                c
+                #              '''')
+
+                p.sendlineafter(b"> ", b"asd")
+
+                p.interactive()
+                EOF
+              '';
+              packages = [
+                  pkgs.gcc
+                  pwndbg
+                  (pkgs.python311.withPackages ( ps: with ps; [
+                    pwntools
+                    pwndbg
+                    pycryptodome
+                  ]))
+              ];
             };
         }
       );
diff --git a/nix/templates/ctf/solve.py b/nix/templates/ctf/solve.py
new file mode 100644
index 0000000..acc4a75
--- /dev/null
+++ b/nix/templates/ctf/solve.py
@@ -0,0 +1,15 @@
+from pwn import *
+
+context.gdbinit="/nix/store/jhvjf5drzzqq54xghzz94h0a6wsn1fs1-pwndbg/share/pwndbg/gdbinit.py"
+
+# exe = ELF("./a.out")
+
+p = remote("138.199.213.51", 31335)
+#p = gdb.debug(exe.path, gdbscript='''
+#                break main
+#                c
+#              ''')
+
+p.sendlineafter(b"> ", b"asd")
+
+p.interactive()
diff --git a/secret_create.sh b/secret_create.sh
index e022cd9..1f27834 100755
--- a/secret_create.sh
+++ b/secret_create.sh
@@ -2,7 +2,7 @@
 # $1 = hostname
 # $2 = secretname (with .age suffix)
 
-set -xe
+# set -xe
 
 if [ $# -lt 2 ]; then
     # TODO: print usage