tuwunel_core/config/mod.rs
1//! Loads and validates server configuration.
2//!
3//! Configuration types preserve startup sources for reloads and expose typed
4//! settings to the rest of the workspace. Field documentation also supplies the
5//! generated example configuration.
6
7pub mod check;
8mod identity_provider_serde;
9pub mod ip_source;
10pub mod manager;
11mod net;
12pub mod proxy;
13mod regenerate;
14pub mod room_version;
15pub mod sources;
16#[cfg(test)]
17mod tests;
18pub mod well_known;
19
20use std::{
21 collections::{BTreeMap, BTreeSet},
22 net::IpAddr,
23 path::{Path, PathBuf},
24};
25
26use bytesize::ByteSize;
27use derive_more::Debug;
28use either::{Either, Either::Left};
29pub use figment::{Figment, value::Value as FigmentValue};
30use figment::{
31 Profile, Provider,
32 providers::{Env, Format, Toml},
33};
34use ipnet::IpNet;
35use itertools::Itertools;
36use regex::RegexSet;
37use ruma::{
38 OwnedMxcUri, OwnedRoomOrAliasId, OwnedServerName, OwnedUserId, RoomVersionId,
39 api::client::discovery::discover_support::ContactRole,
40};
41use serde::{Deserialize, de::IgnoredAny};
42use tuwunel_macros::config_example_generator;
43use url::Url;
44
45pub use self::{
46 check::check,
47 ip_source::IpSource,
48 manager::Manager,
49 regenerate::{
50 Overwrite, RegenerateOptions, RegenerationSummary, example_config, regenerate_config,
51 write_example_config,
52 },
53 sources::Sources,
54};
55use self::{
56 net::{ListeningAddr, ListeningPort},
57 proxy::ProxyConfig,
58};
59use crate::{
60 Err, Result, err, implement, redacted_debug,
61 utils::{self, bytes::deserialize_bytesize_usize, sys},
62};
63
64// Later prefixes override earlier ones.
65pub(crate) const ENV_PREFIXES: [&str; 3] = ["CONDUIT_", "CONDUWUIT_", "TUWUNEL_"];
66
67/// All the config options for tuwunel.
68#[expect(rustdoc::broken_intra_doc_links, rustdoc::bare_urls)]
69#[derive(Clone, Deserialize)]
70#[config_example_generator(
71 filename = "tuwunel-example.toml",
72 section = "global",
73 undocumented = "# This item is undocumented. Please contribute documentation for it.",
74 header = r#"### Tuwunel Configuration
75###
76### THIS FILE IS GENERATED. CHANGES/CONTRIBUTIONS IN THE REPO WILL BE
77### OVERWRITTEN!
78###
79### You should rename this file before configuring your server. Changes to
80### documentation and defaults can be contributed in source code at
81### src/core/config/mod.rs. This file is generated when building.
82###
83### Any values pre-populated are the default values for said config option.
84###
85### At the minimum, you MUST edit all the config options to your environment
86### that say "YOU NEED TO EDIT THIS".
87###
88### For more information, see:
89### https://tuwunel.chat/configuration.html
90"#,
91 ignore = "catchall well_known tls ldap jwt appservice identity_provider storage_provider \
92 registration_terms smtp",
93 hidden = "allow_invalid_tls_certificates",
94 forbidden = "database_restore_backup force_migration"
95)]
96pub struct Config {
97 /// The server_name is the pretty name of this server. It is used as a
98 /// suffix for user and room IDs/aliases.
99 ///
100 /// See the docs for reverse proxying and delegation:
101 /// https://tuwunel.chat/deploying/generic.html#setting-up-the-reverse-proxy
102 ///
103 /// Also see the `[global.well_known]` config section at the very bottom.
104 ///
105 /// Examples of delegation:
106 /// - https://matrix.org/.well-known/matrix/server
107 /// - https://matrix.org/.well-known/matrix/client
108 ///
109 /// YOU NEED TO EDIT THIS. THIS CANNOT BE CHANGED AFTER WITHOUT A DATABASE
110 /// WIPE.
111 ///
112 /// example: "girlboss.ceo"
113 #[cfg_attr(test, serde(default = "default_server_name"))]
114 pub server_name: OwnedServerName,
115
116 /// This is the only directory where tuwunel will save its data, including
117 /// media. Note: this was previously "/var/lib/matrix-conduit".
118 ///
119 /// default: "/var/lib/tuwunel"
120 #[serde(default = "default_database_path")]
121 pub database_path: PathBuf,
122
123 /// Text which will be added to the end of the user's displayname upon
124 /// registration with a space before the text. In Conduit, this was the
125 /// lightning bolt emoji.
126 ///
127 /// To disable, set this to "" (an empty string).
128 ///
129 /// reloadable: yes
130 /// default: "💕"
131 #[serde(default = "default_new_user_displayname_suffix")]
132 pub new_user_displayname_suffix: String,
133
134 #[expect(clippy::doc_link_with_quotes)]
135 /// The default address (IPv4 or IPv6) tuwunel will listen on.
136 ///
137 /// If you are using Docker or a container NAT networking setup, this must
138 /// be "0.0.0.0".
139 ///
140 /// To listen on multiple addresses, specify a vector e.g. ["127.0.0.1",
141 /// "::1"]
142 ///
143 /// An address set here must bind or the server refuses to start. The
144 /// default is only a guess that both loopback families exist, so one of
145 /// them failing to bind is logged and skipped instead.
146 ///
147 /// default: ["127.0.0.1", "::1"]
148 #[serde(default)]
149 address: Option<ListeningAddr>,
150
151 /// The port(s) tuwunel will listen on.
152 ///
153 /// For reverse proxying, see:
154 /// https://tuwunel.chat/deploying/generic.html#setting-up-the-reverse-proxy
155 ///
156 /// If you are using Docker, don't change this, you'll need to map an
157 /// external port to this.
158 ///
159 /// To listen on multiple ports, specify a vector e.g. [8080, 8448]
160 ///
161 /// default: 8008
162 #[serde(default = "default_port")]
163 port: ListeningPort,
164
165 /// Configures direct TLS listeners.
166 ///
167 /// Values are read from the separate `[global.tls]` section. Certificate
168 /// and key paths must be supplied together before TLS is enabled.
169 // external structure; separate section
170 #[serde(default)]
171 pub tls: TlsConfig,
172
173 /// The UNIX socket tuwunel will listen on.
174 ///
175 /// Remember to make sure that your reverse proxy has access to this socket
176 /// file, either by adding your reverse proxy to the 'tuwunel' group or
177 /// granting world R/W permissions with `unix_socket_perms` (666 minimum).
178 ///
179 /// example: "/run/tuwunel/tuwunel.sock"
180 pub unix_socket_path: Option<PathBuf>,
181
182 /// The default permissions (in octal) to create the UNIX socket with.
183 ///
184 /// default: 660
185 #[serde(default = "default_unix_socket_perms")]
186 pub unix_socket_perms: u32,
187
188 /// Error on startup if any config option specified is unknown to Tuwunel.
189 ///
190 /// This is false by default to allow easier deprecation or removal of
191 /// config options in the future without breaking existing deployments. The
192 /// default behaviour is to simply warn on startup.
193 /// reloadable: yes
194 #[serde(default)]
195 pub error_on_unknown_config_opts: bool,
196
197 /// tuwunel supports online database backups using RocksDB's Backup engine
198 /// API. To use this, set a database backup path that tuwunel can write
199 /// to.
200 ///
201 /// For more information, see:
202 /// https://tuwunel.chat/maintenance.html#backups
203 ///
204 /// reloadable: yes
205 /// example: "/opt/tuwunel-db-backups"
206 pub database_backup_path: Option<PathBuf>,
207
208 /// The amount of online RocksDB database backups to keep/retain, if using
209 /// "database_backup_path", before deleting the oldest one. This must be at
210 /// least 1; "backup-database" is an error at 0 or below.
211 ///
212 /// reloadable: yes
213 /// default: 1
214 #[serde(default = "default_database_backups_to_keep")]
215 pub database_backups_to_keep: i16,
216
217 /// Restore this online database backup on startup, before the database is
218 /// opened. The value is a backup ID as listed by `!admin server
219 /// list-backups`, or 0 for the most recent backup. Set by the
220 /// `--restore-backup` command line argument, and refused from a
221 /// configuration file, where it would repeat the restore on every
222 /// startup.
223 pub database_restore_backup: Option<u32>,
224
225 /// Set this to any float value to multiply tuwunel's in-memory LRU caches
226 /// with such as "auth_chain_cache_capacity".
227 ///
228 /// May be useful if you have significant memory to spare to increase
229 /// performance.
230 ///
231 /// If you have low memory, reducing this may be viable.
232 ///
233 /// By default, the individual caches such as "auth_chain_cache_capacity"
234 /// are scaled by your CPU core count.
235 ///
236 /// default: 1.0
237 #[serde(
238 default = "default_cache_capacity_modifier",
239 alias = "conduit_cache_capacity_modifier"
240 )]
241 pub cache_capacity_modifier: f64,
242
243 /// Set this to any float value in megabytes for tuwunel to tell the
244 /// database engine that this much memory is available for database read
245 /// caches.
246 ///
247 /// May be useful if you have significant memory to spare to increase
248 /// performance.
249 ///
250 /// Similar to the individual LRU caches, this is scaled up with your CPU
251 /// core count.
252 ///
253 /// This defaults to 128.0 + (64.0 * CPU core count).
254 ///
255 /// default: varies by system
256 #[serde(default = "default_db_cache_capacity_mb")]
257 pub db_cache_capacity_mb: f64,
258
259 /// Set this to any float value in megabytes for tuwunel to tell the
260 /// database engine that this much memory is available for database write
261 /// caches.
262 ///
263 /// May be useful if you have significant memory to spare to increase
264 /// performance.
265 ///
266 /// Similar to the individual LRU caches, this is scaled up with your CPU
267 /// core count.
268 ///
269 /// This defaults to 48.0 + (4.0 * CPU core count).
270 ///
271 /// default: varies by system
272 #[serde(default = "default_db_write_buffer_capacity_mb")]
273 pub db_write_buffer_capacity_mb: f64,
274
275 /// Maximum number of entries in the RocksDB block cache shared by the
276 /// `pduid_pdu` and `eventid_outlierpdu` column families: a PDU's full
277 /// body, keyed by its PDU ID, and the same body keyed by event ID when
278 /// the PDU is an outlier.
279 ///
280 /// This is an entry count, not a byte size. The cache's actual capacity
281 /// in bytes is this value multiplied by an internal per-column-family
282 /// key+value size estimate, then multiplied by `cache_capacity_modifier`.
283 ///
284 /// Scaled by your CPU core count by default; see
285 /// `cache_capacity_modifier` to scale this along with the other
286 /// individual LRU caches at once.
287 ///
288 /// default: varies by system
289 #[serde(default = "default_pdu_cache_capacity")]
290 pub pdu_cache_capacity: u32,
291
292 /// Maximum number of entries in the RocksDB block cache for the
293 /// `authchainkey_authchain` column family: a room event's full auth
294 /// chain, keyed by the set of events the chain was derived from.
295 ///
296 /// Same entry-count semantics as `pdu_cache_capacity` above; see there
297 /// for how this becomes a byte capacity and how
298 /// `cache_capacity_modifier` applies.
299 ///
300 /// default: varies by system
301 #[serde(default = "default_auth_chain_cache_capacity")]
302 pub auth_chain_cache_capacity: u32,
303
304 /// Maximum number of entries in the RocksDB block cache for the
305 /// `shorteventid_eventid` column family: an event's full event ID,
306 /// looked up from its short event ID.
307 ///
308 /// Same entry-count semantics as `pdu_cache_capacity`.
309 ///
310 /// default: varies by system
311 #[serde(default = "default_shorteventid_cache_capacity")]
312 pub shorteventid_cache_capacity: u32,
313
314 /// Maximum number of entries in the RocksDB block cache for the
315 /// `eventid_shorteventid` column family: an event's short event ID,
316 /// looked up from its full event ID. The reverse lookup of
317 /// `shorteventid_cache_capacity`.
318 ///
319 /// Same entry-count semantics as `pdu_cache_capacity`.
320 ///
321 /// default: varies by system
322 #[serde(default = "default_eventidshort_cache_capacity")]
323 pub eventidshort_cache_capacity: u32,
324
325 /// Maximum number of entries in the RocksDB block cache for the
326 /// `eventid_pduid` column family: an event's PDU ID, looked up from its
327 /// full event ID.
328 ///
329 /// Same entry-count semantics as `pdu_cache_capacity`.
330 ///
331 /// default: varies by system
332 #[serde(default = "default_eventid_pdu_cache_capacity")]
333 pub eventid_pdu_cache_capacity: u32,
334
335 /// Maximum number of entries in the RocksDB block cache for the
336 /// `eventid_backoff` column family: the recent fetch, auth, and upgrade
337 /// outcomes an event is rate-gated against, keyed by federation step,
338 /// event ID, and time bucket.
339 ///
340 /// Same entry-count semantics as `pdu_cache_capacity`. A server working
341 /// through a large missing-ancestry gap reads this column heavily.
342 ///
343 /// default: varies by system
344 #[serde(default = "default_eventid_backoff_cache_capacity")]
345 pub eventid_backoff_cache_capacity: u32,
346
347 /// Maximum number of entries in the RocksDB block cache for the
348 /// `shortstatekey_statekey` column family: a state event's full state
349 /// key, looked up from its short state key.
350 ///
351 /// Same entry-count semantics as `pdu_cache_capacity`.
352 ///
353 /// default: varies by system
354 #[serde(default = "default_shortstatekey_cache_capacity")]
355 pub shortstatekey_cache_capacity: u32,
356
357 /// Maximum number of entries in the RocksDB block cache for the
358 /// `statekey_shortstatekey` column family: a state event's short state
359 /// key, looked up from its full state key. The reverse lookup of
360 /// `shortstatekey_cache_capacity`.
361 ///
362 /// Same entry-count semantics as `pdu_cache_capacity`.
363 ///
364 /// default: varies by system
365 #[serde(default = "default_statekeyshort_cache_capacity")]
366 pub statekeyshort_cache_capacity: u32,
367
368 /// Maximum number of entries in the RocksDB block cache for the
369 /// `servernameevent_data` column family: outbound federation events
370 /// (PDUs and EDUs) queued for delivery, keyed by destination server
371 /// name.
372 ///
373 /// Same entry-count semantics as `pdu_cache_capacity`.
374 ///
375 /// default: varies by system
376 #[serde(default = "default_servernameevent_data_cache_capacity")]
377 pub servernameevent_data_cache_capacity: u32,
378
379 /// Maximum number of entries in the RocksDB block cache for the
380 /// `mediaid_lazycontent` column family: preview images staged by the URL
381 /// preview fetcher, keyed by media ID, until a download promotes the row.
382 ///
383 /// Same entry-count semantics as `pdu_cache_capacity`, against a modal
384 /// 256 KiB entry. Staged rows are read once and deleted at promotion, so
385 /// this mostly holds index blocks, and a single outsized preview can
386 /// exceed the whole pool.
387 ///
388 /// default: 128
389 #[serde(default = "default_mediaid_lazycontent_cache_capacity")]
390 pub mediaid_lazycontent_cache_capacity: u32,
391
392 /// Maximum number of entries in the RocksDB block cache pool shared by the
393 /// `servername_destination` and `servername_override` column families: a
394 /// remote server's resolved federation destination, keyed by server name,
395 /// and the address override for a resolved hostname.
396 ///
397 /// Same entry-count semantics as `pdu_cache_capacity`, counted across the
398 /// pool rather than per column.
399 ///
400 /// default: varies by system
401 #[serde(default = "default_resolver_cache_capacity")]
402 pub resolver_cache_capacity: u32,
403
404 /// Maximum number of entries in the RocksDB block cache for the
405 /// `servername_status` column family: a remote server's recent
406 /// reachability outcome, keyed by server name and time bucket.
407 ///
408 /// Same entry-count semantics as `pdu_cache_capacity`.
409 ///
410 /// default: varies by system
411 #[serde(default = "default_servername_status_cache_capacity")]
412 pub servername_status_cache_capacity: u32,
413
414 /// Maximum number of entries in the in-memory LRU cache of decompressed
415 /// room state (a list of short state-info entries per state hash), used
416 /// by the state compressor to avoid re-walking
417 /// `shortstatehash_statediff` on every lookup.
418 ///
419 /// Unlike the other caches on this page, this one is not backed by
420 /// RocksDB: it is a plain in-process cache sized directly in entries,
421 /// with no per-entry byte-size conversion. `cache_capacity_modifier`
422 /// still applies to it.
423 ///
424 /// default: varies by system
425 #[serde(default = "default_stateinfo_cache_capacity")]
426 pub stateinfo_cache_capacity: u32,
427
428 /// Minimum time-to-live in seconds for room summary entries in the spaces
429 /// cache.
430 ///
431 /// reloadable: yes
432 /// default: 10800
433 #[serde(default = "default_spacehierarchy_cache_ttl_min")]
434 pub spacehierarchy_cache_ttl_min: u64,
435
436 /// Maximum time-to-live in seconds for room summary entries in the spaces
437 /// cache.
438 ///
439 /// reloadable: yes
440 /// default: 64800
441 #[serde(default = "default_spacehierarchy_cache_ttl_max")]
442 pub spacehierarchy_cache_ttl_max: u64,
443
444 /// Minimum timeout a client can request for long-polling sync. Requests
445 /// will be clamped up to this value if smaller.
446 ///
447 /// reloadable: yes
448 /// default: 5000
449 #[serde(default = "default_client_sync_timeout_min")]
450 pub client_sync_timeout_min: u64,
451
452 /// Default timeout for long-polling sync if a client does not request
453 /// another in their query-string.
454 ///
455 /// reloadable: yes
456 /// default: 30000
457 #[serde(default = "default_client_sync_timeout_default")]
458 pub client_sync_timeout_default: u64,
459
460 /// Maximum timeout a client can request for long-polling sync. Requests
461 /// will be clamped down to this value if larger.
462 ///
463 /// reloadable: yes
464 /// default: 90000
465 #[serde(default = "default_client_sync_timeout_max")]
466 pub client_sync_timeout_max: u64,
467
468 /// Custom DNS servers to query instead of the operating system's default
469 /// resolvers; when this list is non-empty, `/etc/resolv.conf` is never
470 /// read. Each entry is an IP address with an optional port, defaulting to
471 /// port 53. The servers are assumed to support both UDP and TCP on that
472 /// port; enable `query_over_tcp_only` if any of them is TCP-only.
473 ///
474 /// example: ["127.0.0.53", "1.1.1.1:5353", "[fd00::1]:53"]
475 ///
476 /// default: []
477 #[serde(default)]
478 pub dns_servers: Vec<String>,
479
480 /// Maximum entries stored in DNS memory-cache. The size of an entry may
481 /// vary so please take care if raising this value excessively. Only
482 /// decrease this when using an external DNS cache. Please note that
483 /// systemd-resolved does *not* count as an external cache, even when
484 /// configured to do so.
485 ///
486 /// default: 32768
487 #[serde(default = "default_dns_cache_entries")]
488 pub dns_cache_entries: u32,
489
490 /// Minimum time-to-live in seconds for entries in the DNS cache. The
491 /// default may appear high to most administrators; this is by design as the
492 /// exotic loads of federating to many other servers require a higher TTL
493 /// than many domains have set. Even when using an external DNS cache the
494 /// problem is shifted to that cache which is ignorant of its role for
495 /// this application and can adhere to many low TTL's increasing its load.
496 ///
497 /// default: 10800
498 #[serde(default = "default_dns_min_ttl")]
499 pub dns_min_ttl: u64,
500
501 /// Minimum time-to-live in seconds for NXDOMAIN entries in the DNS cache.
502 /// This value is critical for the server to federate efficiently.
503 /// NXDOMAIN's are assumed to not be returning to the federation and
504 /// aggressively cached rather than constantly rechecked.
505 ///
506 /// Defaults to 3 days as these are *very rarely* false negatives.
507 ///
508 /// default: 259200
509 #[serde(default = "default_dns_min_ttl_nxdomain")]
510 pub dns_min_ttl_nxdomain: u64,
511
512 /// Number of DNS nameserver retries after a timeout or error.
513 ///
514 /// default: 10
515 #[serde(default = "default_dns_attempts")]
516 pub dns_attempts: u16,
517
518 /// The number of seconds to wait for a reply to a DNS query. Please note
519 /// that recursive queries can take up to several seconds for some domains,
520 /// so this value should not be too low, especially on slower hardware or
521 /// resolvers.
522 ///
523 /// default: 10
524 #[serde(default = "default_dns_timeout")]
525 pub dns_timeout: u64,
526
527 /// Fallback to TCP on DNS errors. Set this to false if unsupported by
528 /// nameserver.
529 #[serde(default = "true_fn")]
530 pub dns_tcp_fallback: bool,
531
532 /// Enable to query all nameservers until the domain is found. Referred to
533 /// as "trust_negative_responses" in hickory_resolver. This can avoid
534 /// useless DNS queries if the first nameserver responds with NXDOMAIN or
535 /// an empty NOERROR response.
536 #[serde(default = "true_fn")]
537 pub query_all_nameservers: bool,
538
539 /// Enable using *only* TCP for querying your specified nameservers instead
540 /// of UDP.
541 ///
542 /// If you are running tuwunel in a container environment, this config
543 /// option may need to be enabled. For more details, see:
544 /// https://tuwunel.chat/troubleshooting.html#potential-dns-issues-when-using-docker
545 #[serde(default)]
546 pub query_over_tcp_only: bool,
547
548 /// DNS A/AAAA record lookup strategy
549 ///
550 /// Takes a number of one of the following options:
551 /// 1 - Ipv4Only (Only query for A records, no AAAA/IPv6)
552 ///
553 /// 2 - Ipv6Only (Only query for AAAA records, no A/IPv4)
554 ///
555 /// 3 - Ipv4AndIpv6 (Query for A and AAAA records in parallel, uses whatever
556 /// returns a successful response first)
557 ///
558 /// 4 - Ipv6thenIpv4 (Query for AAAA record, if that fails then query the A
559 /// record)
560 ///
561 /// 5 - Ipv4thenIpv6 (Query for A record, if that fails then query the AAAA
562 /// record)
563 ///
564 /// If you don't have IPv6 networking, then for better DNS performance it
565 /// may be suitable to set this to Ipv4Only (1) as you will never ever use
566 /// the AAAA record contents even if the AAAA record is successful instead
567 /// of the A record.
568 ///
569 /// default: 5
570 #[serde(default = "default_ip_lookup_strategy")]
571 pub ip_lookup_strategy: u8,
572
573 /// List of domain patterns resolved via the alternative path without any
574 /// persistent cache, very small memory cache, and no enforced TTL. This
575 /// is intended for internal network and application services which require
576 /// these specific properties. This path does not support federation or
577 /// general purposes.
578 ///
579 /// reloadable: yes
580 /// example: ["*\.dns\.podman$"]
581 ///
582 /// default: []
583 #[serde(default, with = "serde_regex")]
584 pub dns_passthru_domains: RegexSet,
585
586 /// Whether to resolve appservices via the alternative path; setting this is
587 /// superior to providing domains in `dns_passthru_domains` if all
588 /// appservices intend to be matched anyway. The overhead of matching regex
589 /// and maintaining the list of domains can be avoided.
590 #[serde(default)]
591 pub dns_passthru_appservices: bool,
592
593 /// Enable or disable case randomization for DNS queries. This is a security
594 /// mitigation where answer spoofing is prevented by having to exactly match
595 /// the question. Occasional errors seen in logs which may have lead you
596 /// here tend to be from overloading DNS. Nevertheless for servers which
597 /// are truly incapable this can be set to false.
598 ///
599 /// This currently defaults to false due to user reports regarding some
600 /// popular DNS caches which may or may not be patched soon. It may again
601 /// default to true in an upcoming release.
602 #[serde(default)]
603 pub dns_case_randomization: bool,
604
605 /// Max request size for file uploads. Accepts an integer byte count or a
606 /// string with SI/IEC suffix such as "24 MiB".
607 ///
608 /// default: 24 MiB
609 #[serde(
610 default = "default_max_request_size",
611 deserialize_with = "deserialize_bytesize_usize"
612 )]
613 pub max_request_size: usize,
614
615 /// Maximum size of a response body buffered from a remote server. Applies
616 /// to federation requests, push gateway and appservice transactions, and
617 /// remote media fetched for URL previews. A peer cannot be trusted to honor
618 /// a requested limit, so this bounds the response held in memory
619 /// regardless, guarding against a remote driving the process out of
620 /// memory. Accepts an integer byte count or a string with SI/IEC suffix
621 /// such as "256 MiB".
622 ///
623 /// default: 256 MiB
624 #[serde(
625 default = "default_max_response_size",
626 deserialize_with = "deserialize_bytesize_usize"
627 )]
628 pub max_response_size: usize,
629
630 /// Maximum number of concurrently pending (asynchronous) media uploads a
631 /// user can have.
632 ///
633 /// reloadable: yes
634 /// default: 5
635 #[serde(default = "default_max_pending_media_uploads")]
636 pub max_pending_media_uploads: usize,
637
638 /// The time in seconds before an unused pending MXC URI expires and is
639 /// removed.
640 ///
641 /// reloadable: yes
642 /// default: 86400 (24 hours)
643 #[serde(default = "default_media_create_unused_expiration_time")]
644 pub media_create_unused_expiration_time: u64,
645
646 /// The maximum number of media create requests per second allowed from a
647 /// single user.
648 ///
649 /// reloadable: yes
650 /// default: 10
651 #[serde(default = "default_media_rc_create_per_second")]
652 pub media_rc_create_per_second: u32,
653
654 /// The maximum burst count for media create requests from a single user.
655 ///
656 /// reloadable: yes
657 /// default: 50
658 #[serde(default = "default_media_rc_create_burst_count")]
659 pub media_rc_create_burst_count: u32,
660
661 /// reloadable: yes
662 /// default: 1024
663 #[serde(default = "default_max_fetch_prev_events")]
664 pub max_fetch_prev_events: u16,
665
666 /// Maximum time, in milliseconds, to wait for the missing prev_events of an
667 /// incoming timeline event to arrive on their own before fetching them over
668 /// federation. A gap that closes within this window skips the fetch. The
669 /// wait is event-driven and wakes the instant the events arrive, so this is
670 /// a ceiling on added latency, not a fixed cost. Set to 0 to fetch
671 /// immediately.
672 ///
673 /// reloadable: yes
674 /// default: 750
675 #[serde(default = "default_fetch_prev_wait_ms")]
676 pub fetch_prev_wait_ms: u64,
677
678 /// Default/base connection timeout (seconds). This is used only by URL
679 /// previews and update/news endpoint checks.
680 ///
681 /// default: 10
682 #[serde(default = "default_request_conn_timeout")]
683 pub request_conn_timeout: u64,
684
685 /// Default/base request timeout (seconds). The time waiting to receive more
686 /// data from another server. This is used only by URL previews,
687 /// update/news, and misc endpoint checks.
688 ///
689 /// default: 35
690 #[serde(default = "default_request_timeout")]
691 pub request_timeout: u64,
692
693 /// Default/base request total timeout (seconds). The time limit for a whole
694 /// request. This is set very high to not cancel healthy requests while
695 /// serving as a backstop. This is used only by URL previews and update/news
696 /// endpoint checks.
697 ///
698 /// default: 320
699 #[serde(default = "default_request_total_timeout")]
700 pub request_total_timeout: u64,
701
702 /// Default/base idle connection pool timeout (seconds). This is used only
703 /// by URL previews and update/news endpoint checks.
704 ///
705 /// default: 5
706 #[serde(default = "default_request_idle_timeout")]
707 pub request_idle_timeout: u64,
708
709 /// Default/base max idle connections per host. This is used only by URL
710 /// previews and update/news endpoint checks. Defaults to 1 as generally the
711 /// same open connection can be re-used.
712 ///
713 /// default: 1
714 #[serde(default = "default_request_idle_per_host")]
715 pub request_idle_per_host: u16,
716
717 /// Allow the outbound HTTP client to negotiate gzip with other servers:
718 /// advertise it in Accept-Encoding and transparently decompress responses.
719 /// This covers federation, media, and URL preview traffic, and is separate
720 /// from `gzip_compression`, which compresses tuwunel's own responses.
721 ///
722 /// Enabled by default. Set to false to force the client to neither request
723 /// nor decompress gzip. Does nothing unless tuwunel was built with the
724 /// `gzip_compression` feature.
725 ///
726 /// default: true
727 #[serde(default = "true_fn")]
728 pub request_gzip: bool,
729
730 /// Allow the outbound HTTP client to negotiate brotli with other servers:
731 /// advertise it in Accept-Encoding and transparently decompress responses.
732 /// This covers federation, media, and URL preview traffic, and is separate
733 /// from `brotli_compression`, which compresses tuwunel's own responses.
734 ///
735 /// Enabled by default. Set to false to force the client to neither request
736 /// nor decompress brotli. Does nothing unless tuwunel was built with the
737 /// `brotli_compression` feature.
738 ///
739 /// default: true
740 #[serde(default = "true_fn")]
741 pub request_brotli: bool,
742
743 /// Allow the outbound HTTP client to negotiate zstd with other servers:
744 /// advertise it in Accept-Encoding and transparently decompress responses.
745 /// This covers federation, media, and URL preview traffic, and is separate
746 /// from `zstd_compression`, which compresses tuwunel's own responses.
747 ///
748 /// Enabled by default. Set to false to force the client to neither request
749 /// nor decompress zstd. Does nothing unless tuwunel was built with the
750 /// `zstd_compression` feature.
751 ///
752 /// default: true
753 #[serde(default = "true_fn")]
754 pub request_zstd: bool,
755
756 /// Federation well-known resolution connection timeout (seconds).
757 ///
758 /// default: 6
759 #[serde(default = "default_well_known_conn_timeout")]
760 pub well_known_conn_timeout: u64,
761
762 /// Federation HTTP well-known resolution request timeout (seconds).
763 ///
764 /// default: 10
765 #[serde(default = "default_well_known_timeout")]
766 pub well_known_timeout: u64,
767
768 /// Federation client request timeout (seconds). This applies to each read
769 /// from the remote server rather than to the request as a whole, which
770 /// remains bounded by `request_total_timeout`.
771 ///
772 /// default: 25
773 #[serde(default = "default_federation_timeout")]
774 pub federation_timeout: u64,
775
776 /// Timeout (seconds) for client-initiated federation key lookups, namely
777 /// /keys/query and /keys/claim against remote servers. Should be well
778 /// below `federation_timeout` so an interactive request to an unresponsive
779 /// server does not outlast the requesting client's own send deadline. A
780 /// lookup that exceeds this bound records a transient federation failure
781 /// for that server, so subsequent lookups back off instead of blocking
782 /// again.
783 ///
784 /// default: 8
785 #[serde(default = "default_federation_keys_timeout")]
786 pub federation_keys_timeout: u64,
787
788 /// Federation client idle connection pool timeout (seconds).
789 ///
790 /// default: 25
791 #[serde(default = "default_federation_idle_timeout")]
792 pub federation_idle_timeout: u64,
793
794 /// Federation client max idle connections per host. Defaults to 1 as
795 /// generally the same open connection can be re-used.
796 ///
797 /// default: 1
798 #[serde(default = "default_federation_idle_per_host")]
799 pub federation_idle_per_host: u16,
800
801 /// Federation sender request timeout (seconds). The time it takes for the
802 /// remote server to process sent transactions can take a while.
803 ///
804 /// default: 180
805 #[serde(default = "default_sender_timeout")]
806 pub sender_timeout: u64,
807
808 /// Federation sender idle connection pool timeout (seconds).
809 ///
810 /// default: 180
811 #[serde(default = "default_sender_idle_timeout")]
812 pub sender_idle_timeout: u64,
813
814 /// Federation sender transaction retry backoff limit (seconds).
815 ///
816 /// reloadable: yes
817 /// default: 86400
818 #[serde(default = "default_sender_retry_backoff_limit")]
819 pub sender_retry_backoff_limit: u64,
820
821 /// Grace period (seconds) before the first retry of a federation
822 /// destination that has failed exactly once, applied in place of the
823 /// quadratic backoff curve so a single transient failure does not hold
824 /// delivery until the next backoff window. A second consecutive failure
825 /// returns to the backoff curve. Set to 0 to disable the grace and back off
826 /// from the first failure.
827 ///
828 /// default: 15
829 #[serde(default = "default_sender_retry_grace")]
830 pub sender_retry_grace: u64,
831
832 /// Appservice URL request connection timeout. Defaults to 35 seconds as
833 /// generally appservices are hosted within the same network.
834 ///
835 /// default: 35
836 #[serde(default = "default_appservice_timeout")]
837 pub appservice_timeout: u64,
838
839 /// Appservice URL idle connection pool timeout (seconds).
840 ///
841 /// default: 300
842 #[serde(default = "default_appservice_idle_timeout")]
843 pub appservice_idle_timeout: u64,
844
845 /// Notification gateway pusher idle connection pool timeout.
846 ///
847 /// default: 15
848 #[serde(default = "default_pusher_idle_timeout")]
849 pub pusher_idle_timeout: u64,
850
851 /// Maximum time to receive a request from a client (seconds).
852 ///
853 /// default: 75
854 #[serde(default = "default_client_receive_timeout")]
855 pub client_receive_timeout: u64,
856
857 /// Maximum time to process a request received from a client (seconds).
858 ///
859 /// default: 240
860 #[serde(default = "default_client_request_timeout")]
861 pub client_request_timeout: u64,
862
863 /// Maximum time to transmit a response to a client (seconds)
864 ///
865 /// default: 120
866 #[serde(default = "default_client_response_timeout")]
867 pub client_response_timeout: u64,
868
869 /// Grace period for clean shutdown of client requests (seconds).
870 ///
871 /// reloadable: yes
872 /// default: 15
873 #[serde(default = "default_client_shutdown_timeout")]
874 pub client_shutdown_timeout: u64,
875
876 /// Source of the client IP address for rate limiting, logging, and
877 /// security tooling.
878 ///
879 /// When unset (the default), the `ClientIp` extractor scans common
880 /// proxy headers in leftmost-IP mode (`X-Forwarded-For`, RFC 7239
881 /// `Forwarded`, `X-Real-IP`, `Fly-Client-IP`, `True-Client-IP`,
882 /// `CF-Connecting-IP`, `CloudFront-Viewer-Address`) and falls back
883 /// to the TCP peer address; clients can spoof their address via
884 /// request headers in that mode.
885 ///
886 /// When set, `ClientIp` resolves exclusively from the selected
887 /// source. The rightmost value is used for multi-valued headers;
888 /// only the proxy can append to the right, so this is resistant to
889 /// client spoofing.
890 ///
891 /// Supported values:
892 /// - "connect_info" - TCP peer address only (direct connections)
893 /// - "rightmost_x_forwarded_for" - nginx, Caddy
894 /// - "rightmost_forwarded" - RFC 7239 proxies
895 /// - "x_real_ip" - nginx `X-Real-IP`
896 /// - "cf_connecting_ip" - Cloudflare / cloudflared
897 /// - "true_client_ip" - Akamai, Cloudflare Enterprise
898 /// - "fly_client_ip" - Fly.io
899 /// - "cloudfront_viewer_address" - AWS CloudFront
900 ///
901 /// On Unix-socket deployments, leave this unset rather than setting
902 /// "connect_info"; that source requires a TCP peer address.
903 ///
904 /// WARNING: A header-based value without a trusted reverse proxy in
905 /// front of tuwunel allows clients to forge their IP. Changing this
906 /// value requires a server restart.
907 ///
908 /// default: unset
909 /// config-example: "connect_info"
910 #[serde(default)]
911 pub ip_source: Option<IpSource>,
912
913 /// Subnets whose TCP peers are treated as trusted and bypass the
914 /// `ip_source`-based extraction, falling through to the same
915 /// insecure header-scan + `ConnectInfo` fallback used when
916 /// `ip_source` is unset. Each entry is CIDR notation, including
917 /// the prefix length (use `/32` or `/128` to trust a single host).
918 ///
919 /// Loopback (`127.0.0.0/8`, `::1/128`) is always bypassed and
920 /// need not be listed.
921 ///
922 /// Use this when locally attached bridges or other server-side
923 /// clients connect from a private container or VPN subnet that
924 /// cannot carry the configured proxy header (e.g. a user-defined
925 /// Docker bridge network without `network_mode: host`).
926 ///
927 /// NOTE: If you configure an entire subnet here, be sure that it
928 /// does not include the address Tuwunel receives external traffic
929 /// from, i.e. that of your proxy. This would, for example, happen
930 /// if you deployed the proxy in a common bridge network with your
931 /// other components (e.g. in a Compose deployment) and specified
932 /// said network's subnet here. Traffic from the proxy would then
933 /// also have the bypass applied, rendering the `ip_source` option
934 /// effectively useless.
935 ///
936 /// WARNING: Any peer in these subnets can forge the client IP via
937 /// request headers. Only include subnets you control end-to-end.
938 /// Changing this value requires a server restart.
939 ///
940 /// default: []
941 /// config-example: ["172.18.0.0/16", "fd00::/8"]
942 #[expect(
943 clippy::doc_link_with_quotes,
944 reason = "config-example directive emits literal quoted strings, not an intra-doc link"
945 )]
946 #[serde(default)]
947 pub ip_source_trusted_subnets: Vec<IpNet>,
948
949 /// Grace period for clean shutdown of federation requests (seconds).
950 ///
951 /// reloadable: yes
952 /// default: 5
953 #[serde(default = "default_sender_shutdown_timeout")]
954 pub sender_shutdown_timeout: u64,
955
956 /// Enables registration. If set to false, no users can register on this
957 /// server.
958 ///
959 /// If set to true without a token configured, users can register with no
960 /// form of 2nd-step only if you set the following option to true:
961 /// `yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse`
962 ///
963 /// If you would like registration only via token reg, please configure
964 /// `registration_token` or `registration_token_file`.
965 /// reloadable: yes
966 #[serde(default)]
967 pub allow_registration: bool,
968
969 /// Enabling this setting opens registration to anyone without restrictions.
970 /// This makes your server vulnerable to abuse
971 /// reloadable: yes
972 #[serde(default)]
973 pub yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse: bool,
974
975 /// A static registration token that new users will have to provide when
976 /// creating an account. If unset and `allow_registration` is true,
977 /// you must set
978 /// `yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse`
979 /// to true to allow open registration without any conditions.
980 ///
981 /// YOU NEED TO EDIT THIS OR USE registration_token_file.
982 ///
983 /// reloadable: yes
984 /// example: "o&^uCtes4HPf0Vu@F20jQeeWE7"
985 ///
986 /// display: sensitive
987 pub registration_token: Option<String>,
988
989 /// Path to a file on the system that gets read for additional registration
990 /// tokens. Multiple tokens can be added if you separate them with
991 /// whitespace
992 ///
993 /// tuwunel must be able to access the file, and it must not be empty
994 ///
995 /// reloadable: yes
996 /// example: "/etc/tuwunel/.reg_token"
997 pub registration_token_file: Option<PathBuf>,
998
999 /// A pre-shared secret enabling out-of-band account creation via the
1000 /// Synapse-style `/_synapse/admin/v1/register` endpoint. The endpoint is
1001 /// only available when this is set. Requests authenticate by HMAC-SHA1
1002 /// keyed on this value; UIAA is bypassed.
1003 ///
1004 /// Use a high-entropy value (at least 32 bytes) and treat it as a
1005 /// secret of equivalent power to a server admin's access token.
1006 ///
1007 /// reloadable: yes
1008 /// example: "kZ2hN5pQ8wXyL4mR7tBfCgJxV3aD6sE1u"
1009 ///
1010 /// display: sensitive
1011 pub registration_shared_secret: Option<String>,
1012
1013 /// Path to a file containing the registration shared secret. Takes
1014 /// precedence over `registration_shared_secret`, and falls back to it when
1015 /// the file cannot be opened. Surrounding whitespace is trimmed off, so a
1016 /// trailing newline does not become part of the secret. A file which is
1017 /// present but blank resolves to no secret rather than falling back.
1018 ///
1019 /// reloadable: yes
1020 /// example: "/etc/tuwunel/.reg_shared_secret"
1021 pub registration_shared_secret_file: Option<PathBuf>,
1022
1023 /// Shared secret the Matrix Authentication Service (MAS) authenticates its
1024 /// provisioning calls with. When set, the `/_synapse/mas/*` endpoints
1025 /// accept only requests bearing this exact secret as their bearer token,
1026 /// rejecting all others; when unset, those endpoints reject every request.
1027 ///
1028 /// Use a high-entropy value and keep it identical to the secret configured
1029 /// on the MAS side.
1030 ///
1031 /// reloadable: yes
1032 /// example: "kZ2hN5pQ8wXyL4mR7tBfCgJxV3aD6sE1u"
1033 ///
1034 /// display: sensitive
1035 pub mas_secret: Option<String>,
1036
1037 /// Controls whether encrypted rooms and events are allowed.
1038 /// reloadable: yes
1039 #[serde(default = "true_fn")]
1040 pub allow_encryption: bool,
1041
1042 /// Controls whether locally-created rooms should be end-to-end encrypted by
1043 /// default. This option is equivalent to the one found in Synapse.
1044 ///
1045 /// Options:
1046 /// - "all": All created rooms are encrypted.
1047 /// - "invite": Any room created with `private_chat` or
1048 /// `trusted_private_chat` presets.
1049 /// - "none": Explicit value for no effect.
1050 /// - Other values default to no effect.
1051 ///
1052 /// reloadable: yes
1053 /// default: "none"
1054 #[serde(default)]
1055 pub encryption_enabled_by_default_for_room_type: Option<String>,
1056
1057 /// Controls whether federation is allowed or not. It is not recommended to
1058 /// disable this after installation due to potential federation breakage but
1059 /// this is technically not a permanent setting.
1060 #[serde(default = "true_fn")]
1061 pub allow_federation: bool,
1062
1063 /// (EXPERIMENTAL) Resolve the base event of a room context request by
1064 /// fetching it from federation when the server never received it.
1065 ///
1066 /// When a client requests
1067 /// `/_matrix/client/v3/rooms/{roomId}/context/{eventId}` for an event
1068 /// the server does not hold locally, the server fetches it from a room
1069 /// peer and persists it before responding, rather than returning a
1070 /// 404. This is gated on `allow_federation`; with federation disabled
1071 /// it has no effect. Other on-demand federation fetch sites are gated
1072 /// separately.
1073 ///
1074 /// reloadable: yes
1075 /// default: false
1076 #[serde(default)]
1077 pub fetch_unreceived_contexts_over_federation: bool,
1078
1079 /// Per-round ceiling on how many servers a federation event fetch contacts
1080 /// concurrently. Tightens the built-in fan-out profile of every fetch kind;
1081 /// it never widens one. 0 leaves the profiles unchanged.
1082 ///
1083 /// reloadable: yes
1084 /// default: 0
1085 #[serde(default)]
1086 pub fetch_fanout_max_width: usize,
1087
1088 /// Ceiling on how many staged rounds a federation event fetch runs before
1089 /// giving up. Tightens the built-in round count of every fetch kind; it
1090 /// never raises one. 0 leaves the profiles unchanged.
1091 ///
1092 /// reloadable: yes
1093 /// default: 0
1094 #[serde(default)]
1095 pub fetch_fanout_rounds: usize,
1096
1097 /// Derive the state at an incoming federation event from locally held
1098 /// events when its previous events are stored but not yet resolved,
1099 /// instead of requesting /state_ids from the origin server. Only an event
1100 /// whose entire unresolved local ancestry is present participates; any
1101 /// other case still falls back to the federation state fetch. Disabling
1102 /// this restores the previous behavior of always fetching.
1103 ///
1104 /// reloadable: yes
1105 #[serde(default = "true_fn")]
1106 pub resolve_state_locally: bool,
1107
1108 /// Ceiling on how many unresolved local events one local state derivation
1109 /// may visit before falling back to the federation state fetch. Bounds
1110 /// worst-case memory and latency in rooms with a large unresolved
1111 /// backlog. 0 disables local derivation entirely.
1112 ///
1113 /// reloadable: yes
1114 /// default: 256
1115 #[serde(default = "default_resolve_state_locally_max")]
1116 pub resolve_state_locally_max: usize,
1117
1118 /// Validation mode for local state derivation: compute the local result,
1119 /// then fetch /state_ids anyway, compare the two, and log any divergence
1120 /// while the fetched state remains authoritative. Federation load is
1121 /// unchanged. For operators soaking resolve_state_locally before trusting
1122 /// it. No effect unless resolve_state_locally is enabled.
1123 ///
1124 /// reloadable: yes
1125 #[serde(default = "true_fn")]
1126 pub resolve_state_locally_shadow: bool,
1127
1128 /// Soft cap on the number of forward extremities tracked per room. When
1129 /// applying an incoming federation event would leave the room's frontier
1130 /// larger than this, the least useful leaves are pruned from the tracked
1131 /// set until it is back at the cap. Pruned events are not deleted and can
1132 /// still be referenced by other servers; this server merely stops citing
1133 /// them as frontier tips. Events created by this server are never pruned.
1134 /// 0 disables automatic pruning.
1135 ///
1136 /// reloadable: yes
1137 /// default: 60
1138 #[serde(default = "default_forward_extremities_max")]
1139 pub forward_extremities_max: usize,
1140
1141 /// Emergency bound on the per-room frontier. A frontier larger than this
1142 /// is cut down to it in a single step, ignoring the per-event pruning
1143 /// batch limit. Values at or below forward_extremities_max remove the
1144 /// pacing entirely, pruning straight to the cap in one step.
1145 ///
1146 /// reloadable: yes
1147 /// default: 256
1148 #[serde(default = "default_forward_extremities_emergency_max")]
1149 pub forward_extremities_emergency_max: usize,
1150
1151 /// Upper bound on how many forward extremities one incoming event may
1152 /// prune while the frontier is between the cap and the emergency bound.
1153 /// Spreads convergence across events to bound the work done by any single
1154 /// one. 0 stops paced pruning, leaving only the emergency bound.
1155 ///
1156 /// reloadable: yes
1157 /// default: 32
1158 #[serde(default = "default_forward_extremities_prune_batch")]
1159 pub forward_extremities_prune_batch: usize,
1160
1161 /// Sets the default `m.federate` property for newly created rooms when the
1162 /// client does not request one. If `allow_federation` is set to false at
1163 /// the same this value is set to false it then always overrides the client
1164 /// requested `m.federate` value to false.
1165 ///
1166 /// Rooms are fixed to the setting at the time of their creation and can
1167 /// never be changed; changing this value only affects new rooms.
1168 /// reloadable: yes
1169 #[serde(default = "true_fn")]
1170 pub federate_created_rooms: bool,
1171
1172 /// Allows federation requests to be made to itself
1173 ///
1174 /// This isn't intended and is very likely a bug if federation requests are
1175 /// being sent to yourself. This currently mainly exists for development
1176 /// purposes.
1177 /// reloadable: yes
1178 #[serde(default)]
1179 pub federation_loopback: bool,
1180
1181 /// Always calls /forget on behalf of the user if leaving a room. This is a
1182 /// part of MSC4267 "Automatically forgetting rooms on leave"
1183 /// reloadable: yes
1184 #[serde(default)]
1185 pub forget_forced_upon_leave: bool,
1186
1187 /// Set this to true to require authentication on the normally
1188 /// unauthenticated profile retrieval endpoints (GET)
1189 /// "/_matrix/client/v3/profile/{userId}".
1190 ///
1191 /// This can prevent profile scraping.
1192 /// reloadable: yes
1193 #[serde(default)]
1194 pub require_auth_for_profile_requests: bool,
1195
1196 /// Preserve per-room profile overrides during a global profile update.
1197 ///
1198 /// When `true` (default), a profile change (displayname or avatar_url)
1199 /// arriving via the profile endpoints skips rooms whose current
1200 /// `m.room.member` already differs from the user's prior global
1201 /// profile. This is the natural behavior users expect after setting a
1202 /// per-room nickname or avatar with a client's `/myroomnick`-style
1203 /// command: a subsequent global change does not clobber the override.
1204 ///
1205 /// Set to `false` to always rewrite every joined room's member event
1206 /// to match the new global profile. That matches the literal spec
1207 /// reading.
1208 ///
1209 /// MSC4466 lets clients pick this per request via the
1210 /// `org.matrix.msc4466.propagate_to` query parameter
1211 /// (`all` / `unchanged` / `none`); an explicit value overrides this
1212 /// default in either direction.
1213 ///
1214 /// reloadable: yes
1215 /// default: true
1216 #[serde(default = "true_fn")]
1217 pub preserve_room_profile_overrides: bool,
1218
1219 /// Set this to true to allow your server's public room directory to be
1220 /// federated. Set this to false to protect against /publicRooms spiders,
1221 /// but will forbid external users from viewing your server's public room
1222 /// directory. If federation is disabled entirely (`allow_federation`), this
1223 /// is inherently false.
1224 /// reloadable: yes
1225 #[serde(default)]
1226 pub allow_public_room_directory_over_federation: bool,
1227
1228 /// Set this to true to allow your server's public room directory to be
1229 /// queried without client authentication (access token) through the Client
1230 /// APIs. Set this to false to protect against /publicRooms spiders.
1231 /// reloadable: yes
1232 #[serde(default)]
1233 pub allow_public_room_directory_without_auth: bool,
1234
1235 /// Allows room directory searches to match on partial room_id's when the
1236 /// search term starts with '!'.
1237 ///
1238 /// reloadable: yes
1239 /// default: true
1240 #[serde(default = "true_fn")]
1241 pub allow_public_room_search_by_id: bool,
1242
1243 /// Set this to false to limit results of rooms when searching by ID to
1244 /// those that would be found by an alias or other query; specifically
1245 /// those listed in the public rooms directory. By default this is set to
1246 /// true allowing any joinable room to match. This satisfies the Principle
1247 /// of Least Expectation when pasting a room_id into a search box with
1248 /// intent to join; many rooms simply opt-out of public listings. Therefor
1249 /// to prevent this feature from abuse, knowledge of several characters of
1250 /// the room_id is required before any results are returned.
1251 ///
1252 /// reloadable: yes
1253 /// default: true
1254 #[serde(default = "true_fn")]
1255 pub allow_unlisted_room_search_by_id: bool,
1256
1257 /// Show all local users in user directory. With this set to false, only
1258 /// users in public rooms or those that share a room with the user making
1259 /// the search will be shown.
1260 ///
1261 /// reloadable: yes
1262 /// default: false
1263 #[serde(default)]
1264 pub show_all_local_users_in_user_directory: bool,
1265
1266 /// Allow guest users to access TURN credentials.
1267 ///
1268 /// This is the equivalent of Synapse's `turn_allow_guests` config option.
1269 /// Setting this to true allows guest users to call the endpoint
1270 /// `/_matrix/client/v3/voip/turnServer`.
1271 /// reloadable: yes
1272 #[serde(default)]
1273 pub turn_allow_guests: bool,
1274
1275 /// Set this to true to lock down your server's public room directory and
1276 /// only allow admins to publish rooms to the room directory. Unpublishing
1277 /// is still allowed by all users with this enabled.
1278 /// reloadable: yes
1279 #[serde(default)]
1280 pub lockdown_public_room_directory: bool,
1281
1282 /// Set this to true to allow federating device display names / allow
1283 /// external users to see your device display name. If federation is
1284 /// disabled entirely (`allow_federation`), this is inherently false. For
1285 /// privacy reasons, this is best left disabled.
1286 /// reloadable: yes
1287 #[serde(default)]
1288 pub allow_device_name_federation: bool,
1289
1290 /// Config option to allow or disallow incoming federation requests that
1291 /// obtain the profiles of our local users from
1292 /// `/_matrix/federation/v1/query/profile`
1293 ///
1294 /// Increases privacy of your local user's such as display names, but some
1295 /// remote users may get a false "this user does not exist" error when they
1296 /// try to invite you to a DM or room. Also can protect against profile
1297 /// spiders.
1298 ///
1299 /// This is inherently false if `allow_federation` is disabled
1300 /// reloadable: yes
1301 #[serde(
1302 default = "true_fn",
1303 alias = "allow_profile_lookup_federation_requests"
1304 )]
1305 pub allow_inbound_profile_lookup_federation_requests: bool,
1306
1307 /// Allow standard users to create rooms. Appservices and admins are always
1308 /// allowed to create rooms
1309 /// reloadable: yes
1310 #[serde(default = "true_fn")]
1311 pub allow_room_creation: bool,
1312
1313 /// Set to false to disable users from joining or creating room versions
1314 /// that aren't officially supported by tuwunel. Unstable room versions may
1315 /// have flawed specifications or our implementation may be non-conforming.
1316 /// Correct operation may not be guaranteed, but incorrect operation may be
1317 /// tolerable and unnoticed.
1318 ///
1319 /// tuwunel officially supports room versions 6+. tuwunel has slightly
1320 /// experimental (though works fine in practice) support for versions 3 - 5.
1321 ///
1322 /// reloadable: yes
1323 /// default: true
1324 #[serde(default = "true_fn")]
1325 pub allow_unstable_room_versions: bool,
1326
1327 /// Set to true to enable experimental room versions.
1328 ///
1329 /// Unlike unstable room versions these versions are either under
1330 /// development, protype spec-changes, or somehow present a serious risk to
1331 /// the server's operation or database corruption. This is for developer use
1332 /// only.
1333 /// reloadable: yes
1334 #[serde(default)]
1335 pub allow_experimental_room_versions: bool,
1336
1337 /// MSC4284: ask the room's policy server to sign outgoing events. When a
1338 /// room has a valid `m.room.policy` state event, the homeserver requests a
1339 /// signature from that policy server's federation `/sign` endpoint before
1340 /// federating each event. Refusal aborts the local request; network or
1341 /// timeout failures fail open with a warn log so a transient policy-server
1342 /// outage does not silently take the room offline.
1343 ///
1344 /// reloadable: yes
1345 /// default: false
1346 #[serde(default)]
1347 pub enable_policy_servers: bool,
1348
1349 /// MSC4284: timeout (seconds) for requests to a room's policy server.
1350 /// Applies to both outbound `/sign` calls and inbound signature-fetches.
1351 ///
1352 /// reloadable: yes
1353 /// default: 5
1354 #[serde(default = "default_policy_server_request_timeout")]
1355 pub policy_server_request_timeout: u64,
1356
1357 /// MSC3925: fold the most recent message edit (an `m.replace` relation)
1358 /// into `unsigned.m.relations` on a served event as the full replacement
1359 /// event, on the client read endpoints. Off by default: it adds a typed
1360 /// index seek per served event and a server-authoritative edit summary that
1361 /// most clients reconstruct locally anyway, so it is opt-in.
1362 ///
1363 /// reloadable: yes
1364 /// default: false
1365 #[serde(default)]
1366 pub bundle_edit_relations: bool,
1367
1368 /// MSC2675/MSC3267: fold reference relations (`m.reference`) into
1369 /// `unsigned.m.relations` on a served event as `{ chunk: [{ event_id },
1370 /// ...] }`, on the client read endpoints. Off by default: no surveyed
1371 /// client renders reference bundles (references are plumbing for polls,
1372 /// beacons, and verification, which clients resolve directly), so most
1373 /// deployments gain nothing from the added read-time cost.
1374 ///
1375 /// reloadable: yes
1376 /// default: false
1377 #[serde(default)]
1378 pub bundle_reference_relations: bool,
1379
1380 /// Default room version tuwunel will create rooms with.
1381 ///
1382 /// The default is prescribed by the spec, but may be selected by developer
1383 /// recommendation. To prevent stale documentation we no longer list it
1384 /// here. It is only advised to override this if you know what you are
1385 /// doing, and by doing so, updates with new versions are precluded.
1386 /// reloadable: yes
1387 #[serde(default = "default_default_room_version")]
1388 pub default_room_version: RoomVersionId,
1389
1390 /// Default power-level overrides applied when this homeserver creates a new
1391 /// room.
1392 ///
1393 /// Uses the same top-level shape as the client `/createRoom`
1394 /// `power_level_content_override` parameter and is merged before any
1395 /// per-request override, so a client can still override it per room. Only
1396 /// affects newly created rooms. Top-level keys replace wholesale rather
1397 /// than deep-merging (matching the client parameter): setting `users` or
1398 /// `events` replaces the entire computed default submap for that key.
1399 ///
1400 /// reloadable: yes
1401 /// default: unset
1402 /// config-example: { users_default = 50 }
1403 #[serde(default)]
1404 pub default_power_level_content_override: Option<serde_json::Value>,
1405
1406 /// Configures Matrix discovery documents and related endpoints.
1407 ///
1408 /// Values are read from the separate `[global.well_known]` section. Client,
1409 /// server, support, and MatrixRTC responses consume these settings.
1410 // external structure; separate section
1411 #[serde(default)]
1412 pub well_known: WellKnownConfig,
1413
1414 /// Enables OTLP span export for Jaeger-compatible tracing.
1415 ///
1416 /// A build with performance measurements installs an OpenTelemetry layer
1417 /// when this is enabled. It defaults to false, and `jaeger_filter` selects
1418 /// the exported spans.
1419 #[serde(default)]
1420 pub allow_jaeger: bool,
1421
1422 /// default: "info"
1423 #[serde(default = "default_jaeger_filter")]
1424 pub jaeger_filter: String,
1425
1426 /// If the 'perf_measurements' compile-time feature is enabled, enables
1427 /// collecting folded stack trace profile of tracing spans using
1428 /// tracing_flame. The resulting profile can be visualized with inferno[1],
1429 /// speedscope[2], or a number of other tools.
1430 ///
1431 /// [1]: https://github.com/jonhoo/inferno
1432 /// [2]: www.speedscope.app
1433 #[serde(default)]
1434 pub tracing_flame: bool,
1435
1436 /// default: "info"
1437 #[serde(default = "default_tracing_flame_filter")]
1438 pub tracing_flame_filter: String,
1439
1440 /// default: "./tracing.folded"
1441 #[serde(default = "default_tracing_flame_output_path")]
1442 pub tracing_flame_output_path: String,
1443
1444 #[cfg(not(doctest))]
1445 /// Examples:
1446 ///
1447 /// - No proxy (default):
1448 ///
1449 /// proxy = "none"
1450 ///
1451 /// - For global proxy, create the section at the bottom of this file:
1452 ///
1453 /// [global.proxy]
1454 /// global = { url = "socks5h://localhost:9050" }
1455 ///
1456 /// - To proxy some domains:
1457 ///
1458 /// [global.proxy]
1459 /// [[global.proxy.by_domain]]
1460 /// url = "socks5h://localhost:9050"
1461 /// include = ["*.onion", "matrix.myspecial.onion"]
1462 /// exclude = ["*.myspecial.onion"]
1463 ///
1464 /// Include vs. Exclude:
1465 ///
1466 /// - If include is an empty list, it is assumed to be `["*"]`.
1467 ///
1468 /// - If a domain matches both the exclude and include list, the proxy will
1469 /// only be used if it was included because of a more specific rule than
1470 /// it was excluded. In the above example, the proxy would be used for
1471 /// `ordinary.onion`, `matrix.myspecial.onion`, but not
1472 /// `hello.myspecial.onion`.
1473 ///
1474 /// default: "none"
1475 #[serde(default)]
1476 pub proxy: ProxyConfig,
1477
1478 #[expect(clippy::doc_link_with_quotes)]
1479 /// Servers listed here will be used to gather public keys of other servers
1480 /// (notary trusted key servers).
1481 ///
1482 /// Currently, tuwunel doesn't support inbound batched key requests, so
1483 /// this list should only contain other Synapse servers.
1484 ///
1485 /// reloadable: yes
1486 /// example: ["matrix.org", "tchncs.de"]
1487 ///
1488 /// default: ["matrix.org"]
1489 #[serde(default = "default_trusted_servers")]
1490 pub trusted_servers: Vec<OwnedServerName>,
1491
1492 /// Whether to query the servers listed in trusted_servers first or query
1493 /// the origin server first. For best security, querying the origin server
1494 /// first is advised to minimize the exposure to a compromised trusted
1495 /// server. For maximum federation/join performance this can be set to true,
1496 /// however other options exist to query trusted servers first under
1497 /// specific high-load circumstances and should be evaluated before setting
1498 /// this to true.
1499 /// reloadable: yes
1500 #[serde(default)]
1501 pub query_trusted_key_servers_first: bool,
1502
1503 /// Whether to query the servers listed in trusted_servers first
1504 /// specifically on room joins. This option limits the exposure to a
1505 /// compromised trusted server to room joins only. The join operation
1506 /// requires gathering keys from many origin servers which can cause
1507 /// significant delays. Therefor this defaults to true to mitigate
1508 /// unexpected delays out-of-the-box. The security-paranoid or those willing
1509 /// to tolerate delays are advised to set this to false. Note that setting
1510 /// query_trusted_key_servers_first to true causes this option to be
1511 /// ignored.
1512 /// reloadable: yes
1513 #[serde(default = "true_fn")]
1514 pub query_trusted_key_servers_first_on_join: bool,
1515
1516 /// Only query trusted servers for keys and never the origin server. This is
1517 /// intended for clusters or custom deployments using their trusted_servers
1518 /// as forwarding-agents to cache and deduplicate requests. Notary servers
1519 /// do not act as forwarding-agents by default, therefor do not enable this
1520 /// unless you know exactly what you are doing.
1521 /// reloadable: yes
1522 #[serde(default)]
1523 pub only_query_trusted_key_servers: bool,
1524
1525 /// Maximum number of keys to request in each trusted server batch query.
1526 ///
1527 /// reloadable: yes
1528 /// default: 192
1529 #[serde(default = "default_trusted_server_batch_size")]
1530 pub trusted_server_batch_size: usize,
1531
1532 /// Maximum number of request batches in flight simultaneously when querying
1533 /// a trusted server.
1534 ///
1535 /// reloadable: yes
1536 /// default: 2
1537 #[serde(default = "default_trusted_server_batch_concurrency")]
1538 pub trusted_server_batch_concurrency: usize,
1539
1540 /// Max log level for tuwunel. Allows debug, info, warn, or error.
1541 ///
1542 /// See also:
1543 /// https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives
1544 ///
1545 /// **Caveat**:
1546 /// For release builds, the tracing crate is configured to only implement
1547 /// levels higher than error to avoid unnecessary overhead in the compiled
1548 /// binary from trace macros. For debug builds, this restriction is not
1549 /// applied.
1550 ///
1551 /// default: "info"
1552 #[serde(default = "default_log")]
1553 pub log: String,
1554
1555 /// Output logs with ANSI colours.
1556 ///
1557 /// Colours are suppressed while entries are submitted to journald, which
1558 /// takes the formatted line verbatim and reads control bytes in it as
1559 /// binary rather than text.
1560 #[serde(default = "true_fn", alias = "log_colours")]
1561 pub log_colors: bool,
1562
1563 /// Sets the log format to compact mode.
1564 #[serde(default)]
1565 pub log_compact: bool,
1566
1567 /// Configures the span events which will be outputted with the log.
1568 ///
1569 /// default: "none"
1570 #[serde(default = "default_log_span_events")]
1571 pub log_span_events: String,
1572
1573 /// Configures whether TUWUNEL_LOG EnvFilter matches values using regular
1574 /// expressions. See the tracing_subscriber documentation on Directives.
1575 ///
1576 /// default: true
1577 #[serde(default = "true_fn")]
1578 pub log_filter_regex: bool,
1579
1580 /// Toggles the display of ThreadId in tracing log output.
1581 ///
1582 /// default: false
1583 #[serde(default)]
1584 pub log_thread_ids: bool,
1585
1586 /// Redirects logging to standard error (stderr). The default is false for
1587 /// stdout. For those using our systemd features the redirection to stderr
1588 /// occurs as necessary and setting this option should not be required. We
1589 /// offer this option for all other users who desire such redirection.
1590 ///
1591 /// default: false
1592 #[serde(default)]
1593 pub log_to_stderr: bool,
1594
1595 /// Submits log output directly to the journald socket instead of the
1596 /// console when running under systemd. Each entry carries its actual
1597 /// severity as the journal priority, so tools such as `journalctl
1598 /// --priority warning` catch Tuwunel's warnings and errors; console output
1599 /// is captured by journald at a single fixed priority instead. The message
1600 /// is formatted exactly as the console formats it, span fields included,
1601 /// while the target, source location and every tracing field are attached
1602 /// as journal fields, the latter under an `F_` prefix for queries such as
1603 /// `journalctl F_ROOM_ID='!room:example.com'`. This option has no effect
1604 /// when not running under systemd, and the console is kept when the
1605 /// journald socket cannot be opened.
1606 ///
1607 /// default: true
1608 #[serde(default = "true_fn")]
1609 pub log_journald: bool,
1610
1611 /// Setting to false disables the logging/tracing system at a lower level.
1612 /// In contrast to configuring an empty `log` string where the system is
1613 /// still operating but muted, when this option is false the system was not
1614 /// initialized and is not operating. Changing this option has no effect
1615 /// after startup. This option is intended for developers and expert use
1616 /// only: configuring an empty log string is preferred over using this.
1617 ///
1618 /// default: true
1619 #[serde(default = "true_fn")]
1620 pub log_enable: bool,
1621
1622 /// Setting to false disables the logging/tracing system at a lower level
1623 /// similar to `log_enable`. In this case the system is configured normally,
1624 /// but not registered as the global handler in the final steps. This option
1625 /// is for developers and expert use only.
1626 ///
1627 /// default: true
1628 #[serde(default = "true_fn")]
1629 pub log_global_default: bool,
1630
1631 /// OpenID token expiration/TTL in seconds.
1632 ///
1633 /// These are the OpenID tokens that are primarily used for Matrix account
1634 /// integrations (e.g. Vector Integrations in Element), *not* OIDC/OpenID
1635 /// Connect/etc.
1636 ///
1637 /// reloadable: yes
1638 /// default: 3600
1639 #[serde(default = "default_openid_token_ttl")]
1640 pub openid_token_ttl: u64,
1641
1642 /// Allow an existing session to mint a login token for another client.
1643 /// This requires interactive authentication, but has security ramifications
1644 /// as a malicious client could use the mechanism to spawn more than one
1645 /// session. Enabled by default.
1646 ///
1647 /// reloadable: yes
1648 /// default: true
1649 #[serde(default = "true_fn")]
1650 pub login_via_existing_session: bool,
1651
1652 /// Whether to enable the login token route to accept login tokens at all.
1653 /// Login tokens may be generated by the server for authorization flows such
1654 /// as SSO; disabling tokens may break such features.
1655 ///
1656 /// This option is distinct from `login_via_existing_session` and does not
1657 /// carry the same security implications; the intent is to leave this
1658 /// enabled while disabling the former to prevent clients from commanding
1659 /// login token creation but without preventing the server from doing so.
1660 ///
1661 /// reloadable: yes
1662 /// default: true
1663 #[serde(default = "true_fn")]
1664 pub login_via_token: bool,
1665
1666 /// Whether to enable login using traditional user/password authorization
1667 /// flow.
1668 ///
1669 /// Set this option to false if you intend to allow logging in only using
1670 /// other mechanisms, such as SSO.
1671 ///
1672 /// reloadable: yes
1673 /// default: true
1674 #[serde(default = "true_fn")]
1675 pub login_with_password: bool,
1676
1677 /// Login token expiration/TTL in milliseconds.
1678 ///
1679 /// These are short-lived tokens for the m.login.token endpoint.
1680 /// This is used to allow existing sessions to create new sessions.
1681 /// see login_via_existing_session.
1682 ///
1683 /// reloadable: yes
1684 /// default: 120000
1685 #[serde(default = "default_login_token_ttl")]
1686 pub login_token_ttl: u64,
1687
1688 /// Access token TTL in seconds.
1689 ///
1690 /// For clients that support refresh-tokens, the access-token provided on
1691 /// login will be invalidated after this amount of time and the client will
1692 /// be soft-logged-out until refreshing it.
1693 ///
1694 /// reloadable: yes
1695 /// default: 604800
1696 #[serde(default = "default_access_token_ttl")]
1697 pub access_token_ttl: u64,
1698
1699 /// Refresh token TTL in seconds.
1700 ///
1701 /// Refresh tokens are rejected once this lifetime elapses. Whether the
1702 /// deadline slides forward on each use or stays fixed at issuance is
1703 /// controlled by `refresh_token_idle_only`. The default of `0` disables
1704 /// refresh-token expiry entirely; a typical enabled value is `259200`
1705 /// (three days).
1706 ///
1707 /// reloadable: yes
1708 /// default: 0
1709 #[serde(default)]
1710 pub refresh_token_ttl: u64,
1711
1712 /// Whether `refresh_token_ttl` acts as an idle timeout or an absolute
1713 /// session lifetime.
1714 ///
1715 /// When `true` (default), each successful refresh resets the deadline to
1716 /// `now + refresh_token_ttl`. A session in continuous use never expires.
1717 /// When `false`, the deadline is fixed at first issuance and rotation
1718 /// carries it forward, forcing re-auth after `refresh_token_ttl`
1719 /// regardless of activity.
1720 ///
1721 /// reloadable: yes
1722 /// default: true
1723 #[serde(default = "true_fn")]
1724 pub refresh_token_idle_only: bool,
1725
1726 /// Whether refresh-token expiry triggers a hard logout instead of a soft
1727 /// one.
1728 ///
1729 /// When `false` (default), an expired refresh token is rejected with
1730 /// `M_UNKNOWN_TOKEN` carrying `soft_logout: true`. The client can preserve
1731 /// E2EE keys and local state, then re-authenticate to resume the same
1732 /// device.
1733 ///
1734 /// When `true`, the device is removed entirely on expiry: the access
1735 /// token is invalidated, the device record is deleted, and the client is
1736 /// signalled with `soft_logout: false`. The next session is a brand-new
1737 /// device, so the client cannot recover E2EE history from local state
1738 /// alone; this is the CWE-613 stance and trades usability for that
1739 /// guarantee.
1740 ///
1741 /// reloadable: yes
1742 /// default: false
1743 #[serde(default)]
1744 pub refresh_token_hard_logout: bool,
1745
1746 /// Grace window in seconds for a benign refresh-token double-submit.
1747 ///
1748 /// After a refresh token rotates, the spent token is retained for one
1749 /// generation so a later reuse is detectable. If that spent token is
1750 /// presented again within this window while its successor is still the
1751 /// device's current refresh token, the request is treated as a client that
1752 /// lost the rotated response: a fresh access token is issued for the
1753 /// unchanged refresh token rather than revoking the device. Outside the
1754 /// window, or once the chain has advanced, a replayed refresh token revokes
1755 /// the device as a suspected compromise. Set to `0` to treat every reuse as
1756 /// a compromise.
1757 ///
1758 /// reloadable: yes
1759 /// default: 15
1760 #[serde(default = "default_refresh_token_reuse_grace")]
1761 pub refresh_token_reuse_grace: u64,
1762
1763 /// Whether a detected refresh-token reuse revokes the device.
1764 ///
1765 /// When true (default), presenting a refresh token that was already rotated
1766 /// (outside the `refresh_token_reuse_grace` window) removes the device, the
1767 /// RFC 6819 stance that treats reuse as a compromised session. When false,
1768 /// the replayed request is rejected but the device is left intact, the
1769 /// laxer behaviour an operator fronting another OAuth client may prefer.
1770 ///
1771 /// reloadable: yes
1772 /// default: true
1773 #[serde(default = "true_fn")]
1774 pub refresh_token_reuse_revoke: bool,
1775
1776 /// Enable native registration and login on the built-in OIDC provider
1777 /// (next-gen auth), authenticating Matrix clients against this server's own
1778 /// accounts without a third-party `identity_provider`.
1779 ///
1780 /// When false (default), the OIDC server runs only to broker for a
1781 /// configured `identity_provider`, redirecting users to that upstream IdP.
1782 /// When true, an authorization request that selects no provider is served a
1783 /// native login or registration page checked against local accounts;
1784 /// `well_known.client` must be set. Native and external providers coexist;
1785 /// a configured `identity_provider` still brokers as before. Registration
1786 /// here honors `allow_registration`, the registration token, and
1787 /// `registration_terms` exactly as the client registration endpoint does.
1788 ///
1789 /// reloadable: yes
1790 /// default: false
1791 #[serde(default)]
1792 pub oidc_native_auth: bool,
1793
1794 /// Require OIDC clients (next-gen auth) to request an MSC2967 device scope.
1795 ///
1796 /// When false, a client that omits the `urn:matrix:client:device:<id>`
1797 /// scope is assigned a server-generated device id, which is echoed back in
1798 /// the granted scope. When true, the authorization-code grant is rejected
1799 /// unless the client supplies a device scope, per the MSC2967 expectation
1800 /// that the client owns its device id.
1801 ///
1802 /// reloadable: yes
1803 /// default: false
1804 #[serde(default)]
1805 pub oidc_require_device_scope: bool,
1806
1807 /// Require PKCE (RFC 7636) with the S256 method on the OIDC
1808 /// authorization-code grant.
1809 ///
1810 /// When true, the authorize endpoint rejects a request that carries no
1811 /// `code_challenge`, as MSC2964 mandates for public clients. A present
1812 /// challenge must always use S256; the `plain` method is rejected
1813 /// regardless of this setting. Set to false only as a transition escape
1814 /// hatch for a legacy client that cannot send a challenge.
1815 ///
1816 /// reloadable: yes
1817 /// default: true
1818 #[serde(default = "true_fn")]
1819 pub oidc_require_pkce: bool,
1820
1821 /// Reject an OIDC authorization-code grant that requests a scope this
1822 /// server does not recognise, instead of narrowing the granted scope down
1823 /// to the recognised tokens.
1824 ///
1825 /// When false (default), an unrecognised scope token is dropped and the
1826 /// narrowed `scope` is echoed back to the client per RFC 6749. When true,
1827 /// an unrecognised scope is rejected. `openid` and the MSC2967 device and
1828 /// api scopes (both spellings) are always recognised.
1829 ///
1830 /// reloadable: yes
1831 /// default: false
1832 #[serde(default)]
1833 pub oidc_strict_scope: bool,
1834
1835 /// Initial access token required to register an OIDC client dynamically
1836 /// (RFC 7591).
1837 ///
1838 /// When set, the registration endpoint requires the caller to present this
1839 /// token as an `Authorization: Bearer` credential. The default (empty)
1840 /// leaves dynamic client registration open.
1841 ///
1842 /// reloadable: yes
1843 /// default:
1844 #[serde(default)]
1845 pub oidc_registration_access_token: String,
1846
1847 /// Allowlist of hostnames permitted in a dynamically-registered OIDC
1848 /// client's redirect_uris.
1849 ///
1850 /// When non-empty, every redirect_uri presented at registration must have a
1851 /// host in this list or the registration is rejected. The default (empty)
1852 /// imposes no host restriction.
1853 ///
1854 /// reloadable: yes
1855 /// default: []
1856 #[serde(default)]
1857 pub oidc_registration_allowed_redirect_hosts: Vec<String>,
1858
1859 /// Require a `client_uri` in dynamic client registration requests
1860 /// (RFC 7591 / MSC2966).
1861 ///
1862 /// When false (default), `client_uri` is optional; a client that supplies
1863 /// one still has it validated (https, host, no userinfo) and the other URLs
1864 /// in the request must share its host or a subdomain. When true, a
1865 /// registration without an https `client_uri` is rejected with
1866 /// `invalid_client_metadata`, enforcing the MSC2966 common-base model on
1867 /// every client.
1868 ///
1869 /// reloadable: yes
1870 /// default: false
1871 #[serde(default)]
1872 pub oidc_registration_require_client_uri: bool,
1873
1874 /// Token-bucket refill rate (requests per second) for the OIDC endpoints.
1875 ///
1876 /// Applies a shared per-client-IP throttle across the authorize, token,
1877 /// dynamic-registration and device-grant endpoints. The default of `0`
1878 /// disables the throttle, preserving open
1879 /// access; raise it together with `oidc_rc_burst_count` to protect a server
1880 /// exposed to a hostile network. The key is the client IP, so a rate low
1881 /// enough to bite a brute-force attempt can also throttle many users behind
1882 /// one NAT; size the burst accordingly.
1883 ///
1884 /// reloadable: yes
1885 /// default: 0
1886 #[serde(default)]
1887 pub oidc_rc_per_second: u32,
1888
1889 /// Token-bucket depth (burst size) for the OIDC endpoint throttle.
1890 ///
1891 /// The number of requests a single client IP may make in a burst before the
1892 /// `oidc_rc_per_second` refill rate governs. Ignored while
1893 /// `oidc_rc_per_second` is `0`.
1894 ///
1895 /// reloadable: yes
1896 /// default: 0
1897 #[serde(default)]
1898 pub oidc_rc_burst_count: u32,
1899
1900 /// Enable the rendezvous session APIs used to sign in with a QR code
1901 /// (MSC4108 and MSC4388).
1902 ///
1903 /// The rendezvous session relays the handshake between two devices before
1904 /// the OAuth device authorization grant completes the sign-in. This
1905 /// requires the built-in OIDC server. When disabled, clients hide the
1906 /// feature and the endpoints return an unrecognized response.
1907 ///
1908 /// reloadable: yes
1909 /// default: true
1910 #[serde(default = "true_fn")]
1911 pub rendezvous_enabled: bool,
1912
1913 /// Maximum size in bytes of a rendezvous session payload.
1914 ///
1915 /// QR sign-in handshake messages are normally much smaller than the
1916 /// default.
1917 ///
1918 /// reloadable: yes
1919 /// default: 4096
1920 #[serde(default = "default_rendezvous_session_max_bytes")]
1921 pub rendezvous_session_max_bytes: usize,
1922
1923 /// Seconds a rendezvous session lives after its last write.
1924 ///
1925 /// Each update restarts the window, but the device displaying the QR
1926 /// times the whole sign-in against the expiry advertised at creation.
1927 /// The default leaves time for an interactive account login on the
1928 /// approval page.
1929 ///
1930 /// reloadable: yes
1931 /// default: 600
1932 #[serde(default = "default_rendezvous_session_ttl")]
1933 pub rendezvous_session_ttl: u64,
1934
1935 /// Maximum number of concurrent rendezvous sessions.
1936 ///
1937 /// Creating a session beyond this limit evicts the oldest session instead
1938 /// of failing. A value of zero retains one session so creation remains
1939 /// available.
1940 ///
1941 /// reloadable: yes
1942 /// default: 100
1943 #[serde(default = "default_rendezvous_max_sessions")]
1944 pub rendezvous_max_sessions: usize,
1945
1946 /// Require an access token for MSC4388 discovery and session creation.
1947 ///
1948 /// When disabled, clients without an access token may discover and create
1949 /// MSC4388 sessions. The MSC4108 endpoint remains open in either mode.
1950 ///
1951 /// reloadable: yes
1952 /// default: true
1953 #[serde(default = "true_fn")]
1954 pub rendezvous_authenticated_only: bool,
1955
1956 /// Per-client-IP request refill rate for the MSC4388 rendezvous endpoints.
1957 ///
1958 /// A value of zero is treated as one request per second.
1959 ///
1960 /// reloadable: yes
1961 /// default: 10
1962 #[serde(default = "default_rendezvous_rc_per_second")]
1963 pub rendezvous_rc_per_second: u32,
1964
1965 /// Token-bucket depth for the MSC4388 rendezvous request throttle.
1966 ///
1967 /// This is the number of requests one client IP may make in a burst before
1968 /// `rendezvous_rc_per_second` governs. A value of zero is treated as one.
1969 ///
1970 /// reloadable: yes
1971 /// default: 20
1972 #[serde(default = "default_rendezvous_rc_burst_count")]
1973 pub rendezvous_rc_burst_count: u32,
1974
1975 /// Static TURN username to provide the client if not using a shared secret
1976 /// ("turn_secret"), It is recommended to use a shared secret over static
1977 /// credentials.
1978 /// reloadable: yes
1979 #[serde(default)]
1980 pub turn_username: String,
1981
1982 /// Static TURN password to provide the client if not using a shared secret
1983 /// ("turn_secret"). It is recommended to use a shared secret over static
1984 /// credentials.
1985 ///
1986 /// display: sensitive
1987 /// reloadable: yes
1988 #[serde(default)]
1989 pub turn_password: String,
1990
1991 #[expect(clippy::doc_link_with_quotes)]
1992 /// Vector list of TURN URIs/servers to use.
1993 ///
1994 /// Replace "example.turn.uri" with your TURN domain, such as the coturn
1995 /// "realm" config option. If using TURN over TLS, replace the URI prefix
1996 /// "turn:" with "turns:".
1997 ///
1998 /// reloadable: yes
1999 /// example: ["turn:example.turn.uri?transport=udp",
2000 /// "turn:example.turn.uri?transport=tcp"]
2001 ///
2002 /// default: []
2003 #[serde(default)]
2004 pub turn_uris: Vec<String>,
2005
2006 /// TURN secret to use for generating the HMAC-SHA1 hash apart of username
2007 /// and password generation.
2008 ///
2009 /// This is more secure, but if needed you can use traditional static
2010 /// username/password credentials.
2011 ///
2012 /// display: sensitive
2013 /// reloadable: yes
2014 #[serde(default)]
2015 pub turn_secret: Option<String>,
2016
2017 /// TURN secret to use that's read from the file path specified.
2018 ///
2019 /// This takes priority over "turn_secret", and falls back to it when the
2020 /// file cannot be opened. Surrounding whitespace is trimmed off, so a
2021 /// trailing newline does not become part of the secret. A file which is
2022 /// present but blank resolves to no secret rather than falling back.
2023 ///
2024 /// reloadable: yes
2025 /// example: "/etc/tuwunel/.turn_secret"
2026 pub turn_secret_file: Option<PathBuf>,
2027
2028 /// TURN TTL, in seconds.
2029 ///
2030 /// reloadable: yes
2031 /// default: 86400
2032 #[serde(default = "default_turn_ttl")]
2033 pub turn_ttl: u64,
2034
2035 #[expect(clippy::doc_link_with_quotes)]
2036 /// List/vector of room IDs or room aliases that tuwunel will make newly
2037 /// registered users join. The rooms specified must be rooms that you have
2038 /// joined at least once on the server, and must be public.
2039 ///
2040 /// reloadable: yes
2041 /// example: ["#tuwunel:grin.hu",
2042 /// "!l2xV0sd51lraysuRcsWVECge4NULaH3g-ou95vgDgiM"]
2043 ///
2044 /// default: []
2045 #[serde(default = "Vec::new")]
2046 pub auto_join_rooms: Vec<OwnedRoomOrAliasId>,
2047
2048 /// Config option to automatically deactivate the account of any user who
2049 /// attempts to join a:
2050 /// - banned room
2051 /// - forbidden room alias
2052 /// - room alias or ID with a forbidden server name
2053 ///
2054 /// This may be useful if all your banned lists consist of toxic rooms or
2055 /// servers that no good faith user would ever attempt to join, and
2056 /// to automatically remediate the problem without any admin user
2057 /// intervention.
2058 ///
2059 /// This will also make the user leave all rooms. Federation (e.g. remote
2060 /// room invites) are ignored here.
2061 ///
2062 /// Defaults to false as rooms can be banned for non-moderation-related
2063 /// reasons and this performs a full user deactivation.
2064 /// reloadable: yes
2065 #[serde(default)]
2066 pub auto_deactivate_banned_room_attempts: bool,
2067
2068 /// RocksDB log level. This is not the same as tuwunel's log level. This
2069 /// is the log level for the RocksDB engine/library which show up in your
2070 /// database folder/path as `LOG` files. tuwunel will log RocksDB errors
2071 /// as normal through tracing or panics if severe for safety.
2072 ///
2073 /// default: "error"
2074 #[serde(default = "default_rocksdb_log_level")]
2075 pub rocksdb_log_level: String,
2076
2077 /// Routes RocksDB log messages to standard error.
2078 ///
2079 /// `rocksdb_log_level` still filters the emitted records. When disabled,
2080 /// RocksDB uses the application's callback logger instead.
2081 #[serde(default)]
2082 pub rocksdb_log_stderr: bool,
2083
2084 /// Max RocksDB `LOG` file size before rotating. Accepts an integer byte
2085 /// count or a string with SI/IEC suffix such as "4 MiB".
2086 ///
2087 /// default: 4194304
2088 #[serde(
2089 default = "default_rocksdb_max_log_file_size",
2090 deserialize_with = "deserialize_bytesize_usize"
2091 )]
2092 pub rocksdb_max_log_file_size: usize,
2093
2094 /// Time in seconds before RocksDB will forcibly rotate logs.
2095 ///
2096 /// default: 0
2097 #[serde(default = "default_rocksdb_log_time_to_roll")]
2098 pub rocksdb_log_time_to_roll: usize,
2099
2100 /// Use RocksDB tunings tailored to spinning disks (HDDs). On NVMe or SSD
2101 /// storage, leave this disabled.
2102 ///
2103 /// When enabled, RocksDB skips compaction readahead and parallel file-open
2104 /// threads at startup. This option does not affect Direct IO; for that, see
2105 /// `rocksdb_direct_io`.
2106 #[serde(default)]
2107 pub rocksdb_optimize_for_spinning_disks: bool,
2108
2109 /// Enables direct-io to increase database performance via unbuffered I/O.
2110 ///
2111 /// For more details about direct I/O and RockDB, see:
2112 /// https://github.com/facebook/rocksdb/wiki/Direct-IO
2113 ///
2114 /// Set this option to false if the database resides on a filesystem which
2115 /// does not support direct-io like FUSE, or any form of complex filesystem
2116 /// setup such as possibly ZFS.
2117 #[serde(default = "true_fn")]
2118 pub rocksdb_direct_io: bool,
2119
2120 /// Amount of threads that RocksDB will use for parallelism on database
2121 /// operations such as cleanup, sync, flush, compaction, etc. Set to 0 to
2122 /// use all your logical threads. Defaults to your CPU logical thread count.
2123 ///
2124 /// default: varies by system
2125 #[serde(default = "default_rocksdb_parallelism_threads")]
2126 pub rocksdb_parallelism_threads: usize,
2127
2128 /// Maximum number of LOG files RocksDB will keep. This must *not* be set to
2129 /// 0. It must be at least 1. Defaults to 3 as these are not very useful
2130 /// unless troubleshooting/debugging a RocksDB bug.
2131 ///
2132 /// default: 3
2133 #[serde(default = "default_rocksdb_max_log_files")]
2134 pub rocksdb_max_log_files: usize,
2135
2136 /// Type of RocksDB database compression to use.
2137 ///
2138 /// Available options are "zstd", "bz2", "lz4", or "none".
2139 ///
2140 /// It is best to use ZSTD as an overall good balance between
2141 /// speed/performance, storage, IO amplification, and CPU usage. For more
2142 /// performance but less compression (more storage used) and less CPU usage,
2143 /// use LZ4.
2144 ///
2145 /// For more details, see:
2146 /// https://github.com/facebook/rocksdb/wiki/Compression
2147 ///
2148 /// "none" will disable compression.
2149 ///
2150 /// default: "zstd"
2151 #[serde(default = "default_rocksdb_compression_algo")]
2152 pub rocksdb_compression_algo: String,
2153
2154 /// Level of compression the specified compression algorithm for RocksDB to
2155 /// use.
2156 ///
2157 /// Default is 32767, which is internally read by RocksDB as the default
2158 /// magic number and translated to the library's default compression level
2159 /// as they all differ. See their `kDefaultCompressionLevel`.
2160 ///
2161 /// Note when using the default value we may override it with a setting
2162 /// tailored specifically tuwunel.
2163 ///
2164 /// default: 32767
2165 #[serde(default = "default_rocksdb_compression_level")]
2166 pub rocksdb_compression_level: i32,
2167
2168 /// Level of compression the specified compression algorithm for the
2169 /// bottommost level/data for RocksDB to use. Default is 32767, which is
2170 /// internally read by RocksDB as the default magic number and translated to
2171 /// the library's default compression level as they all differ. See their
2172 /// `kDefaultCompressionLevel`.
2173 ///
2174 /// Since this is the bottommost level (generally old and least used data),
2175 /// it may be desirable to have a very high compression level here as it's
2176 /// less likely for this data to be used. Research your chosen compression
2177 /// algorithm.
2178 ///
2179 /// Note when using the default value we may override it with a setting
2180 /// tailored specifically tuwunel.
2181 ///
2182 /// default: 32767
2183 #[serde(default = "default_rocksdb_bottommost_compression_level")]
2184 pub rocksdb_bottommost_compression_level: i32,
2185
2186 /// Whether to enable RocksDB's "bottommost_compression".
2187 ///
2188 /// At the expense of more CPU usage, this will further compress the
2189 /// database to reduce more storage. It is recommended to use ZSTD
2190 /// compression with this for best compression results. This may be useful
2191 /// if you're trying to reduce storage usage from the database.
2192 ///
2193 /// See https://github.com/facebook/rocksdb/wiki/Compression for more details.
2194 #[serde(default = "true_fn")]
2195 pub rocksdb_bottommost_compression: bool,
2196
2197 /// Database recovery mode (for RocksDB WAL corruption).
2198 ///
2199 /// Use this option when the server reports corruption and refuses to start.
2200 /// Set mode 2 (PointInTime) to cleanly recover from this corruption. The
2201 /// server will continue from the last good state, several seconds or
2202 /// minutes prior to the crash. Clients may have to run "clear-cache &
2203 /// reload" to account for the rollback. Upon success, you may reset the
2204 /// mode back to default and restart again. Please note in some cases the
2205 /// corruption error may not be cleared for at least 30 minutes of operation
2206 /// in PointInTime mode.
2207 ///
2208 /// As a very last ditch effort, if PointInTime does not fix or resolve
2209 /// anything, you can try mode 3 (SkipAnyCorruptedRecord) but this will
2210 /// leave the server in a potentially inconsistent state.
2211 ///
2212 /// The default mode 1 (TolerateCorruptedTailRecords) will automatically
2213 /// drop the last entry in the database if corrupted during shutdown, but
2214 /// nothing more. It is extraordinarily unlikely this will desynchronize
2215 /// clients. To disable any form of silent rollback set mode 0
2216 /// (AbsoluteConsistency).
2217 ///
2218 /// The options are:
2219 /// 0 = AbsoluteConsistency
2220 /// 1 = TolerateCorruptedTailRecords (default)
2221 /// 2 = PointInTime (use me if trying to recover)
2222 /// 3 = SkipAnyCorruptedRecord (you now voided your tuwunel warranty)
2223 ///
2224 /// For more information on these modes, see:
2225 /// https://github.com/facebook/rocksdb/wiki/WAL-Recovery-Modes
2226 ///
2227 /// For more details on recovering a corrupt database, see:
2228 /// https://tuwunel.chat/troubleshooting.html#database-corruption
2229 ///
2230 /// default: 1
2231 #[serde(default = "default_rocksdb_recovery_mode")]
2232 pub rocksdb_recovery_mode: u8,
2233
2234 /// Enables or disables paranoid SST file checks. This can improve RocksDB
2235 /// database consistency at a potential performance impact due to further
2236 /// safety checks ran.
2237 ///
2238 /// For more information, see:
2239 /// https://github.com/facebook/rocksdb/wiki/Online-Verification#columnfamilyoptionsparanoid_file_checks
2240 #[serde(default)]
2241 pub rocksdb_paranoid_file_checks: bool,
2242
2243 /// Enables or disables checksum verification in rocksdb at runtime.
2244 /// Checksums are usually hardware accelerated with low overhead; they are
2245 /// enabled in rocksdb by default. Older or slower platforms may see gains
2246 /// from disabling.
2247 ///
2248 /// default: true
2249 #[serde(default = "true_fn")]
2250 pub rocksdb_checksums: bool,
2251
2252 /// Enables the "atomic flush" mode in rocksdb. This option is not intended
2253 /// for users. It may be removed or ignored in future versions. Atomic flush
2254 /// may be enabled by the paranoid to possibly improve database integrity at
2255 /// the cost of performance.
2256 #[serde(default)]
2257 pub rocksdb_atomic_flush: bool,
2258
2259 /// Database repair mode (for RocksDB SST corruption).
2260 ///
2261 /// Use this option when the server reports corruption while running or
2262 /// panics. If the server refuses to start use the recovery mode options
2263 /// first. Corruption errors containing the acronym 'SST' which occur after
2264 /// startup will likely require this option.
2265 ///
2266 /// - Backing up your database directory is recommended prior to running the
2267 /// repair.
2268 ///
2269 /// - Disabling repair mode and restarting the server is recommended after
2270 /// running the repair.
2271 ///
2272 /// See https://tuwunel.chat/troubleshooting.html#database-corruption for more details on recovering a corrupt database.
2273 #[serde(default)]
2274 pub rocksdb_repair: bool,
2275
2276 /// Opens RocksDB in read-only mode.
2277 ///
2278 /// Writes are rejected and missing column families cannot be created. This
2279 /// mode is disabled by default.
2280 #[serde(default)]
2281 pub rocksdb_read_only: bool,
2282
2283 /// Opens RocksDB as a secondary follower of a primary instance.
2284 ///
2285 /// Writes are rejected while the primary's latest WAL can be replayed into
2286 /// this instance's view. Missing column families cannot be created.
2287 #[serde(default)]
2288 pub rocksdb_secondary: bool,
2289
2290 /// Enables idle CPU priority for compaction thread. This is not enabled by
2291 /// default to prevent compaction from falling too far behind on busy
2292 /// systems.
2293 #[serde(default)]
2294 pub rocksdb_compaction_prio_idle: bool,
2295
2296 /// Enables idle IO priority for compaction thread. This prevents any
2297 /// unexpected lag in the server's operation and is usually a good idea.
2298 /// Enabled by default.
2299 #[serde(default = "true_fn")]
2300 pub rocksdb_compaction_ioprio_idle: bool,
2301
2302 /// Enables RocksDB compaction. You should never ever have to set this
2303 /// option to false. If you for some reason find yourself needing to use
2304 /// this option as part of troubleshooting or a bug, please reach out to us
2305 /// in the tuwunel Matrix room with information and details.
2306 ///
2307 /// Disabling compaction will lead to a significantly bloated and
2308 /// explosively large database, gradually poor performance, unnecessarily
2309 /// excessive disk read/writes, and slower shutdowns and startups.
2310 #[serde(default = "true_fn")]
2311 pub rocksdb_compaction: bool,
2312
2313 /// Level of statistics collection. Some admin commands to display database
2314 /// statistics may require this option to be set. Database performance may
2315 /// be impacted by higher settings.
2316 ///
2317 /// Option is a number ranging from 0 to 6:
2318 /// 0 = No statistics.
2319 /// 1 = No statistics in release mode (default).
2320 /// 2 to 3 = Statistics with no performance impact.
2321 /// 3 to 5 = Statistics with possible performance impact.
2322 /// 6 = All statistics.
2323 ///
2324 /// default: 1
2325 #[serde(default = "default_rocksdb_stats_level")]
2326 pub rocksdb_stats_level: u8,
2327
2328 /// Ignores the list of dropped columns set by developers.
2329 ///
2330 /// This should be set to true when knowingly moving between versions in
2331 /// ways which are not recommended or otherwise forbidden, or for
2332 /// diagnostic and development purposes; requiring preservation across such
2333 /// movements.
2334 ///
2335 /// The developer's list of dropped columns is meant to safely reduce space
2336 /// by erasing data no longer in use. If this is set to true that storage
2337 /// will not be reclaimed as intended.
2338 ///
2339 /// default: false
2340 #[serde(default)]
2341 pub rocksdb_never_drop_columns: bool,
2342
2343 /// Configures RocksDB to not preallocate WAL logs.
2344 ///
2345 /// Normally, RocksDB allocates certain types of files by calling
2346 /// fallocate, writing the file contents, then truncating the logs to the
2347 /// proper size. This causes pathological disk space usage on btrfs due to
2348 /// how it interacts with its Copy-on-Write implementation. On ZFS,
2349 /// fallocate(2) for preallocation is unsupported and returns EOPNOTSUPP;
2350 /// only `FALLOC_FL_PUNCH_HOLE` and `FALLOC_FL_ZERO_RANGE` are implemented.
2351 ///
2352 /// Set this to false if you run the server on btrfs or ZFS, and do not
2353 /// touch it otherwise.
2354 ///
2355 /// default: true
2356 #[serde(default = "true_fn")]
2357 pub rocksdb_allow_fallocate: bool,
2358
2359 /// This is a password that can be configured that will let you login to the
2360 /// server bot account (currently `@conduit`) for emergency troubleshooting
2361 /// purposes such as recovering/recreating your admin room, or inviting
2362 /// yourself back.
2363 ///
2364 /// See https://tuwunel.chat/troubleshooting.html#lost-access-to-admin-room
2365 /// for other ways to get back into your admin room.
2366 ///
2367 /// Once this password is unset, all sessions will be logged out for
2368 /// security purposes.
2369 ///
2370 /// example: "F670$2CP@Hw8mG7RY1$%!#Ic7YA"
2371 ///
2372 /// display: sensitive
2373 pub emergency_password: Option<String>,
2374
2375 /// reloadable: yes
2376 /// default: "/_matrix/push/v1/notify"
2377 #[serde(default = "default_notification_push_path")]
2378 pub notification_push_path: String,
2379
2380 /// For compatibility and special purpose use only. Setting this option to
2381 /// true will not filter messages sent to pushers based on rules or actions.
2382 /// Everything will be sent to the pusher. This option is offered for
2383 /// several reasons, but should not be necessary:
2384 /// - Bypass to workaround bugs or outdated server-side ruleset support.
2385 /// - Allow clients to evaluate pushrules themselves (due to the above).
2386 /// - Hosting or companies which have custom pushers and internal needs.
2387 ///
2388 /// Note that setting this option to true will not affect the record of
2389 /// notifications found in the notifications pane.
2390 /// reloadable: yes
2391 #[serde(default)]
2392 pub push_everything: bool,
2393
2394 /// Evaluate the `im.nheko.msc3664.related_event_match` push rule condition,
2395 /// which matches on a property of the event that an incoming event relates
2396 /// to.
2397 ///
2398 /// A user can then write a push rule that notifies for replies or reactions
2399 /// to their own messages, which no other condition can express. Enabling
2400 /// this costs one extra event lookup for every event carrying a relation.
2401 ///
2402 /// The default `.im.nheko.msc3664.reply` push rule uses the condition.
2403 /// Disabling evaluation leaves the rule present but unable to match
2404 /// replies, while clients implementing MSC3664 may still evaluate it
2405 /// locally.
2406 ///
2407 /// reloadable: yes
2408 #[serde(default)]
2409 pub msc3664_related_event_match: bool,
2410
2411 /// Setting to false disables the heroes calculation made by sliding and
2412 /// legacy client sync. The heroes calculation is mandated by the Matrix
2413 /// specification and your client may not operate properly unless this
2414 /// option is set to true.
2415 ///
2416 /// This option is intended for custom software deployments seeking purely
2417 /// to minimize unused resources; the overall savings are otherwise
2418 /// negligible.
2419 /// reloadable: yes
2420 #[serde(default = "true_fn")]
2421 pub calculate_heroes: bool,
2422
2423 /// Allow local (your server only) presence updates/requests.
2424 ///
2425 /// Note that presence on tuwunel is very fast unlike Synapse's. If using
2426 /// outgoing presence, this MUST be enabled.
2427 /// reloadable: yes
2428 #[serde(default = "true_fn")]
2429 pub allow_local_presence: bool,
2430
2431 /// Allow incoming federated presence updates/requests.
2432 ///
2433 /// This option receives presence updates from other servers, but does not
2434 /// send any unless `allow_outgoing_presence` is true. Note that presence on
2435 /// tuwunel is very fast unlike Synapse's.
2436 /// reloadable: yes
2437 #[serde(default = "true_fn")]
2438 pub allow_incoming_presence: bool,
2439
2440 /// Allow outgoing presence updates/requests.
2441 ///
2442 /// This option sends presence updates to other servers, but does not
2443 /// receive any unless `allow_incoming_presence` is true. Note that presence
2444 /// on tuwunel is very fast unlike Synapse's. If using outgoing presence,
2445 /// you MUST enable `allow_local_presence` as well.
2446 /// reloadable: yes
2447 #[serde(default = "true_fn")]
2448 pub allow_outgoing_presence: bool,
2449
2450 /// How many seconds without presence updates before you become idle.
2451 /// Defaults to 5 minutes.
2452 ///
2453 /// default: 300
2454 #[serde(default = "default_presence_idle_timeout_s")]
2455 pub presence_idle_timeout_s: u64,
2456
2457 /// How many seconds without presence updates before you become offline.
2458 /// Defaults to 30 minutes.
2459 ///
2460 /// default: 1800
2461 #[serde(default = "default_presence_offline_timeout_s")]
2462 pub presence_offline_timeout_s: u64,
2463
2464 /// Enable the presence idle timer for remote users.
2465 ///
2466 /// Disabling is offered as an optimization for servers participating in
2467 /// many large rooms or when resources are limited. Disabling it may cause
2468 /// incorrect presence states (i.e. stuck online) to be seen for some remote
2469 /// users.
2470 #[serde(default = "true_fn")]
2471 pub presence_timeout_remote_users: bool,
2472
2473 /// Suppresses push notifications for users marked as active. (Experimental)
2474 ///
2475 /// When enabled, users with `Online` presence and recent activity
2476 /// (based on presence state and sync activity) won’t receive push
2477 /// notifications, reducing duplicate alerts while they're active
2478 /// on another client.
2479 ///
2480 /// Disabled by default to preserve legacy behavior.
2481 /// reloadable: yes
2482 #[serde(default)]
2483 pub suppress_push_when_active: bool,
2484
2485 /// Allow receiving incoming read receipts from remote servers.
2486 /// reloadable: yes
2487 #[serde(default = "true_fn")]
2488 pub allow_incoming_read_receipts: bool,
2489
2490 /// Allow sending read receipts to remote servers.
2491 /// reloadable: yes
2492 #[serde(default = "true_fn")]
2493 pub allow_outgoing_read_receipts: bool,
2494
2495 /// Allow outgoing typing updates to federation.
2496 /// reloadable: yes
2497 #[serde(default = "true_fn")]
2498 pub allow_outgoing_typing: bool,
2499
2500 /// Allow incoming typing updates from federation.
2501 /// reloadable: yes
2502 #[serde(default = "true_fn")]
2503 pub allow_incoming_typing: bool,
2504
2505 /// Maximum time federation user can indicate typing.
2506 ///
2507 /// reloadable: yes
2508 /// default: 30
2509 #[serde(default = "default_typing_federation_timeout_s")]
2510 pub typing_federation_timeout_s: u64,
2511
2512 /// Minimum time local client can indicate typing. This does not override a
2513 /// client's request to stop typing. It only enforces a minimum value in
2514 /// case of no stop request.
2515 ///
2516 /// reloadable: yes
2517 /// default: 15
2518 #[serde(default = "default_typing_client_timeout_min_s")]
2519 pub typing_client_timeout_min_s: u64,
2520
2521 /// Maximum time local client can indicate typing.
2522 ///
2523 /// reloadable: yes
2524 /// default: 45
2525 #[serde(default = "default_typing_client_timeout_max_s")]
2526 pub typing_client_timeout_max_s: u64,
2527
2528 /// Set this to true for tuwunel to compress HTTP response bodies using
2529 /// zstd. This option does nothing if tuwunel was not built with
2530 /// `zstd_compression` feature. Please be aware that enabling HTTP
2531 /// compression may weaken TLS. Most users should not need to enable this.
2532 /// See https://breachattack.com/ and https://wikipedia.org/wiki/BREACH
2533 /// before deciding to enable this.
2534 #[serde(default)]
2535 pub zstd_compression: bool,
2536
2537 /// Set this to true for tuwunel to compress HTTP response bodies using
2538 /// gzip. This option does nothing if tuwunel was not built with
2539 /// `gzip_compression` feature. Please be aware that enabling HTTP
2540 /// compression may weaken TLS. Most users should not need to enable this.
2541 /// See https://breachattack.com/ and https://wikipedia.org/wiki/BREACH before
2542 /// deciding to enable this.
2543 ///
2544 /// If you are in a large amount of rooms, you may find that enabling this
2545 /// is necessary to reduce the significantly large response bodies.
2546 #[serde(default)]
2547 pub gzip_compression: bool,
2548
2549 /// Set this to true for tuwunel to compress HTTP response bodies using
2550 /// brotli. This option does nothing if tuwunel was not built with
2551 /// `brotli_compression` feature. Please be aware that enabling HTTP
2552 /// compression may weaken TLS. Most users should not need to enable this.
2553 /// See https://breachattack.com/ and https://wikipedia.org/wiki/BREACH
2554 /// before deciding to enable this.
2555 #[serde(default)]
2556 pub brotli_compression: bool,
2557
2558 /// Set to true to allow user type "guest" registrations. Some clients like
2559 /// Element attempt to register guest users automatically.
2560 /// reloadable: yes
2561 #[serde(default)]
2562 pub allow_guest_registration: bool,
2563
2564 /// Set to true to log guest registrations in the admin room. Note that
2565 /// these may be noisy or unnecessary if you're a public homeserver.
2566 /// reloadable: yes
2567 #[serde(default)]
2568 pub log_guest_registrations: bool,
2569
2570 /// Set to true to allow guest registrations/users to auto join any rooms
2571 /// specified in `auto_join_rooms`.
2572 /// reloadable: yes
2573 #[serde(default)]
2574 pub allow_guests_auto_join_rooms: bool,
2575
2576 /// Enable the legacy unauthenticated Matrix media repository endpoints.
2577 /// These endpoints consist of:
2578 /// - /_matrix/media/*/config
2579 /// - /_matrix/media/*/upload
2580 /// - /_matrix/media/*/preview_url
2581 /// - /_matrix/media/*/download/*
2582 /// - /_matrix/media/*/thumbnail/*
2583 ///
2584 /// The authenticated equivalent endpoints are always enabled.
2585 ///
2586 /// Defaults to false.
2587 #[serde(default)]
2588 pub allow_legacy_media: bool,
2589
2590 /// Fallback to requesting legacy unauthenticated media from remote servers.
2591 /// Unauthenticated media was removed in ~2024Q3; enabling this adds
2592 /// considerable federation requests which are unlikely to succeed.
2593 /// reloadable: yes
2594 #[serde(default)]
2595 pub request_legacy_media: bool,
2596
2597 /// reloadable: yes
2598 #[serde(default = "true_fn")]
2599 pub freeze_legacy_media: bool,
2600
2601 /// Check consistency of the media directory at startup:
2602 /// 1. When `media_compat_file_link` is enabled, this check will upgrade
2603 /// media when switching back and forth between Conduit and tuwunel. Both
2604 /// options must be enabled to handle this.
2605 /// 2. When media is deleted from the directory, this check will also delete
2606 /// its database entry.
2607 ///
2608 /// If none of these checks apply to your use cases, and your media
2609 /// directory is significantly large setting this to false may reduce
2610 /// startup time.
2611 #[serde(default = "true_fn")]
2612 pub media_startup_check: bool,
2613
2614 /// Enable backward-compatibility with Conduit's media directory by creating
2615 /// symlinks of media.
2616 ///
2617 /// This option is only necessary if you plan on using Conduit again.
2618 /// Otherwise setting this to false reduces filesystem clutter and overhead
2619 /// for managing these symlinks in the directory. This is now disabled by
2620 /// default. You may still return to upstream Conduit but you have to run
2621 /// tuwunel at least once with this set to true and allow the
2622 /// media_startup_check to take place before shutting down to return to
2623 /// Conduit.
2624 #[serde(default)]
2625 pub media_compat_file_link: bool,
2626
2627 /// Prune missing media from the database as part of the media startup
2628 /// checks.
2629 ///
2630 /// This means if you delete files from the media directory the
2631 /// corresponding entries will be removed from the database. This is
2632 /// disabled by default because if the media directory is accidentally moved
2633 /// or inaccessible, the metadata entries in the database will be lost with
2634 /// sadness.
2635 #[serde(default)]
2636 pub prune_missing_media: bool,
2637
2638 /// Largest picture, in pixels, the thumbnailer will decode. Dimensions
2639 /// cost memory whatever the encoded file weighs, so a picture declaring
2640 /// more than this is left without a thumbnail instead of decoded. A video
2641 /// frame inherits the resolution of the video it came from and is bounded
2642 /// here too.
2643 ///
2644 /// 50 megapixels is roughly four 8K frames and more than any ordinary
2645 /// camera produces. Each pixel is budgeted at four bytes, so the default
2646 /// admits a decode of about 200 MiB. The budget is per in-flight request,
2647 /// which is what to size it against rather than one decode: thumbnail
2648 /// requests are not otherwise limited in number.
2649 ///
2650 /// reloadable: yes
2651 /// default: 50000000
2652 #[serde(default = "default_media_thumbnail_max_pixels")]
2653 pub media_thumbnail_max_pixels: u64,
2654
2655 /// Program invoked to extract a still frame from a video, giving videos
2656 /// uploaded without a thumbnail one anyway. Tuwunel decodes no video
2657 /// itself; the frame is scaled and cropped like any other image and the
2658 /// result is cached as an ordinary thumbnail.
2659 ///
2660 /// The list is an argument vector whose first entry is the program and
2661 /// whose remaining entries are its arguments. It is executed directly,
2662 /// never through a shell. Every argument has these tokens substituted
2663 /// before each call:
2664 ///
2665 /// - `{input}` path of a temporary file holding the source video.
2666 /// - `{width}` and `{height}` the requested thumbnail dimensions.
2667 ///
2668 /// The program writes one frame to standard output in any format the
2669 /// thumbnailer decodes: PNG, JPEG, WebP or GIF. Videos are served without
2670 /// a thumbnail while the list is empty.
2671 ///
2672 /// reloadable: yes
2673 /// example: [
2674 /// "ffmpeg", "-loglevel", "error", "-i", "{input}", "-vf", "thumbnail",
2675 /// "-frames:v", "1", "-f", "image2pipe", "-c:v", "mjpeg", "pipe:1",
2676 /// ]
2677 ///
2678 /// default: []
2679 #[serde(default)]
2680 pub media_video_thumbnail_command: Vec<String>,
2681
2682 /// Seconds a video thumbnail request may spend on frame extraction. One
2683 /// deadline spans the wait for a free slot, staging the video and the
2684 /// program itself, so a queue cannot compound it into a multiple. On
2685 /// expiry the program and anything it spawned are killed and the video is
2686 /// served without a thumbnail.
2687 ///
2688 /// reloadable: yes
2689 /// default: 30
2690 #[serde(default = "default_media_video_thumbnail_timeout")]
2691 pub media_video_thumbnail_timeout: u64,
2692
2693 /// Video thumbnail extractions permitted to run at once. Decoding video
2694 /// costs far more than scaling an image, so requests past this limit wait
2695 /// for a slot instead of piling load onto the host. A slot is held from
2696 /// staging the video through to the program exiting, so this also bounds
2697 /// how many staged videos occupy the staging directory at once. Raise it
2698 /// where cores are spare; a restart is required to apply a change.
2699 ///
2700 /// default: 1
2701 #[serde(default = "default_media_video_thumbnail_concurrency")]
2702 pub media_video_thumbnail_concurrency: usize,
2703
2704 /// Largest video, in bytes, staged for the thumbnail program, and largest
2705 /// frame read back from it. A video past this is served without a
2706 /// thumbnail rather than written out, and a frame past it is refused
2707 /// rather than decoded from a truncation. Accepts an integer byte count or
2708 /// a string with SI/IEC suffix such as "128 MiB".
2709 ///
2710 /// reloadable: yes
2711 /// default: 128 MiB
2712 #[serde(
2713 default = "default_media_video_thumbnail_max_size",
2714 deserialize_with = "deserialize_bytesize_usize"
2715 )]
2716 pub media_video_thumbnail_max_size: usize,
2717
2718 /// Directory a video is staged in for the thumbnail program to read, one
2719 /// file per running program, removed as soon as it exits. Leave unset to
2720 /// use a `tmp` subdirectory of the database path, which keeps large videos
2721 /// off the memory-backed `/tmp` a service manager commonly provides. Files
2722 /// left behind by a killed server are reclaimed at startup.
2723 ///
2724 /// reloadable: yes
2725 /// example: "/var/tmp/tuwunel"
2726 pub media_video_thumbnail_path: Option<PathBuf>,
2727
2728 /// List of storage providers to use for media. Providers can be configured
2729 /// below in respective sections designated by
2730 /// `global.storage_provider.<NAME>.<brand>` where `NAME` can be listed
2731 /// here.
2732 ///
2733 /// For advanced features and future extensions involving multiple providers
2734 /// the list may contain multiple entries. You MUST take note of other
2735 /// configuration options when listing multiple providers or resource
2736 /// duplication costs and poor performance can result.
2737 ///
2738 /// The list defaults to `["media"]` which is an implicit storage provider
2739 /// representing the media directory on the local filesystem. It can be
2740 /// altered by configuring `global.storage_provider.media.local` explicitly
2741 /// or disabled by omitting it from this list entirely. Users with existing
2742 /// deployments are advised to continue listing "media" as a fallback along
2743 /// with their new provider.
2744 ///
2745 /// reloadable: yes
2746 /// default: ["media"]
2747 #[serde(default = "default_media_storage_providers")]
2748 pub media_storage_providers: BTreeSet<String>,
2749
2750 /// List of configured storage providers where new media will be sent. When
2751 /// this list is not explicitly configured all entries in
2752 /// `media_storage_providers` are used as default.
2753 ///
2754 /// This list is important for users passively migrating to a new media
2755 /// storage provider by only writing to one while querying the other as a
2756 /// fallback.
2757 ///
2758 /// For example:
2759 ///
2760 /// `media_storage_providers = ["media", "media_on_s3"]`
2761 /// `store_media_on_providers = ["media_on_s3"]`
2762 ///
2763 /// Entries in this list must also be listed in `media_storage_providers`.
2764 ///
2765 /// reloadable: yes
2766 /// default: []
2767 #[serde(default)]
2768 pub store_media_on_providers: BTreeSet<String>,
2769
2770 /// Redirect local media downloads to a presigned object-store URL when the
2771 /// client sends `allow_redirect=true` (MSC3860). When a configured storage
2772 /// provider can presign the object (S3), the download responds with a 307
2773 /// to a short-lived URL instead of proxying the bytes. Media held only on
2774 /// the local filesystem is always served directly.
2775 ///
2776 /// reloadable: yes
2777 /// default: false
2778 #[serde(default)]
2779 pub media_allow_redirect: bool,
2780
2781 /// Vector list of regex patterns of server names that tuwunel will refuse
2782 /// to download remote media from.
2783 ///
2784 /// reloadable: yes
2785 /// example: ["badserver\.tld$", "badphrase", "19dollarfortnitecards"]
2786 ///
2787 /// default: []
2788 #[serde(default, with = "serde_regex")]
2789 pub prevent_media_downloads_from: RegexSet,
2790
2791 /// List of forbidden server names via regex patterns that we will block
2792 /// incoming AND outgoing federation with, and block client room joins /
2793 /// remote user invites.
2794 ///
2795 /// This check is applied on the room ID, room alias, sender server name,
2796 /// sender user's server name, inbound federation X-Matrix origin, and
2797 /// outbound federation handler.
2798 ///
2799 /// Basically "global" ACLs.
2800 ///
2801 /// The server's own name is always permitted and is never subject to this
2802 /// list.
2803 ///
2804 /// reloadable: yes
2805 /// example: ["badserver\.tld$", "badphrase", "19dollarfortnitecards"]
2806 ///
2807 /// default: []
2808 #[serde(default, with = "serde_regex")]
2809 pub forbidden_remote_server_names: RegexSet,
2810
2811 /// (EXPERIMENTAL) The behavior of this option will change; the
2812 /// _experimental suffix will be removed for that change in an upcoming
2813 /// release.
2814 ///
2815 /// List of allowed server names via regex patterns. This is an allow-list
2816 /// rather than a deny-list with all the same details as its counterpart in
2817 /// `forbidden_remote_server_names`.
2818 ///
2819 /// This feature becomes active when this list has one or more entries;
2820 /// everything not matching is denied. By default it is empty and inactive.
2821 ///
2822 /// The server's own name is always permitted and is never subject to this
2823 /// list.
2824 ///
2825 /// Entries in `forbidden_remote_server_names` are still applied after
2826 /// this is applied. This allows you to match e.g. "*\.example\.com" here
2827 /// while still singling out "bad\.example\.com" for exclusion.
2828 ///
2829 /// reloadable: yes
2830 /// example: ["badserver\.tld$", "badphrase", "19dollarfortnitecards"]
2831 ///
2832 /// default: []
2833 #[serde(default, with = "serde_regex")]
2834 pub allowed_remote_server_names_experimental: RegexSet,
2835
2836 /// List of forbidden server names via regex patterns that we will block all
2837 /// outgoing federated room directory requests for. Useful for preventing
2838 /// our users from wandering into bad servers or spaces.
2839 ///
2840 /// reloadable: yes
2841 /// example: ["badserver\.tld$", "badphrase", "19dollarfortnitecards"]
2842 ///
2843 /// default: []
2844 #[serde(default, with = "serde_regex")]
2845 pub forbidden_remote_room_directory_server_names: RegexSet,
2846
2847 #[expect(clippy::doc_link_with_quotes)]
2848 /// Vector list of IPv4 and IPv6 CIDR ranges / subnets *in quotes* that you
2849 /// do not want tuwunel to send outbound requests to. Defaults to
2850 /// RFC1918, unroutable, loopback, multicast, and testnet addresses for
2851 /// security.
2852 ///
2853 /// Please be aware that this is *not* a guarantee. You should be using a
2854 /// firewall with zones as doing this on the application layer may have
2855 /// bypasses.
2856 ///
2857 /// Proxy endpoints selected by configuration or environment variables are
2858 /// exempt so a private forward proxy can be reached. Destination addresses
2859 /// remain filtered for direct requests and for locally resolving `socks4`
2860 /// and `socks5` proxies. HTTP(S) forward proxies and `socks4a` or
2861 /// `socks5h` resolve the destination remotely, so their egress policy must
2862 /// enforce the destination network boundary instead.
2863 ///
2864 /// To disable, set this to be an empty vector (`[]`).
2865 ///
2866 /// Defaults to:
2867 /// ["127.0.0.0/8", "10.0.0.0/8", "172.16.0.0/12",
2868 /// "192.168.0.0/16", "100.64.0.0/10", "192.0.0.0/24", "169.254.0.0/16",
2869 /// "192.88.99.0/24", "198.18.0.0/15", "192.0.2.0/24", "198.51.100.0/24",
2870 /// "203.0.113.0/24", "224.0.0.0/4", "::1/128", "fe80::/10", "fc00::/7",
2871 /// "2001:db8::/32", "ff00::/8", "fec0::/10"]
2872 #[serde(default = "default_ip_range_denylist")]
2873 pub ip_range_denylist: Vec<String>,
2874
2875 /// Optional IP address or network interface-name to bind as the source of
2876 /// URL preview requests. If not set, it will not bind to a specific
2877 /// address or interface.
2878 ///
2879 /// Interface names only supported on Linux, Android, and Fuchsia platforms;
2880 /// all other platforms can specify the IP address. To list the interfaces
2881 /// on your system, use the command `ip link show`.
2882 ///
2883 /// example: `"eth0"` or `"1.2.3.4"`
2884 ///
2885 /// default:
2886 #[serde(default, with = "either::serde_untagged_optional")]
2887 pub url_preview_bound_interface: Option<Either<IpAddr, String>>,
2888
2889 /// Vector list of domains allowed to send requests to for URL previews.
2890 ///
2891 /// This is a *contains* match, not an explicit match. Putting "google.com"
2892 /// will match "https://google.com" and
2893 /// "http://mymaliciousdomainexamplegoogle.com" Setting this to "*" will
2894 /// allow all URL previews. Please note that this opens up significant
2895 /// attack surface to your server, you are expected to be aware of the risks
2896 /// by doing so.
2897 ///
2898 /// reloadable: yes
2899 /// default: []
2900 #[serde(default)]
2901 pub url_preview_domain_contains_allowlist: Vec<String>,
2902
2903 /// Vector list of explicit domains allowed to send requests to for URL
2904 /// previews.
2905 ///
2906 /// This is an *explicit* match, not a contains match. Putting "google.com"
2907 /// will match "https://google.com", "http://google.com", but not
2908 /// "https://mymaliciousdomainexamplegoogle.com". Setting this to "*" will
2909 /// allow all URL previews. Please note that this opens up significant
2910 /// attack surface to your server, you are expected to be aware of the risks
2911 /// by doing so.
2912 ///
2913 /// reloadable: yes
2914 /// default: []
2915 #[serde(default)]
2916 pub url_preview_domain_explicit_allowlist: Vec<String>,
2917
2918 /// Vector list of explicit domains not allowed to send requests to for URL
2919 /// previews.
2920 ///
2921 /// This is an *explicit* match, not a contains match. Putting "google.com"
2922 /// will match "https://google.com", "http://google.com", but not
2923 /// "https://mymaliciousdomainexamplegoogle.com". The denylist is checked
2924 /// first before allowlist. Setting this to "*" will not do anything.
2925 ///
2926 /// reloadable: yes
2927 /// default: []
2928 #[serde(default)]
2929 pub url_preview_domain_explicit_denylist: Vec<String>,
2930
2931 /// Vector list of URLs allowed to send requests to for URL previews.
2932 ///
2933 /// Note that this is a *contains* match, not an explicit match. Putting
2934 /// "google.com" will match "https://google.com/",
2935 /// "https://google.com/url?q=https://mymaliciousdomainexample.com", and
2936 /// "https://mymaliciousdomainexample.com/hi/google.com" Setting this to "*"
2937 /// will allow all URL previews. Please note that this opens up significant
2938 /// attack surface to your server, you are expected to be aware of the risks
2939 /// by doing so.
2940 ///
2941 /// reloadable: yes
2942 /// default: []
2943 #[serde(default)]
2944 pub url_preview_url_contains_allowlist: Vec<String>,
2945
2946 /// Maximum body size allowed when spidering a URL for previews.
2947 ///
2948 /// Accepts an integer byte count or a string with SI/IEC suffix such as
2949 /// "768 KiB". A page whose OpenGraph tags sit past this point yields an
2950 /// empty preview, so a site that front-loads a large script block needs a
2951 /// larger budget than one that does not.
2952 ///
2953 /// reloadable: yes
2954 /// default: 786432
2955 #[serde(
2956 default = "default_url_preview_max_spider_size",
2957 deserialize_with = "deserialize_bytesize_usize"
2958 )]
2959 pub url_preview_max_spider_size: usize,
2960
2961 /// Maximum size of a single media item fetched or relayed for a URL
2962 /// preview: the og:image measurement fetch and the lazy-media relay.
2963 /// Media whose advertised length exceeds this is not registered, and a
2964 /// relay that would exceed it is refused. Accepts an integer byte count
2965 /// or a string with SI/IEC suffix such as "50 MiB".
2966 ///
2967 /// reloadable: yes
2968 /// default: 50 MiB
2969 #[serde(
2970 default = "default_url_preview_max_media_size",
2971 deserialize_with = "deserialize_bytesize_usize"
2972 )]
2973 pub url_preview_max_media_size: usize,
2974
2975 /// Option to decide whether you would like to run the domain allowlist
2976 /// checks (contains and explicit) on the root domain or not. Does not apply
2977 /// to URL contains allowlist. Defaults to false.
2978 ///
2979 /// Example usecase: If this is enabled and you have "wikipedia.org" allowed
2980 /// in the explicit and/or contains domain allowlist, it will allow all
2981 /// subdomains under "wikipedia.org" such as "en.m.wikipedia.org" as the
2982 /// root domain is checked and matched. Useful if the domain contains
2983 /// allowlist is still too broad for you but you still want to allow all the
2984 /// subdomains under a root domain.
2985 /// reloadable: yes
2986 #[serde(default)]
2987 pub url_preview_check_root_domain: bool,
2988
2989 /// User-Agent header the URL preview client sends when fetching pages
2990 /// to extract their OpenGraph tags.
2991 ///
2992 /// When unset, the versioned server User-Agent is used followed by
2993 /// "preview", e.g. "Tuwunel/1.8.1 preview". Some origins serve their
2994 /// OpenGraph tags only to an agent they recognise as a link-preview
2995 /// crawler, and serve everyone else a page whose tags sit past
2996 /// `url_preview_max_spider_size`.
2997 ///
2998 /// reloadable: yes
2999 /// default:
3000 #[serde(default)]
3001 pub url_preview_user_agent: Option<String>,
3002
3003 /// User-Agent header sent when fetching and relaying URL preview media
3004 /// files themselves (og:image, og:video, og:audio, and direct links),
3005 /// as opposed to the pages they appear on. When unset, falls back to
3006 /// `url_preview_user_agent`, then to the versioned server User-Agent.
3007 ///
3008 /// reloadable: yes
3009 /// default:
3010 #[serde(default)]
3011 pub url_preview_media_user_agent: Option<String>,
3012
3013 /// List of forbidden room aliases and room IDs as strings of regex
3014 /// patterns.
3015 ///
3016 /// Regex can be used or explicit contains matches can be done by just
3017 /// specifying the words (see example).
3018 ///
3019 /// This is checked upon room alias creation, custom room ID creation if
3020 /// used, and startup as warnings if any room aliases in your database have
3021 /// a forbidden room alias/ID.
3022 ///
3023 /// reloadable: yes
3024 /// example: ["19dollarfortnitecards", "b[4a]droom", "badphrase"]
3025 ///
3026 /// default: []
3027 #[serde(default, with = "serde_regex")]
3028 pub forbidden_alias_names: RegexSet,
3029
3030 /// List of forbidden username patterns/strings.
3031 ///
3032 /// Regex can be used or explicit contains matches can be done by just
3033 /// specifying the words (see example).
3034 ///
3035 /// This is checked upon username availability check, registration, and
3036 /// startup as warnings if any local users in your database have a forbidden
3037 /// username.
3038 ///
3039 /// reloadable: yes
3040 /// example: ["administrator", "b[a4]dusernam[3e]", "badphrase"]
3041 ///
3042 /// default: []
3043 #[serde(default, with = "serde_regex")]
3044 pub forbidden_usernames: RegexSet,
3045
3046 /// List of server names to deprioritize joining through.
3047 ///
3048 /// If a client requests a join through one of these servers,
3049 /// they will be tried last.
3050 ///
3051 /// Useful for preventing failed joins due to timeouts
3052 /// from a certain homeserver.
3053 ///
3054 /// reloadable: yes
3055 /// default: ["matrix\.org"]
3056 #[serde(
3057 default = "default_deprioritize_joins_through_servers",
3058 with = "serde_regex"
3059 )]
3060 pub deprioritize_joins_through_servers: RegexSet,
3061
3062 /// Maximum make_join requests to attempt within each join attempt. Each
3063 /// attempt tries a different server, as each server is only tried once;
3064 /// though retries can occur when the join request as a whole is retried.
3065 ///
3066 /// reloadable: yes
3067 /// default: 48
3068 #[serde(default = "default_max_make_join_attempts_per_join_attempt")]
3069 pub max_make_join_attempts_per_join_attempt: usize,
3070
3071 /// Maximum join attempts to conduct per client join request. Each join
3072 /// attempt consists of one or more make_join requests limited above, and a
3073 /// single send_join request. This value allows for additional servers to
3074 /// act as the join-server prior to reporting the last error back to the
3075 /// client, which can be frustrating for users. Therefor the default value
3076 /// is greater than one, but less than excessively exceeding the client's
3077 /// request timeout, though that may not be avoidable in some cases.
3078 ///
3079 /// reloadable: yes
3080 /// default: 3
3081 #[serde(default = "default_max_join_attempts_per_join_request")]
3082 pub max_join_attempts_per_join_request: usize,
3083
3084 /// Retry failed and incomplete messages to remote servers immediately upon
3085 /// startup. This is called bursting. If this is disabled, said messages may
3086 /// not be delivered until more messages are queued for that server. Do not
3087 /// change this option unless server resources are extremely limited or the
3088 /// scale of the server's deployment is huge. Do not disable this unless you
3089 /// know what you are doing.
3090 #[serde(default = "true_fn")]
3091 pub startup_netburst: bool,
3092
3093 /// Messages are dropped and not reattempted. The `startup_netburst` option
3094 /// must be enabled for this value to have any effect. Do not change this
3095 /// value unless you know what you are doing. Set this value to -1 to
3096 /// reattempt every message without trimming the queues; this may consume
3097 /// significant disk. Set this value to 0 to drop all messages without any
3098 /// attempt at redelivery.
3099 ///
3100 /// default: 50
3101 #[serde(default = "default_startup_netburst_keep")]
3102 pub startup_netburst_keep: i64,
3103
3104 /// Block non-admin local users from sending room invites (local and
3105 /// remote), and block non-admin users from receiving remote room invites.
3106 ///
3107 /// Admins are always allowed to send and receive all room invites.
3108 /// reloadable: yes
3109 #[serde(default)]
3110 pub block_non_admin_invites: bool,
3111
3112 /// Enforce MSC4311 validation of the create event in federated invite and
3113 /// knock stripped state. When enabled, an invite whose m.room.create event
3114 /// is missing, not a full PDU, bound to a different room, or fails
3115 /// signature checks is rejected, and such events are dropped from knock
3116 /// stripped state. When disabled (the default), failures are logged but
3117 /// tolerated to preserve interoperability during ecosystem migration; a
3118 /// create event that is present as a full PDU but cryptographically bound
3119 /// to a different room is always rejected for room version 12 and above
3120 /// regardless of this setting.
3121 ///
3122 /// reloadable: yes
3123 #[serde(default)]
3124 pub enforce_stripped_state_pdu_validation: bool,
3125
3126 /// Allow admins to enter commands in rooms other than "#admins" (admin
3127 /// room) by prefixing your message with "\!admin" or "\\!admin" followed up
3128 /// a normal tuwunel admin command. The reply will be publicly visible to
3129 /// the room, originating from the sender.
3130 ///
3131 /// reloadable: yes
3132 /// example: \\!admin debug ping puppygock.gay
3133 #[serde(default = "true_fn")]
3134 pub admin_escape_commands: bool,
3135
3136 /// Automatically activate the tuwunel admin room console / CLI on
3137 /// startup. This option can also be enabled with `--console` tuwunel
3138 /// argument. Activation requires standard input to be a terminal.
3139 #[serde(default)]
3140 pub admin_console_automatic: bool,
3141
3142 #[expect(clippy::doc_link_with_quotes)]
3143 /// List of admin commands to execute on startup.
3144 ///
3145 /// This option can also be configured with the `--execute` tuwunel
3146 /// argument and can take standard shell commands and environment variables
3147 ///
3148 /// For example: `./tuwunel --execute "server admin-notice tuwunel has
3149 /// started up at $(date)"`
3150 ///
3151 /// example: admin_execute = ["debug ping puppygock.gay", "debug echo hi"]`
3152 ///
3153 /// default: []
3154 #[serde(default)]
3155 pub admin_execute: Vec<String>,
3156
3157 /// Ignore errors in startup commands.
3158 ///
3159 /// If false, tuwunel will error and fail to start if an admin execute
3160 /// command (`--execute` / `admin_execute`) fails.
3161 /// reloadable: yes
3162 #[serde(default)]
3163 pub admin_execute_errors_ignore: bool,
3164
3165 /// List of admin commands to execute on SIGUSR2.
3166 ///
3167 /// Similar to admin_execute, but these commands are executed when the
3168 /// server receives SIGUSR2 on supporting platforms.
3169 ///
3170 /// reloadable: yes
3171 /// default: []
3172 #[serde(default)]
3173 pub admin_signal_execute: Vec<String>,
3174
3175 /// Controls the max log level for admin command log captures (logs
3176 /// generated from running admin commands). Defaults to "info" on release
3177 /// builds, else "debug" on debug builds.
3178 ///
3179 /// reloadable: yes
3180 /// default: "info"
3181 #[serde(default = "default_admin_log_capture")]
3182 pub admin_log_capture: String,
3183
3184 /// The default room tag to apply on the admin room.
3185 ///
3186 /// On some clients like Element, the room tag "m.server_notice" is a
3187 /// special pinned room at the very bottom of your room list. The tuwunel
3188 /// admin room can be pinned here so you always have an easy-to-access
3189 /// shortcut dedicated to your admin room.
3190 ///
3191 /// reloadable: yes
3192 /// default: "m.server_notice"
3193 #[serde(default = "default_admin_room_tag")]
3194 pub admin_room_tag: String,
3195
3196 /// The room that user, room, and event reports are posted to, instead of
3197 /// the admin room. Accepts a room ID or room alias; the server user must be
3198 /// joined with permission to post there. Reports fall back to the admin
3199 /// room when this is unset, cannot be resolved, or the server user is not a
3200 /// member.
3201 ///
3202 /// reloadable: yes
3203 /// default: (none)
3204 #[serde(default)]
3205 pub report_room: Option<OwnedRoomOrAliasId>,
3206
3207 /// Whether to grant the first user to register admin privileges by joining
3208 /// them to the admin room. Note that technically the next user to register
3209 /// when the admin room is empty (or only contains the server-user) is
3210 /// granted, and only when the admin room is enabled.
3211 ///
3212 /// reloadable: yes
3213 /// default: true
3214 #[serde(default = "true_fn")]
3215 pub grant_admin_to_first_user: bool,
3216
3217 /// Whether the admin room is created on first startup. Users should not set
3218 /// this to false. Developers can set this to false during integration tests
3219 /// to reduce activity and output.
3220 ///
3221 /// default: true
3222 #[serde(default = "true_fn")]
3223 pub create_admin_room: bool,
3224
3225 /// Whether to enable federation on the admin room. This cannot be changed
3226 /// after the admin room is created.
3227 ///
3228 /// default: true
3229 #[serde(default = "true_fn")]
3230 pub federate_admin_room: bool,
3231
3232 /// Sentry.io crash/panic reporting, performance monitoring/metrics, etc.
3233 /// This is NOT enabled by default. tuwunel's default Sentry reporting
3234 /// endpoint domain is `o4509498990067712.ingest.us.sentry.io`.
3235 #[serde(default)]
3236 pub sentry: bool,
3237
3238 /// Sentry reporting URL, if a custom one is desired.
3239 ///
3240 /// display: sensitive
3241 /// default: ""
3242 #[serde(default = "default_sentry_endpoint")]
3243 pub sentry_endpoint: Option<Url>,
3244
3245 /// Report your tuwunel server_name in Sentry.io crash reports and
3246 /// metrics.
3247 #[serde(default)]
3248 pub sentry_send_server_name: bool,
3249
3250 /// Performance monitoring/tracing sample rate for Sentry.io.
3251 ///
3252 /// Note that too high values may impact performance, and can be disabled by
3253 /// setting it to 0.0 (0%) This value is read as a percentage to Sentry,
3254 /// represented as a decimal. Defaults to 15% of traces (0.15)
3255 ///
3256 /// default: 0.15
3257 #[serde(default = "default_sentry_traces_sample_rate")]
3258 pub sentry_traces_sample_rate: f32,
3259
3260 /// Whether to attach a stacktrace to Sentry reports.
3261 #[serde(default)]
3262 pub sentry_attach_stacktrace: bool,
3263
3264 /// Send panics to Sentry. This is true by default, but Sentry has to be
3265 /// enabled. The global `sentry` config option must be enabled to send any
3266 /// data.
3267 #[serde(default = "true_fn")]
3268 pub sentry_send_panic: bool,
3269
3270 /// Send errors to sentry. This is true by default, but sentry has to be
3271 /// enabled. This option is only effective in release-mode; forced to false
3272 /// in debug-mode.
3273 #[serde(default = "true_fn")]
3274 pub sentry_send_error: bool,
3275
3276 /// Controls the tracing log level for Sentry to send things like
3277 /// breadcrumbs and transactions
3278 ///
3279 /// default: "info"
3280 #[serde(default = "default_sentry_filter")]
3281 pub sentry_filter: String,
3282
3283 /// Enable the tokio-console. This option is only relevant to developers.
3284 ///
3285 /// For more information, see:
3286 /// https://tuwunel.chat/development.html#debugging-with-tokio-console
3287 #[serde(default)]
3288 pub tokio_console: bool,
3289
3290 /// Arbitrary argument vector for integration testing. Functionality in the
3291 /// server is altered or informed for the requirements of integration tests.
3292 /// - "smoke" performs a shutdown after startup admin commands rather than
3293 /// hanging on client handling.
3294 ///
3295 /// display: hidden
3296 #[serde(default)]
3297 pub test: BTreeSet<String>,
3298
3299 /// Indicates the server has started in maintenance mode. Historically
3300 /// maintenance mode has been enabled by the command line argument
3301 /// `--maintenance` which then sets various configuration items such as
3302 /// `listening=false` among others. That is still the case. This option was
3303 /// only added as a single source of truth that `--maintenance` mode is
3304 /// active.
3305 ///
3306 /// This option must never be set manually.
3307 ///
3308 /// display: hidden
3309 #[serde(default)]
3310 pub maintenance: bool,
3311
3312 /// Controls whether admin room notices like account registrations, password
3313 /// changes, account deactivations, room directory publications, etc will be
3314 /// sent to the admin room. Update notices and normal admin command
3315 /// responses will still be sent.
3316 /// reloadable: yes
3317 #[serde(default = "true_fn")]
3318 pub admin_room_notices: bool,
3319
3320 /// Maximum number of message events an admin command's output may be split
3321 /// across as replies in the admin room. Output needing more events than
3322 /// this is uploaded to the media repository instead and returned as a text
3323 /// file attachment replying to the command. When 1, output which fits in a
3324 /// single event is posted as a single reply and anything larger becomes an
3325 /// attachment. When 0, output is always posted as an attachment regardless
3326 /// of size.
3327 ///
3328 /// reloadable: yes
3329 /// default: 1
3330 #[serde(default = "default_admin_output_max_events")]
3331 pub admin_output_max_events: usize,
3332
3333 /// Post admin command output into a thread on the command event rather than
3334 /// as replies. Output split across multiple events per
3335 /// `admin_output_max_events` is contained in a single thread; attachment
3336 /// outputs are posted into the thread as well.
3337 ///
3338 /// reloadable: yes
3339 #[serde(default)]
3340 pub admin_output_threads: bool,
3341
3342 /// Save original events before applying redaction to them.
3343 ///
3344 /// They can be retrieved with `admin debug get-retained-pdu` or MSC2815.
3345 ///
3346 /// reloadable: yes
3347 /// default: true
3348 #[serde(default = "true_fn")]
3349 pub save_unredacted_events: bool,
3350
3351 /// Redaction retention period in seconds.
3352 ///
3353 /// By default the unredacted events are stored for 60 days.
3354 ///
3355 /// reloadable: yes
3356 /// default: 5184000
3357 #[serde(default = "default_redaction_retention_seconds")]
3358 pub redaction_retention_seconds: u64,
3359
3360 /// Allows users with `redact` power level to request unredacted events with
3361 /// MSC2815.
3362 ///
3363 /// Server admins can request unredacted events regardless of the value of
3364 /// this option.
3365 ///
3366 /// reloadable: yes
3367 /// default: true
3368 #[serde(default = "true_fn")]
3369 pub allow_room_admins_to_request_unredacted_events: bool,
3370
3371 /// Prevents local users from sending redactions.
3372 ///
3373 /// This check does not apply to server admins.
3374 /// reloadable: yes
3375 #[serde(default)]
3376 pub disable_local_redactions: bool,
3377
3378 /// Serve erased senders' events as pruned copies over federation
3379 /// (MSC4025). A requesting server retains the unredacted view only when
3380 /// one of its users was joined in the room state at the event; join
3381 /// handshakes are not gated.
3382 ///
3383 /// reloadable: yes
3384 /// default: true
3385 #[serde(default = "true_fn")]
3386 pub enforce_erasure_over_federation: bool,
3387
3388 /// Enable database pool affinity support. On supporting systems, block
3389 /// device queue topologies are detected and the request pool is optimized
3390 /// for the hardware; db_pool_workers is determined automatically.
3391 ///
3392 /// default: true
3393 #[serde(default = "true_fn")]
3394 pub db_pool_affinity: bool,
3395
3396 /// Sets the number of worker threads in the frontend-pool of the database.
3397 /// This number should reflect the I/O capabilities of the system,
3398 /// such as the queue-depth or the number of simultaneous requests in
3399 /// flight. Defaults to 32 times the number of CPU cores.
3400 ///
3401 /// Note: This value is only used if db_pool_affinity is disabled or not
3402 /// detected on the system, otherwise it is determined automatically.
3403 ///
3404 /// default: 32
3405 #[serde(default = "default_db_pool_workers")]
3406 pub db_pool_workers: usize,
3407
3408 /// When db_pool_affinity is enabled and detected, the size of any worker
3409 /// group will not exceed the determined value. This is necessary when
3410 /// thread-pooling approach does not scale to the full capabilities of
3411 /// high-end hardware; using detected values without limitation could
3412 /// degrade performance.
3413 ///
3414 /// The value is multiplied by the number of cores which share a device
3415 /// queue, since group workers can be scheduled on any of those cores.
3416 ///
3417 /// default: 32
3418 #[serde(default = "default_db_pool_workers_limit")]
3419 pub db_pool_workers_limit: usize,
3420
3421 /// Limits the total number of workers across all worker groups. When the
3422 /// sum of all groups exceeds this value the worker counts are reduced until
3423 /// this constraint is satisfied.
3424 ///
3425 /// By default this value is only effective on larger systems (e.g. 16+
3426 /// cores) where it will tamper the overall thread-count. The thread-pool
3427 /// model will never achieve hardware capacity but this value can be raised
3428 /// on huge systems if the scheduling overhead is determined to not
3429 /// bottleneck and the worker groups are divided too small.
3430 ///
3431 /// default: 2048
3432 #[serde(default = "default_db_pool_max_workers")]
3433 pub db_pool_max_workers: usize,
3434
3435 /// Determines the size of the queues feeding the database's frontend-pool.
3436 /// The size of the queue is determined by multiplying this value with the
3437 /// number of pool workers. When this queue is full, tokio tasks conducting
3438 /// requests will yield until space is available; this is good for
3439 /// flow-control by avoiding buffer-bloat, but can inhibit throughput if
3440 /// too low.
3441 ///
3442 /// default: 4
3443 #[serde(default = "default_db_pool_queue_mult")]
3444 pub db_pool_queue_mult: usize,
3445
3446 /// Sets the initial value for the concurrency of streams. This value simply
3447 /// allows overriding the default in the code. The default is 32, which is
3448 /// the same as the default in the code. Note this value is itself
3449 /// overridden by the computed stream_width_scale, unless that is disabled;
3450 /// this value can serve as a fixed-width instead.
3451 ///
3452 /// default: 32
3453 #[serde(default = "default_stream_width_default")]
3454 pub stream_width_default: usize,
3455
3456 /// Scales the stream width starting from a base value detected for the
3457 /// specific system. The base value is the database pool worker count
3458 /// determined from the hardware queue size (e.g. 32 for SSD or 64 or 128+
3459 /// for NVMe). This float allows scaling the width up or down by multiplying
3460 /// it (e.g. 1.5, 2.0, etc). The maximum result can be the size of the pool
3461 /// queue (see: db_pool_queue_mult) as any larger value will stall the tokio
3462 /// task. The value can also be scaled down (e.g. 0.5) to improve
3463 /// responsiveness for many users at the cost of throughput for each.
3464 ///
3465 /// Setting this value to 0.0 causes the stream width to be fixed at the
3466 /// value of stream_width_default. The default scale is 1.0 to match the
3467 /// capabilities detected for the system.
3468 ///
3469 /// default: 1.0
3470 #[serde(default = "default_stream_width_scale")]
3471 pub stream_width_scale: f32,
3472
3473 /// Sets the initial amplification factor. This controls batch sizes of
3474 /// requests made by each pool worker, multiplying the throughput of each
3475 /// stream. This value is somewhat abstract from specific hardware
3476 /// characteristics and can be significantly larger than any thread count or
3477 /// queue size. This is because each database query may require several
3478 /// index lookups, thus many database queries in a batch may make progress
3479 /// independently while also sharing index and data blocks which may or may
3480 /// not be cached. It is worthwhile to submit huge batches to reduce
3481 /// complexity. The maximum value is 32768, though sufficient hardware is
3482 /// still advised for that.
3483 ///
3484 /// default: 1024
3485 #[serde(default = "default_stream_amplification")]
3486 pub stream_amplification: usize,
3487
3488 /// Number of sender task workers; determines sender parallelism. Default is
3489 /// '0' which means the value is determined internally, likely matching the
3490 /// number of tokio worker-threads or number of cores, etc. Override by
3491 /// setting a non-zero value.
3492 ///
3493 /// default: 0
3494 #[serde(default)]
3495 pub sender_workers: usize,
3496
3497 /// Enables listener sockets; can be set to false to disable listening. This
3498 /// option is intended for developer/diagnostic purposes only.
3499 #[serde(default = "true_fn")]
3500 pub listening: bool,
3501
3502 /// Enables configuration reload when the server receives SIGUSR1 on
3503 /// supporting platforms.
3504 ///
3505 /// reloadable: yes
3506 /// default: true
3507 #[serde(default = "true_fn")]
3508 pub config_reload_signal: bool,
3509
3510 /// Toggles ignore checking/validating TLS certificates
3511 ///
3512 /// This applies to everything, including URL previews, federation requests,
3513 /// etc. This is a hidden argument that should NOT be used in production as
3514 /// it is highly insecure and I will personally yell at you if I catch you
3515 /// using this.
3516 #[serde(default)]
3517 pub allow_invalid_tls_certificates: bool,
3518
3519 /// Sets the `Access-Control-Allow-Origin` header included by this server in
3520 /// all responses. A list of multiple values can be specified. The default
3521 /// is an empty list. The actual header defaults to `*` upon an empty list.
3522 ///
3523 /// There is no reason to configure this without specific intent. Incorrect
3524 /// values may degrade or disrupt clients.
3525 ///
3526 /// default: []
3527 #[serde(default)]
3528 pub access_control_allow_origin: BTreeSet<String>,
3529
3530 /// Backport state-reset security fixes to all room versions.
3531 ///
3532 /// This option applies the State Resolution 2.1 mitigation developed during
3533 /// project Hydra for room version 12 to all prior State Resolution 2.0 room
3534 /// versions (all room versions supported by this server). These mitigations
3535 /// increase resilience to state-resets without any new definition of
3536 /// correctness; therefor it is safe to set this to true for existing rooms.
3537 ///
3538 /// Furthermore, state-reset attacks are not consistent as they result in
3539 /// rooms without any single consensus, therefor it is unnecessary to set
3540 /// this to false to match other servers which set this to false or simply
3541 /// lack support; even if replicating the post-reset state suffered by other
3542 /// servers is somehow desired.
3543 ///
3544 /// This option exists for developer and debug use, and as a failsafe in
3545 /// lieu of hardcoding it.
3546 /// reloadable: yes
3547 #[serde(default = "true_fn")]
3548 pub hydra_backports: bool,
3549
3550 /// Delete rooms when the last user from this server leaves. This feature is
3551 /// experimental and for the purpose of least-surprise is not enabled by
3552 /// default but can be enabled for deployments interested in conserving
3553 /// space. It may eventually default to true in a future release.
3554 ///
3555 /// Note that not all pathways which can remove the last local user
3556 /// currently invoke this operation, so in some cases you may find the room
3557 /// still exists.
3558 ///
3559 /// reloadable: yes
3560 /// default: false
3561 #[serde(default)]
3562 pub delete_rooms_after_leave: bool,
3563
3564 /// Limits the number of One Time Keys per device (not per-algorithm). The
3565 /// reference implementation maintains 50 OTK's at any given time, therefor
3566 /// our default is at least five times that. There is no known reason for an
3567 /// administrator to adjust this value; it is provided here rather than
3568 /// hardcoding it.
3569 ///
3570 /// reloadable: yes
3571 /// default: 256
3572 #[serde(default = "default_one_time_key_limit")]
3573 pub one_time_key_limit: usize,
3574
3575 /// (EXPERIMENTAL) Setting this option to true replaces the list of identity
3576 /// providers displayed on a client's login page with a single button "Sign
3577 /// in with single sign-on" linking to the URL
3578 /// `/_matrix/client/v3/login/sso/redirect`. All configured providers are
3579 /// attempted for authorization. All authorizations associate with the same
3580 /// Matrix user. NOTE: All authorizations must succeed, as there is no
3581 /// reliable way to skip a provider.
3582 ///
3583 /// This option is disabled by default, allowing the client to list
3584 /// configured providers and permitting privacy-conscious users to authorize
3585 /// only their choice.
3586 ///
3587 /// Note that fluffychat always displays a single button anyway. You do not
3588 /// need to enable this to use fluffychat; instead we offer a
3589 /// default-provider option, see `default` in the provider config section.
3590 /// reloadable: yes
3591 #[serde(default)]
3592 pub single_sso: bool,
3593
3594 /// Setting this option to true replaces the list of identity providers on
3595 /// the client's login screen with a single button "Sign in with single
3596 /// sign-on" linking to the URL `/_matrix/client/v3/login/sso/redirect`. The
3597 /// deployment is expected to intercept this URL with their reverse-proxy to
3598 /// provide a custom webpage listing providers; each entry linking or
3599 /// redirecting back to one of the configured identity providers at
3600 /// /_matrix/client/v3/login/sso/redirect/<client_id>`.
3601 ///
3602 /// This option defaults to false, allowing the client to generate the list
3603 /// of providers or hide all SSO-related options when none configured.
3604 /// reloadable: yes
3605 #[serde(default)]
3606 pub sso_custom_providers_page: bool,
3607
3608 /// From MSC3824:
3609 /// > If the client finds oauth_aware_preferred to be true then, assuming it
3610 /// > supports that auth type, it should present this as the only
3611 /// > login/registration method available to the user.
3612 /// reloadable: yes
3613 #[serde(default, alias = "sso_aware_preferred")]
3614 pub oidc_aware_preferred: bool,
3615
3616 /// Directory containing appservice yaml registration files.
3617 ///
3618 /// default: ""
3619 #[serde(default)]
3620 pub appservice_dir: Option<PathBuf>,
3621
3622 /// Skip database migration on startup. This option is intended for
3623 /// developer debugging and testing only. Never set this option to false
3624 /// unless you have been instructed to do so. Setting this option to false
3625 /// may cause permanent damage and permanent loss of data.
3626 ///
3627 /// Any new database migrations will not be applied on startup, and the
3628 /// database schema version will not be adjusted. These migrations and
3629 /// schema changes may be expected by the current codebase but may not be
3630 /// available when this option is set to false.
3631 ///
3632 /// Setting this option to false will have no effect if no new migrations
3633 /// are to be applied. New migrations are applied once during any execution
3634 /// where this option is set to true (which is the default).
3635 #[serde(default = "true_fn")]
3636 pub database_migrations: bool,
3637
3638 /// Open a database whose schema version is newer than this build supports.
3639 ///
3640 /// A database reporting a higher schema version than this build is normally
3641 /// refused, since opening it stamps the schema down to this build's version
3642 /// and may permanently lose data written by the newer build. Setting this
3643 /// to true overrides that refusal: the database opens, one-time migrations
3644 /// run, and the schema is stamped down to this build's version.
3645 ///
3646 /// It has no effect when the discovered version is at or below this build's
3647 /// version, where migrations apply normally either way. It is also not
3648 /// needed to import a Conduit database or a fork of conduwuit; those are
3649 /// recognized by lineage and open without it.
3650 ///
3651 /// This option is extremely dangerous and intended for developer debugging
3652 /// and testing only. Never set it unless you have been instructed to do so;
3653 /// it may cause permanent damage and permanent loss of data.
3654 #[serde(default)]
3655 pub force_migration: bool,
3656
3657 /// When importing a Conduit database in place, the filesystem path to
3658 /// Conduit's media directory. Leave unset to use `<database_path>/media`,
3659 /// which is Conduit's own default location.
3660 ///
3661 /// example: "/var/lib/matrix-conduit/media"
3662 pub conduit_source_media_path: Option<PathBuf>,
3663
3664 /// When importing a Conduit database, the sharding depth of Conduit's media
3665 /// directory (0 for a flat directory). Must match the importing Conduit's
3666 /// `media.directory_structure`; the default matches Conduit's own default
3667 /// of `Deep { length = 2, depth = 2 }`.
3668 ///
3669 /// default: 2
3670 #[serde(default = "default_conduit_media_directory_depth")]
3671 pub conduit_media_directory_depth: u8,
3672
3673 /// When importing a Conduit database, the shard-segment length of Conduit's
3674 /// media directory. Paired with `conduit_media_directory_depth`.
3675 ///
3676 /// default: 2
3677 #[serde(default = "default_conduit_media_directory_length")]
3678 pub conduit_media_directory_length: u8,
3679
3680 /// When importing a Conduit database whose media lived in an S3 bucket
3681 /// rather than on disk, the name of a `[global.storage_provider.<name>]`
3682 /// entry to read the source originals from. Leave unset to read from the
3683 /// filesystem at `conduit_source_media_path`. Define the named provider
3684 /// with Conduit's own S3 credentials and set its `base_path` to Conduit's
3685 /// `media.path` prefix; the importer reads each content-addressed object
3686 /// using `conduit_media_directory_depth`/`length` for the key sharding.
3687 ///
3688 /// Scope `media_storage_providers` to your destination provider only (e.g.
3689 /// `["media"]`) so the import writes solely there; otherwise media is also
3690 /// copied back into the read-only source bucket.
3691 ///
3692 /// example: "conduit_source"
3693 pub conduit_source_media_provider: Option<String>,
3694
3695 /// Set this to true for excluding unencrypted rooms from the common-rooms
3696 /// calculation deciding the receivers of device list updates.
3697 ///
3698 /// Setting this to true can help performance on very large homeservers,
3699 /// but it may not be spec compliant and risky for client expectations.
3700 /// reloadable: yes
3701 #[serde(default)]
3702 pub device_key_update_encrypted_rooms_only: bool,
3703
3704 /// Defines named media storage providers.
3705 ///
3706 /// Each map key names a provider, and each value selects a local or
3707 /// S3-compatible backend or disables the entry. Provider-specific settings
3708 /// live in separate sections.
3709 // external structure; separate section
3710 #[serde(default)]
3711 pub storage_provider: BTreeMap<String, StorageProvider>,
3712
3713 /// Defines policy documents users must accept during registration.
3714 ///
3715 /// Each map key is the policy identifier exposed in the `m.login.terms` UIA
3716 /// stage. An empty map leaves the terms stage disabled.
3717 // external structure; separate section
3718 #[serde(default)]
3719 pub registration_terms: BTreeMap<String, TermsPolicy>,
3720
3721 /// Configures LDAP login integration.
3722 ///
3723 /// Connection, bind, search, and attribute settings live in the separate
3724 /// `[global.ldap]` section. LDAP authentication is disabled by default.
3725 // external structure; separate section
3726 #[serde(default)]
3727 pub ldap: LdapConfig,
3728
3729 /// Configures JSON Web Token login integration.
3730 ///
3731 /// Key format, algorithm, claim validation, and user provisioning settings
3732 /// live in `[global.jwt]`. Token login is disabled by default.
3733 // external structure; separate section
3734 #[serde(default)]
3735 pub jwt: JwtConfig,
3736
3737 /// Configures outbound SMTP email delivery.
3738 ///
3739 /// Providing a connection URI enables the email subsystem. Registration
3740 /// flags determine when a verified address is required.
3741 // external structure; separate section
3742 #[serde(default)]
3743 pub smtp: SmtpConfig,
3744
3745 /// Defines inline application service registrations.
3746 ///
3747 /// Each map key names one registration and supplies its default identifier.
3748 /// The contained settings are converted to Matrix application service data.
3749 // external structure; separate section
3750 #[serde(default)]
3751 pub appservice: BTreeMap<String, AppService>,
3752
3753 /// Defines OpenID Connect identity provider registrations.
3754 ///
3755 /// Each entry configures client credentials, discovery, and account
3756 /// mapping. Its stable `client_id` identifies the provider while `brand`
3757 /// selects provider-specific defaults and workarounds.
3758 // external structure; separate sections
3759 #[serde(default, with = "identity_provider_serde")]
3760 pub identity_provider: BTreeMap<String, IdentityProvider>,
3761
3762 #[serde(flatten)]
3763 #[expect(clippy::zero_sized_map_values)]
3764 // this is a catchall, the map shouldn't be zero at runtime
3765 catchall: BTreeMap<String, IgnoredAny>,
3766}
3767
3768/// Configures direct TLS listener behavior.
3769///
3770/// Certificate and key paths must be supplied together. Optional dual-protocol
3771/// mode accepts encrypted and plain connections on the same listeners.
3772#[derive(Clone, Debug, Deserialize, Default)]
3773#[config_example_generator(filename = "tuwunel-example.toml", section = "global.tls")]
3774pub struct TlsConfig {
3775 /// Path to a valid TLS certificate file.
3776 ///
3777 /// example: "/path/to/my/certificate.crt"
3778 pub certs: Option<String>,
3779
3780 /// Path to a valid TLS certificate private key.
3781 ///
3782 /// example: "/path/to/my/certificate.key"
3783 pub key: Option<String>,
3784
3785 /// Controls whether listeners accept both HTTP and HTTPS.
3786 ///
3787 /// Plain requests are served without redirecting them to HTTPS. This
3788 /// weakens transport security and is disabled by default.
3789 #[serde(default)]
3790 pub dual_protocol: bool,
3791}
3792
3793/// Configures Matrix discovery documents and related response data.
3794///
3795/// Client and server fields drive the standard well-known responses. Support
3796/// contacts, policies, and MatrixRTC transports populate their corresponding
3797/// discovery data.
3798#[expect(rustdoc::bare_urls)]
3799#[derive(Clone, Debug, Deserialize, Default)]
3800#[config_example_generator(
3801 filename = "tuwunel-example.toml",
3802 section = "global.well_known",
3803 ignore = "support_contact support_policy",
3804 hidden = "support_role support_email support_mxid support_page support_pgp_key"
3805)]
3806pub struct WellKnownConfig {
3807 /// The server URL that the client well-known file will serve.
3808 ///
3809 /// This should not contain a port, and should just be a valid HTTPS URL.
3810 /// While this is unset, `/.well-known/matrix/client` answers 404 and
3811 /// auto-discovery from the server name yields nothing, so the base URL has
3812 /// to reach clients some other way. Leave it unset only when a reverse
3813 /// proxy or another host publishes that file for this domain.
3814 ///
3815 /// example: "https://matrix.example.com"
3816 pub client: Option<Url>,
3817
3818 /// The server base domain of the URL with a specific port that the server
3819 /// well-known file will serve. This should contain a port at the end, and
3820 /// should not be a URL.
3821 ///
3822 /// reloadable: yes
3823 /// example: "matrix.example.com:443"
3824 pub server: Option<OwnedServerName>,
3825
3826 /// Defines contacts published by the support discovery endpoint.
3827 ///
3828 /// Each map value becomes one contact while its key is only a config
3829 /// identifier. Legacy scalar support fields are appended separately.
3830 // external structure; separate section
3831 #[serde(default)]
3832 pub support_contact: BTreeMap<String, SupportContact>,
3833
3834 /// Defines policies published by the support discovery endpoint.
3835 ///
3836 /// Each map key becomes a policy identifier. The value supplies its version
3837 /// and localized documents.
3838 // external structure; separate section
3839 #[serde(default)]
3840 pub support_policy: BTreeMap<String, SupportPolicy>,
3841
3842 /// The URL of the support web page. This and the below generate the content
3843 /// of `/.well-known/matrix/support`.
3844 ///
3845 /// example: "https://example.com/support"
3846 pub support_page: Option<Url>,
3847
3848 /// The name of the support role.
3849 ///
3850 ///
3851 /// display: hidden
3852 // This config option is hidden because [global.well_known.support_contact.<ID>] should be
3853 // used instead. However for compatibility purposes the config option will still function and
3854 // be prioritised first.
3855 pub support_role: Option<ContactRole>,
3856
3857 /// The email address for the above support role.
3858 ///
3859 ///
3860 /// display: hidden
3861 // This config option is hidden because [global.well_known.support_contact.<ID>] should be
3862 // used instead. However for compatibility purposes the config option will still function and
3863 // be prioritised first.
3864 pub support_email: Option<String>,
3865
3866 /// The Matrix User ID for the above support role.
3867 ///
3868 /// display: hidden
3869 // This config option is hidden because [global.well_known.support_contact.<ID>] should be
3870 // used instead. However for compatibility purposes the config option will still function and
3871 // be prioritised first.
3872 pub support_mxid: Option<OwnedUserId>,
3873
3874 /// The PGP key (i.e. OpenPGP) that one may use for encrypted communications
3875 /// for the above support role. The value must be a URI. Use a web URL
3876 /// pointing to the key (for example "https://example.com/key.asc"), an
3877 /// OPENPGPKEY DNS record ("dns:..."), or a fingerprint carried with the
3878 /// "openpgp4fpr:" scheme. A bare fingerprint without a scheme, or raw
3879 /// inlined key material, is rejected at startup.
3880 ///
3881 /// As this is a spec proposal (MSC4439), the identifier/prefix for this
3882 /// field is currently "dev.zirco.msc4439.pgp_key"
3883 ///
3884 /// display: hidden
3885 // This config option is hidden because [global.well_known.support_contact.<ID>] should be
3886 // used instead. However for compatibility purposes the config option will still function and
3887 // be prioritised first.
3888 pub support_pgp_key: Option<String>,
3889
3890 /// LiveKit JWT endpoint.
3891 /// Required for Element Call / MatrixRTC (MSC4143).
3892 ///
3893 /// Note: You must also set `client` above to your homeserver URL.
3894 ///
3895 /// reloadable: yes
3896 /// default: ""
3897 #[serde(default)]
3898 pub livekit_url: Option<String>,
3899
3900 /// Custom MatrixRTC transports.
3901 ///
3902 /// If you're looking to setup Element Call / MatrixRTC with Livekit,
3903 /// you should not use this option and instead set `livekit_url`.
3904 /// This is only required if you want to configure a non-livekit MatrixRTC
3905 /// transport. There are no known client implementations that support any
3906 /// other transport types.
3907 ///
3908 /// This option was previously the only way to configure a Livekit
3909 /// transport. It has been superseded by `livekit_url`.
3910 ///
3911 /// Example:
3912 /// ```toml
3913 /// [global.well_known]
3914 /// client = "https://matrix.yourdomain.com"
3915 ///
3916 /// [[global.well_known.rtc_transports]]
3917 /// type = "livekit"
3918 /// livekit_service_url = "https://livekit.yourdomain.com"
3919 /// ```
3920 ///
3921 /// reloadable: yes
3922 /// default: []
3923 #[serde(default)]
3924 pub rtc_transports: Vec<serde_json::Value>,
3925}
3926
3927/// Defines one policy published by the support discovery endpoint.
3928///
3929/// The enclosing map key supplies the policy identifier. Its version and
3930/// localized translations are emitted in the discovery response.
3931#[derive(Clone, Debug, Deserialize)]
3932#[config_example_generator(
3933 filename = "tuwunel-example.toml",
3934 section = "global.well_known.support_policy.<ID>",
3935 ignore = "policy_translation"
3936)]
3937pub struct SupportPolicy {
3938 /// Version string of the policy document.
3939 ///
3940 /// example: "v6.7"
3941 /// reloadable: yes
3942 pub version: String,
3943
3944 /// Maps language identifiers to localized policy documents.
3945 ///
3946 /// Each value supplies the display name and URL for its language. The map
3947 /// is converted to the response's localized policy entries.
3948 // external structure; separate section
3949 pub policy_translation: BTreeMap<String, SupportPolicyTranslation>,
3950}
3951
3952/// Defines one localized support policy document.
3953///
3954/// `name` is the user-facing title for this language. `url` points clients to
3955/// the corresponding policy text.
3956#[derive(Clone, Debug, Deserialize)]
3957#[config_example_generator(
3958 filename = "tuwunel-example.toml",
3959 section = "global.well_known.support_policy.<ID>.policy_translation.<LANG>"
3960)]
3961pub struct SupportPolicyTranslation {
3962 /// User friendly name of the policy document.
3963 ///
3964 /// example: "Privacy Policy"
3965 /// reloadable: yes
3966 pub name: String,
3967
3968 /// Link to the test of the policy document. A valid URL must be specified.
3969 ///
3970 /// example: "https://website.local/privacy-policy"
3971 /// reloadable: yes
3972 pub url: Url,
3973}
3974
3975/// Defines a policy document required during registration.
3976///
3977/// The enclosing map key becomes the policy identifier presented to clients.
3978/// Its version and translations form the `m.login.terms` stage parameters.
3979#[derive(Clone, Debug, Deserialize)]
3980#[config_example_generator(
3981 filename = "tuwunel-example.toml",
3982 section = "global.registration_terms.<ID>",
3983 ignore = "translations"
3984)]
3985pub struct TermsPolicy {
3986 /// Version of this policy document, presented to the client. Configuring
3987 /// any `[global.registration_terms.<ID>]` block makes registration
3988 /// require an `m.login.terms` stage listing every such document; the
3989 /// `<ID>` is the policy id sent to clients.
3990 ///
3991 /// example: "1.2"
3992 /// reloadable: yes
3993 pub version: String,
3994
3995 /// Maps language identifiers to localized registration policy documents.
3996 ///
3997 /// Each value supplies the display name and HTTP or HTTPS URL for its
3998 /// language. These translations are presented in the terms stage.
3999 // external structure; separate section
4000 pub translations: BTreeMap<String, TermsPolicyTranslation>,
4001}
4002
4003/// Defines one localized registration policy document.
4004///
4005/// `name` is the user-facing title for this language. `url` points clients to
4006/// the policy text whose acceptance is recorded.
4007#[derive(Clone, Debug, Deserialize)]
4008#[config_example_generator(
4009 filename = "tuwunel-example.toml",
4010 section = "global.registration_terms.<ID>.translations.<LANG>"
4011)]
4012pub struct TermsPolicyTranslation {
4013 /// User friendly name of the policy document in this language.
4014 ///
4015 /// example: "Terms of Service"
4016 /// reloadable: yes
4017 pub name: String,
4018
4019 /// Link to the text of the policy document. Must be a valid http(s) URL.
4020 ///
4021 /// example: "https://example.org/terms-1.2-en.html"
4022 /// reloadable: yes
4023 pub url: Url,
4024}
4025
4026impl From<SupportPolicyTranslation>
4027 for ruma::api::identity_service::tos::get_terms_of_service::v2::LocalizedPolicy
4028{
4029 fn from(conf: SupportPolicyTranslation) -> Self {
4030 Self {
4031 name: conf.name,
4032 url: conf.url.to_string(),
4033 }
4034 }
4035}
4036
4037/// Defines a contact published by the support discovery endpoint.
4038///
4039/// Every contact has a Matrix support role. Email, Matrix ID, and OpenPGP key
4040/// fields provide optional communication channels.
4041#[derive(Clone, Debug, Deserialize)]
4042#[config_example_generator(
4043 filename = "tuwunel-example.toml",
4044 section = "global.well_known.support_contact.<ID>"
4045)]
4046pub struct SupportContact {
4047 /// The name of the support role.
4048 ///
4049 /// example: "m.role.admin"
4050 pub role: ContactRole,
4051
4052 /// The email address for the above support role.
4053 ///
4054 /// example: "admin@example.com"
4055 pub email_address: Option<String>,
4056
4057 /// The Matrix User ID for the above support role.
4058 ///
4059 /// example "@admin:example.com"
4060 pub matrix_id: Option<OwnedUserId>,
4061
4062 /// The PGP key (i.e. OpenPGP) that one may use for encrypted communications
4063 /// for the above support role. The value must be a URI. Use a web URL
4064 /// pointing to the key (for example "https://example.com/key.asc"), an
4065 /// OPENPGPKEY DNS record ("dns:..."), or a fingerprint carried with the
4066 /// "openpgp4fpr:" scheme. A bare fingerprint without a scheme, or raw
4067 /// inlined key material, is rejected at startup.
4068 ///
4069 /// As this is a spec proposal (MSC4439), the identifier/prefix for this
4070 /// field is currently "dev.zirco.msc4439.pgp_key"
4071 ///
4072 /// example: "openpgp4fpr:8B77919975EAFA5E2456EE03665FE73077489DB0"
4073 pub pgp_key: Option<String>,
4074}
4075
4076impl From<SupportContact> for ruma::api::client::discovery::discover_support::Contact {
4077 fn from(conf: SupportContact) -> Self {
4078 Self {
4079 role: conf.role,
4080 matrix_id: conf.matrix_id,
4081 email_address: conf.email_address,
4082 pgp_key: conf.pgp_key,
4083 }
4084 }
4085}
4086
4087/// Configures LDAP authentication and directory-backed administration.
4088///
4089/// Connection, bind, search, and attribute settings determine how users are
4090/// located and authenticated. Optional admin search settings identify directory
4091/// entries treated as server administrators.
4092#[derive(Clone, Debug, Default, Deserialize)]
4093#[config_example_generator(filename = "tuwunel-example.toml", section = "global.ldap")]
4094pub struct LdapConfig {
4095 /// Whether to enable LDAP login.
4096 ///
4097 /// reloadable: yes
4098 /// example: "true"
4099 #[serde(default)]
4100 pub enable: bool,
4101
4102 /// URI of the LDAP server.
4103 ///
4104 /// reloadable: yes
4105 /// example: "ldap://ldap.example.com:389"
4106 pub uri: Option<Url>,
4107
4108 /// Root of the searches.
4109 ///
4110 /// reloadable: yes
4111 /// example: "ou=users,dc=example,dc=org"
4112 ///
4113 /// default:
4114 #[serde(default)]
4115 pub base_dn: String,
4116
4117 /// Bind DN if anonymous search is not enabled.
4118 ///
4119 /// You can use the variable `{username}` that will be replaced by the
4120 /// entered username. In such case, the password used to bind will be the
4121 /// one provided for the login and not the one given by
4122 /// `bind_password_file`. Beware: automatically granting admin rights will
4123 /// not work if you use this direct bind instead of a LDAP search.
4124 ///
4125 /// reloadable: yes
4126 /// example: "cn=ldap-reader,dc=example,dc=org" or
4127 /// "cn={username},ou=users,dc=example,dc=org"
4128 ///
4129 /// default: ""
4130 #[serde(default)]
4131 pub bind_dn: Option<String>,
4132
4133 /// Path to a file on the system that contains the password for the
4134 /// `bind_dn`.
4135 ///
4136 /// The server must be able to access the file, and it must not be empty.
4137 ///
4138 /// reloadable: yes
4139 /// default: ""
4140 #[serde(default)]
4141 pub bind_password_file: Option<PathBuf>,
4142
4143 /// Search filter to limit user searches.
4144 ///
4145 /// You can use the variable `{username}` that will be replaced by the
4146 /// entered username for more complex filters.
4147 ///
4148 /// reloadable: yes
4149 /// example: "(&(objectClass=person)(memberOf=matrix))"
4150 ///
4151 /// default: "(objectClass=*)"
4152 #[serde(default = "default_ldap_search_filter")]
4153 pub filter: String,
4154
4155 /// Attribute to use to uniquely identify the user.
4156 ///
4157 /// reloadable: yes
4158 /// example: "uid" or "cn"
4159 ///
4160 /// default: "uid"
4161 #[serde(default = "default_ldap_uid_attribute")]
4162 pub uid_attribute: String,
4163
4164 /// Root of the searches for admin users.
4165 ///
4166 /// Defaults to `base_dn` if empty.
4167 ///
4168 /// reloadable: yes
4169 /// example: "ou=admins,dc=example,dc=org"
4170 ///
4171 /// default:
4172 #[serde(default)]
4173 pub admin_base_dn: String,
4174
4175 /// The LDAP search filter to find administrative users for tuwunel.
4176 ///
4177 /// If left blank, administrative state must be configured manually for each
4178 /// user.
4179 ///
4180 /// You can use the variable `{username}` that will be replaced by the
4181 /// entered username for more complex filters.
4182 ///
4183 /// reloadable: yes
4184 /// example: "(objectClass=tuwunelAdmin)" or "(uid={username})"
4185 ///
4186 /// default:
4187 #[serde(default)]
4188 pub admin_filter: String,
4189}
4190
4191/// Configures authentication using JSON Web Tokens.
4192///
4193/// Key format, signature algorithm, and claim rules determine token validity.
4194/// Optional provisioning creates a local account for an otherwise valid token.
4195#[derive(Clone, Debug, Default, Deserialize)]
4196#[config_example_generator(filename = "tuwunel-example.toml", section = "global.jwt")]
4197pub struct JwtConfig {
4198 /// Enable JWT logins
4199 ///
4200 /// reloadable: yes
4201 /// default: false
4202 #[serde(default)]
4203 pub enable: bool,
4204
4205 /// Validation key, also called 'secret' in Synapse config. The type of key
4206 /// can be configured in 'format', but defaults to the common HMAC which
4207 /// is a plaintext shared-secret, so you should keep this value private.
4208 ///
4209 /// display: sensitive
4210 /// reloadable: yes
4211 /// default:
4212 #[serde(default, alias = "secret")]
4213 pub key: String,
4214
4215 /// Format of the 'key'. Only HMAC, ECDSA, and B64HMAC are supported
4216 /// Binary keys cannot be pasted into this config, so B64HMAC is an
4217 /// alternative to HMAC for properly random secret strings.
4218 /// - HMAC is a plaintext shared-secret private-key.
4219 /// - B64HMAC is a base64-encoded version of HMAC.
4220 /// - ECDSA is a PEM-encoded public-key.
4221 /// - EDDSA is a PEM-encoded Ed25519 public-key.
4222 ///
4223 /// reloadable: yes
4224 /// default: "HMAC"
4225 #[serde(default = "default_jwt_format")]
4226 pub format: String,
4227
4228 /// Automatically create new user from a valid claim, otherwise access is
4229 /// denied for an unknown even with an authentic token.
4230 ///
4231 /// reloadable: yes
4232 /// default: true
4233 #[serde(default = "true_fn")]
4234 pub register_user: bool,
4235
4236 /// JWT algorithm
4237 ///
4238 /// reloadable: yes
4239 /// default: "HS256"
4240 #[serde(default = "default_jwt_algorithm")]
4241 pub algorithm: String,
4242
4243 /// Optional audience claim list. The token must claim one or more values
4244 /// from this list when set.
4245 ///
4246 /// reloadable: yes
4247 /// default: []
4248 #[serde(default)]
4249 pub audience: Vec<String>,
4250
4251 /// Optional issuer claim list. The token must claim one or more values
4252 /// from this list when set.
4253 ///
4254 /// reloadable: yes
4255 /// default: []
4256 #[serde(default)]
4257 pub issuer: Vec<String>,
4258
4259 /// Require expiration claim in the token. This defaults to false for
4260 /// synapse migration compatibility.
4261 ///
4262 /// reloadable: yes
4263 /// default: false
4264 #[serde(default)]
4265 pub require_exp: bool,
4266
4267 /// Require not-before claim in the token. This defaults to false for
4268 /// synapse migration compatibility.
4269 ///
4270 /// reloadable: yes
4271 /// default: false
4272 #[serde(default)]
4273 pub require_nbf: bool,
4274
4275 /// Validate expiration time of the token when present. Whether or not it is
4276 /// required depends on require_exp, but when present this ensures the token
4277 /// is not used after a time.
4278 ///
4279 /// reloadable: yes
4280 /// default: true
4281 #[serde(default = "true_fn")]
4282 pub validate_exp: bool,
4283
4284 /// Validate not-before time of the token when present. Whether or not it is
4285 /// required depends on require_nbf, but when present this ensures the token
4286 /// is not used before a time.
4287 ///
4288 /// reloadable: yes
4289 /// default: true
4290 #[serde(default = "true_fn")]
4291 pub validate_nbf: bool,
4292
4293 /// Bypass validation for diagnostic/debug use only.
4294 ///
4295 /// reloadable: yes
4296 /// default: true
4297 #[serde(default = "true_fn")]
4298 pub validate_signature: bool,
4299}
4300
4301/// Configures outbound email verification through SMTP.
4302///
4303/// The connection URI and sender identify the relay and source mailbox.
4304/// Registration flags control which flows require a verified email address.
4305#[derive(Clone, Debug, Default, Deserialize)]
4306#[config_example_generator(filename = "tuwunel-example.toml", section = "global.smtp")]
4307pub struct SmtpConfig {
4308 /// Connection URL for the outbound SMTP relay used to send email
4309 /// verification messages. Setting this enables the email subsystem;
4310 /// without it no mail is sent.
4311 ///
4312 /// Use a `smtp://` URL for an unencrypted or STARTTLS connection and a
4313 /// `smtps://` URL for implicit TLS. Credentials and the host go inline:
4314 /// `smtps://user:pass@host:port`. The port defaults per scheme when
4315 /// omitted.
4316 ///
4317 /// The userinfo component is URL-encoded, so an `@` inside the username
4318 /// must be written as `%40` (for example a login of `bot@example.com`
4319 /// becomes `smtps://bot%40example.com:pass@host:465`). Other reserved
4320 /// characters in the username or password are percent-encoded the same
4321 /// way.
4322 ///
4323 /// example: "smtps://user:pass@mail.example.com:465"
4324 pub connection_uri: Option<String>,
4325
4326 /// The mailbox that outbound verification messages are sent from. Accepts
4327 /// either a bare address or a display-name form.
4328 ///
4329 /// example: "Example <noreply@example.com>"
4330 pub sender: Option<String>,
4331
4332 /// Require a verified email address to complete registration. When set,
4333 /// the registration flow does not finish until the user proves control of
4334 /// an email address.
4335 ///
4336 /// default: false
4337 #[serde(default)]
4338 pub require_email_for_registration: bool,
4339
4340 /// Require a verified email address when registering with a registration
4341 /// token. When set, token-based registration also demands a verified
4342 /// email address.
4343 ///
4344 /// default: false
4345 #[serde(default)]
4346 pub require_email_for_token_registration: bool,
4347}
4348
4349/// Configures one OpenID Connect identity provider.
4350///
4351/// Client credentials and endpoint discovery establish the upstream
4352/// authorization flow. Claim and trust settings control account mapping and
4353/// optional registration.
4354#[derive(Clone, Debug, Deserialize)]
4355#[config_example_generator(
4356 filename = "tuwunel-example.toml",
4357 section = "[global.identity_provider]"
4358)]
4359pub struct IdentityProvider {
4360 /// The brand-name of the service (e.g. Apple, Facebook, GitHub, GitLab,
4361 /// Google) or the software (e.g. keycloak, MAS) providing the identity.
4362 /// When a brand is recognized we apply certain defaults to this config
4363 /// for your convenience. For certain brands we apply essential internal
4364 /// workarounds specific to that provider; it is important to configure this
4365 /// field properly when a provider needs to be recognized (like GitHub for
4366 /// example).
4367 ///
4368 /// Several configured providers can share the same brand name. It is not
4369 /// case-sensitive. As a convenience for common simple deployments we can
4370 /// identify this provider by brand in addition to the unique `client_id` if
4371 /// and only if there is a single provider for the brand; see notes for
4372 /// `client_id`.
4373 #[serde(deserialize_with = "utils::string::de::to_lowercase")]
4374 pub brand: String,
4375
4376 /// The ID of your OAuth application which the provider generates upon
4377 /// registration. This ID then uniquely identifies this configuration
4378 /// instance itself, becoming the identity provider's ID and must be unique
4379 /// and remain unchanged.
4380 ///
4381 /// As a convenience we also identify this config by `brand` if and only if
4382 /// there is a single provider configured for a `brand`. Note carefully that
4383 /// multiple providers configured with the same `brand` is not an error and
4384 /// this provider will simply not be found when querying by `brand`.
4385 pub client_id: String,
4386
4387 /// Secret key the provider generated for you along with the `client_id`
4388 /// above. Unlike the `client_id`, the `client_secret` can be changed here
4389 /// whenever the provider regenerates one for you.
4390 ///
4391 /// display: sensitive
4392 pub client_secret: Option<String>,
4393
4394 /// Secret key to use, read from the file path specified.
4395 ///
4396 /// Alternative to `client_secret` for deployments that prefer to keep the
4397 /// secret outside the config file. When both are configured `client_secret`
4398 /// is used and this field is ignored. The file is read at startup and on
4399 /// each OAuth exchange, must exist and must be non-empty; leading and
4400 /// trailing whitespace is trimmed. Under systemd the path must be visible
4401 /// to the service after sandboxing (`ReadWritePaths` / `ProtectHome`),
4402 /// typically by placing the file under `/etc/tuwunel/`.
4403 ///
4404 /// example: "/etc/tuwunel/.client_secret"
4405 pub client_secret_file: Option<PathBuf>,
4406
4407 /// Issuer URL the provider publishes for you. We have pre-supplied default
4408 /// values for some of the canonical public providers, making this field
4409 /// optional based on the `brand` set above. Otherwise it is required to
4410 /// find self-hosted providers. It must be identical to what is configured
4411 /// and expected by the provider and must never change because we associate
4412 /// identities to it. If the `/.well-known/openid-configuration` is not
4413 /// found behind this URL see `base_path` below as a workaround.
4414 pub issuer_url: Option<Url>,
4415
4416 /// The callback URL configured when registering the OAuth application with
4417 /// the provider. Tuwunel's callback URL must be strictly formatted exactly
4418 /// as instructed. The URL host must point directly at the matrix server and
4419 /// use the following path:
4420 /// `/_matrix/client/unstable/login/sso/callback/<client_id>` where
4421 /// `<client_id>` is the same one configured for this provider above.
4422 pub callback_url: Option<Url>,
4423
4424 /// When more than one identity_provider has been configured and
4425 /// `single_sso` is false and `sso_custom_providers_page` is false this will
4426 /// determine the behavior of the `/_matrix/client/v3/login/sso/redirect`
4427 /// endpoint (note the url lacks a trailing `client_id`).
4428 ///
4429 /// When only one identity_provider is configured it will be interpreted
4430 /// as the default and this does not need to be set. Otherwise a default
4431 /// *must* be selected for some clients (e.g. fluffychat) to work properly
4432 /// when the above conditions require it. To operate out-of-the-box we
4433 /// default to one configured provider if none are explicitly default; a
4434 /// warning will be logged on startup for this condition.
4435 ///
4436 /// (EXPERIMENTAL) Multiple providers can be set to default. All providers
4437 /// configured with this option set to `true` will associate with the same
4438 /// Matrix account when a client flows through
4439 /// `/_matrix/client/v3/login/sso/redirect`.
4440 ///
4441 /// When a user authorizes any provider configured default, the flow will
4442 /// include all other providers configured default as well for association.
4443 /// NOTE: authorization must succeed for ALL default providers.
4444 #[serde(default)]
4445 pub default: bool,
4446
4447 /// Optional display-name for this provider instance seen on the login page
4448 /// by users. It defaults to `brand`. When configuring multiple providers
4449 /// using the same `brand` this can be set to distinguish them.
4450 pub name: Option<String>,
4451
4452 /// Optional icon for the provider. The canonical providers have a default
4453 /// icon based on the `brand` supplied above when this is not supplied. Note
4454 /// that it uses an MXC url which is curious in the auth-media era and may
4455 /// not be reliable.
4456 pub icon: Option<OwnedMxcUri>,
4457
4458 /// Optional list of scopes to authorize.
4459 ///
4460 /// An empty array sends `openid email profile`. The exception is
4461 /// `brand = "MAS"`, which sends only `openid`: MAS rejects `profile`, and
4462 /// its userinfo endpoint returns just `sub` and `username`. Set this to
4463 /// request a different subset. The user can further restrict scopes during
4464 /// their authorization.
4465 ///
4466 /// default: []
4467 #[serde(default)]
4468 pub scope: BTreeSet<String>,
4469
4470 /// Optional list of userinfo claims which shape and restrict the way we
4471 /// compute a Matrix UserId for new registrations. Reviewing Tuwunel's
4472 /// documentation will be necessary for a complete description in detail. An
4473 /// empty array imposes no restriction here, avoiding generated fallbacks as
4474 /// much as possible.
4475 ///
4476 /// For simplicity we reserve a claim called "unique" which can be listed
4477 /// alone to ensure *only* generated ID's are used for registrations.
4478 ///
4479 /// Note that listing the claim "sub" has special significance and will take
4480 /// precedence over all other claims, listed or unlisted. "sub" is not
4481 /// normally used to determine a UserId unless explicitly listed here.
4482 ///
4483 /// As of now arbitrary claims cannot be listed here, we only recognize
4484 /// specific hard-coded claims.
4485 ///
4486 /// default: []
4487 #[serde(default)]
4488 pub userid_claims: BTreeSet<String>,
4489
4490 /// Trusted providers can cause username conflicts (i.e. account hijacking)
4491 /// but this is precisely how an existing matrix account can be associated
4492 /// with a provider. When this option is set to true, the way we compute a
4493 /// Matrix UserId from userinfo claims is inverted: we find the first
4494 /// matching user and grant access to it. Whereas by default, when set to
4495 /// false, we skip matching users and register the first available username;
4496 /// falling-back to random characters to avoid conflicts.
4497 ///
4498 /// Only set this option to true for providers you self-host and control.
4499 /// Never set this option to true for the public providers such as GitHub,
4500 /// GitLab, etc.
4501 ///
4502 /// Note that associating an existing user with an untrusted provider is
4503 /// still possible but only with the command '!admin query oauth associate'.
4504 ///
4505 /// default: false
4506 #[serde(default)]
4507 pub trusted: bool,
4508
4509 /// Setting this option to false will inhibit unique ID's from being
4510 /// generated as a last-resort when determining a UserId from a provider's
4511 /// claims. In the case of untrusted providers, when all provided claims
4512 /// conflict with existing user accounts, a unique fallback ID needs
4513 /// to be generated for registration to not be denied with an error.
4514 ///
4515 /// Set this option to false if you operate a private server or a trusted
4516 /// identity provider where random UserId's are undesirable; the result of a
4517 /// misconfiguration or other issue where an error is warranted.
4518 ///
4519 /// This option should be set to true for public servers or some users may
4520 /// never be able to register.
4521 ///
4522 /// default: true
4523 #[serde(default = "true_fn")]
4524 pub unique_id_fallbacks: bool,
4525
4526 /// Controls whether new user registration is possible from this provider.
4527 /// When this option is set to false, authorizations from this provider
4528 /// only affect existing users and will never result in a new registration
4529 /// when the claims fail to match any existing user (in the case of trusted
4530 /// providers) or an available username is found (in the case of untrusted
4531 /// providers).
4532 ///
4533 /// When LDAP is enabled, a user found in the LDAP directory counts as an
4534 /// existing user and is still provisioned on first login, since the
4535 /// directory is the authoritative account store.
4536 ///
4537 /// Setting this option to false is generally not useful unless there is
4538 /// an explicit reason to do so.
4539 ///
4540 /// default: true
4541 #[serde(default = "true_fn")]
4542 pub registration: bool,
4543
4544 /// Optional extra path components after the issuer_url leading to the
4545 /// location of the `.well-known` directory used for discovery. If the path
4546 /// starts with a slash it will be treated as absolute, meaning overwriting
4547 /// any path in the issuer_url. The path needs to end with a slash. This
4548 /// will be empty for specification-compliant providers.
4549 pub base_path: Option<String>,
4550
4551 /// Overrides the `.well-known` location where the provider's openid
4552 /// configuration is found. It is very unlikely you will need to set this;
4553 /// available for developers or special purposes only.
4554 pub discovery_url: Option<Url>,
4555
4556 /// Overrides the authorize URL requested during the grant phase. This is
4557 /// generally discovered or derived automatically, but may be required as a
4558 /// workaround for any non-standard or undiscoverable provider.
4559 pub authorization_url: Option<Url>,
4560
4561 /// Overrides the access token URL; the same caveats apply as with the other
4562 /// URL overrides.
4563 pub token_url: Option<Url>,
4564
4565 /// Overrides the revocation URL; the same caveats apply as with the other
4566 /// URL overrides.
4567 pub revocation_url: Option<Url>,
4568
4569 /// Overrides the introspection URL; the same caveats apply as with the
4570 /// other URL overrides.
4571 pub introspection_url: Option<Url>,
4572
4573 /// Overrides the userinfo URL; the same caveats apply as with the other URL
4574 /// overrides.
4575 pub userinfo_url: Option<Url>,
4576
4577 /// Whether to perform discovery and adjust this provider's configuration
4578 /// accordingly. This defaults to true. When true, it is an error when
4579 /// discovery fails and authorizations will not be attempted to the
4580 /// provider.
4581 #[serde(default = "true_fn")]
4582 pub discovery: bool,
4583
4584 /// The duration in seconds before a grant authorization session expires.
4585 ///
4586 /// default: 300
4587 #[serde(default = "default_sso_grant_session_duration")]
4588 pub grant_session_duration: Option<u64>,
4589
4590 /// Whether to check the redirect cookie during the callback. This is a
4591 /// security feature and should remain enabled. This is available for
4592 /// developers or deployments which cannot tolerate cookies and are willing
4593 /// to tolerate the risks.
4594 ///
4595 /// default: true
4596 #[serde(default = "true_fn")]
4597 pub check_cookie: bool,
4598
4599 /// Extra query parameters appended to every authorization request sent to
4600 /// the identity provider.
4601 ///
4602 /// E.g. to force re-authentication even if IdP cookies are present:
4603 /// ```toml
4604 /// [[global.identity_provider]]
4605 /// extra_authorization_parameters = { prompt = "login" }
4606 /// ```
4607 ///
4608 /// default: {}
4609 #[serde(default)]
4610 pub extra_authorization_parameters: BTreeMap<String, String>,
4611
4612 /// Forward the MSC3824 `action` query parameter from the SSO redirect
4613 /// endpoints to this provider as an OpenID Connect `prompt` value.
4614 ///
4615 /// When a client appends `action=register` to a `/login/sso/redirect`
4616 /// request the upstream authorization request carries `prompt=create`
4617 /// (the OpenID Connect "Initiating User Registration" extension) so the
4618 /// provider can present its registration screen. `action=login` is left
4619 /// unforwarded to avoid forcing a re-authentication, and a `prompt` set in
4620 /// `extra_authorization_parameters` still applies in that case. An
4621 /// action-derived `prompt` takes precedence over one configured there.
4622 ///
4623 /// Leave this disabled unless the provider supports the `prompt=create`
4624 /// registration extension; a provider that does not may reject or ignore
4625 /// the request.
4626 ///
4627 /// default: false
4628 #[serde(default)]
4629 pub forward_action_prompt: bool,
4630}
4631
4632impl IdentityProvider {
4633 /// Returns the provider's stable identifier.
4634 ///
4635 /// The identifier is the OAuth application's client ID. It is borrowed from
4636 /// this configuration without allocation.
4637 #[must_use]
4638 pub fn id(&self) -> &str { self.client_id.as_str() }
4639
4640 /// Loads the effective client secret.
4641 ///
4642 /// An inline secret takes precedence over a configured secret file. File
4643 /// contents are read asynchronously and trimmed before being returned.
4644 pub async fn get_client_secret(&self) -> Result<String> {
4645 if let Some(client_secret) = &self.client_secret {
4646 return Ok(client_secret.clone());
4647 }
4648
4649 let Some(client_secret_file) = &self.client_secret_file else {
4650 return Err!("No client secret or client secret file configured");
4651 };
4652
4653 let client_secret = tokio::fs::read_to_string(client_secret_file).await?;
4654
4655 Ok(client_secret.trim().to_owned())
4656 }
4657}
4658
4659/// Selects the backend for a named media storage provider.
4660///
4661/// Local providers store objects beneath a filesystem path, while S3 providers
4662/// use a compatible object store. The default variant disables the entry.
4663#[derive(Clone, Debug, Default, Deserialize)]
4664pub enum StorageProvider {
4665 /// Selects a local filesystem backend.
4666 ///
4667 /// The contained settings root object paths beneath a configured directory.
4668 /// Startup checks can require that directory to be usable.
4669 #[expect(non_camel_case_types)]
4670 local(StorageProviderLocal),
4671
4672 /// Selects an S3-compatible object storage backend.
4673 ///
4674 /// The boxed settings configure endpoint, credentials, encryption, and
4675 /// multipart uploads. Custom endpoints permit compatible non-AWS services.
4676 #[expect(non_camel_case_types)]
4677 #[serde(rename = "s3", alias = "S3")]
4678 s3(Box<StorageProviderS3>),
4679
4680 /// Disables this storage provider entry.
4681 ///
4682 /// This is the default when no backend variant is selected. It carries no
4683 /// backend settings.
4684 #[default]
4685 None,
4686}
4687
4688/// Configures local filesystem object storage.
4689///
4690/// `base_path` prefixes every object path belonging to this provider. Remaining
4691/// options control directory creation, cleanup, and startup checks.
4692#[derive(Clone, Debug, Default, Deserialize)]
4693#[config_example_generator(
4694 filename = "tuwunel-example.toml",
4695 section = "global.storage_provider.<ID>.local"
4696)]
4697pub struct StorageProviderLocal {
4698 /// Absolute path to this local filesystem storage provider. Technically the
4699 /// provider exists at the filesystem root, and the base_path is prefixed to
4700 /// all objects.
4701 #[serde(alias = "path")]
4702 pub base_path: String,
4703
4704 /// Creates the directory on the local filesystem if missing. This is not
4705 /// recommended to prevent misconfigured environments and missing mounts
4706 /// from silently succeeding.
4707 #[serde(default)]
4708 pub create_if_missing: bool,
4709
4710 /// Toggles the preservation of a directory after its last file contents are
4711 /// removed.
4712 #[serde(default = "true_fn")]
4713 pub delete_empty_directories: bool,
4714
4715 /// Enables checks performed at startup determining the usability of the
4716 /// local directory. Failures will abort the server's startup.
4717 ///
4718 /// default: true
4719 #[serde(default = "true_fn")]
4720 pub startup_check: bool,
4721}
4722
4723/// Configures an S3-compatible object storage provider.
4724///
4725/// Bucket, endpoint, and credential fields identify the remote store.
4726/// Transport, encryption, multipart, and startup options tune how objects are
4727/// accessed.
4728#[derive(Clone, Debug, Default, Deserialize)]
4729#[config_example_generator(
4730 filename = "tuwunel-example.toml",
4731 section = "global.storage_provider.<ID>.s3",
4732 section_aliases = "S3"
4733)]
4734pub struct StorageProviderS3 {
4735 /// Supply an s3 URL e.g. "s3://bucket/path". These URLs may contain one
4736 /// or all of `bucket`, `region`, and `path` . When not supplied, such
4737 /// additional items can be supplied below individually.
4738 pub url: Option<String>,
4739
4740 /// The name of the S3 bucket. e.g. "bucketname-123456789-us-west-2-an".
4741 pub bucket: Option<String>,
4742
4743 /// The region of the S3 bucket. e.g. "us-west-2".
4744 ///
4745 /// default: "us-east-1"
4746 pub region: Option<String>,
4747
4748 /// Your amazon IAM Key ID with access granted to this bucket.
4749 /// e.g. "ABCDEFG1X1ZZYYXXWWVV"
4750 #[debug("{}", redacted_debug!(key))]
4751 pub key: Option<String>,
4752
4753 /// The secret key component which is approx 40 characters of base64.
4754 ///
4755 /// default:
4756 /// display: sensitive
4757 #[serde(skip_serializing)]
4758 #[debug("{}", redacted_debug!(secret))]
4759 pub secret: Option<String>,
4760
4761 /// Optional path prefix within the bucket where all our operations will
4762 /// take place.
4763 #[serde(alias = "path")]
4764 pub base_path: Option<String>,
4765
4766 /// (expert use) Override the location of s3 applied after components of the
4767 /// parsed `url` (or when none set).
4768 pub endpoint: Option<String>,
4769
4770 /// (expert use) Override this property useful for some self-hosted
4771 /// environments. By default it is derived when parsing the primary `url`.
4772 #[serde(default)]
4773 pub use_vhost_request: Option<bool>,
4774
4775 /// (expert use) Alternative session-token authentication method.
4776 ///
4777 /// display: sensitive
4778 /// default:
4779 #[serde(skip_serializing)]
4780 #[debug("{}", redacted_debug!(token))]
4781 pub token: Option<String>,
4782
4783 /// (expert use) Associated SSE-KMS key material.
4784 ///
4785 /// display: sensitive
4786 #[debug("{}", redacted_debug!(kms))]
4787 pub kms: Option<String>,
4788
4789 /// (expert use) When configured for the bucket it should be reflected here.
4790 pub use_bucket_key: Option<bool>,
4791
4792 /// (expert use) Threshold size for switching to Multi-part uploads. This is
4793 /// a quirk of the S3 protocol which requires us to use a different approach
4794 /// for "large" uploads. This value determines what a "large" upload is. The
4795 /// default value should be sufficient for most providers. The value is a
4796 /// parsed string allowing SI or IEC units for convenience.
4797 ///
4798 /// default: 100 MiB
4799 #[serde(default = "default_multipart_threshold")]
4800 pub multipart_threshold: ByteSize,
4801
4802 /// (expert use) Size of each individual part within a Multi-part upload.
4803 /// Once an upload exceeds `multipart_threshold` the payload is split into
4804 /// parts of this size, each sent as a separate HTTP PUT. Smaller values
4805 /// keep individual requests under per-request timeouts on slow uplinks at
4806 /// the cost of more round-trips. S3 requires every part except the last
4807 /// to be at least 5 MiB. The value is a parsed string allowing SI or IEC
4808 /// units for convenience.
4809 ///
4810 /// default: 10 MiB
4811 #[serde(default = "default_multipart_part_size")]
4812 pub multipart_part_size: ByteSize,
4813
4814 /// (developer use) Allows relaxing default requirement forcing HTTPS.
4815 ///
4816 /// default: true
4817 #[serde(default = "some_true_fn")]
4818 pub use_https: Option<bool>,
4819
4820 /// (developer_use) Allows skipping request header signatures (will be
4821 /// reejected by AWS).
4822 ///
4823 /// default: true
4824 #[serde(default = "some_true_fn")]
4825 pub use_signatures: Option<bool>,
4826
4827 /// (developer_use) Allows disabling request payload signatures.
4828 ///
4829 /// default: true
4830 #[serde(default = "some_true_fn")]
4831 pub use_payload_signatures: Option<bool>,
4832
4833 /// (developer use) Enables checks performed at startup such as pinging the
4834 /// provider. Failures are considered critical startup errors which abort
4835 /// startup. When set to false, faulty providers are only discovered with
4836 /// first use and will not be fatal errors.
4837 ///
4838 /// Only set this to false if you expect a provider to be down at startup or
4839 /// for development/testing purposes; checks are disabled when the server
4840 /// is started in '--maintenance' mode.
4841 ///
4842 /// default: true
4843 #[serde(default = "true_fn")]
4844 pub startup_check: bool,
4845}
4846
4847/// Defines one inline Matrix application service registration.
4848///
4849/// Tokens, namespaces, and protocol flags are converted to the Matrix
4850/// registration model. The enclosing config map supplies the registration ID
4851/// when `id` is empty.
4852#[derive(Clone, Debug, Default, Deserialize)]
4853#[config_example_generator(
4854 filename = "tuwunel-example.toml",
4855 section = "global.appservice.<ID>",
4856 ignore = "users aliases rooms",
4857 hidden = "id"
4858)]
4859pub struct AppService {
4860 /// Identifies the application service registration.
4861 ///
4862 /// An empty value is replaced with the enclosing config map key. An
4863 /// explicit value must match that key.
4864 #[serde(default)]
4865 pub id: String,
4866
4867 /// The URL for the application service.
4868 ///
4869 /// Optionally set to `null` if no traffic is required.
4870 pub url: Option<String>,
4871
4872 /// A unique token for application services to use to authenticate requests
4873 /// to Homeservers.
4874 ///
4875 /// default:
4876 /// display: sensitive
4877 pub as_token: String,
4878
4879 /// A unique token for Homeservers to use to authenticate requests to
4880 /// application services.
4881 ///
4882 /// default:
4883 /// display: sensitive
4884 pub hs_token: String,
4885
4886 /// The localpart of the user associated with the application service.
4887 pub sender_localpart: Option<String>,
4888
4889 /// Events which are sent from certain users.
4890 #[serde(default)]
4891 pub users: Vec<AppServiceNamespace>,
4892
4893 /// Events which are sent in rooms with certain room aliases.
4894 #[serde(default)]
4895 pub aliases: Vec<AppServiceNamespace>,
4896
4897 /// Events which are sent in rooms with certain room IDs.
4898 #[serde(default)]
4899 pub rooms: Vec<AppServiceNamespace>,
4900
4901 /// Whether requests from masqueraded users are rate-limited.
4902 ///
4903 /// The sender is excluded.
4904 #[serde(default)]
4905 pub rate_limited: bool,
4906
4907 /// The external protocols which the application service provides (e.g.
4908 /// IRC).
4909 ///
4910 /// default: []
4911 #[serde(default)]
4912 pub protocols: Vec<String>,
4913
4914 /// Whether the application service wants to receive ephemeral data.
4915 ///
4916 /// default: false
4917 #[serde(default)]
4918 pub receive_ephemeral: bool,
4919
4920 /// Whether the application service wants to do device management, as part
4921 /// of MSC4190.
4922 ///
4923 /// default: false
4924 #[serde(default)]
4925 pub device_management: bool,
4926
4927 /// Whether the application service wants MSC3202 transaction extensions
4928 /// (device lists, one-time-key counts, and unused fallback key types).
4929 ///
4930 /// The registration-file key is `org.matrix.msc3202`; this inline-config
4931 /// key is `msc3202_transaction_extensions`.
4932 ///
4933 /// default: false
4934 #[serde(default)]
4935 pub msc3202_transaction_extensions: bool,
4936}
4937
4938impl From<AppService> for ruma::api::appservice::Registration {
4939 fn from(conf: AppService) -> Self {
4940 use ruma::api::appservice::Namespaces;
4941
4942 let sender_localpart = conf
4943 .sender_localpart
4944 .unwrap_or_else(|| conf.id.clone());
4945
4946 Self {
4947 id: conf.id,
4948 url: conf.url,
4949 as_token: conf.as_token,
4950 hs_token: conf.hs_token,
4951 receive_ephemeral: conf.receive_ephemeral,
4952 device_management: conf.device_management,
4953 msc3202_transaction_extensions: conf.msc3202_transaction_extensions,
4954 protocols: conf.protocols.into(),
4955 rate_limited: conf.rate_limited.into(),
4956 sender_localpart,
4957 namespaces: Namespaces {
4958 users: conf.users.into_iter().map(Into::into).collect(),
4959 aliases: conf.aliases.into_iter().map(Into::into).collect(),
4960 rooms: conf.rooms.into_iter().map(Into::into).collect(),
4961 },
4962 }
4963 }
4964}
4965
4966/// Defines one namespace claimed by an application service.
4967///
4968/// The regular expression selects users, aliases, or rooms according to the
4969/// list containing this value. `exclusive` controls whether the service owns
4970/// every matching identifier.
4971#[derive(Clone, Debug, Default, Deserialize)]
4972#[config_example_generator(
4973 filename = "tuwunel-example.toml",
4974 section = "[global.appservice.<ID>.<users|rooms|aliases>]"
4975)]
4976pub struct AppServiceNamespace {
4977 /// Whether this application service has exclusive access to events within
4978 /// this namespace.
4979 #[serde(default)]
4980 pub exclusive: bool,
4981
4982 /// A regular expression defining which values this namespace includes.
4983 pub regex: String,
4984}
4985
4986impl From<AppServiceNamespace> for ruma::api::appservice::Namespace {
4987 fn from(conf: AppServiceNamespace) -> Self {
4988 Self {
4989 exclusive: conf.exclusive,
4990 regex: conf.regex,
4991 }
4992 }
4993}
4994
4995/// Items matched here will not generate an "unknown to tuwunel" warning when
4996/// configured. This is important for environment variables which share the
4997/// `TUWUNEL_` prefix namespace but aren't config items; match them here in
4998/// their split+lowercased format.
4999static KNOWN_KEYS: &[&str; 2] = &["^config$", "^runtime_[a-z0-9_]+$"];
5000
5001/// Items listed here generate a deprecation warning when configured.
5002static DEPRECATED_KEYS: &[&str; 10] = &[
5003 "cache_capacity",
5004 "conduit_cache_capacity_modifier",
5005 "ldap.name_attribute",
5006 "max_concurrent_requests",
5007 "well_known_client",
5008 "well_known_server",
5009 "well_known_support_page",
5010 "well_known_support_role",
5011 "well_known_support_email",
5012 "well_known_support_mxid",
5013];
5014
5015impl Config {
5016 /// Loads raw configuration from ordered file and environment sources.
5017 ///
5018 /// Explicit paths follow config files selected through the supported
5019 /// environment variables, and later files override earlier files. Config
5020 /// values from the three environment namespaces take precedence over files.
5021 pub fn load<'a, I>(paths: I) -> Result<Figment>
5022 where
5023 I: Iterator<Item = &'a Path>,
5024 {
5025 let paths = Self::file_paths(paths);
5026 let config = Self::load_files(paths)?;
5027
5028 Ok(Self::merge_environment(config))
5029 }
5030}
5031
5032#[implement(Config)]
5033pub(crate) fn file_paths<'a, I>(paths: I) -> impl Iterator<Item = PathBuf>
5034where
5035 I: Iterator<Item = &'a Path>,
5036{
5037 [
5038 Env::var("CONDUIT_CONFIG"),
5039 Env::var("CONDUWUIT_CONFIG"),
5040 Env::var("TUWUNEL_CONFIG"),
5041 ]
5042 .into_iter()
5043 .flatten()
5044 .map(PathBuf::from)
5045 .chain(paths.map(Path::to_path_buf))
5046}
5047
5048#[implement(Config)]
5049pub(crate) fn load_files<I, P>(paths: I) -> Result<Figment>
5050where
5051 I: Iterator<Item = P>,
5052 P: Into<PathBuf>,
5053{
5054 let toml_files = paths.map(Into::into).collect_vec();
5055
5056 let invalid_toml_files = toml_files
5057 .iter()
5058 .filter(|path| !path.exists())
5059 .map(|path| path.as_os_str())
5060 .collect_vec();
5061
5062 if !invalid_toml_files.is_empty() {
5063 return Err!(
5064 "The following config files do not exist or have broken symlinks: \
5065 {invalid_toml_files:?}"
5066 );
5067 }
5068
5069 toml_files
5070 .iter()
5071 .try_fold(Figment::new(), |config, path| {
5072 Self::load_file(path).map(|file| config.merge(file))
5073 })
5074}
5075
5076#[implement(Config)]
5077fn load_file(path: &Path) -> Result<Figment> {
5078 let provider = Toml::file(path);
5079 let profiles = Provider::data(&provider)?;
5080 let values = profiles.get(&Profile::Default);
5081 let headerless = values.is_some_and(|values| {
5082 values
5083 .values()
5084 .any(|value| value.as_dict().is_none())
5085 });
5086
5087 let has_global = values.is_some_and(|values| values.contains_key("global"));
5088
5089 if headerless && has_global {
5090 return Err!(
5091 "Configuration file mixes bare keys with a [global] profile: {}.",
5092 path.display()
5093 );
5094 }
5095
5096 let provider = match headerless {
5097 | true => provider.profile(Profile::Global),
5098 | false => provider.nested(),
5099 };
5100
5101 Ok(Figment::new().merge(provider))
5102}
5103
5104#[implement(Config)]
5105pub(crate) fn merge_environment(config: Figment) -> Figment {
5106 ENV_PREFIXES
5107 .into_iter()
5108 .fold(config, |config, prefix| {
5109 config.merge(Env::prefixed(prefix).global().split("__"))
5110 })
5111}
5112
5113impl Config {
5114 /// Finalize config
5115 pub fn new(raw_config: &Figment) -> Result<Self> {
5116 let config = raw_config
5117 .extract::<Self>()
5118 .map_err(|e| err!("There was a problem with your configuration file: {e}"))?;
5119
5120 Ok(config)
5121 }
5122
5123 /// Validates the complete configuration.
5124 ///
5125 /// The startup checks emit warnings for deprecated or risky settings and
5126 /// reject invalid combinations. Reload-specific comparisons are performed
5127 /// by the configuration manager separately.
5128 pub fn check(&self) -> Result { check(self) }
5129}
5130
5131impl TlsConfig {
5132 /// Returns the configured TLS certificate and key paths together.
5133 ///
5134 /// A pair is returned only when both options are present. Startup
5135 /// validation rejects a configuration containing only one of the two
5136 /// paths.
5137 #[must_use]
5138 pub fn get_tls_cert_key(&self) -> Option<(&Path, &Path)> {
5139 let cert = self.certs.as_ref()?;
5140
5141 let cert = Path::new(cert);
5142
5143 let key = self.key.as_ref()?; // this cannot fail, aborts startup on cert.is_some ^ key.is_some
5144
5145 let key = Path::new(key);
5146
5147 Some((cert, key))
5148 }
5149}
5150
5151fn true_fn() -> bool { true }
5152
5153fn default_policy_server_request_timeout() -> u64 { 5 }
5154
5155fn default_rendezvous_session_max_bytes() -> usize { 4096 }
5156
5157fn default_rendezvous_session_ttl() -> u64 { 600 }
5158
5159fn default_rendezvous_max_sessions() -> usize { 100 }
5160
5161fn default_rendezvous_rc_per_second() -> u32 { 10 }
5162
5163fn default_rendezvous_rc_burst_count() -> u32 { 20 }
5164
5165fn some_true_fn() -> Option<bool> { Some(true) }
5166
5167#[cfg(test)]
5168fn default_server_name() -> OwnedServerName { ruma::owned_server_name!("localhost") }
5169
5170fn default_database_path() -> PathBuf { "/var/lib/tuwunel".to_owned().into() }
5171
5172fn default_conduit_media_directory_depth() -> u8 { 2 }
5173
5174fn default_conduit_media_directory_length() -> u8 { 2 }
5175
5176fn default_port() -> ListeningPort { ListeningPort { ports: Left(8008) } }
5177
5178fn default_unix_socket_perms() -> u32 { 660 }
5179
5180fn default_database_backups_to_keep() -> i16 { 1 }
5181
5182fn default_db_write_buffer_capacity_mb() -> f64 { 48.0 + parallelism_scaled_f64(4.0) }
5183
5184fn default_db_cache_capacity_mb() -> f64 { 128.0 + parallelism_scaled_f64(64.0) }
5185
5186fn default_pdu_cache_capacity() -> u32 { parallelism_scaled_u32(10_000).saturating_add(100_000) }
5187
5188fn default_cache_capacity_modifier() -> f64 { 1.0 }
5189
5190fn default_auth_chain_cache_capacity() -> u32 {
5191 parallelism_scaled_u32(250_000).saturating_add(750_000)
5192}
5193
5194fn default_shorteventid_cache_capacity() -> u32 {
5195 parallelism_scaled_u32(200_000).saturating_add(400_000)
5196}
5197
5198fn default_eventidshort_cache_capacity() -> u32 {
5199 parallelism_scaled_u32(100_000).saturating_add(400_000)
5200}
5201
5202fn default_eventid_pdu_cache_capacity() -> u32 {
5203 parallelism_scaled_u32(100_000).saturating_add(400_000)
5204}
5205
5206fn default_eventid_backoff_cache_capacity() -> u32 {
5207 parallelism_scaled_u32(4_000).saturating_add(256_000)
5208}
5209
5210fn default_shortstatekey_cache_capacity() -> u32 {
5211 parallelism_scaled_u32(4_000).saturating_add(97_000)
5212}
5213
5214fn default_statekeyshort_cache_capacity() -> u32 {
5215 parallelism_scaled_u32(4_000).saturating_add(97_000)
5216}
5217
5218fn default_servernameevent_data_cache_capacity() -> u32 {
5219 parallelism_scaled_u32(60_000).saturating_add(470_000)
5220}
5221
5222fn default_mediaid_lazycontent_cache_capacity() -> u32 { 128 }
5223
5224fn default_resolver_cache_capacity() -> u32 {
5225 parallelism_scaled_u32(4_000).saturating_add(32_000)
5226}
5227
5228fn default_servername_status_cache_capacity() -> u32 {
5229 parallelism_scaled_u32(4_000).saturating_add(64_000)
5230}
5231
5232fn default_stateinfo_cache_capacity() -> u32 { parallelism_scaled_u32(100) }
5233
5234fn default_spacehierarchy_cache_ttl_min() -> u64 { 60 * 60 * 3 }
5235
5236fn default_spacehierarchy_cache_ttl_max() -> u64 { 60 * 60 * 18 }
5237
5238fn default_dns_cache_entries() -> u32 { 32768 }
5239
5240fn default_dns_min_ttl() -> u64 { 60 * 180 }
5241
5242fn default_dns_min_ttl_nxdomain() -> u64 { 60 * 60 * 24 * 3 }
5243
5244fn default_dns_attempts() -> u16 { 10 }
5245
5246fn default_dns_timeout() -> u64 { 10 }
5247
5248fn default_ip_lookup_strategy() -> u8 { 5 }
5249
5250fn default_max_request_size() -> usize { 24 * 1024 * 1024 }
5251
5252fn default_max_response_size() -> usize { 256 * 1024 * 1024 }
5253
5254fn default_max_pending_media_uploads() -> usize { 5 }
5255
5256fn default_media_create_unused_expiration_time() -> u64 { 86400 }
5257
5258fn default_media_rc_create_per_second() -> u32 { 10 }
5259
5260fn default_media_rc_create_burst_count() -> u32 { 50 }
5261
5262fn default_media_thumbnail_max_pixels() -> u64 { 50_000_000 }
5263
5264fn default_media_video_thumbnail_timeout() -> u64 { 30 }
5265
5266fn default_media_video_thumbnail_concurrency() -> usize { 1 }
5267
5268fn default_media_video_thumbnail_max_size() -> usize { 128 * 1024 * 1024 }
5269
5270fn default_request_conn_timeout() -> u64 { 10 }
5271
5272fn default_request_timeout() -> u64 { 35 }
5273
5274fn default_request_total_timeout() -> u64 { 320 }
5275
5276fn default_request_idle_timeout() -> u64 { 5 }
5277
5278fn default_request_idle_per_host() -> u16 { 1 }
5279
5280fn default_well_known_conn_timeout() -> u64 { 6 }
5281
5282fn default_well_known_timeout() -> u64 { 10 }
5283
5284fn default_federation_timeout() -> u64 { 25 }
5285
5286fn default_federation_keys_timeout() -> u64 { 8 }
5287
5288fn default_federation_idle_timeout() -> u64 { 25 }
5289
5290fn default_federation_idle_per_host() -> u16 { 1 }
5291
5292fn default_sender_timeout() -> u64 { 180 }
5293
5294fn default_sender_idle_timeout() -> u64 { 180 }
5295
5296fn default_sender_retry_backoff_limit() -> u64 { 86400 }
5297
5298fn default_sender_retry_grace() -> u64 { 15 }
5299
5300fn default_appservice_timeout() -> u64 { 35 }
5301
5302fn default_appservice_idle_timeout() -> u64 { 300 }
5303
5304fn default_pusher_idle_timeout() -> u64 { 15 }
5305
5306fn default_max_fetch_prev_events() -> u16 { 1024_u16 }
5307
5308fn default_fetch_prev_wait_ms() -> u64 { 750 }
5309
5310fn default_resolve_state_locally_max() -> usize { 256 }
5311
5312fn default_forward_extremities_max() -> usize { 60 }
5313
5314fn default_forward_extremities_emergency_max() -> usize { 256 }
5315
5316fn default_forward_extremities_prune_batch() -> usize { 32 }
5317
5318fn default_tracing_flame_filter() -> String {
5319 cfg!(debug_assertions)
5320 .then_some("trace,h2=off")
5321 .unwrap_or("info")
5322 .to_owned()
5323}
5324
5325fn default_jaeger_filter() -> String {
5326 cfg!(debug_assertions)
5327 .then_some("trace,h2=off")
5328 .unwrap_or("info")
5329 .to_owned()
5330}
5331
5332fn default_tracing_flame_output_path() -> String { "./tracing.folded".to_owned() }
5333
5334fn default_trusted_servers() -> Vec<OwnedServerName> {
5335 vec![OwnedServerName::try_from("matrix.org").expect("valid ServerName")]
5336}
5337
5338/// do debug logging by default for debug builds
5339#[must_use]
5340pub fn default_log() -> String {
5341 cfg!(debug_assertions)
5342 .then_some("debug")
5343 .unwrap_or("info")
5344 .to_owned()
5345}
5346
5347/// Returns the default tracing span-event mode.
5348///
5349/// The value is `none`, which disables span lifecycle event emission. It is
5350/// used when `log_span_events` is omitted.
5351#[must_use]
5352pub fn default_log_span_events() -> String { "none".into() }
5353
5354fn default_notification_push_path() -> String { "/_matrix/push/v1/notify".to_owned() }
5355
5356fn default_openid_token_ttl() -> u64 { 60 * 60 }
5357
5358fn default_login_token_ttl() -> u64 { 2 * 60 * 1000 }
5359
5360fn default_turn_ttl() -> u64 { 60 * 60 * 24 }
5361
5362fn default_presence_idle_timeout_s() -> u64 { 5 * 60 }
5363
5364fn default_presence_offline_timeout_s() -> u64 { 30 * 60 }
5365
5366fn default_typing_federation_timeout_s() -> u64 { 30 }
5367
5368fn default_typing_client_timeout_min_s() -> u64 { 15 }
5369
5370fn default_typing_client_timeout_max_s() -> u64 { 45 }
5371
5372fn default_rocksdb_recovery_mode() -> u8 { 1 }
5373
5374fn default_rocksdb_log_level() -> String { "error".to_owned() }
5375
5376fn default_rocksdb_log_time_to_roll() -> usize { 0 }
5377
5378fn default_rocksdb_max_log_files() -> usize { 3 }
5379
5380fn default_rocksdb_max_log_file_size() -> usize {
5381 // 4 megabytes
5382 4 * 1024 * 1024
5383}
5384
5385fn default_rocksdb_parallelism_threads() -> usize { 0 }
5386
5387fn default_rocksdb_compression_algo() -> String {
5388 cfg!(feature = "zstd_compression")
5389 .then_some("zstd")
5390 .unwrap_or("none")
5391 .to_owned()
5392}
5393
5394/// Default RocksDB compression level is 32767, which is internally read by
5395/// RocksDB as the default magic number and translated to the library's default
5396/// compression level as they all differ. See their `kDefaultCompressionLevel`.
5397#[expect(clippy::doc_markdown)]
5398fn default_rocksdb_compression_level() -> i32 { 32767 }
5399
5400/// Default RocksDB compression level is 32767, which is internally read by
5401/// RocksDB as the default magic number and translated to the library's default
5402/// compression level as they all differ. See their `kDefaultCompressionLevel`.
5403#[expect(clippy::doc_markdown)]
5404fn default_rocksdb_bottommost_compression_level() -> i32 { 32767 }
5405
5406fn default_rocksdb_stats_level() -> u8 { 1 }
5407
5408/// Returns the default Matrix room version.
5409///
5410/// Room version 11 is selected when `default_room_version` is omitted. The
5411/// value is returned without consulting runtime configuration.
5412// I know, it's a great name
5413#[must_use]
5414#[inline]
5415pub fn default_default_room_version() -> RoomVersionId { RoomVersionId::V11 }
5416
5417fn default_ip_range_denylist() -> Vec<String> {
5418 vec![
5419 "127.0.0.0/8".to_owned(),
5420 "10.0.0.0/8".to_owned(),
5421 "172.16.0.0/12".to_owned(),
5422 "192.168.0.0/16".to_owned(),
5423 "100.64.0.0/10".to_owned(),
5424 "192.0.0.0/24".to_owned(),
5425 "169.254.0.0/16".to_owned(),
5426 "192.88.99.0/24".to_owned(),
5427 "198.18.0.0/15".to_owned(),
5428 "192.0.2.0/24".to_owned(),
5429 "198.51.100.0/24".to_owned(),
5430 "203.0.113.0/24".to_owned(),
5431 "224.0.0.0/4".to_owned(),
5432 "::1/128".to_owned(),
5433 "fe80::/10".to_owned(),
5434 "fc00::/7".to_owned(),
5435 "2001:db8::/32".to_owned(),
5436 "ff00::/8".to_owned(),
5437 "fec0::/10".to_owned(),
5438 ]
5439}
5440
5441fn default_url_preview_max_spider_size() -> usize {
5442 768 * 1024 // 768 KiB
5443}
5444
5445fn default_url_preview_max_media_size() -> usize {
5446 50 * 1024 * 1024 // 50 MiB
5447}
5448
5449fn default_new_user_displayname_suffix() -> String { "💕".to_owned() }
5450
5451fn default_sentry_endpoint() -> Option<Url> {
5452 let url = "https://8994b1762a6a95af9502a7900edabc4c@o4509498990067712.ingest.us.sentry.io/4509498993213440"
5453 .try_into()
5454 .expect("default sentry url is invalid");
5455
5456 Some(url)
5457}
5458
5459fn default_sentry_traces_sample_rate() -> f32 { 0.15 }
5460
5461fn default_sentry_filter() -> String { "info".to_owned() }
5462
5463fn default_startup_netburst_keep() -> i64 { 50 }
5464
5465fn default_admin_log_capture() -> String {
5466 cfg!(debug_assertions)
5467 .then_some("debug")
5468 .unwrap_or("info")
5469 .to_owned()
5470}
5471
5472fn default_admin_room_tag() -> String { "m.server_notice".to_owned() }
5473
5474fn default_admin_output_max_events() -> usize { 1 }
5475
5476#[expect(clippy::as_conversions, clippy::cast_precision_loss)]
5477fn parallelism_scaled_f64(val: f64) -> f64 { val * (sys::available_parallelism() as f64) }
5478
5479fn parallelism_scaled_u32(val: u32) -> u32 {
5480 let val = val
5481 .try_into()
5482 .expect("failed to cast u32 to usize");
5483 parallelism_scaled(val)
5484 .try_into()
5485 .unwrap_or(u32::MAX)
5486}
5487
5488fn parallelism_scaled(val: usize) -> usize { val.saturating_mul(sys::available_parallelism()) }
5489
5490fn default_trusted_server_batch_size() -> usize { 192 }
5491
5492fn default_trusted_server_batch_concurrency() -> usize { 2 }
5493
5494fn default_db_pool_workers() -> usize {
5495 sys::available_parallelism()
5496 .saturating_mul(4)
5497 .clamp(32, 1024)
5498}
5499
5500fn default_db_pool_workers_limit() -> usize { 32 }
5501
5502fn default_db_pool_max_workers() -> usize { 2048 }
5503
5504fn default_db_pool_queue_mult() -> usize { 4 }
5505
5506fn default_stream_width_default() -> usize { 32 }
5507
5508fn default_stream_width_scale() -> f32 { 1.0 }
5509
5510fn default_stream_amplification() -> usize { 1024 }
5511
5512fn default_client_receive_timeout() -> u64 { 75 }
5513
5514fn default_client_request_timeout() -> u64 { 240 }
5515
5516fn default_client_response_timeout() -> u64 { 120 }
5517
5518fn default_client_shutdown_timeout() -> u64 { 15 }
5519
5520fn default_sender_shutdown_timeout() -> u64 { 5 }
5521
5522fn default_ldap_search_filter() -> String { "(objectClass=*)".to_owned() }
5523
5524fn default_ldap_uid_attribute() -> String { String::from("uid") }
5525
5526fn default_jwt_algorithm() -> String { "HS256".to_owned() }
5527
5528fn default_jwt_format() -> String { "HMAC".to_owned() }
5529
5530fn default_client_sync_timeout_min() -> u64 { 5000 }
5531
5532fn default_client_sync_timeout_default() -> u64 { 30000 }
5533
5534fn default_client_sync_timeout_max() -> u64 { 90000 }
5535
5536fn default_access_token_ttl() -> u64 { 604_800 }
5537
5538fn default_refresh_token_reuse_grace() -> u64 { 15 }
5539
5540fn default_deprioritize_joins_through_servers() -> RegexSet {
5541 RegexSet::new([r"matrix\.org"]).expect("valid set of regular expressions")
5542}
5543
5544fn default_one_time_key_limit() -> usize { 256 }
5545
5546fn default_max_make_join_attempts_per_join_attempt() -> usize { 48 }
5547
5548fn default_max_join_attempts_per_join_request() -> usize { 3 }
5549
5550fn default_sso_grant_session_duration() -> Option<u64> { Some(300) }
5551
5552fn default_redaction_retention_seconds() -> u64 { 5_184_000 }
5553
5554fn default_media_storage_providers() -> BTreeSet<String> { ["media".to_owned()].into() }
5555
5556fn default_multipart_threshold() -> ByteSize { ByteSize::mib(100) }
5557
5558fn default_multipart_part_size() -> ByteSize { ByteSize::mib(10) }