1{
2 config,
3 lib,
4 options,
5 pkgs,
6 ...
7}:
8
9with lib;
10
11let
12 cfg = config.services.matrix-synapse;
13 format = pkgs.formats.yaml { };
14
15 filterRecursiveNull =
16 o:
17 if isAttrs o then
18 mapAttrs (_: v: filterRecursiveNull v) (filterAttrs (_: v: v != null) o)
19 else if isList o then
20 map filterRecursiveNull (filter (v: v != null) o)
21 else
22 o;
23
24 # remove null values from the final configuration
25 finalSettings = filterRecursiveNull cfg.settings;
26 configFile = format.generate "homeserver.yaml" finalSettings;
27
28 usePostgresql = cfg.settings.database.name == "psycopg2";
29 hasLocalPostgresDB =
30 let
31 args = cfg.settings.database.args;
32 in
33 usePostgresql
34 && (
35 !(args ? host)
36 || (elem args.host [
37 "localhost"
38 "127.0.0.1"
39 "::1"
40 ])
41 )
42 && config.services.postgresql.enable;
43 hasWorkers = cfg.workers != { };
44
45 listenerSupportsResource =
46 resource: listener: lib.any ({ names, ... }: builtins.elem resource names) listener.resources;
47
48 clientListener = findFirst (listenerSupportsResource "client") null (
49 cfg.settings.listeners
50 ++ concatMap ({ worker_listeners, ... }: worker_listeners) (attrValues cfg.workers)
51 );
52
53 registerNewMatrixUser =
54 let
55 isIpv6 = hasInfix ":";
56
57 # add a tail, so that without any bind_addresses we still have a useable address
58 bindAddress = head (clientListener.bind_addresses ++ [ "127.0.0.1" ]);
59 listenerProtocol = if clientListener.tls then "https" else "http";
60 in
61 assert assertMsg (
62 clientListener != null
63 ) "No client listener found in synapse or one of its workers";
64 pkgs.writeShellScriptBin "matrix-synapse-register_new_matrix_user" ''
65 exec ${cfg.package}/bin/register_new_matrix_user \
66 $@ \
67 ${lib.concatMapStringsSep " " (x: "-c ${x}") ([ configFile ] ++ cfg.extraConfigFiles)} \
68 "${listenerProtocol}://${
69 if (isIpv6 bindAddress) then "[${bindAddress}]" else "${bindAddress}"
70 }:${builtins.toString clientListener.port}/"
71 '';
72
73 defaultExtras = [
74 "systemd"
75 "postgres"
76 "url-preview"
77 "user-search"
78 ];
79
80 wantedExtras =
81 cfg.extras
82 ++ lib.optional (cfg.settings ? oidc_providers) "oidc"
83 ++ lib.optional (cfg.settings ? jwt_config) "jwt"
84 ++ lib.optional (cfg.settings ? saml2_config) "saml2"
85 ++ lib.optional (cfg.settings ? redis) "redis"
86 ++ lib.optional (cfg.settings ? sentry) "sentry"
87 ++ lib.optional (cfg.settings ? user_directory) "user-search"
88 ++ lib.optional (cfg.settings.url_preview_enabled) "url-preview"
89 ++ lib.optional (cfg.settings.database.name == "psycopg2") "postgres";
90
91 wrapped = pkgs.matrix-synapse.override {
92 extras = wantedExtras;
93 inherit (cfg) plugins;
94 };
95
96 defaultCommonLogConfig = {
97 version = 1;
98 formatters.journal_fmt.format = "%(name)s: [%(request)s] %(message)s";
99 handlers.journal = {
100 class = "systemd.journal.JournalHandler";
101 formatter = "journal_fmt";
102 };
103 root = {
104 level = "INFO";
105 handlers = [ "journal" ];
106 };
107 disable_existing_loggers = false;
108 };
109
110 defaultCommonLogConfigText = generators.toPretty { } defaultCommonLogConfig;
111
112 logConfigText =
113 logName:
114 lib.literalMD ''
115 Path to a yaml file generated from this Nix expression:
116
117 ```
118 ${generators.toPretty { } (
119 recursiveUpdate defaultCommonLogConfig { handlers.journal.SYSLOG_IDENTIFIER = logName; }
120 )}
121 ```
122 '';
123
124 genLogConfigFile =
125 logName:
126 format.generate "synapse-log-${logName}.yaml" (
127 cfg.log
128 // optionalAttrs (cfg.log ? handlers.journal) {
129 handlers.journal = cfg.log.handlers.journal // {
130 SYSLOG_IDENTIFIER = logName;
131 };
132 }
133 );
134
135 toIntBase8 =
136 str:
137 lib.pipe str [
138 lib.stringToCharacters
139 (map lib.toInt)
140 (lib.foldl (acc: digit: acc * 8 + digit) 0)
141 ];
142
143 toDecimalFilePermission = value: if value == null then null else toIntBase8 value;
144in
145{
146
147 imports = [
148
149 (mkRemovedOptionModule [ "services" "matrix-synapse" "trusted_third_party_id_servers" ] ''
150 The `trusted_third_party_id_servers` option as been removed in `matrix-synapse` v1.4.0
151 as the behavior is now obsolete.
152 '')
153 (mkRemovedOptionModule [ "services" "matrix-synapse" "create_local_database" ] ''
154 Database configuration must be done manually. An exemplary setup is demonstrated in
155 <nixpkgs/nixos/tests/matrix/synapse.nix>
156 '')
157 (mkRemovedOptionModule [ "services" "matrix-synapse" "web_client" ] "")
158 (mkRemovedOptionModule [ "services" "matrix-synapse" "room_invite_state_types" ] ''
159 You may add additional event types via
160 `services.matrix-synapse.room_prejoin_state.additional_event_types` and
161 disable the default events via
162 `services.matrix-synapse.room_prejoin_state.disable_default_event_types`.
163 '')
164
165 # options that don't exist in synapse anymore
166 (mkRemovedOptionModule [ "services" "matrix-synapse" "bind_host" ] "Use listener settings instead.")
167 (mkRemovedOptionModule [ "services" "matrix-synapse" "bind_port" ] "Use listener settings instead.")
168 (mkRemovedOptionModule [ "services" "matrix-synapse" "expire_access_tokens" ] "")
169 (mkRemovedOptionModule [
170 "services"
171 "matrix-synapse"
172 "no_tls"
173 ] "It is no longer supported by synapse.")
174 (mkRemovedOptionModule [
175 "services"
176 "matrix-synapse"
177 "tls_dh_param_path"
178 ] "It was removed from synapse.")
179 (mkRemovedOptionModule [
180 "services"
181 "matrix-synapse"
182 "unsecure_port"
183 ] "Use settings.listeners instead.")
184 (mkRemovedOptionModule [
185 "services"
186 "matrix-synapse"
187 "user_creation_max_duration"
188 ] "It is no longer supported by synapse.")
189 (mkRemovedOptionModule [ "services" "matrix-synapse" "verbose" ] "Use a log config instead.")
190
191 # options that were moved into rfc42 style settings
192 (mkRemovedOptionModule [
193 "services"
194 "matrix-synapse"
195 "app_service_config_files"
196 ] "Use settings.app_service_config_files instead")
197 (mkRemovedOptionModule [
198 "services"
199 "matrix-synapse"
200 "database_args"
201 ] "Use settings.database.args instead")
202 (mkRemovedOptionModule [
203 "services"
204 "matrix-synapse"
205 "database_name"
206 ] "Use settings.database.args.database instead")
207 (mkRemovedOptionModule [
208 "services"
209 "matrix-synapse"
210 "database_type"
211 ] "Use settings.database.name instead")
212 (mkRemovedOptionModule [
213 "services"
214 "matrix-synapse"
215 "database_user"
216 ] "Use settings.database.args.user instead")
217 (mkRemovedOptionModule [
218 "services"
219 "matrix-synapse"
220 "dynamic_thumbnails"
221 ] "Use settings.dynamic_thumbnails instead")
222 (mkRemovedOptionModule [
223 "services"
224 "matrix-synapse"
225 "enable_metrics"
226 ] "Use settings.enable_metrics instead")
227 (mkRemovedOptionModule [
228 "services"
229 "matrix-synapse"
230 "enable_registration"
231 ] "Use settings.enable_registration instead")
232 (mkRemovedOptionModule [ "services" "matrix-synapse" "extraConfig" ] "Use settings instead.")
233 (mkRemovedOptionModule [ "services" "matrix-synapse" "listeners" ] "Use settings.listeners instead")
234 (mkRemovedOptionModule [
235 "services"
236 "matrix-synapse"
237 "logConfig"
238 ] "Use settings.log_config instead")
239 (mkRemovedOptionModule [
240 "services"
241 "matrix-synapse"
242 "max_image_pixels"
243 ] "Use settings.max_image_pixels instead")
244 (mkRemovedOptionModule [
245 "services"
246 "matrix-synapse"
247 "max_upload_size"
248 ] "Use settings.max_upload_size instead")
249 (mkRemovedOptionModule [
250 "services"
251 "matrix-synapse"
252 "presence"
253 "enabled"
254 ] "Use settings.presence.enabled instead")
255 (mkRemovedOptionModule [
256 "services"
257 "matrix-synapse"
258 "public_baseurl"
259 ] "Use settings.public_baseurl instead")
260 (mkRemovedOptionModule [
261 "services"
262 "matrix-synapse"
263 "report_stats"
264 ] "Use settings.report_stats instead")
265 (mkRemovedOptionModule [
266 "services"
267 "matrix-synapse"
268 "server_name"
269 ] "Use settings.server_name instead")
270 (mkRemovedOptionModule [
271 "services"
272 "matrix-synapse"
273 "servers"
274 ] "Use settings.trusted_key_servers instead.")
275 (mkRemovedOptionModule [
276 "services"
277 "matrix-synapse"
278 "tls_certificate_path"
279 ] "Use settings.tls_certificate_path instead")
280 (mkRemovedOptionModule [
281 "services"
282 "matrix-synapse"
283 "tls_private_key_path"
284 ] "Use settings.tls_private_key_path instead")
285 (mkRemovedOptionModule [
286 "services"
287 "matrix-synapse"
288 "turn_shared_secret"
289 ] "Use settings.turn_shared_secret instead")
290 (mkRemovedOptionModule [ "services" "matrix-synapse" "turn_uris" ] "Use settings.turn_uris instead")
291 (mkRemovedOptionModule [
292 "services"
293 "matrix-synapse"
294 "turn_user_lifetime"
295 ] "Use settings.turn_user_lifetime instead")
296 (mkRemovedOptionModule [
297 "services"
298 "matrix-synapse"
299 "url_preview_enabled"
300 ] "Use settings.url_preview_enabled instead")
301 (mkRemovedOptionModule [
302 "services"
303 "matrix-synapse"
304 "url_preview_ip_range_blacklist"
305 ] "Use settings.url_preview_ip_range_blacklist instead")
306 (mkRemovedOptionModule [
307 "services"
308 "matrix-synapse"
309 "url_preview_ip_range_whitelist"
310 ] "Use settings.url_preview_ip_range_whitelist instead")
311 (mkRemovedOptionModule [
312 "services"
313 "matrix-synapse"
314 "url_preview_url_blacklist"
315 ] "Use settings.url_preview_url_blacklist instead")
316
317 # options that are too specific to mention them explicitly in settings
318 (mkRemovedOptionModule [
319 "services"
320 "matrix-synapse"
321 "account_threepid_delegates"
322 "email"
323 ] "Use settings.account_threepid_delegates.email instead")
324 (mkRemovedOptionModule [
325 "services"
326 "matrix-synapse"
327 "account_threepid_delegates"
328 "msisdn"
329 ] "Use settings.account_threepid_delegates.msisdn instead")
330 (mkRemovedOptionModule [
331 "services"
332 "matrix-synapse"
333 "allow_guest_access"
334 ] "Use settings.allow_guest_access instead")
335 (mkRemovedOptionModule [
336 "services"
337 "matrix-synapse"
338 "bcrypt_rounds"
339 ] "Use settings.bcrypt_rounds instead")
340 (mkRemovedOptionModule [
341 "services"
342 "matrix-synapse"
343 "enable_registration_captcha"
344 ] "Use settings.enable_registration_captcha instead")
345 (mkRemovedOptionModule [
346 "services"
347 "matrix-synapse"
348 "event_cache_size"
349 ] "Use settings.event_cache_size instead")
350 (mkRemovedOptionModule [
351 "services"
352 "matrix-synapse"
353 "federation_rc_concurrent"
354 ] "Use settings.rc_federation.concurrent instead")
355 (mkRemovedOptionModule [
356 "services"
357 "matrix-synapse"
358 "federation_rc_reject_limit"
359 ] "Use settings.rc_federation.reject_limit instead")
360 (mkRemovedOptionModule [
361 "services"
362 "matrix-synapse"
363 "federation_rc_sleep_delay"
364 ] "Use settings.rc_federation.sleep_delay instead")
365 (mkRemovedOptionModule [
366 "services"
367 "matrix-synapse"
368 "federation_rc_sleep_limit"
369 ] "Use settings.rc_federation.sleep_limit instead")
370 (mkRemovedOptionModule [
371 "services"
372 "matrix-synapse"
373 "federation_rc_window_size"
374 ] "Use settings.rc_federation.window_size instead")
375 (mkRemovedOptionModule [
376 "services"
377 "matrix-synapse"
378 "key_refresh_interval"
379 ] "Use settings.key_refresh_interval instead")
380 (mkRemovedOptionModule [
381 "services"
382 "matrix-synapse"
383 "rc_messages_burst_count"
384 ] "Use settings.rc_messages.burst_count instead")
385 (mkRemovedOptionModule [
386 "services"
387 "matrix-synapse"
388 "rc_messages_per_second"
389 ] "Use settings.rc_messages.per_second instead")
390 (mkRemovedOptionModule [
391 "services"
392 "matrix-synapse"
393 "recaptcha_private_key"
394 ] "Use settings.recaptcha_private_key instead")
395 (mkRemovedOptionModule [
396 "services"
397 "matrix-synapse"
398 "recaptcha_public_key"
399 ] "Use settings.recaptcha_public_key instead")
400 (mkRemovedOptionModule [
401 "services"
402 "matrix-synapse"
403 "redaction_retention_period"
404 ] "Use settings.redaction_retention_period instead")
405 (mkRemovedOptionModule [
406 "services"
407 "matrix-synapse"
408 "room_prejoin_state"
409 "additional_event_types"
410 ] "Use settings.room_prejoin_state.additional_event_types instead")
411 (mkRemovedOptionModule [
412 "services"
413 "matrix-synapse"
414 "room_prejoin_state"
415 "disable_default_event_types"
416 ] "Use settings.room_prejoin-state.disable_default_event_types instead")
417
418 # Options that should be passed via extraConfigFiles, so they are not persisted into the nix store
419 (mkRemovedOptionModule [
420 "services"
421 "matrix-synapse"
422 "macaroon_secret_key"
423 ] "Pass this value via extraConfigFiles instead")
424 (mkRemovedOptionModule [
425 "services"
426 "matrix-synapse"
427 "registration_shared_secret"
428 ] "Pass this value via extraConfigFiles instead")
429
430 ];
431
432 options =
433 let
434 listenerType =
435 workerContext:
436 types.submodule (
437 { config, ... }:
438 {
439 options = {
440 port = mkOption {
441 type = types.nullOr types.port;
442 default = null;
443 example = 8448;
444 description = ''
445 The port to listen for HTTP(S) requests on.
446 '';
447 };
448
449 bind_addresses = mkOption {
450 type = types.nullOr (types.listOf types.str);
451 default =
452 if config.path != null then
453 null
454 else
455 [
456 "::1"
457 "127.0.0.1"
458 ];
459 defaultText = literalExpression ''
460 if path != null then
461 null
462 else
463 [
464 "::1"
465 "127.0.0.1"
466 ]
467 '';
468 example = literalExpression ''
469 [
470 "::"
471 "0.0.0.0"
472 ]
473 '';
474 description = ''
475 IP addresses to bind the listener to.
476 '';
477 };
478
479 path = mkOption {
480 type = types.nullOr types.path;
481 default = null;
482 description = ''
483 Unix domain socket path to bind this listener to.
484
485 ::: {.note}
486 This option is incompatible with {option}`bind_addresses`, {option}`port`, {option}`tls`
487 and also does not support the `metrics` and `manhole` listener {option}`type`.
488 :::
489 '';
490 };
491
492 mode = mkOption {
493 type = types.nullOr (types.strMatching "^[0,2-7]{3,4}$");
494 default = if config.path != null then "660" else null;
495 defaultText = literalExpression ''
496 if path != null then
497 "660"
498 else
499 null
500 '';
501 example = "660";
502 description = ''
503 File permissions on the UNIX domain socket.
504 '';
505 apply = toDecimalFilePermission;
506 };
507
508 type = mkOption {
509 type = types.enum [
510 "http"
511 "manhole"
512 "metrics"
513 "replication"
514 ];
515 default = "http";
516 example = "metrics";
517 description = ''
518 The type of the listener, usually http.
519 '';
520 };
521
522 tls = mkOption {
523 type = types.nullOr types.bool;
524 default = if config.path != null then null else !workerContext;
525 defaultText = ''
526 Enabled for the main instance listener, unless it is configured with a UNIX domain socket path.
527 '';
528 example = false;
529 description = ''
530 Whether to enable TLS on the listener socket.
531
532 ::: {.note}
533 This option will be ignored for UNIX domain sockets.
534 :::
535 '';
536 };
537
538 x_forwarded = mkOption {
539 type = types.bool;
540 default = config.path != null;
541 defaultText = ''
542 Enabled if the listener is configured with a UNIX domain socket path
543 '';
544 example = true;
545 description = ''
546 Use the X-Forwarded-For (XFF) header as the client IP and not the
547 actual client IP.
548 '';
549 };
550
551 resources = mkOption {
552 type = types.listOf (
553 types.submodule {
554 options = {
555 names = mkOption {
556 type = types.listOf (
557 types.enum [
558 "client"
559 "consent"
560 "federation"
561 "health"
562 "keys"
563 "media"
564 "metrics"
565 "openid"
566 "replication"
567 "static"
568 ]
569 );
570 description = ''
571 List of resources to host on this listener.
572 '';
573 example = [
574 "client"
575 ];
576 };
577 compress = mkOption {
578 default = false;
579 type = types.bool;
580 description = ''
581 Whether synapse should compress HTTP responses to clients that support it.
582 This should be disabled if running synapse behind a load balancer
583 that can do automatic compression.
584 '';
585 };
586 };
587 }
588 );
589 description = ''
590 List of HTTP resources to serve on this listener.
591 '';
592 };
593 };
594 }
595 );
596 in
597 {
598 services.matrix-synapse = {
599 enable = mkEnableOption "matrix.org synapse, the reference homeserver";
600
601 enableRegistrationScript = mkOption {
602 type = types.bool;
603 default = clientListener.bind_addresses != [ ];
604 example = false;
605 defaultText = ''
606 Enabled if the client listener uses TCP sockets
607 '';
608 description = ''
609 Whether to install the `register_new_matrix_user` script, that
610 allows account creation on the terminal.
611
612 ::: {.note}
613 This script does not work when the client listener uses UNIX domain sockets
614 :::
615 '';
616 };
617
618 serviceUnit = lib.mkOption {
619 type = lib.types.str;
620 readOnly = true;
621 description = ''
622 The systemd unit (a service or a target) for other services to depend on if they
623 need to be started after matrix-synapse.
624
625 This option is useful as the actual parent unit for all matrix-synapse processes
626 changes when configuring workers.
627 '';
628 };
629
630 configFile = mkOption {
631 type = types.path;
632 readOnly = true;
633 description = ''
634 Path to the configuration file on the target system. Useful to configure e.g. workers
635 that also need this.
636 '';
637 };
638
639 package = mkOption {
640 type = types.package;
641 readOnly = true;
642 description = ''
643 Reference to the `matrix-synapse` wrapper with all extras
644 (e.g. for `oidc` or `saml2`) added to the `PYTHONPATH` of all executables.
645
646 This option is useful to reference the "final" `matrix-synapse` package that's
647 actually used by `matrix-synapse.service`. For instance, when using
648 workers, it's possible to run
649 `''${config.services.matrix-synapse.package}/bin/synapse_worker` and
650 no additional PYTHONPATH needs to be specified for extras or plugins configured
651 via `services.matrix-synapse`.
652
653 However, this means that this option is supposed to be only declared
654 by the `services.matrix-synapse` module itself and is thus read-only.
655 In order to modify `matrix-synapse` itself, use an overlay to override
656 `pkgs.matrix-synapse-unwrapped`.
657 '';
658 };
659
660 extras = mkOption {
661 type = types.listOf (
662 types.enum (lib.attrNames pkgs.matrix-synapse-unwrapped.optional-dependencies)
663 );
664 default = defaultExtras;
665 example = literalExpression ''
666 [
667 "cache-memory" # Provide statistics about caching memory consumption
668 "jwt" # JSON Web Token authentication
669 "oidc" # OpenID Connect authentication
670 "postgres" # PostgreSQL database backend
671 "redis" # Redis support for the replication stream between worker processes
672 "saml2" # SAML2 authentication
673 "sentry" # Error tracking and performance metrics
674 "systemd" # Provide the JournalHandler used in the default log_config
675 "url-preview" # Support for oEmbed URL previews
676 "user-search" # Support internationalized domain names in user-search
677 ]
678 '';
679 description = ''
680 Explicitly install extras provided by matrix-synapse. Most
681 will require some additional configuration.
682
683 Extras will automatically be enabled, when the relevant
684 configuration sections are present.
685
686 Please note that this option is additive: i.e. when adding a new item
687 to this list, the defaults are still kept. To override the defaults as well,
688 use `lib.mkForce`.
689 '';
690 };
691
692 plugins = mkOption {
693 type = types.listOf types.package;
694 default = [ ];
695 example = literalExpression ''
696 with config.services.matrix-synapse.package.plugins; [
697 matrix-synapse-ldap3
698 matrix-synapse-pam
699 ];
700 '';
701 description = ''
702 List of additional Matrix plugins to make available.
703 '';
704 };
705
706 withJemalloc = mkOption {
707 type = types.bool;
708 default = true;
709 description = ''
710 Whether to preload jemalloc to reduce memory fragmentation and overall usage.
711 '';
712 };
713
714 dataDir = mkOption {
715 type = types.str;
716 default = "/var/lib/matrix-synapse";
717 description = ''
718 The directory where matrix-synapse stores its stateful data such as
719 certificates, media and uploads.
720 '';
721 };
722
723 log = mkOption {
724 type = types.attrsOf format.type;
725 defaultText = literalExpression defaultCommonLogConfigText;
726 description = ''
727 Default configuration for the loggers used by `matrix-synapse` and its workers.
728 The defaults are added with the default priority which means that
729 these will be merged with additional declarations. These additional
730 declarations also take precedence over the defaults when declared
731 with at least normal priority. For instance
732 the log-level for synapse and its workers can be changed like this:
733
734 ```nix
735 { lib, ... }: {
736 services.matrix-synapse.log.root.level = "WARNING";
737 }
738 ```
739
740 And another field can be added like this:
741
742 ```nix
743 {
744 services.matrix-synapse.log = {
745 loggers."synapse.http.matrixfederationclient".level = "DEBUG";
746 };
747 }
748 ```
749
750 Additionally, the field `handlers.journal.SYSLOG_IDENTIFIER` will be added to
751 each log config, i.e.
752 * `synapse` for `matrix-synapse.service`
753 * `synapse-<worker name>` for `matrix-synapse-worker-<worker name>.service`
754
755 This is only done if this option has a `handlers.journal` field declared.
756
757 To discard all settings declared by this option for each worker and synapse,
758 `lib.mkForce` can be used.
759
760 To discard all settings declared by this option for a single worker or synapse only,
761 [](#opt-services.matrix-synapse.workers._name_.worker_log_config) or
762 [](#opt-services.matrix-synapse.settings.log_config) can be used.
763 '';
764 };
765
766 settings = mkOption {
767 default = { };
768 description = ''
769 The primary synapse configuration. See the
770 [sample configuration](https://github.com/element-hq/synapse/blob/v${pkgs.matrix-synapse-unwrapped.version}/docs/sample_config.yaml)
771 for possible values.
772
773 Secrets should be passed in by using the `extraConfigFiles` option.
774 '';
775 type =
776 with types;
777 submodule {
778 freeformType = format.type;
779 options = {
780 # This is a reduced set of popular options and defaults
781 # Do not add every available option here, they can be specified
782 # by the user at their own discretion. This is a freeform type!
783
784 server_name = mkOption {
785 type = types.str;
786 example = "example.com";
787 default = config.networking.hostName;
788 defaultText = literalExpression "config.networking.hostName";
789 description = ''
790 The domain name of the server, with optional explicit port.
791 This is used by remote servers to look up the server address.
792 This is also the last part of your UserID.
793
794 The server_name cannot be changed later so it is important to configure this correctly before you start Synapse.
795 '';
796 };
797
798 enable_registration = mkOption {
799 type = types.bool;
800 default = false;
801 description = ''
802 Enable registration for new users.
803 '';
804 };
805
806 registration_shared_secret = mkOption {
807 type = types.nullOr types.str;
808 default = null;
809 description = ''
810 If set, allows registration by anyone who also has the shared
811 secret, even if registration is otherwise disabled.
812
813 Secrets should be passed in via `extraConfigFiles`!
814 '';
815 };
816
817 macaroon_secret_key = mkOption {
818 type = types.nullOr types.str;
819 default = null;
820 description = ''
821 Secret key for authentication tokens. If none is specified,
822 the registration_shared_secret is used, if one is given; otherwise,
823 a secret key is derived from the signing key.
824
825 Secrets should be passed in via `extraConfigFiles`!
826 '';
827 };
828
829 enable_metrics = mkOption {
830 type = types.bool;
831 default = false;
832 description = ''
833 Enable collection and rendering of performance metrics
834 '';
835 };
836
837 report_stats = mkOption {
838 type = types.bool;
839 default = false;
840 description = ''
841 Whether or not to report anonymized homeserver usage statistics.
842 '';
843 };
844
845 signing_key_path = mkOption {
846 type = types.path;
847 default = "${cfg.dataDir}/homeserver.signing.key";
848 description = ''
849 Path to the signing key to sign messages with.
850 '';
851 };
852
853 pid_file = mkOption {
854 type = types.path;
855 default = "/run/matrix-synapse.pid";
856 readOnly = true;
857 description = ''
858 The file to store the PID in.
859 '';
860 };
861
862 log_config = mkOption {
863 type = types.path;
864 default = genLogConfigFile "synapse";
865 defaultText = logConfigText "synapse";
866 description = ''
867 The file that holds the logging configuration.
868 '';
869 };
870
871 media_store_path = mkOption {
872 type = types.path;
873 default =
874 if lib.versionAtLeast config.system.stateVersion "22.05" then
875 "${cfg.dataDir}/media_store"
876 else
877 "${cfg.dataDir}/media";
878 defaultText = "${cfg.dataDir}/media_store for when system.stateVersion is at least 22.05, ${cfg.dataDir}/media when lower than 22.05";
879 description = ''
880 Directory where uploaded images and attachments are stored.
881 '';
882 };
883
884 public_baseurl = mkOption {
885 type = types.nullOr types.str;
886 default = null;
887 example = "https://example.com:8448/";
888 description = ''
889 The public-facing base URL for the client API (not including _matrix/...)
890 '';
891 };
892
893 tls_certificate_path = mkOption {
894 type = types.nullOr types.str;
895 default = null;
896 example = "/var/lib/acme/example.com/fullchain.pem";
897 description = ''
898 PEM encoded X509 certificate for TLS.
899 You can replace the self-signed certificate that synapse
900 autogenerates on launch with your own SSL certificate + key pair
901 if you like. Any required intermediary certificates can be
902 appended after the primary certificate in hierarchical order.
903 '';
904 };
905
906 tls_private_key_path = mkOption {
907 type = types.nullOr types.str;
908 default = null;
909 example = "/var/lib/acme/example.com/key.pem";
910 description = ''
911 PEM encoded private key for TLS. Specify null if synapse is not
912 speaking TLS directly.
913 '';
914 };
915
916 presence.enabled = mkOption {
917 type = types.bool;
918 default = true;
919 example = false;
920 description = ''
921 Whether to enable presence tracking.
922
923 Presence tracking allows users to see the state (e.g online/offline)
924 of other local and remote users.
925 '';
926 };
927
928 listeners = mkOption {
929 type = types.listOf (listenerType false);
930 default =
931 [
932 {
933 port = 8008;
934 bind_addresses = [ "127.0.0.1" ];
935 type = "http";
936 tls = false;
937 x_forwarded = true;
938 resources = [
939 {
940 names = [ "client" ];
941 compress = true;
942 }
943 {
944 names = [ "federation" ];
945 compress = false;
946 }
947 ];
948 }
949 ]
950 ++ lib.optional hasWorkers {
951 path = "/run/matrix-synapse/main_replication.sock";
952 type = "http";
953 resources = [
954 {
955 names = [ "replication" ];
956 compress = false;
957 }
958 ];
959 };
960 description = ''
961 List of ports that Synapse should listen on, their purpose and their configuration.
962
963 By default, synapse will be configured for client and federation traffic on port 8008, and
964 use a UNIX domain socket for worker replication. See [`services.matrix-synapse.workers`](#opt-services.matrix-synapse.workers)
965 for more details.
966 '';
967 };
968
969 database.name = mkOption {
970 type = types.enum [
971 "sqlite3"
972 "psycopg2"
973 ];
974 default = if versionAtLeast config.system.stateVersion "18.03" then "psycopg2" else "sqlite3";
975 defaultText = literalExpression ''
976 if versionAtLeast config.system.stateVersion "18.03"
977 then "psycopg2"
978 else "sqlite3"
979 '';
980 description = ''
981 The database engine name. Can be sqlite3 or psycopg2.
982 '';
983 };
984
985 database.args.database = mkOption {
986 type = types.str;
987 default =
988 {
989 sqlite3 = "${cfg.dataDir}/homeserver.db";
990 psycopg2 = "matrix-synapse";
991 }
992 .${cfg.settings.database.name};
993 defaultText = literalExpression ''
994 {
995 sqlite3 = "''${${options.services.matrix-synapse.dataDir}}/homeserver.db";
996 psycopg2 = "matrix-synapse";
997 }.''${${options.services.matrix-synapse.settings}.database.name};
998 '';
999 description = ''
1000 Name of the database when using the psycopg2 backend,
1001 path to the database location when using sqlite3.
1002 '';
1003 };
1004
1005 database.args.user = mkOption {
1006 type = types.nullOr types.str;
1007 default =
1008 {
1009 sqlite3 = null;
1010 psycopg2 = "matrix-synapse";
1011 }
1012 .${cfg.settings.database.name};
1013 defaultText = lib.literalExpression ''
1014 {
1015 sqlite3 = null;
1016 psycopg2 = "matrix-synapse";
1017 }.''${cfg.settings.database.name};
1018 '';
1019 description = ''
1020 Username to connect with psycopg2, set to null
1021 when using sqlite3.
1022 '';
1023 };
1024
1025 url_preview_enabled = mkOption {
1026 type = types.bool;
1027 default = true;
1028 example = false;
1029 description = ''
1030 Is the preview URL API enabled? If enabled, you *must* specify an
1031 explicit url_preview_ip_range_blacklist of IPs that the spider is
1032 denied from accessing.
1033 '';
1034 };
1035
1036 url_preview_ip_range_blacklist = mkOption {
1037 type = types.listOf types.str;
1038 default = [
1039 "10.0.0.0/8"
1040 "100.64.0.0/10"
1041 "127.0.0.0/8"
1042 "169.254.0.0/16"
1043 "172.16.0.0/12"
1044 "192.0.0.0/24"
1045 "192.0.2.0/24"
1046 "192.168.0.0/16"
1047 "192.88.99.0/24"
1048 "198.18.0.0/15"
1049 "198.51.100.0/24"
1050 "2001:db8::/32"
1051 "203.0.113.0/24"
1052 "224.0.0.0/4"
1053 "::1/128"
1054 "fc00::/7"
1055 "fe80::/10"
1056 "fec0::/10"
1057 "ff00::/8"
1058 ];
1059 description = ''
1060 List of IP address CIDR ranges that the URL preview spider is denied
1061 from accessing.
1062 '';
1063 };
1064
1065 url_preview_ip_range_whitelist = mkOption {
1066 type = types.listOf types.str;
1067 default = [ ];
1068 description = ''
1069 List of IP address CIDR ranges that the URL preview spider is allowed
1070 to access even if they are specified in url_preview_ip_range_blacklist.
1071 '';
1072 };
1073
1074 url_preview_url_blacklist = mkOption {
1075 # FIXME revert to just `listOf (attrsOf str)` after some time(tm).
1076 type = types.listOf (
1077 types.coercedTo types.str (const (throw ''
1078 Setting `config.services.matrix-synapse.settings.url_preview_url_blacklist`
1079 to a list of strings has never worked. Due to a bug, this was the type accepted
1080 by the module, but in practice it broke on runtime and as a result, no URL
1081 preview worked anywhere if this was set.
1082
1083 See https://element-hq.github.io/synapse/latest/usage/configuration/config_documentation.html#url_preview_url_blacklist
1084 on how to configure it properly.
1085 '')) (types.attrsOf types.str)
1086 );
1087 default = [ ];
1088 example = literalExpression ''
1089 [
1090 { scheme = "http"; } # no http previews
1091 { netloc = "www.acme.com"; path = "/foo"; } # block http(s)://www.acme.com/foo
1092 ]
1093 '';
1094 description = ''
1095 Optional list of URL matches that the URL preview spider is
1096 denied from accessing.
1097 '';
1098 };
1099
1100 max_upload_size = mkOption {
1101 type = types.str;
1102 default = "50M";
1103 example = "100M";
1104 description = ''
1105 The largest allowed upload size in bytes
1106 '';
1107 };
1108
1109 max_image_pixels = mkOption {
1110 type = types.str;
1111 default = "32M";
1112 example = "64M";
1113 description = ''
1114 Maximum number of pixels that will be thumbnailed
1115 '';
1116 };
1117
1118 dynamic_thumbnails = mkOption {
1119 type = types.bool;
1120 default = false;
1121 example = true;
1122 description = ''
1123 Whether to generate new thumbnails on the fly to precisely match
1124 the resolution requested by the client. If true then whenever
1125 a new resolution is requested by the client the server will
1126 generate a new thumbnail. If false the server will pick a thumbnail
1127 from a precalculated list.
1128 '';
1129 };
1130
1131 turn_uris = mkOption {
1132 type = types.listOf types.str;
1133 default = [ ];
1134 example = [
1135 "turn:turn.example.com:3487?transport=udp"
1136 "turn:turn.example.com:3487?transport=tcp"
1137 "turns:turn.example.com:5349?transport=udp"
1138 "turns:turn.example.com:5349?transport=tcp"
1139 ];
1140 description = ''
1141 The public URIs of the TURN server to give to clients
1142 '';
1143 };
1144 turn_shared_secret = mkOption {
1145 type = types.str;
1146 default = "";
1147 example = literalExpression ''
1148 config.services.coturn.static-auth-secret
1149 '';
1150 description = ''
1151 The shared secret used to compute passwords for the TURN server.
1152
1153 Secrets should be passed in via `extraConfigFiles`!
1154 '';
1155 };
1156
1157 trusted_key_servers = mkOption {
1158 type = types.listOf (
1159 types.submodule {
1160 freeformType = format.type;
1161 options = {
1162 server_name = mkOption {
1163 type = types.str;
1164 example = "matrix.org";
1165 description = ''
1166 Hostname of the trusted server.
1167 '';
1168 };
1169 };
1170 }
1171 );
1172 default = [
1173 {
1174 server_name = "matrix.org";
1175 verify_keys = {
1176 "ed25519:auto" = "Noi6WqcDj0QmPxCNQqgezwTlBKrfqehY1u2FyWP9uYw";
1177 };
1178 }
1179 ];
1180 description = ''
1181 The trusted servers to download signing keys from.
1182 '';
1183 };
1184
1185 app_service_config_files = mkOption {
1186 type = types.listOf types.path;
1187 default = [ ];
1188 description = ''
1189 A list of application service config file to use
1190 '';
1191 };
1192
1193 redis = lib.mkOption {
1194 type = types.submodule {
1195 freeformType = format.type;
1196 options = {
1197 enabled = lib.mkOption {
1198 type = types.bool;
1199 default = false;
1200 description = ''
1201 Whether to use redis support
1202 '';
1203 };
1204 };
1205 };
1206 default = { };
1207 description = ''
1208 Redis configuration for synapse.
1209
1210 See the
1211 [upstream documentation](https://github.com/element-hq/synapse/blob/v${pkgs.matrix-synapse-unwrapped.version}/docs/usage/configuration/config_documentation.md#redis)
1212 for available options.
1213 '';
1214 };
1215 };
1216 };
1217 };
1218
1219 workers = lib.mkOption {
1220 default = { };
1221 description = ''
1222 Options for configuring workers. Worker support will be enabled if at least one worker is configured here.
1223
1224 See the [worker documention](https://element-hq.github.io/synapse/latest/workers.html#worker-configuration)
1225 for possible options for each worker. Worker-specific options overriding the shared homeserver configuration can be
1226 specified here for each worker.
1227
1228 ::: {.note}
1229 Worker support will add a replication listener on port 9093 to the main synapse process using the default
1230 value of [`services.matrix-synapse.settings.listeners`](#opt-services.matrix-synapse.settings.listeners) and configure that
1231 listener as `services.matrix-synapse.settings.instance_map.main`.
1232 If you set either of those options, make sure to configure a replication listener yourself.
1233
1234 A redis server is required for running workers. A local one can be enabled
1235 using [`services.matrix-synapse.configureRedisLocally`](#opt-services.matrix-synapse.configureRedisLocally).
1236
1237 Workers also require a proper reverse proxy setup to direct incoming requests to the appropriate process. See
1238 the [reverse proxy documentation](https://element-hq.github.io/synapse/latest/reverse_proxy.html) for a
1239 general reverse proxying setup and
1240 the [worker documentation](https://element-hq.github.io/synapse/latest/workers.html#available-worker-applications)
1241 for the available endpoints per worker application.
1242 :::
1243 '';
1244 type = types.attrsOf (
1245 types.submodule (
1246 { name, ... }:
1247 {
1248 freeformType = format.type;
1249 options = {
1250 worker_app = lib.mkOption {
1251 type = types.enum [
1252 "synapse.app.generic_worker"
1253 "synapse.app.media_repository"
1254 ];
1255 description = "Type of this worker";
1256 default = "synapse.app.generic_worker";
1257 };
1258 worker_listeners = lib.mkOption {
1259 default = [ ];
1260 type = types.listOf (listenerType true);
1261 description = ''
1262 List of ports that this worker should listen on, their purpose and their configuration.
1263 '';
1264 };
1265 worker_log_config = lib.mkOption {
1266 type = types.path;
1267 default = genLogConfigFile "synapse-${name}";
1268 defaultText = logConfigText "synapse-${name}";
1269 description = ''
1270 The file for log configuration.
1271
1272 See the [python documentation](https://docs.python.org/3/library/logging.config.html#configuration-dictionary-schema)
1273 for the schema and the [upstream repository](https://github.com/element-hq/synapse/blob/v${pkgs.matrix-synapse-unwrapped.version}/docs/sample_log_config.yaml)
1274 for an example.
1275 '';
1276 };
1277 };
1278 }
1279 )
1280 );
1281 default = { };
1282 example = lib.literalExpression ''
1283 {
1284 "federation_sender" = { };
1285 "federation_receiver" = {
1286 worker_listeners = [
1287 {
1288 type = "http";
1289 port = 8009;
1290 bind_addresses = [ "127.0.0.1" ];
1291 tls = false;
1292 x_forwarded = true;
1293 resources = [{
1294 names = [ "federation" ];
1295 }];
1296 }
1297 ];
1298 };
1299 }
1300 '';
1301 };
1302
1303 extraConfigFiles = mkOption {
1304 type = types.listOf types.path;
1305 default = [ ];
1306 description = ''
1307 Extra config files to include.
1308
1309 The configuration files will be included based on the command line
1310 argument --config-path. This allows to configure secrets without
1311 having to go through the Nix store, e.g. based on deployment keys if
1312 NixOps is in use.
1313 '';
1314 };
1315
1316 configureRedisLocally = lib.mkOption {
1317 type = types.bool;
1318 default = false;
1319 description = ''
1320 Whether to automatically configure a local redis server for matrix-synapse.
1321 '';
1322 };
1323 };
1324 };
1325
1326 config = mkIf cfg.enable {
1327 assertions =
1328 [
1329 {
1330 assertion = clientListener != null;
1331 message = ''
1332 At least one listener which serves the `client` resource via HTTP is required
1333 by synapse in `services.matrix-synapse.settings.listeners` or in one of the workers!
1334 '';
1335 }
1336 {
1337 assertion = hasWorkers -> cfg.settings.redis.enabled;
1338 message = ''
1339 Workers for matrix-synapse require configuring a redis instance. This can be done
1340 automatically by setting `services.matrix-synapse.configureRedisLocally = true`.
1341 '';
1342 }
1343 {
1344 assertion =
1345 let
1346 main = cfg.settings.instance_map.main;
1347 listener = lib.findFirst (
1348 listener:
1349 (
1350 lib.hasAttr "port" main && listener.port or null == main.port
1351 || lib.hasAttr "path" main && listener.path or null == main.path
1352 )
1353 && listenerSupportsResource "replication" listener
1354 && (
1355 lib.hasAttr "host" main
1356 && lib.any (bind: bind == main.host || bind == "0.0.0.0" || bind == "::") listener.bind_addresses
1357 || lib.hasAttr "path" main
1358 )
1359 ) null cfg.settings.listeners;
1360 in
1361 hasWorkers -> (cfg.settings.instance_map ? main && listener != null);
1362 message = ''
1363 Workers for matrix-synapse require setting `services.matrix-synapse.settings.instance_map.main`
1364 to any listener configured in `services.matrix-synapse.settings.listeners` with a `"replication"`
1365 resource.
1366
1367 This is done by default unless you manually configure either of those settings.
1368 '';
1369 }
1370 {
1371 assertion = cfg.enableRegistrationScript -> clientListener.path == null;
1372 message = ''
1373 The client listener on matrix-synapse is configured to use UNIX domain sockets.
1374 This configuration is incompatible with the `register_new_matrix_user` script.
1375
1376 Disable `services.matrix-synapse.enableRegistrationScript` to continue.
1377 '';
1378 }
1379 ]
1380 ++ (map (listener: {
1381 assertion = (listener.path == null) != (listener.bind_addresses == null);
1382 message = ''
1383 Listeners require either a UNIX domain socket `path` or `bind_addresses` for a TCP socket.
1384 '';
1385 }) cfg.settings.listeners)
1386 ++ (map (listener: {
1387 assertion =
1388 listener.path != null
1389 -> (listener.bind_addresses == null && listener.port == null && listener.tls == null);
1390 message =
1391 let
1392 formatKeyValue = key: value: lib.optionalString (value != null) " - ${key}=${toString value}\n";
1393 in
1394 ''
1395 Listener configured with UNIX domain socket (${toString listener.path}) ignores the following options:
1396 ${formatKeyValue "bind_addresses" listener.bind_addresses}${formatKeyValue "port" listener.port}${formatKeyValue "tls" listener.tls}
1397 '';
1398 }) cfg.settings.listeners)
1399 ++ (map (listener: {
1400 assertion = listener.path == null || listener.type == "http";
1401 message = ''
1402 Listener configured with UNIX domain socket (${toString listener.path}) only supports the "http" listener type.
1403 '';
1404 }) cfg.settings.listeners);
1405
1406 services.matrix-synapse.settings.redis = lib.mkIf cfg.configureRedisLocally {
1407 enabled = true;
1408 path = config.services.redis.servers.matrix-synapse.unixSocket;
1409 };
1410 services.matrix-synapse.settings.instance_map.main = lib.mkIf hasWorkers (
1411 lib.mkDefault {
1412 path = "/run/matrix-synapse/main_replication.sock";
1413 }
1414 );
1415
1416 services.matrix-synapse.serviceUnit =
1417 if hasWorkers then "matrix-synapse.target" else "matrix-synapse.service";
1418 services.matrix-synapse.configFile = configFile;
1419 services.matrix-synapse.package = wrapped;
1420
1421 # default them, so they are additive
1422 services.matrix-synapse.extras = defaultExtras;
1423
1424 services.matrix-synapse.log = mapAttrsRecursive (const mkDefault) defaultCommonLogConfig;
1425
1426 users.users.matrix-synapse = {
1427 group = "matrix-synapse";
1428 home = cfg.dataDir;
1429 createHome = true;
1430 shell = "${pkgs.bash}/bin/bash";
1431 uid = config.ids.uids.matrix-synapse;
1432 };
1433
1434 users.groups.matrix-synapse = {
1435 gid = config.ids.gids.matrix-synapse;
1436 };
1437
1438 systemd.targets.matrix-synapse = lib.mkIf hasWorkers {
1439 description = "Synapse Matrix parent target";
1440 wants = [ "network-online.target" ];
1441 after = [ "network-online.target" ] ++ optional hasLocalPostgresDB "postgresql.service";
1442 wantedBy = [ "multi-user.target" ];
1443 };
1444
1445 systemd.services =
1446 let
1447 targetConfig =
1448 if hasWorkers then
1449 {
1450 partOf = [ "matrix-synapse.target" ];
1451 wantedBy = [ "matrix-synapse.target" ];
1452 unitConfig.ReloadPropagatedFrom = "matrix-synapse.target";
1453 requires = optional hasLocalPostgresDB "postgresql.service";
1454 }
1455 else
1456 {
1457 wants = [ "network-online.target" ];
1458 after = [ "network-online.target" ] ++ optional hasLocalPostgresDB "postgresql.service";
1459 requires = optional hasLocalPostgresDB "postgresql.service";
1460 wantedBy = [ "multi-user.target" ];
1461 };
1462 baseServiceConfig = {
1463 environment = optionalAttrs (cfg.withJemalloc) {
1464 LD_PRELOAD = "${pkgs.jemalloc}/lib/libjemalloc.so";
1465 PYTHONMALLOC = "malloc";
1466 };
1467 serviceConfig = {
1468 Type = "notify";
1469 User = "matrix-synapse";
1470 Group = "matrix-synapse";
1471 WorkingDirectory = cfg.dataDir;
1472 RuntimeDirectory = "matrix-synapse";
1473 RuntimeDirectoryPreserve = true;
1474 ExecReload = "${pkgs.util-linux}/bin/kill -HUP $MAINPID";
1475 Restart = "on-failure";
1476 UMask = "0077";
1477
1478 # Security Hardening
1479 # Refer to systemd.exec(5) for option descriptions.
1480 CapabilityBoundingSet = [ "" ];
1481 LockPersonality = true;
1482 NoNewPrivileges = true;
1483 PrivateDevices = true;
1484 PrivateTmp = true;
1485 PrivateUsers = true;
1486 ProcSubset = "pid";
1487 ProtectClock = true;
1488 ProtectControlGroups = true;
1489 ProtectHome = true;
1490 ProtectHostname = true;
1491 ProtectKernelLogs = true;
1492 ProtectKernelModules = true;
1493 ProtectKernelTunables = true;
1494 ProtectProc = "invisible";
1495 ProtectSystem = "strict";
1496 ReadWritePaths =
1497 [
1498 cfg.dataDir
1499 cfg.settings.media_store_path
1500 ]
1501 ++ (map (listener: dirOf listener.path) (
1502 filter (listener: listener.path != null) cfg.settings.listeners
1503 ));
1504 RemoveIPC = true;
1505 RestrictAddressFamilies = [
1506 "AF_INET"
1507 "AF_INET6"
1508 "AF_UNIX"
1509 ];
1510 RestrictNamespaces = true;
1511 RestrictRealtime = true;
1512 RestrictSUIDSGID = true;
1513 SystemCallArchitectures = "native";
1514 SystemCallFilter = [
1515 "@system-service"
1516 "~@resources"
1517 "~@privileged"
1518 ];
1519 };
1520 } // targetConfig;
1521 genWorkerService =
1522 name: workerCfg:
1523 let
1524 finalWorkerCfg = workerCfg // {
1525 worker_name = name;
1526 };
1527 workerConfigFile = format.generate "worker-${name}.yaml" finalWorkerCfg;
1528 in
1529 {
1530 name = "matrix-synapse-worker-${name}";
1531 value = lib.mkMerge [
1532 baseServiceConfig
1533 {
1534 description = "Synapse Matrix worker ${name}";
1535 # make sure the main process starts first for potential database migrations
1536 after = [ "matrix-synapse.service" ];
1537 requires = [ "matrix-synapse.service" ];
1538 serviceConfig = {
1539 ExecStart = ''
1540 ${cfg.package}/bin/synapse_worker \
1541 ${concatMapStringsSep "\n " (x: "--config-path ${x} \\") (
1542 [
1543 configFile
1544 workerConfigFile
1545 ]
1546 ++ cfg.extraConfigFiles
1547 )}
1548 --keys-directory ${cfg.dataDir}
1549 '';
1550 };
1551 }
1552 ];
1553 };
1554 in
1555 {
1556 matrix-synapse = lib.mkMerge [
1557 baseServiceConfig
1558 {
1559 description = "Synapse Matrix homeserver";
1560 preStart = ''
1561 ${cfg.package}/bin/synapse_homeserver \
1562 --config-path ${configFile} \
1563 --keys-directory ${cfg.dataDir} \
1564 --generate-keys
1565 '';
1566 serviceConfig = {
1567 ExecStartPre = [
1568 (
1569 "+"
1570 + (pkgs.writeShellScript "matrix-synapse-fix-permissions" ''
1571 chown matrix-synapse:matrix-synapse ${cfg.settings.signing_key_path}
1572 chmod 0600 ${cfg.settings.signing_key_path}
1573 '')
1574 )
1575 ];
1576 ExecStart = ''
1577 ${cfg.package}/bin/synapse_homeserver \
1578 ${concatMapStringsSep "\n " (x: "--config-path ${x} \\") (
1579 [ configFile ] ++ cfg.extraConfigFiles
1580 )}
1581 --keys-directory ${cfg.dataDir}
1582 '';
1583 };
1584 }
1585 ];
1586 }
1587 // (lib.mapAttrs' genWorkerService cfg.workers);
1588
1589 services.redis.servers.matrix-synapse = lib.mkIf cfg.configureRedisLocally {
1590 enable = true;
1591 user = "matrix-synapse";
1592 };
1593
1594 environment.systemPackages = lib.optionals cfg.enableRegistrationScript [
1595 registerNewMatrixUser
1596 ];
1597 };
1598
1599 meta = {
1600 inherit (pkgs.matrix-synapse.meta) maintainers;
1601 buildDocsInSandbox = false;
1602 doc = ./synapse.md;
1603 };
1604
1605}