{ inputs, flake, self, }: { pkgs, lib, config, ... }: let cfg = config.hectic.generic.matrix-cluster; s3Cfg = cfg.objectStorage.s3; s3Plugin = pkgs.matrix-synapse-plugins.matrix-synapse-s3-storage-provider; s3ConfigDir = "/run/matrix-synapse"; s3ConfigFile = "${s3ConfigDir}/s3-media-storage.yaml"; pgDataDir = "/var/lib/postgresql/17"; matrixUsers = builtins.attrNames cfg.users; mkUserRegistration = name: let user = cfg.users.${name}; adminFlag = if user.admin then "--admin" else "--no-admin"; in '' if [ ! -r "${user.passwordFile}" ]; then printf 'Missing Matrix password file for %s: %s\n' '${name}' '${user.passwordFile}' >&2 exit 1 fi ${pkgs.matrix-synapse}/bin/register_new_matrix_user \ -u '${name}' \ -p "$(tr -d '\n' < "${user.passwordFile}")" \ -k "$REGISTRATION_SHARED_SECRET" \ ${adminFlag} \ http://127.0.0.1:8008 || true ''; synapseEnabled = if cfg.overrideEnableSynapse != null then cfg.overrideEnableSynapse else cfg.role == "primary"; mkS3Config = '' if [ ! -r "${s3Cfg.credentialsFile}" ]; then printf 'Missing Matrix object storage credentials file: %s\n' '${s3Cfg.credentialsFile}' >&2 exit 1 fi . "${s3Cfg.credentialsFile}" if [ -z "$ACCESS_KEY_ID" ] || [ -z "$SECRET_ACCESS_KEY" ]; then printf 'ACCESS_KEY_ID or SECRET_ACCESS_KEY missing in %s\n' '${s3Cfg.credentialsFile}' >&2 exit 1 fi mkdir -p "${s3ConfigDir}" cat > "${s3ConfigFile}" </dev/null; do sleep 2 done REGISTRATION_SHARED_SECRET="$(awk -F': *' '$1 == "registration_shared_secret" { print $2; exit }' "${cfg.secretsFile}")" if [ -z "$REGISTRATION_SHARED_SECRET" ]; then printf 'registration_shared_secret not found in %s\n' '${cfg.secretsFile}' >&2 exit 1 fi ${lib.concatStringsSep "\n" (map mkUserRegistration matrixUsers)} ''; }; }) { services.postgresql = { enable = true; package = pkgs.postgresql_17; enableTCPIP = true; initdbArgs = [ "--locale=C" "--encoding=UTF8" ]; settings = { wal_level = "replica"; max_wal_senders = 4; hot_standby = "on"; }; }; } (lib.mkIf (cfg.role == "primary") { services.postgresql = { authentication = lib.concatStringsSep "\n" ([ "local all all trust" "host sameuser all 127.0.0.1/32 scram-sha-256" "host sameuser all ::1/128 scram-sha-256" "host all all ::1/128 scram-sha-256" "host all all 0.0.0.0/0 scram-sha-256" "host replication postgres 127.0.0.1/32 scram-sha-256" "host replication postgres ::1/128 scram-sha-256" ] ++ map (cidr: "hostssl replication replication ${cidr} scram-sha-256" ) cfg.replication.allowedSourceIPs); ensureUsers = [ { name = "replication"; ensureClauses = { login = true; replication = true; }; } ]; }; # Apply replication password from SOPS-mounted file after postgres start. systemd.services.matrix-cluster-replication-password = { description = "Set Postgres replication role password from SOPS"; wantedBy = [ "multi-user.target" ]; after = [ "postgresql.service" ]; requires = [ "postgresql.service" ]; serviceConfig = { Type = "oneshot"; User = "postgres"; RemainAfterExit = true; }; script = '' set -eu PW="$(tr -d '\n' < "${cfg.replication.passwordFile}")" ${config.services.postgresql.package}/bin/psql -v ON_ERROR_STOP=1 -c \ "ALTER ROLE replication WITH LOGIN REPLICATION PASSWORD '$PW';" ''; }; }) (lib.mkIf (cfg.role == "standby") { systemd.targets.postgresql.requires = lib.mkForce [ "postgresql.service" ]; # Hot-standby bootstrap: standby.signal + primary_conninfo with passfile. # pg_basebackup must be run manually (see runbook) before this activates # for the first time. systemd.services.matrix-cluster-standby-bootstrap = { description = "Configure Matrix Postgres hot standby"; wantedBy = [ "postgresql.service" ]; before = [ "postgresql.service" ]; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; }; script = '' set -eu if [ ! -d "${pgDataDir}" ]; then echo "Postgres data dir ${pgDataDir} missing; run pg_basebackup first (see MATRIX-FAILOVER-RUNBOOK.md)" >&2 exit 0 fi # Materialize a libpq passfile from the raw password secret. PASSFILE=/var/lib/postgresql/.matrix-cluster-replication.passfile PW="$(tr -d '\n' < "${cfg.replication.passwordFile}")" umask 077 printf '%s:%d:replication:replication:%s\n' \ '${cfg.replication.peerHost}' \ ${toString cfg.replication.peerPort} \ "$PW" > "$PASSFILE" chown postgres:postgres "$PASSFILE" chmod 0600 "$PASSFILE" touch "${pgDataDir}/standby.signal" chown postgres:postgres "${pgDataDir}/standby.signal" CONF="${pgDataDir}/postgresql.auto.conf" touch "$CONF" chown postgres:postgres "$CONF" # Strip any prior primary_conninfo line, then append fresh one. ${pkgs.gnused}/bin/sed -i '/^primary_conninfo/d' "$CONF" printf "primary_conninfo = 'host=%s port=%d user=replication passfile=%s sslmode=%s'\n" \ '${cfg.replication.peerHost}' \ ${toString cfg.replication.peerPort} \ "$PASSFILE" \ '${cfg.replication.sslMode}' >> "$CONF" ''; }; }) (lib.mkIf cfg.acme.enable { security.acme = { acceptTerms = true; defaults.email = lib.mkDefault cfg.acme.email; certs.${cfg.matrixDomain} = { dnsProvider = "porkbun"; webroot = lib.mkForce null; environmentFile = "/run/matrix-cluster/porkbun.env"; }; }; systemd.services.matrix-cluster-acme-env = { description = "Assemble Porkbun ACME environment file"; wantedBy = [ "multi-user.target" ]; before = [ "acme-${cfg.matrixDomain}.service" ]; requiredBy = [ "acme-${cfg.matrixDomain}.service" ]; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; }; script = '' set -eu install -d -m 0755 /run/matrix-cluster API="$(tr -d '\n' < "${cfg.acme.porkbunApiKeyFile}")" SEC="$(tr -d '\n' < "${cfg.acme.porkbunSecretApiKeyFile}")" OUT=/run/matrix-cluster/porkbun.env umask 077 { printf 'PORKBUN_API_KEY=%s\n' "$API" printf 'PORKBUN_SECRET_API_KEY=%s\n' "$SEC" } > "$OUT" chmod 0400 "$OUT" ''; }; }) ]); }