Skip to main content

tuwunel_core/config/
mod.rs

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