v0.25.1

sleep3r/mtproto.zigv0.25.1Jun 4, 2026by sleep3r

Upgrading?

AI Summary

Fixes a systemd timer issue where the tunnel pool timer would become 'elapsed' and stop running periodic failover checks.

Key Highlights

  • Tunnel pool timer now uses `OnUnitInactiveSec` to correctly repeat after each run.
  • Proxy restart is no longer the only way to refresh tunnel routing.
  • Regression test added to prevent future generation of elapsed timers.

Full Release Notes

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

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

`v0.25.1` — patch-релиз для операторов, которые используют AmneziaWG / WireGuard tunnel mode через `mtbuddy setup tunnel`.

В `v0.25.0` tunnel pool получил нормальный lifecycle management: TUI и dashboard научились видеть, заменять и удалять tunnel interfaces. Но в production-варианте обнаружился отдельный systemd edge case: `mtproto-tunnel-pool.timer` мог быть enabled, но больше не запускать periodic failover checks.

Выглядело это так:

```text
mtproto-tunnel-pool.timer: active (elapsed)
Trigger: n/a
LastTriggerUSec: Sat 2026-05-23 ...
```

Из-за этого `setup_tunnel.sh` не перепроверял tunnel health и не переустанавливал `fwmark 200 -> table 200 -> awg0`. Если route/tunnel state деградировал, пользовательские соединения могли зависать на MTProto handshake, а ручной restart `mtproto-proxy` временно всё лечил, потому что `mtproto-proxy.service` снова запускал:

```text
ExecStartPre=/usr/local/bin/setup_tunnel.sh
```

Корень проблемы был в systemd timer contract для oneshot service: timer использовал `OnUnitActiveSec=30s`, а `mtproto-tunnel-pool.service` — это короткий `Type=oneshot`. Для таких health-check jobs надёжнее планировать следующий запуск от момента завершения unit, то есть через `OnUnitInactiveSec=30s`.

`v0.25.1` меняет timer template именно на это поведение и добавляет regression test, чтобы future `mtbuddy setup tunnel` больше не генерировал одноразовый elapsed timer.

> [!NOTE]
> Обновление бинарника само по себе не переписывает уже установленный `/etc/systemd/system/mtproto-tunnel-pool.timer`. После обновления заново выполните tunnel setup через `sudo mtbuddy --interactive` → `Setup tunnel` или повторите `sudo mtbuddy setup tunnel ...` с текущим tunnel config/link, чтобы regenerate systemd units.

> [!TIP]
> После regeneration проверьте:
>
> ```bash
> systemctl list-timers mtproto-tunnel-pool.timer --all
> systemctl status mtproto-tunnel-pool.timer
> ```
>
> У здорового timer должен быть будущий `NEXT` trigger и состояние `active (waiting)`, а не `active (elapsed)` / `Trigger: n/a`.

## Что изменено

### Tunnel pool timer снова повторяется после oneshot run ([#291](https://github.com/sleep3r/mtproto.zig/pull/291))

- `mtproto-tunnel-pool.timer` теперь генерируется с `OnUnitInactiveSec=30s`.
- Следующий запуск планируется после завершения `mtproto-tunnel-pool.service`, что подходит для `Type=oneshot`.
- Periodic failover checks снова продолжают:
  - поднимать/проверять configured tunnel interfaces;
  - обновлять route table 200;
  - подтверждать `ip route get ... mark 200`;
  - писать свежий `/run/mtproto-proxy/tunnel-pool.state`.

### Рестарт прокси больше не должен быть единственным способом «оживить» tunnel route ([#291](https://github.com/sleep3r/mtproto.zig/pull/291))

- До фикса route refresh мог происходить только при `systemctl restart mtproto-proxy`, потому что proxy unit запускал `setup_tunnel.sh` в `ExecStartPre`.
- После regeneration timer сам продолжает выполнять tunnel pool controller.
- Это закрывает класс ситуаций, где MTProto connections застревают в `hs_inflight`, а ручной restart выглядит как единственное восстановление.

### Тесты mtbuddy теперь реально подтягивают tunnel command tests ([#291](https://github.com/sleep3r/mtproto.zig/pull/291))

- Добавлен test hook для `src/ctl/tunnel.zig` из `mtbuddy` test target.
- Добавлен regression test на `OnUnitInactiveSec=30s` в timer template.
- Обновлён один tunnel test под текущий Zig 0.16 file IO API.

## Проверено

- `zig build test --summary all`
- GitHub Actions:
  - `Test & Build`
  - `Bench (No Soak)`
  - `E2E Integration`
  - installer e2e: `debian:12`
  - installer e2e: `ubuntu:24.04`
