diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8e7209d..48aa866 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -52,6 +52,10 @@ By submitting a PR, you confirm that: AI-generated code is treated as **draft** and must be validated like any other external contribution. +The problem isn’t AI as a tool, but the dilution of responsibility. If the commit history says "Claude/GPT authored this", then who is accountable for the bug? Claude? GPT? Anthropic? OpenAI? Samuel Altman? + +The user who didn’t read the diff? No one? But, in a sensitive system, *"no one"* is an unacceptable maintainer model. + PRs that look like unverified AI dumps WILL be closed --- @@ -79,4 +83,4 @@ This includes (but is not limited to): - unverified or low-effort changes - inability to explain the change -These actions follow the Code of Conduct and are intended to preserve signal, quality, and Telemt's integrity \ No newline at end of file +These actions follow the Code of Conduct and are intended to preserve signal, quality, and Telemt's integrity diff --git a/docs/Config_params/CONFIG_PARAMS.en.md b/docs/Config_params/CONFIG_PARAMS.en.md index 61d305a..5bb1c80 100644 --- a/docs/Config_params/CONFIG_PARAMS.en.md +++ b/docs/Config_params/CONFIG_PARAMS.en.md @@ -10,6 +10,8 @@ This document lists all configuration keys accepted by `config.toml`. > > The configuration parameters detailed in this document are intended for advanced users and fine-tuning purposes. Modifying these settings without a clear understanding of their function may lead to application instability or other unexpected behavior. Please proceed with caution and at your own risk. +> `Hot-Reload` marks whether a changed value is applied by the config watcher without restarting the process; `✘` means restart is required for runtime effect. + # Table of contents - [Top-level keys](#top-level-keys) - [general](#general) @@ -29,12 +31,16 @@ This document lists all configuration keys accepted by `config.toml`. # Top-level keys -| Key | Type | Default | -| --- | ---- | ------- | -| [`include`](#include) | `String` (special directive) | — | -| [`show_link`](#show_link) | `"*"` or `String[]` | `[]` (`ShowLink::None`) | -| [`dc_overrides`](#dc_overrides) | `Map` | `{}` | -| [`default_dc`](#default_dc) | `u8` | — (effective fallback: `2` in ME routing) | +| Key | Type | Default | Hot-Reload | +| --- | ---- | ------- | ---------- | +| [`include`](#include) | `String` (special directive) | — | `✔` | +| [`show_link`](#show_link) | `"*"` or `String[]` | `[]` (`ShowLink::None`) | `✘` | +| [`dc_overrides`](#dc_overrides) | `Map` | `{}` | `✘` | +| [`default_dc`](#default_dc) | `u8` | — (effective fallback: `2` in ME routing) | `✘` | +| [`beobachten`](#beobachten) | `bool` | `true` | `✘` | +| [`beobachten_minutes`](#beobachten_minutes) | `u64` | `10` | `✘` | +| [`beobachten_flush_secs`](#beobachten_flush_secs) | `u64` | `15` | `✘` | +| [`beobachten_file`](#beobachten_file) | `String` | `"cache/beobachten.txt"` | `✘` | ## include - **Constraints / validation**: Must be a single-line directive in the form `include = "path/to/file.toml"`. Includes are expanded before TOML parsing. Maximum include depth is 10. @@ -218,6 +224,151 @@ This document lists all configuration keys accepted by `config.toml`. | [`auto_degradation_enabled`](#auto_degradation_enabled) | `bool` | `true` | | [`degradation_min_unavailable_dc_groups`](#degradation_min_unavailable_dc_groups) | `u8` | `2` | | [`rst_on_close`](#rst_on_close) | `"off"`, `"errors"`, or `"always"` | `"off"` | +| Key | Type | Default | Hot-Reload | +| --- | ---- | ------- | ---------- | +| [`data_path`](#data_path) | `String` | — | `✘` | +| [`quota_state_path`](#quota_state_path) | `Path` | `"telemt.limit.json"` | `✘` | +| [`config_strict`](#config_strict) | `bool` | `false` | `✘` | +| [`prefer_ipv6`](#prefer_ipv6) | `bool` | `false` | `✘` | +| [`fast_mode`](#fast_mode) | `bool` | `true` | `✘` | +| [`use_middle_proxy`](#use_middle_proxy) | `bool` | `true` | `✘` | +| [`proxy_secret_path`](#proxy_secret_path) | `String` | `"proxy-secret"` | `✘` | +| [`proxy_secret_url`](#proxy_secret_url) | `String` | `"https://core.telegram.org/getProxySecret"` | `✘` | +| [`proxy_config_v4_cache_path`](#proxy_config_v4_cache_path) | `String` | `"cache/proxy-config-v4.txt"` | `✘` | +| [`proxy_config_v4_url`](#proxy_config_v4_url) | `String` | `"https://core.telegram.org/getProxyConfig"` | `✘` | +| [`proxy_config_v6_cache_path`](#proxy_config_v6_cache_path) | `String` | `"cache/proxy-config-v6.txt"` | `✘` | +| [`proxy_config_v6_url`](#proxy_config_v6_url) | `String` | `"https://core.telegram.org/getProxyConfigV6"` | `✘` | +| [`ad_tag`](#ad_tag) | `String` | — | `✔` | +| [`middle_proxy_nat_ip`](#middle_proxy_nat_ip) | `IpAddr` | — | `✘` | +| [`middle_proxy_nat_probe`](#middle_proxy_nat_probe) | `bool` | `true` | `✘` | +| [`middle_proxy_nat_stun`](#middle_proxy_nat_stun) | `String` | — | `✘` | +| [`middle_proxy_nat_stun_servers`](#middle_proxy_nat_stun_servers) | `String[]` | `[]` | `✘` | +| [`stun_nat_probe_concurrency`](#stun_nat_probe_concurrency) | `usize` | `8` | `✘` | +| [`middle_proxy_pool_size`](#middle_proxy_pool_size) | `usize` | `8` | `✘` | +| [`middle_proxy_warm_standby`](#middle_proxy_warm_standby) | `usize` | `16` | `✘` | +| [`me_init_retry_attempts`](#me_init_retry_attempts) | `u32` | `0` | `✘` | +| [`me2dc_fallback`](#me2dc_fallback) | `bool` | `true` | `✘` | +| [`me2dc_fast`](#me2dc_fast) | `bool` | `false` | `✘` | +| [`me_keepalive_enabled`](#me_keepalive_enabled) | `bool` | `true` | `✘` | +| [`me_keepalive_interval_secs`](#me_keepalive_interval_secs) | `u64` | `8` | `✘` | +| [`me_keepalive_jitter_secs`](#me_keepalive_jitter_secs) | `u64` | `2` | `✘` | +| [`me_keepalive_payload_random`](#me_keepalive_payload_random) | `bool` | `true` | `✘` | +| [`rpc_proxy_req_every`](#rpc_proxy_req_every) | `u64` | `0` | `✘` | +| [`me_writer_cmd_channel_capacity`](#me_writer_cmd_channel_capacity) | `usize` | `4096` | `✘` | +| [`me_route_channel_capacity`](#me_route_channel_capacity) | `usize` | `768` | `✘` | +| [`me_c2me_channel_capacity`](#me_c2me_channel_capacity) | `usize` | `1024` | `✘` | +| [`me_c2me_send_timeout_ms`](#me_c2me_send_timeout_ms) | `u64` | `4000` | `✘` | +| [`me_reader_route_data_wait_ms`](#me_reader_route_data_wait_ms) | `u64` | `2` | `✔` | +| [`me_d2c_flush_batch_max_frames`](#me_d2c_flush_batch_max_frames) | `usize` | `32` | `✔` | +| [`me_d2c_flush_batch_max_bytes`](#me_d2c_flush_batch_max_bytes) | `usize` | `131072` | `✔` | +| [`me_d2c_flush_batch_max_delay_us`](#me_d2c_flush_batch_max_delay_us) | `u64` | `500` | `✔` | +| [`me_d2c_ack_flush_immediate`](#me_d2c_ack_flush_immediate) | `bool` | `true` | `✔` | +| [`me_quota_soft_overshoot_bytes`](#me_quota_soft_overshoot_bytes) | `u64` | `65536` | `✔` | +| [`me_d2c_frame_buf_shrink_threshold_bytes`](#me_d2c_frame_buf_shrink_threshold_bytes) | `usize` | `262144` | `✔` | +| [`direct_relay_copy_buf_c2s_bytes`](#direct_relay_copy_buf_c2s_bytes) | `usize` | `65536` | `✔` | +| [`direct_relay_copy_buf_s2c_bytes`](#direct_relay_copy_buf_s2c_bytes) | `usize` | `262144` | `✔` | +| [`crypto_pending_buffer`](#crypto_pending_buffer) | `usize` | `262144` | `✘` | +| [`max_client_frame`](#max_client_frame) | `usize` | `16777216` | `✘` | +| [`desync_all_full`](#desync_all_full) | `bool` | `false` | `✔` | +| [`beobachten`](#beobachten) | `bool` | `true` | `✘` | +| [`beobachten_minutes`](#beobachten_minutes) | `u64` | `10` | `✘` | +| [`beobachten_flush_secs`](#beobachten_flush_secs) | `u64` | `15` | `✘` | +| [`beobachten_file`](#beobachten_file) | `String` | `"cache/beobachten.txt"` | `✘` | +| [`hardswap`](#hardswap) | `bool` | `true` | `✔` | +| [`me_warmup_stagger_enabled`](#me_warmup_stagger_enabled) | `bool` | `true` | `✘` | +| [`me_warmup_step_delay_ms`](#me_warmup_step_delay_ms) | `u64` | `500` | `✘` | +| [`me_warmup_step_jitter_ms`](#me_warmup_step_jitter_ms) | `u64` | `300` | `✘` | +| [`me_reconnect_max_concurrent_per_dc`](#me_reconnect_max_concurrent_per_dc) | `u32` | `8` | `✘` | +| [`me_reconnect_backoff_base_ms`](#me_reconnect_backoff_base_ms) | `u64` | `500` | `✘` | +| [`me_reconnect_backoff_cap_ms`](#me_reconnect_backoff_cap_ms) | `u64` | `30000` | `✘` | +| [`me_reconnect_fast_retry_count`](#me_reconnect_fast_retry_count) | `u32` | `16` | `✘` | +| [`me_single_endpoint_shadow_writers`](#me_single_endpoint_shadow_writers) | `u8` | `2` | `✔` | +| [`me_single_endpoint_outage_mode_enabled`](#me_single_endpoint_outage_mode_enabled) | `bool` | `true` | `✔` | +| [`me_single_endpoint_outage_disable_quarantine`](#me_single_endpoint_outage_disable_quarantine) | `bool` | `true` | `✔` | +| [`me_single_endpoint_outage_backoff_min_ms`](#me_single_endpoint_outage_backoff_min_ms) | `u64` | `250` | `✔` | +| [`me_single_endpoint_outage_backoff_max_ms`](#me_single_endpoint_outage_backoff_max_ms) | `u64` | `3000` | `✔` | +| [`me_single_endpoint_shadow_rotate_every_secs`](#me_single_endpoint_shadow_rotate_every_secs) | `u64` | `900` | `✔` | +| [`me_floor_mode`](#me_floor_mode) | `"static"` or `"adaptive"` | `"adaptive"` | `✔` | +| [`me_adaptive_floor_idle_secs`](#me_adaptive_floor_idle_secs) | `u64` | `90` | `✔` | +| [`me_adaptive_floor_min_writers_single_endpoint`](#me_adaptive_floor_min_writers_single_endpoint) | `u8` | `1` | `✔` | +| [`me_adaptive_floor_min_writers_multi_endpoint`](#me_adaptive_floor_min_writers_multi_endpoint) | `u8` | `1` | `✔` | +| [`me_adaptive_floor_recover_grace_secs`](#me_adaptive_floor_recover_grace_secs) | `u64` | `180` | `✔` | +| [`me_adaptive_floor_writers_per_core_total`](#me_adaptive_floor_writers_per_core_total) | `u16` | `48` | `✔` | +| [`me_adaptive_floor_cpu_cores_override`](#me_adaptive_floor_cpu_cores_override) | `u16` | `0` | `✔` | +| [`me_adaptive_floor_max_extra_writers_single_per_core`](#me_adaptive_floor_max_extra_writers_single_per_core) | `u16` | `1` | `✔` | +| [`me_adaptive_floor_max_extra_writers_multi_per_core`](#me_adaptive_floor_max_extra_writers_multi_per_core) | `u16` | `2` | `✔` | +| [`me_adaptive_floor_max_active_writers_per_core`](#me_adaptive_floor_max_active_writers_per_core) | `u16` | `64` | `✔` | +| [`me_adaptive_floor_max_warm_writers_per_core`](#me_adaptive_floor_max_warm_writers_per_core) | `u16` | `64` | `✔` | +| [`me_adaptive_floor_max_active_writers_global`](#me_adaptive_floor_max_active_writers_global) | `u32` | `256` | `✔` | +| [`me_adaptive_floor_max_warm_writers_global`](#me_adaptive_floor_max_warm_writers_global) | `u32` | `256` | `✔` | +| [`upstream_connect_retry_attempts`](#upstream_connect_retry_attempts) | `u32` | `2` | `✘` | +| [`upstream_connect_retry_backoff_ms`](#upstream_connect_retry_backoff_ms) | `u64` | `100` | `✘` | +| [`upstream_connect_budget_ms`](#upstream_connect_budget_ms) | `u64` | `3000` | `✘` | +| [`tg_connect`](#tg_connect) | `u64` | `10` | `✘` | +| [`upstream_unhealthy_fail_threshold`](#upstream_unhealthy_fail_threshold) | `u32` | `5` | `✘` | +| [`upstream_connect_failfast_hard_errors`](#upstream_connect_failfast_hard_errors) | `bool` | `false` | `✘` | +| [`stun_iface_mismatch_ignore`](#stun_iface_mismatch_ignore) | `bool` | `false` | `✘` | +| [`unknown_dc_log_path`](#unknown_dc_log_path) | `String` | `"unknown-dc.txt"` | `✘` | +| [`unknown_dc_file_log_enabled`](#unknown_dc_file_log_enabled) | `bool` | `false` | `✘` | +| [`log_level`](#log_level) | `"debug"`, `"verbose"`, `"normal"`, or `"silent"` | `"normal"` | `✔` | +| [`disable_colors`](#disable_colors) | `bool` | `false` | `✘` | +| [`me_socks_kdf_policy`](#me_socks_kdf_policy) | `"strict"` or `"compat"` | `"strict"` | `✔` | +| [`me_route_backpressure_enabled`](#me_route_backpressure_enabled) | `bool` | `false` | `✔` | +| [`me_route_fairshare_enabled`](#me_route_fairshare_enabled) | `bool` | `false` | `✔` | +| [`me_route_backpressure_base_timeout_ms`](#me_route_backpressure_base_timeout_ms) | `u64` | `25` | `✔` | +| [`me_route_backpressure_high_timeout_ms`](#me_route_backpressure_high_timeout_ms) | `u64` | `120` | `✔` | +| [`me_route_backpressure_high_watermark_pct`](#me_route_backpressure_high_watermark_pct) | `u8` | `80` | `✔` | +| [`me_health_interval_ms_unhealthy`](#me_health_interval_ms_unhealthy) | `u64` | `1000` | `✔` | +| [`me_health_interval_ms_healthy`](#me_health_interval_ms_healthy) | `u64` | `3000` | `✔` | +| [`me_admission_poll_ms`](#me_admission_poll_ms) | `u64` | `1000` | `✔` | +| [`me_warn_rate_limit_ms`](#me_warn_rate_limit_ms) | `u64` | `5000` | `✔` | +| [`me_route_no_writer_mode`](#me_route_no_writer_mode) | `"async_recovery_failfast"`, `"inline_recovery_legacy"`, or `"hybrid_async_persistent"` | `"hybrid_async_persistent"` | `✘` | +| [`me_route_no_writer_wait_ms`](#me_route_no_writer_wait_ms) | `u64` | `250` | `✘` | +| [`me_route_hybrid_max_wait_ms`](#me_route_hybrid_max_wait_ms) | `u64` | `3000` | `✘` | +| [`me_route_blocking_send_timeout_ms`](#me_route_blocking_send_timeout_ms) | `u64` | `250` | `✘` | +| [`me_route_inline_recovery_attempts`](#me_route_inline_recovery_attempts) | `u32` | `3` | `✘` | +| [`me_route_inline_recovery_wait_ms`](#me_route_inline_recovery_wait_ms) | `u64` | `3000` | `✘` | +| [`fast_mode_min_tls_record`](#fast_mode_min_tls_record) | `usize` | `0` | `✘` | +| [`update_every`](#update_every) | `u64` | `300` | `✔` | +| [`me_reinit_every_secs`](#me_reinit_every_secs) | `u64` | `900` | `✔` | +| [`me_hardswap_warmup_delay_min_ms`](#me_hardswap_warmup_delay_min_ms) | `u64` | `1000` | `✔` | +| [`me_hardswap_warmup_delay_max_ms`](#me_hardswap_warmup_delay_max_ms) | `u64` | `2000` | `✔` | +| [`me_hardswap_warmup_extra_passes`](#me_hardswap_warmup_extra_passes) | `u8` | `3` | `✔` | +| [`me_hardswap_warmup_pass_backoff_base_ms`](#me_hardswap_warmup_pass_backoff_base_ms) | `u64` | `500` | `✔` | +| [`me_config_stable_snapshots`](#me_config_stable_snapshots) | `u8` | `2` | `✔` | +| [`me_config_apply_cooldown_secs`](#me_config_apply_cooldown_secs) | `u64` | `300` | `✔` | +| [`me_snapshot_require_http_2xx`](#me_snapshot_require_http_2xx) | `bool` | `true` | `✔` | +| [`me_snapshot_reject_empty_map`](#me_snapshot_reject_empty_map) | `bool` | `true` | `✔` | +| [`me_snapshot_min_proxy_for_lines`](#me_snapshot_min_proxy_for_lines) | `u32` | `1` | `✔` | +| [`proxy_secret_stable_snapshots`](#proxy_secret_stable_snapshots) | `u8` | `2` | `✔` | +| [`proxy_secret_rotate_runtime`](#proxy_secret_rotate_runtime) | `bool` | `true` | `✔` | +| [`me_secret_atomic_snapshot`](#me_secret_atomic_snapshot) | `bool` | `true` | `✔` | +| [`proxy_secret_len_max`](#proxy_secret_len_max) | `usize` | `256` | `✔` | +| [`me_pool_drain_ttl_secs`](#me_pool_drain_ttl_secs) | `u64` | `90` | `✔` | +| [`me_instadrain`](#me_instadrain) | `bool` | `false` | `✔` | +| [`me_pool_drain_threshold`](#me_pool_drain_threshold) | `u64` | `32` | `✔` | +| [`me_pool_drain_soft_evict_enabled`](#me_pool_drain_soft_evict_enabled) | `bool` | `true` | `✘` | +| [`me_pool_drain_soft_evict_grace_secs`](#me_pool_drain_soft_evict_grace_secs) | `u64` | `10` | `✘` | +| [`me_pool_drain_soft_evict_per_writer`](#me_pool_drain_soft_evict_per_writer) | `u8` | `2` | `✘` | +| [`me_pool_drain_soft_evict_budget_per_core`](#me_pool_drain_soft_evict_budget_per_core) | `u16` | `16` | `✘` | +| [`me_pool_drain_soft_evict_cooldown_ms`](#me_pool_drain_soft_evict_cooldown_ms) | `u64` | `1000` | `✘` | +| [`me_bind_stale_mode`](#me_bind_stale_mode) | `"never"`, `"ttl"`, or `"always"` | `"ttl"` | `✔` | +| [`me_bind_stale_ttl_secs`](#me_bind_stale_ttl_secs) | `u64` | `90` | `✔` | +| [`me_pool_min_fresh_ratio`](#me_pool_min_fresh_ratio) | `f32` | `0.8` | `✔` | +| [`me_reinit_drain_timeout_secs`](#me_reinit_drain_timeout_secs) | `u64` | `90` | `✔` | +| [`proxy_secret_auto_reload_secs`](#proxy_secret_auto_reload_secs) | `u64` | `3600` | `✔` | +| [`proxy_config_auto_reload_secs`](#proxy_config_auto_reload_secs) | `u64` | `3600` | `✔` | +| [`me_reinit_singleflight`](#me_reinit_singleflight) | `bool` | `true` | `✔` | +| [`me_reinit_trigger_channel`](#me_reinit_trigger_channel) | `usize` | `64` | `✘` | +| [`me_reinit_coalesce_window_ms`](#me_reinit_coalesce_window_ms) | `u64` | `200` | `✔` | +| [`me_deterministic_writer_sort`](#me_deterministic_writer_sort) | `bool` | `true` | `✔` | +| [`me_writer_pick_mode`](#me_writer_pick_mode) | `"sorted_rr"` or `"p2c"` | `"p2c"` | `✔` | +| [`me_writer_pick_sample_size`](#me_writer_pick_sample_size) | `u8` | `3` | `✔` | +| [`ntp_check`](#ntp_check) | `bool` | `true` | `✘` | +| [`ntp_servers`](#ntp_servers) | `String[]` | `["pool.ntp.org"]` | `✘` | +| [`auto_degradation_enabled`](#auto_degradation_enabled) | `bool` | `true` | `✘` | +| [`degradation_min_unavailable_dc_groups`](#degradation_min_unavailable_dc_groups) | `u8` | `2` | `✘` | +| [`rst_on_close`](#rst_on_close) | `"off"`, `"errors"`, or `"always"` | `"off"` | `✘` | ## data_path - **Constraints / validation**: `String` (optional). @@ -228,6 +379,24 @@ This document lists all configuration keys accepted by `config.toml`. [general] data_path = "/var/lib/telemt" ``` +## quota_state_path + - **Constraints / validation**: `Path`. Relative paths are resolved from the process working directory. + - **Description**: JSON state file used to persist runtime per-user quota consumption. + - **Example**: + + ```toml + [general] + quota_state_path = "telemt.limit.json" + ``` +## config_strict + - **Constraints / validation**: `bool`. + - **Description**: Rejects unknown TOML keys during config load. Startup fails fast; hot-reload rejects the new snapshot and keeps the current config. + - **Example**: + + ```toml + [general] + config_strict = true + ``` ## prefer_ipv6 - **Constraints / validation**: Deprecated. Use `network.prefer`. - **Description**: Deprecated legacy IPv6 preference flag migrated to `network.prefer`. @@ -905,6 +1074,15 @@ This document lists all configuration keys accepted by `config.toml`. [general] upstream_connect_budget_ms = 3000 ``` +## tg_connect + - **Constraints / validation**: Must be `> 0` (seconds). + - **Description**: Upstream Telegram connect timeout. + - **Example**: + + ```toml + [general] + tg_connect = 10 + ``` ## upstream_unhealthy_fail_threshold - **Constraints / validation**: Must be `> 0`. - **Description**: Consecutive failed requests before upstream is marked unhealthy. @@ -1520,11 +1698,11 @@ This document lists all configuration keys accepted by `config.toml`. # [general.modes] -| Key | Type | Default | -| --- | ---- | ------- | -| [`classic`](#classic) | `bool` | `false` | -| [`secure`](#secure) | `bool` | `false` | -| [`tls`](#tls) | `bool` | `true` | +| Key | Type | Default | Hot-Reload | +| --- | ---- | ------- | ---------- | +| [`classic`](#classic) | `bool` | `false` | `✘` | +| [`secure`](#secure) | `bool` | `false` | `✘` | +| [`tls`](#tls) | `bool` | `true` | `✘` | ## classic - **Constraints / validation**: `bool`. @@ -1558,11 +1736,11 @@ This document lists all configuration keys accepted by `config.toml`. # [general.links] -| Key | Type | Default | -| --- | ---- | ------- | -| [`show`](#show) | `"*"` or `String[]` | `"*"` | -| [`public_host`](#public_host) | `String` | — | -| [`public_port`](#public_port) | `u16` | — | +| Key | Type | Default | Hot-Reload | +| --- | ---- | ------- | ---------- | +| [`show`](#show) | `"*"` or `String[]` | `"*"` | `✘` | +| [`public_host`](#public_host) | `String` | — | `✘` | +| [`public_port`](#public_port) | `u16` | — | `✘` | ## show - **Constraints / validation**: `"*"` or `String[]`. An empty array means "show none". @@ -1598,11 +1776,11 @@ This document lists all configuration keys accepted by `config.toml`. # [general.telemetry] -| Key | Type | Default | -| --- | ---- | ------- | -| [`core_enabled`](#core_enabled) | `bool` | `true` | -| [`user_enabled`](#user_enabled) | `bool` | `true` | -| [`me_level`](#me_level) | `"silent"`, `"normal"`, or `"debug"` | `"normal"` | +| Key | Type | Default | Hot-Reload | +| --- | ---- | ------- | ---------- | +| [`core_enabled`](#core_enabled) | `bool` | `true` | `✔` | +| [`user_enabled`](#user_enabled) | `bool` | `true` | `✔` | +| [`me_level`](#me_level) | `"silent"`, `"normal"`, or `"debug"` | `"normal"` | `✔` | ## core_enabled - **Constraints / validation**: `bool`. @@ -1636,18 +1814,18 @@ This document lists all configuration keys accepted by `config.toml`. # [network] -| Key | Type | Default | -| --- | ---- | ------- | -| [`ipv4`](#ipv4) | `bool` | `true` | -| [`ipv6`](#ipv6) | `bool` | `false` | -| [`prefer`](#prefer) | `u8` | `4` | -| [`multipath`](#multipath) | `bool` | `false` | -| [`stun_use`](#stun_use) | `bool` | `true` | -| [`stun_servers`](#stun_servers) | `String[]` | Built-in STUN list (13 hosts) | -| [`stun_tcp_fallback`](#stun_tcp_fallback) | `bool` | `true` | -| [`http_ip_detect_urls`](#http_ip_detect_urls) | `String[]` | `["https://ifconfig.me/ip", "https://api.ipify.org"]` | -| [`cache_public_ip_path`](#cache_public_ip_path) | `String` | `"cache/public_ip.txt"` | -| [`dns_overrides`](#dns_overrides) | `String[]` | `[]` | +| Key | Type | Default | Hot-Reload | +| --- | ---- | ------- | ---------- | +| [`ipv4`](#ipv4) | `bool` | `true` | `✘` | +| [`ipv6`](#ipv6) | `bool` | `false` | `✘` | +| [`prefer`](#prefer) | `u8` | `4` | `✘` | +| [`multipath`](#multipath) | `bool` | `false` | `✘` | +| [`stun_use`](#stun_use) | `bool` | `true` | `✘` | +| [`stun_servers`](#stun_servers) | `String[]` | Built-in STUN list (13 hosts) | `✘` | +| [`stun_tcp_fallback`](#stun_tcp_fallback) | `bool` | `true` | `✘` | +| [`http_ip_detect_urls`](#http_ip_detect_urls) | `String[]` | `["https://ifconfig.me/ip", "https://api.ipify.org"]` | `✘` | +| [`cache_public_ip_path`](#cache_public_ip_path) | `String` | `"cache/public_ip.txt"` | `✘` | +| [`dns_overrides`](#dns_overrides) | `String[]` | `[]` | `✔` | ## ipv4 - **Constraints / validation**: `bool`. @@ -1757,23 +1935,27 @@ This document lists all configuration keys accepted by `config.toml`. # [server] -| Key | Type | Default | -| --- | ---- | ------- | -| [`port`](#port) | `u16` | `443` | -| [`listen_addr_ipv4`](#listen_addr_ipv4) | `String` | `"0.0.0.0"` | -| [`listen_addr_ipv6`](#listen_addr_ipv6) | `String` | `"::"` | -| [`listen_unix_sock`](#listen_unix_sock) | `String` | — | -| [`listen_unix_sock_perm`](#listen_unix_sock_perm) | `String` | — | -| [`listen_tcp`](#listen_tcp) | `bool` | — (auto) | -| [`proxy_protocol`](#proxy_protocol) | `bool` | `false` | -| [`proxy_protocol_header_timeout_ms`](#proxy_protocol_header_timeout_ms) | `u64` | `500` | -| [`proxy_protocol_trusted_cidrs`](#proxy_protocol_trusted_cidrs) | `IpNetwork[]` | `[]` | -| [`metrics_port`](#metrics_port) | `u16` | — | -| [`metrics_listen`](#metrics_listen) | `String` | — | -| [`metrics_whitelist`](#metrics_whitelist) | `IpNetwork[]` | `["127.0.0.1/32", "::1/128"]` | -| [`max_connections`](#max_connections) | `u32` | `10000` | -| [`accept_permit_timeout_ms`](#accept_permit_timeout_ms) | `u64` | `250` | -| [`listen_backlog`](#listen_backlog) | `u32` | `1024` | +| Key | Type | Default | Hot-Reload | +| --- | ---- | ------- | ---------- | +| [`port`](#port) | `u16` | `443` | `✘` | +| [`listen_addr_ipv4`](#listen_addr_ipv4) | `String` | `"0.0.0.0"` | `✘` | +| [`listen_addr_ipv6`](#listen_addr_ipv6) | `String` | `"::"` | `✘` | +| [`listen_unix_sock`](#listen_unix_sock) | `String` | — | `✘` | +| [`listen_unix_sock_perm`](#listen_unix_sock_perm) | `String` | — | `✘` | +| [`listen_tcp`](#listen_tcp) | `bool` | — (auto) | `✘` | +| [`proxy_protocol`](#proxy_protocol) | `bool` | `false` | `✘` | +| [`proxy_protocol_header_timeout_ms`](#proxy_protocol_header_timeout_ms) | `u64` | `500` | `✘` | +| [`proxy_protocol_trusted_cidrs`](#proxy_protocol_trusted_cidrs) | `IpNetwork[]` | `[]` | `✘` | +| [`metrics_port`](#metrics_port) | `u16` | — | `✘` | +| [`metrics_listen`](#metrics_listen) | `String` | — | `✘` | +| [`metrics_whitelist`](#metrics_whitelist) | `IpNetwork[]` | `["127.0.0.1/32", "::1/128"]` | `✘` | +| [`api`](#serverapi) | `Table` | built-in defaults | `✘` | +| [`admin_api`](#serverapi) | `Table` | alias for `api` | `✘` | +| [`listeners`](#serverlisteners) | `Table[]` | derived from legacy listener fields | `✘` | +| [`max_connections`](#max_connections) | `u32` | `10000` | `✘` | +| [`accept_permit_timeout_ms`](#accept_permit_timeout_ms) | `u64` | `250` | `✘` | +| [`listen_backlog`](#listen_backlog) | `u32` | `1024` | `✘` | +| [`conntrack_control`](#serverconntrack_control) | `Table` | built-in defaults | `✘` | ## port - **Constraints / validation**: `u16`. @@ -1930,16 +2112,16 @@ Note: When `server.proxy_protocol` is enabled, incoming PROXY protocol headers a Note: The conntrack-control worker runs **only on Linux**. On other operating systems it is not started; if `inline_conntrack_control` is `true`, a warning is logged. Effective operation also requires **CAP_NET_ADMIN** and a usable backend (`nft` or `iptables` / `ip6tables` on `PATH`). The `conntrack` utility is used for optional table entry deletes under pressure. -| Key | Type | Default | -| --- | ---- | ------- | -| [`inline_conntrack_control`](#inline_conntrack_control) | `bool` | `true` | -| [`mode`](#mode) | `String` | `"tracked"` | -| [`backend`](#backend) | `String` | `"auto"` | -| [`profile`](#profile) | `String` | `"balanced"` | -| [`hybrid_listener_ips`](#hybrid_listener_ips) | `IpAddr[]` | `[]` | -| [`pressure_high_watermark_pct`](#pressure_high_watermark_pct) | `u8` | `85` | -| [`pressure_low_watermark_pct`](#pressure_low_watermark_pct) | `u8` | `70` | -| [`delete_budget_per_sec`](#delete_budget_per_sec) | `u64` | `4096` | +| Key | Type | Default | Hot-Reload | +| --- | ---- | ------- | ---------- | +| [`inline_conntrack_control`](#inline_conntrack_control) | `bool` | `true` | `✘` | +| [`mode`](#mode) | `String` | `"tracked"` | `✘` | +| [`backend`](#backend) | `String` | `"auto"` | `✘` | +| [`profile`](#profile) | `String` | `"balanced"` | `✘` | +| [`hybrid_listener_ips`](#hybrid_listener_ips) | `IpAddr[]` | `[]` | `✘` | +| [`pressure_high_watermark_pct`](#pressure_high_watermark_pct) | `u8` | `85` | `✘` | +| [`pressure_low_watermark_pct`](#pressure_low_watermark_pct) | `u8` | `70` | `✘` | +| [`delete_budget_per_sec`](#delete_budget_per_sec) | `u64` | `4096` | `✘` | ## inline_conntrack_control - **Constraints / validation**: `bool`. @@ -2021,21 +2203,21 @@ Note: The conntrack-control worker runs **only on Linux**. On other operating sy Note: This section also accepts the legacy alias `[server.admin_api]` (same schema as `[server.api]`). -| Key | Type | Default | -| --- | ---- | ------- | -| [`enabled`](#enabled) | `bool` | `true` | -| [`listen`](#listen) | `String` | `"0.0.0.0:9091"` | -| [`whitelist`](#whitelist) | `IpNetwork[]` | `["127.0.0.0/8"]` | -| [`auth_header`](#auth_header) | `String` | `""` | -| [`request_body_limit_bytes`](#request_body_limit_bytes) | `usize` | `65536` | -| [`minimal_runtime_enabled`](#minimal_runtime_enabled) | `bool` | `true` | -| [`minimal_runtime_cache_ttl_ms`](#minimal_runtime_cache_ttl_ms) | `u64` | `1000` | -| [`runtime_edge_enabled`](#runtime_edge_enabled) | `bool` | `false` | -| [`runtime_edge_cache_ttl_ms`](#runtime_edge_cache_ttl_ms) | `u64` | `1000` | -| [`runtime_edge_top_n`](#runtime_edge_top_n) | `usize` | `10` | -| [`runtime_edge_events_capacity`](#runtime_edge_events_capacity) | `usize` | `256` | -| [`read_only`](#read_only) | `bool` | `false` | -| [`gray_action`](#gray_action) | `"drop"`, `"api"`, or `"200"` | `"drop"` | +| Key | Type | Default | Hot-Reload | +| --- | ---- | ------- | ---------- | +| [`enabled`](#enabled) | `bool` | `true` | `✘` | +| [`listen`](#listen) | `String` | `"0.0.0.0:9091"` | `✘` | +| [`whitelist`](#whitelist) | `IpNetwork[]` | `["127.0.0.0/8"]` | `✘` | +| [`auth_header`](#auth_header) | `String` | `""` | `✘` | +| [`request_body_limit_bytes`](#request_body_limit_bytes) | `usize` | `65536` | `✘` | +| [`minimal_runtime_enabled`](#minimal_runtime_enabled) | `bool` | `true` | `✘` | +| [`minimal_runtime_cache_ttl_ms`](#minimal_runtime_cache_ttl_ms) | `u64` | `1000` | `✘` | +| [`runtime_edge_enabled`](#runtime_edge_enabled) | `bool` | `false` | `✘` | +| [`runtime_edge_cache_ttl_ms`](#runtime_edge_cache_ttl_ms) | `u64` | `1000` | `✘` | +| [`runtime_edge_top_n`](#runtime_edge_top_n) | `usize` | `10` | `✘` | +| [`runtime_edge_events_capacity`](#runtime_edge_events_capacity) | `usize` | `256` | `✘` | +| [`read_only`](#read_only) | `bool` | `false` | `✘` | +| [`gray_action`](#gray_action) | `"drop"`, `"api"`, or `"200"` | `"drop"` | `✘` | ## enabled - **Constraints / validation**: `bool`. @@ -2159,13 +2341,14 @@ Note: This section also accepts the legacy alias `[server.admin_api]` (same sche # [[server.listeners]] -| Key | Type | Default | -| --- | ---- | ------- | -| [`ip`](#ip) | `IpAddr` | — | -| [`announce`](#announce) | `String` | — | -| [`announce_ip`](#announce_ip) | `IpAddr` | — | -| [`proxy_protocol`](#proxy_protocol) | `bool` | — | -| [`reuse_allow`](#reuse_allow) | `bool` | `false` | +| Key | Type | Default | Hot-Reload | +| --- | ---- | ------- | ---------- | +| [`ip`](#ip) | `IpAddr` | — | `✘` | +| [`port`](#port-serverlisteners) | `u16` | `server.port` | `✘` | +| [`announce`](#announce) | `String` | — | `✘` | +| [`announce_ip`](#announce_ip) | `IpAddr` | — | `✘` | +| [`proxy_protocol`](#proxy_protocol) | `bool` | — | `✘` | +| [`reuse_allow`](#reuse_allow) | `bool` | `false` | `✘` | ## ip - **Constraints / validation**: Required field. Must be an `IpAddr`. @@ -2176,6 +2359,16 @@ Note: This section also accepts the legacy alias `[server.admin_api]` (same sche [[server.listeners]] ip = "0.0.0.0" ``` +## port (server.listeners) + - **Constraints / validation**: `u16` (optional). When omitted, falls back to `server.port`. + - **Description**: Per-listener TCP port. + - **Example**: + + ```toml + [[server.listeners]] + ip = "0.0.0.0" + port = 443 + ``` ## announce - **Constraints / validation**: `String` (optional). Must not be empty when set. - **Description**: Public IP/domain announced in proxy links for this listener. Takes precedence over `announce_ip`. @@ -2209,8 +2402,7 @@ Note: This section also accepts the legacy alias `[server.admin_api]` (same sche ip = "0.0.0.0" proxy_protocol = true ``` -## reuse_allow" -- `reuse_allow` +## reuse_allow - **Constraints / validation**: `bool`. - **Description**: Enables `SO_REUSEPORT` for multi-instance bind sharing (allows multiple telemt instances to listen on the same `ip:port`). - **Example**: @@ -2225,18 +2417,18 @@ Note: This section also accepts the legacy alias `[server.admin_api]` (same sche # [timeouts] -| Key | Type | Default | -| --- | ---- | ------- | -| [`client_handshake`](#client_handshake) | `u64` | `30` | -| [`relay_idle_policy_v2_enabled`](#relay_idle_policy_v2_enabled) | `bool` | `true` | -| [`relay_client_idle_soft_secs`](#relay_client_idle_soft_secs) | `u64` | `120` | -| [`relay_client_idle_hard_secs`](#relay_client_idle_hard_secs) | `u64` | `360` | -| [`relay_idle_grace_after_downstream_activity_secs`](#relay_idle_grace_after_downstream_activity_secs) | `u64` | `30` | -| [`tg_connect`](#tg_connect) | `u64` | `10` | -| [`client_keepalive`](#client_keepalive) | `u64` | `15` | -| [`client_ack`](#client_ack) | `u64` | `90` | -| [`me_one_retry`](#me_one_retry) | `u8` | `12` | -| [`me_one_timeout_ms`](#me_one_timeout_ms) | `u64` | `1200` | +| Key | Type | Default | Hot-Reload | +| --- | ---- | ------- | ---------- | +| [`client_first_byte_idle_secs`](#client_first_byte_idle_secs) | `u64` | `300` | `✘` | +| [`client_handshake`](#client_handshake) | `u64` | `30` | `✘` | +| [`relay_idle_policy_v2_enabled`](#relay_idle_policy_v2_enabled) | `bool` | `true` | `✘` | +| [`relay_client_idle_soft_secs`](#relay_client_idle_soft_secs) | `u64` | `120` | `✘` | +| [`relay_client_idle_hard_secs`](#relay_client_idle_hard_secs) | `u64` | `360` | `✘` | +| [`relay_idle_grace_after_downstream_activity_secs`](#relay_idle_grace_after_downstream_activity_secs) | `u64` | `30` | `✘` | +| [`client_keepalive`](#client_keepalive) | `u64` | `15` | `✘` | +| [`client_ack`](#client_ack) | `u64` | `90` | `✘` | +| [`me_one_retry`](#me_one_retry) | `u8` | `12` | `✘` | +| [`me_one_timeout_ms`](#me_one_timeout_ms) | `u64` | `1200` | `✘` | ## client_handshake - **Constraints / validation**: Must be `> 0`. Value is in seconds. Also used as an upper bound for some TLS emulation delays (see `censorship.server_hello_delay_max_ms`). @@ -2292,15 +2484,6 @@ Note: This section also accepts the legacy alias `[server.admin_api]` (same sche [timeouts] relay_idle_grace_after_downstream_activity_secs = 30 ``` -## tg_connect - - **Constraints / validation**: `u64`. Value is in seconds. - - **Description**: Upstream Telegram connect timeout (seconds). - - **Example**: - - ```toml - [timeouts] - tg_connect = 10 - ``` ## client_keepalive - **Constraints / validation**: `u64`. Value is in seconds. - **Description**: Client keepalive timeout (seconds). @@ -2377,6 +2560,40 @@ Note: This section also accepts the legacy alias `[server.admin_api]` (same sche | [`mask_timing_normalization_enabled`](#mask_timing_normalization_enabled) | `bool` | `false` | | [`mask_timing_normalization_floor_ms`](#mask_timing_normalization_floor_ms) | `u64` | `0` | | [`mask_timing_normalization_ceiling_ms`](#mask_timing_normalization_ceiling_ms) | `u64` | `0` | +| Key | Type | Default | Hot-Reload | +| --- | ---- | ------- | ---------- | +| [`tls_domain`](#tls_domain) | `String` | `"petrovich.ru"` | `✘` | +| [`tls_domains`](#tls_domains) | `String[]` | `[]` | `✘` | +| [`unknown_sni_action`](#unknown_sni_action) | `"drop"`, `"mask"`, `"accept"`, `"reject_handshake"` | `"drop"` | `✘` | +| [`tls_fetch_scope`](#tls_fetch_scope) | `String` | `""` | `✘` | +| [`tls_fetch`](#tls_fetch) | `Table` | built-in defaults | `✘` | +| [`mask`](#mask) | `bool` | `true` | `✘` | +| [`mask_host`](#mask_host) | `String` | — | `✘` | +| [`mask_port`](#mask_port) | `u16` | `443` | `✘` | +| [`mask_unix_sock`](#mask_unix_sock) | `String` | — | `✘` | +| [`fake_cert_len`](#fake_cert_len) | `usize` | `2048` | `✘` | +| [`tls_emulation`](#tls_emulation) | `bool` | `true` | `✘` | +| [`tls_front_dir`](#tls_front_dir) | `String` | `"tlsfront"` | `✘` | +| [`server_hello_delay_min_ms`](#server_hello_delay_min_ms) | `u64` | `0` | `✘` | +| [`server_hello_delay_max_ms`](#server_hello_delay_max_ms) | `u64` | `0` | `✘` | +| [`tls_new_session_tickets`](#tls_new_session_tickets) | `u8` | `0` | `✘` | +| [`tls_full_cert_ttl_secs`](#tls_full_cert_ttl_secs) | `u64` | `90` | `✘` | +| [`serverhello_compact`](#serverhello_compact) | `bool` | `false` | `✘` | +| [`alpn_enforce`](#alpn_enforce) | `bool` | `true` | `✘` | +| [`mask_proxy_protocol`](#mask_proxy_protocol) | `u8` | `0` | `✘` | +| [`mask_shape_hardening`](#mask_shape_hardening) | `bool` | `true` | `✘` | +| [`mask_shape_hardening_aggressive_mode`](#mask_shape_hardening_aggressive_mode) | `bool` | `false` | `✘` | +| [`mask_shape_bucket_floor_bytes`](#mask_shape_bucket_floor_bytes) | `usize` | `512` | `✘` | +| [`mask_shape_bucket_cap_bytes`](#mask_shape_bucket_cap_bytes) | `usize` | `4096` | `✘` | +| [`mask_shape_above_cap_blur`](#mask_shape_above_cap_blur) | `bool` | `false` | `✘` | +| [`mask_shape_above_cap_blur_max_bytes`](#mask_shape_above_cap_blur_max_bytes) | `usize` | `512` | `✘` | +| [`mask_relay_max_bytes`](#mask_relay_max_bytes) | `usize` | `5242880` | `✘` | +| [`mask_relay_timeout_ms`](#mask_relay_timeout_ms) | `u64` | `60_000` | `✘` | +| [`mask_relay_idle_timeout_ms`](#mask_relay_idle_timeout_ms) | `u64` | `5_000` | `✘` | +| [`mask_classifier_prefetch_timeout_ms`](#mask_classifier_prefetch_timeout_ms) | `u64` | `5` | `✘` | +| [`mask_timing_normalization_enabled`](#mask_timing_normalization_enabled) | `bool` | `false` | `✘` | +| [`mask_timing_normalization_floor_ms`](#mask_timing_normalization_floor_ms) | `u64` | `0` | `✘` | +| [`mask_timing_normalization_ceiling_ms`](#mask_timing_normalization_ceiling_ms) | `u64` | `0` | `✘` | ## tls_domain - **Constraints / validation**: Must be a non-empty domain name. Must not contain spaces or `/`. @@ -2810,15 +3027,15 @@ If your backend or network is very bandwidth-constrained, reduce cap first. If p # [censorship.tls_fetch] -| Key | Type | Default | -| --- | ---- | ------- | -| [`profiles`](#profiles) | `String[]` | `["modern_chrome_like", "modern_firefox_like", "compat_tls12", "legacy_minimal"]` | -| [`strict_route`](#strict_route) | `bool` | `true` | -| [`attempt_timeout_ms`](#attempt_timeout_ms) | `u64` | `5000` | -| [`total_budget_ms`](#total_budget_ms) | `u64` | `15000` | -| [`grease_enabled`](#grease_enabled) | `bool` | `false` | -| [`deterministic`](#deterministic) | `bool` | `false` | -| [`profile_cache_ttl_secs`](#profile_cache_ttl_secs) | `u64` | `600` | +| Key | Type | Default | Hot-Reload | +| --- | ---- | ------- | ---------- | +| [`profiles`](#profiles) | `String[]` | `["modern_chrome_like", "modern_firefox_like", "compat_tls12", "legacy_minimal"]` | `✘` | +| [`strict_route`](#strict_route) | `bool` | `true` | `✘` | +| [`attempt_timeout_ms`](#attempt_timeout_ms) | `u64` | `5000` | `✘` | +| [`total_budget_ms`](#total_budget_ms) | `u64` | `15000` | `✘` | +| [`grease_enabled`](#grease_enabled) | `bool` | `false` | `✘` | +| [`deterministic`](#deterministic) | `bool` | `false` | `✘` | +| [`profile_cache_ttl_secs`](#profile_cache_ttl_secs) | `u64` | `600` | `✘` | ## profiles - **Constraints / validation**: `String[]`. Empty list falls back to defaults; values are deduplicated preserving order. @@ -2887,24 +3104,24 @@ If your backend or network is very bandwidth-constrained, reduce cap first. If p # [access] -| Key | Type | Default | -| --- | ---- | ------- | -| [`users`](#users) | `Map` | `{"default": "000…000"}` | -| [`user_ad_tags`](#user_ad_tags) | `Map` | `{}` | -| [`user_max_tcp_conns`](#user_max_tcp_conns) | `Map` | `{}` | -| [`user_max_tcp_conns_global_each`](#user_max_tcp_conns_global_each) | `usize` | `0` | -| [`user_expirations`](#user_expirations) | `Map>` | `{}` | -| [`user_data_quota`](#user_data_quota) | `Map` | `{}` | -| [`user_max_unique_ips`](#user_max_unique_ips) | `Map` | `{}` | -| [`user_max_unique_ips_global_each`](#user_max_unique_ips_global_each) | `usize` | `0` | -| [`user_max_unique_ips_mode`](#user_max_unique_ips_mode) | `"active_window"`, `"time_window"`, or `"combined"` | `"active_window"` | -| [`user_max_unique_ips_window_secs`](#user_max_unique_ips_window_secs) | `u64` | `30` | -| [`user_source_deny`](#user_source_deny) | `Map` | `{}` | -| [`replay_check_len`](#replay_check_len) | `usize` | `65536` | -| [`replay_window_secs`](#replay_window_secs) | `u64` | `120` | -| [`ignore_time_skew`](#ignore_time_skew) | `bool` | `false` | -| [`user_rate_limits`](#user_rate_limits) | `Map` | `{}` | -| [`cidr_rate_limits`](#cidr_rate_limits) | `Map` | `{}` | +| Key | Type | Default | Hot-Reload | +| --- | ---- | ------- | ---------- | +| [`users`](#users) | `Map` | `{"default": "000…000"}` | `✔` | +| [`user_ad_tags`](#user_ad_tags) | `Map` | `{}` | `✔` | +| [`user_max_tcp_conns`](#user_max_tcp_conns) | `Map` | `{}` | `✔` | +| [`user_max_tcp_conns_global_each`](#user_max_tcp_conns_global_each) | `usize` | `0` | `✔` | +| [`user_expirations`](#user_expirations) | `Map>` | `{}` | `✔` | +| [`user_data_quota`](#user_data_quota) | `Map` | `{}` | `✔` | +| [`user_max_unique_ips`](#user_max_unique_ips) | `Map` | `{}` | `✔` | +| [`user_max_unique_ips_global_each`](#user_max_unique_ips_global_each) | `usize` | `0` | `✔` | +| [`user_max_unique_ips_mode`](#user_max_unique_ips_mode) | `"active_window"`, `"time_window"`, or `"combined"` | `"active_window"` | `✔` | +| [`user_max_unique_ips_window_secs`](#user_max_unique_ips_window_secs) | `u64` | `30` | `✔` | +| [`user_source_deny`](#user_source_deny) | `Map` | `{}` | `✘` | +| [`replay_check_len`](#replay_check_len) | `usize` | `65536` | `✘` | +| [`replay_window_secs`](#replay_window_secs) | `u64` | `120` | `✘` | +| [`ignore_time_skew`](#ignore_time_skew) | `bool` | `false` | `✘` | +| [`user_rate_limits`](#user_rate_limits) | `Map` | `{}` | `✔` | +| [`cidr_rate_limits`](#cidr_rate_limits) | `Map` | `{}` | `✔` | ## users - **Constraints / validation**: Must not be empty (at least one user must exist). Each value must be **exactly 32 hex characters**. @@ -3068,19 +3285,23 @@ If your backend or network is very bandwidth-constrained, reduce cap first. If p # [[upstreams]] -| Key | Type | Default | -| --- | ---- | ------- | -| [`type`](#type) | `"direct"`, `"socks4"`, `"socks5"`, or `"shadowsocks"` | — | -| [`weight`](#weight) | `u16` | `1` | -| [`enabled`](#enabled) | `bool` | `true` | -| [`scopes`](#scopes) | `String` | `""` | -| [`interface`](#interface) | `String` | — | -| [`bind_addresses`](#bind_addresses) | `String[]` | — | -| [`url`](#url) | `String` | — | -| [`address`](#address) | `String` | — | -| [`user_id`](#user_id) | `String` | — | -| [`username`](#username) | `String` | — | -| [`password`](#password) | `String` | — | +| Key | Type | Default | Hot-Reload | +| --- | ---- | ------- | ---------- | +| [`type`](#type) | `"direct"`, `"socks4"`, `"socks5"`, or `"shadowsocks"` | — | `✘` | +| [`weight`](#weight) | `u16` | `1` | `✘` | +| [`enabled`](#enabled) | `bool` | `true` | `✘` | +| [`scopes`](#scopes) | `String` | `""` | `✘` | +| [`ipv4`](#ipv4-upstreams) | `bool` | — (auto) | `✘` | +| [`ipv6`](#ipv6-upstreams) | `bool` | — (auto) | `✘` | +| [`interface`](#interface) | `String` | — | `✘` | +| [`bind_addresses`](#bind_addresses) | `String[]` | — | `✘` | +| [`bindtodevice`](#bindtodevice) | `String` | — | `✘` | +| [`force_bind`](#force_bind) | `String` | — | `✘` | +| [`url`](#url) | `String` | — | `✘` | +| [`address`](#address) | `String` | — | `✘` | +| [`user_id`](#user_id) | `String` | — | `✘` | +| [`username`](#username) | `String` | — | `✘` | +| [`password`](#password) | `String` | — | `✘` | ## type - **Constraints / validation**: Required field. Must be one of: `"direct"`, `"socks4"`, `"socks5"`, `"shadowsocks"`. @@ -3131,6 +3352,26 @@ If your backend or network is very bandwidth-constrained, reduce cap first. If p address = "10.0.0.10:1080" scopes = "me, fetch, dc2" ``` +## ipv4 (upstreams) + - **Constraints / validation**: `bool` (optional). + - **Description**: Allows IPv4 DC targets for this upstream. When omitted, Telemt auto-detects support from runtime connectivity state. + - **Example**: + + ```toml + [[upstreams]] + type = "direct" + ipv4 = true + ``` +## ipv6 (upstreams) + - **Constraints / validation**: `bool` (optional). + - **Description**: Allows IPv6 DC targets for this upstream. When omitted, Telemt auto-detects support from runtime connectivity state. + - **Example**: + + ```toml + [[upstreams]] + type = "direct" + ipv6 = false + ``` ## interface - **Constraints / validation**: `String` (optional). - For `"direct"`: may be an IP address (used as explicit local bind) or an OS interface name (resolved to an IP at runtime; Unix only). @@ -3161,6 +3402,26 @@ If your backend or network is very bandwidth-constrained, reduce cap first. If p type = "direct" bind_addresses = ["192.0.2.10", "192.0.2.11"] ``` +## bindtodevice + - **Constraints / validation**: `String` (optional). Applies only to `type = "direct"` and is Linux-only. + - **Description**: Hard interface pinning via `SO_BINDTODEVICE` for outgoing direct TCP connects. + - **Example**: + + ```toml + [[upstreams]] + type = "direct" + bindtodevice = "eth0" + ``` +## force_bind + - **Constraints / validation**: `String` (optional). Alias for `bindtodevice`. + - **Description**: Backward-compatible alias for Linux `SO_BINDTODEVICE` hard interface pinning. + - **Example**: + + ```toml + [[upstreams]] + type = "direct" + force_bind = "eth0" + ``` ## url - **Constraints / validation**: Applies only to `type = "shadowsocks"`. - Must be a valid Shadowsocks URL accepted by the `shadowsocks` crate. diff --git a/docs/Config_params/CONFIG_PARAMS.ru.md b/docs/Config_params/CONFIG_PARAMS.ru.md index 637e742..28041e2 100644 --- a/docs/Config_params/CONFIG_PARAMS.ru.md +++ b/docs/Config_params/CONFIG_PARAMS.ru.md @@ -10,6 +10,8 @@ > > Параметры конфигурации, подробно описанные в этом документе, предназначены для опытных пользователей и для целей тонкой настройки. Изменение этих параметров без четкого понимания их функции может привести к нестабильности приложения или другому неожиданному поведению. Пожалуйста, действуйте осторожно и на свой страх и риск. +> `Hot-Reload` показывает, применяет ли config watcher изменение без перезапуска процесса; `✘` означает, что для runtime-эффекта нужен перезапуск. + # Содержание - [Ключи верхнего уровня](#top-level-keys) - [general](#general) @@ -29,12 +31,16 @@ # Ключи верхнего уровня -| Ключ | Тип | По умолчанию | -| --- | ---- | ------- | -| [`include`](#include) | `String` (специальная директива) | — | -| [`show_link`](#show_link) | `"*"` or `String[]` | `[]` (`ShowLink::None`) | -| [`dc_overrides`](#dc_overrides) | `Map` | `{}` | -| [`default_dc`](#default_dc) | `u8` | — (эффективный резервный вариант: `2` в ME маршрутизации) | +| Ключ | Тип | По умолчанию | Hot-Reload | +| --- | ---- | ------- | ---------- | +| [`include`](#include) | `String` (специальная директива) | — | `✔` | +| [`show_link`](#show_link) | `"*"` or `String[]` | `[]` (`ShowLink::None`) | `✘` | +| [`dc_overrides`](#dc_overrides) | `Map` | `{}` | `✘` | +| [`default_dc`](#default_dc) | `u8` | — (эффективный резервный вариант: `2` в ME маршрутизации) | `✘` | +| [`beobachten`](#beobachten) | `bool` | `true` | `✘` | +| [`beobachten_minutes`](#beobachten_minutes) | `u64` | `10` | `✘` | +| [`beobachten_flush_secs`](#beobachten_flush_secs) | `u64` | `15` | `✘` | +| [`beobachten_file`](#beobachten_file) | `String` | `"cache/beobachten.txt"` | `✘` | ## include - **Ограничения / валидация**: значение должно быть одной строкой в виде `include = "path/to/file.toml"`. Значения параметра обрабатываются перед анализом TOML. Максимальное количество - 10. @@ -218,6 +224,151 @@ | [`auto_degradation_enabled`](#auto_degradation_enabled) | `bool` | `true` | | [`degradation_min_unavailable_dc_groups`](#degradation_min_unavailable_dc_groups) | `u8` | `2` | | [`rst_on_close`](#rst_on_close) | `"off"`, `"errors"` или `"always"` | `"off"` | +| Ключ | Тип | По умолчанию | Hot-Reload | +| --- | ---- | ------- | ---------- | +| [`data_path`](#data_path) | `String` | — | `✘` | +| [`quota_state_path`](#quota_state_path) | `Path` | `"telemt.limit.json"` | `✘` | +| [`config_strict`](#config_strict) | `bool` | `false` | `✘` | +| [`prefer_ipv6`](#prefer_ipv6) | `bool` | `false` | `✘` | +| [`fast_mode`](#fast_mode) | `bool` | `true` | `✘` | +| [`use_middle_proxy`](#use_middle_proxy) | `bool` | `true` | `✘` | +| [`proxy_secret_path`](#proxy_secret_path) | `String` | `"proxy-secret"` | `✘` | +| [`proxy_secret_url`](#proxy_secret_url) | `String` | `"https://core.telegram.org/getProxySecret"` | `✘` | +| [`proxy_config_v4_cache_path`](#proxy_config_v4_cache_path) | `String` | `"cache/proxy-config-v4.txt"` | `✘` | +| [`proxy_config_v4_url`](#proxy_config_v4_url) | `String` | `"https://core.telegram.org/getProxyConfig"` | `✘` | +| [`proxy_config_v6_cache_path`](#proxy_config_v6_cache_path) | `String` | `"cache/proxy-config-v6.txt"` | `✘` | +| [`proxy_config_v6_url`](#proxy_config_v6_url) | `String` | `"https://core.telegram.org/getProxyConfigV6"` | `✘` | +| [`ad_tag`](#ad_tag) | `String` | — | `✔` | +| [`middle_proxy_nat_ip`](#middle_proxy_nat_ip) | `IpAddr` | — | `✘` | +| [`middle_proxy_nat_probe`](#middle_proxy_nat_probe) | `bool` | `true` | `✘` | +| [`middle_proxy_nat_stun`](#middle_proxy_nat_stun) | `String` | — | `✘` | +| [`middle_proxy_nat_stun_servers`](#middle_proxy_nat_stun_servers) | `String[]` | `[]` | `✘` | +| [`stun_nat_probe_concurrency`](#stun_nat_probe_concurrency) | `usize` | `8` | `✘` | +| [`middle_proxy_pool_size`](#middle_proxy_pool_size) | `usize` | `8` | `✘` | +| [`middle_proxy_warm_standby`](#middle_proxy_warm_standby) | `usize` | `16` | `✘` | +| [`me_init_retry_attempts`](#me_init_retry_attempts) | `u32` | `0` | `✘` | +| [`me2dc_fallback`](#me2dc_fallback) | `bool` | `true` | `✘` | +| [`me2dc_fast`](#me2dc_fast) | `bool` | `false` | `✘` | +| [`me_keepalive_enabled`](#me_keepalive_enabled) | `bool` | `true` | `✘` | +| [`me_keepalive_interval_secs`](#me_keepalive_interval_secs) | `u64` | `8` | `✘` | +| [`me_keepalive_jitter_secs`](#me_keepalive_jitter_secs) | `u64` | `2` | `✘` | +| [`me_keepalive_payload_random`](#me_keepalive_payload_random) | `bool` | `true` | `✘` | +| [`rpc_proxy_req_every`](#rpc_proxy_req_every) | `u64` | `0` | `✘` | +| [`me_writer_cmd_channel_capacity`](#me_writer_cmd_channel_capacity) | `usize` | `4096` | `✘` | +| [`me_route_channel_capacity`](#me_route_channel_capacity) | `usize` | `768` | `✘` | +| [`me_c2me_channel_capacity`](#me_c2me_channel_capacity) | `usize` | `1024` | `✘` | +| [`me_c2me_send_timeout_ms`](#me_c2me_send_timeout_ms) | `u64` | `4000` | `✘` | +| [`me_reader_route_data_wait_ms`](#me_reader_route_data_wait_ms) | `u64` | `2` | `✔` | +| [`me_d2c_flush_batch_max_frames`](#me_d2c_flush_batch_max_frames) | `usize` | `32` | `✔` | +| [`me_d2c_flush_batch_max_bytes`](#me_d2c_flush_batch_max_bytes) | `usize` | `131072` | `✔` | +| [`me_d2c_flush_batch_max_delay_us`](#me_d2c_flush_batch_max_delay_us) | `u64` | `500` | `✔` | +| [`me_d2c_ack_flush_immediate`](#me_d2c_ack_flush_immediate) | `bool` | `true` | `✔` | +| [`me_quota_soft_overshoot_bytes`](#me_quota_soft_overshoot_bytes) | `u64` | `65536` | `✔` | +| [`me_d2c_frame_buf_shrink_threshold_bytes`](#me_d2c_frame_buf_shrink_threshold_bytes) | `usize` | `262144` | `✔` | +| [`direct_relay_copy_buf_c2s_bytes`](#direct_relay_copy_buf_c2s_bytes) | `usize` | `65536` | `✔` | +| [`direct_relay_copy_buf_s2c_bytes`](#direct_relay_copy_buf_s2c_bytes) | `usize` | `262144` | `✔` | +| [`crypto_pending_buffer`](#crypto_pending_buffer) | `usize` | `262144` | `✘` | +| [`max_client_frame`](#max_client_frame) | `usize` | `16777216` | `✘` | +| [`desync_all_full`](#desync_all_full) | `bool` | `false` | `✔` | +| [`beobachten`](#beobachten) | `bool` | `true` | `✘` | +| [`beobachten_minutes`](#beobachten_minutes) | `u64` | `10` | `✘` | +| [`beobachten_flush_secs`](#beobachten_flush_secs) | `u64` | `15` | `✘` | +| [`beobachten_file`](#beobachten_file) | `String` | `"cache/beobachten.txt"` | `✘` | +| [`hardswap`](#hardswap) | `bool` | `true` | `✔` | +| [`me_warmup_stagger_enabled`](#me_warmup_stagger_enabled) | `bool` | `true` | `✘` | +| [`me_warmup_step_delay_ms`](#me_warmup_step_delay_ms) | `u64` | `500` | `✘` | +| [`me_warmup_step_jitter_ms`](#me_warmup_step_jitter_ms) | `u64` | `300` | `✘` | +| [`me_reconnect_max_concurrent_per_dc`](#me_reconnect_max_concurrent_per_dc) | `u32` | `8` | `✘` | +| [`me_reconnect_backoff_base_ms`](#me_reconnect_backoff_base_ms) | `u64` | `500` | `✘` | +| [`me_reconnect_backoff_cap_ms`](#me_reconnect_backoff_cap_ms) | `u64` | `30000` | `✘` | +| [`me_reconnect_fast_retry_count`](#me_reconnect_fast_retry_count) | `u32` | `16` | `✘` | +| [`me_single_endpoint_shadow_writers`](#me_single_endpoint_shadow_writers) | `u8` | `2` | `✔` | +| [`me_single_endpoint_outage_mode_enabled`](#me_single_endpoint_outage_mode_enabled) | `bool` | `true` | `✔` | +| [`me_single_endpoint_outage_disable_quarantine`](#me_single_endpoint_outage_disable_quarantine) | `bool` | `true` | `✔` | +| [`me_single_endpoint_outage_backoff_min_ms`](#me_single_endpoint_outage_backoff_min_ms) | `u64` | `250` | `✔` | +| [`me_single_endpoint_outage_backoff_max_ms`](#me_single_endpoint_outage_backoff_max_ms) | `u64` | `3000` | `✔` | +| [`me_single_endpoint_shadow_rotate_every_secs`](#me_single_endpoint_shadow_rotate_every_secs) | `u64` | `900` | `✔` | +| [`me_floor_mode`](#me_floor_mode) | `"static"` or `"adaptive"` | `"adaptive"` | `✔` | +| [`me_adaptive_floor_idle_secs`](#me_adaptive_floor_idle_secs) | `u64` | `90` | `✔` | +| [`me_adaptive_floor_min_writers_single_endpoint`](#me_adaptive_floor_min_writers_single_endpoint) | `u8` | `1` | `✔` | +| [`me_adaptive_floor_min_writers_multi_endpoint`](#me_adaptive_floor_min_writers_multi_endpoint) | `u8` | `1` | `✔` | +| [`me_adaptive_floor_recover_grace_secs`](#me_adaptive_floor_recover_grace_secs) | `u64` | `180` | `✔` | +| [`me_adaptive_floor_writers_per_core_total`](#me_adaptive_floor_writers_per_core_total) | `u16` | `48` | `✔` | +| [`me_adaptive_floor_cpu_cores_override`](#me_adaptive_floor_cpu_cores_override) | `u16` | `0` | `✔` | +| [`me_adaptive_floor_max_extra_writers_single_per_core`](#me_adaptive_floor_max_extra_writers_single_per_core) | `u16` | `1` | `✔` | +| [`me_adaptive_floor_max_extra_writers_multi_per_core`](#me_adaptive_floor_max_extra_writers_multi_per_core) | `u16` | `2` | `✔` | +| [`me_adaptive_floor_max_active_writers_per_core`](#me_adaptive_floor_max_active_writers_per_core) | `u16` | `64` | `✔` | +| [`me_adaptive_floor_max_warm_writers_per_core`](#me_adaptive_floor_max_warm_writers_per_core) | `u16` | `64` | `✔` | +| [`me_adaptive_floor_max_active_writers_global`](#me_adaptive_floor_max_active_writers_global) | `u32` | `256` | `✔` | +| [`me_adaptive_floor_max_warm_writers_global`](#me_adaptive_floor_max_warm_writers_global) | `u32` | `256` | `✔` | +| [`upstream_connect_retry_attempts`](#upstream_connect_retry_attempts) | `u32` | `2` | `✘` | +| [`upstream_connect_retry_backoff_ms`](#upstream_connect_retry_backoff_ms) | `u64` | `100` | `✘` | +| [`upstream_connect_budget_ms`](#upstream_connect_budget_ms) | `u64` | `3000` | `✘` | +| [`tg_connect`](#tg_connect) | `u64` | `10` | `✘` | +| [`upstream_unhealthy_fail_threshold`](#upstream_unhealthy_fail_threshold) | `u32` | `5` | `✘` | +| [`upstream_connect_failfast_hard_errors`](#upstream_connect_failfast_hard_errors) | `bool` | `false` | `✘` | +| [`stun_iface_mismatch_ignore`](#stun_iface_mismatch_ignore) | `bool` | `false` | `✘` | +| [`unknown_dc_log_path`](#unknown_dc_log_path) | `String` | `"unknown-dc.txt"` | `✘` | +| [`unknown_dc_file_log_enabled`](#unknown_dc_file_log_enabled) | `bool` | `false` | `✘` | +| [`log_level`](#log_level) | `"debug"`, `"verbose"`, `"normal"`, or `"silent"` | `"normal"` | `✔` | +| [`disable_colors`](#disable_colors) | `bool` | `false` | `✘` | +| [`me_socks_kdf_policy`](#me_socks_kdf_policy) | `"strict"` or `"compat"` | `"strict"` | `✔` | +| [`me_route_backpressure_enabled`](#me_route_backpressure_enabled) | `bool` | `false` | `✔` | +| [`me_route_fairshare_enabled`](#me_route_fairshare_enabled) | `bool` | `false` | `✔` | +| [`me_route_backpressure_base_timeout_ms`](#me_route_backpressure_base_timeout_ms) | `u64` | `25` | `✔` | +| [`me_route_backpressure_high_timeout_ms`](#me_route_backpressure_high_timeout_ms) | `u64` | `120` | `✔` | +| [`me_route_backpressure_high_watermark_pct`](#me_route_backpressure_high_watermark_pct) | `u8` | `80` | `✔` | +| [`me_health_interval_ms_unhealthy`](#me_health_interval_ms_unhealthy) | `u64` | `1000` | `✔` | +| [`me_health_interval_ms_healthy`](#me_health_interval_ms_healthy) | `u64` | `3000` | `✔` | +| [`me_admission_poll_ms`](#me_admission_poll_ms) | `u64` | `1000` | `✔` | +| [`me_warn_rate_limit_ms`](#me_warn_rate_limit_ms) | `u64` | `5000` | `✔` | +| [`me_route_no_writer_mode`](#me_route_no_writer_mode) | `"async_recovery_failfast"`, `"inline_recovery_legacy"`, or `"hybrid_async_persistent"` | `"hybrid_async_persistent"` | `✘` | +| [`me_route_no_writer_wait_ms`](#me_route_no_writer_wait_ms) | `u64` | `250` | `✘` | +| [`me_route_hybrid_max_wait_ms`](#me_route_hybrid_max_wait_ms) | `u64` | `3000` | `✘` | +| [`me_route_blocking_send_timeout_ms`](#me_route_blocking_send_timeout_ms) | `u64` | `250` | `✘` | +| [`me_route_inline_recovery_attempts`](#me_route_inline_recovery_attempts) | `u32` | `3` | `✘` | +| [`me_route_inline_recovery_wait_ms`](#me_route_inline_recovery_wait_ms) | `u64` | `3000` | `✘` | +| [`fast_mode_min_tls_record`](#fast_mode_min_tls_record) | `usize` | `0` | `✘` | +| [`update_every`](#update_every) | `u64` | `300` | `✔` | +| [`me_reinit_every_secs`](#me_reinit_every_secs) | `u64` | `900` | `✔` | +| [`me_hardswap_warmup_delay_min_ms`](#me_hardswap_warmup_delay_min_ms) | `u64` | `1000` | `✔` | +| [`me_hardswap_warmup_delay_max_ms`](#me_hardswap_warmup_delay_max_ms) | `u64` | `2000` | `✔` | +| [`me_hardswap_warmup_extra_passes`](#me_hardswap_warmup_extra_passes) | `u8` | `3` | `✔` | +| [`me_hardswap_warmup_pass_backoff_base_ms`](#me_hardswap_warmup_pass_backoff_base_ms) | `u64` | `500` | `✔` | +| [`me_config_stable_snapshots`](#me_config_stable_snapshots) | `u8` | `2` | `✔` | +| [`me_config_apply_cooldown_secs`](#me_config_apply_cooldown_secs) | `u64` | `300` | `✔` | +| [`me_snapshot_require_http_2xx`](#me_snapshot_require_http_2xx) | `bool` | `true` | `✔` | +| [`me_snapshot_reject_empty_map`](#me_snapshot_reject_empty_map) | `bool` | `true` | `✔` | +| [`me_snapshot_min_proxy_for_lines`](#me_snapshot_min_proxy_for_lines) | `u32` | `1` | `✔` | +| [`proxy_secret_stable_snapshots`](#proxy_secret_stable_snapshots) | `u8` | `2` | `✔` | +| [`proxy_secret_rotate_runtime`](#proxy_secret_rotate_runtime) | `bool` | `true` | `✔` | +| [`me_secret_atomic_snapshot`](#me_secret_atomic_snapshot) | `bool` | `true` | `✔` | +| [`proxy_secret_len_max`](#proxy_secret_len_max) | `usize` | `256` | `✔` | +| [`me_pool_drain_ttl_secs`](#me_pool_drain_ttl_secs) | `u64` | `90` | `✔` | +| [`me_instadrain`](#me_instadrain) | `bool` | `false` | `✔` | +| [`me_pool_drain_threshold`](#me_pool_drain_threshold) | `u64` | `32` | `✔` | +| [`me_pool_drain_soft_evict_enabled`](#me_pool_drain_soft_evict_enabled) | `bool` | `true` | `✘` | +| [`me_pool_drain_soft_evict_grace_secs`](#me_pool_drain_soft_evict_grace_secs) | `u64` | `10` | `✘` | +| [`me_pool_drain_soft_evict_per_writer`](#me_pool_drain_soft_evict_per_writer) | `u8` | `2` | `✘` | +| [`me_pool_drain_soft_evict_budget_per_core`](#me_pool_drain_soft_evict_budget_per_core) | `u16` | `16` | `✘` | +| [`me_pool_drain_soft_evict_cooldown_ms`](#me_pool_drain_soft_evict_cooldown_ms) | `u64` | `1000` | `✘` | +| [`me_bind_stale_mode`](#me_bind_stale_mode) | `"never"`, `"ttl"`, or `"always"` | `"ttl"` | `✔` | +| [`me_bind_stale_ttl_secs`](#me_bind_stale_ttl_secs) | `u64` | `90` | `✔` | +| [`me_pool_min_fresh_ratio`](#me_pool_min_fresh_ratio) | `f32` | `0.8` | `✔` | +| [`me_reinit_drain_timeout_secs`](#me_reinit_drain_timeout_secs) | `u64` | `90` | `✔` | +| [`proxy_secret_auto_reload_secs`](#proxy_secret_auto_reload_secs) | `u64` | `3600` | `✔` | +| [`proxy_config_auto_reload_secs`](#proxy_config_auto_reload_secs) | `u64` | `3600` | `✔` | +| [`me_reinit_singleflight`](#me_reinit_singleflight) | `bool` | `true` | `✔` | +| [`me_reinit_trigger_channel`](#me_reinit_trigger_channel) | `usize` | `64` | `✘` | +| [`me_reinit_coalesce_window_ms`](#me_reinit_coalesce_window_ms) | `u64` | `200` | `✔` | +| [`me_deterministic_writer_sort`](#me_deterministic_writer_sort) | `bool` | `true` | `✔` | +| [`me_writer_pick_mode`](#me_writer_pick_mode) | `"sorted_rr"` or `"p2c"` | `"p2c"` | `✔` | +| [`me_writer_pick_sample_size`](#me_writer_pick_sample_size) | `u8` | `3` | `✔` | +| [`ntp_check`](#ntp_check) | `bool` | `true` | `✘` | +| [`ntp_servers`](#ntp_servers) | `String[]` | `["pool.ntp.org"]` | `✘` | +| [`auto_degradation_enabled`](#auto_degradation_enabled) | `bool` | `true` | `✘` | +| [`degradation_min_unavailable_dc_groups`](#degradation_min_unavailable_dc_groups) | `u8` | `2` | `✘` | +| [`rst_on_close`](#rst_on_close) | `"off"`, `"errors"` или `"always"` | `"off"` | `✘` | ## data_path - **Ограничения / валидация**: `String` (необязательный параметр). @@ -228,6 +379,24 @@ [general] data_path = "/var/lib/telemt" ``` +## quota_state_path + - **Ограничения / валидация**: `Path`. Относительные пути разрешаются от рабочего каталога процесса. + - **Описание**: JSON-файл состояния для сохранения runtime-расхода квот по пользователям. + - **Пример**: + + ```toml + [general] + quota_state_path = "telemt.limit.json" + ``` +## config_strict + - **Ограничения / валидация**: `bool`. + - **Описание**: Отклоняет неизвестные TOML-ключи во время загрузки конфигурации. При запуске процесс завершается с ошибкой; при hot-reload новый снимок отклоняется, а текущая конфигурация сохраняется. + - **Пример**: + + ```toml + [general] + config_strict = true + ``` ## prefer_ipv6 - **Ограничения / валидация**: Устарело. Используйте `network.prefer`. - **Описание**: Устаревший флаг предпочтения IPv6 перенесен в `network.prefer`. @@ -905,6 +1074,15 @@ [general] upstream_connect_budget_ms = 3000 ``` +## tg_connect + - **Ограничения / валидация**: Должно быть `> 0` (секунды). + - **Описание**: Таймаут подключения к upstream-серверам Telegram. + - **Пример**: + + ```toml + [general] + tg_connect = 10 + ``` ## upstream_unhealthy_fail_threshold - **Ограничения / валидация**: Должно быть `> 0`. - **Описание**: Количество неудачных запросов подряд, после которого upstream помечается, как неработоспособный. @@ -1522,11 +1700,11 @@ # [general.modes] -| Ключ | Тип | По умолчанию | -| --- | ---- | ------- | -| [`classic`](#classic) | `bool` | `false` | -| [`secure`](#secure) | `bool` | `false` | -| [`tls`](#tls) | `bool` | `true` | +| Ключ | Тип | По умолчанию | Hot-Reload | +| --- | ---- | ------- | ---------- | +| [`classic`](#classic) | `bool` | `false` | `✘` | +| [`secure`](#secure) | `bool` | `false` | `✘` | +| [`tls`](#tls) | `bool` | `true` | `✘` | ## classic - **Ограничения / валидация**: `bool`. @@ -1560,11 +1738,11 @@ # [general.links] -| Ключ | Тип | По умолчанию | -| --- | ---- | ------- | -| [`show`](#show) | `"*"` or `String[]` | `"*"` | -| [`public_host`](#public_host) | `String` | — | -| [`public_port`](#public_port) | `u16` | — | +| Ключ | Тип | По умолчанию | Hot-Reload | +| --- | ---- | ------- | ---------- | +| [`show`](#show) | `"*"` or `String[]` | `"*"` | `✘` | +| [`public_host`](#public_host) | `String` | — | `✘` | +| [`public_port`](#public_port) | `u16` | — | `✘` | ## show - **Ограничения / валидация**: `"*"` или `String[]`. Пустое значение означает, что нельзя показывать никому. @@ -1600,11 +1778,11 @@ # [general.telemetry] -| Ключ | Тип | По умолчанию | -| --- | ---- | ------- | -| [`core_enabled`](#core_enabled) | `bool` | `true` | -| [`user_enabled`](#user_enabled) | `bool` | `true` | -| [`me_level`](#me_level) | `"silent"`, `"normal"`, or `"debug"` | `"normal"` | +| Ключ | Тип | По умолчанию | Hot-Reload | +| --- | ---- | ------- | ---------- | +| [`core_enabled`](#core_enabled) | `bool` | `true` | `✔` | +| [`user_enabled`](#user_enabled) | `bool` | `true` | `✔` | +| [`me_level`](#me_level) | `"silent"`, `"normal"`, or `"debug"` | `"normal"` | `✔` | ## core_enabled - **Ограничения / валидация**: `bool`. @@ -1638,18 +1816,18 @@ # [network] -| Ключ | Тип | По умолчанию | -| --- | ---- | ------- | -| [`ipv4`](#ipv4) | `bool` | `true` | -| [`ipv6`](#ipv6) | `bool` | `false` | -| [`prefer`](#prefer) | `u8` | `4` | -| [`multipath`](#multipath) | `bool` | `false` | -| [`stun_use`](#stun_use) | `bool` | `true` | -| [`stun_servers`](#stun_servers) | `String[]` | Встроенный STUN-лист (13 записей) | -| [`stun_tcp_fallback`](#stun_tcp_fallback) | `bool` | `true` | -| [`http_ip_detect_urls`](#http_ip_detect_urls) | `String[]` | `["https://ifconfig.me/ip", "https://api.ipify.org"]` | -| [`cache_public_ip_path`](#cache_public_ip_path) | `String` | `"cache/public_ip.txt"` | -| [`dns_overrides`](#dns_overrides) | `String[]` | `[]` | +| Ключ | Тип | По умолчанию | Hot-Reload | +| --- | ---- | ------- | ---------- | +| [`ipv4`](#ipv4) | `bool` | `true` | `✘` | +| [`ipv6`](#ipv6) | `bool` | `false` | `✘` | +| [`prefer`](#prefer) | `u8` | `4` | `✘` | +| [`multipath`](#multipath) | `bool` | `false` | `✘` | +| [`stun_use`](#stun_use) | `bool` | `true` | `✘` | +| [`stun_servers`](#stun_servers) | `String[]` | Встроенный STUN-лист (13 записей) | `✘` | +| [`stun_tcp_fallback`](#stun_tcp_fallback) | `bool` | `true` | `✘` | +| [`http_ip_detect_urls`](#http_ip_detect_urls) | `String[]` | `["https://ifconfig.me/ip", "https://api.ipify.org"]` | `✘` | +| [`cache_public_ip_path`](#cache_public_ip_path) | `String` | `"cache/public_ip.txt"` | `✘` | +| [`dns_overrides`](#dns_overrides) | `String[]` | `[]` | `✔` | ## ipv4 - **Ограничения / валидация**: `bool`. @@ -1759,23 +1937,27 @@ # [server] -| Ключ | Тип | По умолчанию | -| --- | ---- | ------- | -| [`port`](#port) | `u16` | `443` | -| [`listen_addr_ipv4`](#listen_addr_ipv4) | `String` | `"0.0.0.0"` | -| [`listen_addr_ipv6`](#listen_addr_ipv6) | `String` | `"::"` | -| [`listen_unix_sock`](#listen_unix_sock) | `String` | — | -| [`listen_unix_sock_perm`](#listen_unix_sock_perm) | `String` | — | -| [`listen_tcp`](#listen_tcp) | `bool` | — (auto) | -| [`proxy_protocol`](#proxy_protocol) | `bool` | `false` | -| [`proxy_protocol_header_timeout_ms`](#proxy_protocol_header_timeout_ms) | `u64` | `500` | -| [`proxy_protocol_trusted_cidrs`](#proxy_protocol_trusted_cidrs) | `IpNetwork[]` | `[]` | -| [`metrics_port`](#metrics_port) | `u16` | — | -| [`metrics_listen`](#metrics_listen) | `String` | — | -| [`metrics_whitelist`](#metrics_whitelist) | `IpNetwork[]` | `["127.0.0.1/32", "::1/128"]` | -| [`max_connections`](#max_connections) | `u32` | `10000` | -| [`accept_permit_timeout_ms`](#accept_permit_timeout_ms) | `u64` | `250` | -| [`listen_backlog`](#listen_backlog) | `u32` | `1024` | +| Ключ | Тип | По умолчанию | Hot-Reload | +| --- | ---- | ------- | ---------- | +| [`port`](#port) | `u16` | `443` | `✘` | +| [`listen_addr_ipv4`](#listen_addr_ipv4) | `String` | `"0.0.0.0"` | `✘` | +| [`listen_addr_ipv6`](#listen_addr_ipv6) | `String` | `"::"` | `✘` | +| [`listen_unix_sock`](#listen_unix_sock) | `String` | — | `✘` | +| [`listen_unix_sock_perm`](#listen_unix_sock_perm) | `String` | — | `✘` | +| [`listen_tcp`](#listen_tcp) | `bool` | — (auto) | `✘` | +| [`proxy_protocol`](#proxy_protocol) | `bool` | `false` | `✘` | +| [`proxy_protocol_header_timeout_ms`](#proxy_protocol_header_timeout_ms) | `u64` | `500` | `✘` | +| [`proxy_protocol_trusted_cidrs`](#proxy_protocol_trusted_cidrs) | `IpNetwork[]` | `[]` | `✘` | +| [`metrics_port`](#metrics_port) | `u16` | — | `✘` | +| [`metrics_listen`](#metrics_listen) | `String` | — | `✘` | +| [`metrics_whitelist`](#metrics_whitelist) | `IpNetwork[]` | `["127.0.0.1/32", "::1/128"]` | `✘` | +| [`api`](#serverapi) | `Table` | встроенные значения | `✘` | +| [`admin_api`](#serverapi) | `Table` | алиас для `api` | `✘` | +| [`listeners`](#serverlisteners) | `Table[]` | выводится из legacy listener-полей | `✘` | +| [`max_connections`](#max_connections) | `u32` | `10000` | `✘` | +| [`accept_permit_timeout_ms`](#accept_permit_timeout_ms) | `u64` | `250` | `✘` | +| [`listen_backlog`](#listen_backlog) | `u32` | `1024` | `✘` | +| [`conntrack_control`](#serverconntrack_control) | `Table` | встроенные значения | `✘` | ## port - **Ограничения / валидация**: `u16`. @@ -1931,16 +2113,16 @@ Примечание. Рабочий процесс `conntrack-control` работает **только в Linux**. В других операционных системах не запускается; если inline_conntrack_control имеет значение `true`, в логи записывается предупреждение. Для эффективной работы также требуется **CAP_NET_ADMIN** и пригодный к использованию бэкенд (nft или iptables/ip6tables в PATH). Утилита `conntrack` используется для удаления необязательных записей таблицы под нагрузкой. -| Ключ | Тип | По умолчанию | -| --- | ---- | ------- | -| [`inline_conntrack_control`](#inline_conntrack_control) | `bool` | `true` | -| [`mode`](#mode) | `String` | `"tracked"` | -| [`backend`](#backend) | `String` | `"auto"` | -| [`profile`](#profile) | `String` | `"balanced"` | -| [`hybrid_listener_ips`](#hybrid_listener_ips) | `IpAddr[]` | `[]` | -| [`pressure_high_watermark_pct`](#pressure_high_watermark_pct) | `u8` | `85` | -| [`pressure_low_watermark_pct`](#pressure_low_watermark_pct) | `u8` | `70` | -| [`delete_budget_per_sec`](#delete_budget_per_sec) | `u64` | `4096` | +| Ключ | Тип | По умолчанию | Hot-Reload | +| --- | ---- | ------- | ---------- | +| [`inline_conntrack_control`](#inline_conntrack_control) | `bool` | `true` | `✘` | +| [`mode`](#mode) | `String` | `"tracked"` | `✘` | +| [`backend`](#backend) | `String` | `"auto"` | `✘` | +| [`profile`](#profile) | `String` | `"balanced"` | `✘` | +| [`hybrid_listener_ips`](#hybrid_listener_ips) | `IpAddr[]` | `[]` | `✘` | +| [`pressure_high_watermark_pct`](#pressure_high_watermark_pct) | `u8` | `85` | `✘` | +| [`pressure_low_watermark_pct`](#pressure_low_watermark_pct) | `u8` | `70` | `✘` | +| [`delete_budget_per_sec`](#delete_budget_per_sec) | `u64` | `4096` | `✘` | ## inline_conntrack_control - **Ограничения / валидация**: `bool`. @@ -2027,21 +2209,21 @@ Примечание: В этом разделе также задается устаревший параметр `[server.admin_api]` (аналогично `[server.api]`). -| Ключ | Тип | По умолчанию | -| --- | ---- | ------- | -| [`enabled`](#enabled) | `bool` | `true` | -| [`listen`](#listen) | `String` | `"0.0.0.0:9091"` | -| [`whitelist`](#whitelist) | `IpNetwork[]` | `["127.0.0.0/8"]` | -| [`auth_header`](#auth_header) | `String` | `""` | -| [`request_body_limit_bytes`](#request_body_limit_bytes) | `usize` | `65536` | -| [`minimal_runtime_enabled`](#minimal_runtime_enabled) | `bool` | `true` | -| [`minimal_runtime_cache_ttl_ms`](#minimal_runtime_cache_ttl_ms) | `u64` | `1000` | -| [`runtime_edge_enabled`](#runtime_edge_enabled) | `bool` | `false` | -| [`runtime_edge_cache_ttl_ms`](#runtime_edge_cache_ttl_ms) | `u64` | `1000` | -| [`runtime_edge_top_n`](#runtime_edge_top_n) | `usize` | `10` | -| [`runtime_edge_events_capacity`](#runtime_edge_events_capacity) | `usize` | `256` | -| [`read_only`](#read_only) | `bool` | `false` | -| [`gray_action`](#gray_action) | `"drop"`, `"api"`, or `"200"` | `"drop"` | +| Ключ | Тип | По умолчанию | Hot-Reload | +| --- | ---- | ------- | ---------- | +| [`enabled`](#enabled) | `bool` | `true` | `✘` | +| [`listen`](#listen) | `String` | `"0.0.0.0:9091"` | `✘` | +| [`whitelist`](#whitelist) | `IpNetwork[]` | `["127.0.0.0/8"]` | `✘` | +| [`auth_header`](#auth_header) | `String` | `""` | `✘` | +| [`request_body_limit_bytes`](#request_body_limit_bytes) | `usize` | `65536` | `✘` | +| [`minimal_runtime_enabled`](#minimal_runtime_enabled) | `bool` | `true` | `✘` | +| [`minimal_runtime_cache_ttl_ms`](#minimal_runtime_cache_ttl_ms) | `u64` | `1000` | `✘` | +| [`runtime_edge_enabled`](#runtime_edge_enabled) | `bool` | `false` | `✘` | +| [`runtime_edge_cache_ttl_ms`](#runtime_edge_cache_ttl_ms) | `u64` | `1000` | `✘` | +| [`runtime_edge_top_n`](#runtime_edge_top_n) | `usize` | `10` | `✘` | +| [`runtime_edge_events_capacity`](#runtime_edge_events_capacity) | `usize` | `256` | `✘` | +| [`read_only`](#read_only) | `bool` | `false` | `✘` | +| [`gray_action`](#gray_action) | `"drop"`, `"api"`, or `"200"` | `"drop"` | `✘` | ## enabled - **Ограничения / валидация**: `bool`. @@ -2165,13 +2347,14 @@ # [[server.listeners]] -| Ключ | Тип | По умолчанию | -| --- | ---- | ------- | -| [`ip`](#ip) | `IpAddr` | — | -| [`announce`](#announce) | `String` | — | -| [`announce_ip`](#announce_ip) | `IpAddr` | — | -| [`proxy_protocol`](#proxy_protocol) | `bool` | — | -| [`reuse_allow`](#reuse_allow) | `bool` | `false` | +| Ключ | Тип | По умолчанию | Hot-Reload | +| --- | ---- | ------- | ---------- | +| [`ip`](#ip) | `IpAddr` | — | `✘` | +| [`port`](#port-serverlisteners) | `u16` | `server.port` | `✘` | +| [`announce`](#announce) | `String` | — | `✘` | +| [`announce_ip`](#announce_ip) | `IpAddr` | — | `✘` | +| [`proxy_protocol`](#proxy_protocol) | `bool` | — | `✘` | +| [`reuse_allow`](#reuse_allow) | `bool` | `false` | `✘` | ## ip - **Ограничения / валидация**: Обязательный параметр. Значение должно содержать IP-адрес в формате строки. @@ -2182,6 +2365,16 @@ [[server.listeners]] ip = "0.0.0.0" ``` +## port (server.listeners) + - **Ограничения / валидация**: `u16` (необязательный параметр). Если не задан, используется `server.port`. + - **Описание**: TCP-порт для конкретного listener’а. + - **Пример**: + + ```toml + [[server.listeners]] + ip = "0.0.0.0" + port = 443 + ``` ## announce - **Ограничения / валидация**: `String` (необязательный параметр). Не должен быть пустым, если задан. - **Описание**: Публичный IP-адрес или домен, объявляемый в proxy-ссылках для данного listener’а. Имеет приоритет над `announce_ip`. @@ -2215,8 +2408,7 @@ ip = "0.0.0.0" proxy_protocol = true ``` -## reuse_allow" -- `reuse_allow` +## reuse_allow - **Ограничения / валидация**: `bool`. - **Описание**: Включает `SO_REUSEPORT` для совместного использования привязки нескольких экземпляров (позволяет нескольким экземплярам telemt прослушивать один и тот же `ip:port`). - **Пример**: @@ -2231,18 +2423,18 @@ # [timeouts] -| Ключ | Тип | По умолчанию | -| --- | ---- | ------- | -| [`client_handshake`](#client_handshake) | `u64` | `30` | -| [`relay_idle_policy_v2_enabled`](#relay_idle_policy_v2_enabled) | `bool` | `true` | -| [`relay_client_idle_soft_secs`](#relay_client_idle_soft_secs) | `u64` | `120` | -| [`relay_client_idle_hard_secs`](#relay_client_idle_hard_secs) | `u64` | `360` | -| [`relay_idle_grace_after_downstream_activity_secs`](#relay_idle_grace_after_downstream_activity_secs) | `u64` | `30` | -| [`tg_connect`](#tg_connect) | `u64` | `10` | -| [`client_keepalive`](#client_keepalive) | `u64` | `15` | -| [`client_ack`](#client_ack) | `u64` | `90` | -| [`me_one_retry`](#me_one_retry) | `u8` | `12` | -| [`me_one_timeout_ms`](#me_one_timeout_ms) | `u64` | `1200` | +| Ключ | Тип | По умолчанию | Hot-Reload | +| --- | ---- | ------- | ---------- | +| [`client_first_byte_idle_secs`](#client_first_byte_idle_secs) | `u64` | `300` | `✘` | +| [`client_handshake`](#client_handshake) | `u64` | `30` | `✘` | +| [`relay_idle_policy_v2_enabled`](#relay_idle_policy_v2_enabled) | `bool` | `true` | `✘` | +| [`relay_client_idle_soft_secs`](#relay_client_idle_soft_secs) | `u64` | `120` | `✘` | +| [`relay_client_idle_hard_secs`](#relay_client_idle_hard_secs) | `u64` | `360` | `✘` | +| [`relay_idle_grace_after_downstream_activity_secs`](#relay_idle_grace_after_downstream_activity_secs) | `u64` | `30` | `✘` | +| [`client_keepalive`](#client_keepalive) | `u64` | `15` | `✘` | +| [`client_ack`](#client_ack) | `u64` | `90` | `✘` | +| [`me_one_retry`](#me_one_retry) | `u8` | `12` | `✘` | +| [`me_one_timeout_ms`](#me_one_timeout_ms) | `u64` | `1200` | `✘` | ## client_handshake - **Ограничения / валидация**: Должно быть `> 0`. Значение указано в секундах. Также используется в качестве верхней границы некоторых задержек эмуляции TLS (см. `censorship.server_hello_delay_max_ms`). @@ -2298,15 +2490,6 @@ [timeouts] relay_idle_grace_after_downstream_activity_secs = 30 ``` -## tg_connect - - **Ограничения / валидация**: `u64` (секунд). - - **Описание**: Таймаут подключения к upstream-серверу Telegram (в секундах). - - **Пример**: - - ```toml - [timeouts] - tg_connect = 10 - ``` ## client_keepalive - **Ограничения / валидация**: `u64` (секунд). - **Описание**: Таймаут keepalive для клиента.. @@ -2383,6 +2566,40 @@ | [`mask_timing_normalization_enabled`](#mask_timing_normalization_enabled) | `bool` | `false` | | [`mask_timing_normalization_floor_ms`](#mask_timing_normalization_floor_ms) | `u64` | `0` | | [`mask_timing_normalization_ceiling_ms`](#mask_timing_normalization_ceiling_ms) | `u64` | `0` | +| Ключ | Тип | По умолчанию | Hot-Reload | +| --- | ---- | ------- | ---------- | +| [`tls_domain`](#tls_domain) | `String` | `"petrovich.ru"` | `✘` | +| [`tls_domains`](#tls_domains) | `String[]` | `[]` | `✘` | +| [`unknown_sni_action`](#unknown_sni_action) | `"drop"`, `"mask"`, `"accept"`, `"reject_handshake"` | `"drop"` | `✘` | +| [`tls_fetch_scope`](#tls_fetch_scope) | `String` | `""` | `✘` | +| [`tls_fetch`](#tls_fetch) | `Table` | built-in defaults | `✘` | +| [`mask`](#mask) | `bool` | `true` | `✘` | +| [`mask_host`](#mask_host) | `String` | — | `✘` | +| [`mask_port`](#mask_port) | `u16` | `443` | `✘` | +| [`mask_unix_sock`](#mask_unix_sock) | `String` | — | `✘` | +| [`fake_cert_len`](#fake_cert_len) | `usize` | `2048` | `✘` | +| [`tls_emulation`](#tls_emulation) | `bool` | `true` | `✘` | +| [`tls_front_dir`](#tls_front_dir) | `String` | `"tlsfront"` | `✘` | +| [`server_hello_delay_min_ms`](#server_hello_delay_min_ms) | `u64` | `0` | `✘` | +| [`server_hello_delay_max_ms`](#server_hello_delay_max_ms) | `u64` | `0` | `✘` | +| [`tls_new_session_tickets`](#tls_new_session_tickets) | `u8` | `0` | `✘` | +| [`tls_full_cert_ttl_secs`](#tls_full_cert_ttl_secs) | `u64` | `90` | `✘` | +| [`serverhello_compact`](#serverhello_compact) | `bool` | `false` | `✘` | +| [`alpn_enforce`](#alpn_enforce) | `bool` | `true` | `✘` | +| [`mask_proxy_protocol`](#mask_proxy_protocol) | `u8` | `0` | `✘` | +| [`mask_shape_hardening`](#mask_shape_hardening) | `bool` | `true` | `✘` | +| [`mask_shape_hardening_aggressive_mode`](#mask_shape_hardening_aggressive_mode) | `bool` | `false` | `✘` | +| [`mask_shape_bucket_floor_bytes`](#mask_shape_bucket_floor_bytes) | `usize` | `512` | `✘` | +| [`mask_shape_bucket_cap_bytes`](#mask_shape_bucket_cap_bytes) | `usize` | `4096` | `✘` | +| [`mask_shape_above_cap_blur`](#mask_shape_above_cap_blur) | `bool` | `false` | `✘` | +| [`mask_shape_above_cap_blur_max_bytes`](#mask_shape_above_cap_blur_max_bytes) | `usize` | `512` | `✘` | +| [`mask_relay_max_bytes`](#mask_relay_max_bytes) | `usize` | `5242880` | `✘` | +| [`mask_relay_timeout_ms`](mask_relay_timeout_ms) | `u64` | `60_000` | `✘` | +| [`mask_relay_idle_timeout_ms`](mask_relay_idle_timeout_ms) | `u64` | `5_000` | `✘` | +| [`mask_classifier_prefetch_timeout_ms`](#mask_classifier_prefetch_timeout_ms) | `u64` | `5` | `✘` | +| [`mask_timing_normalization_enabled`](#mask_timing_normalization_enabled) | `bool` | `false` | `✘` | +| [`mask_timing_normalization_floor_ms`](#mask_timing_normalization_floor_ms) | `u64` | `0` | `✘` | +| [`mask_timing_normalization_ceiling_ms`](#mask_timing_normalization_ceiling_ms) | `u64` | `0` | `✘` | ## tls_domain - **Ограничения / валидация**: Не должно быть пустым. Не должно содержать пробелы или `/`. @@ -2817,15 +3034,15 @@ # [censorship.tls_fetch] -| Ключ | Тип | По умолчанию | -| --- | ---- | ------- | -| [`profiles`](#profiles) | `String[]` | `["modern_chrome_like", "modern_firefox_like", "compat_tls12", "legacy_minimal"]` | -| [`strict_route`](#strict_route) | `bool` | `true` | -| [`attempt_timeout_ms`](#attempt_timeout_ms) | `u64` | `5000` | -| [`total_budget_ms`](#total_budget_ms) | `u64` | `15000` | -| [`grease_enabled`](#grease_enabled) | `bool` | `false` | -| [`deterministic`](#deterministic) | `bool` | `false` | -| [`profile_cache_ttl_secs`](#profile_cache_ttl_secs) | `u64` | `600` | +| Ключ | Тип | По умолчанию | Hot-Reload | +| --- | ---- | ------- | ---------- | +| [`profiles`](#profiles) | `String[]` | `["modern_chrome_like", "modern_firefox_like", "compat_tls12", "legacy_minimal"]` | `✘` | +| [`strict_route`](#strict_route) | `bool` | `true` | `✘` | +| [`attempt_timeout_ms`](#attempt_timeout_ms) | `u64` | `5000` | `✘` | +| [`total_budget_ms`](#total_budget_ms) | `u64` | `15000` | `✘` | +| [`grease_enabled`](#grease_enabled) | `bool` | `false` | `✘` | +| [`deterministic`](#deterministic) | `bool` | `false` | `✘` | +| [`profile_cache_ttl_secs`](#profile_cache_ttl_secs) | `u64` | `600` | `✘` | ## profiles - **Ограничения / валидация**: `String[]`. Пустой список возвращает значения по умолчанию; дубликаты удаляются с сохранением порядка. @@ -2894,23 +3111,24 @@ # [access] -| Ключ | Тип | По умолчанию | -| --- | ---- | ------- | -| [`users`](#users) | `Map` | `{"default": "000…000"}` | -| [`user_ad_tags`](#user_ad_tags) | `Map` | `{}` | -| [`user_max_tcp_conns`](#user_max_tcp_conns) | `Map` | `{}` | -| [`user_max_tcp_conns_global_each`](#user_max_tcp_conns_global_each) | `usize` | `0` | -| [`user_expirations`](#user_expirations) | `Map>` | `{}` | -| [`user_data_quota`](#user_data_quota) | `Map` | `{}` | -| [`user_max_unique_ips`](#user_max_unique_ips) | `Map` | `{}` | -| [`user_max_unique_ips_global_each`](#user_max_unique_ips_global_each) | `usize` | `0` | -| [`user_max_unique_ips_mode`](#user_max_unique_ips_mode) | `"active_window"`, `"time_window"`, or `"combined"` | `"active_window"` | -| [`user_max_unique_ips_window_secs`](#user_max_unique_ips_window_secs) | `u64` | `30` | -| [`replay_check_len`](#replay_check_len) | `usize` | `65536` | -| [`replay_window_secs`](#replay_window_secs) | `u64` | `120` | -| [`ignore_time_skew`](#ignore_time_skew) | `bool` | `false` | -| [`user_rate_limits`](#user_rate_limits) | `Map` | `{}` | -| [`cidr_rate_limits`](#cidr_rate_limits) | `Map` | `{}` | +| Ключ | Тип | По умолчанию | Hot-Reload | +| --- | ---- | ------- | ---------- | +| [`users`](#users) | `Map` | `{"default": "000…000"}` | `✔` | +| [`user_ad_tags`](#user_ad_tags) | `Map` | `{}` | `✔` | +| [`user_max_tcp_conns`](#user_max_tcp_conns) | `Map` | `{}` | `✔` | +| [`user_max_tcp_conns_global_each`](#user_max_tcp_conns_global_each) | `usize` | `0` | `✔` | +| [`user_expirations`](#user_expirations) | `Map>` | `{}` | `✔` | +| [`user_data_quota`](#user_data_quota) | `Map` | `{}` | `✔` | +| [`user_max_unique_ips`](#user_max_unique_ips) | `Map` | `{}` | `✔` | +| [`user_max_unique_ips_global_each`](#user_max_unique_ips_global_each) | `usize` | `0` | `✔` | +| [`user_max_unique_ips_mode`](#user_max_unique_ips_mode) | `"active_window"`, `"time_window"`, or `"combined"` | `"active_window"` | `✔` | +| [`user_max_unique_ips_window_secs`](#user_max_unique_ips_window_secs) | `u64` | `30` | `✔` | +| [`user_source_deny`](#user_source_deny) | `Map` | `{}` | `✘` | +| [`replay_check_len`](#replay_check_len) | `usize` | `65536` | `✘` | +| [`replay_window_secs`](#replay_window_secs) | `u64` | `120` | `✘` | +| [`ignore_time_skew`](#ignore_time_skew) | `bool` | `false` | `✘` | +| [`user_rate_limits`](#user_rate_limits) | `Map` | `{}` | `✔` | +| [`cidr_rate_limits`](#cidr_rate_limits) | `Map` | `{}` | `✔` | ## users - **Ограничения / валидация**: Не должно быть пустым (должен существовать хотя бы один пользователь). Каждое значение должно состоять **ровно из 32 шестнадцатеричных символов**. @@ -3010,6 +3228,20 @@ [access] user_max_unique_ips_window_secs = 30 ``` +## user_source_deny + - **Ограничения / валидация**: Таблица `username -> IpNetwork[]`. Каждая сеть должна разбираться как CIDR, например `203.0.113.0/24` или `2001:db8::/32`. + - **Описание**: Deny-list исходных IP/CIDR для конкретного пользователя, применяемый **после успешной аутентификации** в TLS- и MTProto-handshake путях. Совпавший source IP отклоняется тем же fail-closed путём, что и невалидная аутентификация. + - **Пример**: + + ```toml + [access.user_source_deny] + alice = ["203.0.113.0/24", "2001:db8:abcd::/48"] + bob = ["198.51.100.42/32"] + ``` + + - **Краткая проверка**: + - соединение пользователя `alice` с source `203.0.113.55` отклоняется, потому что совпадает с `203.0.113.0/24`; + - соединение пользователя `alice` с source `198.51.100.10` допускается этим набором правил, потому что совпадений нет. ## replay_check_len - **Ограничения / валидация**: `usize`. - **Описание**: Количество последних сообщений/запросов, которое система запоминает, чтобы не допустить их повторной отправки (replay). @@ -3060,19 +3292,23 @@ # [[upstreams]] -| Ключ | Тип | По умолчанию | -| --- | ---- | ------- | -| [`type`](#type) | `"direct"`, `"socks4"`, `"socks5"`, or `"shadowsocks"` | — | -| [`weight`](#weight) | `u16` | `1` | -| [`enabled`](#enabled) | `bool` | `true` | -| [`scopes`](#scopes) | `String` | `""` | -| [`interface`](#interface) | `String` | — | -| [`bind_addresses`](#bind_addresses) | `String[]` | — | -| [`url`](#url) | `String` | — | -| [`address`](#address) | `String` | — | -| [`user_id`](#user_id) | `String` | — | -| [`username`](#username) | `String` | — | -| [`password`](#password) | `String` | — | +| Ключ | Тип | По умолчанию | Hot-Reload | +| --- | ---- | ------- | ---------- | +| [`type`](#type) | `"direct"`, `"socks4"`, `"socks5"`, or `"shadowsocks"` | — | `✘` | +| [`weight`](#weight) | `u16` | `1` | `✘` | +| [`enabled`](#enabled) | `bool` | `true` | `✘` | +| [`scopes`](#scopes) | `String` | `""` | `✘` | +| [`ipv4`](#ipv4-upstreams) | `bool` | — (auto) | `✘` | +| [`ipv6`](#ipv6-upstreams) | `bool` | — (auto) | `✘` | +| [`interface`](#interface) | `String` | — | `✘` | +| [`bind_addresses`](#bind_addresses) | `String[]` | — | `✘` | +| [`bindtodevice`](#bindtodevice) | `String` | — | `✘` | +| [`force_bind`](#force_bind) | `String` | — | `✘` | +| [`url`](#url) | `String` | — | `✘` | +| [`address`](#address) | `String` | — | `✘` | +| [`user_id`](#user_id) | `String` | — | `✘` | +| [`username`](#username) | `String` | — | `✘` | +| [`password`](#password) | `String` | — | `✘` | ## type - **Ограничения / валидация**: Обязательный параметр.`"direct"`, `"socks4"`, `"socks5"`, `"shadowsocks"`. @@ -3123,6 +3359,26 @@ address = "10.0.0.10:1080" scopes = "me, fetch, dc2" ``` +## ipv4 (upstreams) + - **Ограничения / валидация**: `bool` (необязательный параметр). + - **Описание**: Разрешает IPv4 DC-targets для этого upstream. Если не задан, Telemt определяет поддержку автоматически по runtime-состоянию connectivity. + - **Пример**: + + ```toml + [[upstreams]] + type = "direct" + ipv4 = true + ``` +## ipv6 (upstreams) + - **Ограничения / валидация**: `bool` (необязательный параметр). + - **Описание**: Разрешает IPv6 DC-targets для этого upstream. Если не задан, Telemt определяет поддержку автоматически по runtime-состоянию connectivity. + - **Пример**: + + ```toml + [[upstreams]] + type = "direct" + ipv6 = false + ``` ## interface - **Ограничения / валидация**: `String` (необязательный параметр). - для `"direct"`: может быть IP-адресом (используется как явный local bind) или именем сетевого интерфейса ОС (резолвится в IP во время выполнения; только Unix). @@ -3153,6 +3409,26 @@ type = "direct" bind_addresses = ["192.0.2.10", "192.0.2.11"] ``` +## bindtodevice + - **Ограничения / валидация**: `String` (необязательный параметр). Применяется только для `type = "direct"` и только в Linux. + - **Описание**: Жёсткая привязка исходящих direct TCP-connect к интерфейсу через `SO_BINDTODEVICE`. + - **Пример**: + + ```toml + [[upstreams]] + type = "direct" + bindtodevice = "eth0" + ``` +## force_bind + - **Ограничения / валидация**: `String` (необязательный параметр). Алиас для `bindtodevice`. + - **Описание**: Обратно-совместимый алиас для жёсткой Linux-привязки к интерфейсу через `SO_BINDTODEVICE`. + - **Пример**: + + ```toml + [[upstreams]] + type = "direct" + force_bind = "eth0" + ``` ## url - **Ограничения / валидация**: Применяется в случае, если `type = "shadowsocks"`. - Должен быть действительный URL-адрес Shadowsocks, принятый `shadowsocks` контейнером. diff --git a/install.sh b/install.sh index cad8ca7..78c0678 100644 --- a/install.sh +++ b/install.sh @@ -105,6 +105,7 @@ set_language() { L_OUT_UNINST_H="УДАЛЕНИЕ ЗАВЕРШЕНО" L_OUT_LINK="Ваша ссылка для подключения к Telegram Proxy:\n" L_ERR_INCORR_ROOT_LOGIN="Используйте 'su -' или 'sudo -i' для входа под пользователем root" + L_OUT_LOGS="Чтобы посмотреть логи (в случае проблем), используйте команду:" ;; *) L_ERR_DOMAIN_REQ="requires a domain argument." @@ -180,6 +181,7 @@ set_language() { L_OUT_UNINST_H="UNINSTALLATION COMPLETE" L_OUT_LINK="Your Telegram Proxy connection link:\n" L_ERR_INCORR_ROOT_LOGIN="Use 'su -' or 'sudo -i' to login under root" + L_OUT_LOGS="To view logs (in case of issues), use the following command:" ;; esac } @@ -392,7 +394,7 @@ verify_common() { if [ "$(id -u)" -eq 0 ]; then SUDO="" - if [ "$(id -u)" -ne 0 ]; then + if [ "${USER:-}" != "root" ] && [ "${LOGNAME:-}" != "root" ]; then die "$L_ERR_INCORR_ROOT_LOGIN" fi else @@ -539,7 +541,7 @@ install_binary() { fi $SUDO mkdir -p "$INSTALL_DIR" || die "$L_ERR_MKDIR" - + $SUDO rm -f "$bin_dst" 2>/dev/null || true if command -v install >/dev/null 2>&1; then @@ -609,33 +611,33 @@ install_config() { tmp_conf="${TEMP_DIR}/config.tmp" $SUDO cat "$CONFIG_FILE" > "$tmp_conf" - + escaped_domain="$(printf '%s\n' "$TLS_DOMAIN" | tr -d '[:cntrl:]' | sed 's/\\/\\\\/g; s/"/\\"/g')" awk -v port="$SERVER_PORT" -v secret="$USER_SECRET" -v domain="$escaped_domain" -v ad_tag="$AD_TAG" \ -v flag_p="$PORT_PROVIDED" -v flag_s="$SECRET_PROVIDED" -v flag_d="$DOMAIN_PROVIDED" -v flag_a="$AD_TAG_PROVIDED" ' BEGIN { ad_tag_handled = 0 } - + flag_p == "1" && /^[ \t]*port[ \t]*=/ { print "port = " port; next } flag_s == "1" && /^[ \t]*hello[ \t]*=/ { print "hello = \"" secret "\""; next } flag_d == "1" && /^[ \t]*tls_domain[ \t]*=/ { print "tls_domain = \"" domain "\""; next } - - flag_a == "1" && /^[ \t]*ad_tag[ \t]*=/ { - if (!ad_tag_handled) { - print "ad_tag = \"" ad_tag "\""; - ad_tag_handled = 1; - } - next + + flag_a == "1" && /^[ \t]*ad_tag[ \t]*=/ { + if (!ad_tag_handled) { + print "ad_tag = \"" ad_tag "\""; + ad_tag_handled = 1; + } + next } - flag_a == "1" && /^\[general\]/ { - print; - if (!ad_tag_handled) { - print "ad_tag = \"" ad_tag "\""; - ad_tag_handled = 1; - } - next + flag_a == "1" && /^\[general\]/ { + print; + if (!ad_tag_handled) { + print "ad_tag = \"" ad_tag "\""; + ad_tag_handled = 1; + } + next } - + { print } ' "$tmp_conf" > "${tmp_conf}.new" && mv "${tmp_conf}.new" "$tmp_conf" @@ -785,11 +787,11 @@ uninstall() { say "$L_U_STAGE_5" $SUDO rm -rf "$CONFIG_DIR" "$WORK_DIR" $SUDO rm -f "$CONFIG_FILE" - + if check_os_entity passwd telemt; then $SUDO userdel telemt 2>/dev/null || $SUDO deluser telemt 2>/dev/null || true fi - + if check_os_entity group telemt; then $SUDO groupdel telemt 2>/dev/null || $SUDO delgroup telemt 2>/dev/null || true fi @@ -916,7 +918,7 @@ case "$ACTION" in if command -v curl >/dev/null 2>&1; then SERVER_IP="$(curl -s4 -m 3 ifconfig.me 2>/dev/null || curl -s4 -m 3 api.ipify.org 2>/dev/null || true)" elif command -v wget >/dev/null 2>&1; then SERVER_IP="$(wget -qO- -T 3 ifconfig.me 2>/dev/null || wget -qO- -T 3 api.ipify.org 2>/dev/null || true)"; fi [ -z "$SERVER_IP" ] && SERVER_IP="" - + if command -v xxd >/dev/null 2>&1; then HEX_DOMAIN="$(printf '%s' "$TLS_DOMAIN" | xxd -p | tr -d '\n')" elif command -v hexdump >/dev/null 2>&1; then HEX_DOMAIN="$(printf '%s' "$TLS_DOMAIN" | hexdump -v -e '/1 "%02x"')" elif command -v od >/dev/null 2>&1; then HEX_DOMAIN="$(printf '%s' "$TLS_DOMAIN" | od -A n -t x1 | tr -d ' \n')" @@ -927,6 +929,15 @@ case "$ACTION" in printf '%b\n' "$L_OUT_LINK" printf ' tg://proxy?server=%s&port=%s&secret=%s\n\n' "$SERVER_IP" "$SERVER_PORT" "$CLIENT_SECRET" + svc="$(get_svc_mgr)" + if [ "$svc" = "systemd" ]; then + printf '%s\n' "$L_OUT_LOGS" + printf ' sudo journalctl -u %s -f\n\n' "$SERVICE_NAME" + elif [ "$svc" = "openrc" ]; then + printf '%s\n' "$L_OUT_LOGS" + printf ' sudo tail -f /var/log/messages /var/log/syslog 2>/dev/null | grep -i %s\n\n' "$SERVICE_NAME" + fi + printf '====================================================================\n' ;; esac