# Канонический локальный план VM101

## Автономное восстановление VPN egress HideMyName

## 1. Область действия

Этот локальный план относится только к VM101.

VM100 и VM121:

- не участвуют в реализации этой state machine;
- не являются обязательными критериями PASS локальных этапов;
- могут проверяться позже в отдельных интеграционных планах.

Основная цель VM101 — автономно сохранять интернет у клиентов при последовательной деградации VPN-инфраструктуры.

Приоритеты:

1. Локально восстановить отказавший VPN-слот.
2. При накоплении отказов полностью обновить пул HideMyName.
3. При недоступности HideMyName продолжить работу на оставшихся VPN.
4. При исчерпании кандидатов перераспределять пользователей на оставшиеся рабочие VPN.
5. При отсутствии всех рабочих VPN перевести пользователей в Direct.
6. В режиме Direct продолжать bootstrap-восстановление.
7. После получения рабочего транспорта обновить HideMyName-пул, восстановить пять VPN-слотов и вернуть пользователей на VPN.

---

# 2. Основные сущности

## 2.1. Рабочие VPN-слоты

Рабочий набор:

- `vpn1`
- `vpn2`
- `vpn3`
- `vpn4`
- `vpn5`

Каждый слот содержит один активный предварительно протестированный HideMyName-туннель.

## 2.2. Пул кандидатов

После получения списка HideMyName система:

1. тестирует доступные туннели;
2. формирует локальный упорядоченный пул;
3. назначает первые пять подходящих кандидатов в `vpn1..vpn5`;
4. оставляет остальные как резерв;
5. исключает активные дубликаты;
6. исключает quarantined endpoints текущего поколения.

Пул должен иметь идентификатор поколения.

## 2.3. Quarantine

Когда endpoint активного слота перестаёт работать:

1. endpoint удаляется из рабочего обращения;
2. endpoint помещается в quarantine;
3. endpoint не может повторно использоваться в текущем поколении пула;
4. повторное использование возможно только после:
   - нового получения списка HideMyName;
   - нового тестирования;
   - успешного прохождения теста.

Полное обновление списка не означает автоматического доверия старым endpoints. Рабочими становятся только заново протестированные endpoints.

## 2.4. Счётчик ремонтов

Глобальный счётчик VM101:

`repair_events_since_full_refresh`

Он увеличивается после каждой успешной замены отказавшего активного VPN-туннеля.

Это могут быть:

- отказы пяти разных слотов;
- несколько последовательных отказов одного слота;
- любая комбинация успешных локальных замен.

Счётчик:

- не сбрасывается после единичного ремонта;
- не сбрасывается от временного возвращения системы в рабочее состояние;
- сбрасывается только после успешного полного refresh, тестирования и установки нового рабочего набора.

Параметр:

`FULL_REFRESH_AFTER_REPAIRS`

Начальное значение:

`5`

---

# 3. State machine

## STATE A — NORMAL

Условия:

- пять VPN-слотов работают;
- пользователи распределены по VPN;
- Direct failopen выключен;
- health monitor контролирует каждый слот.

При отказе одного слота:

`NORMAL -> LOCAL_REPAIR`

---

## STATE B — LOCAL_REPAIR

Health monitor обнаруживает отказ и вызывает recovery manager HideMyName.

Recovery manager:

1. подтверждает отказ;
2. помещает текущий endpoint в quarantine;
3. выбирает следующий протестированный резервный endpoint;
4. меняет только отказавший слот;
5. поднимает интерфейс;
6. проверяет фактический интернет через этот интерфейс;
7. при успехе увеличивает `repair_events_since_full_refresh`.

Если счётчик меньше порога:

`LOCAL_REPAIR -> NORMAL`

Если счётчик достиг порога:

`LOCAL_REPAIR -> FULL_POOL_REFRESH`

Если кандидата нет:

`LOCAL_REPAIR -> SLOT_EXHAUSTED`

---

## STATE C — FULL_POOL_REFRESH

После достижения порога система пытается:

1. получить новый список туннелей HideMyName;
2. протестировать полученные туннели;
3. сформировать новое поколение пула;
4. выбрать пять рабочих endpoints;
5. загрузить их в `vpn1..vpn5`;
6. проверить каждый слот;
7. проверить таблицы маршрутизации;
8. проверить фактический egress;
9. активировать новый рабочий набор.

