diff --git a/nixos/doc/manual/redirects.json b/nixos/doc/manual/redirects.json index 5f9265ab667d..264b17c856e8 100644 --- a/nixos/doc/manual/redirects.json +++ b/nixos/doc/manual/redirects.json @@ -65,6 +65,42 @@ "module-services-keycloak-unix-socket": [ "index.html#module-services-keycloak-unix-socket" ], + "module-services-mautrix-discord": [ + "index.html#module-services-mautrix-discord" + ], + "module-services-mautrix-discord-advanced": [ + "index.html#module-services-mautrix-discord-advanced" + ], + "module-services-mautrix-discord-authentication": [ + "index.html#module-services-mautrix-discord-authentication" + ], + "module-services-mautrix-discord-backfill": [ + "index.html#module-services-mautrix-discord-backfill" + ], + "module-services-mautrix-discord-basic-example": [ + "index.html#module-services-mautrix-discord-basic-example" + ], + "module-services-mautrix-discord-basic-usage": [ + "index.html#module-services-mautrix-discord-basic-usage" + ], + "module-services-mautrix-discord-double-puppet": [ + "index.html#module-services-mautrix-discord-double-puppet" + ], + "module-services-mautrix-discord-encryption": [ + "index.html#module-services-mautrix-discord-encryption" + ], + "module-services-mautrix-discord-server-defaults": [ + "index.html#module-services-mautrix-discord-server-defaults" + ], + "module-services-mautrix-discord-setup": [ + "index.html#module-services-mautrix-discord-setup" + ], + "module-services-mautrix-discord-synapse": [ + "index.html#module-services-mautrix-discord-synapse" + ], + "module-services-mautrix-discord-troubleshooting": [ + "index.html#module-services-mautrix-discord-troubleshooting" + ], "module-services-tandoor-recipes-migrating-media-option-move": [ "index.html#module-services-tandoor-recipes-migrating-media-option-move", "index.html#module-services-tandoor-recipes-migrating-media-option-1" diff --git a/nixos/modules/services/matrix/mautrix-discord.md b/nixos/modules/services/matrix/mautrix-discord.md new file mode 100644 index 000000000000..ef356f600da7 --- /dev/null +++ b/nixos/modules/services/matrix/mautrix-discord.md @@ -0,0 +1,156 @@ +# Mautrix-Discord {#module-services-mautrix-discord} + +*Source:* {file}`modules/services/matrix/mautrix-discord` + +*Upstream documentation:* + +[Mautrix-Discord](https://github.com/mautrix/discord) is a Matrix-Discord bridge. + +## Basic Usage {#module-services-mautrix-discord-basic-usage} + +The common setup is to enable the bridge, point it at your homeserver, and set the permissions you want to allow: + +1. Set `services.mautrix-discord.enable` to `true`. +2. Set `services.mautrix-discord.settings.homeserver.address` and `services.mautrix-discord.settings.homeserver.domain`. +3. Override `services.mautrix-discord.settings.bridge.permissions` if the default relay permissions do not fit your deployment. + +The module provides sensible defaults for the appservice listener, registration tokens, and relay permissions. + +### Basic Example {#module-services-mautrix-discord-basic-example} + +```nix +{ + services.mautrix-discord = { + enable = true; + registerToSynapse = true; + settings = { + homeserver = { + address = "http://localhost:8008"; + domain = "example.com"; + }; + bridge.permissions = { + "example.com" = "user"; + "@admin:example.com" = "admin"; + }; + }; + }; +} +``` + +### Server Defaults {#module-services-mautrix-discord-server-defaults} + +By default, the bridge listens on `http://localhost:29334` and generates its appservice tokens automatically. + +## Authentication {#module-services-mautrix-discord-authentication} + +If you want to store the bridge database outside the default SQLite file, set `settings.appservice.database` to use PostgreSQL instead of SQLite: + +```nix +{ + services.mautrix-discord.settings.appservice.database = { + type = "postgres"; + uri = "postgresql:///mautrix-discord?host=/run/postgresql"; + }; +} +``` + +If the connection needs a password, combine it with `services.mautrix-discord.environmentFile`: + +```nix +{ + services.mautrix-discord = { + environmentFile = "/run/secrets/mautrix-discord-env"; + settings.appservice.database.uri = "postgresql://mautrix:$DB_PASSWORD@localhost/mautrix-discord"; + }; +} +``` + +Use `services.mautrix-discord.environmentFile` for any secret you do not want in the Nix store. +This includes database passwords, shared secrets, and similar values. + +Example: + +```nix +{ + services.mautrix-discord = { + environmentFile = "/run/secrets/mautrix-discord-env"; + settings.bridge.login_shared_secret_map = { + "example.com" = "$SHARED_SECRET"; + }; + }; +} +``` + +## Bridge Setup {#module-services-mautrix-discord-setup} + +After the service starts, open a chat with `@discordbot:example.com`, send `login`, and follow the instructions to link your Discord account. + +For more detail, see the [Mautrix-Discord documentation](https://docs.mau.fi/bridges/go/discord/index.html). + +## Advanced Configuration {#module-services-mautrix-discord-advanced} + +The upstream default configuration is available at [example-config.yaml](https://github.com/mautrix/discord/blob/main/example-config.yaml). To print the generated default configuration from the package, run: + +```bash +nix-shell -p mautrix-discord --run "mautrix-discord -e" +``` + +### Encryption {#module-services-mautrix-discord-encryption} + +```nix +{ + services.mautrix-discord.settings.bridge.encryption = { + allow = true; + default = true; + require = false; + }; +} +``` + +Encryption needs additional bridge-side setup. See the [bridge encryption documentation](https://docs.mau.fi/bridges/general/end-to-bridge-encryption.html) for details. + +### Backfill {#module-services-mautrix-discord-backfill} + +```nix +{ + services.mautrix-discord.settings.bridge.backfill.forward_limits.initial = { + dm = 50; + channel = 50; + thread = 50; + }; +} +``` + +### Double Puppeting {#module-services-mautrix-discord-double-puppet} + +```nix +{ + services.mautrix-discord = { + environmentFile = "/run/secrets/mautrix-discord-env"; + settings.bridge.login_shared_secret_map = { + "example.com" = "$SHARED_SECRET"; + }; + }; +} +``` +where `/run/secrets/mautrix-discord-env` contains +``` +SHARED_SECRET=aProtectedSecret +``` + +## Synapse Integration {#module-services-mautrix-discord-synapse} + +When `services.mautrix-discord.registerToSynapse` is `true`, the bridge writes its registration file automatically and Synapse picks it up. + +If Synapse is enabled, this option defaults to `true`. + +## Troubleshooting {#module-services-mautrix-discord-troubleshooting} + +- View logs with `journalctl -u mautrix-discord.service -f`. +- Check `systemctl status mautrix-discord` if the bridge does not start. +- Verify the homeserver can reach the configured appservice address. +- Ensure the registration file exists and Synapse can read it. + +For more help, see the +[Mautrix-Discord documentation](https://docs.mau.fi/bridges/go/discord/index.html) +or the support room at [#discord:maunium.net](https://matrix.to/#/#discord:maunium.net). diff --git a/nixos/modules/services/matrix/mautrix-discord.nix b/nixos/modules/services/matrix/mautrix-discord.nix index f0796ca1df84..5ed5e4ae54ce 100644 --- a/nixos/modules/services/matrix/mautrix-discord.nix +++ b/nixos/modules/services/matrix/mautrix-discord.nix @@ -5,15 +5,59 @@ ... }: let + defaultDataDir = "/var/lib/mautrix-discord"; cfg = config.services.mautrix-discord; dataDir = cfg.dataDir; format = pkgs.formats.yaml { }; + serviceDependencies = [ + "mautrix-discord-registration.service" + ] + ++ (lib.lists.optional config.services.matrix-synapse.enable config.services.matrix-synapse.serviceUnit) + ++ (lib.lists.optional config.services.matrix-conduit.enable "matrix-conduit.service") + ++ (lib.lists.optional config.services.dendrite.enable "dendrite.service"); registrationFile = "${dataDir}/discord-registration.yaml"; settingsFile = "${dataDir}/config.yaml"; settingsFileUnformatted = format.generate "discord-config-unsubstituted.yaml" cfg.settings; default_token = "This value is generated when generating the registration"; + settingsDefault = { + homeserver = { + address = ""; + domain = ""; + }; + + appservice = { + address = "http://localhost:29334"; + hostname = "0.0.0.0"; + port = 29334; + database = { + type = "sqlite3"; + uri = "file:${defaultDataDir}/mautrix-discord.db?_txlock=immediate"; + }; + id = "discord"; + bot = { + username = "discordbot"; + displayname = "Discord bridge bot"; + avatar = "mxc://maunium.net/nIdEykemnwdisvHbpxflpDlC"; + }; + as_token = default_token; + hs_token = default_token; + }; + + bridge.permissions."*" = "relay"; + + logging = { + min_level = "info"; + writers = [ + { + type = "stdout"; + format = "pretty-colored"; + time_format = " "; + } + ]; + }; + }; in { options = { @@ -23,238 +67,14 @@ in package = lib.mkPackageOption pkgs "mautrix-discord" { }; settings = lib.mkOption { - type = lib.types.submodule { - freeformType = format.type; - - config = { - _module.args = { inherit cfg lib; }; - }; - - options = { - homeserver = lib.mkOption { - type = lib.types.attrs; - default = { - software = "standard"; - status_endpoint = null; - message_send_checkpoint_endpoint = null; - async_media = false; - websocket = false; - ping_interval_seconds = 0; - }; - description = '' - fullDataDiration. - See [example-config.yaml](https://github.com/mautrix/discord/blob/main/example-config.yaml) - for more information. - ''; - }; - - appservice = lib.mkOption { - type = lib.types.attrs; - default = { - address = "http://localhost:29334"; - hostname = "0.0.0.0"; - port = 29334; - database = { - type = "sqlite3"; - uri = "file:/var/lib/mautrix-discord/mautrix-discord.db?_txlock=immediate"; - max_open_conns = 20; - max_idle_conns = 2; - max_conn_idle_time = null; - max_conn_lifetime = null; - }; - id = "discord"; - bot = { - username = "discordbot"; - displayname = "Discord bridge bot"; - avatar = "mxc://maunium.net/nIdEykemnwdisvHbpxflpDlC"; - }; - ephemeral_events = true; - async_transactions = false; - as_token = "This value is generated when generating the registration"; - hs_token = "This value is generated when generating the registration"; - }; - defaultText = lib.literalExpression '' - { - address = "http://localhost:29334"; - hostname = "0.0.0.0"; - port = 29334; - database = { - type = "sqlite3"; - uri = "file:''${config.services.mautrix-discord.dataDir}/mautrix-discord.db?_txlock=immediate"; - max_open_conns = 20; - max_idle_conns = 2; - max_conn_idle_time = null; - max_conn_lifetime = null; - }; - id = "discord"; - bot = { - username = "discordbot"; - displayname = "Discord bridge bot"; - avatar = "mxc://maunium.net/nIdEykemnwdisvHbpxflpDlC"; - }; - ephemeral_events = true; - async_transactions = false; - as_token = "This value is generated when generating the registration"; - hs_token = "This value is generated when generating the registration"; - } - ''; - description = '' - Appservice configuration. - See [example-config.yaml](https://github.com/mautrix/discord/blob/main/example-config.yaml) - for more information. - ''; - }; - - bridge = lib.mkOption { - type = lib.types.attrs; - default = { - username_template = "discord_{{.}}"; - displayname_template = "{{if .Webhook}}Webhook{{else}}{{or .GlobalName .Username}}{{if .Bot}} (bot){{end}}{{end}}"; - channel_name_template = "{{if or (eq .Type 3) (eq .Type 4)}}{{.Name}}{{else}}#{{.Name}}{{end}}"; - guild_name_template = "{{.Name}}"; - private_chat_portal_meta = "default"; - public_address = null; - avatar_proxy_key = "generate"; - portal_message_buffer = 128; - startup_private_channel_create_limit = 5; - delivery_receipts = false; - message_status_events = false; - message_error_notices = true; - restricted_rooms = true; - autojoin_thread_on_open = true; - embed_fields_as_tables = true; - mute_channels_on_create = false; - sync_direct_chat_list = false; - resend_bridge_info = false; - custom_emoji_reactions = true; - delete_portal_on_channel_delete = false; - delete_guild_on_leave = true; - federate_rooms = true; - prefix_webhook_messages = true; - enable_webhook_avatars = false; - use_discord_cdn_upload = true; - #proxy = - cache_media = "unencrypted"; - direct_media = { - enabled = false; - #server_name = "discord-media.example.com"; - #well_known_response = - allow_proxy = true; - server_key = "generate"; - }; - animated_sticker = { - target = "webp"; - args = { - width = 320; - height = 320; - fps = 25; - }; - }; - double_puppet_server_map = { - #"example.com" = "https://example.com"; - }; - double_puppet_allow_discovery = false; - login_shared_secret_map = { - #"example.com" = "foobar"; - }; - command_prefix = "!discord"; - management_room_text = { - welcome = "Hello, I'm a Discord bridge bot."; - welcome_connected = "Use `help` for help."; - welcome_unconnected = "Use `help` for help or `login` to log in."; - additional_help = ""; - }; - backfill = { - forward_limits = { - initial = { - dm = 0; - channel = 0; - thread = 0; - }; - missed = { - dm = 0; - channel = 0; - thread = 0; - }; - max_guild_members = -1; - }; - }; - encryption = { - allow = false; - default = false; - appservice = false; - msc4190 = false; - require = false; - allow_key_sharing = false; - plaintext_mentions = false; - delete_keys = { - delete_outbound_on_ack = false; - dont_store_outbound = false; - ratchet_on_decrypt = false; - delete_fully_used_on_decrypt = false; - delete_prev_on_new_session = false; - delete_on_device_delete = false; - periodically_delete_expired = false; - delete_outdated_inbound = false; - }; - verification_levels = { - receive = "unverified"; - send = "unverified"; - share = "cross-signed-tofu"; - }; - rotation = { - enable_custom = false; - milliseconds = 604800000; - messages = 100; - disable_device_change_key_rotation = false; - }; - }; - provisioning = { - prefix = "/_matrix/provision"; - shared_secret = "generate"; - debug_endpoints = false; - }; - permissions = { - "*" = "relay"; - #"example.com" = "user"; - #"@admin:example.com": "admin"; - }; - }; - description = '' - Bridge configuration. - See [example-config.yaml](https://github.com/mautrix/discord/blob/main/example-config.yaml) - for more information. - ''; - }; - logging = lib.mkOption { - type = lib.types.attrs; - default = { - min_level = "info"; - writers = lib.singleton { - type = "stdout"; - format = "pretty-colored"; - time_format = " "; - }; - }; - description = '' - Logging configuration. - See [example-config.yaml](https://github.com/mautrix/discord/blob/main/example-config.yaml) - for more information. - ''; - }; - }; - }; - default = { }; + apply = lib.recursiveUpdate settingsDefault; + type = format.type; + default = settingsDefault; example = lib.literalExpression '' { homeserver = { address = "http://localhost:8008"; - domain = "public-domain.tld"; - }; - - appservice.public = { - prefix = "/public"; - external = "https://public-appservice-address/public"; + domain = "example.com"; }; bridge.permissions = { @@ -265,8 +85,12 @@ in ''; description = '' {file}`config.yaml` configuration as a Nix attribute set. + Configuration options should match those described in [example-config.yaml](https://github.com/mautrix/discord/blob/main/example-config.yaml). + + Secret tokens should be specified using {option}`environmentFile` + instead of this world-readable attribute set. ''; }; @@ -282,12 +106,9 @@ in dataDir = lib.mkOption { type = lib.types.path; - default = "/var/lib/mautrix-discord"; - defaultText = "/var/lib/mautrix-discord"; - description = '' - Directory to store the bridge's configuration and database files. - This directory will be created if it does not exist. - ''; + default = defaultDataDir; + defaultText = defaultDataDir; + description = "Directory to store the bridge's data."; }; # TODO: Get upstream to add an environment File option. Refer to https://github.com/NixOS/nixpkgs/pull/404871#issuecomment-2895663652 and https://github.com/mautrix/discord/issues/187 @@ -295,92 +116,31 @@ in type = lib.types.nullOr lib.types.path; default = null; description = '' - File containing environment variables to substitute when copying the configuration - out of Nix store to the `services.mautrix-discord.dataDir`. - Can be used for storing the secrets without making them available in the Nix store. - For example, you can set `services.mautrix-discord.settings.appservice.as_token = "$MAUTRIX_DISCORD_APPSERVICE_AS_TOKEN"` - and then specify `MAUTRIX_DISCORD_APPSERVICE_AS_TOKEN="{token}"` in the environment file. - This value will get substituted into the configuration file as a token. + File containing environment variables for secret substitution. + Variables in the config like `$VARIABLE` will be replaced. ''; }; - serviceUnit = lib.mkOption { - type = lib.types.str; - readOnly = true; - default = "mautrix-discord.service"; - description = '' - The systemd unit (a service or a target) for other services to depend on if they - need to be started after matrix-synapse. - This option is useful as the actual parent unit for all matrix-synapse processes - changes when configuring workers. - ''; - }; - - registrationServiceUnit = lib.mkOption { - type = lib.types.str; - readOnly = true; - default = "mautrix-discord-registration.service"; - description = '' - The registration service that generates the registration file. - Systemd unit (a service or a target) for other services to depend on if they - need to be started after mautrix-discord registration service. - This option is useful as the actual parent unit for all matrix-synapse processes - changes when configuring workers. - ''; - }; - - serviceDependencies = lib.mkOption { - type = lib.types.listOf lib.types.str; - default = [ - cfg.registrationServiceUnit - ] - ++ (lib.lists.optional config.services.matrix-synapse.enable config.services.matrix-synapse.serviceUnit) - ++ (lib.lists.optional config.services.matrix-conduit.enable "matrix-conduit.service") - ++ (lib.lists.optional config.services.dendrite.enable "dendrite.service"); - - defaultText = '' - [ cfg.registrationServiceUnit ] ++ - (lib.lists.optional config.services.matrix-synapse.enable config.services.matrix-synapse.serviceUnit) ++ - (lib.lists.optional config.services.matrix-conduit.enable "matrix-conduit.service") ++ - (lib.lists.optional config.services.dendrite.enable "dendrite.service"); - ''; - description = '' - List of Systemd services to require and wait for when starting the application service. - ''; - }; }; }; config = lib.mkIf cfg.enable { + assertions = [ { assertion = - cfg.settings.homeserver.domain or "" != "" && cfg.settings.homeserver.address or "" != ""; - message = '' - The options with information about the homeserver: - `services.mautrix-discord.settings.homeserver.domain` and - `services.mautrix-discord.settings.homeserver.address` have to be set. - ''; - } - { - assertion = cfg.settings.bridge.permissions or { } != { }; - message = '' - The option `services.mautrix-discord.settings.bridge.permissions` has to be set. - ''; + cfg.settings.homeserver.address or "" != "" && cfg.settings.homeserver.domain or "" != ""; + message = "services.mautrix-discord.settings.homeserver.{address,domain} must be set."; } ]; users.users.mautrix-discord = { isSystemUser = true; group = "mautrix-discord"; - extraGroups = [ "mautrix-discord-registration" ]; home = dataDir; description = "Mautrix-Discord bridge user"; }; users.groups.mautrix-discord = { }; - users.groups.mautrix-discord-registration = { - members = lib.lists.optional config.services.matrix-synapse.enable "matrix-synapse"; - }; services.matrix-synapse = lib.mkIf cfg.registerToSynapse { settings.app_service_config_files = [ registrationFile ]; @@ -392,7 +152,9 @@ in systemd.services = { matrix-synapse = lib.mkIf cfg.registerToSynapse { - serviceConfig.SupplementaryGroups = [ "mautrix-discord-registration" ]; + serviceConfig.SupplementaryGroups = [ + "mautrix-discord" + ]; # Make synapse depend on the registration service when auto-registering wants = [ "mautrix-discord-registration.service" ]; after = [ "mautrix-discord-registration.service" ]; @@ -471,15 +233,18 @@ in '${settingsFile}' '${registrationFile}' > '${registrationFile}.tmp' mv '${registrationFile}.tmp' '${registrationFile}' + # Application services should not be rate limited by default. + yq -Y '.rate_limited = false' '${registrationFile}' > '${registrationFile}.tmp' + mv '${registrationFile}.tmp' '${registrationFile}' + umask $old_umask - chown :mautrix-discord-registration '${registrationFile}' chmod 640 '${registrationFile}' ''; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; - UMask = 27; + UMask = "027"; User = "mautrix-discord"; Group = "mautrix-discord"; @@ -501,8 +266,8 @@ in description = "Mautrix-Discord, a Matrix-Discord puppeting/relaybot bridge"; wantedBy = [ "multi-user.target" ]; - wants = [ "network-online.target" ] ++ cfg.serviceDependencies; - after = [ "network-online.target" ] ++ cfg.serviceDependencies; + wants = [ "network-online.target" ] ++ serviceDependencies; + after = [ "network-online.target" ] ++ serviceDependencies; path = [ pkgs.lottieconverter pkgs.ffmpeg-headless @@ -536,6 +301,8 @@ in ProtectHostname = true; ProtectClock = true; + UMask = "027"; + SystemCallArchitectures = "native"; SystemCallErrorNumber = "EPERM"; SystemCallFilter = "@system-service"; @@ -546,10 +313,12 @@ in }; }; - meta = { - maintainers = with lib.maintainers; [ - mistyttm - ]; - }; + }; + + meta = { + maintainers = with lib.maintainers; [ + mistyttm + ]; + doc = ./mautrix-discord.md; }; } diff --git a/nixos/tests/matrix/mautrix-discord.nix b/nixos/tests/matrix/mautrix-discord.nix index 1e53baac7af2..fe27b1db8843 100644 --- a/nixos/tests/matrix/mautrix-discord.nix +++ b/nixos/tests/matrix/mautrix-discord.nix @@ -1,4 +1,4 @@ -{ pkgs, lib, ... }: +{ pkgs, ... }: let homeserverUrl = "http://homeserver:8008"; in @@ -53,6 +53,7 @@ in appservice = { address = "http://homeserver:8009"; + hostname = "0.0.0.0"; port = 8009; id = "discord"; bot = { @@ -60,8 +61,6 @@ in displayname = "Discord bridge bot"; avatar = "mxc://maunium.net/nIdEykemnwdisvHbpxflpDlC"; }; - # Don't override as_token/hs_token - let them use the default placeholder - # which will trigger automatic generation database = { type = "sqlite3"; @@ -75,6 +74,17 @@ in "*" = "relay"; }; }; + + logging = { + min_level = "info"; + writers = [ + { + type = "stdout"; + format = "pretty-colored"; + time_format = " "; + } + ]; + }; }; }; @@ -157,6 +167,57 @@ in with subtest("verify registration file was created"): homeserver.wait_until_succeeds("test -f /var/lib/mautrix-discord/discord-registration.yaml") + # Verify the module wrote the expected bridge configuration. + config_homeserver_address = homeserver.succeed("yq -r '.homeserver.address' /var/lib/mautrix-discord/config.yaml").strip() + config_homeserver_domain = homeserver.succeed("yq -r '.homeserver.domain' /var/lib/mautrix-discord/config.yaml").strip() + config_appservice_address = homeserver.succeed("yq -r '.appservice.address' /var/lib/mautrix-discord/config.yaml").strip() + config_appservice_hostname = homeserver.succeed("yq -r '.appservice.hostname' /var/lib/mautrix-discord/config.yaml").strip() + config_appservice_port = homeserver.succeed("yq -r '.appservice.port' /var/lib/mautrix-discord/config.yaml").strip() + config_appservice_id = homeserver.succeed("yq -r '.appservice.id' /var/lib/mautrix-discord/config.yaml").strip() + config_bot_username = homeserver.succeed("yq -r '.appservice.bot.username' /var/lib/mautrix-discord/config.yaml").strip() + config_bot_displayname = homeserver.succeed("yq -r '.appservice.bot.displayname' /var/lib/mautrix-discord/config.yaml").strip() + config_bot_avatar = homeserver.succeed("yq -r '.appservice.bot.avatar' /var/lib/mautrix-discord/config.yaml").strip() + config_database_type = homeserver.succeed("yq -r '.appservice.database.type' /var/lib/mautrix-discord/config.yaml").strip() + config_database_uri = homeserver.succeed("yq -r '.appservice.database.uri' /var/lib/mautrix-discord/config.yaml").strip() + config_permission = homeserver.succeed("yq -r '.bridge.permissions[\"*\"]' /var/lib/mautrix-discord/config.yaml").strip() + config_logging_min_level = homeserver.succeed("yq -r '.logging.min_level' /var/lib/mautrix-discord/config.yaml").strip() + config_logging_writer_type = homeserver.succeed("yq -r '.logging.writers[0].type' /var/lib/mautrix-discord/config.yaml").strip() + config_logging_writer_format = homeserver.succeed("yq -r '.logging.writers[0].format' /var/lib/mautrix-discord/config.yaml").strip() + reg_rate_limited = homeserver.succeed("yq -r '.rate_limited' /var/lib/mautrix-discord/discord-registration.yaml").strip() + + assert config_homeserver_address == "http://homeserver:8008", \ + f"Unexpected homeserver address: {config_homeserver_address}" + assert config_homeserver_domain == "homeserver", \ + f"Unexpected homeserver domain: {config_homeserver_domain}" + assert config_appservice_address == "http://homeserver:8009", \ + f"Unexpected appservice address: {config_appservice_address}" + assert config_appservice_hostname == "0.0.0.0", \ + f"Unexpected appservice hostname: {config_appservice_hostname}" + assert config_appservice_port == "8009", \ + f"Unexpected appservice port: {config_appservice_port}" + assert config_appservice_id == "discord", \ + f"Unexpected appservice id: {config_appservice_id}" + assert config_bot_username == "discordbot", \ + f"Unexpected bot username: {config_bot_username}" + assert config_bot_displayname == "Discord bridge bot", \ + f"Unexpected bot displayname: {config_bot_displayname}" + assert config_bot_avatar == "mxc://maunium.net/nIdEykemnwdisvHbpxflpDlC", \ + f"Unexpected bot avatar: {config_bot_avatar}" + assert config_database_type == "sqlite3-fk-wal", \ + f"Unexpected database type: {config_database_type}" + assert config_database_uri == "file:/var/lib/mautrix-discord/mautrix-discord.db?_txlock=immediate", \ + f"Unexpected database uri: {config_database_uri}" + assert config_permission == "relay", \ + f"Unexpected default permission mapping: {config_permission}" + assert config_logging_min_level == "info", \ + f"Unexpected logging min_level: {config_logging_min_level}" + assert config_logging_writer_type == "stdout", \ + f"Unexpected logging writer type: {config_logging_writer_type}" + assert config_logging_writer_format == "pretty-colored", \ + f"Unexpected logging writer format: {config_logging_writer_format}" + assert reg_rate_limited == "false", \ + f"Registration file should disable rate limiting by default, got: {reg_rate_limited}" + # Verify tokens were generated and are not default values config_as_token = homeserver.succeed("yq -r '.appservice.as_token' /var/lib/mautrix-discord/config.yaml").strip() config_hs_token = homeserver.succeed("yq -r '.appservice.hs_token' /var/lib/mautrix-discord/config.yaml").strip()