Skip to main content

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) }