Enable mjolnir for synapse
diff --git a/modules/matrix.nix b/modules/matrix.nix
index 81cc257..ec10a03 100644
--- a/modules/matrix.nix
+++ b/modules/matrix.nix
@@ -5,6 +5,10 @@
     enable = true;
     withJemalloc = true;
 
+    plugins = with config.services.matrix-synapse.package.plugins; [
+      matrix-synapse-mjolnir-antispam
+    ];
+
     settings = rec {
       server_name = "coded.codes";
       auto_join_rooms = [ "#general:${server_name}" ];
@@ -30,6 +34,33 @@
     };
   };
 
+  services.mjolnir = {
+    enable = true;
+
+    settings = {
+      autojoinOnlyIfManager = true;
+      automaticallyRedactForReasons = [ "nsfw" "gore" "spam" "harassment" "hate" ];
+      recordIgnoredInvites = true;
+      admin.enableMakeRoomAdminCommand = true;
+      allowNoPrefix = true;
+      protections.wordlist.words = [ ];
+    };
+
+    pantalaimon = {
+      enable = true;
+      username = "system";
+      passwordFile = config.sops.secrets.mjolnir_password.path;
+      options = {
+        ssl = false;
+        listenAddress = "127.0.0.1";
+      };
+    };
+
+    homeserverUrl = "http://localhost:4527";
+
+    managementRoom = "#moderation-commands:coded.codes";
+  };
+
   sops.secrets = {
     registration_shared_secret = {
       mode = "0400";
@@ -46,89 +77,97 @@
       format = "binary";
       path = config.services.matrix-synapse.settings.signing_key_path;
     };
+    mjolnir_password = {
+      mode = "0600";
+      owner = config.users.users.mjolnir.name;
+      group = config.users.users.mjolnir.group;
+      sopsFile = ../secrets/matrix.json;
+      format = "json";
+    };
   };