Refresh считается успешным только после фактической проверки нового набора.

После успеха:

- `repair_events_since_full_refresh=0`;
- создаётся новое поколение пула;
- старый quarantine архивируется;
- успешно протестированные старые endpoints снова могут использоваться;
- система возвращается в `NORMAL`.

Переход:

`FULL_POOL_REFRESH -> NORMAL`

Если HideMyName недоступен или кандидатов недостаточно:

`FULL_POOL_REFRESH -> DEGRADED_POOL`

Неуспешный refresh не должен уничтожать оставшиеся рабочие VPN.

---

## STATE D — DEGRADED_POOL

HideMyName временно недоступен, но один или несколько VPN ещё работают.

Система:

1. сохраняет оставшиеся рабочие VPN;
2. продолжает обслуживать пользователей;
3. продолжает заменять отказавшие слоты из последнего протестированного пула;
4. соблюдает quarantine;
5. периодически повторяет full refresh;
6. не сбрасывает repair counter;
7. не считает систему полностью восстановленной.

Параметр:

`HMN_REFRESH_RETRY_INTERVAL_SEC`

Если refresh удался:

`DEGRADED_POOL -> NORMAL`

Если свободного кандидата для очередного слота нет:

`DEGRADED_POOL -> SLOT_EXHAUSTED`

---

## STATE E — SLOT_EXHAUSTED / CONSOLIDATION

Если слот отказал, а рабочего резервного кандидата нет:

1. слот объявляется недоступным;
2. пользователи слота перераспределяются по оставшимся рабочим VPN;
3. балансировка использует только подтверждённо рабочие слоты;
4. система продолжает попытки full refresh;
5. оставшиеся слоты продолжают ремонтироваться, пока существуют кандидаты.

Варианты:

- четыре рабочих слота — пользователи распределяются по четырём;
- два рабочих слота — пользователи распределяются по двум;
- один рабочий слот — все VPN-пользователи временно работают через него;
- ноль рабочих слотов — переход в Direct.

Уменьшение числа слотов само по себе не включает Direct.

Если появляется новый рабочий кандидат:

`SLOT_EXHAUSTED -> DEGRADED_POOL`

или:

`SLOT_EXHAUSTED -> NORMAL`

Если рабочие слоты закончились:

`SLOT_EXHAUSTED -> DIRECT_EMERGENCY`

---

## STATE F — DIRECT_EMERGENCY

Условие входа:

`healthy_vpn_slot_count=0`

Действия:

1. все пользователи переводятся в Direct;
2. фиксируется аварийное состояние;
3. Direct становится временным клиентским egress;
4. последний пул не удаляется;
5. quarantine не удаляется;
6. recovery manager продолжает работу;
7. запускается bootstrap recovery manager.

Direct — не восстановление VPN. Это последний способ сохранить клиентам интернет.

Переход:

`DIRECT_EMERGENCY -> BOOTSTRAP_RECOVERY`

---

# 4. Bootstrap recovery manager

## 4.1. Общий контракт

`bootstrap_recovery_manager` — абстрактный компонент.

Его задача:

> Получить любой рабочий транспорт, через который можно обратиться к HideMyName или получить новый рабочий VPN-пул.

Основная state machine не должна зависеть от конкретной bootstrap-стратегии.

Стратегии выполняются в настраиваемом порядке:

`BOOTSTRAP_STRATEGY_ORDER`

---

## 4.2. Стратегия по умолчанию: cached pool через vpn1

Название:

`cached_pool_vpn1_strategy`

Алгоритм:

1. Клиентский трафик остаётся в Direct.
2. `vpn1` используется как технический bootstrap-слот.
3. В `vpn1` по очереди загружаются кандидаты из последнего сохранённого пула.
4. После каждого кандидата проверяется VPN egress.
5. Неуспешный endpoint помечается как bootstrap-failed или остаётся quarantined.
6. Через настраиваемый интервал пробуется следующий кандидат.
7. Перебор продолжается по заданной политике.

Параметры:

- `BOOTSTRAP_SLOT=vpn1`
- `BOOTSTRAP_RETRY_INTERVAL_SEC`
- `BOOTSTRAP_CANDIDATE_RETRY_COUNT`

Bootstrap-туннель не должен автоматически принимать клиентский трафик.

Его первая задача — дать управляющий транспорт для обращения к HideMyName.

---

## 4.3. Альтернативная стратегия: WireGuard через Деденево

