🇷🇺 Что нового (RU)
Что решает этот релиз
v1.0.1 — патч-релиз для middleproxy-режима (use_middle_proxy = true), который чинит коварный сценарий: прокси выглядит полностью здоровым, но клиенты не подключаются.
Production-инцидент выглядел так: процесс жив, слушает :443, туннель в норме (свежий handshake, до DC по TCP достукивается) — но users_total=0, и каждое соединение уходит в hs_timeout. Клиенты проходят FakeTLS, но застревают на upstream RPC-handshake с middleproxy, который перестал отвечать.
Причина: метаданные MiddleProxy (адреса DC из getProxyConfig + секрет из getProxySecret) обновлялись по таймеру раз в 24 часа, а Telegram ротирует их быстрее — в нашем случае dc4 уехал 91.108.4.139 → 91.108.4.200 примерно через 10 часов после загрузки на старте. Кэш протух, и весь middleproxy-трафик встал до ручного рестарта.
v1.0.1 обновляет метаданные часто (раз в час), а при первых же зависших handshake'ах — немедленно (реактивно), и заодно делает саму загрузку метаданных устойчивой к битому /etc/resolv.conf.
[!NOTE]
Менятьconfig.tomlне нужно — фикс работает на текущих настройках. Затрагивает только middleproxy-режим (use_middle_proxy = true); чисто обфусцированных / FakeTLS-без-middleproxy деплоев это не касается.
[!TIP]
Что реактивное обновление сработало, видно в логах — строкаMiddle-proxy reactive refresh: ..., а следомMiddle-proxy cache updated: dc4=... secret_len=128.
Что изменено
Метаданные MiddleProxy обновляются раз в час, а не раз в сутки (#305)
- Период периодического обновления (
middle_proxy_update_period_ns) сокращён с 24 часов до 1 часа. - Telegram ротирует адреса DC и секрет в пределах суток (в инциденте — за ~10 часов), поэтому суточная частота позволяла кэшу протухнуть и обрушить весь middleproxy-трафик до рестарта.
- Загрузка дешёвая и best-effort: при сетевом сбое прокси держит текущий кэш, так что дополнительные опросы стоят практически ничего.
- Худший случай протухания сужен с суток до часа.
Реактивное обновление при зависших handshake'ах (#305)
- Если соединение уходит в
hs_timeout, всё ещё находясь в RPC-handshake с middleproxy, это прямой признак, что кэш протух — data-plane просит updater обновиться не дожидаясь часового таймера. - Протухший DC/секрет заменяется за ~
handshake_timeout+ одну загрузку (десятки секунд) вместо «до часа». - Дебаунс 60 секунд: шквал падающих handshake'ов — или реальный сбой на стороне middleproxy, который свежие метаданные не починят — не устроит refresh-шторм.
- Реализовано lock-free (атомарный флаг на data-plane, который читает wait-цикл updater'а); на горячий путь ретрансляции не влияет.
Загрузка метаданных устойчива к битому resolv.conf (#305)
- Стандартный резолвер Zig кидает
ResolvConfParseFailedна/etc/resolv.confбез завершающего перевода строки (SolusVM и ряд VPS-образов) — из-за чего каждая загрузка логировалаunreachable directly … retrying via tunnel. - Теперь именно на эту ошибку загрузка делает фоллбэк на curl (резолв через NSS/getent, который такой файл переваривает).
- Бездепендентный std-путь сохранён для здоровых хостов; для middleproxy с прямым egress на таких хостах загрузка метаданных перестаёт быть сломанной, а шум в логах уходит.
Проверено
zig build test(включая новый тест предикатаMiddleProxyHandshakeStep.awaitingMiddleProxy) и кросс-компиляция подx86_64-linux+aarch64-linux— зелёные;zig fmtчистый.- GitHub Actions:
Test & Build,E2E Integration,Bench (No Soak). - Инцидент диагностирован и восстановлен на живом деплое (
proxy.sleep3r.ru): подтянулись свежиеdc4=91.108.4.200+ секрет,users_totalснова растёт,hs_timeoutпропал.
🇬🇧 Release notes (EN)
What this release addresses
v1.0.1 is a patch release for middleproxy mode (use_middle_proxy = true) that fixes a sneaky failure: the proxy looks completely healthy, yet clients can't connect.
The production incident looked like this: the process is up and listening on :443, the tunnel is fine (fresh handshake, DCs reachable over TCP) — but users_total=0 and every connection hits hs_timeout. Clients pass FakeTLS but then stall on the upstream RPC handshake with a middleproxy that stopped answering.
Root cause: the MiddleProxy metadata (getProxyConfig DC addresses + getProxySecret) refreshed on a 24-hour timer, but Telegram rotates it faster — here the dc4 media-DC address moved 91.108.4.139 → 91.108.4.200 about 10 hours after the startup fetch. The cache went stale and stalled all middleproxy traffic until a manual restart.
v1.0.1 refreshes the metadata often (hourly), refreshes it immediately (reactively) at the first stalled handshakes, and makes the fetch itself resilient to a malformed /etc/resolv.conf.
[!NOTE]
Noconfig.tomlchanges needed — the fix works on your current settings. It only affects middleproxy mode (use_middle_proxy = true); plain-obfuscated / FakeTLS-without-middleproxy deployments are unaffected.
[!TIP]
You can see the reactive refresh fire in the logs — the lineMiddle-proxy reactive refresh: ...followed byMiddle-proxy cache updated: dc4=... secret_len=128.
What changed
MiddleProxy metadata refreshes hourly, not daily (#305)
- The periodic refresh period (
middle_proxy_update_period_ns) is cut from 24 hours to 1 hour. - Telegram rotates the DC addresses and secret within a day (~10 hours in the incident), so the daily cadence let the cache go stale and stall all middleproxy traffic until a restart.
- The fetch is cheap and best-effort: on a network failure the proxy keeps its current cache, so the extra polls cost effectively nothing.
- Bounds the worst-case stale window from a day to an hour.
Reactive refresh on stalled handshakes (#305)
- When a connection hits
hs_timeoutwhile still in the RPC handshake with the middleproxy, that's a direct sign the cache went stale — the data plane asks the updater to refresh without waiting for the hourly timer. - A stale DC/secret is replaced in ~
handshake_timeout+ one fetch (tens of seconds) instead of up to an hour. - Debounced by 60 seconds: a flood of failing handshakes — or a genuine middleproxy-side outage that fresh metadata can't fix — can't cause a refresh storm.
- Implemented lock-free (an atomic flag on the data plane, read by the updater's wait loop); no effect on the relay hot path.
Metadata fetch resilient to a malformed resolv.conf (#305)
- Zig's std resolver throws
ResolvConfParseFailedon a/etc/resolv.confwithout a trailing newline (SolusVM and several VPS images) — which is why every fetch loggedunreachable directly … retrying via tunnel. - On exactly that error, the fetch now falls back to curl (NSS/getent resolution, which tolerates that file).
- The dependency-free std path is kept for healthy hosts; direct-egress middleproxy on such hosts stops being broken, and the log noise goes away.
Verified
zig build test(including a newMiddleProxyHandshakeStep.awaitingMiddleProxypredicate test) and cross-compilation forx86_64-linux+aarch64-linux— green;zig fmtclean.- GitHub Actions:
Test & Build,E2E Integration,Bench (No Soak). - The incident was diagnosed and restored on the live deployment (
proxy.sleep3r.ru): freshdc4=91.108.4.200+ secret pulled,users_totalclimbing again,hs_timeoutgone.