Install postgres and dendrite
diff --git a/modules/grafana.nix b/modules/grafana.nix
index 69c231e..6aee70f 100644
--- a/modules/grafana.nix
+++ b/modules/grafana.nix
@@ -1,15 +1,26 @@
-{
+{ lib, config, ... }: {
   services.grafana = {
     enable = true;
 
     settings = {
       server = rec {
         domain = "logs.clicks.codes";
-        root_url ="https://${domain}";
+        root_url = "https://${domain}";
         http_port = 9052;
         enable_gzip = true;
       };
       analytics.reporting_enabled = false;
     };
+
+    provision.datasources.settings.datasources = [{
+      name = "clicks-postgresql";
+      type = "postgres";
+      access = "proxy";
+
+      url = "postgres://localhost:${toString config.services.postgresql.port}";
+      user = "clicks_grafana";
+      password = "$__file{${config.sops.secrets.clicks_grafana_db_password.path}}";
+      # defined in postgres.nix
+    }];
   };
 }
diff --git a/modules/home-manager-users.nix b/modules/home-manager-users.nix
index e83e0cb..a3f450e 100644
--- a/modules/home-manager-users.nix
+++ b/modules/home-manager-users.nix
@@ -1,6 +1,6 @@
 # Home manager is used separately from this deploy, but we still need to create
 # user accounts in the system config
-{ pkgs, lib, ... }:
+{ base, pkgs, lib, config, ... }:
 let
   mkUser = username: {
     isSystemUser = true;
@@ -9,7 +9,11 @@
     home = "/services/${username}";
     group = "clicks";
     shell = pkgs.bashInteractive;
-  };
+  } // (
+    if builtins.pathExists "${../services}/${username}/system.nix"
+    then import "${../services}/${username}/system.nix"
+    else { }
+  );
 in
 {
   imports = [
@@ -26,4 +30,14 @@
     (map (name: { inherit name; value = mkUser name; }))
     builtins.listToAttrs
   ];
-}
+} // (
+  if (base != null)
+  then {
+/*    users.groups = lib.mapAttrs'
+      (_: user: {
+        name = user.group;
+        value = { };
+      })
+      base.config.users.users;*/
+  } else { }
+)
diff --git a/modules/matrix.nix b/modules/matrix.nix
new file mode 100644
index 0000000..f669943
--- /dev/null
+++ b/modules/matrix.nix
@@ -0,0 +1,103 @@
+{ 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 = {
+    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";
+
+      client_api.registration_shared_secret = "!!registration_shared_secret!!";
+    };
+  };
+
+  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 = {
+      mode = "0400";
+      owner = config.users.users.dendrite.name;
+      group = config.users.users.dendrite.group;
+      sopsFile = ../secrets/matrix_private_key.pem;
+      format = "binary";
+    };
+  };
+} // (
+  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
+      ExecStartPre = "${base.config.systemd.services.dendrite.serviceConfig.ExecStartPre}";
+      dendrite_cfgfile = builtins.head (builtins.match ".*-i ([^[:space:]]+).*" "${ExecStartPre}");
+    in
+    {
+      scalpel.trafos."dendrite.yaml" = {
+        source = dendrite_cfgfile;
+        matchers."dendrite_db_password".secret =
+          config.sops.secrets.dendrite_db_password.path; # Defined in postgres.nix
+        matchers."registration_shared_secret".secret =
+          config.sops.secrets.registration_shared_secret.path;
+        owner = config.users.users.dendrite.name;
+        group = config.users.users.dendrite.group;
+        mode = "0400";
+      };
+
+      systemd.services.dendrite.serviceConfig.ExecStartPre = lib.mkForce (
+        builtins.replaceStrings
+          [ "${dendrite_cfgfile}" ]
+          [ "${config.scalpel.trafos."dendrite.yaml".destination}" ]
+          "${ExecStartPre}"
+      );
+    }
+  else { }
+)
diff --git a/modules/postgres.nix b/modules/postgres.nix
new file mode 100644
index 0000000..63e7697
--- /dev/null
+++ b/modules/postgres.nix
@@ -0,0 +1,84 @@
+{ lib, config, pkgs, ... }: {
+  services.postgresql = {
+    enable = true;
+
+    package = pkgs.postgresql;
+    settings = {
+      log_connections = true;
+      log_statement = "all";
+      logging_collector = true;
+      log_disconnections = true;
+      log_destination = lib.mkForce "syslog";
+    };
+
+    ensureUsers = [
+      {
+        name = "clicks_grafana";
+        ensurePermissions = {
+          "ALL TABLES IN SCHEMA public" = "SELECT";
+          "SCHEMA public" = "USAGE";
+        };
+      }
+      {
+        name = "dendrite";
+        ensurePermissions = {
+          "DATABASE dendrite_account_database" = "ALL PRIVILEGES";
+          "DATABASE dendrite_device_database" = "ALL PRIVILEGES";
+          "DATABASE dendrite_sync_api" = "ALL PRIVILEGES";
+          "DATABASE dendrite_room_server" = "ALL PRIVILEGES";
+          "DATABASE dendrite_mscs" = "ALL PRIVILEGES";
+          "DATABASE dendrite_media_api" = "ALL PRIVILEGES";
+          "DATABASE dendrite_key_server" = "ALL PRIVILEGES";
+          "DATABASE dendrite_federation_api" = "ALL PRIVILEGES";
+          "DATABASE dendrite_app_service_api" = "ALL PRIVILEGES";
+        };
+      }
+    ] ++ (map
+      (name: (
+        {
+          inherit name;
+          ensurePermissions = { "ALL TABLES IN SCHEMA public" = "ALL PRIVILEGES"; };
+        }
+      )) [ "minion" "coded" "pinea" ]);
+
+    ensureDatabases = [
+      "dendrite_account_database"
+      "dendrite_device_database"
+      "dendrite_sync_api"
+      "dendrite_sync_api"
+      "dendrite_room_server"
+      "dendrite_mscs"
+      "dendrite_media_api"
+      "dendrite_key_server"
+      "dendrite_federation_api"
+      "dendrite_app_service_api"
+    ];
+  };
+
+  systemd.services.postgresql.postStart = lib.mkAfter (lib.pipe [
+    { user = "clicks_grafana"; passwordFile = config.sops.secrets.clicks_grafana_db_password.path; }
+    { user = "dendrite"; passwordFile = config.sops.secrets.dendrite_db_password.path; }
+  ] [
+    (map (userData: ''
+      $PSQL -tAc "ALTER USER ${userData.user} PASSWORD '$(cat ${userData.passwordFile})';"
+    ''))
+    (lib.concatStringsSep "\n")
+  ]);
+
+  sops.secrets = lib.pipe [
+    "clicks_grafana_db_password"
+    "dendrite_db_password"
+  ] [
+    (map (name: {
+      inherit name;
+      value = {
+        mode = "0400";
+        owner = config.services.postgresql.superUser;
+        group = config.users.users.${config.services.postgresql.superUser}.group;
+        sopsFile = ../secrets/postgres.json;
+        format = "json";
+      };
+    }))
+    builtins.listToAttrs
+  ];
+}
diff --git a/modules/scalpel.nix b/modules/scalpel.nix
new file mode 100644
index 0000000..569d2b4
--- /dev/null
+++ b/modules/scalpel.nix
@@ -0,0 +1,12 @@
+{ lib, config, ... }: let
+  cfg = config.scalpel;
+in {
+  system.activationScripts.scalpelCreateStore.text = lib.mkForce ''
+    echo "[scalpel] Ensuring existance of ${cfg.secretsDir}"
+    mkdir -p ${cfg.secretsDir}
+    grep -q "${cfg.secretsDir} ramfs" /proc/mounts || mount -t ramfs none "${cfg.secretsDir}" -o nodev,nosuid,mode=0751
+
+    echo "[scalpel] Clearing old secrets from ${cfg.secretsDir}"
+    find '${cfg.secretsDir}' -wholename '${cfg.secretsDir}' -o -prune -exec rm -rf -- {} +
+  '';
+}