Название:

`ddn_wireguard_bootstrap_strategy`

Алгоритм:

1. VM101 поднимает резервный WireGuard до Деденево.
2. Проверяет наличие интернета через Деденево.
3. При успехе запрос HideMyName выполняется через этот транспорт.
4. Новый пул тестируется.
5. Восстанавливаются `vpn1..vpn5`.
6. После возврата штатного VPN резервный транспорт отключается или остаётся в standby.

В дальнейшем могут быть добавлены другие стратегии без переписывания основной state machine.

---

# 5. Возврат из Direct

Когда bootstrap-стратегия получила рабочий транспорт:

1. через него выполняется запрос нового списка HideMyName;
2. тестируются кандидаты;
3. создаётся новое поколение пула;
4. пять рабочих endpoints назначаются в `vpn1..vpn5`;
5. проверяется каждый интерфейс;
6. проверяется policy routing;
7. проверяется фактический egress;
8. проверяется отсутствие quarantined endpoints в активном наборе.

Только после успешной проверки штатного набора:

- пользователи переводятся с Direct обратно на VPN;
- Direct failopen выключается;
- repair counter сбрасывается;
- bootstrap-состояние очищается;
- система возвращается в `NORMAL`.

Поднятие одного bootstrap-туннеля не означает завершение восстановления.

---

# 6. Настраиваемые параметры

Минимальный набор:

- `HEALTH_CHECK_INTERVAL_SEC`
- `HEALTH_FAILURES_BEFORE_DOWN`
- `LOCAL_REPAIR_RETEST_COUNT`
- `FULL_REFRESH_AFTER_REPAIRS`
- `HMN_REFRESH_RETRY_INTERVAL_SEC`
- `BOOTSTRAP_RETRY_INTERVAL_SEC`
- `BOOTSTRAP_CANDIDATE_RETRY_COUNT`
- `BOOTSTRAP_SLOT`
- `BOOTSTRAP_STRATEGY_ORDER`
- `CANDIDATE_TEST_TIMEOUT_SEC`
- `CANDIDATE_TEST_RETRIES`
- `MIN_HEALTHY_SLOTS_TO_EXIT_DIRECT`
- `DIRECT_FAILOPEN_ENABLED`
- `EMERGENCY_COMMIT_ENABLED`

Начальные значения архитектуры:

- `BOOTSTRAP_SLOT=vpn1`
- `FULL_REFRESH_AFTER_REPAIRS=5`
- вход в Direct при `healthy_vpn_slot_count=0`;
- выход из Direct после восстановления и проверки штатного рабочего набора.

---

# 7. Safety-инварианты

1. Отказ одного слота не останавливает остальные.
2. Quarantined endpoint не возвращается без нового тестирования.
3. Неуспешный full refresh не уничтожает рабочие VPN.
4. Direct включается только при отсутствии рабочих VPN.
5. Direct не останавливает recovery.
6. Bootstrap-слот не принимает клиентский трафик без отдельного решения.
7. Возврат с Direct выполняется только после проверки нового рабочего набора.
8. Изменение VPN-конфигурации имеет backup и rollback.
9. Пороги и интервалы настраиваются.
10. Provider-specific HideMyName-код отделён от общей state machine.
11. Bootstrap-стратегии реализуются через общий контракт.
12. Локальный план ограничен VM101.
13. VM100 и VM121 не являются блокирующими критериями локальных STEP.
14. Repair counter сбрасывается только после успешного full refresh.
15. Старый пул сохраняется до доказанного запуска нового пула.

---

# 8. Полное испытание

После реализации выполняется последовательная имитация на VM101:

1. Отказ одного VPN.
2. Local repair.
3. Quarantine.
4. Повторные отказы до порога.
5. Успешный full HideMyName refresh.
6. Новая серия отказов.
7. Недоступность HideMyName.
8. Работа на остаточном пуле.
9. Исчерпание резервных кандидатов.
10. Консолидация пользователей на оставшихся слотах.
11. Последовательное уничтожение всех рабочих VPN.
12. Переход пользователей в Direct.
13. Cached-pool bootstrap через `vpn1`.
14. Проверка альтернативного bootstrap transport.
15. Получение нового HideMyName-пула.
16. Восстановление всех пяти VPN.
17. Возврат пользователей с Direct на VPN.
18. Проверка перезагрузочной устойчивости state machine.
