From 2a168b2600c4176d890b1e3910de61e7d21ce1ed Mon Sep 17 00:00:00 2001 From: Dmitry Zarva Date: Fri, 17 Apr 2026 12:23:17 +0000 Subject: [PATCH 1/4] feat: make URLS to obtain proxy_secret, getProxyConfig, getProxyConfigV6 files optionally configurable --- docs/Config_params/CONFIG_PARAMS.en.md | 29 +++++++++++++++++++- docs/Config_params/CONFIG_PARAMS.ru.md | 29 +++++++++++++++++++- src/config/types.rs | 15 ++++++++++ src/maestro/me_startup.rs | 13 +++++++-- src/transport/middle_proxy/config_updater.rs | 15 ++++++++-- src/transport/middle_proxy/secret.rs | 16 +++++++---- 6 files changed, 106 insertions(+), 11 deletions(-) diff --git a/docs/Config_params/CONFIG_PARAMS.en.md b/docs/Config_params/CONFIG_PARAMS.en.md index 26abe44..1ed6baf 100644 --- a/docs/Config_params/CONFIG_PARAMS.en.md +++ b/docs/Config_params/CONFIG_PARAMS.en.md @@ -255,13 +255,22 @@ This document lists all configuration keys accepted by `config.toml`. ``` ## proxy_secret_path - **Constraints / validation**: `String`. When omitted, the default path is `"proxy-secret"`. Empty values are accepted by TOML/serde but will likely fail at runtime (invalid file path). - - **Description**: Path to Telegram infrastructure `proxy-secret` cache file used by ME handshake/RPC auth. Telemt always tries a fresh download from `https://core.telegram.org/getProxySecret` first, caches it to this path on success, and falls back to reading the cached file (any age) on download failure. + - **Description**: Path to Telegram infrastructure `proxy-secret` cache file used by ME handshake/RPC auth. Telemt always tries a fresh download from `https://core.telegram.org/getProxySecret` first (unless `proxy_secret_url` is set) , caches it to this path on success, and falls back to reading the cached file (any age) on download failure. - **Example**: ```toml [general] proxy_secret_path = "proxy-secret" ``` +## proxy_secret_url + - **Constraints / validation**: `String`. When omitted, the `"https://core.telegram.org/getProxySecret"` is used. + - **Description**: Optional URL to obtain `proxy-secret` file used by ME handshake/RPC auth. Telemt always tries a fresh download from this URL first (with fallback to `https://core.telegram.org/getProxySecret` if absent). + - **Example**: + + ```toml + [general] + proxy_secret_url = "https://core.telegram.org/getProxySecret" + ``` ## proxy_config_v4_cache_path - **Constraints / validation**: `String`. When set, must not be empty/whitespace-only. - **Description**: Optional disk cache path for raw `getProxyConfig` (IPv4) snapshot. At startup Telemt tries to fetch a fresh snapshot first; on fetch failure or empty snapshot it falls back to this cache file when present and non-empty. @@ -271,6 +280,15 @@ This document lists all configuration keys accepted by `config.toml`. [general] proxy_config_v4_cache_path = "cache/proxy-config-v4.txt" ``` +## proxy_config_v4_url +- **Constraints / validation**: `String`. When omitted, the `"https://core.telegram.org/getProxyConfig"` is used. +- **Description**: Optional URL to obtain raw `getProxyConfig` (IPv4). Telemt always tries a fresh download from this URL first (with fallback to `https://core.telegram.org/getProxyConfig` if absent). +- **Example**: + + ```toml + [general] + proxy_config_v4_url = "https://core.telegram.org/getProxyConfig" + ``` ## proxy_config_v6_cache_path - **Constraints / validation**: `String`. When set, must not be empty/whitespace-only. - **Description**: Optional disk cache path for raw `getProxyConfigV6` (IPv6) snapshot. At startup Telemt tries to fetch a fresh snapshot first; on fetch failure or empty snapshot it falls back to this cache file when present and non-empty. @@ -280,6 +298,15 @@ This document lists all configuration keys accepted by `config.toml`. [general] proxy_config_v6_cache_path = "cache/proxy-config-v6.txt" ``` +## proxy_config_v6_url +- **Constraints / validation**: `String`. When omitted, the `"https://core.telegram.org/getProxyConfigV6"` is used. +- **Description**: Optional URL to obtain raw `getProxyConfigV6` (IPv6). Telemt always tries a fresh download from this URL first (with fallback to `https://core.telegram.org/getProxyConfigV6` if absent). +- **Example**: + + ```toml + [general] + proxy_config_v6_url = "https://core.telegram.org/getProxyConfigV6" + ``` ## ad_tag - **Constraints / validation**: `String` (optional). When set, must be exactly 32 hex characters; invalid values are disabled during config load. - **Description**: Global fallback sponsored-channel `ad_tag` (used when user has no override in `access.user_ad_tags`). An all-zero tag is accepted but has no effect (and is warned about) until replaced with a real tag from `@MTProxybot`. diff --git a/docs/Config_params/CONFIG_PARAMS.ru.md b/docs/Config_params/CONFIG_PARAMS.ru.md index eb00ffc..f6a67e0 100644 --- a/docs/Config_params/CONFIG_PARAMS.ru.md +++ b/docs/Config_params/CONFIG_PARAMS.ru.md @@ -255,13 +255,22 @@ ``` ## proxy_secret_path - **Ограничения / валидация**: `String`. Если этот параметр не указан, используется путь по умолчанию — «proxy-secret». Пустые значения принимаются TOML/serde, но во время выполнения произойдет ошибка (invalid file path). - - **Описание**: Путь к файлу кэша `proxy-secret` инфраструктуры Telegram, используемому ME-handshake/аутентификацией RPC. Telemt всегда сначала пытается выполнить новую загрузку с https://core.telegram.org/getProxySecret, в случае успеха кэширует ее по этому пути и возвращается к чтению кэшированного файла в случае сбоя загрузки. + - **Описание**: Путь к файлу кэша `proxy-secret` инфраструктуры Telegram, используемому ME-handshake/аутентификацией RPC. Telemt всегда сначала пытается выполнить новую загрузку с https://core.telegram.org/getProxySecret (если не установлен `proxy_secret_path`), в случае успеха кэширует ее по этому пути и возвращается к чтению кэшированного файла в случае сбоя загрузки. - **Пример**: ```toml [general] proxy_secret_path = "proxy-secret" ``` +## proxy_secret_url + - **Ограничения / валидация**: `String`. Если не указан, используется `"https://core.telegram.org/getProxySecret"`. + - **Описание**: Необязательный URL для получения файла `proxy-secret` используемого ME-handshake/аутентификацией RPC. Telemt всегда сначала пытается выполнить новую загрузку с этого URL, в случае успеха кэширует ее по этому пути и возвращается к чтению кэшированного файла в случае сбоя загрузки. + - **Пример**: + + ```toml + [general] + proxy_secret_url = "https://core.telegram.org/getProxySecret" + ``` ## proxy_config_v4_cache_path - **Ограничения / валидация**: `String`. Если используется, значение не должно быть пустым или содержать только пробелы. - **Описание**: Необязательный путь к кэшу для необработанного (raw) снимка getProxyConfig (IPv4). При запуске Telemt сначала пытается получить свежий снимок; в случае сбоя выборки или пустого снимка он возвращается к этому файлу кэша, если он присутствует и не пуст. @@ -271,6 +280,15 @@ [general] proxy_config_v4_cache_path = "cache/proxy-config-v4.txt" ``` +## proxy_config_v4_url + - **Ограничения / валидация**: `String`. Если не указан, используется `"https://core.telegram.org/getProxyConfig"`. + - **Описание**: Необязательный URL для получения `getProxyConfig` (IPv4). Telemt при всегда пытается выполнить новую загрузку с этого URL (и если не задан, использует `https://core.telegram.org/getProxyConfig`). + - **Example**: + + ```toml + [general] + proxy_config_v4_url = "https://core.telegram.org/getProxyConfig" + ``` ## proxy_config_v6_cache_path - **Ограничения / валидация**: `String`. Если используется, значение не должно быть пустым или содержать только пробелы. - **Описание**: Необязательный путь к кэшу для необработанного (raw) снимка getProxyConfigV6 (IPv6). При запуске Telemt сначала пытается получить свежий снимок; в случае сбоя выборки или пустого снимка он возвращается к этому файлу кэша, если он присутствует и не пуст. @@ -280,6 +298,15 @@ [general] proxy_config_v6_cache_path = "cache/proxy-config-v6.txt" ``` +## proxy_config_v6_url +- **Ограничения / валидация**: `String`. Если не указан, используется `"https://core.telegram.org/getProxyConfigV6"`. +- **Описание**: Необязательный URL для получения `getProxyConfigV6` (IPv6). Telemt при всегда пытается выполнить новую загрузку с этого URL (и если не задан, использует `https://core.telegram.org/getProxyConfigV6`). +- **Example**: + + ```toml + [general] + proxy_config_v6_url = "https://core.telegram.org/getProxyConfigV6" + ``` ## ad_tag - **Ограничения / валидация**: `String` (необязательный параметр). Если используется, значение должно быть ровно 32 символа в шестнадцатеричной системе; недопустимые значения отключаются во время загрузки конфигурации. - **Описание**: Глобальный резервный спонсируемый канал `ad_tag` (используется, когда у пользователя нет переопределения в `access.user_ad_tags`). Тег со всеми нулями принимается, но не имеет никакого эффекта, пока не будет заменен реальным тегом от `@MTProxybot`. diff --git a/src/config/types.rs b/src/config/types.rs index 9f7e0f4..56764a5 100644 --- a/src/config/types.rs +++ b/src/config/types.rs @@ -392,14 +392,26 @@ pub struct GeneralConfig { #[serde(default = "default_proxy_secret_path")] pub proxy_secret_path: Option, + /// Optional custom URL for infrastructure secret (https://core.telegram.org/getProxySecret if absent). + #[serde(default)] + pub proxy_secret_url: Option, + /// Optional path to cache raw getProxyConfig (IPv4) snapshot for startup fallback. #[serde(default = "default_proxy_config_v4_cache_path")] pub proxy_config_v4_cache_path: Option, + /// Optional custom URL for getProxyConfig (https://core.telegram.org/getProxyConfig if absent). + #[serde(default)] + pub proxy_config_v4_url: Option, + /// Optional path to cache raw getProxyConfigV6 snapshot for startup fallback. #[serde(default = "default_proxy_config_v6_cache_path")] pub proxy_config_v6_cache_path: Option, + /// Optional custom URL for getProxyConfigV6 (https://core.telegram.org/getProxyConfigV6 if absent). + #[serde(default)] + pub proxy_config_v6_url: Option, + /// Global ad_tag (32 hex chars from @MTProxybot). Fallback when user has no per-user tag in access.user_ad_tags. #[serde(default)] pub ad_tag: Option, @@ -960,8 +972,11 @@ impl Default for GeneralConfig { use_middle_proxy: default_true(), ad_tag: None, proxy_secret_path: default_proxy_secret_path(), + proxy_secret_url: None, proxy_config_v4_cache_path: default_proxy_config_v4_cache_path(), + proxy_config_v4_url: None, proxy_config_v6_cache_path: default_proxy_config_v6_cache_path(), + proxy_config_v6_url: None, middle_proxy_nat_ip: None, middle_proxy_nat_probe: default_true(), middle_proxy_nat_stun: default_middle_proxy_nat_stun(), diff --git a/src/maestro/me_startup.rs b/src/maestro/me_startup.rs index 4e49e9e..b647915 100644 --- a/src/maestro/me_startup.rs +++ b/src/maestro/me_startup.rs @@ -66,6 +66,7 @@ pub(crate) async fn initialize_me_pool( match crate::transport::middle_proxy::fetch_proxy_secret_with_upstream( proxy_secret_path, config.general.proxy_secret_len_max, + config.general.proxy_secret_url.as_deref(), Some(upstream_manager.clone()), ) .await @@ -126,7 +127,11 @@ pub(crate) async fn initialize_me_pool( .set_me_status(StartupMeStatus::Initializing, COMPONENT_ME_PROXY_CONFIG_V4) .await; let cfg_v4 = load_startup_proxy_config_snapshot( - "https://core.telegram.org/getProxyConfig", + config + .general + .proxy_config_v4_url + .as_deref() + .unwrap_or("https://core.telegram.org/getProxyConfig"), config.general.proxy_config_v4_cache_path.as_deref(), me2dc_fallback, "getProxyConfig", @@ -158,7 +163,11 @@ pub(crate) async fn initialize_me_pool( .set_me_status(StartupMeStatus::Initializing, COMPONENT_ME_PROXY_CONFIG_V6) .await; let cfg_v6 = load_startup_proxy_config_snapshot( - "https://core.telegram.org/getProxyConfigV6", + config + .general + .proxy_config_v6_url + .as_deref() + .unwrap_or("https://core.telegram.org/getProxyConfigV6"), config.general.proxy_config_v6_cache_path.as_deref(), me2dc_fallback, "getProxyConfigV6", diff --git a/src/transport/middle_proxy/config_updater.rs b/src/transport/middle_proxy/config_updater.rs index ebe45fc..116bd77 100644 --- a/src/transport/middle_proxy/config_updater.rs +++ b/src/transport/middle_proxy/config_updater.rs @@ -321,7 +321,14 @@ async fn run_update_cycle( let mut maps_changed = false; let mut ready_v4: Option<(ProxyConfigData, u64)> = None; - let cfg_v4 = retry_fetch("https://core.telegram.org/getProxyConfig", upstream.clone()).await; + let cfg_v4 = retry_fetch( + cfg.general + .proxy_config_v4_url + .as_deref() + .unwrap_or("https://core.telegram.org/getProxyConfig"), + upstream.clone(), + ) + .await; if let Some(cfg_v4) = cfg_v4 && snapshot_passes_guards(cfg, &cfg_v4, "getProxyConfig") { @@ -346,7 +353,10 @@ async fn run_update_cycle( let mut ready_v6: Option<(ProxyConfigData, u64)> = None; let cfg_v6 = retry_fetch( - "https://core.telegram.org/getProxyConfigV6", + cfg.general + .proxy_config_v6_url + .as_deref() + .unwrap_or("https://core.telegram.org/getProxyConfigV6"), upstream.clone(), ) .await; @@ -430,6 +440,7 @@ async fn run_update_cycle( match download_proxy_secret_with_max_len_via_upstream( cfg.general.proxy_secret_len_max, upstream, + cfg.general.proxy_secret_url.as_deref() ) .await { diff --git a/src/transport/middle_proxy/secret.rs b/src/transport/middle_proxy/secret.rs index a167773..ba1b161 100644 --- a/src/transport/middle_proxy/secret.rs +++ b/src/transport/middle_proxy/secret.rs @@ -37,20 +37,21 @@ pub(super) fn validate_proxy_secret_len(data_len: usize, max_len: usize) -> Resu /// Fetch Telegram proxy-secret binary. #[allow(dead_code)] -pub async fn fetch_proxy_secret(cache_path: Option<&str>, max_len: usize) -> Result> { - fetch_proxy_secret_with_upstream(cache_path, max_len, None).await +pub async fn fetch_proxy_secret(cache_path: Option<&str>, max_len: usize, proxy_secret_url: Option<&str>) -> Result> { + fetch_proxy_secret_with_upstream(cache_path, max_len, proxy_secret_url, None).await } /// Fetch Telegram proxy-secret binary, optionally through upstream routing. pub async fn fetch_proxy_secret_with_upstream( cache_path: Option<&str>, max_len: usize, + proxy_secret_url: Option<&str>, upstream: Option>, ) -> Result> { let cache = cache_path.unwrap_or("proxy-secret"); // 1) Try fresh download first. - match download_proxy_secret_with_max_len_via_upstream(max_len, upstream).await { + match download_proxy_secret_with_max_len_via_upstream(max_len, upstream, proxy_secret_url).await { Ok(data) => { if let Err(e) = tokio::fs::write(cache, &data).await { warn!(error = %e, "Failed to cache proxy-secret (non-fatal)"); @@ -91,14 +92,19 @@ pub async fn fetch_proxy_secret_with_upstream( #[allow(dead_code)] pub async fn download_proxy_secret_with_max_len(max_len: usize) -> Result> { - download_proxy_secret_with_max_len_via_upstream(max_len, None).await + download_proxy_secret_with_max_len_via_upstream(max_len, None, None).await } pub async fn download_proxy_secret_with_max_len_via_upstream( max_len: usize, upstream: Option>, + proxy_secret_url: Option<&str>, ) -> Result> { - let resp = https_get("https://core.telegram.org/getProxySecret", upstream).await?; + let resp = https_get( + proxy_secret_url.unwrap_or("https://core.telegram.org/getProxySecret"), + upstream, + ) + .await?; if !(200..=299).contains(&resp.status) { return Err(ProxyError::Proxy(format!( From 7b9b46291da9441669c86a87273e46c9332a12a7 Mon Sep 17 00:00:00 2001 From: Dmitry Zarva Date: Fri, 17 Apr 2026 13:19:29 +0000 Subject: [PATCH 2/4] - fix: param name in ru docs --- docs/Config_params/CONFIG_PARAMS.ru.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Config_params/CONFIG_PARAMS.ru.md b/docs/Config_params/CONFIG_PARAMS.ru.md index f6a67e0..aa3daa1 100644 --- a/docs/Config_params/CONFIG_PARAMS.ru.md +++ b/docs/Config_params/CONFIG_PARAMS.ru.md @@ -255,7 +255,7 @@ ``` ## proxy_secret_path - **Ограничения / валидация**: `String`. Если этот параметр не указан, используется путь по умолчанию — «proxy-secret». Пустые значения принимаются TOML/serde, но во время выполнения произойдет ошибка (invalid file path). - - **Описание**: Путь к файлу кэша `proxy-secret` инфраструктуры Telegram, используемому ME-handshake/аутентификацией RPC. Telemt всегда сначала пытается выполнить новую загрузку с https://core.telegram.org/getProxySecret (если не установлен `proxy_secret_path`), в случае успеха кэширует ее по этому пути и возвращается к чтению кэшированного файла в случае сбоя загрузки. + - **Описание**: Путь к файлу кэша `proxy-secret` инфраструктуры Telegram, используемому ME-handshake/аутентификацией RPC. Telemt всегда сначала пытается выполнить новую загрузку с https://core.telegram.org/getProxySecret (если не установлен `proxy_secret_url`), в случае успеха кэширует ее по этому пути и возвращается к чтению кэшированного файла в случае сбоя загрузки. - **Пример**: ```toml From 4e59e524540ee4cd784e281261c22a9d5a0c60f7 Mon Sep 17 00:00:00 2001 From: Dmitry Zarva Date: Fri, 17 Apr 2026 14:10:20 +0000 Subject: [PATCH 3/4] - fix: ru docs --- docs/Config_params/CONFIG_PARAMS.ru.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Config_params/CONFIG_PARAMS.ru.md b/docs/Config_params/CONFIG_PARAMS.ru.md index aa3daa1..d4b696a 100644 --- a/docs/Config_params/CONFIG_PARAMS.ru.md +++ b/docs/Config_params/CONFIG_PARAMS.ru.md @@ -264,7 +264,7 @@ ``` ## proxy_secret_url - **Ограничения / валидация**: `String`. Если не указан, используется `"https://core.telegram.org/getProxySecret"`. - - **Описание**: Необязательный URL для получения файла `proxy-secret` используемого ME-handshake/аутентификацией RPC. Telemt всегда сначала пытается выполнить новую загрузку с этого URL, в случае успеха кэширует ее по этому пути и возвращается к чтению кэшированного файла в случае сбоя загрузки. + - **Описание**: Необязательный URL для получения файла `proxy-secret` используемого ME-handshake/аутентификацией RPC. Telemt всегда сначала пытается выполнить новую загрузку с этого URL (если не задан, используется https://core.telegram.org/getProxySecret). - **Пример**: ```toml From fa3566a9cbc7309331dab17ae56d887d684ab2ad Mon Sep 17 00:00:00 2001 From: Dmitry Zarva Date: Fri, 17 Apr 2026 16:20:16 +0000 Subject: [PATCH 4/4] - fix: fmt issues --- src/transport/middle_proxy/config_updater.rs | 2 +- src/transport/middle_proxy/secret.rs | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/transport/middle_proxy/config_updater.rs b/src/transport/middle_proxy/config_updater.rs index 116bd77..b777445 100644 --- a/src/transport/middle_proxy/config_updater.rs +++ b/src/transport/middle_proxy/config_updater.rs @@ -440,7 +440,7 @@ async fn run_update_cycle( match download_proxy_secret_with_max_len_via_upstream( cfg.general.proxy_secret_len_max, upstream, - cfg.general.proxy_secret_url.as_deref() + cfg.general.proxy_secret_url.as_deref(), ) .await { diff --git a/src/transport/middle_proxy/secret.rs b/src/transport/middle_proxy/secret.rs index ba1b161..e3495e8 100644 --- a/src/transport/middle_proxy/secret.rs +++ b/src/transport/middle_proxy/secret.rs @@ -37,7 +37,11 @@ pub(super) fn validate_proxy_secret_len(data_len: usize, max_len: usize) -> Resu /// Fetch Telegram proxy-secret binary. #[allow(dead_code)] -pub async fn fetch_proxy_secret(cache_path: Option<&str>, max_len: usize, proxy_secret_url: Option<&str>) -> Result> { +pub async fn fetch_proxy_secret( + cache_path: Option<&str>, + max_len: usize, + proxy_secret_url: Option<&str>, +) -> Result> { fetch_proxy_secret_with_upstream(cache_path, max_len, proxy_secret_url, None).await } @@ -51,7 +55,8 @@ pub async fn fetch_proxy_secret_with_upstream( let cache = cache_path.unwrap_or("proxy-secret"); // 1) Try fresh download first. - match download_proxy_secret_with_max_len_via_upstream(max_len, upstream, proxy_secret_url).await { + match download_proxy_secret_with_max_len_via_upstream(max_len, upstream, proxy_secret_url).await + { Ok(data) => { if let Err(e) = tokio::fs::write(cache, &data).await { warn!(error = %e, "Failed to cache proxy-secret (non-fatal)");