tuwunel_core/config/mod.rs
1pub mod check;
2mod identity_provider_serde;
3pub mod ip_source;
4pub mod manager;
5mod net;
6pub mod proxy;
7pub mod room_version;
8#[cfg(test)]
9mod tests;
10
11use std::{
12 collections::{BTreeMap, BTreeSet},
13 net::IpAddr,
14 path::{Path, PathBuf},
15};
16
17use bytesize::ByteSize;
18use derive_more::Debug;
19use either::{Either, Either::Left};
20use figment::providers::{Data, Env, Format, Toml};
21pub use figment::{Figment, value::Value as FigmentValue};
22use itertools::Itertools;
23use regex::RegexSet;
24use ruma::{
25 OwnedMxcUri, OwnedRoomOrAliasId, OwnedServerName, OwnedUserId, RoomVersionId,
26 api::client::discovery::discover_support::ContactRole,
27};
28use serde::{Deserialize, de::IgnoredAny};
29use tuwunel_macros::config_example_generator;
30use url::Url;
31
32pub use self::{check::check, ip_source::IpSource, manager::Manager};
33use self::{
34 net::{ListeningAddr, ListeningPort},
35 proxy::ProxyConfig,
36};
37use crate::{
38 Err, Result, err, redacted_debug,
39 utils::{
40 self,
41 bytes::{deserialize_bytesize_u64, deserialize_bytesize_usize},
42 sys,
43 },
44};
45
46/// All the config options for tuwunel.
47#[expect(rustdoc::broken_intra_doc_links, rustdoc::bare_urls)]
48#[derive(Clone, Deserialize)]
49#[config_example_generator(
50 filename = "tuwunel-example.toml",
51 section = "global",
52 undocumented = "# This item is undocumented. Please contribute documentation for it.",
53 header = r#"### Tuwunel Configuration
54###
55### THIS FILE IS GENERATED. CHANGES/CONTRIBUTIONS IN THE REPO WILL BE
56### OVERWRITTEN!
57###
58### You should rename this file before configuring your server. Changes to
59### documentation and defaults can be contributed in source code at
60### src/core/config/mod.rs. This file is generated when building.
61###
62### Any values pre-populated are the default values for said config option.
63###
64### At the minimum, you MUST edit all the config options to your environment
65### that say "YOU NEED TO EDIT THIS".
66###
67### For more information, see:
68### https://tuwunel.chat/configuration.html
69"#,
70 ignore = "catchall well_known tls blurhashing allow_invalid_tls_certificates ldap jwt \
71 appservice identity_provider storage_provider"
72)]
73pub struct Config {
74 /// The server_name is the pretty name of this server. It is used as a
75 /// suffix for user and room IDs/aliases.
76 ///
77 /// See the docs for reverse proxying and delegation:
78 /// https://tuwunel.chat/deploying/generic.html#setting-up-the-reverse-proxy
79 ///
80 /// Also see the `[global.well_known]` config section at the very bottom.
81 ///
82 /// Examples of delegation:
83 /// - https://matrix.org/.well-known/matrix/server
84 /// - https://matrix.org/.well-known/matrix/client
85 ///
86 /// YOU NEED TO EDIT THIS. THIS CANNOT BE CHANGED AFTER WITHOUT A DATABASE
87 /// WIPE.
88 ///
89 /// example: "girlboss.ceo"
90 #[cfg_attr(test, serde(default = "default_server_name"))]
91 pub server_name: OwnedServerName,
92
93 /// This is the only directory where tuwunel will save its data, including
94 /// media. Note: this was previously "/var/lib/matrix-conduit".
95 ///
96 /// default: "/var/lib/tuwunel"
97 #[serde(default = "default_database_path")]
98 pub database_path: PathBuf,
99
100 /// Text which will be added to the end of the user's displayname upon
101 /// registration with a space before the text. In Conduit, this was the
102 /// lightning bolt emoji.
103 ///
104 /// To disable, set this to "" (an empty string).
105 ///
106 /// reloadable: yes
107 /// default: "💕"
108 #[serde(default = "default_new_user_displayname_suffix")]
109 pub new_user_displayname_suffix: String,
110
111 #[expect(clippy::doc_link_with_quotes)]
112 /// The default address (IPv4 or IPv6) tuwunel will listen on.
113 ///
114 /// If you are using Docker or a container NAT networking setup, this must
115 /// be "0.0.0.0".
116 ///
117 /// To listen on multiple addresses, specify a vector e.g. ["127.0.0.1",
118 /// "::1"]
119 ///
120 /// default: ["127.0.0.1", "::1"]
121 #[serde(default)]
122 address: Option<ListeningAddr>,
123
124 /// The port(s) tuwunel will listen on.
125 ///
126 /// For reverse proxying, see:
127 /// https://tuwunel.chat/deploying/generic.html#setting-up-the-reverse-proxy
128 ///
129 /// If you are using Docker, don't change this, you'll need to map an
130 /// external port to this.
131 ///
132 /// To listen on multiple ports, specify a vector e.g. [8080, 8448]
133 ///
134 /// default: 8008
135 #[serde(default = "default_port")]
136 port: ListeningPort,
137
138 // external structure; separate section
139 #[serde(default)]
140 pub tls: TlsConfig,
141
142 /// The UNIX socket tuwunel will listen on.
143 ///
144 /// Remember to make sure that your reverse proxy has access to this socket
145 /// file, either by adding your reverse proxy to the 'tuwunel' group or
146 /// granting world R/W permissions with `unix_socket_perms` (666 minimum).
147 ///
148 /// example: "/run/tuwunel/tuwunel.sock"
149 pub unix_socket_path: Option<PathBuf>,
150
151 /// The default permissions (in octal) to create the UNIX socket with.
152 ///
153 /// default: 660
154 #[serde(default = "default_unix_socket_perms")]
155 pub unix_socket_perms: u32,
156
157 /// Error on startup if any config option specified is unknown to Tuwunel.
158 ///
159 /// This is false by default to allow easier deprecation or removal of
160 /// config options in the future without breaking existing deployments. The
161 /// default behaviour is to simply warn on startup.
162 /// reloadable: yes
163 #[serde(default)]
164 pub error_on_unknown_config_opts: bool,
165
166 /// tuwunel supports online database backups using RocksDB's Backup engine
167 /// API. To use this, set a database backup path that tuwunel can write
168 /// to.
169 ///
170 /// For more information, see:
171 /// https://tuwunel.chat/maintenance.html#backups
172 ///
173 /// reloadable: yes
174 /// example: "/opt/tuwunel-db-backups"
175 pub database_backup_path: Option<PathBuf>,
176
177 /// The amount of online RocksDB database backups to keep/retain, if using
178 /// "database_backup_path", before deleting the oldest one.
179 ///
180 /// reloadable: yes
181 /// default: 1
182 #[serde(default = "default_database_backups_to_keep")]
183 pub database_backups_to_keep: i16,
184
185 /// Set this to any float value to multiply tuwunel's in-memory LRU caches
186 /// with such as "auth_chain_cache_capacity".
187 ///
188 /// May be useful if you have significant memory to spare to increase
189 /// performance.
190 ///
191 /// If you have low memory, reducing this may be viable.
192 ///
193 /// By default, the individual caches such as "auth_chain_cache_capacity"
194 /// are scaled by your CPU core count.
195 ///
196 /// default: 1.0
197 #[serde(
198 default = "default_cache_capacity_modifier",
199 alias = "conduit_cache_capacity_modifier"
200 )]
201 pub cache_capacity_modifier: f64,
202
203 /// Set this to any float value in megabytes for tuwunel to tell the
204 /// database engine that this much memory is available for database read
205 /// caches.
206 ///
207 /// May be useful if you have significant memory to spare to increase
208 /// performance.
209 ///
210 /// Similar to the individual LRU caches, this is scaled up with your CPU
211 /// core count.
212 ///
213 /// This defaults to 128.0 + (64.0 * CPU core count).
214 ///
215 /// default: varies by system
216 #[serde(default = "default_db_cache_capacity_mb")]
217 pub db_cache_capacity_mb: f64,
218
219 /// Set this to any float value in megabytes for tuwunel to tell the
220 /// database engine that this much memory is available for database write
221 /// caches.
222 ///
223 /// May be useful if you have significant memory to spare to increase
224 /// performance.
225 ///
226 /// Similar to the individual LRU caches, this is scaled up with your CPU
227 /// core count.
228 ///
229 /// This defaults to 48.0 + (4.0 * CPU core count).
230 ///
231 /// default: varies by system
232 #[serde(default = "default_db_write_buffer_capacity_mb")]
233 pub db_write_buffer_capacity_mb: f64,
234
235 /// default: varies by system
236 #[serde(default = "default_pdu_cache_capacity")]
237 pub pdu_cache_capacity: u32,
238
239 /// default: varies by system
240 #[serde(default = "default_auth_chain_cache_capacity")]
241 pub auth_chain_cache_capacity: u32,
242
243 /// default: varies by system
244 #[serde(default = "default_shorteventid_cache_capacity")]
245 pub shorteventid_cache_capacity: u32,
246
247 /// default: varies by system
248 #[serde(default = "default_eventidshort_cache_capacity")]
249 pub eventidshort_cache_capacity: u32,
250
251 /// default: varies by system
252 #[serde(default = "default_eventid_pdu_cache_capacity")]
253 pub eventid_pdu_cache_capacity: u32,
254
255 /// default: varies by system
256 #[serde(default = "default_shortstatekey_cache_capacity")]
257 pub shortstatekey_cache_capacity: u32,
258
259 /// default: varies by system
260 #[serde(default = "default_statekeyshort_cache_capacity")]
261 pub statekeyshort_cache_capacity: u32,
262
263 /// default: varies by system
264 #[serde(default = "default_servernameevent_data_cache_capacity")]
265 pub servernameevent_data_cache_capacity: u32,
266
267 /// default: varies by system
268 #[serde(default = "default_stateinfo_cache_capacity")]
269 pub stateinfo_cache_capacity: u32,
270
271 /// Minimum time-to-live in seconds for room summary entries in the spaces
272 /// cache.
273 ///
274 /// reloadable: yes
275 /// default: 21600
276 #[serde(default = "default_spacehierarchy_cache_ttl_min")]
277 pub spacehierarchy_cache_ttl_min: u64,
278
279 /// Maximum time-to-live in seconds for room summary entries in the spaces
280 /// cache.
281 ///
282 /// reloadable: yes
283 /// default: 129600
284 #[serde(default = "default_spacehierarchy_cache_ttl_max")]
285 pub spacehierarchy_cache_ttl_max: u64,
286
287 /// Minimum timeout a client can request for long-polling sync. Requests
288 /// will be clamped up to this value if smaller.
289 ///
290 /// reloadable: yes
291 /// default: 5000
292 #[serde(default = "default_client_sync_timeout_min")]
293 pub client_sync_timeout_min: u64,
294
295 /// Default timeout for long-polling sync if a client does not request
296 /// another in their query-string.
297 ///
298 /// reloadable: yes
299 /// default: 30000
300 #[serde(default = "default_client_sync_timeout_default")]
301 pub client_sync_timeout_default: u64,
302
303 /// Maximum timeout a client can request for long-polling sync. Requests
304 /// will be clamped down to this value if larger.
305 ///
306 /// reloadable: yes
307 /// default: 90000
308 #[serde(default = "default_client_sync_timeout_max")]
309 pub client_sync_timeout_max: u64,
310
311 /// Maximum entries stored in DNS memory-cache. The size of an entry may
312 /// vary so please take care if raising this value excessively. Only
313 /// decrease this when using an external DNS cache. Please note that
314 /// systemd-resolved does *not* count as an external cache, even when
315 /// configured to do so.
316 ///
317 /// default: 32768
318 #[serde(default = "default_dns_cache_entries")]
319 pub dns_cache_entries: u32,
320
321 /// Minimum time-to-live in seconds for entries in the DNS cache. The
322 /// default may appear high to most administrators; this is by design as the
323 /// exotic loads of federating to many other servers require a higher TTL
324 /// than many domains have set. Even when using an external DNS cache the
325 /// problem is shifted to that cache which is ignorant of its role for
326 /// this application and can adhere to many low TTL's increasing its load.
327 ///
328 /// default: 10800
329 #[serde(default = "default_dns_min_ttl")]
330 pub dns_min_ttl: u64,
331
332 /// Minimum time-to-live in seconds for NXDOMAIN entries in the DNS cache.
333 /// This value is critical for the server to federate efficiently.
334 /// NXDOMAIN's are assumed to not be returning to the federation and
335 /// aggressively cached rather than constantly rechecked.
336 ///
337 /// Defaults to 3 days as these are *very rarely* false negatives.
338 ///
339 /// default: 259200
340 #[serde(default = "default_dns_min_ttl_nxdomain")]
341 pub dns_min_ttl_nxdomain: u64,
342
343 /// Number of DNS nameserver retries after a timeout or error.
344 ///
345 /// default: 10
346 #[serde(default = "default_dns_attempts")]
347 pub dns_attempts: u16,
348
349 /// The number of seconds to wait for a reply to a DNS query. Please note
350 /// that recursive queries can take up to several seconds for some domains,
351 /// so this value should not be too low, especially on slower hardware or
352 /// resolvers.
353 ///
354 /// default: 10
355 #[serde(default = "default_dns_timeout")]
356 pub dns_timeout: u64,
357
358 /// Fallback to TCP on DNS errors. Set this to false if unsupported by
359 /// nameserver.
360 #[serde(default = "true_fn")]
361 pub dns_tcp_fallback: bool,
362
363 /// Enable to query all nameservers until the domain is found. Referred to
364 /// as "trust_negative_responses" in hickory_resolver. This can avoid
365 /// useless DNS queries if the first nameserver responds with NXDOMAIN or
366 /// an empty NOERROR response.
367 #[serde(default = "true_fn")]
368 pub query_all_nameservers: bool,
369
370 /// Enable using *only* TCP for querying your specified nameservers instead
371 /// of UDP.
372 ///
373 /// If you are running tuwunel in a container environment, this config
374 /// option may need to be enabled. For more details, see:
375 /// https://tuwunel.chat/troubleshooting.html#potential-dns-issues-when-using-docker
376 #[serde(default)]
377 pub query_over_tcp_only: bool,
378
379 /// DNS A/AAAA record lookup strategy
380 ///
381 /// Takes a number of one of the following options:
382 /// 1 - Ipv4Only (Only query for A records, no AAAA/IPv6)
383 ///
384 /// 2 - Ipv6Only (Only query for AAAA records, no A/IPv4)
385 ///
386 /// 3 - Ipv4AndIpv6 (Query for A and AAAA records in parallel, uses whatever
387 /// returns a successful response first)
388 ///
389 /// 4 - Ipv6thenIpv4 (Query for AAAA record, if that fails then query the A
390 /// record)
391 ///
392 /// 5 - Ipv4thenIpv6 (Query for A record, if that fails then query the AAAA
393 /// record)
394 ///
395 /// If you don't have IPv6 networking, then for better DNS performance it
396 /// may be suitable to set this to Ipv4Only (1) as you will never ever use
397 /// the AAAA record contents even if the AAAA record is successful instead
398 /// of the A record.
399 ///
400 /// default: 5
401 #[serde(default = "default_ip_lookup_strategy")]
402 pub ip_lookup_strategy: u8,
403
404 /// List of domain patterns resolved via the alternative path without any
405 /// persistent cache, very small memory cache, and no enforced TTL. This
406 /// is intended for internal network and application services which require
407 /// these specific properties. This path does not support federation or
408 /// general purposes.
409 ///
410 /// reloadable: yes
411 /// example: ["*\.dns\.podman$"]
412 ///
413 /// default: []
414 #[serde(default, with = "serde_regex")]
415 pub dns_passthru_domains: RegexSet,
416
417 /// Whether to resolve appservices via the alternative path; setting this is
418 /// superior to providing domains in `dns_passthru_domains` if all
419 /// appservices intend to be matched anyway. The overhead of matching regex
420 /// and maintaining the list of domains can be avoided.
421 #[serde(default)]
422 pub dns_passthru_appservices: bool,
423
424 /// Enable or disable case randomization for DNS queries. This is a security
425 /// mitigation where answer spoofing is prevented by having to exactly match
426 /// the question. Occasional errors seen in logs which may have lead you
427 /// here tend to be from overloading DNS. Nevertheless for servers which
428 /// are truly incapable this can be set to false.
429 ///
430 /// This currently defaults to false due to user reports regarding some
431 /// popular DNS caches which may or may not be patched soon. It may again
432 /// default to true in an upcoming release.
433 #[serde(default)]
434 pub dns_case_randomization: bool,
435
436 /// Max request size for file uploads. Accepts an integer byte count or a
437 /// string with SI/IEC suffix such as "24 MiB".
438 ///
439 /// default: 24 MiB
440 #[serde(
441 default = "default_max_request_size",
442 deserialize_with = "deserialize_bytesize_usize"
443 )]
444 pub max_request_size: usize,
445
446 /// Maximum number of concurrently pending (asynchronous) media uploads a
447 /// user can have.
448 ///
449 /// reloadable: yes
450 /// default: 5
451 #[serde(default = "default_max_pending_media_uploads")]
452 pub max_pending_media_uploads: usize,
453
454 /// The time in seconds before an unused pending MXC URI expires and is
455 /// removed.
456 ///
457 /// reloadable: yes
458 /// default: 86400 (24 hours)
459 #[serde(default = "default_media_create_unused_expiration_time")]
460 pub media_create_unused_expiration_time: u64,
461
462 /// The maximum number of media create requests per second allowed from a
463 /// single user.
464 ///
465 /// reloadable: yes
466 /// default: 10
467 #[serde(default = "default_media_rc_create_per_second")]
468 pub media_rc_create_per_second: u32,
469
470 /// The maximum burst count for media create requests from a single user.
471 ///
472 /// reloadable: yes
473 /// default: 50
474 #[serde(default = "default_media_rc_create_burst_count")]
475 pub media_rc_create_burst_count: u32,
476
477 /// reloadable: yes
478 /// default: 192
479 #[serde(default = "default_max_fetch_prev_events")]
480 pub max_fetch_prev_events: u16,
481
482 /// Default/base connection timeout (seconds). This is used only by URL
483 /// previews and update/news endpoint checks.
484 ///
485 /// default: 10
486 #[serde(default = "default_request_conn_timeout")]
487 pub request_conn_timeout: u64,
488
489 /// Default/base request timeout (seconds). The time waiting to receive more
490 /// data from another server. This is used only by URL previews,
491 /// update/news, and misc endpoint checks.
492 ///
493 /// default: 35
494 #[serde(default = "default_request_timeout")]
495 pub request_timeout: u64,
496
497 /// Default/base request total timeout (seconds). The time limit for a whole
498 /// request. This is set very high to not cancel healthy requests while
499 /// serving as a backstop. This is used only by URL previews and update/news
500 /// endpoint checks.
501 ///
502 /// default: 320
503 #[serde(default = "default_request_total_timeout")]
504 pub request_total_timeout: u64,
505
506 /// Default/base idle connection pool timeout (seconds). This is used only
507 /// by URL previews and update/news endpoint checks.
508 ///
509 /// default: 5
510 #[serde(default = "default_request_idle_timeout")]
511 pub request_idle_timeout: u64,
512
513 /// Default/base max idle connections per host. This is used only by URL
514 /// previews and update/news endpoint checks. Defaults to 1 as generally the
515 /// same open connection can be re-used.
516 ///
517 /// default: 1
518 #[serde(default = "default_request_idle_per_host")]
519 pub request_idle_per_host: u16,
520
521 /// Federation well-known resolution connection timeout (seconds).
522 ///
523 /// default: 6
524 #[serde(default = "default_well_known_conn_timeout")]
525 pub well_known_conn_timeout: u64,
526
527 /// Federation HTTP well-known resolution request timeout (seconds).
528 ///
529 /// default: 10
530 #[serde(default = "default_well_known_timeout")]
531 pub well_known_timeout: u64,
532
533 /// Federation client request timeout (seconds). You most definitely want
534 /// this to be high to account for extremely large room joins, slow
535 /// homeservers, your own resources etc.
536 ///
537 /// default: 300
538 #[serde(default = "default_federation_timeout")]
539 pub federation_timeout: u64,
540
541 /// Federation client idle connection pool timeout (seconds).
542 ///
543 /// default: 25
544 #[serde(default = "default_federation_idle_timeout")]
545 pub federation_idle_timeout: u64,
546
547 /// Federation client max idle connections per host. Defaults to 1 as
548 /// generally the same open connection can be re-used.
549 ///
550 /// default: 1
551 #[serde(default = "default_federation_idle_per_host")]
552 pub federation_idle_per_host: u16,
553
554 /// Federation sender request timeout (seconds). The time it takes for the
555 /// remote server to process sent transactions can take a while.
556 ///
557 /// default: 180
558 #[serde(default = "default_sender_timeout")]
559 pub sender_timeout: u64,
560
561 /// Federation sender idle connection pool timeout (seconds).
562 ///
563 /// default: 180
564 #[serde(default = "default_sender_idle_timeout")]
565 pub sender_idle_timeout: u64,
566
567 /// Federation sender transaction retry backoff limit (seconds).
568 ///
569 /// reloadable: yes
570 /// default: 86400
571 #[serde(default = "default_sender_retry_backoff_limit")]
572 pub sender_retry_backoff_limit: u64,
573
574 /// Appservice URL request connection timeout. Defaults to 35 seconds as
575 /// generally appservices are hosted within the same network.
576 ///
577 /// default: 35
578 #[serde(default = "default_appservice_timeout")]
579 pub appservice_timeout: u64,
580
581 /// Appservice URL idle connection pool timeout (seconds).
582 ///
583 /// default: 300
584 #[serde(default = "default_appservice_idle_timeout")]
585 pub appservice_idle_timeout: u64,
586
587 /// Notification gateway pusher idle connection pool timeout.
588 ///
589 /// default: 15
590 #[serde(default = "default_pusher_idle_timeout")]
591 pub pusher_idle_timeout: u64,
592
593 /// Maximum time to receive a request from a client (seconds).
594 ///
595 /// default: 75
596 #[serde(default = "default_client_receive_timeout")]
597 pub client_receive_timeout: u64,
598
599 /// Maximum time to process a request received from a client (seconds).
600 ///
601 /// default: 240
602 #[serde(default = "default_client_request_timeout")]
603 pub client_request_timeout: u64,
604
605 /// Maximum time to transmit a response to a client (seconds)
606 ///
607 /// default: 120
608 #[serde(default = "default_client_response_timeout")]
609 pub client_response_timeout: u64,
610
611 /// Grace period for clean shutdown of client requests (seconds).
612 ///
613 /// reloadable: yes
614 /// default: 10
615 #[serde(default = "default_client_shutdown_timeout")]
616 pub client_shutdown_timeout: u64,
617
618 /// Source of the client IP address for rate limiting, logging, and
619 /// security tooling.
620 ///
621 /// When unset (the default), the `ClientIp` extractor falls back to
622 /// `axum-client-ip`'s `InsecureClientIp` for backward compatibility;
623 /// clients can spoof their address via request headers in that mode.
624 ///
625 /// When set, tuwunel installs `SecureClientIpSource` with the selected
626 /// variant and `ClientIp` resolves exclusively from that source. The
627 /// rightmost value is used for multi-valued headers; only the proxy can
628 /// append to the right, so this is resistant to client spoofing.
629 ///
630 /// Supported values:
631 /// - "connect_info" - TCP peer address only (direct connections)
632 /// - "rightmost_x_forwarded_for" - nginx, Caddy
633 /// - "rightmost_forwarded" - RFC 7239 proxies
634 /// - "x_real_ip" - nginx `X-Real-IP`
635 /// - "cf_connecting_ip" - Cloudflare / cloudflared
636 /// - "true_client_ip" - Akamai, Cloudflare Enterprise
637 /// - "fly_client_ip" - Fly.io
638 /// - "cloudfront_viewer_address" - AWS CloudFront
639 ///
640 /// On Unix-socket deployments, leave this unset rather than setting
641 /// "connect_info"; that source requires a TCP peer address.
642 ///
643 /// WARNING: a header-based value without a trusted reverse proxy in
644 /// front of tuwunel allows clients to forge their IP. Changing this
645 /// value requires a server restart.
646 ///
647 /// default: unset
648 /// config-example: "connect_info"
649 #[serde(default)]
650 pub ip_source: Option<IpSource>,
651
652 /// Grace period for clean shutdown of federation requests (seconds).
653 ///
654 /// reloadable: yes
655 /// default: 5
656 #[serde(default = "default_sender_shutdown_timeout")]
657 pub sender_shutdown_timeout: u64,
658
659 /// Enables registration. If set to false, no users can register on this
660 /// server.
661 ///
662 /// If set to true without a token configured, users can register with no
663 /// form of 2nd-step only if you set the following option to true:
664 /// `yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse`
665 ///
666 /// If you would like registration only via token reg, please configure
667 /// `registration_token` or `registration_token_file`.
668 /// reloadable: yes
669 #[serde(default)]
670 pub allow_registration: bool,
671
672 /// Enabling this setting opens registration to anyone without restrictions.
673 /// This makes your server vulnerable to abuse
674 /// reloadable: yes
675 #[serde(default)]
676 pub yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse: bool,
677
678 /// A static registration token that new users will have to provide when
679 /// creating an account. If unset and `allow_registration` is true,
680 /// you must set
681 /// `yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse`
682 /// to true to allow open registration without any conditions.
683 ///
684 /// YOU NEED TO EDIT THIS OR USE registration_token_file.
685 ///
686 /// reloadable: yes
687 /// example: "o&^uCtes4HPf0Vu@F20jQeeWE7"
688 ///
689 /// display: sensitive
690 pub registration_token: Option<String>,
691
692 /// Path to a file on the system that gets read for additional registration
693 /// tokens. Multiple tokens can be added if you separate them with
694 /// whitespace
695 ///
696 /// tuwunel must be able to access the file, and it must not be empty
697 ///
698 /// reloadable: yes
699 /// example: "/etc/tuwunel/.reg_token"
700 pub registration_token_file: Option<PathBuf>,
701
702 /// Controls whether encrypted rooms and events are allowed.
703 /// reloadable: yes
704 #[serde(default = "true_fn")]
705 pub allow_encryption: bool,
706
707 /// Controls whether locally-created rooms should be end-to-end encrypted by
708 /// default. This option is equivalent to the one found in Synapse.
709 ///
710 /// Options:
711 /// - "all": All created rooms are encrypted.
712 /// - "invite": Any room created with `private_chat` or
713 /// `trusted_private_chat` presets.
714 /// - "none": Explicit value for no effect.
715 /// - Other values default to no effect.
716 ///
717 /// reloadable: yes
718 /// default: "none"
719 #[serde(default)]
720 pub encryption_enabled_by_default_for_room_type: Option<String>,
721
722 /// Controls whether federation is allowed or not. It is not recommended to
723 /// disable this after installation due to potential federation breakage but
724 /// this is technically not a permanent setting.
725 #[serde(default = "true_fn")]
726 pub allow_federation: bool,
727
728 /// Sets the default `m.federate` property for newly created rooms when the
729 /// client does not request one. If `allow_federation` is set to false at
730 /// the same this value is set to false it then always overrides the client
731 /// requested `m.federate` value to false.
732 ///
733 /// Rooms are fixed to the setting at the time of their creation and can
734 /// never be changed; changing this value only affects new rooms.
735 /// reloadable: yes
736 #[serde(default = "true_fn")]
737 pub federate_created_rooms: bool,
738
739 /// Allows federation requests to be made to itself
740 ///
741 /// This isn't intended and is very likely a bug if federation requests are
742 /// being sent to yourself. This currently mainly exists for development
743 /// purposes.
744 /// reloadable: yes
745 #[serde(default)]
746 pub federation_loopback: bool,
747
748 /// Always calls /forget on behalf of the user if leaving a room. This is a
749 /// part of MSC4267 "Automatically forgetting rooms on leave"
750 /// reloadable: yes
751 #[serde(default)]
752 pub forget_forced_upon_leave: bool,
753
754 /// Set this to true to require authentication on the normally
755 /// unauthenticated profile retrieval endpoints (GET)
756 /// "/_matrix/client/v3/profile/{userId}".
757 ///
758 /// This can prevent profile scraping.
759 /// reloadable: yes
760 #[serde(default)]
761 pub require_auth_for_profile_requests: bool,
762
763 /// Preserve per-room profile overrides during a global profile update.
764 ///
765 /// When `true` (default), a profile change (displayname or avatar_url)
766 /// arriving via the profile endpoints skips rooms whose current
767 /// `m.room.member` already differs from the user's prior global
768 /// profile. This is the natural behavior users expect after setting a
769 /// per-room nickname or avatar with a client's `/myroomnick`-style
770 /// command: a subsequent global change does not clobber the override.
771 ///
772 /// Set to `false` to always rewrite every joined room's member event
773 /// to match the new global profile. That matches the literal spec
774 /// reading.
775 ///
776 /// MSC4466 lets clients pick this per request via the
777 /// `org.matrix.msc4466.propagate_to` query parameter
778 /// (`all` / `unchanged` / `none`); an explicit value overrides this
779 /// default in either direction.
780 ///
781 /// reloadable: yes
782 /// default: true
783 #[serde(default = "true_fn")]
784 pub preserve_room_profile_overrides: bool,
785
786 /// Set this to true to allow your server's public room directory to be
787 /// federated. Set this to false to protect against /publicRooms spiders,
788 /// but will forbid external users from viewing your server's public room
789 /// directory. If federation is disabled entirely (`allow_federation`), this
790 /// is inherently false.
791 /// reloadable: yes
792 #[serde(default)]
793 pub allow_public_room_directory_over_federation: bool,
794
795 /// Set this to true to allow your server's public room directory to be
796 /// queried without client authentication (access token) through the Client
797 /// APIs. Set this to false to protect against /publicRooms spiders.
798 /// reloadable: yes
799 #[serde(default)]
800 pub allow_public_room_directory_without_auth: bool,
801
802 /// Allows room directory searches to match on partial room_id's when the
803 /// search term starts with '!'.
804 ///
805 /// reloadable: yes
806 /// default: true
807 #[serde(default = "true_fn")]
808 pub allow_public_room_search_by_id: bool,
809
810 /// Set this to false to limit results of rooms when searching by ID to
811 /// those that would be found by an alias or other query; specifically
812 /// those listed in the public rooms directory. By default this is set to
813 /// true allowing any joinable room to match. This satisfies the Principle
814 /// of Least Expectation when pasting a room_id into a search box with
815 /// intent to join; many rooms simply opt-out of public listings. Therefor
816 /// to prevent this feature from abuse, knowledge of several characters of
817 /// the room_id is required before any results are returned.
818 ///
819 /// reloadable: yes
820 /// default: true
821 #[serde(default = "true_fn")]
822 pub allow_unlisted_room_search_by_id: bool,
823
824 /// Show all local users in user directory. With this set to false, only
825 /// users in public rooms or those that share a room with the user making
826 /// the search will be shown.
827 ///
828 /// reloadable: yes
829 /// default: false
830 #[serde(default)]
831 pub show_all_local_users_in_user_directory: bool,
832
833 /// Allow guests/unauthenticated users to access TURN credentials.
834 ///
835 /// This is the equivalent of Synapse's `turn_allow_guests` config option.
836 /// This allows any unauthenticated user to call the endpoint
837 /// `/_matrix/client/v3/voip/turnServer`.
838 ///
839 /// It is unlikely you need to enable this as all major clients support
840 /// authentication for this endpoint and prevents misuse of your TURN server
841 /// from potential bots.
842 /// reloadable: yes
843 #[serde(default)]
844 pub turn_allow_guests: bool,
845
846 /// Set this to true to lock down your server's public room directory and
847 /// only allow admins to publish rooms to the room directory. Unpublishing
848 /// is still allowed by all users with this enabled.
849 /// reloadable: yes
850 #[serde(default)]
851 pub lockdown_public_room_directory: bool,
852
853 /// Set this to true to allow federating device display names / allow
854 /// external users to see your device display name. If federation is
855 /// disabled entirely (`allow_federation`), this is inherently false. For
856 /// privacy reasons, this is best left disabled.
857 /// reloadable: yes
858 #[serde(default)]
859 pub allow_device_name_federation: bool,
860
861 /// Config option to allow or disallow incoming federation requests that
862 /// obtain the profiles of our local users from
863 /// `/_matrix/federation/v1/query/profile`
864 ///
865 /// Increases privacy of your local user's such as display names, but some
866 /// remote users may get a false "this user does not exist" error when they
867 /// try to invite you to a DM or room. Also can protect against profile
868 /// spiders.
869 ///
870 /// This is inherently false if `allow_federation` is disabled
871 /// reloadable: yes
872 #[serde(
873 default = "true_fn",
874 alias = "allow_profile_lookup_federation_requests"
875 )]
876 pub allow_inbound_profile_lookup_federation_requests: bool,
877
878 /// Allow standard users to create rooms. Appservices and admins are always
879 /// allowed to create rooms
880 /// reloadable: yes
881 #[serde(default = "true_fn")]
882 pub allow_room_creation: bool,
883
884 /// Set to false to disable users from joining or creating room versions
885 /// that aren't officially supported by tuwunel. Unstable room versions may
886 /// have flawed specifications or our implementation may be non-conforming.
887 /// Correct operation may not be guaranteed, but incorrect operation may be
888 /// tolerable and unnoticed.
889 ///
890 /// tuwunel officially supports room versions 6+. tuwunel has slightly
891 /// experimental (though works fine in practice) support for versions 3 - 5.
892 ///
893 /// reloadable: yes
894 /// default: true
895 #[serde(default = "true_fn")]
896 pub allow_unstable_room_versions: bool,
897
898 /// Set to true to enable experimental room versions.
899 ///
900 /// Unlike unstable room versions these versions are either under
901 /// development, protype spec-changes, or somehow present a serious risk to
902 /// the server's operation or database corruption. This is for developer use
903 /// only.
904 /// reloadable: yes
905 #[serde(default)]
906 pub allow_experimental_room_versions: bool,
907
908 /// MSC4284: ask the room's policy server to sign outgoing events. When a
909 /// room has a valid `m.room.policy` state event, the homeserver requests a
910 /// signature from that policy server's federation `/sign` endpoint before
911 /// federating each event. Refusal aborts the local request; network or
912 /// timeout failures fail open with a warn log so a transient policy-server
913 /// outage does not silently take the room offline.
914 ///
915 /// reloadable: yes
916 /// default: false
917 #[serde(default)]
918 pub enable_policy_servers: bool,
919
920 /// MSC4284: timeout (seconds) for requests to a room's policy server.
921 /// Applies to both outbound `/sign` calls and inbound signature-fetches.
922 ///
923 /// reloadable: yes
924 /// default: 5
925 #[serde(default = "default_policy_server_request_timeout")]
926 pub policy_server_request_timeout: u64,
927
928 /// Default room version tuwunel will create rooms with.
929 ///
930 /// The default is prescribed by the spec, but may be selected by developer
931 /// recommendation. To prevent stale documentation we no longer list it
932 /// here. It is only advised to override this if you know what you are
933 /// doing, and by doing so, updates with new versions are precluded.
934 /// reloadable: yes
935 #[serde(default = "default_default_room_version")]
936 pub default_room_version: RoomVersionId,
937
938 // external structure; separate section
939 #[serde(default)]
940 pub well_known: WellKnownConfig,
941
942 #[serde(default)]
943 pub allow_jaeger: bool,
944
945 /// default: "info"
946 #[serde(default = "default_jaeger_filter")]
947 pub jaeger_filter: String,
948
949 /// If the 'perf_measurements' compile-time feature is enabled, enables
950 /// collecting folded stack trace profile of tracing spans using
951 /// tracing_flame. The resulting profile can be visualized with inferno[1],
952 /// speedscope[2], or a number of other tools.
953 ///
954 /// [1]: https://github.com/jonhoo/inferno
955 /// [2]: www.speedscope.app
956 #[serde(default)]
957 pub tracing_flame: bool,
958
959 /// default: "info"
960 #[serde(default = "default_tracing_flame_filter")]
961 pub tracing_flame_filter: String,
962
963 /// default: "./tracing.folded"
964 #[serde(default = "default_tracing_flame_output_path")]
965 pub tracing_flame_output_path: String,
966
967 #[cfg(not(doctest))]
968 /// Examples:
969 ///
970 /// - No proxy (default):
971 ///
972 /// proxy = "none"
973 ///
974 /// - For global proxy, create the section at the bottom of this file:
975 ///
976 /// [global.proxy]
977 /// global = { url = "socks5h://localhost:9050" }
978 ///
979 /// - To proxy some domains:
980 ///
981 /// [global.proxy]
982 /// [[global.proxy.by_domain]]
983 /// url = "socks5h://localhost:9050"
984 /// include = ["*.onion", "matrix.myspecial.onion"]
985 /// exclude = ["*.myspecial.onion"]
986 ///
987 /// Include vs. Exclude:
988 ///
989 /// - If include is an empty list, it is assumed to be `["*"]`.
990 ///
991 /// - If a domain matches both the exclude and include list, the proxy will
992 /// only be used if it was included because of a more specific rule than
993 /// it was excluded. In the above example, the proxy would be used for
994 /// `ordinary.onion`, `matrix.myspecial.onion`, but not
995 /// `hello.myspecial.onion`.
996 ///
997 /// default: "none"
998 #[serde(default)]
999 pub proxy: ProxyConfig,
1000
1001 #[expect(clippy::doc_link_with_quotes)]
1002 /// Servers listed here will be used to gather public keys of other servers
1003 /// (notary trusted key servers).
1004 ///
1005 /// Currently, tuwunel doesn't support inbound batched key requests, so
1006 /// this list should only contain other Synapse servers.
1007 ///
1008 /// reloadable: yes
1009 /// example: ["matrix.org", "tchncs.de"]
1010 ///
1011 /// default: ["matrix.org"]
1012 #[serde(default = "default_trusted_servers")]
1013 pub trusted_servers: Vec<OwnedServerName>,
1014
1015 /// Whether to query the servers listed in trusted_servers first or query
1016 /// the origin server first. For best security, querying the origin server
1017 /// first is advised to minimize the exposure to a compromised trusted
1018 /// server. For maximum federation/join performance this can be set to true,
1019 /// however other options exist to query trusted servers first under
1020 /// specific high-load circumstances and should be evaluated before setting
1021 /// this to true.
1022 /// reloadable: yes
1023 #[serde(default)]
1024 pub query_trusted_key_servers_first: bool,
1025
1026 /// Whether to query the servers listed in trusted_servers first
1027 /// specifically on room joins. This option limits the exposure to a
1028 /// compromised trusted server to room joins only. The join operation
1029 /// requires gathering keys from many origin servers which can cause
1030 /// significant delays. Therefor this defaults to true to mitigate
1031 /// unexpected delays out-of-the-box. The security-paranoid or those willing
1032 /// to tolerate delays are advised to set this to false. Note that setting
1033 /// query_trusted_key_servers_first to true causes this option to be
1034 /// ignored.
1035 /// reloadable: yes
1036 #[serde(default = "true_fn")]
1037 pub query_trusted_key_servers_first_on_join: bool,
1038
1039 /// Only query trusted servers for keys and never the origin server. This is
1040 /// intended for clusters or custom deployments using their trusted_servers
1041 /// as forwarding-agents to cache and deduplicate requests. Notary servers
1042 /// do not act as forwarding-agents by default, therefor do not enable this
1043 /// unless you know exactly what you are doing.
1044 /// reloadable: yes
1045 #[serde(default)]
1046 pub only_query_trusted_key_servers: bool,
1047
1048 /// Maximum number of keys to request in each trusted server batch query.
1049 ///
1050 /// reloadable: yes
1051 /// default: 192
1052 #[serde(default = "default_trusted_server_batch_size")]
1053 pub trusted_server_batch_size: usize,
1054
1055 /// Maximum number of request batches in flight simultaneously when querying
1056 /// a trusted server.
1057 ///
1058 /// reloadable: yes
1059 /// default: 2
1060 #[serde(default = "default_trusted_server_batch_concurrency")]
1061 pub trusted_server_batch_concurrency: usize,
1062
1063 /// Max log level for tuwunel. Allows debug, info, warn, or error.
1064 ///
1065 /// See also:
1066 /// https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives
1067 ///
1068 /// **Caveat**:
1069 /// For release builds, the tracing crate is configured to only implement
1070 /// levels higher than error to avoid unnecessary overhead in the compiled
1071 /// binary from trace macros. For debug builds, this restriction is not
1072 /// applied.
1073 ///
1074 /// default: "info"
1075 #[serde(default = "default_log")]
1076 pub log: String,
1077
1078 /// Output logs with ANSI colours.
1079 #[serde(default = "true_fn", alias = "log_colours")]
1080 pub log_colors: bool,
1081
1082 /// Sets the log format to compact mode.
1083 #[serde(default)]
1084 pub log_compact: bool,
1085
1086 /// Configures the span events which will be outputted with the log.
1087 ///
1088 /// default: "none"
1089 #[serde(default = "default_log_span_events")]
1090 pub log_span_events: String,
1091
1092 /// Configures whether TUWUNEL_LOG EnvFilter matches values using regular
1093 /// expressions. See the tracing_subscriber documentation on Directives.
1094 ///
1095 /// default: true
1096 #[serde(default = "true_fn")]
1097 pub log_filter_regex: bool,
1098
1099 /// Toggles the display of ThreadId in tracing log output.
1100 ///
1101 /// default: false
1102 #[serde(default)]
1103 pub log_thread_ids: bool,
1104
1105 /// Redirects logging to standard error (stderr). The default is false for
1106 /// stdout. For those using our systemd features the redirection to stderr
1107 /// occurs as necessary and setting this option should not be required. We
1108 /// offer this option for all other users who desire such redirection.
1109 ///
1110 /// default: false
1111 #[serde(default)]
1112 pub log_to_stderr: bool,
1113
1114 /// Setting to false disables the logging/tracing system at a lower level.
1115 /// In contrast to configuring an empty `log` string where the system is
1116 /// still operating but muted, when this option is false the system was not
1117 /// initialized and is not operating. Changing this option has no effect
1118 /// after startup. This option is intended for developers and expert use
1119 /// only: configuring an empty log string is preferred over using this.
1120 ///
1121 /// default: true
1122 #[serde(default = "true_fn")]
1123 pub log_enable: bool,
1124
1125 /// Setting to false disables the logging/tracing system at a lower level
1126 /// similar to `log_enable`. In this case the system is configured normally,
1127 /// but not registered as the global handler in the final steps. This option
1128 /// is for developers and expert use only.
1129 ///
1130 /// default: true
1131 #[serde(default = "true_fn")]
1132 pub log_global_default: bool,
1133
1134 /// OpenID token expiration/TTL in seconds.
1135 ///
1136 /// These are the OpenID tokens that are primarily used for Matrix account
1137 /// integrations (e.g. Vector Integrations in Element), *not* OIDC/OpenID
1138 /// Connect/etc.
1139 ///
1140 /// reloadable: yes
1141 /// default: 3600
1142 #[serde(default = "default_openid_token_ttl")]
1143 pub openid_token_ttl: u64,
1144
1145 /// Allow an existing session to mint a login token for another client.
1146 /// This requires interactive authentication, but has security ramifications
1147 /// as a malicious client could use the mechanism to spawn more than one
1148 /// session. Enabled by default.
1149 ///
1150 /// reloadable: yes
1151 /// default: true
1152 #[serde(default = "true_fn")]
1153 pub login_via_existing_session: bool,
1154
1155 /// Whether to enable the login token route to accept login tokens at all.
1156 /// Login tokens may be generated by the server for authorization flows such
1157 /// as SSO; disabling tokens may break such features.
1158 ///
1159 /// This option is distinct from `login_via_existing_session` and does not
1160 /// carry the same security implications; the intent is to leave this
1161 /// enabled while disabling the former to prevent clients from commanding
1162 /// login token creation but without preventing the server from doing so.
1163 ///
1164 /// reloadable: yes
1165 /// default: true
1166 #[serde(default = "true_fn")]
1167 pub login_via_token: bool,
1168
1169 /// Whether to enable login using traditional user/password authorization
1170 /// flow.
1171 ///
1172 /// Set this option to false if you intend to allow logging in only using
1173 /// other mechanisms, such as SSO.
1174 ///
1175 /// reloadable: yes
1176 /// default: true
1177 #[serde(default = "true_fn")]
1178 pub login_with_password: bool,
1179
1180 /// Login token expiration/TTL in milliseconds.
1181 ///
1182 /// These are short-lived tokens for the m.login.token endpoint.
1183 /// This is used to allow existing sessions to create new sessions.
1184 /// see login_via_existing_session.
1185 ///
1186 /// reloadable: yes
1187 /// default: 120000
1188 #[serde(default = "default_login_token_ttl")]
1189 pub login_token_ttl: u64,
1190
1191 /// Access token TTL in seconds.
1192 ///
1193 /// For clients that support refresh-tokens, the access-token provided on
1194 /// login will be invalidated after this amount of time and the client will
1195 /// be soft-logged-out until refreshing it.
1196 ///
1197 /// reloadable: yes
1198 /// default: 604800
1199 #[serde(default = "default_access_token_ttl")]
1200 pub access_token_ttl: u64,
1201
1202 /// Refresh token TTL in seconds.
1203 ///
1204 /// Refresh tokens are rejected once this lifetime elapses. Whether the
1205 /// deadline slides forward on each use or stays fixed at issuance is
1206 /// controlled by `refresh_token_idle_only`. The default of `0` disables
1207 /// refresh-token expiry entirely; a typical enabled value is `259200`
1208 /// (three days).
1209 ///
1210 /// reloadable: yes
1211 /// default: 0
1212 #[serde(default)]
1213 pub refresh_token_ttl: u64,
1214
1215 /// Whether `refresh_token_ttl` acts as an idle timeout or an absolute
1216 /// session lifetime.
1217 ///
1218 /// When `true` (default), each successful refresh resets the deadline to
1219 /// `now + refresh_token_ttl`. A session in continuous use never expires.
1220 /// When `false`, the deadline is fixed at first issuance and rotation
1221 /// carries it forward, forcing re-auth after `refresh_token_ttl`
1222 /// regardless of activity.
1223 ///
1224 /// reloadable: yes
1225 /// default: true
1226 #[serde(default = "true_fn")]
1227 pub refresh_token_idle_only: bool,
1228
1229 /// Whether refresh-token expiry triggers a hard logout instead of a soft
1230 /// one.
1231 ///
1232 /// When `false` (default), an expired refresh token is rejected with
1233 /// `M_UNKNOWN_TOKEN` carrying `soft_logout: true`. The client can preserve
1234 /// E2EE keys and local state, then re-authenticate to resume the same
1235 /// device.
1236 ///
1237 /// When `true`, the device is removed entirely on expiry: the access
1238 /// token is invalidated, the device record is deleted, and the client is
1239 /// signalled with `soft_logout: false`. The next session is a brand-new
1240 /// device, so the client cannot recover E2EE history from local state
1241 /// alone; this is the CWE-613 stance and trades usability for that
1242 /// guarantee.
1243 ///
1244 /// reloadable: yes
1245 /// default: false
1246 #[serde(default)]
1247 pub refresh_token_hard_logout: bool,
1248
1249 /// Static TURN username to provide the client if not using a shared secret
1250 /// ("turn_secret"), It is recommended to use a shared secret over static
1251 /// credentials.
1252 /// reloadable: yes
1253 #[serde(default)]
1254 pub turn_username: String,
1255
1256 /// Static TURN password to provide the client if not using a shared secret
1257 /// ("turn_secret"). It is recommended to use a shared secret over static
1258 /// credentials.
1259 ///
1260 /// display: sensitive
1261 /// reloadable: yes
1262 #[serde(default)]
1263 pub turn_password: String,
1264
1265 #[expect(clippy::doc_link_with_quotes)]
1266 /// Vector list of TURN URIs/servers to use.
1267 ///
1268 /// Replace "example.turn.uri" with your TURN domain, such as the coturn
1269 /// "realm" config option. If using TURN over TLS, replace the URI prefix
1270 /// "turn:" with "turns:".
1271 ///
1272 /// reloadable: yes
1273 /// example: ["turn:example.turn.uri?transport=udp",
1274 /// "turn:example.turn.uri?transport=tcp"]
1275 ///
1276 /// default: []
1277 #[serde(default)]
1278 pub turn_uris: Vec<String>,
1279
1280 /// TURN secret to use for generating the HMAC-SHA1 hash apart of username
1281 /// and password generation.
1282 ///
1283 /// This is more secure, but if needed you can use traditional static
1284 /// username/password credentials.
1285 ///
1286 /// display: sensitive
1287 #[serde(default)]
1288 pub turn_secret: Option<String>,
1289
1290 /// TURN secret to use that's read from the file path specified.
1291 ///
1292 /// This takes priority over "turn_secret" first, and falls back to
1293 /// "turn_secret" if invalid or failed to open.
1294 ///
1295 /// example: "/etc/tuwunel/.turn_secret"
1296 pub turn_secret_file: Option<PathBuf>,
1297
1298 /// TURN TTL, in seconds.
1299 ///
1300 /// reloadable: yes
1301 /// default: 86400
1302 #[serde(default = "default_turn_ttl")]
1303 pub turn_ttl: u64,
1304
1305 #[expect(clippy::doc_link_with_quotes)]
1306 /// List/vector of room IDs or room aliases that tuwunel will make newly
1307 /// registered users join. The rooms specified must be rooms that you have
1308 /// joined at least once on the server, and must be public.
1309 ///
1310 /// reloadable: yes
1311 /// example: ["#tuwunel:grin.hu",
1312 /// "!l2xV0sd51lraysuRcsWVECge4NULaH3g-ou95vgDgiM"]
1313 ///
1314 /// default: []
1315 #[serde(default = "Vec::new")]
1316 pub auto_join_rooms: Vec<OwnedRoomOrAliasId>,
1317
1318 /// Config option to automatically deactivate the account of any user who
1319 /// attempts to join a:
1320 /// - banned room
1321 /// - forbidden room alias
1322 /// - room alias or ID with a forbidden server name
1323 ///
1324 /// This may be useful if all your banned lists consist of toxic rooms or
1325 /// servers that no good faith user would ever attempt to join, and
1326 /// to automatically remediate the problem without any admin user
1327 /// intervention.
1328 ///
1329 /// This will also make the user leave all rooms. Federation (e.g. remote
1330 /// room invites) are ignored here.
1331 ///
1332 /// Defaults to false as rooms can be banned for non-moderation-related
1333 /// reasons and this performs a full user deactivation.
1334 /// reloadable: yes
1335 #[serde(default)]
1336 pub auto_deactivate_banned_room_attempts: bool,
1337
1338 /// RocksDB log level. This is not the same as tuwunel's log level. This
1339 /// is the log level for the RocksDB engine/library which show up in your
1340 /// database folder/path as `LOG` files. tuwunel will log RocksDB errors
1341 /// as normal through tracing or panics if severe for safety.
1342 ///
1343 /// default: "error"
1344 #[serde(default = "default_rocksdb_log_level")]
1345 pub rocksdb_log_level: String,
1346
1347 #[serde(default)]
1348 pub rocksdb_log_stderr: bool,
1349
1350 /// Max RocksDB `LOG` file size before rotating. Accepts an integer byte
1351 /// count or a string with SI/IEC suffix such as "4 MiB".
1352 ///
1353 /// default: 4194304
1354 #[serde(
1355 default = "default_rocksdb_max_log_file_size",
1356 deserialize_with = "deserialize_bytesize_usize"
1357 )]
1358 pub rocksdb_max_log_file_size: usize,
1359
1360 /// Time in seconds before RocksDB will forcibly rotate logs.
1361 ///
1362 /// default: 0
1363 #[serde(default = "default_rocksdb_log_time_to_roll")]
1364 pub rocksdb_log_time_to_roll: usize,
1365
1366 /// Use RocksDB tunings tailored to spinning disks (HDDs). On NVMe or SSD
1367 /// storage, leave this disabled.
1368 ///
1369 /// When enabled, RocksDB skips compaction readahead and parallel file-open
1370 /// threads at startup. This option does not affect Direct IO; for that, see
1371 /// `rocksdb_direct_io`.
1372 #[serde(default)]
1373 pub rocksdb_optimize_for_spinning_disks: bool,
1374
1375 /// Enables direct-io to increase database performance via unbuffered I/O.
1376 ///
1377 /// For more details about direct I/O and RockDB, see:
1378 /// https://github.com/facebook/rocksdb/wiki/Direct-IO
1379 ///
1380 /// Set this option to false if the database resides on a filesystem which
1381 /// does not support direct-io like FUSE, or any form of complex filesystem
1382 /// setup such as possibly ZFS.
1383 #[serde(default = "true_fn")]
1384 pub rocksdb_direct_io: bool,
1385
1386 /// Amount of threads that RocksDB will use for parallelism on database
1387 /// operations such as cleanup, sync, flush, compaction, etc. Set to 0 to
1388 /// use all your logical threads. Defaults to your CPU logical thread count.
1389 ///
1390 /// default: varies by system
1391 #[serde(default = "default_rocksdb_parallelism_threads")]
1392 pub rocksdb_parallelism_threads: usize,
1393
1394 /// Maximum number of LOG files RocksDB will keep. This must *not* be set to
1395 /// 0. It must be at least 1. Defaults to 3 as these are not very useful
1396 /// unless troubleshooting/debugging a RocksDB bug.
1397 ///
1398 /// default: 3
1399 #[serde(default = "default_rocksdb_max_log_files")]
1400 pub rocksdb_max_log_files: usize,
1401
1402 /// Type of RocksDB database compression to use.
1403 ///
1404 /// Available options are "zstd", "bz2", "lz4", or "none".
1405 ///
1406 /// It is best to use ZSTD as an overall good balance between
1407 /// speed/performance, storage, IO amplification, and CPU usage. For more
1408 /// performance but less compression (more storage used) and less CPU usage,
1409 /// use LZ4.
1410 ///
1411 /// For more details, see:
1412 /// https://github.com/facebook/rocksdb/wiki/Compression
1413 ///
1414 /// "none" will disable compression.
1415 ///
1416 /// default: "zstd"
1417 #[serde(default = "default_rocksdb_compression_algo")]
1418 pub rocksdb_compression_algo: String,
1419
1420 /// Level of compression the specified compression algorithm for RocksDB to
1421 /// use.
1422 ///
1423 /// Default is 32767, which is internally read by RocksDB as the default
1424 /// magic number and translated to the library's default compression level
1425 /// as they all differ. See their `kDefaultCompressionLevel`.
1426 ///
1427 /// Note when using the default value we may override it with a setting
1428 /// tailored specifically tuwunel.
1429 ///
1430 /// default: 32767
1431 #[serde(default = "default_rocksdb_compression_level")]
1432 pub rocksdb_compression_level: i32,
1433
1434 /// Level of compression the specified compression algorithm for the
1435 /// bottommost level/data for RocksDB to use. Default is 32767, which is
1436 /// internally read by RocksDB as the default magic number and translated to
1437 /// the library's default compression level as they all differ. See their
1438 /// `kDefaultCompressionLevel`.
1439 ///
1440 /// Since this is the bottommost level (generally old and least used data),
1441 /// it may be desirable to have a very high compression level here as it's
1442 /// less likely for this data to be used. Research your chosen compression
1443 /// algorithm.
1444 ///
1445 /// Note when using the default value we may override it with a setting
1446 /// tailored specifically tuwunel.
1447 ///
1448 /// default: 32767
1449 #[serde(default = "default_rocksdb_bottommost_compression_level")]
1450 pub rocksdb_bottommost_compression_level: i32,
1451
1452 /// Whether to enable RocksDB's "bottommost_compression".
1453 ///
1454 /// At the expense of more CPU usage, this will further compress the
1455 /// database to reduce more storage. It is recommended to use ZSTD
1456 /// compression with this for best compression results. This may be useful
1457 /// if you're trying to reduce storage usage from the database.
1458 ///
1459 /// See https://github.com/facebook/rocksdb/wiki/Compression for more details.
1460 #[serde(default = "true_fn")]
1461 pub rocksdb_bottommost_compression: bool,
1462
1463 /// Database recovery mode (for RocksDB WAL corruption).
1464 ///
1465 /// Use this option when the server reports corruption and refuses to start.
1466 /// Set mode 2 (PointInTime) to cleanly recover from this corruption. The
1467 /// server will continue from the last good state, several seconds or
1468 /// minutes prior to the crash. Clients may have to run "clear-cache &
1469 /// reload" to account for the rollback. Upon success, you may reset the
1470 /// mode back to default and restart again. Please note in some cases the
1471 /// corruption error may not be cleared for at least 30 minutes of operation
1472 /// in PointInTime mode.
1473 ///
1474 /// As a very last ditch effort, if PointInTime does not fix or resolve
1475 /// anything, you can try mode 3 (SkipAnyCorruptedRecord) but this will
1476 /// leave the server in a potentially inconsistent state.
1477 ///
1478 /// The default mode 1 (TolerateCorruptedTailRecords) will automatically
1479 /// drop the last entry in the database if corrupted during shutdown, but
1480 /// nothing more. It is extraordinarily unlikely this will desynchronize
1481 /// clients. To disable any form of silent rollback set mode 0
1482 /// (AbsoluteConsistency).
1483 ///
1484 /// The options are:
1485 /// 0 = AbsoluteConsistency
1486 /// 1 = TolerateCorruptedTailRecords (default)
1487 /// 2 = PointInTime (use me if trying to recover)
1488 /// 3 = SkipAnyCorruptedRecord (you now voided your tuwunel warranty)
1489 ///
1490 /// For more information on these modes, see:
1491 /// https://github.com/facebook/rocksdb/wiki/WAL-Recovery-Modes
1492 ///
1493 /// For more details on recovering a corrupt database, see:
1494 /// https://tuwunel.chat/troubleshooting.html#database-corruption
1495 ///
1496 /// default: 1
1497 #[serde(default = "default_rocksdb_recovery_mode")]
1498 pub rocksdb_recovery_mode: u8,
1499
1500 /// Enables or disables paranoid SST file checks. This can improve RocksDB
1501 /// database consistency at a potential performance impact due to further
1502 /// safety checks ran.
1503 ///
1504 /// For more information, see:
1505 /// https://github.com/facebook/rocksdb/wiki/Online-Verification#columnfamilyoptionsparanoid_file_checks
1506 #[serde(default)]
1507 pub rocksdb_paranoid_file_checks: bool,
1508
1509 /// Enables or disables checksum verification in rocksdb at runtime.
1510 /// Checksums are usually hardware accelerated with low overhead; they are
1511 /// enabled in rocksdb by default. Older or slower platforms may see gains
1512 /// from disabling.
1513 ///
1514 /// default: true
1515 #[serde(default = "true_fn")]
1516 pub rocksdb_checksums: bool,
1517
1518 /// Enables the "atomic flush" mode in rocksdb. This option is not intended
1519 /// for users. It may be removed or ignored in future versions. Atomic flush
1520 /// may be enabled by the paranoid to possibly improve database integrity at
1521 /// the cost of performance.
1522 #[serde(default)]
1523 pub rocksdb_atomic_flush: bool,
1524
1525 /// Database repair mode (for RocksDB SST corruption).
1526 ///
1527 /// Use this option when the server reports corruption while running or
1528 /// panics. If the server refuses to start use the recovery mode options
1529 /// first. Corruption errors containing the acronym 'SST' which occur after
1530 /// startup will likely require this option.
1531 ///
1532 /// - Backing up your database directory is recommended prior to running the
1533 /// repair.
1534 ///
1535 /// - Disabling repair mode and restarting the server is recommended after
1536 /// running the repair.
1537 ///
1538 /// See https://tuwunel.chat/troubleshooting.html#database-corruption for more details on recovering a corrupt database.
1539 #[serde(default)]
1540 pub rocksdb_repair: bool,
1541
1542 #[serde(default)]
1543 pub rocksdb_read_only: bool,
1544
1545 #[serde(default)]
1546 pub rocksdb_secondary: bool,
1547
1548 /// Enables idle CPU priority for compaction thread. This is not enabled by
1549 /// default to prevent compaction from falling too far behind on busy
1550 /// systems.
1551 #[serde(default)]
1552 pub rocksdb_compaction_prio_idle: bool,
1553
1554 /// Enables idle IO priority for compaction thread. This prevents any
1555 /// unexpected lag in the server's operation and is usually a good idea.
1556 /// Enabled by default.
1557 #[serde(default = "true_fn")]
1558 pub rocksdb_compaction_ioprio_idle: bool,
1559
1560 /// Enables RocksDB compaction. You should never ever have to set this
1561 /// option to false. If you for some reason find yourself needing to use
1562 /// this option as part of troubleshooting or a bug, please reach out to us
1563 /// in the tuwunel Matrix room with information and details.
1564 ///
1565 /// Disabling compaction will lead to a significantly bloated and
1566 /// explosively large database, gradually poor performance, unnecessarily
1567 /// excessive disk read/writes, and slower shutdowns and startups.
1568 #[serde(default = "true_fn")]
1569 pub rocksdb_compaction: bool,
1570
1571 /// Level of statistics collection. Some admin commands to display database
1572 /// statistics may require this option to be set. Database performance may
1573 /// be impacted by higher settings.
1574 ///
1575 /// Option is a number ranging from 0 to 6:
1576 /// 0 = No statistics.
1577 /// 1 = No statistics in release mode (default).
1578 /// 2 to 3 = Statistics with no performance impact.
1579 /// 3 to 5 = Statistics with possible performance impact.
1580 /// 6 = All statistics.
1581 ///
1582 /// default: 1
1583 #[serde(default = "default_rocksdb_stats_level")]
1584 pub rocksdb_stats_level: u8,
1585
1586 /// Ignores the list of dropped columns set by developers.
1587 ///
1588 /// This should be set to true when knowingly moving between versions in
1589 /// ways which are not recommended or otherwise forbidden, or for
1590 /// diagnostic and development purposes; requiring preservation across such
1591 /// movements.
1592 ///
1593 /// The developer's list of dropped columns is meant to safely reduce space
1594 /// by erasing data no longer in use. If this is set to true that storage
1595 /// will not be reclaimed as intended.
1596 ///
1597 /// default: false
1598 #[serde(default)]
1599 pub rocksdb_never_drop_columns: bool,
1600
1601 /// Configures RocksDB to not preallocate WAL logs.
1602 ///
1603 /// Normally, RocksDB allocates certain types of files by calling
1604 /// fallocate, writing the file contents, then truncating the logs to the
1605 /// proper size. This causes pathological disk space usage on btrfs due to
1606 /// how it interacts with its Copy-on-Write implementation. On ZFS,
1607 /// fallocate(2) for preallocation is unsupported and returns EOPNOTSUPP;
1608 /// only `FALLOC_FL_PUNCH_HOLE` and `FALLOC_FL_ZERO_RANGE` are implemented.
1609 ///
1610 /// Set this to false if you run the server on btrfs or ZFS, and do not
1611 /// touch it otherwise.
1612 ///
1613 /// default: true
1614 #[serde(default = "true_fn")]
1615 pub rocksdb_allow_fallocate: bool,
1616
1617 /// This is a password that can be configured that will let you login to the
1618 /// server bot account (currently `@conduit`) for emergency troubleshooting
1619 /// purposes such as recovering/recreating your admin room, or inviting
1620 /// yourself back.
1621 ///
1622 /// See https://tuwunel.chat/troubleshooting.html#lost-access-to-admin-room
1623 /// for other ways to get back into your admin room.
1624 ///
1625 /// Once this password is unset, all sessions will be logged out for
1626 /// security purposes.
1627 ///
1628 /// example: "F670$2CP@Hw8mG7RY1$%!#Ic7YA"
1629 ///
1630 /// display: sensitive
1631 pub emergency_password: Option<String>,
1632
1633 /// reloadable: yes
1634 /// default: "/_matrix/push/v1/notify"
1635 #[serde(default = "default_notification_push_path")]
1636 pub notification_push_path: String,
1637
1638 /// For compatibility and special purpose use only. Setting this option to
1639 /// true will not filter messages sent to pushers based on rules or actions.
1640 /// Everything will be sent to the pusher. This option is offered for
1641 /// several reasons, but should not be necessary:
1642 /// - Bypass to workaround bugs or outdated server-side ruleset support.
1643 /// - Allow clients to evaluate pushrules themselves (due to the above).
1644 /// - Hosting or companies which have custom pushers and internal needs.
1645 ///
1646 /// Note that setting this option to true will not affect the record of
1647 /// notifications found in the notifications pane.
1648 /// reloadable: yes
1649 #[serde(default)]
1650 pub push_everything: bool,
1651
1652 /// Setting to false disables the heroes calculation made by sliding and
1653 /// legacy client sync. The heroes calculation is mandated by the Matrix
1654 /// specification and your client may not operate properly unless this
1655 /// option is set to true.
1656 ///
1657 /// This option is intended for custom software deployments seeking purely
1658 /// to minimize unused resources; the overall savings are otherwise
1659 /// negligible.
1660 /// reloadable: yes
1661 #[serde(default = "true_fn")]
1662 pub calculate_heroes: bool,
1663
1664 /// Allow local (your server only) presence updates/requests.
1665 ///
1666 /// Note that presence on tuwunel is very fast unlike Synapse's. If using
1667 /// outgoing presence, this MUST be enabled.
1668 /// reloadable: yes
1669 #[serde(default = "true_fn")]
1670 pub allow_local_presence: bool,
1671
1672 /// Allow incoming federated presence updates/requests.
1673 ///
1674 /// This option receives presence updates from other servers, but does not
1675 /// send any unless `allow_outgoing_presence` is true. Note that presence on
1676 /// tuwunel is very fast unlike Synapse's.
1677 /// reloadable: yes
1678 #[serde(default = "true_fn")]
1679 pub allow_incoming_presence: bool,
1680
1681 /// Allow outgoing presence updates/requests.
1682 ///
1683 /// This option sends presence updates to other servers, but does not
1684 /// receive any unless `allow_incoming_presence` is true. Note that presence
1685 /// on tuwunel is very fast unlike Synapse's. If using outgoing presence,
1686 /// you MUST enable `allow_local_presence` as well.
1687 /// reloadable: yes
1688 #[serde(default = "true_fn")]
1689 pub allow_outgoing_presence: bool,
1690
1691 /// How many seconds without presence updates before you become idle.
1692 /// Defaults to 5 minutes.
1693 ///
1694 /// default: 300
1695 #[serde(default = "default_presence_idle_timeout_s")]
1696 pub presence_idle_timeout_s: u64,
1697
1698 /// How many seconds without presence updates before you become offline.
1699 /// Defaults to 30 minutes.
1700 ///
1701 /// default: 1800
1702 #[serde(default = "default_presence_offline_timeout_s")]
1703 pub presence_offline_timeout_s: u64,
1704
1705 /// Enable the presence idle timer for remote users.
1706 ///
1707 /// Disabling is offered as an optimization for servers participating in
1708 /// many large rooms or when resources are limited. Disabling it may cause
1709 /// incorrect presence states (i.e. stuck online) to be seen for some remote
1710 /// users.
1711 #[serde(default = "true_fn")]
1712 pub presence_timeout_remote_users: bool,
1713
1714 /// Suppresses push notifications for users marked as active. (Experimental)
1715 ///
1716 /// When enabled, users with `Online` presence and recent activity
1717 /// (based on presence state and sync activity) won’t receive push
1718 /// notifications, reducing duplicate alerts while they're active
1719 /// on another client.
1720 ///
1721 /// Disabled by default to preserve legacy behavior.
1722 /// reloadable: yes
1723 #[serde(default)]
1724 pub suppress_push_when_active: bool,
1725
1726 /// Allow receiving incoming read receipts from remote servers.
1727 /// reloadable: yes
1728 #[serde(default = "true_fn")]
1729 pub allow_incoming_read_receipts: bool,
1730
1731 /// Allow sending read receipts to remote servers.
1732 /// reloadable: yes
1733 #[serde(default = "true_fn")]
1734 pub allow_outgoing_read_receipts: bool,
1735
1736 /// Allow outgoing typing updates to federation.
1737 /// reloadable: yes
1738 #[serde(default = "true_fn")]
1739 pub allow_outgoing_typing: bool,
1740
1741 /// Allow incoming typing updates from federation.
1742 /// reloadable: yes
1743 #[serde(default = "true_fn")]
1744 pub allow_incoming_typing: bool,
1745
1746 /// Maximum time federation user can indicate typing.
1747 ///
1748 /// reloadable: yes
1749 /// default: 30
1750 #[serde(default = "default_typing_federation_timeout_s")]
1751 pub typing_federation_timeout_s: u64,
1752
1753 /// Minimum time local client can indicate typing. This does not override a
1754 /// client's request to stop typing. It only enforces a minimum value in
1755 /// case of no stop request.
1756 ///
1757 /// reloadable: yes
1758 /// default: 15
1759 #[serde(default = "default_typing_client_timeout_min_s")]
1760 pub typing_client_timeout_min_s: u64,
1761
1762 /// Maximum time local client can indicate typing.
1763 ///
1764 /// reloadable: yes
1765 /// default: 45
1766 #[serde(default = "default_typing_client_timeout_max_s")]
1767 pub typing_client_timeout_max_s: u64,
1768
1769 /// Set this to true for tuwunel to compress HTTP response bodies using
1770 /// zstd. This option does nothing if tuwunel was not built with
1771 /// `zstd_compression` feature. Please be aware that enabling HTTP
1772 /// compression may weaken TLS. Most users should not need to enable this.
1773 /// See https://breachattack.com/ and https://wikipedia.org/wiki/BREACH
1774 /// before deciding to enable this.
1775 #[serde(default)]
1776 pub zstd_compression: bool,
1777
1778 /// Set this to true for tuwunel to compress HTTP response bodies using
1779 /// gzip. This option does nothing if tuwunel was not built with
1780 /// `gzip_compression` feature. Please be aware that enabling HTTP
1781 /// compression may weaken TLS. Most users should not need to enable this.
1782 /// See https://breachattack.com/ and https://wikipedia.org/wiki/BREACH before
1783 /// deciding to enable this.
1784 ///
1785 /// If you are in a large amount of rooms, you may find that enabling this
1786 /// is necessary to reduce the significantly large response bodies.
1787 #[serde(default)]
1788 pub gzip_compression: bool,
1789
1790 /// Set this to true for tuwunel to compress HTTP response bodies using
1791 /// brotli. This option does nothing if tuwunel was not built with
1792 /// `brotli_compression` feature. Please be aware that enabling HTTP
1793 /// compression may weaken TLS. Most users should not need to enable this.
1794 /// See https://breachattack.com/ and https://wikipedia.org/wiki/BREACH
1795 /// before deciding to enable this.
1796 #[serde(default)]
1797 pub brotli_compression: bool,
1798
1799 /// Set to true to allow user type "guest" registrations. Some clients like
1800 /// Element attempt to register guest users automatically.
1801 /// reloadable: yes
1802 #[serde(default)]
1803 pub allow_guest_registration: bool,
1804
1805 /// Set to true to log guest registrations in the admin room. Note that
1806 /// these may be noisy or unnecessary if you're a public homeserver.
1807 /// reloadable: yes
1808 #[serde(default)]
1809 pub log_guest_registrations: bool,
1810
1811 /// Set to true to allow guest registrations/users to auto join any rooms
1812 /// specified in `auto_join_rooms`.
1813 /// reloadable: yes
1814 #[serde(default)]
1815 pub allow_guests_auto_join_rooms: bool,
1816
1817 /// Enable the legacy unauthenticated Matrix media repository endpoints.
1818 /// These endpoints consist of:
1819 /// - /_matrix/media/*/config
1820 /// - /_matrix/media/*/upload
1821 /// - /_matrix/media/*/preview_url
1822 /// - /_matrix/media/*/download/*
1823 /// - /_matrix/media/*/thumbnail/*
1824 ///
1825 /// The authenticated equivalent endpoints are always enabled.
1826 ///
1827 /// Defaults to false.
1828 #[serde(default)]
1829 pub allow_legacy_media: bool,
1830
1831 /// Fallback to requesting legacy unauthenticated media from remote servers.
1832 /// Unauthenticated media was removed in ~2024Q3; enabling this adds
1833 /// considerable federation requests which are unlikely to succeed.
1834 /// reloadable: yes
1835 #[serde(default)]
1836 pub request_legacy_media: bool,
1837
1838 /// reloadable: yes
1839 #[serde(default = "true_fn")]
1840 pub freeze_legacy_media: bool,
1841
1842 /// Check consistency of the media directory at startup:
1843 /// 1. When `media_compat_file_link` is enabled, this check will upgrade
1844 /// media when switching back and forth between Conduit and tuwunel. Both
1845 /// options must be enabled to handle this.
1846 /// 2. When media is deleted from the directory, this check will also delete
1847 /// its database entry.
1848 ///
1849 /// If none of these checks apply to your use cases, and your media
1850 /// directory is significantly large setting this to false may reduce
1851 /// startup time.
1852 #[serde(default = "true_fn")]
1853 pub media_startup_check: bool,
1854
1855 /// Enable backward-compatibility with Conduit's media directory by creating
1856 /// symlinks of media.
1857 ///
1858 /// This option is only necessary if you plan on using Conduit again.
1859 /// Otherwise setting this to false reduces filesystem clutter and overhead
1860 /// for managing these symlinks in the directory. This is now disabled by
1861 /// default. You may still return to upstream Conduit but you have to run
1862 /// tuwunel at least once with this set to true and allow the
1863 /// media_startup_check to take place before shutting down to return to
1864 /// Conduit.
1865 #[serde(default)]
1866 pub media_compat_file_link: bool,
1867
1868 /// Prune missing media from the database as part of the media startup
1869 /// checks.
1870 ///
1871 /// This means if you delete files from the media directory the
1872 /// corresponding entries will be removed from the database. This is
1873 /// disabled by default because if the media directory is accidentally moved
1874 /// or inaccessible, the metadata entries in the database will be lost with
1875 /// sadness.
1876 #[serde(default)]
1877 pub prune_missing_media: bool,
1878
1879 /// List of storage providers to use for media. Providers can be configured
1880 /// below in respective sections designated by
1881 /// `global.storage_provider.<NAME>.<brand>` where `NAME` can be listed
1882 /// here.
1883 ///
1884 /// For advanced features and future extensions involving multiple providers
1885 /// the list may contain multiple entries. You MUST take note of other
1886 /// configuration options when listing multiple providers or resource
1887 /// duplication costs and poor performance can result.
1888 ///
1889 /// The list defaults to `["media"]` which is an implicit storage provider
1890 /// representing the media directory on the local filesystem. It can be
1891 /// altered by configuring `global.storage_provider.media.local` explicitly
1892 /// or disabled by omitting it from this list entirely. Users with existing
1893 /// deployments are advised to continue listing "media" as a fallback along
1894 /// with their new provider.
1895 ///
1896 /// reloadable: yes
1897 /// default: ["media"]
1898 #[serde(default = "default_media_storage_providers")]
1899 pub media_storage_providers: BTreeSet<String>,
1900
1901 /// List of configured storage providers where new media will be sent. When
1902 /// this list is not explicitly configured all entries in
1903 /// `media_storage_providers` are used as default.
1904 ///
1905 /// This list is important for users passively migrating to a new media
1906 /// storage provider by only writing to one while querying the other as a
1907 /// fallback.
1908 ///
1909 /// For example:
1910 ///
1911 /// `media_storage_providers = ["media", "media_on_s3"]`
1912 /// `store_media_on_providers = ["media_on_s3"]`
1913 ///
1914 /// Entries in this list must also be listed in `media_storage_providers`.
1915 ///
1916 /// reloadable: yes
1917 /// default: []
1918 #[serde(default)]
1919 pub store_media_on_providers: BTreeSet<String>,
1920
1921 /// Vector list of regex patterns of server names that tuwunel will refuse
1922 /// to download remote media from.
1923 ///
1924 /// reloadable: yes
1925 /// example: ["badserver\.tld$", "badphrase", "19dollarfortnitecards"]
1926 ///
1927 /// default: []
1928 #[serde(default, with = "serde_regex")]
1929 pub prevent_media_downloads_from: RegexSet,
1930
1931 /// List of forbidden server names via regex patterns that we will block
1932 /// incoming AND outgoing federation with, and block client room joins /
1933 /// remote user invites.
1934 ///
1935 /// This check is applied on the room ID, room alias, sender server name,
1936 /// sender user's server name, inbound federation X-Matrix origin, and
1937 /// outbound federation handler.
1938 ///
1939 /// Basically "global" ACLs.
1940 ///
1941 /// reloadable: yes
1942 /// example: ["badserver\.tld$", "badphrase", "19dollarfortnitecards"]
1943 ///
1944 /// default: []
1945 #[serde(default, with = "serde_regex")]
1946 pub forbidden_remote_server_names: RegexSet,
1947
1948 /// (EXPERIMENTAL) The behavior of this option will change; the
1949 /// _experimental suffix will be removed for that change in an upcoming
1950 /// release.
1951 ///
1952 /// List of allowed server names via regex patterns. This is an allow-list
1953 /// rather than a deny-list with all the same details as its counterpart in
1954 /// `forbidden_remote_server_names`.
1955 ///
1956 /// This feature becomes active when this list has one or more entries;
1957 /// everything not matching is denied. By default it is empty and inactive.
1958 ///
1959 /// Entries in `forbidden_remote_server_names` are still applied after
1960 /// this is applied. This allows you to match e.g. "*\.example\.com" here
1961 /// while still singling out "bad\.example\.com" for exclusion.
1962 ///
1963 /// reloadable: yes
1964 /// example: ["badserver\.tld$", "badphrase", "19dollarfortnitecards"]
1965 ///
1966 /// default: []
1967 #[serde(default, with = "serde_regex")]
1968 pub allowed_remote_server_names_experimental: RegexSet,
1969
1970 /// List of forbidden server names via regex patterns that we will block all
1971 /// outgoing federated room directory requests for. Useful for preventing
1972 /// our users from wandering into bad servers or spaces.
1973 ///
1974 /// reloadable: yes
1975 /// example: ["badserver\.tld$", "badphrase", "19dollarfortnitecards"]
1976 ///
1977 /// default: []
1978 #[serde(default, with = "serde_regex")]
1979 pub forbidden_remote_room_directory_server_names: RegexSet,
1980
1981 #[expect(clippy::doc_link_with_quotes)]
1982 /// Vector list of IPv4 and IPv6 CIDR ranges / subnets *in quotes* that you
1983 /// do not want tuwunel to send outbound requests to. Defaults to
1984 /// RFC1918, unroutable, loopback, multicast, and testnet addresses for
1985 /// security.
1986 ///
1987 /// Please be aware that this is *not* a guarantee. You should be using a
1988 /// firewall with zones as doing this on the application layer may have
1989 /// bypasses.
1990 ///
1991 /// Currently this does not account for proxies in use like Synapse does.
1992 ///
1993 /// To disable, set this to be an empty vector (`[]`).
1994 ///
1995 /// Defaults to:
1996 /// ["127.0.0.0/8", "10.0.0.0/8", "172.16.0.0/12",
1997 /// "192.168.0.0/16", "100.64.0.0/10", "192.0.0.0/24", "169.254.0.0/16",
1998 /// "192.88.99.0/24", "198.18.0.0/15", "192.0.2.0/24", "198.51.100.0/24",
1999 /// "203.0.113.0/24", "224.0.0.0/4", "::1/128", "fe80::/10", "fc00::/7",
2000 /// "2001:db8::/32", "ff00::/8", "fec0::/10"]
2001 #[serde(default = "default_ip_range_denylist")]
2002 pub ip_range_denylist: Vec<String>,
2003
2004 /// Optional IP address or network interface-name to bind as the source of
2005 /// URL preview requests. If not set, it will not bind to a specific
2006 /// address or interface.
2007 ///
2008 /// Interface names only supported on Linux, Android, and Fuchsia platforms;
2009 /// all other platforms can specify the IP address. To list the interfaces
2010 /// on your system, use the command `ip link show`.
2011 ///
2012 /// example: `"eth0"` or `"1.2.3.4"`
2013 ///
2014 /// default:
2015 #[serde(default, with = "either::serde_untagged_optional")]
2016 pub url_preview_bound_interface: Option<Either<IpAddr, String>>,
2017
2018 /// Vector list of domains allowed to send requests to for URL previews.
2019 ///
2020 /// This is a *contains* match, not an explicit match. Putting "google.com"
2021 /// will match "https://google.com" and
2022 /// "http://mymaliciousdomainexamplegoogle.com" Setting this to "*" will
2023 /// allow all URL previews. Please note that this opens up significant
2024 /// attack surface to your server, you are expected to be aware of the risks
2025 /// by doing so.
2026 ///
2027 /// reloadable: yes
2028 /// default: []
2029 #[serde(default)]
2030 pub url_preview_domain_contains_allowlist: Vec<String>,
2031
2032 /// Vector list of explicit domains allowed to send requests to for URL
2033 /// previews.
2034 ///
2035 /// This is an *explicit* match, not a contains match. Putting "google.com"
2036 /// will match "https://google.com", "http://google.com", but not
2037 /// "https://mymaliciousdomainexamplegoogle.com". Setting this to "*" will
2038 /// allow all URL previews. Please note that this opens up significant
2039 /// attack surface to your server, you are expected to be aware of the risks
2040 /// by doing so.
2041 ///
2042 /// reloadable: yes
2043 /// default: []
2044 #[serde(default)]
2045 pub url_preview_domain_explicit_allowlist: Vec<String>,
2046
2047 /// Vector list of explicit domains not allowed to send requests to for URL
2048 /// previews.
2049 ///
2050 /// This is an *explicit* match, not a contains match. Putting "google.com"
2051 /// will match "https://google.com", "http://google.com", but not
2052 /// "https://mymaliciousdomainexamplegoogle.com". The denylist is checked
2053 /// first before allowlist. Setting this to "*" will not do anything.
2054 ///
2055 /// reloadable: yes
2056 /// default: []
2057 #[serde(default)]
2058 pub url_preview_domain_explicit_denylist: Vec<String>,
2059
2060 /// Vector list of URLs allowed to send requests to for URL previews.
2061 ///
2062 /// Note that this is a *contains* match, not an explicit match. Putting
2063 /// "google.com" will match "https://google.com/",
2064 /// "https://google.com/url?q=https://mymaliciousdomainexample.com", and
2065 /// "https://mymaliciousdomainexample.com/hi/google.com" Setting this to "*"
2066 /// will allow all URL previews. Please note that this opens up significant
2067 /// attack surface to your server, you are expected to be aware of the risks
2068 /// by doing so.
2069 ///
2070 /// reloadable: yes
2071 /// default: []
2072 #[serde(default)]
2073 pub url_preview_url_contains_allowlist: Vec<String>,
2074
2075 /// Maximum body size allowed when spidering a URL for previews. Accepts an
2076 /// integer byte count or a string with SI/IEC suffix such as "256 KB".
2077 ///
2078 /// reloadable: yes
2079 /// default: 256000
2080 #[serde(
2081 default = "default_url_preview_max_spider_size",
2082 deserialize_with = "deserialize_bytesize_usize"
2083 )]
2084 pub url_preview_max_spider_size: usize,
2085
2086 /// Option to decide whether you would like to run the domain allowlist
2087 /// checks (contains and explicit) on the root domain or not. Does not apply
2088 /// to URL contains allowlist. Defaults to false.
2089 ///
2090 /// Example usecase: If this is enabled and you have "wikipedia.org" allowed
2091 /// in the explicit and/or contains domain allowlist, it will allow all
2092 /// subdomains under "wikipedia.org" such as "en.m.wikipedia.org" as the
2093 /// root domain is checked and matched. Useful if the domain contains
2094 /// allowlist is still too broad for you but you still want to allow all the
2095 /// subdomains under a root domain.
2096 /// reloadable: yes
2097 #[serde(default)]
2098 pub url_preview_check_root_domain: bool,
2099
2100 /// List of forbidden room aliases and room IDs as strings of regex
2101 /// patterns.
2102 ///
2103 /// Regex can be used or explicit contains matches can be done by just
2104 /// specifying the words (see example).
2105 ///
2106 /// This is checked upon room alias creation, custom room ID creation if
2107 /// used, and startup as warnings if any room aliases in your database have
2108 /// a forbidden room alias/ID.
2109 ///
2110 /// reloadable: yes
2111 /// example: ["19dollarfortnitecards", "b[4a]droom", "badphrase"]
2112 ///
2113 /// default: []
2114 #[serde(default, with = "serde_regex")]
2115 pub forbidden_alias_names: RegexSet,
2116
2117 /// List of forbidden username patterns/strings.
2118 ///
2119 /// Regex can be used or explicit contains matches can be done by just
2120 /// specifying the words (see example).
2121 ///
2122 /// This is checked upon username availability check, registration, and
2123 /// startup as warnings if any local users in your database have a forbidden
2124 /// username.
2125 ///
2126 /// reloadable: yes
2127 /// example: ["administrator", "b[a4]dusernam[3e]", "badphrase"]
2128 ///
2129 /// default: []
2130 #[serde(default, with = "serde_regex")]
2131 pub forbidden_usernames: RegexSet,
2132
2133 /// List of server names to deprioritize joining through.
2134 ///
2135 /// If a client requests a join through one of these servers,
2136 /// they will be tried last.
2137 ///
2138 /// Useful for preventing failed joins due to timeouts
2139 /// from a certain homeserver.
2140 ///
2141 /// reloadable: yes
2142 /// default: ["matrix\.org"]
2143 #[serde(
2144 default = "default_deprioritize_joins_through_servers",
2145 with = "serde_regex"
2146 )]
2147 pub deprioritize_joins_through_servers: RegexSet,
2148
2149 /// Maximum make_join requests to attempt within each join attempt. Each
2150 /// attempt tries a different server, as each server is only tried once;
2151 /// though retries can occur when the join request as a whole is retried.
2152 ///
2153 /// reloadable: yes
2154 /// default: 48
2155 #[serde(default = "default_max_make_join_attempts_per_join_attempt")]
2156 pub max_make_join_attempts_per_join_attempt: usize,
2157
2158 /// Maximum join attempts to conduct per client join request. Each join
2159 /// attempt consists of one or more make_join requests limited above, and a
2160 /// single send_join request. This value allows for additional servers to
2161 /// act as the join-server prior to reporting the last error back to the
2162 /// client, which can be frustrating for users. Therefor the default value
2163 /// is greater than one, but less than excessively exceeding the client's
2164 /// request timeout, though that may not be avoidable in some cases.
2165 ///
2166 /// reloadable: yes
2167 /// default: 3
2168 #[serde(default = "default_max_join_attempts_per_join_request")]
2169 pub max_join_attempts_per_join_request: usize,
2170
2171 /// Retry failed and incomplete messages to remote servers immediately upon
2172 /// startup. This is called bursting. If this is disabled, said messages may
2173 /// not be delivered until more messages are queued for that server. Do not
2174 /// change this option unless server resources are extremely limited or the
2175 /// scale of the server's deployment is huge. Do not disable this unless you
2176 /// know what you are doing.
2177 #[serde(default = "true_fn")]
2178 pub startup_netburst: bool,
2179
2180 /// Messages are dropped and not reattempted. The `startup_netburst` option
2181 /// must be enabled for this value to have any effect. Do not change this
2182 /// value unless you know what you are doing. Set this value to -1 to
2183 /// reattempt every message without trimming the queues; this may consume
2184 /// significant disk. Set this value to 0 to drop all messages without any
2185 /// attempt at redelivery.
2186 ///
2187 /// default: 50
2188 #[serde(default = "default_startup_netburst_keep")]
2189 pub startup_netburst_keep: i64,
2190
2191 /// Block non-admin local users from sending room invites (local and
2192 /// remote), and block non-admin users from receiving remote room invites.
2193 ///
2194 /// Admins are always allowed to send and receive all room invites.
2195 /// reloadable: yes
2196 #[serde(default)]
2197 pub block_non_admin_invites: bool,
2198
2199 /// Allow admins to enter commands in rooms other than "#admins" (admin
2200 /// room) by prefixing your message with "\!admin" or "\\!admin" followed up
2201 /// a normal tuwunel admin command. The reply will be publicly visible to
2202 /// the room, originating from the sender.
2203 ///
2204 /// reloadable: yes
2205 /// example: \\!admin debug ping puppygock.gay
2206 #[serde(default = "true_fn")]
2207 pub admin_escape_commands: bool,
2208
2209 /// Automatically activate the tuwunel admin room console / CLI on
2210 /// startup. This option can also be enabled with `--console` tuwunel
2211 /// argument.
2212 #[serde(default)]
2213 pub admin_console_automatic: bool,
2214
2215 #[expect(clippy::doc_link_with_quotes)]
2216 /// List of admin commands to execute on startup.
2217 ///
2218 /// This option can also be configured with the `--execute` tuwunel
2219 /// argument and can take standard shell commands and environment variables
2220 ///
2221 /// For example: `./tuwunel --execute "server admin-notice tuwunel has
2222 /// started up at $(date)"`
2223 ///
2224 /// example: admin_execute = ["debug ping puppygock.gay", "debug echo hi"]`
2225 ///
2226 /// default: []
2227 #[serde(default)]
2228 pub admin_execute: Vec<String>,
2229
2230 /// Ignore errors in startup commands.
2231 ///
2232 /// If false, tuwunel will error and fail to start if an admin execute
2233 /// command (`--execute` / `admin_execute`) fails.
2234 /// reloadable: yes
2235 #[serde(default)]
2236 pub admin_execute_errors_ignore: bool,
2237
2238 /// List of admin commands to execute on SIGUSR2.
2239 ///
2240 /// Similar to admin_execute, but these commands are executed when the
2241 /// server receives SIGUSR2 on supporting platforms.
2242 ///
2243 /// reloadable: yes
2244 /// default: []
2245 #[serde(default)]
2246 pub admin_signal_execute: Vec<String>,
2247
2248 /// Controls the max log level for admin command log captures (logs
2249 /// generated from running admin commands). Defaults to "info" on release
2250 /// builds, else "debug" on debug builds.
2251 ///
2252 /// reloadable: yes
2253 /// default: "info"
2254 #[serde(default = "default_admin_log_capture")]
2255 pub admin_log_capture: String,
2256
2257 /// The default room tag to apply on the admin room.
2258 ///
2259 /// On some clients like Element, the room tag "m.server_notice" is a
2260 /// special pinned room at the very bottom of your room list. The tuwunel
2261 /// admin room can be pinned here so you always have an easy-to-access
2262 /// shortcut dedicated to your admin room.
2263 ///
2264 /// reloadable: yes
2265 /// default: "m.server_notice"
2266 #[serde(default = "default_admin_room_tag")]
2267 pub admin_room_tag: String,
2268
2269 /// Whether to grant the first user to register admin privileges by joining
2270 /// them to the admin room. Note that technically the next user to register
2271 /// when the admin room is empty (or only contains the server-user) is
2272 /// granted, and only when the admin room is enabled.
2273 ///
2274 /// reloadable: yes
2275 /// default: true
2276 #[serde(default = "true_fn")]
2277 pub grant_admin_to_first_user: bool,
2278
2279 /// Whether the admin room is created on first startup. Users should not set
2280 /// this to false. Developers can set this to false during integration tests
2281 /// to reduce activity and output.
2282 ///
2283 /// default: true
2284 #[serde(default = "true_fn")]
2285 pub create_admin_room: bool,
2286
2287 /// Whether to enable federation on the admin room. This cannot be changed
2288 /// after the admin room is created.
2289 ///
2290 /// default: true
2291 #[serde(default = "true_fn")]
2292 pub federate_admin_room: bool,
2293
2294 /// Sentry.io crash/panic reporting, performance monitoring/metrics, etc.
2295 /// This is NOT enabled by default. tuwunel's default Sentry reporting
2296 /// endpoint domain is `o4509498990067712.ingest.us.sentry.io`.
2297 #[serde(default)]
2298 pub sentry: bool,
2299
2300 /// Sentry reporting URL, if a custom one is desired.
2301 ///
2302 /// display: sensitive
2303 /// default: ""
2304 #[serde(default = "default_sentry_endpoint")]
2305 pub sentry_endpoint: Option<Url>,
2306
2307 /// Report your tuwunel server_name in Sentry.io crash reports and
2308 /// metrics.
2309 #[serde(default)]
2310 pub sentry_send_server_name: bool,
2311
2312 /// Performance monitoring/tracing sample rate for Sentry.io.
2313 ///
2314 /// Note that too high values may impact performance, and can be disabled by
2315 /// setting it to 0.0 (0%) This value is read as a percentage to Sentry,
2316 /// represented as a decimal. Defaults to 15% of traces (0.15)
2317 ///
2318 /// default: 0.15
2319 #[serde(default = "default_sentry_traces_sample_rate")]
2320 pub sentry_traces_sample_rate: f32,
2321
2322 /// Whether to attach a stacktrace to Sentry reports.
2323 #[serde(default)]
2324 pub sentry_attach_stacktrace: bool,
2325
2326 /// Send panics to Sentry. This is true by default, but Sentry has to be
2327 /// enabled. The global `sentry` config option must be enabled to send any
2328 /// data.
2329 #[serde(default = "true_fn")]
2330 pub sentry_send_panic: bool,
2331
2332 /// Send errors to sentry. This is true by default, but sentry has to be
2333 /// enabled. This option is only effective in release-mode; forced to false
2334 /// in debug-mode.
2335 #[serde(default = "true_fn")]
2336 pub sentry_send_error: bool,
2337
2338 /// Controls the tracing log level for Sentry to send things like
2339 /// breadcrumbs and transactions
2340 ///
2341 /// default: "info"
2342 #[serde(default = "default_sentry_filter")]
2343 pub sentry_filter: String,
2344
2345 /// Enable the tokio-console. This option is only relevant to developers.
2346 ///
2347 /// For more information, see:
2348 /// https://tuwunel.chat/development.html#debugging-with-tokio-console
2349 #[serde(default)]
2350 pub tokio_console: bool,
2351
2352 /// Arbitrary argument vector for integration testing. Functionality in the
2353 /// server is altered or informed for the requirements of integration tests.
2354 /// - "smoke" performs a shutdown after startup admin commands rather than
2355 /// hanging on client handling.
2356 ///
2357 /// display: hidden
2358 #[serde(default)]
2359 pub test: BTreeSet<String>,
2360
2361 /// Indicates the server has started in maintenance mode. Historically
2362 /// maintenance mode has been enabled by the command line argument
2363 /// `--maintenance` which then sets various configuration items such as
2364 /// `listening=false` among others. That is still the case. This option was
2365 /// only added as a single source of truth that `--maintenance` mode is
2366 /// active.
2367 ///
2368 /// This option must never be set manually.
2369 ///
2370 /// display: hidden
2371 #[serde(default)]
2372 pub maintenance: bool,
2373
2374 /// Controls whether admin room notices like account registrations, password
2375 /// changes, account deactivations, room directory publications, etc will be
2376 /// sent to the admin room. Update notices and normal admin command
2377 /// responses will still be sent.
2378 /// reloadable: yes
2379 #[serde(default = "true_fn")]
2380 pub admin_room_notices: bool,
2381
2382 /// Save original events before applying redaction to them.
2383 ///
2384 /// They can be retrieved with `admin debug get-retained-pdu` or MSC2815.
2385 ///
2386 /// reloadable: yes
2387 /// default: true
2388 #[serde(default = "true_fn")]
2389 pub save_unredacted_events: bool,
2390
2391 /// Redaction retention period in seconds.
2392 ///
2393 /// By default the unredacted events are stored for 60 days.
2394 ///
2395 /// reloadable: yes
2396 /// default: 5184000
2397 #[serde(default = "default_redaction_retention_seconds")]
2398 pub redaction_retention_seconds: u64,
2399
2400 /// Allows users with `redact` power level to request unredacted events with
2401 /// MSC2815.
2402 ///
2403 /// Server admins can request unredacted events regardless of the value of
2404 /// this option.
2405 ///
2406 /// reloadable: yes
2407 /// default: true
2408 #[serde(default = "true_fn")]
2409 pub allow_room_admins_to_request_unredacted_events: bool,
2410
2411 /// Prevents local users from sending redactions.
2412 ///
2413 /// This check does not apply to server admins.
2414 /// reloadable: yes
2415 #[serde(default)]
2416 pub disable_local_redactions: bool,
2417
2418 /// Enable database pool affinity support. On supporting systems, block
2419 /// device queue topologies are detected and the request pool is optimized
2420 /// for the hardware; db_pool_workers is determined automatically.
2421 ///
2422 /// default: true
2423 #[serde(default = "true_fn")]
2424 pub db_pool_affinity: bool,
2425
2426 /// Sets the number of worker threads in the frontend-pool of the database.
2427 /// This number should reflect the I/O capabilities of the system,
2428 /// such as the queue-depth or the number of simultaneous requests in
2429 /// flight. Defaults to 32 times the number of CPU cores.
2430 ///
2431 /// Note: This value is only used if db_pool_affinity is disabled or not
2432 /// detected on the system, otherwise it is determined automatically.
2433 ///
2434 /// default: 32
2435 #[serde(default = "default_db_pool_workers")]
2436 pub db_pool_workers: usize,
2437
2438 /// When db_pool_affinity is enabled and detected, the size of any worker
2439 /// group will not exceed the determined value. This is necessary when
2440 /// thread-pooling approach does not scale to the full capabilities of
2441 /// high-end hardware; using detected values without limitation could
2442 /// degrade performance.
2443 ///
2444 /// The value is multiplied by the number of cores which share a device
2445 /// queue, since group workers can be scheduled on any of those cores.
2446 ///
2447 /// default: 64
2448 #[serde(default = "default_db_pool_workers_limit")]
2449 pub db_pool_workers_limit: usize,
2450
2451 /// Limits the total number of workers across all worker groups. When the
2452 /// sum of all groups exceeds this value the worker counts are reduced until
2453 /// this constraint is satisfied.
2454 ///
2455 /// By default this value is only effective on larger systems (e.g. 16+
2456 /// cores) where it will tamper the overall thread-count. The thread-pool
2457 /// model will never achieve hardware capacity but this value can be raised
2458 /// on huge systems if the scheduling overhead is determined to not
2459 /// bottleneck and the worker groups are divided too small.
2460 ///
2461 /// default: 2048
2462 #[serde(default = "default_db_pool_max_workers")]
2463 pub db_pool_max_workers: usize,
2464
2465 /// Determines the size of the queues feeding the database's frontend-pool.
2466 /// The size of the queue is determined by multiplying this value with the
2467 /// number of pool workers. When this queue is full, tokio tasks conducting
2468 /// requests will yield until space is available; this is good for
2469 /// flow-control by avoiding buffer-bloat, but can inhibit throughput if
2470 /// too low.
2471 ///
2472 /// default: 4
2473 #[serde(default = "default_db_pool_queue_mult")]
2474 pub db_pool_queue_mult: usize,
2475
2476 /// Sets the initial value for the concurrency of streams. This value simply
2477 /// allows overriding the default in the code. The default is 32, which is
2478 /// the same as the default in the code. Note this value is itself
2479 /// overridden by the computed stream_width_scale, unless that is disabled;
2480 /// this value can serve as a fixed-width instead.
2481 ///
2482 /// default: 32
2483 #[serde(default = "default_stream_width_default")]
2484 pub stream_width_default: usize,
2485
2486 /// Scales the stream width starting from a base value detected for the
2487 /// specific system. The base value is the database pool worker count
2488 /// determined from the hardware queue size (e.g. 32 for SSD or 64 or 128+
2489 /// for NVMe). This float allows scaling the width up or down by multiplying
2490 /// it (e.g. 1.5, 2.0, etc). The maximum result can be the size of the pool
2491 /// queue (see: db_pool_queue_mult) as any larger value will stall the tokio
2492 /// task. The value can also be scaled down (e.g. 0.5) to improve
2493 /// responsiveness for many users at the cost of throughput for each.
2494 ///
2495 /// Setting this value to 0.0 causes the stream width to be fixed at the
2496 /// value of stream_width_default. The default scale is 1.0 to match the
2497 /// capabilities detected for the system.
2498 ///
2499 /// default: 1.0
2500 #[serde(default = "default_stream_width_scale")]
2501 pub stream_width_scale: f32,
2502
2503 /// Sets the initial amplification factor. This controls batch sizes of
2504 /// requests made by each pool worker, multiplying the throughput of each
2505 /// stream. This value is somewhat abstract from specific hardware
2506 /// characteristics and can be significantly larger than any thread count or
2507 /// queue size. This is because each database query may require several
2508 /// index lookups, thus many database queries in a batch may make progress
2509 /// independently while also sharing index and data blocks which may or may
2510 /// not be cached. It is worthwhile to submit huge batches to reduce
2511 /// complexity. The maximum value is 32768, though sufficient hardware is
2512 /// still advised for that.
2513 ///
2514 /// default: 1024
2515 #[serde(default = "default_stream_amplification")]
2516 pub stream_amplification: usize,
2517
2518 /// Number of sender task workers; determines sender parallelism. Default is
2519 /// '0' which means the value is determined internally, likely matching the
2520 /// number of tokio worker-threads or number of cores, etc. Override by
2521 /// setting a non-zero value.
2522 ///
2523 /// default: 0
2524 #[serde(default)]
2525 pub sender_workers: usize,
2526
2527 /// Enables listener sockets; can be set to false to disable listening. This
2528 /// option is intended for developer/diagnostic purposes only.
2529 #[serde(default = "true_fn")]
2530 pub listening: bool,
2531
2532 /// Enables configuration reload when the server receives SIGUSR1 on
2533 /// supporting platforms.
2534 ///
2535 /// reloadable: yes
2536 /// default: true
2537 #[serde(default = "true_fn")]
2538 pub config_reload_signal: bool,
2539
2540 /// Toggles ignore checking/validating TLS certificates
2541 ///
2542 /// This applies to everything, including URL previews, federation requests,
2543 /// etc. This is a hidden argument that should NOT be used in production as
2544 /// it is highly insecure and I will personally yell at you if I catch you
2545 /// using this.
2546 #[serde(default)]
2547 pub allow_invalid_tls_certificates: bool,
2548
2549 /// Sets the `Access-Control-Allow-Origin` header included by this server in
2550 /// all responses. A list of multiple values can be specified. The default
2551 /// is an empty list. The actual header defaults to `*` upon an empty list.
2552 ///
2553 /// There is no reason to configure this without specific intent. Incorrect
2554 /// values may degrade or disrupt clients.
2555 ///
2556 /// default: []
2557 #[serde(default)]
2558 pub access_control_allow_origin: BTreeSet<String>,
2559
2560 /// Backport state-reset security fixes to all room versions.
2561 ///
2562 /// This option applies the State Resolution 2.1 mitigation developed during
2563 /// project Hydra for room version 12 to all prior State Resolution 2.0 room
2564 /// versions (all room versions supported by this server). These mitigations
2565 /// increase resilience to state-resets without any new definition of
2566 /// correctness; therefor it is safe to set this to true for existing rooms.
2567 ///
2568 /// Furthermore, state-reset attacks are not consistent as they result in
2569 /// rooms without any single consensus, therefor it is unnecessary to set
2570 /// this to false to match other servers which set this to false or simply
2571 /// lack support; even if replicating the post-reset state suffered by other
2572 /// servers is somehow desired.
2573 ///
2574 /// This option exists for developer and debug use, and as a failsafe in
2575 /// lieu of hardcoding it.
2576 /// reloadable: yes
2577 #[serde(default = "true_fn")]
2578 pub hydra_backports: bool,
2579
2580 /// Delete rooms when the last user from this server leaves. This feature is
2581 /// experimental and for the purpose of least-surprise is not enabled by
2582 /// default but can be enabled for deployments interested in conserving
2583 /// space. It may eventually default to true in a future release.
2584 ///
2585 /// Note that not all pathways which can remove the last local user
2586 /// currently invoke this operation, so in some cases you may find the room
2587 /// still exists.
2588 ///
2589 /// reloadable: yes
2590 /// default: false
2591 #[serde(default)]
2592 pub delete_rooms_after_leave: bool,
2593
2594 /// Limits the number of One Time Keys per device (not per-algorithm). The
2595 /// reference implementation maintains 50 OTK's at any given time, therefor
2596 /// our default is at least five times that. There is no known reason for an
2597 /// administrator to adjust this value; it is provided here rather than
2598 /// hardcoding it.
2599 ///
2600 /// reloadable: yes
2601 /// default: 256
2602 #[serde(default = "default_one_time_key_limit")]
2603 pub one_time_key_limit: usize,
2604
2605 /// (EXPERIMENTAL) Setting this option to true replaces the list of identity
2606 /// providers displayed on a client's login page with a single button "Sign
2607 /// in with single sign-on" linking to the URL
2608 /// `/_matrix/client/v3/login/sso/redirect`. All configured providers are
2609 /// attempted for authorization. All authorizations associate with the same
2610 /// Matrix user. NOTE: All authorizations must succeed, as there is no
2611 /// reliable way to skip a provider.
2612 ///
2613 /// This option is disabled by default, allowing the client to list
2614 /// configured providers and permitting privacy-conscious users to authorize
2615 /// only their choice.
2616 ///
2617 /// Note that fluffychat always displays a single button anyway. You do not
2618 /// need to enable this to use fluffychat; instead we offer a
2619 /// default-provider option, see `default` in the provider config section.
2620 /// reloadable: yes
2621 #[serde(default)]
2622 pub single_sso: bool,
2623
2624 /// Setting this option to true replaces the list of identity providers on
2625 /// the client's login screen with a single button "Sign in with single
2626 /// sign-on" linking to the URL `/_matrix/client/v3/login/sso/redirect`. The
2627 /// deployment is expected to intercept this URL with their reverse-proxy to
2628 /// provide a custom webpage listing providers; each entry linking or
2629 /// redirecting back to one of the configured identity providers at
2630 /// /_matrix/client/v3/login/sso/redirect/<client_id>`.
2631 ///
2632 /// This option defaults to false, allowing the client to generate the list
2633 /// of providers or hide all SSO-related options when none configured.
2634 /// reloadable: yes
2635 #[serde(default)]
2636 pub sso_custom_providers_page: bool,
2637
2638 /// From MSC3824:
2639 /// > If the client finds oauth_aware_preferred to be true then, assuming it
2640 /// > supports that auth type, it should present this as the only
2641 /// > login/registration method available to the user.
2642 /// reloadable: yes
2643 #[serde(default, alias = "sso_aware_preferred")]
2644 pub oidc_aware_preferred: bool,
2645
2646 /// Directory containing appservice yaml registration files.
2647 ///
2648 /// default: ""
2649 #[serde(default)]
2650 pub appservice_dir: Option<PathBuf>,
2651
2652 /// Skip database migration on startup. This option is intended for
2653 /// developer debugging and testing only. Never set this option to false
2654 /// unless you have been instructed to do so. Setting this option to false
2655 /// may cause permanent damage and permanent loss of data.
2656 ///
2657 /// Any new database migrations will not be applied on startup, and the
2658 /// database schema version will not be adjusted. These migrations and
2659 /// schema changes may be expected by the current codebase but may not be
2660 /// available when this option is set to false.
2661 ///
2662 /// Setting this option to false will have no effect if no new migrations
2663 /// are to be applied. New migrations are applied once during any execution
2664 /// where this option is set to true (which is the default).
2665 #[serde(default = "true_fn")]
2666 pub database_migrations: bool,
2667
2668 /// Force the database to set its version to the current version known to
2669 /// the executable.
2670 ///
2671 /// - When the discovered version is less than the current version any
2672 /// migrations are applied normally.
2673 /// - When the discovered version is equal to the current version,
2674 /// unversioned migrations are applied normally.
2675 /// - When the discovered database version is greater than the current
2676 /// version, one-time migrations are applied normally and the discoverable
2677 /// version is regressed back to the current version.
2678 ///
2679 /// This option extremely dangerous and intended for developer debugging and
2680 /// testing only. Never set this option unless you have been instructed to
2681 /// do so. Setting this option may cause permanent damage and permanent loss
2682 /// of data.
2683 #[serde(default)]
2684 pub force_migration: bool,
2685
2686 /// Set this to true for excluding unencrypted rooms from the common-rooms
2687 /// calculation deciding the receivers of device list updates.
2688 ///
2689 /// Setting this to true can help performance on very large homeservers,
2690 /// but it may not be spec compliant and risky for client expectations.
2691 /// reloadable: yes
2692 #[serde(default)]
2693 pub device_key_update_encrypted_rooms_only: bool,
2694
2695 // external structure; separate section
2696 #[serde(default)]
2697 pub blurhashing: BlurhashConfig,
2698
2699 // external structure; separate section
2700 #[serde(default)]
2701 pub storage_provider: BTreeMap<String, StorageProvider>,
2702
2703 // external structure; separate section
2704 #[serde(default)]
2705 pub ldap: LdapConfig,
2706
2707 // external structure; separate section
2708 #[serde(default)]
2709 pub jwt: JwtConfig,
2710
2711 // external structure; separate section
2712 #[serde(default)]
2713 pub appservice: BTreeMap<String, AppService>,
2714
2715 // external structure; separate sections
2716 #[serde(default, with = "identity_provider_serde")]
2717 pub identity_provider: BTreeMap<String, IdentityProvider>,
2718
2719 #[serde(flatten)]
2720 #[expect(clippy::zero_sized_map_values)]
2721 // this is a catchall, the map shouldn't be zero at runtime
2722 catchall: BTreeMap<String, IgnoredAny>,
2723}
2724
2725#[derive(Clone, Debug, Deserialize, Default)]
2726#[config_example_generator(filename = "tuwunel-example.toml", section = "global.tls")]
2727pub struct TlsConfig {
2728 /// Path to a valid TLS certificate file.
2729 ///
2730 /// example: "/path/to/my/certificate.crt"
2731 pub certs: Option<String>,
2732
2733 /// Path to a valid TLS certificate private key.
2734 ///
2735 /// example: "/path/to/my/certificate.key"
2736 pub key: Option<String>,
2737
2738 /// Whether to listen and allow for HTTP and HTTPS connections (insecure!)
2739 #[serde(default)]
2740 pub dual_protocol: bool,
2741}
2742
2743#[expect(rustdoc::bare_urls)]
2744#[derive(Clone, Debug, Deserialize, Default)]
2745#[config_example_generator(
2746 filename = "tuwunel-example.toml",
2747 section = "global.well_known"
2748)]
2749pub struct WellKnownConfig {
2750 /// The server URL that the client well-known file will serve. This should
2751 /// not contain a port, and should just be a valid HTTPS URL.
2752 ///
2753 /// example: "https://matrix.example.com"
2754 pub client: Option<Url>,
2755
2756 /// The server base domain of the URL with a specific port that the server
2757 /// well-known file will serve. This should contain a port at the end, and
2758 /// should not be a URL.
2759 ///
2760 /// reloadable: yes
2761 /// example: "matrix.example.com:443"
2762 pub server: Option<OwnedServerName>,
2763
2764 /// The URL of the support web page. This and the below generate the content
2765 /// of `/.well-known/matrix/support`.
2766 ///
2767 /// reloadable: yes
2768 /// example: "https://example.com/support"
2769 pub support_page: Option<Url>,
2770
2771 /// The name of the support role.
2772 ///
2773 /// reloadable: yes
2774 /// example: "m.role.admin"
2775 pub support_role: Option<ContactRole>,
2776
2777 /// The email address for the above support role.
2778 ///
2779 /// reloadable: yes
2780 /// example: "admin@example.com"
2781 pub support_email: Option<String>,
2782
2783 /// The Matrix User ID for the above support role.
2784 ///
2785 /// example "@admin:example.com"
2786 /// reloadable: yes
2787 pub support_mxid: Option<OwnedUserId>,
2788
2789 /// LiveKit JWT endpoint.
2790 /// Required for Element Call / MatrixRTC (MSC4143).
2791 ///
2792 /// Note: You must also set `client` above to your homeserver URL.
2793 ///
2794 /// reloadable: yes
2795 /// default: ""
2796 #[serde(default)]
2797 pub livekit_url: Option<String>,
2798
2799 /// Custom MatrixRTC transports.
2800 ///
2801 /// If you're looking to setup Element Call / MatrixRTC with Livekit,
2802 /// you should not use this option and instead set `livekit_url`.
2803 /// This is only required if you want to configure a non-livekit MatrixRTC
2804 /// transport. There are no known client implementations that support any
2805 /// other transport types.
2806 ///
2807 /// This option was previously the only way to configure a Livekit
2808 /// transport. It has been superseded by `livekit_url`.
2809 ///
2810 /// Example:
2811 /// ```toml
2812 /// [global.well_known]
2813 /// client = "https://matrix.yourdomain.com"
2814 ///
2815 /// [[global.well_known.rtc_transports]]
2816 /// type = "livekit"
2817 /// livekit_service_url = "https://livekit.yourdomain.com"
2818 /// ```
2819 ///
2820 /// reloadable: yes
2821 /// default: []
2822 #[serde(default)]
2823 pub rtc_transports: Vec<serde_json::Value>,
2824}
2825
2826#[derive(Clone, Copy, Debug, Deserialize, Default)]
2827#[expect(rustdoc::bare_urls)]
2828#[config_example_generator(
2829 filename = "tuwunel-example.toml",
2830 section = "global.blurhashing"
2831)]
2832pub struct BlurhashConfig {
2833 /// blurhashing x component, 4 is recommended by https://blurha.sh/
2834 ///
2835 /// reloadable: yes
2836 /// default: 4
2837 #[serde(default = "default_blurhash_x_component")]
2838 pub components_x: u32,
2839 /// blurhashing y component, 3 is recommended by https://blurha.sh/
2840 ///
2841 /// reloadable: yes
2842 /// default: 3
2843 #[serde(default = "default_blurhash_y_component")]
2844 pub components_y: u32,
2845 /// Max raw size that the server will blurhash, this is the size of the
2846 /// image after converting it to raw data, it should be higher than the
2847 /// upload limit but not too high. The higher it is the higher the
2848 /// potential load will be for clients requesting blurhashes. Accepts an
2849 /// integer byte count or a string with SI/IEC suffix such as "32 MiB".
2850 /// Setting it to 0 disables blurhashing.
2851 ///
2852 /// reloadable: yes
2853 /// default: 33554432
2854 #[serde(
2855 default = "default_blurhash_max_raw_size",
2856 deserialize_with = "deserialize_bytesize_u64"
2857 )]
2858 pub blurhash_max_raw_size: u64,
2859}
2860
2861#[derive(Clone, Debug, Default, Deserialize)]
2862#[config_example_generator(filename = "tuwunel-example.toml", section = "global.ldap")]
2863pub struct LdapConfig {
2864 /// Whether to enable LDAP login.
2865 ///
2866 /// reloadable: yes
2867 /// example: "true"
2868 #[serde(default)]
2869 pub enable: bool,
2870
2871 /// URI of the LDAP server.
2872 ///
2873 /// reloadable: yes
2874 /// example: "ldap://ldap.example.com:389"
2875 pub uri: Option<Url>,
2876
2877 /// Root of the searches.
2878 ///
2879 /// reloadable: yes
2880 /// example: "ou=users,dc=example,dc=org"
2881 ///
2882 /// default:
2883 #[serde(default)]
2884 pub base_dn: String,
2885
2886 /// Bind DN if anonymous search is not enabled.
2887 ///
2888 /// You can use the variable `{username}` that will be replaced by the
2889 /// entered username. In such case, the password used to bind will be the
2890 /// one provided for the login and not the one given by
2891 /// `bind_password_file`. Beware: automatically granting admin rights will
2892 /// not work if you use this direct bind instead of a LDAP search.
2893 ///
2894 /// reloadable: yes
2895 /// example: "cn=ldap-reader,dc=example,dc=org" or
2896 /// "cn={username},ou=users,dc=example,dc=org"
2897 ///
2898 /// default: ""
2899 #[serde(default)]
2900 pub bind_dn: Option<String>,
2901
2902 /// Path to a file on the system that contains the password for the
2903 /// `bind_dn`.
2904 ///
2905 /// The server must be able to access the file, and it must not be empty.
2906 ///
2907 /// reloadable: yes
2908 /// default: ""
2909 #[serde(default)]
2910 pub bind_password_file: Option<PathBuf>,
2911
2912 /// Search filter to limit user searches.
2913 ///
2914 /// You can use the variable `{username}` that will be replaced by the
2915 /// entered username for more complex filters.
2916 ///
2917 /// reloadable: yes
2918 /// example: "(&(objectClass=person)(memberOf=matrix))"
2919 ///
2920 /// default: "(objectClass=*)"
2921 #[serde(default = "default_ldap_search_filter")]
2922 pub filter: String,
2923
2924 /// Attribute to use to uniquely identify the user.
2925 ///
2926 /// reloadable: yes
2927 /// example: "uid" or "cn"
2928 ///
2929 /// default: "uid"
2930 #[serde(default = "default_ldap_uid_attribute")]
2931 pub uid_attribute: String,
2932
2933 /// Attribute containing the distinguished name of the user.
2934 ///
2935 /// reloadable: yes
2936 /// example: "givenName" or "sn"
2937 ///
2938 /// default: "givenName"
2939 #[serde(default = "default_ldap_name_attribute")]
2940 pub name_attribute: String,
2941
2942 /// Root of the searches for admin users.
2943 ///
2944 /// Defaults to `base_dn` if empty.
2945 ///
2946 /// reloadable: yes
2947 /// example: "ou=admins,dc=example,dc=org"
2948 ///
2949 /// default:
2950 #[serde(default)]
2951 pub admin_base_dn: String,
2952
2953 /// The LDAP search filter to find administrative users for tuwunel.
2954 ///
2955 /// If left blank, administrative state must be configured manually for each
2956 /// user.
2957 ///
2958 /// You can use the variable `{username}` that will be replaced by the
2959 /// entered username for more complex filters.
2960 ///
2961 /// reloadable: yes
2962 /// example: "(objectClass=tuwunelAdmin)" or "(uid={username})"
2963 ///
2964 /// default:
2965 #[serde(default)]
2966 pub admin_filter: String,
2967}
2968
2969#[derive(Clone, Debug, Default, Deserialize)]
2970#[config_example_generator(filename = "tuwunel-example.toml", section = "global.jwt")]
2971pub struct JwtConfig {
2972 /// Enable JWT logins
2973 ///
2974 /// reloadable: yes
2975 /// default: false
2976 #[serde(default)]
2977 pub enable: bool,
2978
2979 /// Validation key, also called 'secret' in Synapse config. The type of key
2980 /// can be configured in 'format', but defaults to the common HMAC which
2981 /// is a plaintext shared-secret, so you should keep this value private.
2982 ///
2983 /// display: sensitive
2984 /// reloadable: yes
2985 /// default:
2986 #[serde(default, alias = "secret")]
2987 pub key: String,
2988
2989 /// Format of the 'key'. Only HMAC, ECDSA, and B64HMAC are supported
2990 /// Binary keys cannot be pasted into this config, so B64HMAC is an
2991 /// alternative to HMAC for properly random secret strings.
2992 /// - HMAC is a plaintext shared-secret private-key.
2993 /// - B64HMAC is a base64-encoded version of HMAC.
2994 /// - ECDSA is a PEM-encoded public-key.
2995 /// - EDDSA is a PEM-encoded Ed25519 public-key.
2996 ///
2997 /// reloadable: yes
2998 /// default: "HMAC"
2999 #[serde(default = "default_jwt_format")]
3000 pub format: String,
3001
3002 /// Automatically create new user from a valid claim, otherwise access is
3003 /// denied for an unknown even with an authentic token.
3004 ///
3005 /// reloadable: yes
3006 /// default: true
3007 #[serde(default = "true_fn")]
3008 pub register_user: bool,
3009
3010 /// JWT algorithm
3011 ///
3012 /// reloadable: yes
3013 /// default: "HS256"
3014 #[serde(default = "default_jwt_algorithm")]
3015 pub algorithm: String,
3016
3017 /// Optional audience claim list. The token must claim one or more values
3018 /// from this list when set.
3019 ///
3020 /// reloadable: yes
3021 /// default: []
3022 #[serde(default)]
3023 pub audience: Vec<String>,
3024
3025 /// Optional issuer claim list. The token must claim one or more values
3026 /// from this list when set.
3027 ///
3028 /// reloadable: yes
3029 /// default: []
3030 #[serde(default)]
3031 pub issuer: Vec<String>,
3032
3033 /// Require expiration claim in the token. This defaults to false for
3034 /// synapse migration compatibility.
3035 ///
3036 /// reloadable: yes
3037 /// default: false
3038 #[serde(default)]
3039 pub require_exp: bool,
3040
3041 /// Require not-before claim in the token. This defaults to false for
3042 /// synapse migration compatibility.
3043 ///
3044 /// reloadable: yes
3045 /// default: false
3046 #[serde(default)]
3047 pub require_nbf: bool,
3048
3049 /// Validate expiration time of the token when present. Whether or not it is
3050 /// required depends on require_exp, but when present this ensures the token
3051 /// is not used after a time.
3052 ///
3053 /// reloadable: yes
3054 /// default: true
3055 #[serde(default = "true_fn")]
3056 pub validate_exp: bool,
3057
3058 /// Validate not-before time of the token when present. Whether or not it is
3059 /// required depends on require_nbf, but when present this ensures the token
3060 /// is not used before a time.
3061 ///
3062 /// reloadable: yes
3063 /// default: true
3064 #[serde(default = "true_fn")]
3065 pub validate_nbf: bool,
3066
3067 /// Bypass validation for diagnostic/debug use only.
3068 ///
3069 /// reloadable: yes
3070 /// default: true
3071 #[serde(default = "true_fn")]
3072 pub validate_signature: bool,
3073}
3074
3075#[derive(Clone, Debug, Deserialize)]
3076#[config_example_generator(
3077 filename = "tuwunel-example.toml",
3078 section = "[global.identity_provider]"
3079)]
3080pub struct IdentityProvider {
3081 /// The brand-name of the service (e.g. Apple, Facebook, GitHub, GitLab,
3082 /// Google) or the software (e.g. keycloak, MAS) providing the identity.
3083 /// When a brand is recognized we apply certain defaults to this config
3084 /// for your convenience. For certain brands we apply essential internal
3085 /// workarounds specific to that provider; it is important to configure this
3086 /// field properly when a provider needs to be recognized (like GitHub for
3087 /// example).
3088 ///
3089 /// Several configured providers can share the same brand name. It is not
3090 /// case-sensitive. As a convenience for common simple deployments we can
3091 /// identify this provider by brand in addition to the unique `client_id` if
3092 /// and only if there is a single provider for the brand; see notes for
3093 /// `client_id`.
3094 #[serde(deserialize_with = "utils::string::de::to_lowercase")]
3095 pub brand: String,
3096
3097 /// The ID of your OAuth application which the provider generates upon
3098 /// registration. This ID then uniquely identifies this configuration
3099 /// instance itself, becoming the identity provider's ID and must be unique
3100 /// and remain unchanged.
3101 ///
3102 /// As a convenience we also identify this config by `brand` if and only if
3103 /// there is a single provider configured for a `brand`. Note carefully that
3104 /// multiple providers configured with the same `brand` is not an error and
3105 /// this provider will simply not be found when querying by `brand`.
3106 pub client_id: String,
3107
3108 /// Secret key the provider generated for you along with the `client_id`
3109 /// above. Unlike the `client_id`, the `client_secret` can be changed here
3110 /// whenever the provider regenerates one for you.
3111 ///
3112 /// display: sensitive
3113 pub client_secret: Option<String>,
3114
3115 /// Secret key to use that's read from the file path specified.
3116 ///
3117 /// This takes priority over "client_secret" first, and falls back to
3118 /// "client_secret" if invalid or failed to open.
3119 ///
3120 /// example: "/etc/tuwunel/.client_secret"
3121 pub client_secret_file: Option<PathBuf>,
3122
3123 /// Issuer URL the provider publishes for you. We have pre-supplied default
3124 /// values for some of the canonical public providers, making this field
3125 /// optional based on the `brand` set above. Otherwise it is required to
3126 /// find self-hosted providers. It must be identical to what is configured
3127 /// and expected by the provider and must never change because we associate
3128 /// identities to it. If the `/.well-known/openid-configuration` is not
3129 /// found behind this URL see `base_path` below as a workaround.
3130 pub issuer_url: Option<Url>,
3131
3132 /// The callback URL configured when registering the OAuth application with
3133 /// the provider. Tuwunel's callback URL must be strictly formatted exactly
3134 /// as instructed. The URL host must point directly at the matrix server and
3135 /// use the following path:
3136 /// `/_matrix/client/unstable/login/sso/callback/<client_id>` where
3137 /// `<client_id>` is the same one configured for this provider above.
3138 pub callback_url: Option<Url>,
3139
3140 /// When more than one identity_provider has been configured and
3141 /// `single_sso` is false and `sso_custom_providers_page` is false this will
3142 /// determine the behavior of the `/_matrix/client/v3/login/sso/redirect`
3143 /// endpoint (note the url lacks a trailing `client_id`).
3144 ///
3145 /// When only one identity_provider is configured it will be interpreted
3146 /// as the default and this does not need to be set. Otherwise a default
3147 /// *must* be selected for some clients (e.g. fluffychat) to work properly
3148 /// when the above conditions require it. To operate out-of-the-box we
3149 /// default to one configured provider if none are explicitly default; a
3150 /// warning will be logged on startup for this condition.
3151 ///
3152 /// (EXPERIMENTAL) Multiple providers can be set to default. All providers
3153 /// configured with this option set to `true` will associate with the same
3154 /// Matrix account when a client flows through
3155 /// `/_matrix/client/v3/login/sso/redirect`.
3156 ///
3157 /// When a user authorizes any provider configured default, the flow will
3158 /// include all other providers configured default as well for association.
3159 /// NOTE: authorization must succeed for ALL default providers.
3160 #[serde(default)]
3161 pub default: bool,
3162
3163 /// Optional display-name for this provider instance seen on the login page
3164 /// by users. It defaults to `brand`. When configuring multiple providers
3165 /// using the same `brand` this can be set to distinguish them.
3166 pub name: Option<String>,
3167
3168 /// Optional icon for the provider. The canonical providers have a default
3169 /// icon based on the `brand` supplied above when this is not supplied. Note
3170 /// that it uses an MXC url which is curious in the auth-media era and may
3171 /// not be reliable.
3172 pub icon: Option<OwnedMxcUri>,
3173
3174 /// Optional list of scopes to authorize. An empty array does not impose any
3175 /// restrictions from here, effectively defaulting to all scopes you
3176 /// configured for the OAuth application at the provider. This setting
3177 /// allows for restricting to a subset of those scopes for this instance.
3178 /// Note the user can further restrict scopes during their authorization.
3179 ///
3180 /// default: []
3181 #[serde(default)]
3182 pub scope: BTreeSet<String>,
3183
3184 /// Optional list of userinfo claims which shape and restrict the way we
3185 /// compute a Matrix UserId for new registrations. Reviewing Tuwunel's
3186 /// documentation will be necessary for a complete description in detail. An
3187 /// empty array imposes no restriction here, avoiding generated fallbacks as
3188 /// much as possible.
3189 ///
3190 /// For simplicity we reserve a claim called "unique" which can be listed
3191 /// alone to ensure *only* generated ID's are used for registrations.
3192 ///
3193 /// Note that listing the claim "sub" has special significance and will take
3194 /// precedence over all other claims, listed or unlisted. "sub" is not
3195 /// normally used to determine a UserId unless explicitly listed here.
3196 ///
3197 /// As of now arbitrary claims cannot be listed here, we only recognize
3198 /// specific hard-coded claims.
3199 ///
3200 /// default: []
3201 #[serde(default)]
3202 pub userid_claims: BTreeSet<String>,
3203
3204 /// Trusted providers can cause username conflicts (i.e. account hijacking)
3205 /// but this is precisely how an existing matrix account can be associated
3206 /// with a provider. When this option is set to true, the way we compute a
3207 /// Matrix UserId from userinfo claims is inverted: we find the first
3208 /// matching user and grant access to it. Whereas by default, when set to
3209 /// false, we skip matching users and register the first available username;
3210 /// falling-back to random characters to avoid conflicts.
3211 ///
3212 /// Only set this option to true for providers you self-host and control.
3213 /// Never set this option to true for the public providers such as GitHub,
3214 /// GitLab, etc.
3215 ///
3216 /// Note that associating an existing user with an untrusted provider is
3217 /// still possible but only with the command '!admin query oauth associate'.
3218 ///
3219 /// default: false
3220 #[serde(default)]
3221 pub trusted: bool,
3222
3223 /// Setting this option to false will inhibit unique ID's from being
3224 /// generated as a last-resort when determining a UserId from a provider's
3225 /// claims. In the case of untrusted providers, when all provided claims
3226 /// conflict with existing user accounts, a unique fallback ID needs
3227 /// to be generated for registration to not be denied with an error.
3228 ///
3229 /// Set this option to false if you operate a private server or a trusted
3230 /// identity provider where random UserId's are undesirable; the result of a
3231 /// misconfiguration or other issue where an error is warranted.
3232 ///
3233 /// This option should be set to true for public servers or some users may
3234 /// never be able to register.
3235 ///
3236 /// default: true
3237 #[serde(default = "true_fn")]
3238 pub unique_id_fallbacks: bool,
3239
3240 /// Controls whether new user registration is possible from this provider.
3241 /// When this option is set to false, authorizations from this provider
3242 /// only affect existing users and will never result in a new registration
3243 /// when the claims fail to match any existing user (in the case of trusted
3244 /// providers) or an available username is found (in the case of untrusted
3245 /// providers).
3246 ///
3247 /// Setting this option to false is generally not useful unless there is
3248 /// an explicit reason to do so.
3249 ///
3250 /// default: true
3251 #[serde(default = "true_fn")]
3252 pub registration: bool,
3253
3254 /// Optional extra path components after the issuer_url leading to the
3255 /// location of the `.well-known` directory used for discovery. If the path
3256 /// starts with a slash it will be treated as absolute, meaning overwriting
3257 /// any path in the issuer_url. The path needs to end with a slash. This
3258 /// will be empty for specification-compliant providers. We have supplied
3259 /// any known values based on `brand` (e.g. `login/oauth/` for GitHub).
3260 pub base_path: Option<String>,
3261
3262 /// Overrides the `.well-known` location where the provider's openid
3263 /// configuration is found. It is very unlikely you will need to set this;
3264 /// available for developers or special purposes only.
3265 pub discovery_url: Option<Url>,
3266
3267 /// Overrides the authorize URL requested during the grant phase. This is
3268 /// generally discovered or derived automatically, but may be required as a
3269 /// workaround for any non-standard or undiscoverable provider.
3270 pub authorization_url: Option<Url>,
3271
3272 /// Overrides the access token URL; the same caveats apply as with the other
3273 /// URL overrides.
3274 pub token_url: Option<Url>,
3275
3276 /// Overrides the revocation URL; the same caveats apply as with the other
3277 /// URL overrides.
3278 pub revocation_url: Option<Url>,
3279
3280 /// Overrides the introspection URL; the same caveats apply as with the
3281 /// other URL overrides.
3282 pub introspection_url: Option<Url>,
3283
3284 /// Overrides the userinfo URL; the same caveats apply as with the other URL
3285 /// overrides.
3286 pub userinfo_url: Option<Url>,
3287
3288 /// Whether to perform discovery and adjust this provider's configuration
3289 /// accordingly. This defaults to true. When true, it is an error when
3290 /// discovery fails and authorizations will not be attempted to the
3291 /// provider.
3292 #[serde(default = "true_fn")]
3293 pub discovery: bool,
3294
3295 /// The duration in seconds before a grant authorization session expires.
3296 ///
3297 /// default: 300
3298 #[serde(default = "default_sso_grant_session_duration")]
3299 pub grant_session_duration: Option<u64>,
3300
3301 /// Whether to check the redirect cookie during the callback. This is a
3302 /// security feature and should remain enabled. This is available for
3303 /// developers or deployments which cannot tolerate cookies and are willing
3304 /// to tolerate the risks.
3305 ///
3306 /// default: true
3307 #[serde(default = "true_fn")]
3308 pub check_cookie: bool,
3309
3310 /// Extra query parameters appended to every authorization request sent to
3311 /// the identity provider.
3312 ///
3313 /// E.g. to force re-authentication even if IdP cookies are present:
3314 /// ```toml
3315 /// [[global.identity_provider]]
3316 /// extra_authorization_parameters = { prompt = "login" }
3317 /// ```
3318 ///
3319 /// default: {}
3320 #[serde(default)]
3321 pub extra_authorization_parameters: BTreeMap<String, String>,
3322}
3323
3324impl IdentityProvider {
3325 #[must_use]
3326 pub fn id(&self) -> &str { self.client_id.as_str() }
3327
3328 pub async fn get_client_secret(&self) -> Result<String> {
3329 if let Some(client_secret) = &self.client_secret {
3330 return Ok(client_secret.clone());
3331 }
3332
3333 let Some(client_secret_file) = &self.client_secret_file else {
3334 return Err!("No client secret or client secret file configured");
3335 };
3336
3337 let client_secret = tokio::fs::read_to_string(client_secret_file).await?;
3338
3339 Ok(client_secret.trim().to_owned())
3340 }
3341}
3342
3343#[derive(Clone, Debug, Default, Deserialize)]
3344pub enum StorageProvider {
3345 #[expect(non_camel_case_types)]
3346 local(StorageProviderLocal),
3347 #[expect(non_camel_case_types)]
3348 #[serde(rename = "s3", alias = "S3")]
3349 s3(Box<StorageProviderS3>),
3350 #[default]
3351 None,
3352}
3353
3354#[derive(Clone, Debug, Default, Deserialize)]
3355#[config_example_generator(
3356 filename = "tuwunel-example.toml",
3357 section = "global.storage_provider.<ID>.local"
3358)]
3359pub struct StorageProviderLocal {
3360 /// Absolute path to this local filesystem storage provider. Technically the
3361 /// provider exists at the filesystem root, and the base_path is prefixed to
3362 /// all objects.
3363 #[serde(alias = "path")]
3364 pub base_path: String,
3365
3366 /// Creates the directory on the local filesystem if missing. This is not
3367 /// recommended to prevent misconfigured environments and missing mounts
3368 /// from silently succeeding.
3369 #[serde(default)]
3370 pub create_if_missing: bool,
3371
3372 /// Toggles the preservation of a directory after its last file contents are
3373 /// removed.
3374 #[serde(default = "true_fn")]
3375 pub delete_empty_directories: bool,
3376
3377 /// Enables checks performed at startup determining the usability of the
3378 /// local directory. Failures will abort the server's startup.
3379 ///
3380 /// default: true
3381 #[serde(default = "true_fn")]
3382 pub startup_check: bool,
3383}
3384
3385#[derive(Clone, Debug, Default, Deserialize)]
3386#[config_example_generator(
3387 filename = "tuwunel-example.toml",
3388 section = "global.storage_provider.<ID>.s3"
3389)]
3390pub struct StorageProviderS3 {
3391 /// Supply an s3 URL e.g. "s3://bucket/path". These URLs may contain one
3392 /// or all of `bucket`, `region`, and `path` . When not supplied, such
3393 /// additional items can be supplied below individually.
3394 pub url: Option<String>,
3395
3396 /// The name of the S3 bucket. e.g. "bucketname-123456789-us-west-2-an".
3397 pub bucket: Option<String>,
3398
3399 /// The region of the S3 bucket. e.g. "us-west-2".
3400 ///
3401 /// default: "us-east-1"
3402 pub region: Option<String>,
3403
3404 /// Your amazon IAM Key ID with access granted to this bucket.
3405 /// e.g. "ABCDEFG1X1ZZYYXXWWVV"
3406 #[debug("{}", redacted_debug!(key))]
3407 pub key: Option<String>,
3408
3409 /// The secret key component which is approx 40 characters of base64.
3410 ///
3411 /// default:
3412 /// display: sensitive
3413 #[serde(skip_serializing)]
3414 #[debug("{}", redacted_debug!(secret))]
3415 pub secret: Option<String>,
3416
3417 /// Optional path prefix within the bucket where all our operations will
3418 /// take place.
3419 #[serde(alias = "path")]
3420 pub base_path: Option<String>,
3421
3422 /// (expert use) Override the location of s3 applied after components of the
3423 /// parsed `url` (or when none set).
3424 pub endpoint: Option<String>,
3425
3426 /// (expert use) Override this property useful for some self-hosted
3427 /// environments. By default it is derived when parsing the primary `url`.
3428 #[serde(default)]
3429 pub use_vhost_request: Option<bool>,
3430
3431 /// (expert use) Alternative session-token authentication method.
3432 ///
3433 /// display: sensitive
3434 /// default:
3435 #[serde(skip_serializing)]
3436 #[debug("{}", redacted_debug!(token))]
3437 pub token: Option<String>,
3438
3439 /// (expert use) Associated SSE-KMS key material.
3440 ///
3441 /// display: sensitive
3442 #[debug("{}", redacted_debug!(kms))]
3443 pub kms: Option<String>,
3444
3445 /// (expert use) When configured for the bucket it should be reflected here.
3446 pub use_bucket_key: Option<bool>,
3447
3448 /// (expert use) Threshold size for switching to Multi-part uploads. This is
3449 /// a quirk of the S3 protocol which requires us to use a different approach
3450 /// for "large" uploads. This value determines what a "large" upload is. The
3451 /// default value should be sufficient for most providers. The value is a
3452 /// parsed string allowing SI or IEC units for convenience.
3453 ///
3454 /// default: 100 MiB
3455 #[serde(default = "default_multipart_threshold")]
3456 pub multipart_threshold: ByteSize,
3457
3458 /// (expert use) Size of each individual part within a Multi-part upload.
3459 /// Once an upload exceeds `multipart_threshold` the payload is split into
3460 /// parts of this size, each sent as a separate HTTP PUT. Smaller values
3461 /// keep individual requests under per-request timeouts on slow uplinks at
3462 /// the cost of more round-trips. S3 requires every part except the last
3463 /// to be at least 5 MiB. The value is a parsed string allowing SI or IEC
3464 /// units for convenience.
3465 ///
3466 /// default: 10 MiB
3467 #[serde(default = "default_multipart_part_size")]
3468 pub multipart_part_size: ByteSize,
3469
3470 /// (developer use) Allows relaxing default requirement forcing HTTPS.
3471 ///
3472 /// default: true
3473 #[serde(default = "some_true_fn")]
3474 pub use_https: Option<bool>,
3475
3476 /// (developer_use) Allows skipping request header signatures (will be
3477 /// reejected by AWS).
3478 ///
3479 /// default: true
3480 #[serde(default = "some_true_fn")]
3481 pub use_signatures: Option<bool>,
3482
3483 /// (developer_use) Allows disabling request payload signatures.
3484 ///
3485 /// default: true
3486 #[serde(default = "some_true_fn")]
3487 pub use_payload_signatures: Option<bool>,
3488
3489 /// (developer use) Enables checks performed at startup such as pinging the
3490 /// provider. Failures are considered critical startup errors which abort
3491 /// startup. When set to false, faulty providers are only discovered with
3492 /// first use and will not be fatal errors.
3493 ///
3494 /// Only set this to false if you expect a provider to be down at startup or
3495 /// for development/testing purposes; checks are disabled when the server
3496 /// is started in '--maintenance' mode.
3497 ///
3498 /// default: true
3499 #[serde(default = "true_fn")]
3500 pub startup_check: bool,
3501}
3502
3503#[derive(Clone, Debug, Default, Deserialize)]
3504#[config_example_generator(
3505 filename = "tuwunel-example.toml",
3506 section = "global.appservice.<ID>",
3507 ignore = "id users aliases rooms"
3508)]
3509pub struct AppService {
3510 #[serde(default)]
3511 pub id: String,
3512
3513 /// The URL for the application service.
3514 ///
3515 /// Optionally set to `null` if no traffic is required.
3516 pub url: Option<String>,
3517
3518 /// A unique token for application services to use to authenticate requests
3519 /// to Homeservers.
3520 ///
3521 /// default:
3522 /// display: sensitive
3523 pub as_token: String,
3524
3525 /// A unique token for Homeservers to use to authenticate requests to
3526 /// application services.
3527 ///
3528 /// default:
3529 /// display: sensitive
3530 pub hs_token: String,
3531
3532 /// The localpart of the user associated with the application service.
3533 pub sender_localpart: Option<String>,
3534
3535 /// Events which are sent from certain users.
3536 #[serde(default)]
3537 pub users: Vec<AppServiceNamespace>,
3538
3539 /// Events which are sent in rooms with certain room aliases.
3540 #[serde(default)]
3541 pub aliases: Vec<AppServiceNamespace>,
3542
3543 /// Events which are sent in rooms with certain room IDs.
3544 #[serde(default)]
3545 pub rooms: Vec<AppServiceNamespace>,
3546
3547 /// Whether requests from masqueraded users are rate-limited.
3548 ///
3549 /// The sender is excluded.
3550 #[serde(default)]
3551 pub rate_limited: bool,
3552
3553 /// The external protocols which the application service provides (e.g.
3554 /// IRC).
3555 ///
3556 /// default: []
3557 #[serde(default)]
3558 pub protocols: Vec<String>,
3559
3560 /// Whether the application service wants to receive ephemeral data.
3561 ///
3562 /// default: false
3563 #[serde(default)]
3564 pub receive_ephemeral: bool,
3565
3566 /// Whether the application service wants to do device management, as part
3567 /// of MSC4190.
3568 ///
3569 /// default: false
3570 #[serde(default)]
3571 pub device_management: bool,
3572}
3573
3574impl From<AppService> for ruma::api::appservice::Registration {
3575 fn from(conf: AppService) -> Self {
3576 use ruma::api::appservice::Namespaces;
3577
3578 let sender_localpart = conf
3579 .sender_localpart
3580 .unwrap_or_else(|| conf.id.clone());
3581
3582 Self {
3583 id: conf.id,
3584 url: conf.url,
3585 as_token: conf.as_token,
3586 hs_token: conf.hs_token,
3587 receive_ephemeral: conf.receive_ephemeral,
3588 device_management: conf.device_management,
3589 protocols: conf.protocols.into(),
3590 rate_limited: conf.rate_limited.into(),
3591 sender_localpart,
3592 namespaces: Namespaces {
3593 users: conf.users.into_iter().map(Into::into).collect(),
3594 aliases: conf.aliases.into_iter().map(Into::into).collect(),
3595 rooms: conf.rooms.into_iter().map(Into::into).collect(),
3596 },
3597 }
3598 }
3599}
3600
3601#[derive(Clone, Debug, Default, Deserialize)]
3602#[config_example_generator(
3603 filename = "tuwunel-example.toml",
3604 section = "[global.appservice.<ID>.<users|rooms|aliases>]"
3605)]
3606pub struct AppServiceNamespace {
3607 /// Whether this application service has exclusive access to events within
3608 /// this namespace.
3609 #[serde(default)]
3610 pub exclusive: bool,
3611
3612 /// A regular expression defining which values this namespace includes.
3613 pub regex: String,
3614}
3615
3616impl From<AppServiceNamespace> for ruma::api::appservice::Namespace {
3617 fn from(conf: AppServiceNamespace) -> Self {
3618 Self {
3619 exclusive: conf.exclusive,
3620 regex: conf.regex,
3621 }
3622 }
3623}
3624
3625const DEPRECATED_KEYS: &[&str; 9] = &[
3626 "cache_capacity",
3627 "conduit_cache_capacity_modifier",
3628 "max_concurrent_requests",
3629 "well_known_client",
3630 "well_known_server",
3631 "well_known_support_page",
3632 "well_known_support_role",
3633 "well_known_support_email",
3634 "well_known_support_mxid",
3635];
3636
3637impl Config {
3638 /// Pre-initialize config
3639 pub fn load<'a, I>(paths: I) -> Result<Figment>
3640 where
3641 I: Iterator<Item = &'a Path>,
3642 {
3643 let envs = [
3644 Env::var("CONDUIT_CONFIG"),
3645 Env::var("CONDUWUIT_CONFIG"),
3646 Env::var("TUWUNEL_CONFIG"),
3647 ];
3648
3649 let toml_files = envs
3650 .iter()
3651 .flatten()
3652 .map(PathBuf::from)
3653 .chain(paths.map(Path::to_path_buf))
3654 .collect_vec();
3655
3656 let invalid_toml_files = toml_files
3657 .iter()
3658 .filter(|path| !path.exists())
3659 .map(|path| path.clone().into_os_string())
3660 .collect_vec();
3661
3662 if !invalid_toml_files.is_empty() {
3663 return Err!(
3664 "The following config files do not exist or have broken symlinks: \
3665 {invalid_toml_files:?}"
3666 );
3667 }
3668
3669 let config = toml_files
3670 .iter()
3671 .map(Toml::file)
3672 .map(Data::nested)
3673 .fold(Figment::new(), Figment::merge)
3674 .merge(Env::prefixed("CONDUIT_").global().split("__"))
3675 .merge(Env::prefixed("CONDUWUIT_").global().split("__"))
3676 .merge(Env::prefixed("TUWUNEL_").global().split("__"));
3677
3678 Ok(config)
3679 }
3680
3681 /// Finalize config
3682 pub fn new(raw_config: &Figment) -> Result<Self> {
3683 let config = raw_config
3684 .extract::<Self>()
3685 .map_err(|e| err!("There was a problem with your configuration file: {e}"))?;
3686
3687 Ok(config)
3688 }
3689
3690 pub fn check(&self) -> Result { check(self) }
3691}
3692
3693impl TlsConfig {
3694 #[must_use]
3695 pub fn get_tls_cert_key(&self) -> Option<(&Path, &Path)> {
3696 let cert = self.certs.as_ref()?;
3697
3698 let cert = Path::new(cert);
3699
3700 let key = self.key.as_ref()?; // this cannot fail, aborts startup on cert.is_some ^ key.is_some
3701
3702 let key = Path::new(key);
3703
3704 Some((cert, key))
3705 }
3706}
3707
3708fn true_fn() -> bool { true }
3709
3710fn default_policy_server_request_timeout() -> u64 { 5 }
3711
3712fn some_true_fn() -> Option<bool> { Some(true) }
3713
3714#[cfg(test)]
3715fn default_server_name() -> OwnedServerName { ruma::owned_server_name!("localhost") }
3716
3717fn default_database_path() -> PathBuf { "/var/lib/tuwunel".to_owned().into() }
3718
3719fn default_port() -> ListeningPort { ListeningPort { ports: Left(8008) } }
3720
3721fn default_unix_socket_perms() -> u32 { 660 }
3722
3723fn default_database_backups_to_keep() -> i16 { 1 }
3724
3725fn default_db_write_buffer_capacity_mb() -> f64 { 48.0 + parallelism_scaled_f64(4.0) }
3726
3727fn default_db_cache_capacity_mb() -> f64 { 128.0 + parallelism_scaled_f64(64.0) }
3728
3729fn default_pdu_cache_capacity() -> u32 { parallelism_scaled_u32(10_000).saturating_add(100_000) }
3730
3731fn default_cache_capacity_modifier() -> f64 { 1.0 }
3732
3733fn default_auth_chain_cache_capacity() -> u32 {
3734 parallelism_scaled_u32(200_000).saturating_add(100_000)
3735}
3736
3737fn default_shorteventid_cache_capacity() -> u32 {
3738 parallelism_scaled_u32(200_000).saturating_add(400_000)
3739}
3740
3741fn default_eventidshort_cache_capacity() -> u32 {
3742 parallelism_scaled_u32(100_000).saturating_add(400_000)
3743}
3744
3745fn default_eventid_pdu_cache_capacity() -> u32 {
3746 parallelism_scaled_u32(100_000).saturating_add(400_000)
3747}
3748
3749fn default_shortstatekey_cache_capacity() -> u32 {
3750 parallelism_scaled_u32(4_000).saturating_add(97_000)
3751}
3752
3753fn default_statekeyshort_cache_capacity() -> u32 {
3754 parallelism_scaled_u32(4_000).saturating_add(97_000)
3755}
3756
3757fn default_servernameevent_data_cache_capacity() -> u32 {
3758 parallelism_scaled_u32(60_000).saturating_add(470_000)
3759}
3760
3761fn default_stateinfo_cache_capacity() -> u32 { parallelism_scaled_u32(100) }
3762
3763fn default_spacehierarchy_cache_ttl_min() -> u64 { 60 * 60 * 3 }
3764
3765fn default_spacehierarchy_cache_ttl_max() -> u64 { 60 * 60 * 18 }
3766
3767fn default_dns_cache_entries() -> u32 { 32768 }
3768
3769fn default_dns_min_ttl() -> u64 { 60 * 180 }
3770
3771fn default_dns_min_ttl_nxdomain() -> u64 { 60 * 60 * 24 * 3 }
3772
3773fn default_dns_attempts() -> u16 { 10 }
3774
3775fn default_dns_timeout() -> u64 { 10 }
3776
3777fn default_ip_lookup_strategy() -> u8 { 5 }
3778
3779fn default_max_request_size() -> usize { 24 * 1024 * 1024 }
3780
3781fn default_max_pending_media_uploads() -> usize { 5 }
3782
3783fn default_media_create_unused_expiration_time() -> u64 { 86400 }
3784
3785fn default_media_rc_create_per_second() -> u32 { 10 }
3786
3787fn default_media_rc_create_burst_count() -> u32 { 50 }
3788
3789fn default_request_conn_timeout() -> u64 { 10 }
3790
3791fn default_request_timeout() -> u64 { 35 }
3792
3793fn default_request_total_timeout() -> u64 { 320 }
3794
3795fn default_request_idle_timeout() -> u64 { 5 }
3796
3797fn default_request_idle_per_host() -> u16 { 1 }
3798
3799fn default_well_known_conn_timeout() -> u64 { 6 }
3800
3801fn default_well_known_timeout() -> u64 { 10 }
3802
3803fn default_federation_timeout() -> u64 { 25 }
3804
3805fn default_federation_idle_timeout() -> u64 { 25 }
3806
3807fn default_federation_idle_per_host() -> u16 { 1 }
3808
3809fn default_sender_timeout() -> u64 { 180 }
3810
3811fn default_sender_idle_timeout() -> u64 { 180 }
3812
3813fn default_sender_retry_backoff_limit() -> u64 { 86400 }
3814
3815fn default_appservice_timeout() -> u64 { 35 }
3816
3817fn default_appservice_idle_timeout() -> u64 { 300 }
3818
3819fn default_pusher_idle_timeout() -> u64 { 15 }
3820
3821fn default_max_fetch_prev_events() -> u16 { 192_u16 }
3822
3823fn default_tracing_flame_filter() -> String {
3824 cfg!(debug_assertions)
3825 .then_some("trace,h2=off")
3826 .unwrap_or("info")
3827 .to_owned()
3828}
3829
3830fn default_jaeger_filter() -> String {
3831 cfg!(debug_assertions)
3832 .then_some("trace,h2=off")
3833 .unwrap_or("info")
3834 .to_owned()
3835}
3836
3837fn default_tracing_flame_output_path() -> String { "./tracing.folded".to_owned() }
3838
3839fn default_trusted_servers() -> Vec<OwnedServerName> {
3840 vec![OwnedServerName::try_from("matrix.org").expect("valid ServerName")]
3841}
3842
3843/// do debug logging by default for debug builds
3844#[must_use]
3845pub fn default_log() -> String {
3846 cfg!(debug_assertions)
3847 .then_some("debug")
3848 .unwrap_or("info")
3849 .to_owned()
3850}
3851
3852#[must_use]
3853pub fn default_log_span_events() -> String { "none".into() }
3854
3855fn default_notification_push_path() -> String { "/_matrix/push/v1/notify".to_owned() }
3856
3857fn default_openid_token_ttl() -> u64 { 60 * 60 }
3858
3859fn default_login_token_ttl() -> u64 { 2 * 60 * 1000 }
3860
3861fn default_turn_ttl() -> u64 { 60 * 60 * 24 }
3862
3863fn default_presence_idle_timeout_s() -> u64 { 5 * 60 }
3864
3865fn default_presence_offline_timeout_s() -> u64 { 30 * 60 }
3866
3867fn default_typing_federation_timeout_s() -> u64 { 30 }
3868
3869fn default_typing_client_timeout_min_s() -> u64 { 15 }
3870
3871fn default_typing_client_timeout_max_s() -> u64 { 45 }
3872
3873fn default_rocksdb_recovery_mode() -> u8 { 1 }
3874
3875fn default_rocksdb_log_level() -> String { "error".to_owned() }
3876
3877fn default_rocksdb_log_time_to_roll() -> usize { 0 }
3878
3879fn default_rocksdb_max_log_files() -> usize { 3 }
3880
3881fn default_rocksdb_max_log_file_size() -> usize {
3882 // 4 megabytes
3883 4 * 1024 * 1024
3884}
3885
3886fn default_rocksdb_parallelism_threads() -> usize { 0 }
3887
3888fn default_rocksdb_compression_algo() -> String {
3889 cfg!(feature = "zstd_compression")
3890 .then_some("zstd")
3891 .unwrap_or("none")
3892 .to_owned()
3893}
3894
3895/// Default RocksDB compression level is 32767, which is internally read by
3896/// RocksDB as the default magic number and translated to the library's default
3897/// compression level as they all differ. See their `kDefaultCompressionLevel`.
3898#[expect(clippy::doc_markdown)]
3899fn default_rocksdb_compression_level() -> i32 { 32767 }
3900
3901/// Default RocksDB compression level is 32767, which is internally read by
3902/// RocksDB as the default magic number and translated to the library's default
3903/// compression level as they all differ. See their `kDefaultCompressionLevel`.
3904#[expect(clippy::doc_markdown)]
3905fn default_rocksdb_bottommost_compression_level() -> i32 { 32767 }
3906
3907fn default_rocksdb_stats_level() -> u8 { 1 }
3908
3909// I know, it's a great name
3910#[must_use]
3911#[inline]
3912pub fn default_default_room_version() -> RoomVersionId { RoomVersionId::V11 }
3913
3914fn default_ip_range_denylist() -> Vec<String> {
3915 vec![
3916 "127.0.0.0/8".to_owned(),
3917 "10.0.0.0/8".to_owned(),
3918 "172.16.0.0/12".to_owned(),
3919 "192.168.0.0/16".to_owned(),
3920 "100.64.0.0/10".to_owned(),
3921 "192.0.0.0/24".to_owned(),
3922 "169.254.0.0/16".to_owned(),
3923 "192.88.99.0/24".to_owned(),
3924 "198.18.0.0/15".to_owned(),
3925 "192.0.2.0/24".to_owned(),
3926 "198.51.100.0/24".to_owned(),
3927 "203.0.113.0/24".to_owned(),
3928 "224.0.0.0/4".to_owned(),
3929 "::1/128".to_owned(),
3930 "fe80::/10".to_owned(),
3931 "fc00::/7".to_owned(),
3932 "2001:db8::/32".to_owned(),
3933 "ff00::/8".to_owned(),
3934 "fec0::/10".to_owned(),
3935 ]
3936}
3937
3938fn default_url_preview_max_spider_size() -> usize {
3939 256_000 // 256KB
3940}
3941
3942fn default_new_user_displayname_suffix() -> String { "💕".to_owned() }
3943
3944fn default_sentry_endpoint() -> Option<Url> {
3945 let url = "https://8994b1762a6a95af9502a7900edabc4c@o4509498990067712.ingest.us.sentry.io/4509498993213440"
3946 .try_into()
3947 .expect("default sentry url is invalid");
3948
3949 Some(url)
3950}
3951
3952fn default_sentry_traces_sample_rate() -> f32 { 0.15 }
3953
3954fn default_sentry_filter() -> String { "info".to_owned() }
3955
3956fn default_startup_netburst_keep() -> i64 { 50 }
3957
3958fn default_admin_log_capture() -> String {
3959 cfg!(debug_assertions)
3960 .then_some("debug")
3961 .unwrap_or("info")
3962 .to_owned()
3963}
3964
3965fn default_admin_room_tag() -> String { "m.server_notice".to_owned() }
3966
3967#[expect(clippy::as_conversions, clippy::cast_precision_loss)]
3968fn parallelism_scaled_f64(val: f64) -> f64 { val * (sys::available_parallelism() as f64) }
3969
3970fn parallelism_scaled_u32(val: u32) -> u32 {
3971 let val = val
3972 .try_into()
3973 .expect("failed to cast u32 to usize");
3974 parallelism_scaled(val)
3975 .try_into()
3976 .unwrap_or(u32::MAX)
3977}
3978
3979fn parallelism_scaled(val: usize) -> usize { val.saturating_mul(sys::available_parallelism()) }
3980
3981fn default_trusted_server_batch_size() -> usize { 192 }
3982
3983fn default_trusted_server_batch_concurrency() -> usize { 2 }
3984
3985fn default_db_pool_workers() -> usize {
3986 sys::available_parallelism()
3987 .saturating_mul(4)
3988 .clamp(32, 1024)
3989}
3990
3991fn default_db_pool_workers_limit() -> usize { 64 }
3992
3993fn default_db_pool_max_workers() -> usize { 2048 }
3994
3995fn default_db_pool_queue_mult() -> usize { 4 }
3996
3997fn default_stream_width_default() -> usize { 32 }
3998
3999fn default_stream_width_scale() -> f32 { 1.0 }
4000
4001fn default_stream_amplification() -> usize { 1024 }
4002
4003fn default_client_receive_timeout() -> u64 { 75 }
4004
4005fn default_client_request_timeout() -> u64 { 240 }
4006
4007fn default_client_response_timeout() -> u64 { 120 }
4008
4009fn default_client_shutdown_timeout() -> u64 { 15 }
4010
4011fn default_sender_shutdown_timeout() -> u64 { 5 }
4012
4013// blurhashing defaults recommended by https://blurha.sh/
4014// 2^25
4015fn default_blurhash_max_raw_size() -> u64 { 33_554_432 }
4016
4017fn default_blurhash_x_component() -> u32 { 4 }
4018
4019fn default_blurhash_y_component() -> u32 { 3 }
4020
4021fn default_ldap_search_filter() -> String { "(objectClass=*)".to_owned() }
4022
4023fn default_ldap_uid_attribute() -> String { String::from("uid") }
4024
4025fn default_ldap_name_attribute() -> String { String::from("givenName") }
4026
4027fn default_jwt_algorithm() -> String { "HS256".to_owned() }
4028
4029fn default_jwt_format() -> String { "HMAC".to_owned() }
4030
4031fn default_client_sync_timeout_min() -> u64 { 5000 }
4032
4033fn default_client_sync_timeout_default() -> u64 { 30000 }
4034
4035fn default_client_sync_timeout_max() -> u64 { 90000 }
4036
4037fn default_access_token_ttl() -> u64 { 604_800 }
4038
4039fn default_deprioritize_joins_through_servers() -> RegexSet {
4040 RegexSet::new([r"matrix\.org"]).expect("valid set of regular expressions")
4041}
4042
4043fn default_one_time_key_limit() -> usize { 256 }
4044
4045fn default_max_make_join_attempts_per_join_attempt() -> usize { 48 }
4046
4047fn default_max_join_attempts_per_join_request() -> usize { 3 }
4048
4049fn default_sso_grant_session_duration() -> Option<u64> { Some(300) }
4050
4051fn default_redaction_retention_seconds() -> u64 { 5_184_000 }
4052
4053fn default_media_storage_providers() -> BTreeSet<String> { ["media".to_owned()].into() }
4054
4055fn default_multipart_threshold() -> ByteSize { ByteSize::mib(100) }
4056
4057fn default_multipart_part_size() -> ByteSize { ByteSize::mib(10) }