-} (
-  let
-    isDerived = base != null;
-  in
-  if isDerived
-  # We cannot use mkIf as both sides are evaluated no matter the condition value
-  # Given we use base as an attrset, mkIf will error if base is null in here
-  then
+}
+  (
     let
-      synapse_cfgfile = config.services.matrix-synapse.configFile;
+      isDerived = base != null;
     in
-    {
-      scalpel.trafos."synapse.yaml" = {
-        source = toString synapse_cfgfile;
-        matchers."registration_shared_secret".secret =
-          config.sops.secrets.registration_shared_secret.path;
-        owner = config.users.users.matrix-synapse.name;
-        group = config.users.users.matrix-synapse.group;
-        mode = "0400";
-      };
+    if isDerived
+    # We cannot use mkIf as both sides are evaluated no matter the condition value
+    # Given we use base as an attrset, mkIf will error if base is null in here
+    then
+      let
+        synapse_cfgfile = config.services.matrix-synapse.configFile;
+      in
+      {
+        scalpel.trafos."synapse.yaml" = {
+          source = toString synapse_cfgfile;
+          matchers."registration_shared_secret".secret =
+            config.sops.secrets.registration_shared_secret.path;
+          owner = config.users.users.matrix-synapse.name;
+          group = config.users.users.matrix-synapse.group;
+          mode = "0400";
+        };
 
-      systemd.services.matrix-synapse.serviceConfig.ExecStart = lib.mkForce (
-        builtins.replaceStrings
-          [ "${synapse_cfgfile}" ]
-          [ "${config.scalpel.trafos."synapse.yaml".destination}" ]
-          "${base.config.systemd.services.matrix-synapse.serviceConfig.ExecStart}"
-      );
+        systemd.services.matrix-synapse.serviceConfig.ExecStart = lib.mkForce (
+          builtins.replaceStrings
+            [ "${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}"
-      );
+        systemd.services.matrix-synapse.preStart = lib.mkForce (
+          builtins.replaceStrings
+            [ "${synapse_cfgfile}" ]
+            [ "${config.scalpel.trafos."synapse.yaml".destination}" ]
+            "${base.config.systemd.services.matrix-synapse.preStart}"
+        );
 
-      systemd.services.matrix-synapse.restartTriggers = [ synapse_cfgfile ];
+        systemd.services.matrix-synapse.restartTriggers = [ synapse_cfgfile ];
 
-      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.
+        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 { }
-)
+                # 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 { }
+  )
diff --git a/secrets/matrix.json b/secrets/matrix.json
index 680e715..9937ecc 100644
--- a/secrets/matrix.json
+++ b/secrets/matrix.json
@@ -1,5 +1,6 @@
 {
 	"registration_shared_secret": "ENC[AES256_GCM,data:Kg9BvMxn2/QqbU5C6OnMrlvT8uFmtu3v3UMb+OqqHNSxEtLcXRPMw304+QeTYIg7m9gfZ0m3i3fUnRgya0+Lhg==,iv:2PnEIgd7kicBrR9fdIDJ7j4YklalWxn2BrrS4ipTL2Q=,tag:tgxTZGEAAEZdaqu5kh3a6w==,type:str]",
+	"mjolnir_password": "ENC[AES256_GCM,data:RWBt2hca41QJJ/0E5WHJaWj0PwjrXVVxtoKfNidiCFQgWCo6z9MaNZu1EZRcPPEoxiT3FsYb2mPIpQu8v6qww76nxJPp4K3Ko/RWWMqr0on0MusMbVcFiQHGKHSsTRSu9aWjwWk1UEdCi+eJeFQTz1vW5QhgzyOfTc0haKm2e9s=,iv:RFc0ElVWfkGohLTYMUg9ehCfcZOQNMgO+wGwKJqRgj8=,tag:s9g2y711fXT3He7RNn555w==,type:str]",
 	"sops": {
 		"kms": null,
 		"gcp_kms": null,
@@ -15,8 +16,8 @@
 				"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAvK25uL25GVHVkNmlNVjZq\nVFgvQXVtcFVhMEQ2QlQ3alcyWXRTU2xXNXhNClBzY1hXblJ4b2QxNFlycFdreldQ\nRWhFMjBNN1JjRjg1TUJJRUlqbHRZZHMKLS0tIGZxVXJtMGtlZG5aTStJQUEzYi9H\nRm1JOEgrelZPUXJNMkppVVVROUpFSEUKpDpQ+CclvVsdEf1JD/EmrRVmsbj24VPM\nn4jfNwDDWTn2kflz2p/TiVdEXskqxdR81EjmnhSZw5lnqF206p8AdQ==\n-----END AGE ENCRYPTED FILE-----\n"
 			}
 		],
-		"lastmodified": "2023-05-19T11:14:54Z",
-		"mac": "ENC[AES256_GCM,data:BgaUXQUgtbQasC3lnUoMPOMtxb4/oZ+0PezXL0ixf3wDHZ0WUYoOxH1OhaagTbU1XJMvvMv3O4ktALrKxk3dNUFlPoLWHOey9FndbGqlpdxFr4SbSWPzimXTepT+uSHmHJ38bzn2AHbI0ycrntJqEvtidf4NC/5X5U7HTDhYuYs=,iv:xiLvQrG2EIiX/eHX9BVITar+Hm5gbtqOfnSJuwPnmcc=,tag:zegDNHVNeGdCgavnxvap/w==,type:str]",
+		"lastmodified": "2023-06-08T07:26:46Z",
+		"mac": "ENC[AES256_GCM,data:zwsliYrvBEQdr5ghsblFwAnZkr65MDgIAXrTbG6cel4pBmHC00uo2MQDlOKKYW6FWLrUX3egicg7/XTsKW/8Fr5zh8BfOYGLVO38sEgwhLbs3t9YngBrHcGFLzeyRI6lf+ez1wZZ9FcdRGViId7tZPNGZA/bwSyy8Sqxsjzm5p4=,iv:Ug8LDZRlOoCngbYbKm2XFdq6diMqqJ8BphcpRTjnvgY=,tag:8x90MK8OE4zlLgRtB/uHFA==,type:str]",
 		"pgp": null,
 		"unencrypted_suffix": "_unencrypted",
 		"version": "3.7.3"