0
2
mirror of https://github.com/dali99/nixos-matrix-modules.git synced 2026-04-27 08:47:03 +02:00

Compare commits

...

19 Commits

Author SHA1 Message Date
oysteikt f3fcbc2b40 WIP: create tests 2023-10-29 05:38:37 +01:00
oysteikt f5bb4ac8c2 WIP: make workers use path 2023-10-29 05:38:37 +01:00
oysteikt 1f1475aec6 add gitignore 2023-10-29 05:38:35 +01:00
danio e098146571 Update README.MD 2023-10-22 03:02:59 +02:00
danio 1e370b9622 matrix-sliding-sync: 0.99.10 -> 0.99.11 2023-10-16 03:49:32 +02:00
danio 161d1ed360 document some breaking changes 2023-09-24 04:39:20 +02:00
danio 50ae1b6e57 Implement easy sliding sync setup
Co-authored-by: h7x4 <h7x4@nani.wtf>
2023-09-24 04:39:20 +02:00
danio bedede1e6d Import sliding sync from nixpkgs unstable
Co-authored-by: Sandro Jäckel <sandro.jaeckel@gmail.com>
Co-authored-by: Emily <55066419+emilylange@users.noreply.github.com>
2023-09-24 03:32:54 +02:00
danio 66ff528912 Update README.MD 2023-09-11 00:04:19 +02:00
danio 8199f88a5a Update README.MD 2023-09-11 00:03:44 +02:00
lon bf997073d9 fix: don't force enableACME to allow useACMEHost 2023-07-27 22:10:46 +02:00
danio c158a35ea2 emergency handling of deprecations 2023-07-13 04:16:00 +02:00
danio 362496f4aa move matrix-lib to let block
Else it has to be called with { } which modules cant do
2023-02-17 23:59:59 +01:00
danio cf89fa8eb9 load matrix-lib directly inside module
To enable use with non-flakes
2023-02-17 23:44:47 +01:00
danio 59e39d551d Add a license
Co-authored-by: h7x4 <h7x4@nani.wtf>
2023-02-17 01:16:02 +01:00
danio 07e95170e8 introduce matrix-lib 2023-01-20 08:11:33 +01:00
danio 5ef8873997 simplify mainReplicationListener stuff 2023-01-20 08:11:33 +01:00
danio fbee6a0c0d Merge pull request #2 from h7x4/master 2023-01-19 21:48:06 +01:00
oysteikt 2fd07f83b5 Cleaned up matrix synapse module 2023-01-19 20:53:14 +01:00
16 changed files with 1183 additions and 797 deletions
+1
View File
@@ -0,0 +1 @@
result
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020, 2022-2023 Daniel Løvbrøtte Olsen and contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+10
View File
@@ -0,0 +1,10 @@
# Migrations
This is a best effort document descibing neccecary changes you might have to do when updating
## 0.5.0 UNRELEASED
The module has been renamed from `synapse` to `default`
+21 -1
View File
@@ -9,6 +9,10 @@ With matrix.YOURDOMAIN pointing at the server:
workers.federationSenders = 1; workers.federationSenders = 1;
workers.federationReceivers = 1; workers.federationReceivers = 1;
workers.initialSyncers = 1;
workers.normalSyncers = 1;
workers.eventPersisters = 2;
workers.useUserDirectoryWorker = true;
enableNginx = true; enableNginx = true;
@@ -31,4 +35,20 @@ With matrix.YOURDOMAIN pointing at the server:
} }
``` ```
is ~enough to get a functional matrix-server running one federation sender and one federation receiver is ~enough to get a functional matrix-server running with some workers
## Sliding Sync (Element X)
Just add the following to your config and point `slidingsync.YOURDOMAIN` at the server
```
services.matrix-synapse-next = {
enableSlidingSync = true;
};
services.matrix-synapse.sliding-sync.environmentFile = "/some/file/containing/SYNCV3_SECRET=<some secret>";
```
If using [well-known delagation](https://matrix-org.github.io/synapse/v1.37/delegate.html) make sure `YOURDOMAIN/.well-known/matrix/client` matches
what's in `matrix.YOURDOMAIN/.well-known/matrix/client`
Generated
+42
View File
@@ -0,0 +1,42 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1690789960,
"narHash": "sha256-3K+2HuyGTiJUSZNJxXXvc0qj4xFx1FHC/ItYtEa7/Xs=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "fb942492b7accdee4e6d17f5447091c65897dde4",
"type": "github"
},
"original": {
"id": "nixpkgs",
"ref": "nixos-unstable",
"type": "indirect"
}
},
"nixpkgs-lib": {
"locked": {
"lastModified": 1673743903,
"narHash": "sha256-sloY6KYyVOozJ1CkbgJPpZ99TKIjIvM+04V48C04sMQ=",
"owner": "nix-community",
"repo": "nixpkgs.lib",
"rev": "7555e2dfcbac1533f047021f1744ac8871150f9f",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixpkgs.lib",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs",
"nixpkgs-lib": "nixpkgs-lib"
}
}
},
"root": "root",
"version": 7
}
+21 -2
View File
@@ -1,9 +1,28 @@
{ {
description = "NixOS modules for matrix related services"; description = "NixOS modules for matrix related services";
outputs = { self }: { inputs = {
nixpkgs-lib.url = "github:nix-community/nixpkgs.lib";
nixpkgs.url = "nixpkgs/nixos-unstable";
};
outputs = { self, nixpkgs, nixpkgs-lib }: {
nixosModules = { nixosModules = {
synapse = import ./synapse-module; default = import ./module.nix;
};
lib = import ./lib.nix { lib = nixpkgs-lib.lib; };
packages = let
system = "x86_64-linux";
pkgs = nixpkgs.legacyPackages.${system};
in {
${system}.tests = import ./tests {
inherit system;
inherit nixpkgs;
inherit pkgs;
nixosModule = self.outputs.nixosModules.synapse;
};
}; };
}; };
} }
+20
View File
@@ -0,0 +1,20 @@
{ lib }:
rec {
# checks if given listener configuration has type as a resource
isListenerType = type: l: lib.any (r: lib.any (n: n == type) r.names) l.resources;
# Get the first listener that includes the given resource from worker
firstListenerOfType = type: ls: lib.lists.findFirst (isListenerType type)
(lib.throw "No listener with resource: ${type} configured")
ls;
# Get an attrset of the host and port from a listener
connectionInfo = l: {
host = lib.head l.bind_addresses;
port = l.port;
};
# Get an attrset of the host and port from a worker given a type
workerConnectionResource = r: w: let
l = firstListenerOfType r w.settings.worker_listeners;
in connectionInfo l;
}
+8
View File
@@ -0,0 +1,8 @@
{ ... }:
{
imports = [
./synapse-module
./sliding-sync
];
}
+37
View File
@@ -0,0 +1,37 @@
{ lib
, buildGoModule
, fetchFromGitHub
}:
buildGoModule rec {
pname = "matrix-sliding-sync";
version = "0.99.11";
src = fetchFromGitHub {
owner = "matrix-org";
repo = "sliding-sync";
rev = "refs/tags/v${version}";
hash = "sha256-Wd/nnJhKg+BDyOIz42zEScjzQRrpEq6YG9/9Tk24hgg=";
};
vendorHash = "sha256-0QSyYhOht1j1tWNxHQh+NUZA/W1xy7ANu+29H/gusOE=";
subPackages = [ "cmd/syncv3" ];
ldflags = [
"-s"
"-w"
"-X main.GitCommit=${src.rev}"
];
# requires a running matrix-synapse
doCheck = false;
meta = with lib; {
description = "A sliding sync implementation of MSC3575 for matrix";
homepage = "https://github.com/matrix-org/sliding-sync";
license = with licenses; [ asl20 ];
maintainers = with maintainers; [ emilylange ];
mainProgram = "syncv3";
};
}
+117
View File
@@ -0,0 +1,117 @@
{ config, lib, pkgs, ... }:
let
cfg = config.services.matrix-synapse.sliding-sync;
in
{
disabledModules = [ "services/matrix/matrix-sliding-sync.nix" ];
options.services.matrix-synapse.sliding-sync = {
enable = lib.mkEnableOption (lib.mdDoc "sliding sync");
package = lib.mkOption {
type = lib.types.package;
default = pkgs.callPackage ../pkgs/matrix-sliding-sync { };
description = "What package to use for the sliding-sync proxy.";
};
enableNginx = lib.mkEnableOption (lib.mdDoc "autogenerated nginx config");
publicBaseUrl = lib.mkOption {
type = lib.types.str;
description = "The domain where clients connect, only has an effect with enableNginx";
example = "slidingsync.matrix.org";
};
settings = lib.mkOption {
type = lib.types.submodule {
freeformType = with lib.types; attrsOf str;
options = {
SYNCV3_SERVER = lib.mkOption {
type = lib.types.str;
description = lib.mdDoc ''
The destination homeserver to talk to not including `/_matrix/` e.g `https://matrix.example.org`.
'';
};
SYNCV3_DB = lib.mkOption {
type = lib.types.str;
default = "postgresql:///matrix-sliding-sync?host=/run/postgresql";
description = lib.mdDoc ''
The postgres connection string.
Refer to <https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING>.
'';
};
SYNCV3_BINDADDR = lib.mkOption {
type = lib.types.str;
default = "127.0.0.1:8009";
example = "[::]:8008";
description = lib.mdDoc "The interface and port to listen on.";
};
SYNCV3_LOG_LEVEL = lib.mkOption {
type = lib.types.enum [ "trace" "debug" "info" "warn" "error" "fatal" ];
default = "info";
description = lib.mdDoc "The level of verbosity for messages logged.";
};
};
};
default = { };
description = ''
Freeform environment variables passed to the sliding sync proxy.
Refer to <https://github.com/matrix-org/sliding-sync#setup> for all supported values.
'';
};
createDatabase = lib.mkOption {
type = lib.types.bool;
default = true;
description = lib.mdDoc ''
Whether to enable and configure `services.postgres` to ensure that the database user `matrix-sliding-sync`
and the database `matrix-sliding-sync` exist.
'';
};
environmentFile = lib.mkOption {
type = lib.types.str;
description = lib.mdDoc ''
Environment file as defined in {manpage}`systemd.exec(5)`.
This must contain the {env}`SYNCV3_SECRET` variable which should
be generated with {command}`openssl rand -hex 32`.
'';
};
};
config = lib.mkIf cfg.enable {
services.postgresql = lib.optionalAttrs cfg.createDatabase {
enable = true;
ensureDatabases = [ "matrix-sliding-sync" ];
ensureUsers = [ rec {
name = "matrix-sliding-sync";
ensurePermissions."DATABASE \"${name}\"" = "ALL PRIVILEGES";
} ];
};
systemd.services.matrix-sliding-sync = {
after = lib.optional cfg.createDatabase "postgresql.service";
wantedBy = [ "multi-user.target" ];
environment = cfg.settings;
serviceConfig = {
DynamicUser = true;
EnvironmentFile = cfg.environmentFile;
ExecStart = lib.getExe cfg.package;
StateDirectory = "matrix-sliding-sync";
WorkingDirectory = "%S/matrix-sliding-sync";
};
};
services.nginx.virtualHosts.${cfg.publicBaseUrl} = lib.mkIf cfg.enableNginx {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = lib.replaceStrings [ "0.0.0.0" "::" ] [ "127.0.0.1" "::1" ] "http://${cfg.settings.SYNCV3_BINDADDR}";
};
};
};
}
+195 -466
View File
@@ -1,35 +1,52 @@
{ lib, pkgs, config, ... }: { pkgs, lib, config, ... }:
let let
matrix-lib = (import ../lib.nix { inherit lib; });
cfg = config.services.matrix-synapse-next; cfg = config.services.matrix-synapse-next;
wcfg = cfg.workers; wcfg = cfg.workers;
# Used to generate proper defaultTexts.
cfgText = "config.services.matrix-synapse-next";
wcfgText = "config.services.matrix-synapse-next.workers";
format = pkgs.formats.yaml {}; format = pkgs.formats.yaml {};
matrix-synapse-common-config = format.generate "matrix-synapse-common-config.yaml" cfg.settings; matrix-synapse-common-config = format.generate "matrix-synapse-common-config.yaml" cfg.settings;
pluginsEnv = cfg.package.python.buildEnv.override { pluginsEnv = cfg.package.python.buildEnv.override {
extraLibs = cfg.plugins; extraLibs = cfg.plugins;
}; };
genAttrs' = items: f: g: builtins.listToAttrs (builtins.map (i: lib.attrsets.nameValuePair (f i) (g i)) items); inherit (lib)
literalExpression
mkEnableOption
mkIf
mkMerge
mkOption
mkPackageOption
types;
isListenerType = type: listener: lib.lists.any (r: lib.lists.any (n: n == type) r.names) listener.resources; throw' = str: throw ''
matrix-synapse-next error:
${str}
'';
in in
{ {
imports = [ ./nginx.nix ]; imports = [
./nginx.nix
(import ./workers.nix {
inherit matrix-lib throw' format matrix-synapse-common-config pluginsEnv;
})
];
options.services.matrix-synapse-next = { options.services.matrix-synapse-next = {
enable = lib.mkEnableOption "matrix-synapse"; enable = mkEnableOption "matrix-synapse";
package = lib.mkOption { package = mkPackageOption pkgs "matrix-synapse" {};
type = lib.types.package;
default = pkgs.matrix-synapse;
};
plugins = lib.mkOption { plugins = mkOption {
type = lib.types.listOf lib.types.package; type = types.listOf types.package;
default = [ ]; default = [ ];
example = lib.literalExample '' example = literalExpression ''
with config.services.matrix-synapse-advanced.package.plugins; [ with ${cfgText}.package.plugins; [
matrix-synapse-ldap3 matrix-synapse-ldap3
matrix-synapse-pam matrix-synapse-pam
]; ];
@@ -39,8 +56,8 @@ in
''; '';
}; };
dataDir = lib.mkOption { dataDir = mkOption {
type = lib.types.str; type = types.path;
default = "/var/lib/matrix-synapse"; default = "/var/lib/matrix-synapse";
description = '' description = ''
The directory where matrix-synapse stores its stateful data such as The directory where matrix-synapse stores its stateful data such as
@@ -48,239 +65,33 @@ in
''; '';
}; };
enableNginx = lib.mkEnableOption "Enable the synapse module managing nginx"; enableNginx = mkEnableOption "The synapse module managing nginx";
public_baseurl = lib.mkOption { public_baseurl = mkOption {
type = lib.types.str; type = types.str;
default = "matrix.${cfg.settings.server_name}"; default = "matrix.${cfg.settings.server_name}";
description = "The domain where clients and such will connect (May be different from server_name if using delegation)"; defaultText =
}; literalExpression ''matrix.''${${cfgText}.settings.server_name}'';
mainLogConfig = lib.mkOption {
type = lib.types.lines;
description = "A yaml python logging config file";
default = lib.readFile ./matrix-synapse-log_config.yaml;
};
workers = let
isReplication = l: lib.lists.any (r: lib.lists.any (n: n == "replication") r.names) l.resources;
dMRL = lib.lists.findFirst isReplication
(throw "No replication listener configured!")
cfg.settings.listeners;
dMRH = lib.findFirst (x: true) (throw "Replication listener had no addresses")
dMRL.bind_addresses;
dMRP = dMRL.port;
in {
mainReplicationHost = lib.mkOption {
type = lib.types.str;
default = if builtins.elem dMRH [ "0.0.0.0" "::" ] then "127.0.0.1" else dMRH;
description = "Host of the main synapse instance's replication listener";
};
mainReplicationPort = lib.mkOption {
type = lib.types.port;
default = dMRP;
description = "Port for the main synapse instance's replication listener";
};
defaultListenerAddress = lib.mkOption {
type = lib.types.str;
default = "127.0.0.1";
description = "The default listener address for the worker";
};
workerStartingPort = lib.mkOption {
type = lib.types.port;
description = "What port should the automatically configured workers start enumerating from";
default = 8083;
};
enableMetrics = lib.mkOption {
type = lib.types.bool;
default = cfg.settings.enable_metrics;
};
metricsStartingPort = lib.mkOption {
type = lib.types.port;
default = 18083;
};
federationSenders = lib.mkOption {
type = lib.types.ints.unsigned;
description = "How many automatically configured federation senders to set up";
default = 0;
};
federationReceivers = lib.mkOption {
type = lib.types.ints.unsigned;
description = "How many automatically configured federation recievers to set up";
default = 0;
};
initialSyncers = lib.mkOption {
type = lib.types.ints.unsigned;
description = "How many automatically configured intial syncers to set up";
default = 0;
};
normalSyncers = lib.mkOption {
type = lib.types.ints.unsigned;
description = "How many automatically configured sync workers to set up";
default = 0;
};
eventPersisters = lib.mkOption {
type = lib.types.ints.unsigned;
description = "How many automatically configured event-persisters to set up";
default = 0;
};
useUserDirectoryWorker = lib.mkEnableOption "user directory worker";
instances = lib.mkOption {
type = lib.types.attrsOf (lib.types.submodule ({config, ...}: {
options.isAuto = lib.mkOption {
type = lib.types.bool;
internal = true;
default = false;
};
options.index = lib.mkOption {
internal = true;
type = lib.types.ints.positive;
};
# The custom string type here is mainly for the name to use for the metrics of custom worker types
options.type = lib.mkOption {
type = lib.types.either (lib.types.str) (lib.types.enum [ "fed-sender" "fed-receiver" ]);
};
options.settings = let
instanceCfg = config;
inherit (instanceCfg) type isAuto;
in lib.mkOption {
type = lib.types.submodule ({config, ...}: {
freeformType = format.type;
options.worker_app = let
mapTypeApp = t: {
"fed-sender" = "synapse.app.generic_worker";
"fed-receiver" = "synapse.app.generic_worker";
"initial-sync" = "synapse.app.generic_worker";
"normal-sync" = "synapse.app.generic_worker";
"event-persist" = "synapse.app.generic_worker";
"user-dir" = "synapse.app.generic_worker";
}.${t};
defaultApp = if (!isAuto)
then "synapse.app.generic_worker"
else mapTypeApp type;
in lib.mkOption {
type = lib.types.enum [
"synapse.app.generic_worker"
"synapse.app.appservice"
"synapse.app.media_repository"
"synapse.app.user_dir"
];
description = "The type of worker application";
default = defaultApp;
};
options.worker_replication_host = lib.mkOption {
type = lib.types.str;
default = cfg.workers.mainReplicationHost;
description = "The replication listeners ip on the main synapse process";
};
options.worker_replication_http_port = lib.mkOption {
type = lib.types.port;
default = cfg.workers.mainReplicationPort;
description = "The replication listeners port on the main synapse process";
};
options.worker_listeners = lib.mkOption {
type = lib.types.listOf (lib.types.submodule {
options.type = lib.mkOption {
type = lib.types.enum [ "http" "metrics" ];
description = "The type of the listener";
default = "http";
};
options.port = lib.mkOption {
type = lib.types.port;
description = "the TCP port to bind to";
};
options.bind_addresses = lib.mkOption {
type = lib.types.listOf lib.types.str;
description = "A list of local addresses to listen on";
default = [ cfg.workers.defaultListenerAddress ];
};
options.tls = lib.mkOption {
type = lib.types.bool;
description = "set to true to enable TLS for this listener. Will use the TLS key/cert specified in tls_private_key_path / tls_certificate_path.";
default = false;
};
options.x_forwarded = lib.mkOption {
type = lib.types.bool;
description = '' description = ''
Only valid for an 'http' listener. Set to true to use the X-Forwarded-For header as the client IP. The domain where clients and such will connect.
Useful when Synapse is behind a reverse-proxy. This may be different from server_name if using delegation.
''; '';
default = true;
};
options.resources = let
typeToResources = t: {
"fed-receiver" = [ "federation" ];
"fed-sender" = [ ];
"initial-sync" = [ "client" ];
"normal-sync" = [ "client" ];
"event-persist" = [ "replication" ];
"user-dir" = [ "client" ];
}.${t};
in lib.mkOption {
type = lib.types.listOf (lib.types.submodule {
options.names = lib.mkOption {
type = lib.types.listOf (lib.types.enum [ "client" "consent" "federation" "keys" "media" "metrics" "openid" "replication" "static" "webclient" ]);
description = "A list of resources to host on this port";
default = lib.optionals isAuto (typeToResources type);
};
options.compress = lib.mkOption {
type = lib.types.bool;
description = "enable HTTP compression for this resource";
default = false;
};
});
default = [{ }];
};
});
description = "Listener configuration for the worker, similar to the main synapse listener";
default = [ ];
};
});
default = { };
};
}));
default = { };
description = "Worker configuration";
example = {
"federation_sender1" = {
settings = {
worker_name = "federation_sender1";
worker_app = "synapse.app.generic_worker";
worker_replication_host = "127.0.0.1";
worker_replication_http_port = 9093;
worker_listeners = [ ];
};
};
};
};
}; };
settings = lib.mkOption { mainLogConfig = mkOption {
type = lib.types.submodule { type = with types; coercedTo path lib.readFile lines;
default = ./matrix-synapse-log_config.yaml;
description = "A yaml python logging config file";
};
enableSlidingSync = mkEnableOption (lib.mdDoc "automatic Sliding Sync setup at `slidingsync.<domain>`");
settings = mkOption {
type = types.submodule {
freeformType = format.type; freeformType = format.type;
options = {
options.server_name = lib.mkOption { server_name = mkOption {
type = lib.types.str; type = types.str;
description = '' description = ''
The server_name name will appear at the end of usernames and room addresses The server_name name will appear at the end of usernames and room addresses
created on this server. For example if the server_name was example.com, created on this server. For example if the server_name was example.com,
@@ -300,55 +111,80 @@ in
example = "matrix.org"; example = "matrix.org";
}; };
options.use_presence = lib.mkOption { use_presence = mkOption {
type = lib.types.bool; type = types.bool;
description = "disable presence tracking, if you're having perfomance issues this can have a big impact"; description = "Disable presence tracking, if you're having perfomance issues this can have a big impact";
default = true; default = true;
}; };
options.listeners = lib.mkOption {
type = lib.types.listOf (lib.types.submodule { listeners = mkOption {
options.port = lib.mkOption { type = types.listOf (types.submodule {
type = lib.types.port; options = {
description = "the TCP port to bind to"; port = mkOption {
type = types.port;
description = "The TCP port to bind to";
example = 8448; example = 8448;
}; };
options.bind_addresses = lib.mkOption {
type = lib.types.listOf lib.types.str; bind_addresses = mkOption {
type = types.listOf types.str;
description = "A list of local addresses to listen on"; description = "A list of local addresses to listen on";
}; };
options.type = lib.mkOption {
type = lib.types.enum [ "http" "manhole" "metrics" "replication" ]; type = mkOption {
type = types.enum [ "http" "manhole" "metrics" "replication" ];
description = "The type of the listener"; description = "The type of the listener";
default = "http"; default = "http";
}; };
options.tls = lib.mkOption {
type = lib.types.bool; tls = mkOption {
description = "set to true to enable TLS for this listener. Will use the TLS key/cert specified in tls_private_key_path / tls_certificate_path."; type = types.bool;
description = ''
Set to true to enable TLS for this listener.
Will use the TLS key/cert specified in tls_private_key_path / tls_certificate_path.
'';
default = false; default = false;
}; };
options.x_forwarded = lib.mkOption {
type = lib.types.bool; x_forwarded = mkOption {
type = types.bool;
description = '' description = ''
Only valid for an 'http' listener. Set to true to use the X-Forwarded-For header as the client IP. Set to true to use the X-Forwarded-For header as the client IP.
Only valid for an 'http' listener.
Useful when Synapse is behind a reverse-proxy. Useful when Synapse is behind a reverse-proxy.
''; '';
default = true; default = true;
}; };
options.resources = lib.mkOption {
type = lib.types.listOf (lib.types.submodule { resources = mkOption {
options.names = lib.mkOption { type = types.listOf (types.submodule {
type = lib.types.listOf (lib.types.enum [ "client" "consent" "federation" "keys" "media" "metrics" "openid" "replication" "static" "webclient" ]); options = {
names = mkOption {
type = with types; listOf (enum [
"client"
"consent"
"federation"
"keys"
"media"
"metrics"
"openid"
"replication"
"static"
"webclient"
]);
description = "A list of resources to host on this port"; description = "A list of resources to host on this port";
}; };
options.compress = lib.mkOption {
type = lib.types.bool; compress = mkEnableOption "HTTP compression for this resource";
description = "enable HTTP compression for this resource";
default = false;
}; };
}); });
}; };
};
}); });
description = "List of ports that Synapse should listen on, their purpose and their configuration"; description = "List of ports that Synapse should listen on, their purpose and their configuration";
# TODO: add defaultText
default = [ default = [
{ {
port = 8008; port = 8008;
@@ -358,14 +194,14 @@ in
{ names = [ "federation" ]; compress = false; } { names = [ "federation" ]; compress = false; }
]; ];
} }
(lib.mkIf (wcfg.instances != { }) { (mkIf (wcfg.instances != { }) {
port = 9093; port = 9093;
bind_addresses = [ "127.0.0.1" ]; bind_addresses = [ "127.0.0.1" ];
resources = [ resources = [
{ names = [ "replication" ]; } { names = [ "replication" ]; }
]; ];
}) })
(lib.mkIf cfg.settings.enable_metrics { (mkIf cfg.settings.enable_metrics {
port = 9000; port = 9000;
bind_addresses = [ "127.0.0.1" ]; bind_addresses = [ "127.0.0.1" ];
resources = [ resources = [
@@ -375,8 +211,8 @@ in
]; ];
}; };
options.federation_ip_range_blacklist = lib.mkOption { federation_ip_range_blacklist = mkOption {
type = lib.types.listOf lib.types.str; type = types.listOf types.str;
description = '' description = ''
Prevent federation requests from being sent to the following Prevent federation requests from being sent to the following
blacklist IP address CIDR ranges. If this option is not specified, or blacklist IP address CIDR ranges. If this option is not specified, or
@@ -394,62 +230,55 @@ in
"fc00::/7" "fc00::/7"
]; ];
}; };
options.log_config = lib.mkOption {
type = lib.types.path; log_config = mkOption {
type = types.path;
description = '' description = ''
A yaml python logging config file as described by A yaml python logging config file as described by
https://docs.python.org/3.7/library/logging.config.html#configuration-dictionary-schema https://docs.python.org/3.7/library/logging.config.html#configuration-dictionary-schema
''; '';
default = pkgs.writeText "log_config.yaml" cfg.mainLogConfig; default = pkgs.writeText "log_config.yaml" cfg.mainLogConfig;
defaultText = "A config file generated from ${cfgText}.mainLogConfig";
}; };
options.media_store_path = lib.mkOption { media_store_path = mkOption {
type = lib.types.path; type = types.path;
description = "Directory where uploaded images and attachments are stored"; description = "Directory where uploaded images and attachments are stored";
default = "${cfg.dataDir}/media_store"; default = "${cfg.dataDir}/media_store";
defaultText = literalExpression ''''${${cfgText}.dataDir}/media_store'';
}; };
options.max_upload_size = lib.mkOption {
type = lib.types.str; max_upload_size = mkOption {
type = types.str;
description = "The largest allowed upload size in bytes"; description = "The largest allowed upload size in bytes";
default = "50M"; default = "50M";
example = "800K";
}; };
options.enable_registration = lib.mkOption { enable_registration = mkEnableOption "registration for new users";
type = lib.types.bool; enable_metrics = mkEnableOption "collection and rendering of performance metrics";
description = "Enable registration for new users"; report_stats = mkEnableOption "reporting usage stats";
default = false;
};
options.enable_metrics = lib.mkOption { app_service_config_files = mkOption {
type = lib.types.bool; type = types.listOf types.path;
description = "Enable collection and rendering of performance metrics";
default = false;
};
options.report_stats = lib.mkOption {
type = lib.types.bool;
description = "TODO: Enable and Disable reporting usage stats";
default = false;
};
options.app_service_config_files = lib.mkOption {
type = lib.types.listOf lib.types.path;
description = "A list of application service config files to use"; description = "A list of application service config files to use";
default = []; default = [];
}; };
options.signing_key_path = lib.mkOption { signing_key_path = mkOption {
type = lib.types.path; type = types.path;
description = "Path to the signing key to sign messages with"; description = "Path to the signing key to sign messages with";
default = "${cfg.dataDir}/homeserver.signing.key"; default = "${cfg.dataDir}/homeserver.signing.key";
defaultText = literalExpression ''''${${cfgText}.dataDir}/homeserver.signing.key'';
}; };
options.trusted_key_servers = lib.mkOption { trusted_key_servers = mkOption {
type = lib.types.listOf (lib.types.submodule { type = types.listOf (types.submodule {
freeformType = format.type; freeformType = format.type;
options.server_name = lib.mkOption { options.server_name = mkOption {
type = lib.types.str; type = types.str;
description = "the name of the server. required"; description = "The name of the server. This is required.";
}; };
}); });
description = "The trusted servers to download signing keys from"; description = "The trusted servers to download signing keys from";
@@ -461,36 +290,42 @@ in
]; ];
}; };
options.federation_sender_instances = lib.mkOption { federation_sender_instances = mkOption {
type = lib.types.listOf lib.types.str; type = types.listOf types.str;
description = '' description = ''
This configuration must be shared between all federation sender workers, and if This configuration must be shared between all federation sender workers.
changed all federation sender workers must be stopped at the same time and then
started, to ensure that all instances are running with the same config (otherwise When changed, all federation sender workers must be stopped at the same time and
events may be dropped) restarted, to ensure that all instances are running with the same config.
Otherwise, events may be dropped.
''; '';
default = [ ]; default = [ ];
}; };
options.redis = lib.mkOption { redis = mkOption {
type = lib.types.submodule { type = types.submodule {
freeformType = format.type; freeformType = format.type;
options.enabled = lib.mkOption { options.enabled = mkOption {
type = lib.types.bool; type = types.bool;
description = "Enables using redis, required for worker support"; description = ''
default = (lib.lists.count (x: true) Whether to enable redis within synapse.
(lib.attrsets.attrValues cfg.workers.instances)) > 0;
This is required for worker support.
'';
default = wcfg.instances != { };
defaultText = literalExpression "${wcfgText}.instances != { }";
}; };
}; };
default = { }; default = { };
description = "configuration of redis for synapse and workers"; description = "Redis configuration for synapse and workers";
};
}; };
}; };
}; };
extraConfigFiles = lib.mkOption { extraConfigFiles = mkOption {
type = lib.types.listOf lib.types.path; type = types.listOf types.path;
default = []; default = [];
description = '' description = ''
Extra config files to include. Extra config files to include.
@@ -500,11 +335,9 @@ in
NixOPS is in use. NixOPS is in use.
''; '';
}; };
}; };
config = lib.mkIf cfg.enable (lib.mkMerge [ config = mkIf cfg.enable {
({
users.users.matrix-synapse = { users.users.matrix-synapse = {
group = "matrix-synapse"; group = "matrix-synapse";
home = cfg.dataDir; home = cfg.dataDir;
@@ -512,174 +345,70 @@ in
shell = "${pkgs.bash}/bin/bash"; shell = "${pkgs.bash}/bin/bash";
uid = config.ids.uids.matrix-synapse; uid = config.ids.uids.matrix-synapse;
}; };
users.groups.matrix-synapse = { users.groups.matrix-synapse = {
gid = config.ids.gids.matrix-synapse; gid = config.ids.gids.matrix-synapse;
}; };
systemd.targets.matrix-synapse = {
description = "Synapse parent target"; systemd = {
targets.matrix-synapse = {
description = "Matrix synapse parent target";
after = [ "network.target" ]; after = [ "network.target" ];
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
}; };
})
({ slices.system-matrix-synapse = {
systemd.services.matrix-synapse = { description = "Matrix synapse slice";
requires= [ "system.slice" ];
after= [ "system.slice" ];
};
services.matrix-synapse = {
description = "Synapse Matrix homeserver"; description = "Synapse Matrix homeserver";
partOf = [ "matrix-synapse.target" ]; partOf = [ "matrix-synapse.target" ];
wantedBy = [ "matrix-synapse.target" ]; wantedBy = [ "matrix-synapse.target" ];
preStart = ''
${cfg.package}/bin/synapse_homeserver \ preStart = let
${ lib.concatMapStringsSep "\n " (x: "--config-path ${x} \\") ([ matrix-synapse-common-config ] ++ cfg.extraConfigFiles) } flags = lib.cli.toGNUCommandLineShell {} {
--keys-directory ${cfg.dataDir} \ config-path = [ matrix-synapse-common-config ] ++ cfg.extraConfigFiles;
--generate-keys keys-directory = cfg.dataDir;
''; generate-keys = true;
environment.PYTHONPATH = lib.makeSearchPathOutput "lib" cfg.package.python.sitePackages [ pluginsEnv ]; };
in "${cfg.package}/bin/synapse_homeserver ${flags}";
environment.PYTHONPATH =
lib.makeSearchPathOutput "lib" cfg.package.python.sitePackages [ pluginsEnv ];
serviceConfig = { serviceConfig = {
Type = "notify"; Type = "notify";
User = "matrix-synapse"; User = "matrix-synapse";
Group = "matrix-synapse"; Group = "matrix-synapse";
Slice = "system-matrix-synapse.slice";
WorkingDirectory = cfg.dataDir; WorkingDirectory = cfg.dataDir;
ExecStart = '' ExecStart = let
${cfg.package}/bin/synapse_homeserver \ flags = lib.cli.toGNUCommandLineShell {} {
${ lib.concatMapStringsSep "\n " (x: "--config-path ${x} \\") ([ matrix-synapse-common-config ] ++ cfg.extraConfigFiles) } config-path = [ matrix-synapse-common-config ] ++ cfg.extraConfigFiles;
--keys-directory ${cfg.dataDir} keys-directory = cfg.dataDir;
''; };
in "${cfg.package}/bin/synapse_homeserver ${flags}";
ExecReload = "${pkgs.utillinux}/bin/kill -HUP $MAINPID"; ExecReload = "${pkgs.utillinux}/bin/kill -HUP $MAINPID";
Restart = "on-failure"; Restart = "on-failure";
}; };
}; };
})
(lib.mkMerge [
({
services.matrix-synapse-next.settings.federation_sender_instances = lib.genList (i: "auto-fed-sender${toString (i + 1)}") cfg.workers.federationSenders;
services.matrix-synapse-next.workers.instances = genAttrs' (lib.lists.range 1 cfg.workers.federationSenders)
(i: "auto-fed-sender${toString i}")
(i: {
isAuto = true; type = "fed-sender"; index = i;
settings.worker_listeners = lib.mkIf wcfg.enableMetrics [
{ port = cfg.workers.metricsStartingPort + i - 1;
resources = [ { names = [ "metrics" ]; } ];
}
];
});
})
({
services.matrix-synapse-next.workers.instances = genAttrs' (lib.lists.range 1 cfg.workers.federationReceivers)
(i: "auto-fed-receiver${toString i}")
(i: {
isAuto = true; type = "fed-receiver"; index = i;
settings.worker_listeners = [{ port = cfg.workers.workerStartingPort + i - 1; }]
++ lib.optional wcfg.enableMetrics { port = cfg.workers.metricsStartingPort + cfg.workers.federationSenders + i;
resources = [ { names = [ "metrics" ]; } ];
}; };
});
})
services.matrix-synapse-next.settings.extra_well_known_client_content."org.matrix.msc3575.proxy" = mkIf cfg.enableSlidingSync {
({ url = "https://${config.services.matrix-synapse.sliding-sync.publicBaseUrl}";
services.matrix-synapse-next.workers.instances = genAttrs' (lib.lists.range 1 cfg.workers.initialSyncers)
(i: "auto-initial-sync${toString i}")
(i: {
isAuto = true; type = "initial-sync"; index = i;
settings.worker_listeners = [{ port = cfg.workers.workerStartingPort + cfg.workers.federationReceivers + i - 1; }]
++ lib.optional wcfg.enableMetrics { port = cfg.workers.metricsStartingPort + cfg.workers.federationSenders + cfg.workers.federationReceivers + i;
resources = [ { names = [ "metrics" ]; } ];
}; };
}); services.matrix-synapse.sliding-sync = mkIf cfg.enableSlidingSync {
}) enable = true;
enableNginx = lib.mkDefault cfg.enableNginx;
publicBaseUrl = lib.mkDefault "slidingsync.${cfg.settings.server_name}";
({ settings = {
services.matrix-synapse-next.workers.instances = genAttrs' (lib.lists.range 1 cfg.workers.normalSyncers) SYNCV3_SERVER = lib.mkDefault "https://${cfg.public_baseurl}";
(i: "auto-normal-sync${toString i}") SYNCV3_PROM = lib.mkIf cfg.settings.enable_metrics (lib.mkDefault "127.0.0.1:9001");
(i: {
isAuto = true; type = "normal-sync"; index = i;
settings.worker_listeners = [{ port = cfg.workers.workerStartingPort + cfg.workers.federationReceivers + cfg.workers.initialSyncers + i - 1; }]
++ lib.optional wcfg.enableMetrics { port = cfg.workers.metricsStartingPort + cfg.workers.federationSenders + cfg.workers.federationReceivers + cfg.workers.initialSyncers + i;
resources = [ { names = [ "metrics" ]; } ];
};
});
})
({
services.matrix-synapse-next.settings.instance_map = genAttrs' (lib.lists.range 1 cfg.workers.eventPersisters)
(i: "auto-event-persist${toString i}")
(i: let
isReplication = l: lib.lists.any (r: lib.lists.any (n: n == "replication") r.names) l.resources;
wRL = lib.lists.findFirst isReplication
(throw "No replication listener configured!")
cfg.workers.instances."auto-event-persist${toString i}".settings.worker_listeners;
wRH = lib.findFirst (x: true) (throw "Replication listener had no addresses")
wRL.bind_addresses;
wRP = wRL.port;
in {
host = wRH;
port = wRP;
}
);
services.matrix-synapse-next.settings.stream_writers.events = lib.mkIf (cfg.workers.eventPersisters > 0) (lib.genList (i: "auto-event-persist${toString (i + 1)}") cfg.workers.eventPersisters);
services.matrix-synapse-next.workers.instances = genAttrs' (lib.lists.range 1 cfg.workers.eventPersisters)
(i: "auto-event-persist${toString i}")
(i: {
isAuto = true; type = "event-persist"; index = i;
settings.worker_listeners = [{ port = cfg.workers.workerStartingPort + cfg.workers.federationReceivers + cfg.workers.initialSyncers + cfg.workers.normalSyncers + i - 1;}]
++ lib.optional wcfg.enableMetrics { port = cfg.workers.metricsStartingPort + cfg.workers.federationSenders + cfg.workers.federationReceivers + cfg.workers.initialSyncers + cfg.workers.normalSyncers + i;
resources = [ { names = [ "metrics" ]; } ];
};
});
})
(lib.mkIf cfg.workers.useUserDirectoryWorker {
services.matrix-synapse-next.workers.instances."auto-user-dir" = {
isAuto = true; type = "user-dir"; index = 1;
settings.worker_listeners = [{ port = cfg.workers.workerStartingPort + cfg.workers.federationReceivers + cfg.workers.initialSyncers + cfg.workers.normalSyncers + cfg.workers.eventPersisters + 1 - 1;}]
++ lib.optional wcfg.enableMetrics { port = cfg.workers.metricsStartingPort + cfg.workers.federationSenders + cfg.workers.federationReceivers + cfg.workers.initialSyncers + cfg.workers.normalSyncers + cfg.workers.eventPersisters + 1;
resources = [ { names = [ "metrics"]; } ];
}; };
}; };
services.matrix-synapse-next.settings.update_user_directory_from_worker = "auto-user-dir";
})
])
({
systemd.services = let
workerList = lib.mapAttrsToList (name: value: lib.nameValuePair name value ) cfg.workers.instances;
workerName = worker: worker.name;
workerSettings = worker: (worker.value.settings // {worker_name = (workerName worker);});
workerConfig = worker: format.generate "matrix-synapse-worker-${workerName worker}-config.yaml" (workerSettings worker);
in builtins.listToAttrs (map (worker:
{
name = "matrix-synapse-worker-${workerName worker}";
value = {
description = "Synapse Matrix Worker";
partOf = [ "matrix-synapse.target" ];
wantedBy = [ "matrix-synapse.target" ];
after = [ "matrix-synapse.service" ];
requires = [ "matrix-synapse.service" ];
environment.PYTHONPATH = lib.makeSearchPathOutput "lib" cfg.package.python.sitePackages [
pluginsEnv
];
serviceConfig = {
Type = "notify";
User = "matrix-synapse";
Group = "matrix-synapse";
WorkingDirectory = cfg.dataDir;
ExecStartPre = pkgs.writers.writeBash "wait-for-synapse" ''
# From https://md.darmstadt.ccc.de/synapse-at-work
while ! systemctl is-active -q matrix-synapse.service; do
sleep 1
done
'';
ExecStart = ''
${cfg.package}/bin/synapse_worker \
${ lib.concatMapStringsSep "\n " (x: "--config-path ${x} \\") ([ matrix-synapse-common-config (workerConfig worker) ] ++ cfg.extraConfigFiles) }
--keys-directory ${cfg.dataDir}
'';
}; };
};
}
) workerList);
})
]);
} }
+7 -4
View File
@@ -1,4 +1,4 @@
{ lib, pkgs, config, ...}: { pkgs, lib, config, ... }:
let let
cfg = config.services.matrix-synapse-next; cfg = config.services.matrix-synapse-next;
@@ -140,7 +140,7 @@ in
services.nginx.upstreams.synapse_master.servers = let services.nginx.upstreams.synapse_master.servers = let
isMainListener = l: isListenerType "client" l && isListenerType "federation" l; isMainListener = l: isListenerType "client" l && isListenerType "federation" l;
firstMainListener = lib.findFirst isMainListener firstMainListener = lib.findFirst isMainListener
(throw "No cartch-all listener configured") cfg.settings.listeners; (throw "No catch-all listener configured") cfg.settings.listeners;
address = lib.findFirst (_: true) (throw "No address in main listener") firstMainListener.bind_addresses; address = lib.findFirst (_: true) (throw "No address in main listener") firstMainListener.bind_addresses;
port = firstMainListener.port; port = firstMainListener.port;
socketAddress = "${address}:${builtins.toString port}"; socketAddress = "${address}:${builtins.toString port}";
@@ -198,7 +198,7 @@ in
}; };
services.nginx.virtualHosts."${cfg.public_baseurl}" = { services.nginx.virtualHosts."${cfg.public_baseurl}" = {
enableACME = true; enableACME = lib.mkDefault true;
forceSSL = true; forceSSL = true;
locations."/_matrix" = { locations."/_matrix" = {
proxyPass = "http://$synapse_backend"; proxyPass = "http://$synapse_backend";
@@ -230,6 +230,9 @@ in
locations."/_synapse/client" = { locations."/_synapse/client" = {
proxyPass = "http://$synapse_backend"; proxyPass = "http://$synapse_backend";
}; };
locations."/.well-known/matrix" = {
proxyPass = "http://$synapse_backend";
};
};
}; };
};
} }
+318
View File
@@ -0,0 +1,318 @@
{ matrix-synapse-common-config,
matrix-lib,
pluginsEnv,
throw',
format
}:
{ pkgs, lib, config, ... }: let
cfg = config.services.matrix-synapse-next;
wcfg = config.services.matrix-synapse-next.workers;
# Used to generate proper defaultTexts.
cfgText = "config.services.matrix-synapse-next";
wcfgText = "config.services.matrix-synapse-next.workers";
inherit (lib) types mkOption mkEnableOption mkIf mkMerge literalExpression;
mkWorkerCountOption = workerType: mkOption {
type = types.ints.unsigned;
description = "How many automatically configured ${workerType} workers to set up";
default = 0;
example = 1;
};
genAttrs' = items: f: g: builtins.listToAttrs (map (i: lib.nameValuePair (f i) (g i)) items);
mainReplicationListener = matrix-lib.firstListenerOfType "replication" cfg.settings.listeners;
in {
# See https://github.com/matrix-org/synapse/blob/develop/docs/workers.md for more info
options.services.matrix-synapse-next.workers = let
workerInstanceType = types.submodule ({ config, ... }: {
options = {
isAuto = mkOption {
type = types.bool;
internal = true;
default = false;
description = ''
This is an internal flag that signals that this worker is part of the
workers generated by either of the following:
- federationSenders
- federationReceivers
- initialSyncers
- normalSyncers
- eventPersisters
- useUserDirectoryWorker
'';
};
index = mkOption {
internal = true;
type = types.ints.positive;
description = ''
This is an internal variable that indexes the worker of this type.
'';
};
# The custom string type here is mainly for the name to use
# for the metrics of custom worker types
type = mkOption {
type = types.str;
# TODO: add description and possibly default value?
};
settings = mkOption {
type = workerSettingsType config;
default = { };
};
};
});
workerSettingsType = instanceCfg: types.submodule {
freeformType = format.type;
options = {
worker_app = mkOption {
type = types.enum [
"synapse.app.generic_worker"
"synapse.app.appservice"
"synapse.app.media_repository"
"synapse.app.user_dir"
];
description = "The type of worker application";
default = "synapse.app.generic_worker";
};
worker_listeners = mkOption {
type = types.listOf (workerListenerType instanceCfg);
description = "Listener configuration for the worker, similar to the main synapse listener";
default = [ ];
};
};
};
workerListenerType = instanceCfg: types.submodule {
options = {
type = mkOption {
type = types.enum [ "http" "metrics" ];
description = "The type of the listener";
default = "http";
};
path = mkOption {
type = types.path;
default = instanceCfg.name;
description = ''
A path and filename for a Unix socket.
'';
};
# port = mkOption {
# type = types.port;
# description = "The TCP port to bind to";
# };
# bind_addresses = mkOption {
# type = with types; listOf str;
# description = "A list of local addresses to listen on";
# default = [ wcfg.defaultListenerAddress ];
# defaultText = literalExpression "[ ${wcfgText}.defaultListenerAddress ]";
# };
tls = mkOption {
type = types.bool;
description = ''
Whether to enable TLS for this listener.
Will use the TLS key/cert specified in tls_private_key_path / tls_certificate_path.
'';
default = false;
example = true;
};
x_forwarded = mkOption {
type = types.bool;
description = ''
Whether to use the X-Forwarded-For HTTP header as the client IP.
This option is only valid for an 'http' listener.
It is useful when Synapse is running behind a reverse-proxy.
'';
default = true;
example = false;
};
resources = let
typeToResources = t: {
"fed-receiver" = [ "federation" ];
"fed-sender" = [ ];
"initial-sync" = [ "client" ];
"normal-sync" = [ "client" ];
"event-persist" = [ "replication" ];
"user-dir" = [ "client" ];
}.${t};
in mkOption {
type = types.listOf (types.submodule {
options = {
names = mkOption {
type = with types; listOf (enum [
"client"
"consent"
"federation"
"keys"
"media"
"metrics"
"openid"
"replication"
"static"
"webclient"
]);
description = "A list of resources to host on this port";
default = lib.optionals instanceCfg.isAuto (typeToResources instanceCfg.type);
defaultText = ''
If the worker is generated from other config, the resource type will
be determined automatically.
'';
};
compress = mkEnableOption "HTTP compression for this resource";
};
});
default = [{ }];
};
};
};
in {
enableMetrics = mkOption {
type = types.bool;
default = cfg.settings.enable_metrics;
defaultText = literalExpression "${cfgText}.settings.enable_metrics";
# TODO: add description
};
federationSenders = mkWorkerCountOption "federation-sender";
federationReceivers = mkWorkerCountOption "federation-reciever";
initialSyncers = mkWorkerCountOption "initial-syncer";
normalSyncers = mkWorkerCountOption "sync";
eventPersisters = mkWorkerCountOption "event-persister";
useUserDirectoryWorker = mkEnableOption "user directory worker";
instances = mkOption {
type = types.attrsOf workerInstanceType;
default = { };
description = "Worker configuration";
example = {
"federation-sender-1" = {
settings = {
worker_name = "federation-sender-1";
worker_app = "synapse.app.generic_worker";
path = "/run/matrix-synapse/federation-sender-1.sock";
# worker_replication_host = "127.0.0.1";
# worker_replication_http_port = 9093;
worker_listeners = [ ];
};
};
};
};
};
config = let
genList1 = f: builtins.genList (i: f (i + 1));
in {
services.matrix-synapse-next.settings = {
federation_sender_instances =
genList1 (i: "auto-fed-sender-${toString i}") wcfg.federationSenders;
instance_map = lib.mkIf (cfg.workers.instances != { }) ({
main.path = "/run/matrix-synapse/main-replication-worker.sock";
} // builtins.mapAttrs (n: v: {
inherit (builtins.head v.settings.worker_listeners) path;
}) wcfg.instances);
stream_writers.events =
mkIf (wcfg.eventPersisters > 0)
(genList1 (i: "auto-event-persist-${toString i}") wcfg.eventPersisters);
update_user_directory_from_worker =
mkIf wcfg.useUserDirectoryWorker "auto-user-dir-1";
};
services.matrix-synapse-next.workers.instances =
let
workerInstances = {
"fed-sender" = wcfg.federationSenders;
"fed-receiver" = wcfg.federationReceivers;
"initial-sync" = wcfg.initialSyncers;
"normal-sync" = wcfg.normalSyncers;
"event-persist" = wcfg.eventPersisters;
"user-dir" = if wcfg.useUserDirectoryWorker then 1 else 0;
};
in
lib.pipe workerInstances [
(lib.mapAttrsToList (type: count: { inherit type count; }))
(map ({ type, count }: genList1 (i: rec {
name = "auto-${type}-${toString i}";
value = {
inherit type;
isAuto = true;
index = i;
settings.worker_listeners =
[
{ path = "/run/matrix-synapse/${name}.sock"; }
] ++ lib.optionals wcfg.enableMetrics [{
path = "/run/matrix-synapse/${name}-metrics.sock";
resources = [{ names = [ "metrics" ]; }];
type = "metrics";
}];
};
}) count))
lib.flatten
builtins.listToAttrs
];
systemd.services = let
workerList = lib.mapAttrsToList lib.nameValuePair wcfg.instances;
workerConfig = worker: format.generate "matrix-synapse-worker-${worker.name}-config.yaml"
(worker.value.settings // { worker_name = worker.name; });
in builtins.listToAttrs (lib.flip map workerList (worker: {
name = "matrix-synapse-worker-${worker.name}";
value = {
documentation = [ "https://github.com/matrix-org/synapse/blob/develop/docs/workers.md" ];
description = "Synapse Matrix Worker";
partOf = [ "matrix-synapse.target" ];
wantedBy = [ "matrix-synapse.target" ];
after = [ "matrix-synapse.service" ];
requires = [ "matrix-synapse.service" ];
restartTriggers = [ matrix-synapse-common-config (workerConfig worker) ] ++ cfg.extraConfigFiles;
environment = {
PYTHONPATH = lib.makeSearchPathOutput "lib" cfg.package.python.sitePackages [
pluginsEnv
];
};
serviceConfig = {
Restart = "always";
Type = "notify";
User = "matrix-synapse";
Group = "matrix-synapse";
Slice = "system-matrix-synapse.slice";
WorkingDirectory = cfg.dataDir;
RuntimeDirectory = [ "matrix-synapse" ];
ExecStartPre = pkgs.writers.writeBash "wait-for-synapse" ''
# From https://md.darmstadt.ccc.de/synapse-at-work
while ! systemctl is-active -q matrix-synapse.service; do
sleep 1
done
'';
ExecStart = let
flags = lib.cli.toGNUCommandLineShell {} {
config-path = [ matrix-synapse-common-config (workerConfig worker) ] ++ cfg.extraConfigFiles;
keys-directory = cfg.dataDir;
};
in "${cfg.package}/bin/synapse_worker ${flags}";
};
};
}));
};
}
+18
View File
@@ -0,0 +1,18 @@
{ pkgs, lib, ... }:
{
services.matrix-synapse-next = {
enable = true;
settings.server_name = "matrix.example.com";
workers = {
enableMetrics = true;
federationSenders = 2;
federationReceivers = 2;
initialSyncers = 2;
normalSyncers = 2;
eventPersisters = 2;
useUserDirectoryWorker = true;
};
};
}
+7
View File
@@ -0,0 +1,7 @@
{ pkgs, lib, ... }:
{
services.matrix-synapse-next = {
enable = true;
settings.server_name = "matrix.example.com";
};
}
+16
View File
@@ -0,0 +1,16 @@
{ nixpkgs, pkgs, system ? pkgs.system, nixosModule, ... }: let
buildSystemWithConfig = configPath: (nixpkgs.lib.nixosSystem {
inherit system;
modules = [
nixosModule
configPath
{
boot.isContainer = true;
}
];
}).config.system.build.toplevel;
in {
a = pkgs.writeText "hello-world" ''a'';
base-config = buildSystemWithConfig ./base-config.nix;
auto-workers-config = buildSystemWithConfig ./auto-workers-config.nix;
}