at 25.11-pre 59 kB view raw
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}