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