- На affected host проверено, что обновлённый timer переходит в `active (waiting)` и повторно запускает `mtproto-tunnel-pool.service`.

</details>

---

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

## What this release addresses

`v0.25.1` is a patch release for operators using AmneziaWG / WireGuard tunnel mode through `mtbuddy setup tunnel`.

`v0.25.0` added proper tunnel pool lifecycle management: the TUI and dashboard can now inspect, replace, and delete tunnel interfaces. A separate production systemd edge case showed up afterwards: `mtproto-tunnel-pool.timer` could remain enabled while no longer scheduling periodic failover checks.

It looked like this:

```text
mtproto-tunnel-pool.timer: active (elapsed)
Trigger: n/a
LastTriggerUSec: Sat 2026-05-23 ...
```

As a result, `setup_tunnel.sh` stopped rechecking tunnel health and stopped refreshing `fwmark 200 -> table 200 -> awg0`. If route or tunnel state degraded, user connections could get stuck during MTProto handshake, while manually restarting `mtproto-proxy` appeared to fix it because the proxy unit ran:

```text
ExecStartPre=/usr/local/bin/setup_tunnel.sh
```

The root cause was the timer contract for a oneshot service: the timer used `OnUnitActiveSec=30s`, while `mtproto-tunnel-pool.service` is a short `Type=oneshot` job. For this kind of health check, the next run should be scheduled after the unit exits, using `OnUnitInactiveSec=30s`.

`v0.25.1` changes the generated timer template to that behavior and adds a regression test so future `mtbuddy setup tunnel` runs no longer create a one-shot elapsed timer.

> [!NOTE]
> Updating the binary alone does not rewrite an already installed `/etc/systemd/system/mtproto-tunnel-pool.timer`. After updating, rerun tunnel setup through `sudo mtbuddy --interactive` → `Setup tunnel`, or rerun `sudo mtbuddy setup tunnel ...` with the current tunnel config/link to regenerate the systemd units.

> [!TIP]
> After regeneration, check:
>
> ```bash
> systemctl list-timers mtproto-tunnel-pool.timer --all
> systemctl status mtproto-tunnel-pool.timer
> ```
>
> A healthy timer should have a future `NEXT` trigger and show `active (waiting)`, not `active (elapsed)` / `Trigger: n/a`.

## What changed

### Tunnel pool timer repeats after each oneshot run again ([#291](https://github.com/sleep3r/mtproto.zig/pull/291))

- `mtproto-tunnel-pool.timer` is now generated with `OnUnitInactiveSec=30s`.
- The next run is scheduled after `mtproto-tunnel-pool.service` finishes, matching its `Type=oneshot` behavior.
- Periodic failover checks keep:
  - bringing up/checking configured tunnel interfaces;
  - refreshing route table 200;
  - validating `ip route get ... mark 200`;
  - writing fresh `/run/mtproto-proxy/tunnel-pool.state`.

### Restarting the proxy should no longer be the only way to refresh tunnel routing ([#291](https://github.com/sleep3r/mtproto.zig/pull/291))

- Before this fix, route refresh could happen only on `systemctl restart mtproto-proxy`, because the proxy unit ran `setup_tunnel.sh` in `ExecStartPre`.
- After regenerating the units, the timer continues running the tunnel pool controller by itself.
- This closes the class of incidents where MTProto connections accumulate in `hs_inflight`, while a manual proxy restart looks like the only recovery path.

### mtbuddy tests now include tunnel command tests ([#291](https://github.com/sleep3r/mtproto.zig/pull/291))

- Added a test hook so the `mtbuddy` test target references `src/ctl/tunnel.zig`.
- Added a regression test for `OnUnitInactiveSec=30s` in the generated timer template.
- Updated one tunnel test to the current Zig 0.16 file IO API.

## Verified

- `zig build test --summary all`
- GitHub Actions:
  - `Test & Build`
  - `Bench (No Soak)`
  - `E2E Integration`
  - installer e2e: `debian:12`
  - installer e2e: `ubuntu:24.04`
- Verified on the affected host that the updated timer enters `active (waiting)` and repeatedly triggers `mtproto-tunnel-pool.service`.

</details>

## Changelog

* fix: keep tunnel pool timer recurring ([#291](https://github.com/sleep3r/mtproto.zig/pull/291)) ([59e2608](https://github.com/sleep3r/mtproto.zig/commit/59e2608f6f71c88e8d3173f832848956f002a9ac))
* chore(main): release 0.25.1 ([#293](https://github.com/sleep3r/mtproto.zig/pull/293)) ([1cc4f1b](https://github.com/sleep3r/mtproto.zig/commit/1cc4f1b))

Related Repositories