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