diff --git a/docs/TUNING.de.md b/docs/TUNING.de.md new file mode 100644 index 0000000..8c3c950 --- /dev/null +++ b/docs/TUNING.de.md @@ -0,0 +1,219 @@ +# Telemt Tuning-Leitfaden: Middle-End und Upstreams + +Dieses Dokument beschreibt das aktuelle Laufzeitverhalten für Middle-End (ME) und Upstream-Routing basierend auf: +- `src/config/types.rs` +- `src/config/defaults.rs` +- `src/config/load.rs` +- `src/transport/upstream.rs` + +Die unten angegebenen `Default`-Werte sind Code-Defaults (bei fehlendem Schlüssel), nicht zwingend die Werte aus `config.full.toml`. + +## Middle-End-Parameter + +### 1) ME-Grundmodus, NAT und STUN + +| Parameter | Typ | Default | Einschränkungen / Validierung | Laufzeiteffekt | Beispiel | +|---|---|---:|---|---|---| +| `general.use_middle_proxy` | `bool` | `true` | keine | Aktiviert den ME-Transportmodus. Bei `false` wird Direct-Modus verwendet. | `use_middle_proxy = true` | +| `general.proxy_secret_path` | `Option` | `"proxy-secret"` | Pfad kann `null` sein | Pfad zur Telegram-Infrastrukturdatei `proxy-secret`. | `proxy_secret_path = "proxy-secret"` | +| `general.middle_proxy_nat_ip` | `Option` | `null` | gültige IP bei gesetztem Wert | Manueller Override der öffentlichen NAT-IP für ME-Adressmaterial. | `middle_proxy_nat_ip = "203.0.113.10"` | +| `general.middle_proxy_nat_probe` | `bool` | `true` | wird auf `true` erzwungen, wenn `use_middle_proxy=true` | Aktiviert NAT-Probing für ME. | `middle_proxy_nat_probe = true` | +| `general.stun_nat_probe_concurrency` | `usize` | `8` | muss `> 0` sein | Maximale parallele STUN-Probes während NAT-Erkennung. | `stun_nat_probe_concurrency = 16` | +| `network.stun_use` | `bool` | `true` | keine | Globaler STUN-Schalter. Bei `false` wird STUN deaktiviert. | `stun_use = true` | +| `network.stun_servers` | `Vec` | integrierter öffentlicher Pool | Duplikate/leer werden entfernt | Primäre STUN-Serverliste für NAT/Public-Endpoint-Erkennung. | `stun_servers = ["stun1.l.google.com:19302"]` | +| `network.stun_tcp_fallback` | `bool` | `true` | keine | Aktiviert TCP-Fallback, wenn UDP-STUN blockiert ist. | `stun_tcp_fallback = true` | +| `network.http_ip_detect_urls` | `Vec` | `ifconfig.me` + `api.ipify.org` | keine | HTTP-Fallback zur öffentlichen IPv4-Erkennung, falls STUN ausfällt. | `http_ip_detect_urls = ["https://api.ipify.org"]` | +| `general.stun_iface_mismatch_ignore` | `bool` | `false` | keine | Reserviertes Feld in der aktuellen Revision (derzeit kein aktiver Runtime-Verbrauch). | `stun_iface_mismatch_ignore = false` | +| `timeouts.me_one_retry` | `u8` | `12` | keine | Anzahl schneller Reconnect-Versuche bei Single-Endpoint-DC-Fällen. | `me_one_retry = 6` | +| `timeouts.me_one_timeout_ms` | `u64` | `1200` | keine | Timeout pro schnellem Einzelversuch (ms). | `me_one_timeout_ms = 1500` | + +### 2) Poolgröße, Keepalive und Reconnect-Policy + +| Parameter | Typ | Default | Einschränkungen / Validierung | Laufzeiteffekt | Beispiel | +|---|---|---:|---|---|---| +| `general.middle_proxy_pool_size` | `usize` | `8` | keine | Zielgröße des aktiven ME-Writer-Pools. | `middle_proxy_pool_size = 12` | +| `general.middle_proxy_warm_standby` | `usize` | `16` | keine | Reserviertes Kompatibilitätsfeld in der aktuellen Revision (kein aktiver Runtime-Consumer). | `middle_proxy_warm_standby = 16` | +| `general.me_keepalive_enabled` | `bool` | `true` | keine | Aktiviert periodischen ME-Keepalive/Ping-Traffic. | `me_keepalive_enabled = true` | +| `general.me_keepalive_interval_secs` | `u64` | `25` | keine | Basisintervall für Keepalive (Sekunden). | `me_keepalive_interval_secs = 20` | +| `general.me_keepalive_jitter_secs` | `u64` | `5` | keine | Keepalive-Jitter zur Vermeidung synchroner Peaks. | `me_keepalive_jitter_secs = 3` | +| `general.me_keepalive_payload_random` | `bool` | `true` | keine | Randomisiert Keepalive-Payload-Bytes. | `me_keepalive_payload_random = true` | +| `general.me_warmup_stagger_enabled` | `bool` | `true` | keine | Aktiviert gestaffeltes Warmup zusätzlicher ME-Verbindungen. | `me_warmup_stagger_enabled = true` | +| `general.me_warmup_step_delay_ms` | `u64` | `500` | keine | Basisverzögerung zwischen Warmup-Schritten (ms). | `me_warmup_step_delay_ms = 300` | +| `general.me_warmup_step_jitter_ms` | `u64` | `300` | keine | Zusätzlicher zufälliger Warmup-Jitter (ms). | `me_warmup_step_jitter_ms = 200` | +| `general.me_reconnect_max_concurrent_per_dc` | `u32` | `8` | keine | Begrenzung paralleler Reconnect-Worker pro DC. | `me_reconnect_max_concurrent_per_dc = 12` | +| `general.me_reconnect_backoff_base_ms` | `u64` | `500` | keine | Initiales Reconnect-Backoff (ms). | `me_reconnect_backoff_base_ms = 250` | +| `general.me_reconnect_backoff_cap_ms` | `u64` | `30000` | keine | Maximales Reconnect-Backoff (ms). | `me_reconnect_backoff_cap_ms = 10000` | +| `general.me_reconnect_fast_retry_count` | `u32` | `16` | keine | Budget für Sofort-Retries vor längerem Backoff. | `me_reconnect_fast_retry_count = 8` | + +### 3) Reinit/Hardswap, Secret-Rotation und Degradation + +| Parameter | Typ | Default | Einschränkungen / Validierung | Laufzeiteffekt | Beispiel | +|---|---|---:|---|---|---| +| `general.hardswap` | `bool` | `true` | keine | Aktiviert generation-basierte Hardswap-Strategie für den ME-Pool. | `hardswap = true` | +| `general.me_reinit_every_secs` | `u64` | `900` | muss `> 0` sein | Intervall für periodische ME-Reinitialisierung. | `me_reinit_every_secs = 600` | +| `general.me_hardswap_warmup_delay_min_ms` | `u64` | `1000` | muss `<= me_hardswap_warmup_delay_max_ms` sein | Untere Grenze für Warmup-Dial-Abstände. | `me_hardswap_warmup_delay_min_ms = 500` | +| `general.me_hardswap_warmup_delay_max_ms` | `u64` | `2000` | muss `> 0` sein | Obere Grenze für Warmup-Dial-Abstände. | `me_hardswap_warmup_delay_max_ms = 1200` | +| `general.me_hardswap_warmup_extra_passes` | `u8` | `3` | Bereich `[0,10]` | Zusätzliche Warmup-Pässe nach dem Basispass. | `me_hardswap_warmup_extra_passes = 2` | +| `general.me_hardswap_warmup_pass_backoff_base_ms` | `u64` | `500` | muss `> 0` sein | Basis-Backoff zwischen zusätzlichen Warmup-Pässen. | `me_hardswap_warmup_pass_backoff_base_ms = 400` | +| `general.me_config_stable_snapshots` | `u8` | `2` | muss `> 0` sein | Anzahl identischer ME-Config-Snapshots vor Apply. | `me_config_stable_snapshots = 3` | +| `general.me_config_apply_cooldown_secs` | `u64` | `300` | keine | Cooldown zwischen angewendeten ME-Map-Updates. | `me_config_apply_cooldown_secs = 120` | +| `general.proxy_secret_stable_snapshots` | `u8` | `2` | muss `> 0` sein | Anzahl identischer Secret-Snapshots vor Rotation. | `proxy_secret_stable_snapshots = 3` | +| `general.proxy_secret_rotate_runtime` | `bool` | `true` | keine | Aktiviert Runtime-Rotation des Proxy-Secrets. | `proxy_secret_rotate_runtime = true` | +| `general.proxy_secret_len_max` | `usize` | `256` | Bereich `[32,4096]` | Obergrenze für akzeptierte Secret-Länge. | `proxy_secret_len_max = 512` | +| `general.update_every` | `Option` | `300` | wenn gesetzt: `> 0`; bei `null`: Legacy-Min-Fallback | Einheitliches Refresh-Intervall für ME-Config + Secret-Updater. | `update_every = 300` | +| `general.me_pool_drain_ttl_secs` | `u64` | `90` | keine | Zeitraum, in dem stale Writer noch als Fallback zulässig sind. | `me_pool_drain_ttl_secs = 120` | +| `general.me_pool_min_fresh_ratio` | `f32` | `0.8` | Bereich `[0.0,1.0]` | Coverage-Schwelle vor Drain der alten Generation. | `me_pool_min_fresh_ratio = 0.9` | +| `general.me_reinit_drain_timeout_secs` | `u64` | `120` | `0` = kein Force-Close; wenn `>0 && < TTL`, dann auf TTL angehoben | Force-Close-Timeout für draining stale Writer. | `me_reinit_drain_timeout_secs = 0` | +| `general.auto_degradation_enabled` | `bool` | `true` | keine | Reserviertes Kompatibilitätsfeld in aktueller Revision (kein aktiver Runtime-Consumer). | `auto_degradation_enabled = true` | +| `general.degradation_min_unavailable_dc_groups` | `u8` | `2` | keine | Reservierter Kompatibilitäts-Schwellenwert in aktueller Revision (kein aktiver Runtime-Consumer). | `degradation_min_unavailable_dc_groups = 2` | + +## Deprecated / Legacy Parameter + +| Parameter | Status | Ersatz | Aktuelles Verhalten | Migrationshinweis | +|---|---|---|---|---| +| `general.middle_proxy_nat_stun` | Deprecated | `network.stun_servers` | Wird nur dann in `network.stun_servers` gemerged, wenn `network.stun_servers` nicht explizit gesetzt ist. | Wert nach `network.stun_servers` verschieben, Legacy-Key entfernen. | +| `general.middle_proxy_nat_stun_servers` | Deprecated | `network.stun_servers` | Wird nur dann in `network.stun_servers` gemerged, wenn `network.stun_servers` nicht explizit gesetzt ist. | Werte nach `network.stun_servers` verschieben, Legacy-Key entfernen. | +| `general.proxy_secret_auto_reload_secs` | Deprecated | `general.update_every` | Nur aktiv, wenn `update_every = null` (Legacy-Fallback). | `general.update_every` explizit setzen, Legacy-Key entfernen. | +| `general.proxy_config_auto_reload_secs` | Deprecated | `general.update_every` | Nur aktiv, wenn `update_every = null` (Legacy-Fallback). | `general.update_every` explizit setzen, Legacy-Key entfernen. | + +## Wie Upstreams konfiguriert werden + +### Upstream-Schema + +| Feld | Gilt für | Typ | Pflicht | Default | Bedeutung | +|---|---|---|---|---|---| +| `[[upstreams]].type` | alle Upstreams | `"direct" \| "socks4" \| "socks5"` | ja | n/a | Upstream-Transporttyp. | +| `[[upstreams]].weight` | alle Upstreams | `u16` | nein | `1` | Basisgewicht für weighted-random Auswahl. | +| `[[upstreams]].enabled` | alle Upstreams | `bool` | nein | `true` | Deaktivierte Einträge werden beim Start ignoriert. | +| `[[upstreams]].scopes` | alle Upstreams | `String` | nein | `""` | Komma-separierte Scope-Tags für Request-Routing. | +| `interface` | `direct` | `Option` | nein | `null` | Interface-Name (z. B. `eth0`) oder lokale Literal-IP. | +| `bind_addresses` | `direct` | `Option>` | nein | `null` | Explizite Source-IP-Kandidaten (strikter Vorrang vor `interface`). | +| `address` | `socks4` | `String` | ja | n/a | SOCKS4-Server (`ip:port` oder `host:port`). | +| `interface` | `socks4` | `Option` | nein | `null` | Wird nur genutzt, wenn `address` als `ip:port` angegeben ist. | +| `user_id` | `socks4` | `Option` | nein | `null` | SOCKS4 User-ID für CONNECT. | +| `address` | `socks5` | `String` | ja | n/a | SOCKS5-Server (`ip:port` oder `host:port`). | +| `interface` | `socks5` | `Option` | nein | `null` | Wird nur genutzt, wenn `address` als `ip:port` angegeben ist. | +| `username` | `socks5` | `Option` | nein | `null` | SOCKS5 Benutzername. | +| `password` | `socks5` | `Option` | nein | `null` | SOCKS5 Passwort. | + +### Runtime-Regeln (wichtig) + +1. Wenn `[[upstreams]]` fehlt, injiziert der Loader einen Default-`direct`-Upstream. +2. Scope-Filterung basiert auf exaktem Token-Match: +- mit Request-Scope -> nur Einträge, deren `scopes` genau dieses Token enthält; +- ohne Request-Scope -> nur Einträge mit leerem `scopes`. +3. Unter healthy Upstreams erfolgt die Auswahl per weighted random: `weight * latency_factor`. +4. Gibt es im gefilterten Set keinen healthy Upstream, wird zufällig aus dem gefilterten Set gewählt. +5. `direct`-Bind-Auflösung: +- zuerst `bind_addresses` (nur gleiche IP-Familie wie Target); +- bei `interface` (Name) + `bind_addresses` wird jede Candidate-IP gegen Interface-Adressen validiert; +- ungültige Kandidaten werden mit `WARN` verworfen; +- bleiben keine gültigen Kandidaten übrig, erfolgt unbound direct connect (`bind_ip=None`); +- wenn `bind_addresses` nicht passt, wird `interface` verwendet (Literal-IP oder Interface-Primäradresse). +6. Für `socks4/socks5` mit Hostname-`address` ist Interface-Binding nicht unterstützt und wird mit Warnung ignoriert. +7. Runtime DNS Overrides werden für Hostname-Auflösung bei Upstream-Verbindungen genutzt. +8. Im ME-Modus wird der gewählte Upstream auch für den ME-TCP-Dial-Pfad verwendet. +9. Im ME-Modus ist bei `direct` mit bind/interface die STUN-Reflection bind-aware für KDF-Adressmaterial. +10. Im ME-Modus werden bei SOCKS-Upstream `BND.ADDR/BND.PORT` für KDF verwendet, wenn gültig/öffentlich und gleiche IP-Familie. + +## Upstream-Konfigurationsbeispiele + +### Beispiel 1: Minimaler direct Upstream + +```toml +[[upstreams]] +type = "direct" +weight = 1 +enabled = true +``` + +### Beispiel 2: direct mit Interface + expliziten bind IPs + +```toml +[[upstreams]] +type = "direct" +interface = "eth0" +bind_addresses = ["192.168.1.100", "192.168.1.101"] +weight = 3 +enabled = true +``` + +### Beispiel 3: SOCKS5 Upstream mit Authentifizierung + +```toml +[[upstreams]] +type = "socks5" +address = "198.51.100.30:1080" +username = "proxy-user" +password = "proxy-pass" +weight = 2 +enabled = true +``` + +### Beispiel 4: Gemischte Upstreams mit Scopes + +```toml +[[upstreams]] +type = "direct" +weight = 5 +enabled = true +scopes = "" + +[[upstreams]] +type = "socks5" +address = "203.0.113.40:1080" +username = "edge" +password = "edgepass" +weight = 3 +enabled = true +scopes = "premium,me" +``` + +### Beispiel 5: ME-orientiertes Tuning-Profil + +```toml +[general] +use_middle_proxy = true +proxy_secret_path = "proxy-secret" +middle_proxy_nat_probe = true +stun_nat_probe_concurrency = 16 +middle_proxy_pool_size = 12 +me_keepalive_enabled = true +me_keepalive_interval_secs = 20 +me_keepalive_jitter_secs = 4 +me_reconnect_max_concurrent_per_dc = 12 +me_reconnect_backoff_base_ms = 300 +me_reconnect_backoff_cap_ms = 10000 +me_reconnect_fast_retry_count = 10 +hardswap = true +me_reinit_every_secs = 600 +me_hardswap_warmup_delay_min_ms = 500 +me_hardswap_warmup_delay_max_ms = 1200 +me_hardswap_warmup_extra_passes = 2 +me_hardswap_warmup_pass_backoff_base_ms = 400 +me_config_stable_snapshots = 3 +me_config_apply_cooldown_secs = 120 +proxy_secret_stable_snapshots = 3 +proxy_secret_rotate_runtime = true +proxy_secret_len_max = 512 +update_every = 300 +me_pool_drain_ttl_secs = 120 +me_pool_min_fresh_ratio = 0.9 +me_reinit_drain_timeout_secs = 180 + +[timeouts] +me_one_retry = 8 +me_one_timeout_ms = 1200 + +[network] +stun_use = true +stun_tcp_fallback = true +stun_servers = [ + "stun1.l.google.com:19302", + "stun2.l.google.com:19302" +] +http_ip_detect_urls = [ + "https://api.ipify.org", + "https://ifconfig.me/ip" +] +``` diff --git a/docs/TUNING.en.md b/docs/TUNING.en.md new file mode 100644 index 0000000..1bbc439 --- /dev/null +++ b/docs/TUNING.en.md @@ -0,0 +1,219 @@ +# Telemt Tuning Guide: Middle-End and Upstreams + +This document describes the current runtime behavior for Middle-End (ME) and upstream routing based on: +- `src/config/types.rs` +- `src/config/defaults.rs` +- `src/config/load.rs` +- `src/transport/upstream.rs` + +Defaults below are code defaults (used when a key is omitted), not necessarily values from `config.full.toml` examples. + +## Middle-End Parameters + +### 1) Core ME mode, NAT, and STUN + +| Parameter | Type | Default | Constraints / validation | Runtime effect | Example | +|---|---|---:|---|---|---| +| `general.use_middle_proxy` | `bool` | `true` | none | Enables ME transport mode. If `false`, Direct mode is used. | `use_middle_proxy = true` | +| `general.proxy_secret_path` | `Option` | `"proxy-secret"` | path may be `null` | Path to Telegram infrastructure proxy-secret file. | `proxy_secret_path = "proxy-secret"` | +| `general.middle_proxy_nat_ip` | `Option` | `null` | valid IP when set | Manual public NAT IP override for ME address material. | `middle_proxy_nat_ip = "203.0.113.10"` | +| `general.middle_proxy_nat_probe` | `bool` | `true` | auto-forced to `true` when `use_middle_proxy=true` | Enables ME NAT probing. | `middle_proxy_nat_probe = true` | +| `general.stun_nat_probe_concurrency` | `usize` | `8` | must be `> 0` | Max parallel STUN probes during NAT discovery. | `stun_nat_probe_concurrency = 16` | +| `network.stun_use` | `bool` | `true` | none | Global STUN switch. If `false`, STUN probing is disabled. | `stun_use = true` | +| `network.stun_servers` | `Vec` | built-in public pool | deduplicated + empty values removed | Primary STUN server list for NAT/public endpoint discovery. | `stun_servers = ["stun1.l.google.com:19302"]` | +| `network.stun_tcp_fallback` | `bool` | `true` | none | Enables TCP fallback path when UDP STUN is blocked. | `stun_tcp_fallback = true` | +| `network.http_ip_detect_urls` | `Vec` | `ifconfig.me` + `api.ipify.org` | none | HTTP fallback for public IPv4 detection if STUN is unavailable. | `http_ip_detect_urls = ["https://api.ipify.org"]` | +| `general.stun_iface_mismatch_ignore` | `bool` | `false` | none | Reserved flag in current revision (not consumed by runtime path). | `stun_iface_mismatch_ignore = false` | +| `timeouts.me_one_retry` | `u8` | `12` | none | Fast reconnect attempts for single-endpoint DC cases. | `me_one_retry = 6` | +| `timeouts.me_one_timeout_ms` | `u64` | `1200` | none | Timeout per quick single-endpoint attempt (ms). | `me_one_timeout_ms = 1500` | + +### 2) Pool size, keepalive, and reconnect policy + +| Parameter | Type | Default | Constraints / validation | Runtime effect | Example | +|---|---|---:|---|---|---| +| `general.middle_proxy_pool_size` | `usize` | `8` | none | Target active ME writer pool size. | `middle_proxy_pool_size = 12` | +| `general.middle_proxy_warm_standby` | `usize` | `16` | none | Reserved compatibility field in current revision (no active runtime consumer). | `middle_proxy_warm_standby = 16` | +| `general.me_keepalive_enabled` | `bool` | `true` | none | Enables periodic ME keepalive/ping traffic. | `me_keepalive_enabled = true` | +| `general.me_keepalive_interval_secs` | `u64` | `25` | none | Base keepalive interval (seconds). | `me_keepalive_interval_secs = 20` | +| `general.me_keepalive_jitter_secs` | `u64` | `5` | none | Keepalive jitter to avoid synchronization bursts. | `me_keepalive_jitter_secs = 3` | +| `general.me_keepalive_payload_random` | `bool` | `true` | none | Randomizes keepalive payload bytes. | `me_keepalive_payload_random = true` | +| `general.me_warmup_stagger_enabled` | `bool` | `true` | none | Staggers extra ME warmup dials to avoid spikes. | `me_warmup_stagger_enabled = true` | +| `general.me_warmup_step_delay_ms` | `u64` | `500` | none | Base delay between warmup dial steps (ms). | `me_warmup_step_delay_ms = 300` | +| `general.me_warmup_step_jitter_ms` | `u64` | `300` | none | Additional random delay for warmup steps (ms). | `me_warmup_step_jitter_ms = 200` | +| `general.me_reconnect_max_concurrent_per_dc` | `u32` | `8` | none | Limits concurrent reconnect workers per DC in health recovery. | `me_reconnect_max_concurrent_per_dc = 12` | +| `general.me_reconnect_backoff_base_ms` | `u64` | `500` | none | Initial reconnect backoff (ms). | `me_reconnect_backoff_base_ms = 250` | +| `general.me_reconnect_backoff_cap_ms` | `u64` | `30000` | none | Maximum reconnect backoff (ms). | `me_reconnect_backoff_cap_ms = 10000` | +| `general.me_reconnect_fast_retry_count` | `u32` | `16` | none | Immediate retry budget before long backoff behavior. | `me_reconnect_fast_retry_count = 8` | + +### 3) Reinit/hardswap, secret rotation, and degradation + +| Parameter | Type | Default | Constraints / validation | Runtime effect | Example | +|---|---|---:|---|---|---| +| `general.hardswap` | `bool` | `true` | none | Enables generation-based ME hardswap strategy. | `hardswap = true` | +| `general.me_reinit_every_secs` | `u64` | `900` | must be `> 0` | Periodic ME reinit interval. | `me_reinit_every_secs = 600` | +| `general.me_hardswap_warmup_delay_min_ms` | `u64` | `1000` | must be `<= me_hardswap_warmup_delay_max_ms` | Lower bound for hardswap warmup dial spacing. | `me_hardswap_warmup_delay_min_ms = 500` | +| `general.me_hardswap_warmup_delay_max_ms` | `u64` | `2000` | must be `> 0` | Upper bound for hardswap warmup dial spacing. | `me_hardswap_warmup_delay_max_ms = 1200` | +| `general.me_hardswap_warmup_extra_passes` | `u8` | `3` | must be within `[0,10]` | Additional warmup passes after base pass. | `me_hardswap_warmup_extra_passes = 2` | +| `general.me_hardswap_warmup_pass_backoff_base_ms` | `u64` | `500` | must be `> 0` | Base backoff between extra warmup passes. | `me_hardswap_warmup_pass_backoff_base_ms = 400` | +| `general.me_config_stable_snapshots` | `u8` | `2` | must be `> 0` | Number of identical ME config snapshots required before apply. | `me_config_stable_snapshots = 3` | +| `general.me_config_apply_cooldown_secs` | `u64` | `300` | none | Cooldown between applied ME map updates. | `me_config_apply_cooldown_secs = 120` | +| `general.proxy_secret_stable_snapshots` | `u8` | `2` | must be `> 0` | Number of identical proxy-secret snapshots required before rotation. | `proxy_secret_stable_snapshots = 3` | +| `general.proxy_secret_rotate_runtime` | `bool` | `true` | none | Enables runtime proxy-secret rotation. | `proxy_secret_rotate_runtime = true` | +| `general.proxy_secret_len_max` | `usize` | `256` | must be within `[32,4096]` | Upper limit for accepted proxy-secret length. | `proxy_secret_len_max = 512` | +| `general.update_every` | `Option` | `300` | if set: must be `> 0`; if `null`: legacy min fallback | Unified refresh interval for ME config + secret updater. | `update_every = 300` | +| `general.me_pool_drain_ttl_secs` | `u64` | `90` | none | Time window where stale writers remain fallback-eligible. | `me_pool_drain_ttl_secs = 120` | +| `general.me_pool_min_fresh_ratio` | `f32` | `0.8` | must be within `[0.0,1.0]` | Coverage threshold before stale generation can be drained. | `me_pool_min_fresh_ratio = 0.9` | +| `general.me_reinit_drain_timeout_secs` | `u64` | `120` | `0` means no force-close; if `>0 && < TTL` it is bumped to TTL | Force-close timeout for draining stale writers. | `me_reinit_drain_timeout_secs = 0` | +| `general.auto_degradation_enabled` | `bool` | `true` | none | Reserved compatibility flag in current revision (no active runtime consumer). | `auto_degradation_enabled = true` | +| `general.degradation_min_unavailable_dc_groups` | `u8` | `2` | none | Reserved compatibility threshold in current revision (no active runtime consumer). | `degradation_min_unavailable_dc_groups = 2` | + +## Deprecated / Legacy Parameters + +| Parameter | Status | Replacement | Current behavior | Migration recommendation | +|---|---|---|---|---| +| `general.middle_proxy_nat_stun` | Deprecated | `network.stun_servers` | Merged into `network.stun_servers` only when `network.stun_servers` is not explicitly set. | Move value into `network.stun_servers` and remove legacy key. | +| `general.middle_proxy_nat_stun_servers` | Deprecated | `network.stun_servers` | Merged into `network.stun_servers` only when `network.stun_servers` is not explicitly set. | Move values into `network.stun_servers` and remove legacy key. | +| `general.proxy_secret_auto_reload_secs` | Deprecated | `general.update_every` | Used only when `update_every = null` (legacy fallback path). | Set `general.update_every` explicitly and remove legacy key. | +| `general.proxy_config_auto_reload_secs` | Deprecated | `general.update_every` | Used only when `update_every = null` (legacy fallback path). | Set `general.update_every` explicitly and remove legacy key. | + +## How Upstreams Are Configured + +### Upstream schema + +| Field | Applies to | Type | Required | Default | Meaning | +|---|---|---|---|---|---| +| `[[upstreams]].type` | all upstreams | `"direct" \| "socks4" \| "socks5"` | yes | n/a | Upstream transport type. | +| `[[upstreams]].weight` | all upstreams | `u16` | no | `1` | Base weight for weighted-random selection. | +| `[[upstreams]].enabled` | all upstreams | `bool` | no | `true` | Disabled entries are ignored at startup. | +| `[[upstreams]].scopes` | all upstreams | `String` | no | `""` | Comma-separated scope tags for request-level routing. | +| `interface` | `direct` | `Option` | no | `null` | Interface name (e.g. `eth0`) or literal local IP for bind selection. | +| `bind_addresses` | `direct` | `Option>` | no | `null` | Explicit local source IP candidates (strict priority over `interface`). | +| `address` | `socks4` | `String` | yes | n/a | SOCKS4 server endpoint (`ip:port` or `host:port`). | +| `interface` | `socks4` | `Option` | no | `null` | Used only for SOCKS server `ip:port` dial path. | +| `user_id` | `socks4` | `Option` | no | `null` | SOCKS4 user ID for CONNECT request. | +| `address` | `socks5` | `String` | yes | n/a | SOCKS5 server endpoint (`ip:port` or `host:port`). | +| `interface` | `socks5` | `Option` | no | `null` | Used only for SOCKS server `ip:port` dial path. | +| `username` | `socks5` | `Option` | no | `null` | SOCKS5 username auth. | +| `password` | `socks5` | `Option` | no | `null` | SOCKS5 password auth. | + +### Runtime rules (important) + +1. If `[[upstreams]]` is omitted, loader injects one default `direct` upstream. +2. Scope filtering is exact-token based: +- when request scope is set -> only entries whose `scopes` contains that exact token; +- when request scope is not set -> only entries with empty `scopes`. +3. Healthy upstreams are selected by weighted random using: `weight * latency_factor`. +4. If no healthy upstream exists in filtered set, random selection is used among filtered entries. +5. `direct` bind resolution order: +- `bind_addresses` candidates (same IP family as target) first; +- if `interface` is an interface name and `bind_addresses` is set, each candidate IP is validated against addresses currently assigned to that interface; +- invalid candidates are dropped with `WARN`; +- if no valid candidate remains, connection falls back to unbound direct connect (`bind_ip=None`); +- if no `bind_addresses` candidate, `interface` is used (literal IP or resolved interface primary IP). +6. For `socks4/socks5` with `address` as hostname, interface binding is not supported and is ignored with warning. +7. Runtime DNS overrides are used for upstream hostname resolution. +8. In ME mode, the selected upstream is also used for ME TCP dial path. +9. In ME mode for `direct` upstream with bind/interface, STUN reflection logic is bind-aware for KDF source material. +10. In ME mode for SOCKS upstream, SOCKS `BND.ADDR/BND.PORT` is used for KDF when it is valid/public for the same family. + +## Upstream Configuration Examples + +### Example 1: Minimal direct upstream + +```toml +[[upstreams]] +type = "direct" +weight = 1 +enabled = true +``` + +### Example 2: Direct with interface + explicit bind addresses + +```toml +[[upstreams]] +type = "direct" +interface = "eth0" +bind_addresses = ["192.168.1.100", "192.168.1.101"] +weight = 3 +enabled = true +``` + +### Example 3: SOCKS5 upstream with authentication + +```toml +[[upstreams]] +type = "socks5" +address = "198.51.100.30:1080" +username = "proxy-user" +password = "proxy-pass" +weight = 2 +enabled = true +``` + +### Example 4: Mixed upstreams with scopes + +```toml +[[upstreams]] +type = "direct" +weight = 5 +enabled = true +scopes = "" + +[[upstreams]] +type = "socks5" +address = "203.0.113.40:1080" +username = "edge" +password = "edgepass" +weight = 3 +enabled = true +scopes = "premium,me" +``` + +### Example 5: ME-focused tuning profile + +```toml +[general] +use_middle_proxy = true +proxy_secret_path = "proxy-secret" +middle_proxy_nat_probe = true +stun_nat_probe_concurrency = 16 +middle_proxy_pool_size = 12 +me_keepalive_enabled = true +me_keepalive_interval_secs = 20 +me_keepalive_jitter_secs = 4 +me_reconnect_max_concurrent_per_dc = 12 +me_reconnect_backoff_base_ms = 300 +me_reconnect_backoff_cap_ms = 10000 +me_reconnect_fast_retry_count = 10 +hardswap = true +me_reinit_every_secs = 600 +me_hardswap_warmup_delay_min_ms = 500 +me_hardswap_warmup_delay_max_ms = 1200 +me_hardswap_warmup_extra_passes = 2 +me_hardswap_warmup_pass_backoff_base_ms = 400 +me_config_stable_snapshots = 3 +me_config_apply_cooldown_secs = 120 +proxy_secret_stable_snapshots = 3 +proxy_secret_rotate_runtime = true +proxy_secret_len_max = 512 +update_every = 300 +me_pool_drain_ttl_secs = 120 +me_pool_min_fresh_ratio = 0.9 +me_reinit_drain_timeout_secs = 180 + +[timeouts] +me_one_retry = 8 +me_one_timeout_ms = 1200 + +[network] +stun_use = true +stun_tcp_fallback = true +stun_servers = [ + "stun1.l.google.com:19302", + "stun2.l.google.com:19302" +] +http_ip_detect_urls = [ + "https://api.ipify.org", + "https://ifconfig.me/ip" +] +``` diff --git a/docs/TUNING.ru.md b/docs/TUNING.ru.md new file mode 100644 index 0000000..48a2b6c --- /dev/null +++ b/docs/TUNING.ru.md @@ -0,0 +1,219 @@ +# Руководство по тюнингу Telemt: Middle-End и Upstreams + +Документ описывает актуальное поведение Middle-End (ME) и маршрутизации через upstream на основе: +- `src/config/types.rs` +- `src/config/defaults.rs` +- `src/config/load.rs` +- `src/transport/upstream.rs` + +Значения `Default` ниже — это значения из кода при отсутствии ключа в конфиге, а не обязательно значения из примеров `config.full.toml`. + +## Параметры Middle-End + +### 1) Базовый режим ME, NAT и STUN + +| Параметр | Тип | Default | Ограничения / валидация | Влияние на runtime | Пример | +|---|---|---:|---|---|---| +| `general.use_middle_proxy` | `bool` | `true` | нет | Включает транспорт ME. При `false` используется Direct-режим. | `use_middle_proxy = true` | +| `general.proxy_secret_path` | `Option` | `"proxy-secret"` | путь может быть `null` | Путь к инфраструктурному proxy-secret Telegram. | `proxy_secret_path = "proxy-secret"` | +| `general.middle_proxy_nat_ip` | `Option` | `null` | валидный IP при задании | Ручной override публичного NAT IP для адресного материала ME. | `middle_proxy_nat_ip = "203.0.113.10"` | +| `general.middle_proxy_nat_probe` | `bool` | `true` | авто-принудительно `true`, если `use_middle_proxy=true` | Включает NAT probing для ME. | `middle_proxy_nat_probe = true` | +| `general.stun_nat_probe_concurrency` | `usize` | `8` | должно быть `> 0` | Максимум параллельных STUN-проб при NAT-детекте. | `stun_nat_probe_concurrency = 16` | +| `network.stun_use` | `bool` | `true` | нет | Глобальный переключатель STUN. При `false` STUN отключен. | `stun_use = true` | +| `network.stun_servers` | `Vec` | встроенный публичный пул | удаляются дубликаты и пустые значения | Основной список STUN-серверов для NAT/public endpoint discovery. | `stun_servers = ["stun1.l.google.com:19302"]` | +| `network.stun_tcp_fallback` | `bool` | `true` | нет | Включает TCP fallback, если UDP STUN недоступен. | `stun_tcp_fallback = true` | +| `network.http_ip_detect_urls` | `Vec` | `ifconfig.me` + `api.ipify.org` | нет | HTTP fallback для определения публичного IPv4 при недоступности STUN. | `http_ip_detect_urls = ["https://api.ipify.org"]` | +| `general.stun_iface_mismatch_ignore` | `bool` | `false` | нет | Зарезервированный флаг в текущей ревизии (runtime его не использует). | `stun_iface_mismatch_ignore = false` | +| `timeouts.me_one_retry` | `u8` | `12` | нет | Количество быстрых reconnect-попыток для DC с одним endpoint. | `me_one_retry = 6` | +| `timeouts.me_one_timeout_ms` | `u64` | `1200` | нет | Таймаут одной быстрой попытки (мс). | `me_one_timeout_ms = 1500` | + +### 2) Размер пула, keepalive и reconnect-политика + +| Параметр | Тип | Default | Ограничения / валидация | Влияние на runtime | Пример | +|---|---|---:|---|---|---| +| `general.middle_proxy_pool_size` | `usize` | `8` | нет | Целевой размер активного пула ME-writer соединений. | `middle_proxy_pool_size = 12` | +| `general.middle_proxy_warm_standby` | `usize` | `16` | нет | Зарезервированное поле совместимости в текущей ревизии (активного runtime-consumer нет). | `middle_proxy_warm_standby = 16` | +| `general.me_keepalive_enabled` | `bool` | `true` | нет | Включает периодические keepalive/ping кадры ME. | `me_keepalive_enabled = true` | +| `general.me_keepalive_interval_secs` | `u64` | `25` | нет | Базовый интервал keepalive (сек). | `me_keepalive_interval_secs = 20` | +| `general.me_keepalive_jitter_secs` | `u64` | `5` | нет | Джиттер keepalive для предотвращения синхронных всплесков. | `me_keepalive_jitter_secs = 3` | +| `general.me_keepalive_payload_random` | `bool` | `true` | нет | Рандомизирует payload keepalive-кадров. | `me_keepalive_payload_random = true` | +| `general.me_warmup_stagger_enabled` | `bool` | `true` | нет | Включает staggered warmup дополнительных ME-коннектов. | `me_warmup_stagger_enabled = true` | +| `general.me_warmup_step_delay_ms` | `u64` | `500` | нет | Базовая задержка между шагами warmup (мс). | `me_warmup_step_delay_ms = 300` | +| `general.me_warmup_step_jitter_ms` | `u64` | `300` | нет | Дополнительный случайный warmup-джиттер (мс). | `me_warmup_step_jitter_ms = 200` | +| `general.me_reconnect_max_concurrent_per_dc` | `u32` | `8` | нет | Ограничивает параллельные reconnect worker'ы на один DC. | `me_reconnect_max_concurrent_per_dc = 12` | +| `general.me_reconnect_backoff_base_ms` | `u64` | `500` | нет | Начальный backoff reconnect (мс). | `me_reconnect_backoff_base_ms = 250` | +| `general.me_reconnect_backoff_cap_ms` | `u64` | `30000` | нет | Верхняя граница backoff reconnect (мс). | `me_reconnect_backoff_cap_ms = 10000` | +| `general.me_reconnect_fast_retry_count` | `u32` | `16` | нет | Бюджет быстрых retry до длинного backoff. | `me_reconnect_fast_retry_count = 8` | + +### 3) Reinit/hardswap, ротация секрета и деградация + +| Параметр | Тип | Default | Ограничения / валидация | Влияние на runtime | Пример | +|---|---|---:|---|---|---| +| `general.hardswap` | `bool` | `true` | нет | Включает generation-based стратегию hardswap для ME-пула. | `hardswap = true` | +| `general.me_reinit_every_secs` | `u64` | `900` | должно быть `> 0` | Интервал периодического reinit ME-пула. | `me_reinit_every_secs = 600` | +| `general.me_hardswap_warmup_delay_min_ms` | `u64` | `1000` | должно быть `<= me_hardswap_warmup_delay_max_ms` | Нижняя граница пауз между warmup dial попытками. | `me_hardswap_warmup_delay_min_ms = 500` | +| `general.me_hardswap_warmup_delay_max_ms` | `u64` | `2000` | должно быть `> 0` | Верхняя граница пауз между warmup dial попытками. | `me_hardswap_warmup_delay_max_ms = 1200` | +| `general.me_hardswap_warmup_extra_passes` | `u8` | `3` | диапазон `[0,10]` | Дополнительные warmup-проходы после базового. | `me_hardswap_warmup_extra_passes = 2` | +| `general.me_hardswap_warmup_pass_backoff_base_ms` | `u64` | `500` | должно быть `> 0` | Базовый backoff между extra-pass в warmup. | `me_hardswap_warmup_pass_backoff_base_ms = 400` | +| `general.me_config_stable_snapshots` | `u8` | `2` | должно быть `> 0` | Количество одинаковых snapshot перед применением ME map update. | `me_config_stable_snapshots = 3` | +| `general.me_config_apply_cooldown_secs` | `u64` | `300` | нет | Cooldown между применёнными обновлениями ME map. | `me_config_apply_cooldown_secs = 120` | +| `general.proxy_secret_stable_snapshots` | `u8` | `2` | должно быть `> 0` | Количество одинаковых snapshot перед runtime-rotation proxy-secret. | `proxy_secret_stable_snapshots = 3` | +| `general.proxy_secret_rotate_runtime` | `bool` | `true` | нет | Включает runtime-ротацию proxy-secret. | `proxy_secret_rotate_runtime = true` | +| `general.proxy_secret_len_max` | `usize` | `256` | диапазон `[32,4096]` | Верхний лимит длины принимаемого proxy-secret. | `proxy_secret_len_max = 512` | +| `general.update_every` | `Option` | `300` | если задано: `> 0`; если `null`: fallback на legacy минимум | Единый интервал refresh для ME config + secret updater. | `update_every = 300` | +| `general.me_pool_drain_ttl_secs` | `u64` | `90` | нет | Время, когда stale writer ещё может использоваться как fallback. | `me_pool_drain_ttl_secs = 120` | +| `general.me_pool_min_fresh_ratio` | `f32` | `0.8` | диапазон `[0.0,1.0]` | Порог покрытия fresh-поколения перед drain старого поколения. | `me_pool_min_fresh_ratio = 0.9` | +| `general.me_reinit_drain_timeout_secs` | `u64` | `120` | `0` = без force-close; если `>0 && < TTL`, поднимается до TTL | Таймаут force-close для draining stale writer. | `me_reinit_drain_timeout_secs = 0` | +| `general.auto_degradation_enabled` | `bool` | `true` | нет | Зарезервированный флаг совместимости в текущей ревизии (активного runtime-consumer нет). | `auto_degradation_enabled = true` | +| `general.degradation_min_unavailable_dc_groups` | `u8` | `2` | нет | Зарезервированный порог совместимости в текущей ревизии (активного runtime-consumer нет). | `degradation_min_unavailable_dc_groups = 2` | + +## Устаревшие / legacy параметры + +| Параметр | Статус | Замена | Текущее поведение | Рекомендация миграции | +|---|---|---|---|---| +| `general.middle_proxy_nat_stun` | Deprecated | `network.stun_servers` | Добавляется в `network.stun_servers`, только если `network.stun_servers` не задан явно. | Перенести значение в `network.stun_servers`, legacy-ключ удалить. | +| `general.middle_proxy_nat_stun_servers` | Deprecated | `network.stun_servers` | Добавляется в `network.stun_servers`, только если `network.stun_servers` не задан явно. | Перенести значения в `network.stun_servers`, legacy-ключ удалить. | +| `general.proxy_secret_auto_reload_secs` | Deprecated | `general.update_every` | Используется только если `update_every = null` (legacy fallback). | Явно задать `general.update_every`, legacy-ключ удалить. | +| `general.proxy_config_auto_reload_secs` | Deprecated | `general.update_every` | Используется только если `update_every = null` (legacy fallback). | Явно задать `general.update_every`, legacy-ключ удалить. | + +## Как конфигурируются Upstreams + +### Схема upstream + +| Поле | Применимость | Тип | Обязательно | Default | Назначение | +|---|---|---|---|---|---| +| `[[upstreams]].type` | все upstream | `"direct" \| "socks4" \| "socks5"` | да | n/a | Тип upstream транспорта. | +| `[[upstreams]].weight` | все upstream | `u16` | нет | `1` | Базовый вес в weighted-random выборе. | +| `[[upstreams]].enabled` | все upstream | `bool` | нет | `true` | Выключенные записи игнорируются на старте. | +| `[[upstreams]].scopes` | все upstream | `String` | нет | `""` | Список scope-токенов через запятую для маршрутизации. | +| `interface` | `direct` | `Option` | нет | `null` | Имя интерфейса (например `eth0`) или literal локальный IP. | +| `bind_addresses` | `direct` | `Option>` | нет | `null` | Явные кандидаты source IP (имеют приоритет над `interface`). | +| `address` | `socks4` | `String` | да | n/a | Адрес SOCKS4 сервера (`ip:port` или `host:port`). | +| `interface` | `socks4` | `Option` | нет | `null` | Используется только если `address` задан как `ip:port`. | +| `user_id` | `socks4` | `Option` | нет | `null` | SOCKS4 user ID в CONNECT-запросе. | +| `address` | `socks5` | `String` | да | n/a | Адрес SOCKS5 сервера (`ip:port` или `host:port`). | +| `interface` | `socks5` | `Option` | нет | `null` | Используется только если `address` задан как `ip:port`. | +| `username` | `socks5` | `Option` | нет | `null` | Логин SOCKS5 auth. | +| `password` | `socks5` | `Option` | нет | `null` | Пароль SOCKS5 auth. | + +### Runtime-правила (важно) + +1. Если `[[upstreams]]` отсутствует, loader добавляет один upstream `direct` по умолчанию. +2. Scope-фильтрация — по точному совпадению токена: +- если scope запроса задан -> используются только записи, где `scopes` содержит такой же токен; +- если scope запроса не задан -> используются только записи с пустым `scopes`. +3. Среди healthy upstream используется weighted-random выбор: `weight * latency_factor`. +4. Если в отфильтрованном наборе нет healthy upstream, выбирается случайный из отфильтрованных. +5. Порядок выбора bind для `direct`: +- сначала `bind_addresses` (только IP нужного семейства); +- если одновременно заданы `interface` (имя) и `bind_addresses`, каждый IP проверяется на принадлежность интерфейсу; +- несовпадающие IP отбрасываются с `WARN`; +- если валидных IP не осталось, используется unbound direct connect (`bind_ip=None`); +- если `bind_addresses` не подходит, применяется `interface` (literal IP или адрес интерфейса). +6. Для `socks4/socks5` с `address` в виде hostname интерфейсный bind не поддерживается и игнорируется с предупреждением. +7. Runtime DNS overrides применяются к резолвингу hostname в upstream-подключениях. +8. В ME-режиме выбранный upstream также используется для ME TCP dial path. +9. В ME-режиме для `direct` upstream с bind/interface STUN-рефлексия выполняется bind-aware для KDF материала. +10. В ME-режиме для SOCKS upstream используются `BND.ADDR/BND.PORT` для KDF, если адрес валиден/публичен и соответствует IP family. + +## Примеры конфигурации Upstreams + +### Пример 1: минимальный direct upstream + +```toml +[[upstreams]] +type = "direct" +weight = 1 +enabled = true +``` + +### Пример 2: direct с interface + явными bind IP + +```toml +[[upstreams]] +type = "direct" +interface = "eth0" +bind_addresses = ["192.168.1.100", "192.168.1.101"] +weight = 3 +enabled = true +``` + +### Пример 3: SOCKS5 upstream с аутентификацией + +```toml +[[upstreams]] +type = "socks5" +address = "198.51.100.30:1080" +username = "proxy-user" +password = "proxy-pass" +weight = 2 +enabled = true +``` + +### Пример 4: смешанные upstream с scopes + +```toml +[[upstreams]] +type = "direct" +weight = 5 +enabled = true +scopes = "" + +[[upstreams]] +type = "socks5" +address = "203.0.113.40:1080" +username = "edge" +password = "edgepass" +weight = 3 +enabled = true +scopes = "premium,me" +``` + +### Пример 5: профиль тюнинга под ME + +```toml +[general] +use_middle_proxy = true +proxy_secret_path = "proxy-secret" +middle_proxy_nat_probe = true +stun_nat_probe_concurrency = 16 +middle_proxy_pool_size = 12 +me_keepalive_enabled = true +me_keepalive_interval_secs = 20 +me_keepalive_jitter_secs = 4 +me_reconnect_max_concurrent_per_dc = 12 +me_reconnect_backoff_base_ms = 300 +me_reconnect_backoff_cap_ms = 10000 +me_reconnect_fast_retry_count = 10 +hardswap = true +me_reinit_every_secs = 600 +me_hardswap_warmup_delay_min_ms = 500 +me_hardswap_warmup_delay_max_ms = 1200 +me_hardswap_warmup_extra_passes = 2 +me_hardswap_warmup_pass_backoff_base_ms = 400 +me_config_stable_snapshots = 3 +me_config_apply_cooldown_secs = 120 +proxy_secret_stable_snapshots = 3 +proxy_secret_rotate_runtime = true +proxy_secret_len_max = 512 +update_every = 300 +me_pool_drain_ttl_secs = 120 +me_pool_min_fresh_ratio = 0.9 +me_reinit_drain_timeout_secs = 180 + +[timeouts] +me_one_retry = 8 +me_one_timeout_ms = 1200 + +[network] +stun_use = true +stun_tcp_fallback = true +stun_servers = [ + "stun1.l.google.com:19302", + "stun2.l.google.com:19302" +] +http_ip_detect_urls = [ + "https://api.ipify.org", + "https://ifconfig.me/ip" +] +```