v1.10.2
sleep3r/mtproto.zigv1.10.2Aug 1, 2026by sleep3r
Upgrading?
AI Summary
Fixes the tunnel failover watchdog timer, which could stop re-arming, and adds auto-healing for stale WireGuard tunnels.
Key Highlights
- Fixed mtproto-tunnel-pool.timer stopping permanently.
- Added auto-healing for stale WG sessions after consecutive failures.
New Features
- Fixed pool timer
- Auto-heal stale tunnels
Full Release Notes
<details open> <summary>🇷🇺 Что нового (RU)</summary> ## Коротко `v1.10.2` чинит два связанных бага в фейловере тоннеля (`upstream.type = "tunnel"`, WireGuard/AmneziaWG egress): watchdog-таймер health-check мог остановиться навсегда и никогда не перезапуститься сам, а протухший (но формально «up») тоннель мог использоваться бесконечно без попытки самовосстановления. Ядро MTProto-прокси и сетевой протокол не менялись. Обновление безопасно и рекомендуется всем, кто использует `upstream.type = "tunnel"`. ## Таймер `mtproto-tunnel-pool.timer` останавливался навсегда **Симптом:** обнаружено вживую на проде (`proxy.sleep3r.ru`) — прокси переставал доставать до Telegram, хотя `systemctl status mtproto-tunnel-pool.timer` показывал `active`. `LastTriggerUSec` был заморожен на дате более чем десятидневной давности, `NextElapseUSecRealtime` — пустой: сам failover-watchdog фактически не работал уже давно. **Причина:** таймер стоял на связке `OnBootSec=30s` + `OnUnitInactiveSec=30s`. `OnUnitInactiveSec` перевзводится от перехода связанного `.service` в inactive; после обычного `systemctl restart` самого таймера (без ребута хоста) `OnBootSec` больше не сработает — он привязан к уже прошедшему бут-циклу, — а `OnUnitInactiveSec` не имеет свежего перехода, от которого оттолкнуться. Таймер замолкает навсегда. Воспроизведено вживую: `systemctl restart` таймера не помогал, а разовый ручной `systemctl start` самого `.service` давал `OnUnitInactiveSec` нужный «якорь», и таймер снова затикал — это подтвердило причину и легло в основу фикса. **Фикс:** таймер переведён на `OnCalendar=*-*-* *:*:00/30` — расписание пересчитывается от wall-clock при каждом (ре)старте юнита и не зависит от истории состояния связанного сервиса, так что застрять описанным образом больше не может. ## Протухший тоннель использовался вечно вместо самовосстановления **Симптом:** health-check пула уже умел определять «тоннель up, но Telegram через него не достаётся» — но при единственном тоннеле в пуле фейловериться было не с кем, и скрипт просто продолжал маршрутизировать через тот же деградировавший тоннель, помечая статус `degraded`. Тот же класс проблемы, что VPN на телефоне, который выглядит подключённым, но не работает, пока его не выключить и включить руками. **Причина:** проверка интерфейса поднимала его, только если он вообще отсутствовал; если интерфейс был на месте, а WG-сессия внутри протухла (например, после сетевого сбоя), её никто и никогда не передёргивал заново. **Фикс:** добавлен счётчик подряд идущих «degraded»-проверок на интерфейс. После `BOUNCE_AFTER_DEGRADED_CHECKS` (2, то есть ~60с при 30-секундном такте) подряд неудачных проб тоннель один раз передёргивается (`awg-quick`/`wg-quick` down → up), чтобы форсировать свежий handshake, и перепроверяется — прежде чем соглашаться на деградированный fallback. Порог намеренно выше одного цикла, чтобы разовый всплеск не дёргал здоровый тоннель; счётчик сбрасывается при успехе или после попытки bounce, так что по-настоящему заблокированный маршрут не будет передёргиваться каждый цикл бесконечно. ## Проверено - `zig build test`: 269/269 зелёные; кросс-сборка `x86_64-linux` — чистая. - Задеплоено и подтверждено на живом проде (`proxy.sleep3r.ru`): после обновления таймер сразу показал корректный пересчитанный `Trigger` (не пустой `NextElapseUSecRealtime`, как раньше), несколько циклов подряд отработали чисто с новым скриптом, тоннель остался `healthy`. ## Changelog - fix(ctl): repair dead pool-failover timer + auto-heal stale WG sessions ([#384](https://github.com/sleep3r/mtproto.zig/pull/384)) </details> --- <details> <summary>🇬🇧 Release notes (EN)</summary> ## TL;DR `v1.10.2` fixes two related bugs in the tunnel failover path (`upstream.type = "tunnel"`, WireGuard/AmneziaWG egress): the health-check watchdog timer could stop for good and never re-arm itself, and a stale-but-nominally-up tunnel could be used forever without ever attempting to self-heal. The MTProto proxy core and wire protocol are unchanged. Upgrading is safe and recommended for anyone running `upstream.type = "tunnel"`. ## `mtproto-tunnel-pool.timer` stopped re-arming for good **Symptom:** found live on prod (`proxy.sleep3r.ru`) — the proxy stopped reaching Telegram even though `systemctl status mtproto-tunnel-pool.timer` reported `active`. `LastTriggerUSec` was frozen more than ten days in the past, and `NextElapseUSecRealtime` was empty: the failover watchdog had effectively been dead the whole time. **Cause:** the timer used `OnBootSec=30s` + `OnUnitInactiveSec=30s`. `OnUnitInactiveSec` re-arms off the paired `.service`'s own inactive-transition; after a plain `systemctl restart` of just the timer (no host reboot), `OnBootSec` won't fire again — it's tied to a boot cycle that already happened — and `OnUnitInactiveSec` has no fresh transition to key off of. The timer goes silent for good. Reproduced live: restarting the timer alone never helped, while manually running the paired `.service` once gave `OnUnitInactiveSec` the anchor it needed and the timer started ticking again — confirming the cause and the fix. **Fix:** the timer now uses `OnCalendar=*-*-* *:*:00/30`, which recomputes from wall-clock on every (re)start instead of chaining off the paired unit's state history, so it can't get stuck this way again. ## A stale tunnel was used forever instead of self-healing **Symptom:** the pool health-check could already detect "tunnel up, but Telegram unreachable through it" — but with a single tunnel in the pool there was nothing to fail over to, so the script just kept routing through the same degraded tunnel, marking it `degraded`. The same failure class as a phone VPN that looks connected but passes nothing until manually toggled off and on. **Cause:** the interface check only brought the interface up if it was missing entirely; if the interface was present but its WG session had gone stale (e.g. after a network hiccup), nothing ever bounced it. **Fix:** added a consecutive-degraded-checks counter per interface. After `BOUNCE_AFTER_DEGRADED_CHECKS` (2, ~60s at the 30s cadence) failed probes in a row, the tunnel is bounced once (`awg-quick`/`wg-quick` down → up) to force a fresh handshake and re-probed, before falling back to the degraded state. The threshold is kept above a single cycle so one transient probe blip doesn't churn a healthy tunnel; the counter resets on success or after an attempted bounce, so a genuinely-blocked route isn't bounced every cycle forever. ## Verified - `zig build test`: 269/269 pass; `x86_64-linux` cross-compile is clean. - Deployed and confirmed on live prod (`proxy.sleep3r.ru`): after the update the timer immediately showed a correctly recomputed `Trigger` (no longer an empty `NextElapseUSecRealtime`), several cycles ran clean with the new script, and the tunnel stayed `healthy`. ## Changelog - fix(ctl): repair dead pool-failover timer + auto-heal stale WG sessions ([#384](https://github.com/sleep3r/mtproto.zig/pull/384)) </details> --- **Full Changelog**: https://github.com/sleep3r/mtproto.zig/compare/v1.10.1...v1.10.2
Related Repositories
lightpanda-io/browser★ 36,848
Lightpanda: the headless browser designed for AI and automation
tigerbeetle/tigerbeetle★ 17,228
The financial transactions database designed for mission critical safety and performance.
nullclaw/nullclaw★ 8,130
Fastest, smallest, and fully autonomous AI assistant infrastructure written in Zig
zml/zml★ 4,194
Any model. Any hardware. Zero compromise. Built with @ziglang / @openxla / MLIR / @bazelbuild
dmtrKovalenko/odiff★ 3,280
A very fast SIMD-first image comparison library (with nodejs API)