Refactor caddy module, add cloudflare to caddy, remove scalpel antipatterns
diff --git a/modules/caddy.nix b/modules/caddy.nix
index c152989..51efde5 100644
--- a/modules/caddy.nix
+++ b/modules/caddy.nix
@@ -1,4 +1,4 @@
-{ config, pkgs, lib, ... }: {
+{ base, config, pkgs, lib, ... }: lib.recursiveUpdate {
   services.caddy.enable = true;
   services.caddy.configFile = lib.pipe ./caddy/caddyfile.nix [
     import
@@ -9,4 +9,34 @@
   services.caddy.package = pkgs.callPackage ../packages/caddy.nix { };
   services.caddy.user = "root";
   systemd.services.caddy.serviceConfig.ProtectHome = lib.mkForce false;
-}
+
+  sops.secrets.cloudflare_token = {
+    mode = "0600";
+    owner = config.users.users.root.name;
+    group = config.users.users.nobody.group;
+    sopsFile = ../secrets/caddy.json;
+    format = "json";
+  };
+} (
+  let
+    isDerived = base != null;
+  in
+  if isDerived
+  then
+    let
+      caddy_json = base.config.services.caddy.configFile;
+    in
+    {
+      scalpel.trafos."caddy.json" = {
+        source = toString caddy_json;
+        matchers."cloudflare_token".secret =
+          config.sops.secrets.cloudflare_token.path;
+        owner = config.users.users.root.name;
+        group = config.users.users.nobody.group;
+        mode = "0400";
+      };
+
+      services.caddy.configFile = lib.mkForce config.scalpel.trafos."caddy.json".destination;
+    }
+  else { }
+)
diff --git a/modules/caddy/caddyfile.nix b/modules/caddy/caddyfile.nix
index a7deb60..8ebf226 100644
--- a/modules/caddy/caddyfile.nix
+++ b/modules/caddy/caddyfile.nix
@@ -75,250 +75,255 @@
 in
 { pkgs, lib }: {
   apps = {
-    http = {
-      servers = {
-        srv0 = {
-          listen = [ ":443" ];
-          routes = [
-            (HTTPReverseProxyRoute [ "signup.hopescaramels.com" ] [ "192.168.0.4:3035" ])
-            (HTTPReverseProxyRoute [ "homebridge.coded.codes" ] [ "localhost:8581" ])
-            {
-              handle = [
-                {
-                  handler = "subroute";
-                  routes = [
-                    {
-                      handle = [
-                        {
-                          error = "You can't access admin routes from outside the server. Please use SSH tunneling, cURL on the host or similar";
-                          handler = "error";
-                          status_code = "403";
-                        }
-                      ];
-                      match = [{ path = [ "/_dendrite/admin/*" "/_synapse/admin/*" ]; }];
-                      terminal = true;
-                    }
-                    {
-                      handle = [
-                        {
-                          handler = "reverse_proxy";
-                          transport = { protocol = "http"; };
-                          upstreams = [{ dial = "localhost:4527"; }];
-                        }
-                      ];
-                    }
-                  ];
-                }
-              ];
-              match = [{ host = [ "matrix-backend.coded.codes" ]; }];
-              terminal = true;
-            }
-            (HTTPReverseProxyRoute
-              [
-                "mail.coded.codes"
-                "mail.clicks.codes"
-                "mail.hopescaramels.com"
-              ]
-              [ "localhost:1080" ]
-            )
-            (HTTPReverseProxyRoute [ "logs.clicks.codes" ] [ "localhost:9052" ])
-            (HTTPRedirectRoute
-              [
-                "hopescaramels.com"
-                "www.hopescaramels.com"
-              ]
-              "https://etsy.com/shop/HopesCaramels"
-            )
-            # (HTTPReverseProxyRoute [ "omv.coded.codes" ] [ "localhost:6773" ])
-            # (HTTPReverseProxyRoute [ "jellyfin.coded.codes" ] [ "localhost:8096" ])
-            (HTTPReverseProxyRoute [ "codedpc.coded.codes" ] [ "192.168.0.2:3389" ])
-            (HTTPReverseProxyRoute [ "testing.coded.codes" ] [ "192.168.0.2:3030" ])
-            (HTTPReverseProxyRoute [ "kavita.clicks.codes" ] [ "localhost:5000" ])
-            {
-              handle = [
-                {
-                  handler = "subroute";
-                  routes = [
-                    {
-                      handle = [
-                        {
-                          handler = "subroute";
-                          routes = [
-                            {
-                              handle = [
-                                {
-                                  handler = "rewrite";
-                                  strip_path_prefix = "/nucleus";
-                                }
-                              ];
-                            }
-                            {
-                              handle = [
-                                {
-                                  handler = "reverse_proxy";
-                                  upstreams = [{ dial = "127.0.0.1:10000"; }];
-                                }
-                              ];
-                            }
-                          ];
-                        }
-                      ];
-                      match = [{ path = [ "/nucleus/*" ]; }];
-                    }
-                    {
-                      handle = [
-                        {
-                          handler = "error";
-                          error = "This API route does not exist";
-                          status_code = 404;
-                        }
-                      ];
-                    }
-                  ];
-                }
-              ];
-              match = [{ host = [ "api.clicks.codes" ]; }];
-              terminal = true;
-            }
-            {
-              handle = [
-                {
-                  handler = "subroute";
-                  routes = [
-                    {
-                      handle = [
-                        {
-                          handler = "subroute";
-                          routes = [
-                            {
-                              handle = [
-                                {
-                                  handler = "rewrite";
-                                  strip_path_prefix = "/nucleus";
-                                }
-                              ];
-                            }
-                            {
-                              handle = [
-                                {
-                                  handler = "reverse_proxy";
-                                  upstreams = [{ dial = "192.168.0.2:10000"; }];
-                                }
-                              ];
-                            }
-                          ];
-                        }
-                      ];
-                      match = [{ path = [ "/nucleus/*" ]; }];
-                    }
-                    {
-                      handle = [
-                        {
-                          handler = "error";
-                          error = "This API route does not exist";
-                          status_code = 404;
-                        }
-                      ];
-                    }
-                  ];
-                }
-              ];
-              match = [{ host = [ "api.coded.codes" ]; }];
-              terminal = true;
-            }
-            (HTTPRedirectRoute
-              [
-                "www.clicks.codes"
-              ]
-              "https://clicks.codes{http.request.uri}"
-            )
-            (HTTPReverseProxyRoute [ "clicks.codes" ] [ "127.0.0.1:3000" ])
-            {
-              handle = [
-                {
-                  handler = "subroute";
-                  routes = [
-                    {
-                      handle = [
-                        {
-                          handler = "static_response";
-                          status_code = 200;
-                          body = builtins.readFile ./coded.codes/.well-known/matrix;
-                          headers = { Access-Control-Allow-Origin = [ "*" ]; };
-                        }
-                      ];
-                      match = [{
-                        path = [
-                          "/.well-known/matrix/server"
-                          "/.well-known/matrix/client"
-                        ];
-                      }];
-                      terminal = true;
-                    }
-                    {
-                      handle = [
-                        {
-                          handler = "static_response";
-                          headers = { Location = [ "https://clicks.codes{http.request.uri}" ]; };
-                          status_code = 302;
-                        }
-                      ];
-                    }
-                  ];
-                }
-              ];
-              match = [{ host = [ "coded.codes" ]; }];
-              terminal = true;
-            }
-            (HTTPFileServerRoute [ "matrix.coded.codes" ] (
-              pkgs.element-web.override {
-                conf = {
-                  default_server_config = lib.pipe ./coded.codes/.well-known/matrix [
-                    builtins.readFile
-                    builtins.fromJSON
-                  ];
-                };
+    http.servers = {
+      srv0 = {
+        listen = [ ":443" ];
+        routes = [
+          (HTTPReverseProxyRoute [ "signup.hopescaramels.com" ] [ "192.168.0.4:3035" ])
+          (HTTPReverseProxyRoute [ "homebridge.coded.codes" ] [ "localhost:8581" ])
+          {
+            handle = [
+              {
+                handler = "subroute";
+                routes = [
+                  {
+                    handle = [
+                      {
+                        error = "You can't access admin routes from outside the server. Please use SSH tunneling, cURL on the host or similar";
+                        handler = "error";
+                        status_code = "403";
+                      }
+                    ];
+                    match = [{ path = [ "/_dendrite/admin/*" "/_synapse/admin/*" ]; }];
+                    terminal = true;
+                  }
+                  {
+                    handle = [
+                      {
+                        handler = "reverse_proxy";
+                        transport = { protocol = "http"; };
+                        upstreams = [{ dial = "localhost:4527"; }];
+                      }
+                    ];
+                  }
+                ];
               }
-            ))
-          ];
-        };
-        srv1 = {
-          listen = [ ":80" ];
-          routes = [
-            (HTTPReverseProxyRoute
-              [
-                "mail.clicks.codes"
-                "mail.coded.codes"
-                "mail.hopescaramels.com"
-                "autoconfig.coded.codes"
-                "autoconfig.clicks.codes"
-                "autoconfig.hopescaramels.com"
-                "imap.coded.codes"
-                "imap.clicks.codes"
-                "imap.hopescaramels.com"
-                "pop.coded.codes"
-                "pop.clicks.codes"
-                "pop.hopescaramels.com"
-                "smtp.coded.codes"
-                "smtp.clicks.codes"
-                "smtp.hopescaramels.com"
-              ]
-              [ "localhost:1080" ]
-            )
-          ];
-        };
+            ];
+            match = [{ host = [ "matrix-backend.coded.codes" ]; }];
+            terminal = true;
+          }
+          (HTTPReverseProxyRoute
+            [
+              "mail.coded.codes"
+              "mail.clicks.codes"
+              "mail.hopescaramels.com"
+            ]
+            [ "localhost:1080" ]
+          )
+          (HTTPReverseProxyRoute [ "logs.clicks.codes" ] [ "localhost:9052" ])
+          (HTTPRedirectRoute
+            [
+              "hopescaramels.com"
+              "www.hopescaramels.com"
+            ]
+            "https://etsy.com/shop/HopesCaramels"
+          )
+          # (HTTPReverseProxyRoute [ "omv.coded.codes" ] [ "localhost:6773" ])
+          # (HTTPReverseProxyRoute [ "jellyfin.coded.codes" ] [ "localhost:8096" ])
+          (HTTPReverseProxyRoute [ "codedpc.coded.codes" ] [ "192.168.0.2:3389" ])
+          (HTTPReverseProxyRoute [ "testing.coded.codes" ] [ "192.168.0.2:3030" ])
+          (HTTPReverseProxyRoute [ "kavita.coded.codes" ] [ "localhost:5000" ])
+          {
+            handle = [
+              {
+                handler = "subroute";
+                routes = [
+                  {
+                    handle = [
+                      {
+                        handler = "subroute";
+                        routes = [
+                          {
+                            handle = [
+                              {
+                                handler = "rewrite";
+                                strip_path_prefix = "/nucleus";
+                              }
+                            ];
+                          }
+                          {
+                            handle = [
+                              {
+                                handler = "reverse_proxy";
+                                upstreams = [{ dial = "127.0.0.1:10000"; }];
+                              }
+                            ];
+                          }
+                        ];
+                      }
+                    ];
+                    match = [{ path = [ "/nucleus/*" ]; }];
+                  }
+                  {
+                    handle = [
+                      {
+                        handler = "error";
+                        error = "This API route does not exist";
+                        status_code = 404;
+                      }
+                    ];
+                  }
+                ];
+              }
+            ];
+            match = [{ host = [ "api.clicks.codes" ]; }];
+            terminal = true;
+          }
+          {
+            handle = [
+              {
+                handler = "subroute";
+                routes = [
+                  {
+                    handle = [
+                      {
+                        handler = "subroute";
+                        routes = [
+                          {
+                            handle = [
+                              {
+                                handler = "rewrite";
+                                strip_path_prefix = "/nucleus";
+                              }
+                            ];
+                          }
+                          {
+                            handle = [
+                              {
+                                handler = "reverse_proxy";
+                                upstreams = [{ dial = "192.168.0.2:10000"; }];
+                              }
+                            ];
+                          }
+                        ];
+                      }
+                    ];
+                    match = [{ path = [ "/nucleus/*" ]; }];
+                  }
+                  {
+                    handle = [
+                      {
+                        handler = "error";
+                        error = "This API route does not exist";
+                        status_code = 404;
+                      }
+                    ];
+                  }
+                ];
+              }
+            ];
+            match = [{ host = [ "api.coded.codes" ]; }];
+            terminal = true;
+          }
+          (HTTPRedirectRoute
+            [
+              "www.clicks.codes"
+            ]
+            "https://clicks.codes{http.request.uri}"
+          )
+          (HTTPReverseProxyRoute [ "clicks.codes" ] [ "127.0.0.1:3000" ])
+          {
+            handle = [
+              {
+                handler = "subroute";
+                routes = [
+                  {
+                    handle = [
+                      {
+                        handler = "static_response";
+                        status_code = 200;
+                        body = builtins.readFile ./coded.codes/.well-known/matrix;
+                        headers = { Access-Control-Allow-Origin = [ "*" ]; };
+                      }
+                    ];
+                    match = [{
+                      path = [
+                        "/.well-known/matrix/server"
+                        "/.well-known/matrix/client"
+                      ];
+                    }];
+                    terminal = true;
+                  }
+                  {
+                    handle = [
+                      {
+                        handler = "static_response";
+                        headers = { Location = [ "https://clicks.codes{http.request.uri}" ]; };
+                        status_code = 302;
+                      }
+                    ];
+                  }
+                ];
+              }
+            ];
+            match = [{ host = [ "coded.codes" ]; }];
+            terminal = true;
+          }
+          (HTTPFileServerRoute [ "matrix.coded.codes" ] (
+            pkgs.element-web.override {
+              conf = {
+                default_server_config = lib.pipe ./coded.codes/.well-known/matrix [
+                  builtins.readFile
+                  builtins.fromJSON
+                ];
+              };
+            }
+          ))
+        ];
+      };
+      srv1 = {
+        listen = [ ":80" ];
+        routes = [
+          (HTTPReverseProxyRoute
+            [
+              "mail.clicks.codes"
+              "mail.coded.codes"
+              "mail.hopescaramels.com"
+              "autoconfig.coded.codes"
+              "autoconfig.clicks.codes"
+              "autoconfig.hopescaramels.com"
+              "imap.coded.codes"
+              "imap.clicks.codes"
+              "imap.hopescaramels.com"
+              "pop.coded.codes"
+              "pop.clicks.codes"
+              "pop.hopescaramels.com"
+              "smtp.coded.codes"
+              "smtp.clicks.codes"
+              "smtp.hopescaramels.com"
+            ]
+            [ "localhost:1080" ]
+          )
+        ];
       };
     };
-    layer4 = {
-      servers = {
-        imap-143 = (TCPReverseProxyRoute [ 143 ] [ "localhost:1143" ]);
-        imap-993 = (TCPReverseProxyRoute [ 993 ] [ "localhost:1993" ]);
-        pop-110 = (TCPReverseProxyRoute [ 110 ] [ "localhost:1110" ]);
-        pop-995 = (TCPReverseProxyRoute [ 995 ] [ "localhost:1995" ]);
-        smtp-25 = (TCPReverseProxyRoute [ 25 ] [ "localhost:1025" ]);
-        smtp-465 = (TCPReverseProxyRoute [ 465 ] [ "localhost:1465" ]);
-        smtp-587 = (TCPReverseProxyRoute [ 587 ] [ "localhost:1587" ]);
-      };
+    layer4.servers = {
+      imap-143 = (TCPReverseProxyRoute [ 143 ] [ "localhost:1143" ]);
+      imap-993 = (TCPReverseProxyRoute [ 993 ] [ "localhost:1993" ]);
+      pop-110 = (TCPReverseProxyRoute [ 110 ] [ "localhost:1110" ]);
+      pop-995 = (TCPReverseProxyRoute [ 995 ] [ "localhost:1995" ]);
+      smtp-25 = (TCPReverseProxyRoute [ 25 ] [ "localhost:1025" ]);
+      smtp-465 = (TCPReverseProxyRoute [ 465 ] [ "localhost:1465" ]);
+      smtp-587 = (TCPReverseProxyRoute [ 587 ] [ "localhost:1587" ]);
     };
+    tls.automation.policies = [{
+      issuers = [{
+        module = "acme";
+        challenges.dns.provider = {
+          name = "cloudflare";
+          api_token = "!!cloudflare_token!!";
+        };
+      }];
+    }];
   };
 }
diff --git a/modules/matrix.nix b/modules/matrix.nix
index b83e3f4..4be396e 100644
--- a/modules/matrix.nix
+++ b/modules/matrix.nix
@@ -1,4 +1,5 @@
 { base, config, lib, pkgs, ... }:
+lib.recursiveUpdate
 {
   services.matrix-synapse = {
     enable = true;
@@ -46,7 +47,7 @@
       path = config.services.matrix-synapse.settings.signing_key_path;
     };
   };
-} // (
+} (
   let
     isDerived = base != null;
   in
diff --git a/packages/caddy.nix b/packages/caddy.nix
index 6856bf2..24f2f85 100644
--- a/packages/caddy.nix
+++ b/packages/caddy.nix
@@ -5,17 +5,26 @@
 with pkgs;
 
 let
-  pluginSrc = fetchFromGitHub {
+  caddySrc = fetchFromGitHub {
+    # github.com/caddyserver/caddy/v2
+    owner = "caddyserver";
+    repo = "caddy";
+    rev = "v2.6.4";
+    hash = "sha256-3a3+nFHmGONvL/TyQRqgJtrSDIn0zdGy9YwhZP17mU0=";
+  };
+  l4Src = fetchFromGitHub {
+    # github.com/mholt/caddy-l4
     owner = "mholt";
     repo = "caddy-l4";
     rev = "aa8cf68a3b5197c45a8b4ffd99b74465f0b5a6b1";
     hash = "sha256-3KcoOAB+YkOU8qKM75uQo58/dljRBmP25dionQ9K2dc=";
   };
-  caddySrc = fetchFromGitHub {
-    owner = "caddyserver";
-    repo = "caddy";
-    rev = "v2.6.4";
-    hash = "sha256-3a3+nFHmGONvL/TyQRqgJtrSDIn0zdGy9YwhZP17mU0=";
+  cloudflareSrc = fetchFromGitHub {
+    # github.com/caddy-dns/cloudflare
+    owner = "caddy-dns";
+    repo = "cloudflare";
+    rev = "a9d3ae2690a1d232bc9f8fc8b15bd4e0a6960eec";
+    hash = "sha256-bqnk4XkhUI7YhCv24ha8mds5EaYphnYj8wy/mFOieqI=";
   };
 
   combinedSrc = stdenv.mkDerivation {
@@ -30,29 +39,37 @@
       mkdir -p "$out/ourcaddy"
 
       cp -r ${caddySrc} "$out/caddy"
-      cp -r ${pluginSrc} "$out/plugin"
+      cp -r ${l4Src} "$out/l4"
+      cp -r ${cloudflareSrc} "$out/cloudflare"
 
       cd "$out/ourcaddy"
 
       go mod init caddy
       echo "package main" >> main.go
+
       echo 'import caddycmd "github.com/caddyserver/caddy/v2/cmd"' >> main.go
+
       echo 'import _ "github.com/caddyserver/caddy/v2/modules/standard"' >> main.go
       echo 'import _ "github.com/mholt/caddy-l4"' >> main.go
+      echo 'import _ "github.com/caddy-dns/cloudflare"' >> main.go
+
       echo "func main(){ caddycmd.Main() }" >> main.go
+
       go mod edit -require=github.com/caddyserver/caddy/v2@v2.6.4
       go mod edit -replace github.com/caddyserver/caddy/v2=../caddy
       go mod edit -require=github.com/mholt/caddy-l4@v0.0.0
-      go mod edit -replace github.com/mholt/caddy-l4=../plugin
+      go mod edit -replace github.com/mholt/caddy-l4=../l4
+      go mod edit -require=github.com/caddy-dns/cloudflare@v0.0.0
+      go mod edit -replace github.com/caddy-dns/cloudflare=../cloudflare
     '';
   };
 in
 buildGoModule {
-  name = "meowdy";
+  name = "caddy-with-plugins";
 
   src = combinedSrc;
 
-  vendorHash = "sha256-a49J7gKBYi9mQLlg+YFGaOetvbup5yRrWzX7kicvy+o=";
+  vendorHash = "sha256-34o91x7Y7DjIHom2Tk2ARBcJ3PzBVm+ALWK9ucj1g5A=";
 
   overrideModAttrs = _: {
     postPatch = "cd ourcaddy";
diff --git a/secrets/caddy.json b/secrets/caddy.json
new file mode 100644
index 0000000..6e917f6
--- /dev/null
+++ b/secrets/caddy.json
@@ -0,0 +1,24 @@
+{
+	"cloudflare_token": "ENC[AES256_GCM,data:ZrVPHcTEED9TnK23wvtZQDEwcWC5vyh5HJkhBlC4VjLsJkVUURsNmw==,iv:oFincLWd0ESOuNQoFRoioFPupdCl76sVpZLHZL3kV38=,tag:11wCHPq3BktPPH/g77Lg/Q==,type:str]",
+	"sops": {
+		"kms": null,
+		"gcp_kms": null,
+		"azure_kv": null,
+		"hc_vault": null,
+		"age": [
+			{
+				"recipient": "age15mv77dpnh5762gk5rsw2u79uza4tg8cu6r3nlwjudlzmdqqck3ss6mg9dy",
+				"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBYWlBkT0NlcGtabzZPL0FG\ncjNHMEtYWCtkMFNIaDNNdjBuV3ZQQWZNN2w0CkxLa1U5SW1JbE54NnIvL2NXZktY\nNWZNSHFTKzBPYU8rbzY3c002dEZFTGMKLS0tIDBaN2I5OEMrOWgxODdhMXhwRURJ\nYm9CS0Jxb3U0THdnRzlrMWVwYXdwWFkKGqM2dWaZYXL7kFW9YCVEeKF2OwlIyI2l\nKu/X3VslSlwx+B6vAwTFTqnJc0XbiVbelP4/HRC2IQOFjp+5jEJJLQ==\n-----END AGE ENCRYPTED FILE-----\n"
+			},
+			{
+				"recipient": "age1fxxnmkeuqhhct93c43pwkzhuzzq8857s5hye6pgfpku70kjn4ecqtamfqr",
+				"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBsL0l4Z0NtNWwreVF0VXJr\nYlF4dEhIQ3NLWXowcGRYa1BPb2lYejdYMFdFCm9JdXVHT1JZZnpZS0hoTlpTRmh6\ncW5nMnZDdlg5SmtHQURxbWNBdnRJQjQKLS0tIEV5V2l5RW5KVUFGbW5FOFlGcHFo\nWkhrUDhGNmtDOE8zMzlhU3lKUWNKRkEKcXASv8OxPxeX9ZulhWkXl2qCVSSRFdQ5\nWqx+rHejzQhO81cT0Wf+uiYQDvX+otJQW5Akgv8zcDMQxSyGgG+B+A==\n-----END AGE ENCRYPTED FILE-----\n"
+			}
+		],
+		"lastmodified": "2023-05-24T15:18:01Z",
+		"mac": "ENC[AES256_GCM,data:ZETpBaOW1TCYmDYflRBz4Gz8bHvXCuZS+kN54MZ59gt3xh31n+2MBYWavh6H1EPppL6WVt1gynAF3GWPuBqfo+OeOWGaL6c3VCnnnWgbTWDb2K3Qn7JYpsAMUnSXT9DF+oAtrtvuFX+K3ubR4UL+3PYIVFmwr5esCQKI0ngz34U=,iv:CNIh86JUlMvzCfJfsqRWirNtCuUKvlcwOj/8xPR/J8M=,tag:yFRV0TB8ay86U1Z1HbpCnQ==,type:str]",
+		"pgp": null,
+		"unencrypted_suffix": "_unencrypted",
+		"version": "3.7.3"
+	}
+}
\ No newline at end of file