v0.23.0

sleep3r/mtproto.zigv0.23.0May 10, 2026by sleep3r

Upgrading?

AI Summary

Feature release introducing tunnel pool failover, allowing operators to manage multiple tunnel interfaces with automatic switching to the next healthy interface.

Key Highlights

  • Ordered pool of tunnel interfaces with automatic failover
  • New `pinned_interface` option for manual preference
  • Dashboard updates to show pool state and per-interface health
  • Added `README.ru.md` documentation

New Features

  • Tunnel pool failover and management

Full Release Notes

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

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

`v0.23.0` - большой feature-релиз про tunnel pool failover, более честную документацию по ограничениям и нормальную русскую версию README.

До этого `mtbuddy setup tunnel` фактически был рассчитан на один tunnel interface: прокси ставил `SO_MARK=200`, а host routing table 200 указывала на один настроенный `awg0`. Это работало, пока один туннель жив. Но если Telegram route через него умирал, оператору приходилось вручную менять интерфейс, перезапускать routing setup и держать в голове текущее состояние.

В `v0.23.0` tunnel mode стал pool-based: `mtproto-proxy` по-прежнему использует один data-plane контракт (`SO_MARK=200 -> table 200`), а `mtbuddy` управляет ordered pool туннелей и выбирает первый живой интерфейс. Если основной туннель отваливается, новые upstream-соединения уходят через следующий здоровый tunnel. Когда приоритетный туннель возвращается, контроллер автоматически переключает table 200 обратно, если не задан manual pin.

> [!NOTE]
> Уже открытые TCP-соединения не мигрируют между туннелями. Failover влияет на новые upstream connections и клиентские reconnects.

> [!IMPORTANT]
> Telegram calls по-прежнему не поддерживаются. В Telegram они завязаны на SOCKS-поведение, которое нельзя нормально замаскировать в этой модели. Также медиа на аккаунтах без Premium требует MiddleProxy: без него такие загрузки не станут надёжно работать только за счёт tunnel pool.

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

### Tunnel pool failover ([#250](https://github.com/sleep3r/mtproto.zig/pull/250))

- `[upstream.tunnel]` теперь поддерживает ordered pool:
  - `interfaces = ["awg0", "awg1", ...]`;
  - `pinned_interface = "awg1"` для ручного предпочтения;
  - старый `interface = "awg0"` остаётся совместимым fallback.
- `mtbuddy` ставит `mtproto-tunnel-pool.service` и `mtproto-tunnel-pool.timer`.
- `/usr/local/bin/setup_tunnel.sh` теперь:
  - поднимает все интерфейсы из пула;
  - проверяет `fwmark 200 -> table 200`;
  - пробует Telegram через каждый интерфейс;
  - переключает route через `ip -4 route replace default dev <iface> table 200`.
- Selection policy - priority auto-failback: первый живой интерфейс в списке снова становится активным, когда восстанавливается.
- Automatic failover не перезапускает `mtproto-proxy`.

### Настройка tunnel pool через mtbuddy ([#250](https://github.com/sleep3r/mtproto.zig/pull/250))

- Повторный `mtbuddy setup tunnel <conf|vpn://>` добавляет следующий свободный `awgN`.
- Добавлен `--iface <name>`, чтобы установить или заменить конкретный участник пула.
- Интерактивный wizard теперь показывает:
  - выбор VPN-типа (пока AmneziaWG);
  - текущий tunnel pool;
  - действие: создать новый tunnel или заменить существующий.
- Конфиги сохраняются в `/etc/amnezia/amneziawg/<iface>.conf`.
- Input sanitizing стал явным: удаляются `DNS`, пустые AWG assignments, добавляется `Table = off`.

### Runtime и dashboard знают про активный tunnel ([#250](https://github.com/sleep3r/mtproto.zig/pull/250))

- `Config` парсит `interfaces` и `pinned_interface` вместе с legacy `interface`.
- MiddleProxy metadata refresh больше не предполагает один legacy interface: активный tunnel определяется из table 200 / pool state.
- Dashboard routing card показывает ordered pool, active route, pinned interface, last switch, health reason и per-interface probe status.
- Dashboard action `Set iface` переосмыслен как `Pin`: он пишет `pinned_interface`, запускает pool controller один раз и не рестартит прокси.
- `/api/stats.routing` получил поля для pool/active/pinned/health state.

### Документация и UX ([#250](https://github.com/sleep3r/mtproto.zig/pull/250), [#252](https://github.com/sleep3r/mtproto.zig/pull/252))

- Добавлен `README.ru.md`.
- В верхней части README появился более заметный переключатель языков EN/RU.
- Docker описан честнее: контейнер полезен для тестов, упаковки и простых запусков, но основной production path проекта - native Linux host под управлением `mtbuddy`.
- README теперь явно описывает tunnel pool, `pinned_interface`, dashboard monitoring/pinning и ограничения Telegram calls / non-Premium media без MiddleProxy.
- `config.toml.example` обновлён под ordered tunnel pool.
- `THREAT_MODEL.md` дополнен ограничениями по calls/media.

## Проверено

- `python3 -m py_compile src/ctl/dashboard_assets/server.py`
- `node --check src/ctl/dashboard_assets/static/app.js`
- generated tunnel controller script checked with `bash -n`
- `zig build test`
- `zig build -Dtarget=x86_64-linux`
- `git diff --check`

