Migrate caddy.json to nix and setup matrix well-known
diff --git a/modules/caddy.nix b/modules/caddy.nix
index 834db88..a861694 100644
--- a/modules/caddy.nix
+++ b/modules/caddy.nix
@@ -1,6 +1,10 @@
 { config, pkgs, lib, ... }: {
   services.caddy.enable = true;
-  services.caddy.configFile = ./caddy/caddy.json;
+  services.caddy.configFile = lib.pipe ./caddy/caddyfile.nix [
+    import
+    builtins.toJSON
+    (pkgs.writeText "caddy.json")
+  ];
   services.caddy.package = pkgs.callPackage ../packages/caddy.nix {
     vendorSha256 = "sha256-3KcoOAB+YkOU8qKM75uQo58/dljRBmP25dionr9r2dc=";
   };
diff --git a/modules/caddy/caddy.json b/modules/caddy/caddy.json
deleted file mode 100755
index 686a431..0000000
--- a/modules/caddy/caddy.json
+++ /dev/null
@@ -1,690 +0,0 @@
-{
-  "apps": {
-    "http": {
-      "servers": {
-        "srv0": {
-          "listen": [":443"],
-          "routes": [
-            {
-              "match": [
-                {
-                  "host": ["signup.hopescaramels.com"]
-                }
-              ],
-              "handle": [
-                {
-                  "handler": "subroute",
-                  "routes": [
-                    {
-                      "handle": [
-                        {
-                          "handler": "reverse_proxy",
-                          "upstreams": [
-                            {
-                              "dial": "192.168.0.4:3035"
-                            }
-                          ]
-                        }
-                      ]
-                    }
-                  ]
-                }
-              ],
-              "terminal": true
-            },
-            {
-              "match": [
-                {
-                  "host": ["homebridge.coded.codes"]
-                }
-              ],
-              "handle": [
-                {
-                  "handler": "subroute",
-                  "routes": [
-                    {
-                      "handle": [
-                        {
-                          "handler": "reverse_proxy",
-                          "upstreams": [
-                            {
-                              "dial": "localhost:8581"
-                            }
-                          ]
-                        }
-                      ]
-                    }
-                  ]
-                }
-              ],
-              "terminal": true
-            },
-            {
-              "match": [
-                {
-                  "host": [
-                    "mail.coded.codes",
-                    "mail.clicks.codes",
-                    "mail.hopescaramels.com"
-                  ]
-                }
-              ],
-              "handle": [
-                {
-                  "handler": "subroute",
-                  "routes": [
-                    {
-                      "handle": [
-                        {
-                          "handler": "reverse_proxy",
-                          "transport": {
-                            "protocol": "http"
-                          },
-                          "upstreams": [
-                            {
-                              "dial": "localhost:1080"
-                            }
-                          ]
-                        }
-                      ]
-                    }
-                  ]
-                }
-              ],
-              "terminal": true
-            },
-            {
-              "match": [
-                {
-                  "host": ["logs.clicks.codes"]
-                }
-              ],
-              "handle": [
-                {
-                  "handler": "subroute",
-                  "routes": [
-                    {
-                      "handle": [
-                        {
-                          "handler": "reverse_proxy",
-                          "upstreams": [
-                            {
-                              "dial": "127.0.0.1:9052"
-                            }
-                          ]
-                        }
-                      ]
-                    }
-                  ]
-                }
-              ],
-              "terminal": true
-            },
-            {
-              "match": [
-                {
-                  "host": ["etherpad.clicks.codes"]
-                }
-              ],
-              "handle": [
-                {
-                  "handler": "subroute",
-                  "routes": [
-                    {
-                      "handle": [
-                        {
-                          "handler": "reverse_proxy",
-                          "upstreams": [
-                            {
-                              "dial": "127.0.0.1:9001"
-                            }
-                          ]
-                        }
-                      ]
-                    }
-                  ]
-                }
-              ],
-              "terminal": true
-            },
-            {
-              "match": [
-                {
-                  "host": ["www.hopescaramels.com"]
-                }
-              ],
-              "handle": [
-                {
-                  "handler": "subroute",
-                  "routes": [
-                    {
-                      "handle": [
-                        {
-                          "handler": "static_response",
-                          "headers": {
-                            "Location": ["https://etsy.com/shop/HopesCaramels"]
-                          },
-                          "status_code": 302
-                        }
-                      ]
-                    }
-                  ]
-                }
-              ],
-              "terminal": true
-            },
-            {
-              "match": [
-                {
-                  "host": ["jellyfin.coded.codes"]
-                }
-              ],
-              "handle": [
-                {
-                  "handler": "subroute",
-                  "routes": [
-                    {
-                      "handle": [
-                        {
-                          "handler": "reverse_proxy",
-                          "upstreams": [
-                            {
-                              "dial": "localhost:8096"
-                            }
-                          ]
-                        }
-                      ]
-                    }
-                  ]
-                }
-              ],
-              "terminal": true
-            },
-            {
-              "match": [
-                {
-                  "host": ["codedpc.coded.codes"]
-                }
-              ],
-              "handle": [
-                {
-                  "handler": "subroute",
-                  "routes": [
-                    {
-                      "handle": [
-                        {
-                          "handler": "reverse_proxy",
-                          "upstreams": [
-                            {
-                              "dial": "192.168.0.2:3389"
-                            }
-                          ]
-                        }
-                      ]
-                    }
-                  ]
-                }
-              ],
-              "terminal": true
-            },
-            {
-              "match": [
-                {
-                  "host": ["testing.coded.codes"]
-                }
-              ],
-              "handle": [
-                {
-                  "handler": "subroute",
-                  "routes": [
-                    {
-                      "handle": [
-                        {
-                          "handler": "reverse_proxy",
-                          "upstreams": [
-                            {
-                              "dial": "192.168.0.2:3030"
-                            }
-                          ]
-                        }
-                      ]
-                    }
-                  ]
-                }
-              ],
-              "terminal": true
-            },
-            {
-              "match": [
-                {
-                  "host": ["kavita.coded.codes"]
-                }
-              ],
-              "handle": [
-                {
-                  "handler": "subroute",
-                  "routes": [
-                    {
-                      "handle": [
-                        {
-                          "handler": "reverse_proxy",
-                          "upstreams": [
-                            {
-                              "dial": "localhost:5000"
-                            }
-                          ]
-                        }
-                      ]
-                    }
-                  ]
-                }
-              ],
-              "terminal": true
-            },
-            {
-              "match": [
-                {
-                  "host": ["hopescaramels.com"]
-                }
-              ],
-              "handle": [
-                {
-                  "handler": "subroute",
-                  "routes": [
-                    {
-                      "handle": [
-                        {
-                          "handler": "static_response",
-                          "headers": {
-                            "Location": ["https://etsy.com/shop/HopesCaramels"]
-                          },
-                          "status_code": 302
-                        }
-                      ]
-                    }
-                  ]
-                }
-              ],
-              "terminal": true
-            },
-            {
-              "match": [
-                {
-                  "host": ["api.clicks.codes"]
-                }
-              ],
-              "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": "static_response",
-                          "status_code": 503
-                        }
-                      ]
-                    }
-                  ]
-                }
-              ],
-              "terminal": true
-            },
-            {
-              "match": [
-                {
-                  "host": ["omv.coded.codes"]
-                }
-              ],
-              "handle": [
-                {
-                  "handler": "subroute",
-                  "routes": [
-                    {
-                      "handle": [
-                        {
-                          "handler": "reverse_proxy",
-                          "upstreams": [
-                            {
-                              "dial": "localhost:6773"
-                            }
-                          ]
-                        }
-                      ]
-                    }
-                  ]
-                }
-              ],
-              "terminal": true
-            },
-            {
-              "match": [
-                {
-                  "host": ["api.coded.codes"]
-                }
-              ],
-              "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": "static_response",
-                          "status_code": 404
-                        }
-                      ]
-                    }
-                  ]
-                }
-              ],
-              "terminal": true
-            },
-            {
-              "match": [
-                {
-                  "host": ["www.clicks.codes"]
-                }
-              ],
-              "handle": [
-                {
-                  "handler": "subroute",
-                  "routes": [
-                    {
-                      "handle": [
-                        {
-                          "handler": "static_response",
-                          "headers": {
-                            "Location": [
-                              "https://clicks.codes{http.request.uri}"
-                            ]
-                          },
-                          "status_code": 302
-                        }
-                      ]
-                    }
-                  ]
-                }
-              ],
-              "terminal": true
-            },
-            {
-              "match": [
-                {
-                  "host": ["clicks.codes"]
-                }
-              ],
-              "handle": [
-                {
-                  "handler": "subroute",
-                  "routes": [
-                    {
-                      "handle": [
-                        {
-                          "handler": "reverse_proxy",
-                          "upstreams": [
-                            {
-                              "dial": "127.0.0.1:3000"
-                            }
-                          ]
-                        }
-                      ]
-                    }
-                  ]
-                }
-              ],
-              "terminal": true
-            },
-            {
-              "match": [
-                {
-                  "host": ["coded.codes"]
-                }
-              ],
-              "handle": [
-                {
-                  "handler": "subroute",
-                  "routes": [
-                    {
-                      "handle": [
-                        {
-                          "handler": "static_response",
-                          "headers": {
-                            "Location": [
-                              "https://clicks.codes{http.request.uri}"
-                            ]
-                          },
-                          "status_code": 302
-                        }
-                      ]
-                    }
-                  ]
-                }
-              ],
-              "terminal": true
-            }
-          ]
-        },
-        "srv1": {
-          "listen": [":80"],
-          "routes": [
-            {
-              "match": [
-                {
-                  "host": [
-                    "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"
-                  ]
-                }
-              ],
-              "handle": [
-                {
-                  "handler": "subroute",
-                  "routes": [
-                    {
-                      "handle": [
-                        {
-                          "handler": "reverse_proxy",
-                          "upstreams": [
-                            {
-                              "dial": "localhost:1080"
-                            }
-                          ]
-                        }
-                      ]
-                    }
-                  ]
-                }
-              ],
-              "terminal": true
-            }
-          ]
-        }
-      }
-    },
-    "layer4": {
-      "servers": {
-        "smtp-25": {
-          "listen": ["0.0.0.0:25"],
-          "routes": [
-            {
-              "handle": [
-                {
-                  "handler": "proxy",
-                  "proxy_protocol": "v2",
-                  "upstreams": [{ "dial": ["127.0.0.1:1025"] }]
-                }
-              ]
-            }
-          ]
-        },
-        "smtp-465": {
-          "listen": ["0.0.0.0:465"],
-          "routes": [
-            {
-              "handle": [
-                {
-                  "handler": "proxy",
-                  "proxy_protocol": "v2",
-                  "upstreams": [{ "dial": ["127.0.0.1:1465"] }]
-                }
-              ]
-            }
-          ]
-        },
-        "smtp-587": {
-          "listen": ["0.0.0.0:587"],
-          "routes": [
-            {
-              "handle": [
-                {
-                  "handler": "proxy",
-                  "proxy_protocol": "v2",
-                  "upstreams": [{ "dial": ["127.0.0.1:1587"] }]
-                }
-              ]
-            }
-          ]
-        },
-        "pop-110": {
-          "listen": ["0.0.0.0:110"],
-          "routes": [
-            {
-              "handle": [
-                {
-                  "handler": "proxy",
-                  "proxy_protocol": "v2",
-                  "upstreams": [{ "dial": ["127.0.0.1:1110"] }]
-                }
-              ]
-            }
-          ]
-        },
-        "pop-995": {
-          "listen": ["0.0.0.0:995"],
-          "routes": [
-            {
-              "handle": [
-                {
-                  "handler": "proxy",
-                  "proxy_protocol": "v2",
-                  "upstreams": [{ "dial": ["127.0.0.1:1995"] }]
-                }
-              ]
-            }
-          ]
-        },
-        "imap-143": {
-          "listen": ["0.0.0.0:143"],
-          "routes": [
-            {
-              "handle": [
-                {
-                  "handler": "proxy",
-                  "proxy_protocol": "v2",
-                  "upstreams": [{ "dial": ["127.0.0.1:1143"] }]
-                }
-              ]
-            }
-          ]
-        },
-        "imap-993": {
-          "listen": ["0.0.0.0:993"],
-          "routes": [
-            {
-              "handle": [
-                {
-                  "handler": "proxy",
-                  "proxy_protocol": "v2",
-                  "upstreams": [{ "dial": ["127.0.0.1:1993"] }]
-                }
-              ]
-            }
-          ]
-        }
-      }
-    }
-  }
-}
diff --git a/modules/caddy/caddyfile.nix b/modules/caddy/caddyfile.nix
new file mode 100644
index 0000000..5d890bd
--- /dev/null
+++ b/modules/caddy/caddyfile.nix
@@ -0,0 +1,294 @@
+let
+  HTTPReverseProxyRoute = hosts: upstreams: {
+    handle = [
+      {
+        handler = "subroute";
+        routes = [
+          {
+            handle = [
+              {
+                handler = "reverse_proxy";
+                upstreams = map (upstream: { dial = upstream; }) upstreams;
+              }
+            ];
+          }
+        ];
+      }
+    ];
+    match = [{ host = hosts; }];
+    terminal = true;
+  };
+  HTTPRedirectRoute = hosts: goto: {
+    handle = [
+      {
+        handler = "subroute";
+        routes = [
+          {
+            handle = [
+              {
+                handler = "static_response";
+                headers = { Location = [ goto ]; };
+                status_code = 302;
+              }
+            ];
+          }
+        ];
+      }
+    ];
+    match = [{ host = hosts; }];
+    terminal = true;
+  };
+  TCPReverseProxyRoute = ports: upstreams: {
+    listen = map (port: "0.0.0.0:${toString port}") ports;
+    routes = [
+      {
+        handle = [
+          {
+            handler = "proxy";
+            proxy_protocol = "v2";
+            upstreams = [{ dial = upstreams; }];
+          }
+        ];
+      }
+    ];
+  };
+in
+{
+  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;
+            }
+          ];
+        };
+        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" ]);
+      };
+    };
+  };
+}
diff --git a/modules/caddy/coded.codes/.well-known/matrix b/modules/caddy/coded.codes/.well-known/matrix
new file mode 100644
index 0000000..891445a
--- /dev/null
+++ b/modules/caddy/coded.codes/.well-known/matrix
@@ -0,0 +1,6 @@
+{
+  "m.server": "matrix-backend.coded.codes:443",
+  "m.homeserver": {
+    "base_url": "https://matrix-backend.coded.codes:443"
+  }
+}