#[macro_export]
macro_rules! for_each_setting {
($emit:ident) => {
$emit! {
{ name: listen_port, class: immediate, wire: "listen_port",
doc: "TCP listen port for incoming peer connections (default: 42020)." }
{ name: randomize_port_on_startup, class: stored, wire: "",
doc: "Randomize the listen port each time the session starts. Default: false." }
{ name: download_dir, class: restart, wire: "save_path",
doc: "Default download directory for new torrents (default: \".\")." }
{ name: max_torrents, class: stored, wire: "",
doc: "Maximum number of concurrent torrents (default: 100)." }
{ name: resume_data_dir, class: stored, wire: "",
doc: "Directory for fast-resume data files. If `None`, resume data is not persisted." }
{ name: save_resume_interval_secs, class: immediate, wire: "save_resume_interval",
doc: "Interval in seconds between periodic resume file saves (0 = disabled)." }
{ name: enable_dht, class: immediate, wire: "dht",
doc: "Enable Kademlia DHT peer discovery (BEP 5). Default: true." }
{ name: enable_pex, class: restart, wire: "pex",
doc: "Enable Peer Exchange (BEP 11). Default: true." }
{ name: enable_lsd, class: immediate, wire: "lsd",
doc: "Enable Local Service Discovery via multicast (BEP 14). Default: true." }
{ name: enable_fast_extension, class: stored, wire: "",
doc: "Enable BEP 6 Fast Extension (`AllowedFast`, `HaveAll`, `HaveNone`, Reject," }
{ name: enable_utp, class: stored, wire: "",
doc: "Enable uTP (BEP 29) micro transport protocol. When enabled, outbound" }
{ name: enable_upnp, class: restart, wire: "upnp",
doc: "Enable `UPnP` IGD port mapping (last resort after PCP and NAT-PMP)." }
{ name: enable_natpmp, class: restart, wire: "natpmp",
doc: "Enable NAT-PMP (RFC 6886) and PCP (RFC 6887) port mapping." }
{ name: enable_ipv6, class: stored, wire: "",
doc: "Enable IPv6 dual-stack support (BEP 7, 24). Binds listeners on both" }
{ name: enable_web_seed, class: stored, wire: "",
doc: "Enable HTTP/web seeding (BEP 19 `GetRight`, BEP 17 Hoffman). Torrents" }
{ name: enable_holepunch, class: stored, wire: "",
doc: "Enable BEP 55 holepunch extension for NAT traversal. Advertises" }
{ name: enable_bep40_eviction, class: stored, wire: "",
doc: "Enable BEP 40 canonical peer priority for connection eviction." }
{ name: enable_diagnostic_counters, class: stored, wire: "",
doc: "Enable diagnostic counters (dispatch timing, backpressure high-water," }
{ name: encryption_mode, class: restart, wire: "encryption",
doc: "Connection encryption mode (MSE/PE). Default: Disabled." }
{ name: anonymous_mode, class: restart, wire: "anonymous_mode",
doc: "Suppress identifying information (client version in BEP 10 handshake)" }
{ name: external_ip, class: stored, wire: "",
doc: "Manually configured external IP for BEP 40 peer priority." }
{ name: seed_ratio_limit, class: immediate, wire: "max_ratio",
doc: "Stop seeding when this upload/download ratio is reached. `None` = unlimited." }
{ name: seed_time_limit_secs, class: immediate, wire: "max_seeding_time",
doc: "M171: Stop seeding after this many cumulative seeding seconds." }
{ name: inactive_seed_time_limit_secs, class: immediate, wire: "max_inactive_seeding_time",
doc: "M171: Stop seeding after this many seconds of inactivity while in the" }
{ name: max_ratio_action, class: immediate, wire: "max_ratio_act",
doc: "M171: What to do when `seed_ratio_limit` is reached." }
{ name: create_subfolder, class: immediate, wire: "create_subfolder_enabled",
doc: "M171: Create a subfolder named after the torrent when adding a" }
{ name: auto_manage_torrents, class: immediate, wire: "auto_tmm_enabled",
doc: "M171: Automatically manage torrent resources via the queueing" }
{ name: queueing_enabled, class: immediate, wire: "queueing_enabled",
doc: "M171: Enable the download/upload queueing subsystem. When `false`," }
{ name: default_super_seeding, class: stored, wire: "",
doc: "Enable BEP 16 super seeding for new torrents. Reveals pieces one-per-peer" }
{ name: default_share_mode, class: stored, wire: "",
doc: "Default share mode for new torrents. When true, torrents relay pieces" }
{ name: upload_only_announce, class: stored, wire: "",
doc: "Advertise upload-only status via extension handshake when a torrent" }
{ name: upload_rate_limit, class: immediate, wire: "up_limit",
doc: "Global upload rate limit in bytes/sec (0 = unlimited)." }
{ name: download_rate_limit, class: immediate, wire: "dl_limit",
doc: "Global download rate limit in bytes/sec (0 = unlimited)." }
{ name: tcp_upload_rate_limit, class: stored, wire: "",
doc: "TCP upload rate limit in bytes/sec (0 = unlimited)." }
{ name: tcp_download_rate_limit, class: stored, wire: "",
doc: "TCP download rate limit in bytes/sec (0 = unlimited)." }
{ name: utp_upload_rate_limit, class: stored, wire: "",
doc: "uTP upload rate limit in bytes/sec (0 = unlimited)." }
{ name: utp_download_rate_limit, class: stored, wire: "",
doc: "uTP download rate limit in bytes/sec (0 = unlimited)." }
{ name: auto_upload_slots, class: stored, wire: "",
doc: "Automatically adjust the number of upload slots based on bandwidth. Default: true." }
{ name: auto_upload_slots_min, class: stored, wire: "",
doc: "Minimum number of automatic upload slots (default: 2)." }
{ name: auto_upload_slots_max, class: stored, wire: "",
doc: "Maximum number of automatic upload slots (default: 20)." }
{ name: max_upload_slots_global, class: stored, wire: "",
doc: "Maximum upload slots across all torrents (-1 = unlimited). Default: -1." }
{ name: max_upload_slots_per_torrent, class: stored, wire: "",
doc: "Maximum upload slots per torrent. Default: 4." }
{ name: max_connections_global, class: immediate, wire: "max_connec_global",
doc: "Maximum peer connections across all torrents (-1 = unlimited). Default: -1." }
{ name: max_uploads_per_torrent, class: immediate, wire: "max_uploads_per_torrent",
doc: "Maximum unchoked upload slots per torrent (-1 = unlimited). Default: -1." }
{ name: alt_download_rate_limit, class: stored, wire: "",
doc: "Alternative download rate limit in bytes/sec (0 = unlimited)." }
{ name: alt_upload_rate_limit, class: stored, wire: "",
doc: "Alternative upload rate limit in bytes/sec (0 = unlimited)." }
{ name: alt_speed_enabled, class: stored, wire: "",
doc: "Whether alternative speed limits are currently active. Default: false." }
{ name: alt_speed_schedule_enabled, class: stored, wire: "",
doc: "Whether the alternative speed schedule is enabled. Default: false." }
{ name: alt_speed_schedule_from, class: stored, wire: "",
doc: "Schedule start time in minutes-of-day (0-1439). Default: 0." }
{ name: alt_speed_schedule_to, class: stored, wire: "",
doc: "Schedule end time in minutes-of-day (0-1439). Default: 0." }
{ name: alt_speed_schedule_days, class: stored, wire: "",
doc: "Schedule active days as a bitmask (bit 0 = Mon .. bit 6 = Sun). Default: 0." }
{ name: rate_limit_includes_overhead, class: stored, wire: "",
doc: "Include protocol overhead in rate limit calculations. Default: true." }
{ name: rate_limit_utp, class: stored, wire: "",
doc: "Apply rate limits to uTP connections. Default: true." }
{ name: rate_limit_lan, class: stored, wire: "",
doc: "Apply rate limits to LAN connections. Default: false." }
{ name: mixed_mode_algorithm, class: stored, wire: "",
doc: "Mixed-mode TCP/uTP bandwidth allocation algorithm." }
{ name: active_downloads, class: stored, wire: "",
doc: "Maximum concurrent auto-managed downloading torrents (-1 = unlimited, default: 3)." }
{ name: active_seeds, class: stored, wire: "",
doc: "Maximum concurrent auto-managed seeding torrents (-1 = unlimited, default: 5)." }
{ name: active_limit, class: stored, wire: "",
doc: "Hard cap on all active auto-managed torrents (-1 = unlimited, default: 500)." }
{ name: active_checking, class: stored, wire: "",
doc: "Maximum concurrent hash-check operations (default: 1)." }
{ name: dont_count_slow_torrents, class: stored, wire: "",
doc: "Exempt inactive torrents from download/seed limits. A torrent is inactive" }
{ name: inactive_down_rate, class: stored, wire: "",
doc: "Download rate threshold (bytes/sec) below which a torrent is considered" }
{ name: inactive_up_rate, class: stored, wire: "",
doc: "Upload rate threshold (bytes/sec) below which a torrent is considered" }
{ name: auto_manage_interval, class: stored, wire: "",
doc: "Interval in seconds between queue evaluations (default: 30)." }
{ name: auto_manage_startup, class: stored, wire: "",
doc: "Grace period in seconds where a torrent is considered active regardless" }
{ name: auto_manage_prefer_seeds, class: stored, wire: "",
doc: "Allocate seeding slots before download slots. Default: false." }
{ name: queue_rate_ewma_alpha, class: stored, wire: "",
doc: "EWMA smoothing factor for queue rate classification (0.0–1.0)." }
{ name: seed_queue_min_active_secs, class: stored, wire: "",
doc: "Anti-flap grace period for seeding torrents, in seconds." }
{ name: alert_mask, class: stored, wire: "",
doc: "Bitmask of alert categories to receive (default: ALL)." }
{ name: alert_channel_size, class: stored, wire: "",
doc: "Capacity of the alert broadcast channel (default: 1024)." }
{ name: smart_ban_max_failures, class: stored, wire: "",
doc: "Number of hash-failure involvements before a peer is auto-banned." }
{ name: smart_ban_parole, class: stored, wire: "",
doc: "Enable parole mode: re-download a failed piece from a single uninvolved" }
{ name: disk_io_threads, class: stored, wire: "",
doc: "Number of concurrent disk I/O threads (default: 4)." }
{ name: max_blocking_threads, class: stored, wire: "",
doc: "Maximum number of concurrent blocking I/O operations dispatched via" }
{ name: storage_mode, class: stored, wire: "",
doc: "Storage allocation mode: Auto, `FullPreallocate`, or `SparseFile` (default: Auto)." }
{ name: preallocate_mode, class: stored, wire: "",
doc: "Override pre-allocation strategy (None/Sparse/Full). When `None` (default)," }
{ name: disk_cache_size, class: stored, wire: "",
doc: "Total ARC disk cache size in bytes (default: 16 MiB, minimum: 1 MiB)." }
{ name: disk_write_cache_ratio, class: stored, wire: "",
doc: "Fraction of disk cache reserved for write buffering (0.0–1.0, default: 0.5)." }
{ name: disk_channel_capacity, class: stored, wire: "",
doc: "Capacity of the async disk I/O command channel (default: 512)." }
{ name: buffer_pool_capacity, class: stored, wire: "",
doc: "Unified buffer pool capacity in bytes (default: 64 MiB)." }
{ name: enable_mlock, class: stored, wire: "",
doc: "Lock cached piece data in physical memory (default: true on Unix)." }
{ name: io_uring_sq_depth, class: stored, wire: "",
doc: "`io_uring` submission queue depth (number of SQEs). Only used when" }
{ name: io_uring_direct_io, class: stored, wire: "",
doc: "Enable `O_DIRECT` for `io_uring` writes, bypassing the kernel page cache." }
{ name: filesystem_direct_io, class: stored, wire: "",
doc: "Enable direct I/O for filesystem storage (bypasses kernel page cache)." }
{ name: io_uring_batch_threshold, class: stored, wire: "",
doc: "Minimum number of file segments to batch before using `io_uring`." }
{ name: iocp_concurrent_threads, class: stored, wire: "",
doc: "IOCP concurrent thread count (0 = system default). Only used when" }
{ name: iocp_direct_io, class: stored, wire: "",
doc: "Enable `FILE_FLAG_NO_BUFFERING` for IOCP I/O, bypassing the OS page cache." }
{ name: hashing_threads, class: immediate, wire: "hashing_threads",
doc: "Number of concurrent piece hash verification threads (default: 2)." }
{ name: max_request_queue_depth, class: stored, wire: "",
doc: "Maximum per-peer request queue depth (default: 250)." }
{ name: initial_queue_depth, class: stored, wire: "",
doc: "Initial per-peer request queue depth (default: 128). Higher values let" }
{ name: request_queue_time, class: stored, wire: "",
doc: "Request queue time multiplier in seconds (default: 3.0)." }
{ name: block_request_timeout_secs, class: stored, wire: "",
doc: "Block request timeout in seconds before the request is considered" }
{ name: max_concurrent_stream_reads, class: stored, wire: "",
doc: "Maximum concurrent `FileStream` readers. Controls how many simultaneous" }
{ name: auto_sequential, class: stored, wire: "",
doc: "Automatically switch to sequential piece picking when too many partial" }
{ name: strict_end_game, class: stored, wire: "",
doc: "In end-game mode, cancel duplicate requests when a piece completes." }
{ name: max_web_seeds, class: stored, wire: "",
doc: "Maximum concurrent web seed connections per torrent (default: 4)." }
{ name: web_seed_retry_base_secs, class: stored, wire: "",
doc: "M186: Base delay (seconds) for web seed exponential backoff. Default: 10." }
{ name: web_seed_retry_factor, class: stored, wire: "",
doc: "M186: Multiplier for web seed exponential backoff. Default: 6." }
{ name: web_seed_retry_cap_secs, class: stored, wire: "",
doc: "M186: Maximum backoff (seconds) for web seed retry. Default: 3600." }
{ name: web_seed_max_failures, class: stored, wire: "",
doc: "M186: Consecutive failures before permanently banning a web seed. Default: 10." }
{ name: initial_picker_threshold, class: stored, wire: "",
doc: "Completed piece count below which the picker uses random selection" }
{ name: whole_pieces_threshold, class: stored, wire: "",
doc: "Seconds to download a piece — if a peer is faster, it gets exclusive" }
{ name: snub_timeout_secs, class: stored, wire: "",
doc: "Seconds without data from a peer before marking it as snubbed." }
{ name: readahead_pieces, class: stored, wire: "",
doc: "Number of pieces ahead of the streaming cursor to prioritize (default: 8)." }
{ name: streaming_timeout_escalation, class: stored, wire: "",
doc: "Escalate streaming piece requests that exceed the mean RTT. Default: true." }
{ name: steal_threshold_ratio, class: stored, wire: "",
doc: "Steal blocks from peers this many times slower than the requesting peer (default: 10.0)." }
{ name: use_block_stealing, class: stored, wire: "",
doc: "Enable per-block stealing: fast peers can steal individual unrequested" }
{ name: steal_stale_piece_secs, class: stored, wire: "",
doc: "Seconds between steal-queue population scans. Every N seconds, all" }
{ name: steal_threshold_endgame, class: stored, wire: "",
doc: "M149: Steal threshold multiplier when >90% complete (endgame)." }
{ name: fixed_pipeline_depth, class: stored, wire: "",
doc: "Fixed per-peer pipeline depth (number of concurrent requests per peer)." }
{ name: piece_extent_affinity, class: stored, wire: "",
doc: "Prefer pieces adjacent to those already downloaded for improved sequential" }
{ name: suggest_mode, class: stored, wire: "",
doc: "Enable BEP 6 `SuggestPiece`: suggest newly verified pieces to peers that" }
{ name: max_suggest_pieces, class: stored, wire: "",
doc: "Maximum `SuggestPiece` messages per peer to avoid flooding (default: 10)." }
{ name: predictive_piece_announce_ms, class: stored, wire: "",
doc: "Predictive piece announce delay in milliseconds. When > 0, a Have message" }
{ name: proxy, class: stored, wire: "",
doc: "Proxy configuration for peer and tracker connections. Default: no proxy." }
{ name: force_proxy, class: restart, wire: "force_proxy",
doc: "Force all connections through the configured proxy. Disables listen" }
{ name: ip_filter_enabled, class: immediate, wire: "ip_filter_enabled",
doc: "Enable the IP filter (blocklist). Default: false." }
{ name: ip_filter_path, class: stored, wire: "",
doc: "Path to the IP filter file (e.g. `ipfilter.dat`). Default: empty." }
{ name: ip_filter_auto_refresh, class: immediate, wire: "ip_filter_auto_refresh",
doc: "Automatically refresh the IP filter when the file changes. Default: false." }
{ name: apply_ip_filter_to_trackers, class: stored, wire: "",
doc: "Check tracker IP addresses against the IP filter. When false, trackers" }
{ name: dht_queries_per_second, class: stored, wire: "",
doc: "Maximum DHT queries per second to control network traffic (default: 50)." }
{ name: dht_query_timeout_secs, class: stored, wire: "",
doc: "Timeout in seconds for a single DHT query before it is abandoned (default: 5)." }
{ name: dht_enforce_node_id, class: stored, wire: "",
doc: "BEP 42: Enforce node ID verification in DHT routing table." }
{ name: dht_restrict_routing_ips, class: stored, wire: "",
doc: "BEP 42: Restrict DHT routing table to one node per IP." }
{ name: dht_max_items, class: stored, wire: "",
doc: "Maximum number of BEP 44 items stored in the DHT (immutable + mutable)." }
{ name: dht_item_lifetime_secs, class: stored, wire: "",
doc: "Lifetime of BEP 44 DHT items in seconds before expiry (default: 7200 = 2 hours)." }
{ name: dht_sample_infohashes_interval, class: stored, wire: "",
doc: "Interval in seconds for periodic `sample_infohashes` queries (BEP 51)." }
{ name: dht_read_only, class: stored, wire: "",
doc: "BEP 43: Run DHT in read-only mode. Read-only nodes can query the DHT" }
{ name: upnp_lease_duration, class: stored, wire: "",
doc: "`UPnP` lease duration in seconds (default: 3600)." }
{ name: natpmp_lifetime, class: stored, wire: "",
doc: "NAT-PMP mapping lifetime in seconds (default: 7200)." }
{ name: utp_max_connections, class: stored, wire: "",
doc: "Maximum concurrent uTP connections (default: 256)." }
{ name: enable_i2p, class: stored, wire: "",
doc: "Enable I2P anonymous network support (requires SAM bridge)." }
{ name: i2p_hostname, class: stored, wire: "",
doc: "SAM bridge hostname (default: \"127.0.0.1\")." }
{ name: i2p_port, class: stored, wire: "",
doc: "SAM bridge port (default: 7656)." }
{ name: i2p_inbound_quantity, class: stored, wire: "",
doc: "Number of inbound I2P tunnels (1-16, default: 3)." }
{ name: i2p_outbound_quantity, class: stored, wire: "",
doc: "Number of outbound I2P tunnels (1-16, default: 3)." }
{ name: i2p_inbound_length, class: stored, wire: "",
doc: "Number of hops in inbound I2P tunnels (0-7, default: 3)." }
{ name: i2p_outbound_length, class: stored, wire: "",
doc: "Number of hops in outbound I2P tunnels (0-7, default: 3)." }
{ name: allow_i2p_mixed, class: stored, wire: "",
doc: "Allow mixing I2P and clearnet peers in the same torrent." }
{ name: ssl_listen_port, class: stored, wire: "",
doc: "SSL listen port for SSL torrent incoming connections." }
{ name: ssl_cert_path, class: stored, wire: "",
doc: "Path to the PEM-encoded certificate file for SSL torrent connections." }
{ name: ssl_key_path, class: stored, wire: "",
doc: "Path to the PEM-encoded private key file for SSL torrent connections." }
{ name: seed_choking_algorithm, class: stored, wire: "",
doc: "Algorithm for ranking peers during seed-mode choking." }
{ name: choking_algorithm, class: stored, wire: "",
doc: "Algorithm for determining the number of unchoke slots." }
{ name: max_peers_per_torrent, class: immediate, wire: "max_connec",
doc: "Maximum peer connections per torrent (default: 128). When `0`, falls back" }
{ name: pass0_grace_secs, class: stored, wire: "",
doc: "v0.187.3 / OV2 / 12A: seconds after a peer goes Live before Pass 0" }
{ name: proactive_evictions_per_minute_limit, class: stored, wire: "",
doc: "v0.187.3 / 3A: sliding-window cap on proactive evictions in any" }
{ name: eviction_ban_duration_secs, class: stored, wire: "",
doc: "v0.187.3: how long a Pass 0 eviction victim is blocked from" }
{ name: eviction_ban_set_cap, class: stored, wire: "",
doc: "v0.187.3 / OV4: FIFO cap on the banned-peer set. Default 1024 (was" }
{ name: peer_read_timeout_secs, class: stored, wire: "",
doc: "M133: Seconds without any wire message before disconnecting a peer." }
{ name: peer_write_timeout_secs, class: stored, wire: "",
doc: "M133: Seconds before a stalled outgoing write disconnects a peer." }
{ name: data_contribution_timeout_secs, class: stored, wire: "",
doc: "M137: Data contribution timeout — seconds without receiving a Piece" }
{ name: choke_rotation_max_evictions, class: stored, wire: "",
doc: "M138: Maximum peers to evict per choke rotation tick (0 = disabled)." }
{ name: max_concurrent_connects, class: stored, wire: "",
doc: "M138: Maximum concurrent outbound peer connections (throttles connect ramp)." }
{ name: connect_soft_timeout, class: stored, wire: "",
doc: "M147: Seconds without TCP SYN-ACK before soft reap disconnects a connecting" }
{ name: dispatch_backlog_cap, class: stored, wire: "",
doc: "M182: dispatch-channel backlog cap. The reader's `BackpressureQueue`" }
{ name: event_backlog_cap, class: stored, wire: "",
doc: "M182: event-channel backlog cap. Same role as" }
{ name: peer_writer_channel_cap, class: stored, wire: "",
doc: "M243 (L6): per-peer outbound writer channel capacity. The writer" }
{ name: use_actor_dispatch, class: stored, wire: "",
doc: "M187 A/B: use actor-centralised dispatch (true) or per-peer CAS dispatch (false)." }
{ name: web_seed_progress_throttle_ms, class: stored, wire: "",
doc: "M178: Minimum milliseconds between `PeerEvent::WebSeedProgress` emissions" }
{ name: ssrf_mitigation, class: stored, wire: "",
doc: "Enable SSRF mitigation: restrict localhost tracker paths, block" }
{ name: allow_idna, class: stored, wire: "",
doc: "Allow internationalised (non-ASCII) domain names in tracker/web seed URLs." }
{ name: validate_https_trackers, class: stored, wire: "",
doc: "Require HTTPS for HTTP tracker announces (UDP trackers are unaffected)." }
{ name: max_metadata_size, class: stored, wire: "",
doc: "Maximum BEP 9 metadata size in bytes that will be accepted from peers." }
{ name: max_message_size, class: stored, wire: "",
doc: "Maximum wire protocol message size in bytes. Messages exceeding this are" }
{ name: max_piece_length, class: stored, wire: "",
doc: "Maximum accepted piece length when adding a torrent. Rejects torrents" }
{ name: max_outstanding_requests, class: stored, wire: "",
doc: "Maximum outstanding incoming requests per peer. When a peer sends more" }
{ name: max_in_flight_pieces, class: stored, wire: "",
doc: "Maximum number of pieces simultaneously in-flight (downloaded but not" }
{ name: peer_connect_timeout, class: stored, wire: "",
doc: "Timeout in seconds for outbound TCP peer connections." }
{ name: peer_dscp, class: stored, wire: "",
doc: "DSCP (Differentiated Services Code Point) value for peer traffic sockets." }
{ name: stats_report_interval, class: stored, wire: "",
doc: "Interval in milliseconds between `SessionStatsAlert` emissions." }
{ name: runtime_worker_threads, class: stored, wire: "",
doc: "Number of tokio worker threads. Default: min(available cores, 8)." }
{ name: pin_cores, class: stored, wire: "",
doc: "Pin tokio worker threads to CPU cores for cache locality. Default: true." }
{ name: lock_warn_threshold_ms, class: stored, wire: "",
doc: "Warning threshold in milliseconds for lock hold duration." }
{ name: dht_saved_nodes, class: stored, wire: "",
doc: "Previously saved DHT routing table nodes for fast bootstrap." }
{ name: dht_node_id, class: stored, wire: "",
doc: "BEP 42-compliant DHT node ID from previous session." }
{ name: qbt_compat, class: stored, wire: "",
doc: "qBittorrent `WebUI` v2 compatibility layer (M168)." }
{ name: category_registry_path, class: stored, wire: "",
doc: "M170: Path to the qBt-compat category registry TOML file. When" }
{ name: tag_registry_path, class: stored, wire: "",
doc: "M171: Path to the qBt-compat tag registry TOML file. When `None`," }
{ name: notify_on_complete, class: immediate, wire: "notify_on_complete",
doc: "M226: Fire an OS desktop notification when a torrent finishes" }
{ name: notify_on_error, class: immediate, wire: "notify_on_error",
doc: "M226: Fire an OS desktop notification when a torrent enters an error" }
{ name: on_complete_program, class: immediate, wire: "on_complete_program",
doc: "M226: Path to a program to run on torrent completion (qBt parity" }
{ name: use_incomplete_dir, class: immediate, wire: "use_incomplete_dir",
doc: "M226: Whether in-progress downloads use a separate directory before" }
{ name: incomplete_dir, class: immediate, wire: "incomplete_dir",
doc: "M226: Directory for in-progress downloads (paired with" }
{ name: default_skip_hash_check, class: immediate, wire: "default_skip_hash_check",
doc: "M226: Default value for `AddTorrentParams.skip_checking` when the" }
{ name: incomplete_extension_enabled, class: immediate, wire: "incomplete_extension_enabled",
doc: "M226: Append `.!ut` to filenames during download (qBt convention to" }
{ name: watched_folder, class: immediate, wire: "watched_folder",
doc: "M226: Path to a folder to watch for new `.torrent` files; on detection" }
{ name: delete_torrent_after_add, class: immediate, wire: "delete_torrent_after_add",
doc: "M226: After successfully adding a `.torrent` file from `watched_folder`," }
{ name: move_completed_enabled, class: immediate, wire: "move_completed_enabled",
doc: "M226: Whether to move completed torrents to `move_completed_to`." }
{ name: move_completed_to, class: immediate, wire: "move_completed_to",
doc: "M226: Destination for completed torrents (paired with" }
{ name: web_ui_https_enabled, class: immediate, wire: "web_ui_https_enabled",
doc: "M226: Enable `HTTPS` for the qBt v2 `WebUI` listener. STORED ONLY —" }
{ name: network_interface, class: immediate, wire: "network_interface",
doc: "M226: Bind peer listeners to a specific network interface (qBt" }
{ name: default_add_paused, class: immediate, wire: "default_add_paused",
doc: "M226: When `AddTorrentParams.paused` is `None`, this default decides" }
}
};
}
#[macro_export]
macro_rules! for_each_qbt_compat_setting {
($emit:ident) => {
$emit! {
{ name: enabled, class: stored, wire: "",
doc: "Master enable flag. When `false`, all `/api/v2/*` routes return 404" }
{ name: username, class: stored, wire: "",
doc: "Username required for qBt v2 login. Default: `\"admin\"` (qBt factory" }
{ name: password_hash, class: stored, wire: "",
doc: "Argon2id PHC-format password hash (M172a). Example:" }
{ name: password, class: stored, wire: "",
doc: "Legacy plaintext password — **deprecated, migration-only**. Populated" }
{ name: spoof_app_version, class: stored, wire: "",
doc: "Version string returned by `GET /api/v2/app/version`. Must match the" }
{ name: spoof_webapi_version, class: stored, wire: "",
doc: "Version string returned by `GET /api/v2/app/webapiVersion`. Must match" }
{ name: session_ttl_secs, class: stored, wire: "",
doc: "Session cookie TTL in seconds. Bounds: `[60, 604_800]` (1 minute to" }
{ name: max_sessions, class: stored, wire: "",
doc: "Maximum concurrent sessions. Prevents unbounded growth on login" }
{ name: max_concurrent_argon2_ops, class: stored, wire: "",
doc: "Optional override for the global argon2 verification semaphore size" }
{ name: port, class: restart, wire: "webui_port",
doc: "v0.187.3 / 2A: TCP port the Web UI listens on. Single source of truth" }
{ name: bind_address, class: restart, wire: "webui_bind",
doc: "v0.187.3 / 2A: bind address the Web UI listens on. Single source of" }
{ name: csrf_protection_enabled, class: immediate, wire: "web_ui_csrf_protection_enabled",
doc: "M172a Lane B: enable Origin/Referer CSRF checks on mutating requests" }
{ name: host_header_validation_enabled, class: immediate, wire: "web_ui_host_header_validation_enabled",
doc: "M172a Lane B: enable Host-header validation against Origin/Referer." }
{ name: web_ui_reverse_proxy_enabled, class: immediate, wire: "web_ui_reverse_proxy_enabled",
doc: "M172a Lane B: when true, the CSRF middleware resolves the real client" }
{ name: web_ui_reverse_proxies_list, class: immediate, wire: "web_ui_reverse_proxies_list",
doc: "M172a Lane B: list of CIDRs trusted to supply `X-Forwarded-For` and" }
{ name: max_failed_auth_count, class: immediate, wire: "web_ui_max_auth_fail_count",
doc: "Maximum number of failed `auth/login` attempts from a single source" }
{ name: ban_duration_secs, class: immediate, wire: "web_ui_ban_duration",
doc: "Ban duration (seconds) after hitting [`Self::max_failed_auth_count`]." }
{ name: bypass_local_auth, class: immediate, wire: "bypass_local_auth",
doc: "When `true`, any request whose resolved client IP is loopback" }
{ name: bypass_auth_subnet_whitelist, class: immediate, wire: "bypass_auth_subnet_whitelist",
doc: "CIDR strings whose resolved client IP bypasses authentication" }
{ name: brute_force_registry_capacity, class: immediate, wire: "brute_force_registry_capacity",
doc: "Optional override for the brute-force-ban registry's LRU capacity." }
}
};
}
#[macro_export]
macro_rules! for_each_proxy_setting {
($emit:ident) => {
$emit! {
{ name: proxy_type, class: restart, wire: "proxy_type",
doc: "Proxy protocol to use." }
{ name: hostname, class: restart, wire: "proxy_ip",
doc: "Proxy server hostname or IP address." }
{ name: port, class: restart, wire: "proxy_port",
doc: "Proxy server port." }
{ name: username, class: restart, wire: "proxy_username",
doc: "Username for authenticated proxy types." }
{ name: password, class: restart, wire: "proxy_password",
doc: "Password for authenticated proxy types." }
{ name: proxy_peer_connections, class: restart, wire: "proxy_peer_connections",
doc: "Route peer connections (incl. web seeds) through proxy." }
{ name: proxy_tracker_connections, class: stored, wire: "",
doc: "Route tracker HTTP connections through proxy." }
{ name: proxy_hostnames, class: restart, wire: "proxy_hostnames",
doc: "Resolve hostnames through proxy (SOCKS5/HTTP only)." }
{ name: socks5_udp_send_local_ep, class: stored, wire: "",
doc: "Include local endpoint in SOCKS5 UDP ASSOCIATE." }
}
};
}
#[macro_export]
macro_rules! for_each_delta_setting {
($emit:ident) => {
$emit! {
{ src: enable_dht, delta: enable_dht, clone: false }
{ src: enable_pex, delta: enable_pex, clone: false }
{ src: max_peers_per_torrent, delta: max_peers, clone: false }
{ src: seed_ratio_limit, delta: seed_ratio_limit, clone: false }
{ src: seed_time_limit_secs, delta: seed_time_limit_secs, clone: false }
{ src: inactive_seed_time_limit_secs, delta: inactive_seed_time_limit_secs, clone: false }
{ src: max_ratio_action, delta: max_ratio_action, clone: false }
{ src: encryption_mode, delta: encryption_mode, clone: false }
{ src: anonymous_mode, delta: anonymous_mode, clone: false }
{ src: max_uploads_per_torrent, delta: max_uploads_per_torrent, clone: false }
{ src: save_resume_interval_secs, delta: save_resume_interval_secs, clone: false }
{ src: hashing_threads, delta: hashing_threads, clone: false }
{ src: ip_filter_enabled, delta: ip_filter_enabled, clone: false }
{ src: notify_on_complete, delta: notify_on_complete, clone: false }
{ src: notify_on_error, delta: notify_on_error, clone: false }
{ src: on_complete_program, delta: on_complete_program, clone: true }
{ src: use_incomplete_dir, delta: use_incomplete_dir, clone: false }
{ src: incomplete_dir, delta: incomplete_dir, clone: true }
{ src: default_skip_hash_check, delta: default_skip_hash_check, clone: false }
{ src: incomplete_extension_enabled, delta: incomplete_extension_enabled, clone: false }
{ src: watched_folder, delta: watched_folder, clone: true }
{ src: delete_torrent_after_add, delta: delete_torrent_after_add, clone: false }
{ src: move_completed_enabled, delta: move_completed_enabled, clone: false }
{ src: move_completed_to, delta: move_completed_to, clone: true }
{ src: ip_filter_auto_refresh, delta: ip_filter_auto_refresh, clone: false }
{ src: web_ui_https_enabled, delta: web_ui_https_enabled, clone: false }
{ src: network_interface, delta: network_interface, clone: true }
{ src: default_add_paused, delta: default_add_paused, clone: false }
}
};
}
macro_rules! emit_settings_lock {
( $({ name: $name:ident, class: $class:tt, wire: $wire:literal, doc: $doc:literal })* ) => {
#[allow(dead_code)]
fn _settings_completeness_lock(s: &crate::Settings) {
let crate::Settings { $($name: _,)* } = s;
}
};
}
crate::for_each_setting!(emit_settings_lock);
macro_rules! emit_qbt_lock {
( $({ name: $name:ident, class: $class:tt, wire: $wire:literal, doc: $doc:literal })* ) => {
#[allow(dead_code)]
fn _qbt_completeness_lock(s: &crate::QbtCompatSettings) {
let crate::QbtCompatSettings { $($name: _,)* } = s;
}
};
}
crate::for_each_qbt_compat_setting!(emit_qbt_lock);
macro_rules! emit_proxy_lock {
( $({ name: $name:ident, class: $class:tt, wire: $wire:literal, doc: $doc:literal })* ) => {
#[allow(dead_code)]
fn _proxy_completeness_lock(s: &irontide_core::ProxyConfig) {
let irontide_core::ProxyConfig { $($name: _,)* } = s;
}
};
}
crate::for_each_proxy_setting!(emit_proxy_lock);
#[cfg(test)]
mod registry_consistency {
use std::collections::HashSet;
macro_rules! collect_meta {
( $({ name: $name:ident, class: $class:tt, wire: $wire:literal, doc: $doc:literal })* ) => {{
let out: Vec<(&'static str, &'static str, &'static str)> = vec![
$( (stringify!($class), stringify!($name), $wire), )*
];
out
}};
}
fn all_meta() -> Vec<(&'static str, &'static str, &'static str)> {
let mut v = crate::for_each_setting!(collect_meta);
v.extend(crate::for_each_qbt_compat_setting!(collect_meta));
v.extend(crate::for_each_proxy_setting!(collect_meta));
v
}
#[test]
fn class_wire_consistency() {
for (class, name, wire) in all_meta() {
match class {
"immediate" | "restart" => assert!(
!wire.is_empty(),
"{name} ({class}) must carry a non-empty wire-name"
),
"stored" => assert!(
wire.is_empty(),
"{name} (stored) must have an empty wire-name, got {wire:?}"
),
other => panic!("{name}: unknown class `{other}`"),
}
}
}
#[test]
fn wire_names_unique() {
let wires: Vec<&str> = all_meta()
.into_iter()
.map(|(_, _, w)| w)
.filter(|w| !w.is_empty())
.collect();
let set: HashSet<&str> = wires.iter().copied().collect();
assert_eq!(
wires.len(),
set.len(),
"duplicate wire-name in the registry"
);
}
#[test]
fn immediate_wire_set_pinned() {
let got: HashSet<&str> = all_meta()
.into_iter()
.filter(|(c, _, _)| *c == "immediate")
.map(|(_, _, w)| w)
.collect();
let expected: HashSet<&str> = [
"dl_limit",
"up_limit",
"max_connec",
"max_ratio_act",
"create_subfolder_enabled",
"auto_tmm_enabled",
"queueing_enabled",
"max_ratio",
"listen_port",
"dht",
"lsd",
"max_connec_global",
"max_uploads_per_torrent",
"max_seeding_time",
"max_inactive_seeding_time",
"save_resume_interval",
"hashing_threads",
"ip_filter_enabled",
"notify_on_complete",
"notify_on_error",
"on_complete_program",
"use_incomplete_dir",
"incomplete_dir",
"default_skip_hash_check",
"incomplete_extension_enabled",
"watched_folder",
"delete_torrent_after_add",
"move_completed_enabled",
"move_completed_to",
"ip_filter_auto_refresh",
"web_ui_https_enabled",
"network_interface",
"default_add_paused",
"web_ui_csrf_protection_enabled",
"web_ui_host_header_validation_enabled",
"web_ui_reverse_proxy_enabled",
"web_ui_reverse_proxies_list",
"web_ui_max_auth_fail_count",
"web_ui_ban_duration",
"bypass_local_auth",
"bypass_auth_subnet_whitelist",
"brute_force_registry_capacity",
]
.into_iter()
.collect();
assert_eq!(got, expected, "classify_immediate wire-name SET drifted");
}
#[test]
fn restart_wire_set_pinned() {
let got: HashSet<&str> = all_meta()
.into_iter()
.filter(|(c, _, _)| *c == "restart")
.map(|(_, _, w)| w)
.collect();
let expected: HashSet<&str> = [
"pex",
"encryption",
"anonymous_mode",
"save_path",
"upnp",
"natpmp",
"force_proxy",
"webui_port",
"webui_bind",
"proxy_type",
"proxy_ip",
"proxy_port",
"proxy_username",
"proxy_password",
"proxy_peer_connections",
"proxy_hostnames",
]
.into_iter()
.collect();
assert_eq!(
got, expected,
"classify_restart_required wire-name SET drifted"
);
}
}