</details>

---

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

## What this release addresses

`v0.23.0` is a larger feature release for tunnel pool failover, clearer compatibility documentation, and first-class Russian README coverage.

Before this release, `mtbuddy setup tunnel` was effectively designed around a single tunnel interface: the proxy marked upstream sockets with `SO_MARK=200`, and host routing table 200 pointed at one configured `awg0`. That was fine while the tunnel stayed healthy. Once that route failed, operators had to manually change the interface, rerun routing setup, and keep the current state in their head.

`v0.23.0` makes tunnel mode pool-based. `mtproto-proxy` still keeps one data-plane contract (`SO_MARK=200 -> table 200`), while `mtbuddy` manages an ordered pool of tunnels and selects the first healthy interface. If the primary tunnel fails, new upstream connections use the next healthy tunnel. When the higher-priority tunnel recovers, the controller automatically points table 200 back to it unless a manual pin is set.

> [!NOTE]
> Existing TCP connections are not migrated between tunnels. Failover affects new upstream connections and client reconnects.

> [!IMPORTANT]
> Telegram calls are still unsupported. Telegram calls depend on SOCKS behavior that cannot be masked properly in this model. Media loading on non-Premium accounts also still requires MiddleProxy; the tunnel pool alone does not make that path reliable.

## What changed

### Tunnel pool failover ([#250](https://github.com/sleep3r/mtproto.zig/pull/250))

- `[upstream.tunnel]` now supports an ordered pool:
  - `interfaces = ["awg0", "awg1", ...]`;
  - `pinned_interface = "awg1"` for manual preference;
  - legacy `interface = "awg0"` remains a compatible fallback.
- `mtbuddy` installs `mtproto-tunnel-pool.service` and `mtproto-tunnel-pool.timer`.
- `/usr/local/bin/setup_tunnel.sh` now:
  - brings up all configured pool interfaces;
  - ensures `fwmark 200 -> table 200`;
  - probes Telegram through each interface;
  - switches the route with `ip -4 route replace default dev <iface> table 200`.
- The selection policy is priority auto-failback: the first healthy interface in the list becomes active again when it recovers.
- Automatic failover does not restart `mtproto-proxy`.

### Tunnel pool setup through mtbuddy ([#250](https://github.com/sleep3r/mtproto.zig/pull/250))

- Re-running `mtbuddy setup tunnel <conf|vpn://>` appends the next free `awgN`.
- Added `--iface <name>` to install or replace a specific pool member.
- The interactive wizard now shows:
  - VPN type selection (AmneziaWG for now);
  - the current tunnel pool;
  - an action choice: create a new tunnel or replace an existing one.
- Configs are stored as `/etc/amnezia/amneziawg/<iface>.conf`.
- Input sanitizing is explicit: `DNS` is removed, empty AWG assignments are removed, and `Table = off` is added.

### Runtime and dashboard know the active tunnel ([#250](https://github.com/sleep3r/mtproto.zig/pull/250))

- `Config` parses `interfaces` and `pinned_interface` alongside the legacy `interface`.
- MiddleProxy metadata refresh no longer assumes one legacy interface; it resolves the active tunnel from table 200 / pool state.
- The dashboard routing card shows the ordered pool, active route, pinned interface, last switch, health reason, and per-interface probe status.
- Dashboard `Set iface` is now a `Pin` action: it writes `pinned_interface`, invokes the pool controller once, and does not restart the proxy.
- `/api/stats.routing` now exposes pool/active/pinned/health state.

### Documentation and UX ([#250](https://github.com/sleep3r/mtproto.zig/pull/250), [#252](https://github.com/sleep3r/mtproto.zig/pull/252))

- Added `README.ru.md`.
- The README language switcher near the top is now easier to spot.
- Docker documentation is more explicit: containers are useful for testing, packaging, and simple runs, but the main production path is a native Linux host managed by `mtbuddy`.
- README now documents the tunnel pool, `pinned_interface`, dashboard monitoring/pinning, and the Telegram calls / non-Premium media limitations without MiddleProxy.
- `config.toml.example` now includes the ordered tunnel pool fields.
- `THREAT_MODEL.md` documents the calls/media limitations.

## Verified

- `python3 -m py_compile src/ctl/dashboard_assets/server.py`
- `node --check src/ctl/dashboard_assets/static/app.js`
- generated tunnel controller script checked with `bash -n`
- `zig build test`
- `zig build -Dtarget=x86_64-linux`
- `git diff --check`

</details>

## Changelog

* feat: add tunnel pool failover and localized docs ([#250](https://github.com/sleep3r/mtproto.zig/pull/250)) ([6eb7dff](https://github.com/sleep3r/mtproto.zig/commit/6eb7dffa92af499793a304c69236a427027386f4))
* docs: highlight README language switcher ([#252](https://github.com/sleep3r/mtproto.zig/pull/252)) ([d0611b3](https://github.com/sleep3r/mtproto.zig/commit/d0611b3140cf343c33f973641a7c2e8402843cb3))
* chore(main): release 0.23.0 ([#251](https://github.com/sleep3r/mtproto.zig/pull/251)) ([beaa437](https://github.com/sleep3r/mtproto.zig/commit/beaa437a22aba91f91d528fa850d66be4b3bfde3))

Related Repositories