Replace dendrite with synapse
diff --git a/modules/matrix.nix b/modules/matrix.nix
index f669943..967e9d9 100644
--- a/modules/matrix.nix
+++ b/modules/matrix.nix
@@ -1,71 +1,48 @@
 { base, config, lib, pkgs, ... }:
-let
-  postgresUrlFor = service:
-    "postgres://dendrite:!!dendrite_db_password!!@localhost:${toString config.services.postgresql.port}/dendrite_${service}?sslmode=disable";
-in
 {
-  services.dendrite = {
+  services.matrix-synapse = {
     enable = true;
-    httpPort = 4527;
-    settings = {
-      global = {
-        server_name = "coded.codes";
-        private_key = config.sops.secrets.matrix_private_key.path;
-      };
-      user_api = {
-        account_database.connection_string = postgresUrlFor "account_database";
-        device_database.connection_string = postgresUrlFor "device_database";
-      };
-      sync_api = {
-        search.enable = true;
-        database.connection_string = postgresUrlFor "sync_api";
-      };
-      room_server.database.connection_string = postgresUrlFor "room_server";
-      mscs.database.connection_string = postgresUrlFor "mscs";
-      media_api.database.connection_string = postgresUrlFor "media_api";
-      key_server.database.connection_string = postgresUrlFor "key_server";
-      federation_api.database.connection_string = postgresUrlFor "federation_api";
-      app_service_api.database.connection_string = postgresUrlFor "app_service_api";
+    withJemalloc = true;
 
-      client_api.registration_shared_secret = "!!registration_shared_secret!!";
+    settings = {
+      server_name = "coded.codes";
+      enable_registration = true;
+      registration_requires_token = true;
+      registration_shared_secret = "!!registration_shared_secret!!";
+      public_baseurl = "https://matrix-backend.coded.codes/";
+      max_upload_size = "100M";
+      listeners = [{
+        x_forwarded = true;
+        tls = false;
+        resources = [{
+          names = [
+            "client"
+            "federation"
+          ];
+          compress = true;
+        }];
+        port = 4527;
+      }];
+      enable_metrics = true;
+      database.args.database = "synapse";
     };
   };
 
-  users.users.dendrite = {
-    isSystemUser = true;
-    createHome = true;
-    home = config.systemd.services.dendrite.serviceConfig.WorkingDirectory;
-    group = "clicks";
-    shell = pkgs.bashInteractive;
-  };
-
-  systemd.services.dendrite.serviceConfig = {
-    DynamicUser = lib.mkForce false;
-    User = lib.mkForce config.users.users.dendrite.name;
-    Group = lib.mkForce config.users.users.dendrite.group;
-  };
-
-  sops.secrets = (lib.pipe [
-    "registration_shared_secret"
-  ] [
-    (map (name: {
-      inherit name;
-      value = {
-        mode = "0400";
-        owner = config.users.users.root.name;
-        group = config.users.users.nobody.group;
-        sopsFile = ../secrets/matrix.json;
-        format = "json";
-      };
-    }))
-    builtins.listToAttrs
-  ]) // {
-    matrix_private_key = {
+  sops.secrets = {
+    registration_shared_secret = {
       mode = "0400";
-      owner = config.users.users.dendrite.name;
-      group = config.users.users.dendrite.group;
+      owner = config.users.users.root.name;
+      group = config.users.users.nobody.group;
+      sopsFile = ../secrets/matrix.json;
+      format = "json";
+    };
+    matrix_private_key = {
+      mode = "0600";
+      owner = config.users.users.matrix-synapse.name;
+      group = config.users.users.matrix-synapse.group;
       sopsFile = ../secrets/matrix_private_key.pem;
       format = "binary";
+      path = config.services.matrix-synapse.settings.signing_key_path;
     };
   };
 } // (
@@ -77,27 +54,77 @@
   # Given we use base as an attrset, mkIf will error if base is null in here
   then
     let
-      ExecStartPre = "${base.config.systemd.services.dendrite.serviceConfig.ExecStartPre}";
-      dendrite_cfgfile = builtins.head (builtins.match ".*-i ([^[:space:]]+).*" "${ExecStartPre}");
+      synapse_cfgfile = config.services.matrix-synapse.configFile;
     in
     {
-      scalpel.trafos."dendrite.yaml" = {
-        source = dendrite_cfgfile;
-        matchers."dendrite_db_password".secret =
-          config.sops.secrets.dendrite_db_password.path; # Defined in postgres.nix
+      scalpel.trafos."synapse.yaml" = {
+        source = toString synapse_cfgfile;
         matchers."registration_shared_secret".secret =
           config.sops.secrets.registration_shared_secret.path;
-        owner = config.users.users.dendrite.name;
-        group = config.users.users.dendrite.group;
+        owner = config.users.users.matrix-synapse.name;
+        group = config.users.users.matrix-synapse.group;
         mode = "0400";
       };
 
-      systemd.services.dendrite.serviceConfig.ExecStartPre = lib.mkForce (
+      systemd.services.matrix-synapse.serviceConfig.ExecStart = lib.mkForce (
         builtins.replaceStrings
-          [ "${dendrite_cfgfile}" ]
-          [ "${config.scalpel.trafos."dendrite.yaml".destination}" ]
-          "${ExecStartPre}"
+          [ "${synapse_cfgfile}" ]
+          [ "${config.scalpel.trafos."synapse.yaml".destination}" ]
+          "${base.config.systemd.services.matrix-synapse.serviceConfig.ExecStart}"
       );
+
+      systemd.services.matrix-synapse.preStart = lib.mkForce (
+        builtins.replaceStrings
+          [ "${synapse_cfgfile}" ]
+          [ "${config.scalpel.trafos."synapse.yaml".destination}" ]
+          "${base.config.systemd.services.matrix-synapse.preStart}"
+      );
+
+      environment.systemPackages =
+        with lib; let
+          cfg = config.services.matrix-synapse;
+          registerNewMatrixUser =
+            let
+              isIpv6 = x: lib.length (lib.splitString ":" x) > 1;
+              listener =
+                lib.findFirst
+                  (
+                    listener: lib.any
+                      (
+                        resource: lib.any
+                          (
+                            name: name == "client"
+                          )
+                          resource.names
+                      )
+                      listener.resources
+                  )
+                  (lib.last cfg.settings.listeners)
+                  cfg.settings.listeners;
+              # FIXME: Handle cases with missing client listener properly,
+              # don't rely on lib.last, this will not work.
+
+              # add a tail, so that without any bind_addresses we still have a useable address
+              bindAddress = head (listener.bind_addresses ++ [ "127.0.0.1" ]);
+              listenerProtocol =
+                if listener.tls
+                then "https"
+                else "http";
+            in
+            pkgs.writeShellScriptBin "matrix-synapse-register_new_matrix_user" ''
+              exec ${cfg.package}/bin/register_new_matrix_user \
+                $@ \
+                ${lib.concatMapStringsSep " " (x: "-c ${x}") ([
+                  config.scalpel.trafos."synapse.yaml".destination ] ++ cfg.extraConfigFiles)} \
+                "${listenerProtocol}://${
+                  if (isIpv6 bindAddress) then
+                    "[${bindAddress}]"
+                  else
+                    "${bindAddress}"
+                }:${builtins.toString listener.port}/"
+            '';
+        in
+        [ (lib.meta.hiPrio registerNewMatrixUser) ];
     }
   else { }
 )