v1.0.1

sleep3r/mtproto.zigv1.0.1Jun 7, 2026by sleep3r

Upgrading?

AI Summary

Fixes a middleproxy mode issue where the proxy appeared healthy but clients could not connect due to stale metadata that failed to refresh frequently enough.

Key Highlights

  • MiddleProxy metadata refresh period reduced from 24 hours to 1 hour.
  • Reactive metadata refresh triggered immediately upon stalled handshakes.
  • Metadata fetch now resilient to malformed `/etc/resolv.conf` files using a `getent` fallback.

Full Release Notes

<details open>
<summary>🇷🇺 Что нового (RU)</summary>

## Что решает этот релиз

`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](https://github.com/sleep3r/mtproto.zig/pull/305))

- Период периодического обновления (`middle_proxy_update_period_ns`) сокращён с 24 часов до **1 часа**.
- Telegram ротирует адреса DC и секрет в пределах суток (в инциденте — за ~10 часов), поэтому суточная частота позволяла кэшу протухнуть и обрушить весь middleproxy-трафик до рестарта.
- Загрузка дешёвая и best-effort: при сетевом сбое прокси держит текущий кэш, так что дополнительные опросы стоят практически ничего.
- Худший случай протухания сужен с суток до часа.

### Реактивное обновление при зависших handshake'ах ([#305](https://github.com/sleep3r/mtproto.zig/pull/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](https://github.com/sleep3r/mtproto.zig/pull/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` пропал.

</details>

---

<details>
<summary>🇬🇧 Release notes (EN)</summary>

## 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]
> No `config.toml` changes 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 line `Middle-proxy reactive refresh: ...` followed by `Middle-proxy cache updated: dc4=... secret_len=128`.

## What changed

### MiddleProxy metadata refreshes hourly, not daily ([#305](https://github.com/sleep3r/mtproto.zig/pull/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](https://github.com/sleep3r/mtproto.zig/pull/305))

- When a connection hits `hs_timeout` **while 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](https://github.com/sleep3r/mtproto.zig/pull/305))

- Zig's std resolver throws `ResolvConfParseFailed` on a `/etc/resolv.conf` without a trailing newline (SolusVM and several VPS images) — which is why every fetch logged `unreachable 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 new `MiddleProxyHandshakeStep.awaitingMiddleProxy` predicate test) and cross-compilation for `x86_64-linux` + `aarch64-linux` — green; `zig fmt` clean.
- GitHub Actions: `Test & Build`, `E2E Integration`, `Bench (No Soak)`.
- The incident was diagnosed and restored on the live deployment (`proxy.sleep3r.ru`): fresh `dc4=91.108.4.200` + secret pulled, `users_total` climbing again, `hs_timeout` gone.

</details>

## Changelog

* fix(middleproxy): hourly + reactive refresh, and a getent fallback on the metadata fetch ([#305](https://github.com/sleep3r/mtproto.zig/pull/305)) ([86ccf03](https://github.com/sleep3r/mtproto.zig/commit/86ccf034be72604628f9fc4632ce978d4a24eea2))

Related Repositories