Защищённый доступ

Modem Guardian

Неверный логин или пароль

M Прокси-ферма Modem Guardian
Обновление...
Прокси
Инциденты 24 часа0
Доступность 24 часа100%
Инциденты 30 дней0
Доступность 30 дней100%
Белые списки сейчас Не обнаружены
За 24 часа 0 мин · 0% 0 включений
За 7 дней 0 мин · 0% 0 включений
За 30 дней 0 мин · 0% 0 включений

Локации

Отказы, простой и режим белых списков считаются независимо для каждой ноды.

Модемы

Нет данных от агентов

Данные ещё не поступили

Служебные данные модемов

Параметры оборудования и признаки аномалий

Подходящих устройств нет

Последние инциденты

0 записей

Ноды

Агенты сами подключаются к контроллеру; входящие порты на нодах не требуются.

Установка агента

Установщик и пакет текущей клиентской части хранятся на этом сервере.

Установщик Пакет агента
Сначала создайте одноразовый токен добавления ноды.

Только администратор

Активация cross-location handoff

Панель только показывает серверные квалификации и не запускает переносы автоматически.

UNKNOWN: данные ещё не загружены.

Изменения недоступны, пока серверный статус неизвестен.

Ручной режим и точный scope

Автоматический режим отображается отдельно и не меняется этой формой.

Автоматический режим: UNKNOWN

Пустой список запрещает новые ручные handoff.

Двусторонняя квалификация

Для возврата требуются обе направленные квалификации пары.

Серверные блокеры

Готовность к active определяется только полем сервера ready_for_active.

Содержание Назначение Архитектура Обнаружение модемов Проверка здоровья Локации и белые списки Статусы Инциденты Автовосстановление Ротация IP Состояния ротации Драйверы модемов Производительность Участие в проде Соединения SMS Данные и статистика Менеджер прокси Безопасность Эксплуатация Файлы и каталоги Разбор проблем Расширение системы История изменений

Внутренняя документация

Modem Guardian: устройство и эксплуатация

Версия документа: 0.18.8 · обновлено 17.09.2026

При изменении поведения системы этот раздел обновляется в том же релизе. Внизу обязательно добавляется дата, версия и краткое описание изменения.

Показаны все разделы
Есть проблема сейчасСтатусы, симптомы и быстрый разбор ВосстановлениеКаскад действий и ограничения IP и белые спискиРотация и ограничения операторов Прокси и клиентыВыдачи, соединения и ёмкость Оборудование и SMSМодели, драйверы и данные SIM ЭксплуатацияБезопасность, данные и восстановление системы

1. Назначение и границы ответственности

Modem Guardian обнаруживает локальные модемные маршруты на серверах фермы, проверяет каждый маршрут независимо, хранит историю, типирует отказы, выполняет ограниченное автоматическое восстановление и управляет плановой ротацией публичных IP.

Система не является прокси-сервером и не участвует в передаче клиентского трафика. Остановка ModGuard не останавливает 3proxy. Она только прекращает мониторинг, автоматическое восстановление и команды ротации.

Kraken не используется как источник истины о модели, операторе, IP, здоровье или способе управления модемом. ModGuard читает фактическую локальную сеть, конфигурации 3proxy и API оборудования. Это позволяет в будущем заменить Kraken без переписывания модели данных и панели.

Как устроена эта Wiki

Документацию можно читать последовательно по оглавлению или как гипертекст. Карта знаний ведёт от рабочей задачи к нужной теме, блок «Связанные темы» продолжает диагностический маршрут, а «Сюда ссылаются» показывает обратные связи. Новая функция считается документированной только когда описаны её назначение, входные данные, решение, побочные эффекты, журналирование, восстановление после ошибки и связи с соседними механизмами.

2. Архитектура

Controller

Центральный controller работает на VDS, слушает только loopback-порт 65321 и публикуется через nginx по HTTPS. Он хранит SQLite, принимает снимки агентов, планирует команды, ведёт пользователей, инциденты, аудит и отдаёт веб-интерфейс.

Controller является единственным владельцем решений, которые могут изменить клиентский трафик или состояние оборудования: recovery, страховочная ротация, временная пересадка, failback и управляемый restart 3proxy проходят через один persistent command plane и общий per-modem арбитраж. Диагностика связи, возраст IP и watchdog клиентского endpoint остаются независимыми источниками сигналов, но не независимыми исполнителями. Agent не выбирает разрушительное действие самостоятельно: он сообщает диагноз и исполняет только принятую controller команду.

Agent

На каждом Kraken работает root-agent. Root нужен для чтения локальных интерфейсов, привязки запросов к конкретному адресу и прямого управления роутерами через проверенный API, SSH или CLI. Агент сам устанавливает исходящее HTTPS-соединение с controller; входящий порт на Kraken не требуется.

Agent сохраняет recovery state, события и результаты команд в локальном SQLite spool. Потеря связи с VDS не приводит к самостоятельному reboot: диагностика продолжается, а запрос действия будет доставлен после восстановления controller. Непосредственно перед reboot agent заново читает живые 3proxy-конфиги и отклоняет команду, если на source остался хотя бы один клиентский профиль.

Два независимых цикла агента

  • Диагностический цикл строит полный снимок всех модемов. Он тяжёлый: проверяет маршруты, роутеры, API, интернет, прокси-профили и периодически идентификацию оборудования.
  • Командный цикл каждые 5 секунд обменивается результатами и командами recovery, ротации, speed test и 3proxy. Он не ждёт завершения полного снимка парка.

Разделение сделано потому, что полный снимок 60–300 модемов может занимать минуты, а команда ротации должна быть выдана за секунды.

Поток данных

  1. Agent находит локальные интерфейсы и 3proxy-конфигурации.
  2. Agent выполняет проверки строго через адрес конкретного модема.
  3. Controller принимает снимок и обновляет текущее состояние.
  4. Controller сводит recovery, ротацию, speed test, обслуживание 3proxy и proxy failover в единый план действий.
  5. Быстрый командный цикл получает пачку команд.
  6. Agent параллельно выполняет команды разных модемов, но сериализует действия одного ресурса и самостоятельно проверяет фактический результат.
  7. Результат немедленно возвращается controller; полный снимок позднее подтверждает состояние.

3. Обнаружение модемов и прокси

Основной источник инвентаря — локальные файлы /etc/3proxy/modem-id-*.cfg. Из них извлекаются modem ID, исходящий адрес, входящие прокси-порты, пользователи, maxconn и принадлежность профиля клиенту или служебному доступу.

Фактическая принадлежность 3proxy-профиля определяется по исходящему адресу -e, а не только по номеру в имени файла. Это важно после перенумерации в Kraken: например, modem-id-129.cfg с адресом 192.168.223.2 относится к локальному port-223. Логины и пароли профилей в controller не передаются.

Наличие интерфейса и адреса подтверждается через Linux, а не через Kraken API. Для схемы с VLAN имя интерфейса и адрес должны относиться к одной виртуальной линии. Наличие базового физического интерфейса не скрывает отсутствие требуемого VLAN.

Удалённый из локального инвентаря порт помечается неактивным, перестаёт отображаться в рабочем парке, а незавершённый инцидент закрывается как исключённый из статистики.

Идентификация оборудования

  • Huawei HiLink: API устройства, при необходимости через SSH роутера.
  • Fibocom L860: AT-команды и UCI на GoldenOrb/ROOter.
  • Fibocom L860 на Keenetic: RCI и Keenetic CLI; Huawei API для этого драйвера не проверяется.
  • Модель, прошивка, WebUI, IMEI, серийный номер, SIM, телефон и Cell ID сохраняются только из фактического ответа оборудования.
  • Для Huawei аппаратная версия CL2E3372HM нормализуется как E3372h-153.

Полная идентификация выполняется пакетами и обновляется реже сетевого здоровья, чтобы не перегружать слабые роутеры. Последние достоверные аппаратные поля сохраняются при временно неполном ответе API, но оператор и текущий IP не подменяются архивными данными.

4. Как проверяется здоровье

Проверка идёт слоями. Ошибка верхнего слоя не должна маскировать нижний:

  1. Локальный маршрут. Существует ли интерфейс и исходный IPv4.
  2. Роутер. Доступен ли gateway конкретного порта.
  3. Модем. Отвечает ли HiLink API или диагностический интерфейс роутера.
  4. Международный интернет. Несколько IP-чекеров должны вернуть корректный публичный IPv4.
  5. Российское определение IP. Яндекс Интернетометр возвращает публичный IPv4 даже во время операторских белых списков.
  6. Российские контрольные сайты. VK и OK дают независимый сигнал и помогают отличить общий отказ от режима белых списков.
  7. Заглушка оператора. Отдельный HTTP-запрос ищет известный редирект или сигнатуру страницы оплаты.

IP-чекер ModGuard использует nonce: ответ должен содержать одноразовое значение запроса. Яндекс Интернетометр также вызывается с уникальным query-параметром, чтобы промежуточный кэш не подменил фактический адрес. Один исправный внешний IP-чекер подтверждает полный интернет. Если внешние чекеры закрыты, но Яндекс возвращает IPv4 или есть кворум российских сайтов, система фиксирует белые списки.

5. Локации и статистика белых списков

Сигнал отдельного модема

Модем получает состояние SELECTIVE_ACCESS_ACTIVE, когда обычные внешние проверки ограничены, но российская связность подтверждена и, по возможности, Яндекс Интернетометр вернул публичный IPv4. Для модема хранится отдельный интервал с началом, последней проверкой, окончанием и последним IP.

Когда режим считается включённым на локации

Локация соответствует agent-ноду, например kraken-1 или kraken-2. Один нестабильный модем не должен объявлять ограничение всей локации. Поэтому controller открывает локационный период, когда режим подтверждён минимум на 20% обнаруженных рабочих модемов, но не менее чем на двух устройствах. Для маленькой ноды из одного или двух модемов требуется подтверждение всех доступных устройств.

Порог пересчитывается по фактическим локальным данным: учитываются только мониторируемые, не находящиеся в карантине и физически обнаруженные модемы. Kraken не сообщает controller, включены ли белые списки.

Текущее состояние и устаревшие данные

Индикатор «Работают» показывается только при свежем снимке agent не старше 10 минут. Если нода перестала присылать данные, панель показывает «Нет свежих данных», а статистика не продолжает бесконечно начислять время белых списков после последней подтверждённой проверки.

Как считаются сутки, неделя и месяц

  • Длительность — сумма объединённых частей интервалов внутри скользящих окон 24 часа, 7 и 30 дней.
  • Процент — длительность режима, делённая на полную продолжительность окна: 24 часа, 168 часов или 720 часов. Поэтому пять часов белых списков равны 20,83% суток, даже если ModGuard установлен недавно. Время до установки является неизвестным, а не подтверждённым периодом белых списков.
  • Включения — число новых периодов, начавшихся внутри окна. Период, начавшийся раньше окна и продолжающийся внутри него, добавляет длительность, но не новое включение.
  • Глобальная длительность — объединение локационных интервалов. Одновременный час на двух локациях считается одним глобальным часом, а в строках локаций остаётся по одному часу на каждой.

На главной странице видны глобальный индикатор и строки каждой локации с текущим состоянием, числом затронутых модемов, длительностью, процентом и включениями. В истории конкретного модема показываются те же показатели за 24 часа, 7 и 30 дней.

Белые списки не открывают сетевой инцидент, не начисляют простой и не запускают каскад восстановления. Переходы по модемам пишутся как SELECTIVE_ACCESS_STARTED/ENDED, а по локациям — как LOCATION_SELECTIVE_ACCESS_STARTED/ENDED.

6. Статусы парка

СтатусЧто подтвержденоЧто делает система
РаботаетЧерез модем получен корректный публичный IPv4.Продолжает мониторинг и плановую ротацию.
Работает: белые спискиВнешние сервисы закрыты, но российская связность подтверждена; по возможности IPv4 получен через Яндекс Интернетометр.Не открывает инцидент, не начисляет простой, продолжает ротацию и записывает отдельный период.
ПроверяемПорт найден, но данных ещё недостаточно.Не выполняет разрушительных действий.
ВосстанавливаемОтказ подтверждён или связь уже вернулась, но проверка стабильности не завершена.Продолжает один инцидент и его каскад действий.
Требует вниманияАвтоматический каскад исчерпан либо отсутствует проверенный способ управления.Сохраняет диагностику и ждёт оператора.
Оператор ограничил интернетПолучен подтверждённый редирект на служебную страницу оператора; баланс по этому сигналу неизвестен.После grace-периода перезагружает разрешённый модем и повторяет reboot раз в 30 минут, пока интернет не появится.
КарантинПорт вручную исключён из production либо автоматически выведен после длительного подтверждённого отказа и эвакуации клиентов.Не предлагает модем для новых клиентов, исключает последующее время карантина из KPI и продолжает мониторинг. Правило возврата зависит от причины карантина.

Текст под статусом — краткий диагноз. Наведение показывает расширенное объяснение: какой слой проверен, что не подтвердилось и что проверять физически.

Показатели «Работает», «Проверяем», «Восстанавливаем», «Требует внимания», «Оператор ограничил интернет», «Карантин» и «Белые списки» являются фильтрами. Нажатие прокручивает страницу к таблице и оставляет только соответствующие модемы; повторное нажатие снимает фильтр.

В верхней статистике показывается доступность за 24 часа и 30 дней. Суммарные модемо-часы простоя там не выводятся: при большом парке это число выглядит как длительность единого отказа и плохо подходит для оперативной оценки. Простой конкретного модема остаётся в его строке, истории и деталях инцидентов.

7. Инциденты и расчёт простоя

Краткий отвал во время штатной смены IP не считается инцидентом. Первый сбой создаёт состояние подозрения. Инцидент открывается только после непрерывного отсутствия связи не менее 300 секунд.

Режим белых списков не является инцидентом и не входит в простой. Для каждого порта controller отдельно хранит начало, последнюю подтверждённую проверку и окончание периода в selective_access_periods; те же переходы пишутся в audit log как SELECTIVE_ACCESS_STARTED и SELECTIVE_ACCESS_ENDED.

Счётчик «периодов» означает число отдельных непрерывных интервалов, начавшихся внутри выбранного окна. Это не количество выполненных проверок. Например, «12 периодов» означает двенадцать подтверждённых переходов из обычной сети в режим белых списков.

Reconnect, reboot модема, reboot роутера и последующая проверка являются этапами одного инцидента. Они не увеличивают счётчик сбоев отдельно.

Подтверждённый простой хранится сегментами. Когда интернет впервые вернулся, активный сегмент останавливается, даже если система ещё выполняет три проверки стабильности. Если проверка снова упала, открывается новый сегмент того же инцидента. Пересекающиеся сегменты не суммируются дважды.

Расчёт ограничивается временем фактического мониторинга модема. Поэтому модем, наблюдаемый меньше суток, не может получить несколько суток простоя из старой записи.

8. Автоматическое восстановление

Восстановление и плановая ротация являются разными причинами запуска одного управляющего контура. Восстановление начинается из-за отсутствия связи; ротация — на здоровом модеме ради нового IP. После появления сигнала дальнейшие destructive-действия, защита клиентских proxy, проверка результата и возврат выполняются одним controller.

Кто принимает решение

Agent диагностирует отказ, выдерживает grace-период и отправляет controller типизированный запрос recover_reconnect, recover_modem_reboot или recover_router_reboot. В production параметр controller_managed_recovery=true запрещает агенту выполнить каскад напрямую. Controller дедуплицирует запрос, отменяет ещё не выданные speed test и ротацию того же модема и создаёт одну команду либо persistent maintenance operation. Если ротация или recovery уже выданы этому modem, новый сигнал присоединяется к существующему владельцу и не создаёт параллельное действие.

Обычный каскад

  1. Ожидание grace-периода 300 секунд.
  2. Reconnect мобильной сессии.
  3. Проверка появления интернета.
  4. Перезагрузка модема.
  5. Повторная проверка.
  6. Перезагрузка роутера.
  7. Проверка результата. Если связь не вернулась, новый каскад продолжается после per-modem cooldown; состояние и история не теряются.

Занятый модем

Reconnect не требует отключения питания и выдаётся через приоритетную controller-команду. Перед reboot модема или роутера controller рассматривает все активные клиентские профили source как одну группу. Планировщик резервирует места на здоровых модемах той же локации, учитывает реальную ёмкость, уже запланированные пересадки и по возможности не выдаёт одному клиенту второй proxy того же modem.

  1. Создаётся persistent maintenance operation с причиной и incident ID.
  2. Для каждого клиентского assignment атомарно резервируется target slot.
  3. Kraken меняет только исполнительную привязку proxy к modem; публичный endpoint, порт, login и password клиента сохраняются.
  4. Controller делает Kraken read-back, при необходимости точечный restart нужного 3proxy config и фактическую data-plane проверку каждого proxy.
  5. Только после успеха всей группы source повторно проверяется на отсутствие клиентов.
  6. Agent ещё раз читает живые локальные 3proxy-конфиги. Ненулевое число профилей является последним жёстким fence и отменяет reboot.
  7. После ротационного reboot клиенты возвращаются на home modem через пять минут после завершения maintenance, свежего здорового снимка и подтверждения нового IP. После recovery reboot применяется десятиминутная проверка стабильности. Неуспешный возврат получает persistent backoff и не образует ping-pong.

Частичная ошибка до reboot вызывает rollback всей изменённой группы: Kraken, назначения ModGuard и NAT возвращаются к исходному состоянию. Нехватка мест даёт BLOCKED_CAPACITY; controller не пытается поселить всех клиентов на один перегруженный modem.

Арбитраж действий

Recovery имеет приоритет над ротацией и speed test того же модема. Пока recovery incident, disruptive-команда или фактически начатая maintenance operation активны, новые тяжёлые тесты не ставятся, ожидающие ещё не выданные тесты отменяются, ротация не планируется, а data-plane watchdog не запускает конкурентную пересадку из-за ожидаемого краткого разрыва. Подавление по maintenance начинается только со стадии RESERVED; PENDING, PLANNED, BLOCKED_CAPACITY и BLOCKED_MAPPING оставляют клиентский endpoint под обычным watchdog, потому что никакого безопасного handoff ещё не произошло. Подавление выданной командой ограничено: PENDING учитывается 120 секунд, DELIVERED — 10 минут, успешное завершение — ещё 90 секунд. После этого клиентский endpoint снова обязан пройти обычную проверку. Точечный restart 3proxy может идти параллельно с действием другого модема, но ждёт команду того же config ID. Fleet restart ждёт завершения всех modem-команд на ноде. Периодический fleet restart выключен по умолчанию и не требуется для обычной работы.

Состояние EVACUATING переживает рестарт controller. Reconciler не повторяет side effect вслепую: он перечитывает фактические assignment, target listener и source. Если вся группа уже пересажена и source свободен, операция продолжается с HANDOFF_VERIFIED. Если хотя бы один target не подтверждён, операция завершается FAILED, capacity reservation освобождается, а reboot не выдаётся. Поэтому перезапуск VDS не может превратить незавершённую эвакуацию в опасную аппаратную команду.

Режим обслуживания active требует непустой MODGUARD_MAINTENANCE_HANDOFF_ALLOWLIST_JSON. Это позволяет сначала проверить один modem, затем расширить разрешение на всю ноду. Операции вне canary-scope видны в журнале, но не получают разрушительную команду. План, сохранённый прежним режимом OBSERVED, никогда не повышается до боевой операции: при активном rollout controller переводит его в терминальный CANCELLED с причиной dry_run_closed_on_active_rollout. Такой старый dry-run также не исключает клиентский proxy из обычного data-plane watchdog и failover.

Для Huawei за нестандартным HiLink gateway первым действием также является строгий десятисекундный разрыв HiLink data-сессии, выполненный через SSH роутера. Agent требует перехода 901 → 902 → 901. Это не считается доказательством снятия регистрации с базовой станции. Если после одной подтверждённой попытки оператор сохранил прежний IP, следующим этапом каскада становится reboot модема.

Agent-side fleet circuit breaker подавляет массовые действия, если одновременно получили UPSTREAM_TIMEOUT минимум десять отслеживаемых модемов либо 25% отслеживаемого парка. Он проверяется и при создании запроса, и непосредственно перед исполнением уже выданной команды. Проверки, история и уже открытые инциденты продолжают обновляться, но reconnect/reboot не выполняются. Отдельный location outage guard одновременно запрещает массовую пересадку proxy. Это защищает от перезагрузки или бессмысленного перемещения всего парка при проблеме VDS, IP-чекера, оператора или общей сети.

Независимые модемы не используют один глобальный action-slot. За один snapshot-cycle агент обслуживает до трёх самых старых готовых действий, но прекращает набор новых действий, если первое уже заняло установленный временной бюджет. Это снимает искусственную очередь из сотен модемов и одновременно не позволяет медленному роутеру растянуть один проход на неопределённое время. Состояние и rate limit по-прежнему хранятся отдельно для каждого modem ID.

Для обычного отказа modem reboot разрешён не чаще одного раза в 15 минут и не более четырёх раз за скользящий час на modem ID. Router reboot не имеет отдельного редкого лимита: он выполняется, когда предыдущий modem reboot не восстановил связь. Новый каскад всё равно начинается только после освобождения modem-reboot cooldown, поэтому router reboot не может образовать бесконечный быстрый цикл. Ограничение оператора живёт отдельно и разрешает только один modem reboot в 30 минут.

Оператор ограничил интернет

Положительный HTTP-редирект на известную служебную страницу оператора является отдельным состоянием OPERATOR_CAPTIVE_PORTAL. Оно не означает, что ModGuard прочитал баланс: причиной могут быть закончившийся пакет, тарифное ограничение, задержка биллинга или зависшая абонентская сессия.

После непрерывного подтверждения в течение 300 секунд открывается один инцидент. Если отдельная политика captive_portal_reboot_enabled включена, modem ID разрешён её allowlist и драйвер умеет перезагружать модем, ModGuard выполняет reboot только модема. Если ограничение остаётся, reboot повторяется через captive_portal_reboot_interval, в production это 1800 секунд. Роутер по этой причине не перезагружается.

Каждая попытка записывается шагом того же инцидента и в локальную таблицу recovery actions. После первого успешного выхода в интернет начисление простоя останавливается, затем три последовательные проверки закрывают инцидент. Отдельный acknowledgement captive-portal-reboot-v1 и независимый allowlist позволяют включить правило для всего парка, не расширяя обычный каскад восстановления.

9. Плановая и страховочная ротация IP

Условия допуска

Команда создаётся только если agent включён, порт мониторится, не находится в резерве, модем обнаружен, состояние HEALTHY, драйвер поддерживает ротацию, известны текущий IP и время его получения, а по модему нет открытого инцидента. HEALTHY включает подтверждённый режим белых списков: в нём текущий и новый IP проверяются через Яндекс Интернетометр. Положение переключателя плановой ротации не отключает страховочный контроль.

Когда создаётся команда

  • Плановая ротация: переключатель не равен «Выкл.» и возраст IP достиг индивидуального периода из отдельного столбца «Плановая ротация».
  • Страховка: подтверждённый IP не менялся 900 секунд, то есть 15 минут. Окно даёт watchcat с таймером 600 секунд время на свою проверку, reconnect, восстановление связи и несколько свежих снимков ModGuard. Страховка действует и при положении «Выкл.».
  • Дубликат: одинаковый публичный IP подтверждён на двух или более здоровых модемах не одним снимком, а минимум тремя наблюдениями на протяжении не менее 60 секунд. Ротируется тот, который получил адрес позже; более старое назначение сохраняется.

Приоритет очереди: подтверждённые дубликаты IP, затем 15-минутная страховка, затем обычное пользовательское расписание. Внутри одного уровня первыми обслуживаются самые старые IP.

Что означает «Выкл.»

«Выкл.» отключает только собственный таймер ModGuard. Это безопасный режим по умолчанию для текущих и новых модемов, пока ротация настроена на оборудовании или в Kraken. Наблюдение за возрастом IP, 15-минутная страховка, устранение подтверждённых дубликатов, проверка результата и повторы остаются включёнными.

Штатные таймеры оборудования пока являются внешними источниками смены IP, но не источниками диагноза и не владельцами recovery. Controller принимает только фактический новый IP из независимой проверки. Полный переход к одному расписанию выполняется по моделям: после canary внешняя ротация отключается, а период переносится в ModGuard. До этого 15-минутная страховка вмешивается только после подтверждённой просрочки и не опирается на флаг Kraken. Непосредственно перед любым разрывом сессии agent делает одноразовую свежую IP-проверку. Если адрес уже новый, reconnect или reboot отменяется без даунтайма.

Жизненный цикл команды

  1. PENDING — controller создал команду.
  2. DELIVERED — агент получил её через быстрый канал.
  3. COMPLETED — агент вернул проверенный результат.

Результат успешен только если агент получил новый IPv4 или увидел, что IP уже изменился до исполнения команды. Ответ «команда принята» сам по себе успехом не является.

Строгий HiLink reconnect технически доступен для recovery-диагностики: агент вызывает /api/dialup/dial с Action=0, требует фактический статус 902 (disconnected), выдерживает 10 полных секунд, вызывает Action=1 и требует возврат 901 (connected). mobile-dataswitch не используется: dataswitch=0 подтверждает лишь запрет передачи данных. Даже статус 902 не доказывает снятие регистрации с базовой станции.

Контрольный production-эксперимент 03.08.2026 на свободных Huawei обеих локаций подтвердил это ограничение. Строгие переходы 901 → 903 → 902 → 901 с выдержками 5, 10, 15 и 30 секунд ни в одном из пяти измерений не изменили публичный IP. Новые TCP-соединения снова начинали проходить через 1–3,4 секунды, хотя API продолжал показывать 902 до команды включения. Ещё три прошивки вместо 902 проходили 901 → 903 → 900 → 901 и самостоятельно поднимали сессию примерно через пять секунд, поэтому заданную выдержку невозможно было применить. Следовательно, увеличение HiLink hold не является надёжным radio/PDP detach и не используется как production-замена reboot для ротации IP.

Production-ротация Huawei не использует reconnect как первую ступень. Аудит 105 поднятых Huawei-профилей на обеих нодах не нашёл отдельной Watchcat-опции mobile reconnect: штатная цепочка везде ping → router reboot. Живое наблюдение также показало, что большинство строгих reconnect возвращает прежний IP и создаёт лишний клиентский разрыв. Поэтому занятый Huawei сначала проходит same-location maintenance-эвакуацию, проверку listener и data plane, и только затем reboot. Свободный Huawei может сразу получить reboot. За один цикл создаётся не более одной эвакуации на локацию.

Если оператор вернул тот же IP

  1. Агент завершает попытку с ошибкой did not change.
  2. Controller сохраняет ошибку, результат IP и увеличивает счётчики.
  3. Для Huawei production-ротация сразу использует контролируемый reboot; повторять недоказанный reconnect перед ним система не будет.
  4. Независимый предел Huawei — не более четырёх reboot-ротаций за скользящий час на один modem ID и не чаще одного reboot в 15 минут. После достижения предела ModGuard продолжает наблюдение и создаст следующую команду только после освобождения обоих окон.
  5. Для Fibocom повторяется проверенный AT-reconnect.
  6. Жёсткий предел — 10 любых попыток за скользящий час на один modem ID. В лимит входят успешные, неуспешные, ожидающие и уже выданные команды; поэтому успешный reboot тоже расходует лимит и не может образовать бесконечный цикл.
  7. Controller держит не более восьми ожидающих modem-команд на одну ноду. Это ограничивает волну массовых действий; свободные worker агента определяют, сколько команд действительно выдаётся в работу.

Успешная команда немедленно обновляет IP и время смены в controller, не ожидая тяжёлого снимка. Если IP изменился штатной автоматикой модема или другой системой, следующий фактический снимок также обновляет время и удаляет устаревшую ошибку предыдущей попытки.

Persistent-очередь нужна для гарантированной доставки после перезапуска controller, агента или сети, но не является последовательной очередью исполнения. Агент сообщает controller число свободных regular workers и получает ровно столько команд. До восьми разных modem выполняются параллельно; второй modem не ждёт завершения первого. Перед ротацией агент повторно сверяет фактический текущий IP: если адрес уже изменился, устаревшая команда завершается без reconnect. Recovery того же modem отменяет ротацию и speed test. Точечный restart 3proxy имеет срочную полосу, но сериализуется с командой того же config ID; fleet restart не накладывается на modem-команды.

Планировщик не создаёт новые страховочные ротации, пока в активном maintenance-scope есть хотя бы одна исполняемая операция эвакуации, reboot или проверки результата. Это backpressure не отменяет уже выданные команды и не тормозит аварийное восстановление клиентских proxy; оно лишь не даёт потоку новых плановых разрывов обгонять обслуживание уже начатых. Заблокированные по нехватке ёмкости или mapping записи, а также операции вне central allowlist, ротацию всего парка не останавливают.

Точечный reload сначала поднимает временный bridge-listener, затем перечитывает Supervisor config без разрыва приёма. Если после прежних reload остались несколько старых 3proxy-процессов одного config, они прекращают принимать новые соединения одним пакетным переключением, а не последовательно. Процессы с действующими клиентскими соединениями остаются только на drain и очищаются фоновым reconciler; актуальный listener уже обслуживает все новые подключения. Поэтому число исторических дублей не умножает время срочного restart.

История и показатели качества ротации

Срабатывание страховки, повтор из-за одинакового IP и reboot для получения нового адреса являются нештатными эксплуатационными событиями, но не сетевыми инцидентами. Они хранятся в agent_commands и показываются отдельной хронологией в истории модема: причина, действие, ожидаемый IP, результат и подробность ошибки.

Фактические смены IP хранятся отдельно в public_ip_changes. Источник observed означает внешнюю смену, найденную снимком: watchcat, Kraken или автоматика модема. Источник modguard означает новый адрес, подтверждённый результатом команды ModGuard. Логика не привязана к номеру порта или внешней системе.

В колонке «Внешний IP» за последние 24 часа показываются число подтверждённых переходов и число различных новых адресов. Разница между ними выявляет частый возврат к одному пулу адресов. Рядом выводится предупреждение, если ротация требует заметного участия страховки.

Оценка за 24 часаДинамическое правилоЧто означает
НормаПоказатели не превышают текущий p90 парка и не достигают абсолютных защитных порогов.Поведение находится внутри фактического диапазона большинства работающих модемов.
ПредупреждениеХотя бы один показатель строго выше динамического p90 либо достигнут защитный порог предупреждения.Жёлтая пометка выделяет относительного аутсайдера даже после общего улучшения парка.
Критическая аномалияХотя бы один показатель строго выше динамического p95 либо достигнут абсолютный критический порог.Красная пометка показывает крайний выброс или опасное значение независимо от состояния остальных модемов.

p90 и p95 пересчитываются при каждом запросе дашборда по скользящему окну 24 часа. Для количества страховок, reboot и возвратов того же IP учитываются модемы хотя бы с одной попыткой. Процент неудач сравнивается после 10 попыток, а эффективность reboot после пяти reboot. Значение должно быть строго выше перцентиля, поэтому группа с одинаковым пограничным результатом не помечается целиком.

Для динамического расчёта требуется минимум 20 подходящих модемов. При меньшей выборке используются резервные пороги, откалиброванные 24.07.2026. Независимо от динамики действуют абсолютные границы: предупреждение при 18 страховках, 10 reboot, 20 возвратах того же IP или 75% неудач; критический уровень при 60 страховках, 14 reboot, 32 возвратах того же IP или 85% неудач. Неэффективность reboot оценивается отдельно. Так улучшение парка постоянно открывает новых аутсайдеров, а массовое ухудшение не становится новой нормой.

Пороги относятся к действиям за скользящие 24 часа. Обычная плановая ротация сама по себе не ухудшает оценку; ухудшают её просрочка, повторяющийся адрес, неудачи и чрезмерный либо неэффективный переход к reboot. Счётчики автоматически уменьшаются, когда старые события выходят из суточного окна.

Переход к полному управлению из ModGuard

Пока идёт пилот, ModGuard принимает как факт и внешнюю смену IP, сделанную watchcat или Kraken. Но два независимых таймера могут одновременно дёрнуть один модем, исказить простой и вернуть старый адрес. Поэтому после проверки конкретной модели внешнюю ротацию для неё нужно отключить и оставить ModGuard единственным владельцем расписания. Источником решения Kraken при этом не является.

Почему может быть больше заданного периода

Период — момент постановки задачи, а не обещание мгновенного нового адреса. К нему добавляются до 5 секунд командного опроса, время reconnect и проверка IP. При возврате того же IP добавляются cooldown и повтор. Состояние всегда видно в подсказке.

10. Как читать колонки ротации

Колонка «Внешний IP» отвечает только за фактическое наблюдение: текущий адрес, время подтверждённой смены, дубликаты, очередь, результат последней попытки и состояние страховки. Управление вынесено в соседнюю колонку «Плановая ротация».

Короткий текстЗначение
Срок ротации наступилПланировщик увидит модем на ближайшем быстром опросе.
Страховка: IP не менялся более 13 минВнешняя штатная ротация просрочена; ModGuard перед своей командой ещё раз проверит текущий IP.
Выполняется смена IPКоманда PENDING или DELIVERED; подробности показывают этап и стратегию.
IP не сменился: оператор вернул тот же IPПопытка завершена корректно, но результат неприемлем. Будет retry или reboot.
Выполняется перезагрузка модемаДве попытки Huawei не дали новый IP, включён следующий этап.
Одинаковый IP используется на нескольких модемахАдрес одновременно подтверждён на двух или более модемах. Будет ротирован тот модем, который получил его позже.
Оператор вернул тот же IPЭто другая ситуация: reconnect одного модема завершился тем же адресом, который был до команды.
Частые проблемы при смене IPЗа 24 часа модем чаще большинства парка требовал страховку, reboot или получал неудачный результат. Это не означает, что штатных смен IP было слишком много.
Смена IP часто не срабатываетДостигнут красный динамический порог качества. В подсказке указано, какая именно проблема выбивается из нормы парка.

Порог аномалии динамический: ModGuard сравнивает модем с распределением показателей всего активного парка за скользящие 24 часа и использует p90/p95 при достаточной выборке. Поэтому предупреждение означает «этот модем заметно хуже соседей по числу проблемных действий», а не «он слишком часто и успешно меняет IP». Абсолютные защитные границы не дают полностью проблемному парку превратить массовую неисправность в новую норму.

В спокойном состоянии колонка «Внешний IP» не дублирует «Плановая ротация выключена»: положение переключателя видно в соседней колонке. Под IP остаются только фактический возраст, число смен и уникальных адресов за сутки. Короткая предупреждающая строка появляется лишь при очереди, ошибке, дубликате, просрочке или суточной аномалии.

Наведение на короткий текст показывает причину команды, PENDING/DELIVERED, текущую стратегию, число ошибок подряд и за час, следующий шаг и защитный лимит. Подчёркивается только сам текст, а ширина колонок зафиксирована, поэтому появление другой подсказки не перестраивает таблицу.

Пример: «IP сменился 3 минуты назад» вместе со старой ошибкой больше не должен отображаться. Если фактический IP изменился после неудачной команды, ошибка очищается. Если ошибка остаётся, значит после последней подтверждённой смены уже выполнялась новая попытка, например из-за дубликата IP.

В истории команды поле «IP до команды» — адрес, который требовалось заменить. Это не прогноз нового адреса: оператор заранее его не сообщает. «IP после проверки» — фактически подтверждённый результат.

11. Драйверы и конкретные модели

Huawei E3372h-153, обычный HiLink

Источник маршрута известен на Kraken, API доступен обычно по 192.168.8.1. Ротация использует один строгий HiLink data-session reconnect через /api/dialup/dial: agent подтверждает 901 → 902, отсчитывает полные 10 секунд, включает соединение и подтверждает возврат 901. mobile-dataswitch для смены IP не применяется. Переход 902 означает отключённую data-сессию, но не доказывает снятие регистрации с базовой станции. Если новый IP не получен, следующая попытка выполняется reboot, а не повтором того же reconnect.

Huawei за роутером с нестандартным адресом

Agent входит на роутер, получает фактический default gateway модема и работает с API по найденному адресу. Адрес и порт не зашиты в код. На port-216 таким способом найден 192.168.43.1 и подтверждён reboot без перезагрузки TP-Link.

Fibocom L860 на GoldenOrb/ROOter

Agent получает номер модема из UCI, определяет WAN и доступный ttyACM, затем выполняет AT-detach/attach. Пауза выбирается по прошивке, а не по номеру порта:

ПрошивкаРабочая пауза
18600.5001.00.35.00.026 секунд
18600.5001.00.35.01.226 секунд
18600.5001.00.35.01.286 секунд
18601.5001.00.01.16.487 секунд
18600.5001.00.35.01.578 секунд

Перестановка L860 в другой совместимый GoldenOrb/ROOter не ломает драйвер: номер порта не участвует в выборе алгоритма.

Fibocom L860 на Keenetic Launcher KN-1221

Agent узнаёт Keenetic по прямому ответу роутера, авторизуется в локальном RCI и читает show/version и show/interface. Kraken не участвует в диагностике. Из UsbLte0 извлекаются модель L860, USB ID 8087:095a, прошивка, IMEI, IMSI, ICCID, оператор, SIM, сигнал, eNB/сектор и состояние мобильной сессии.

ModGuard не реализует собственный USB-драйвер и не обращается к последовательному порту Linux. Все действия выполняются готовым компонентом Mobile/UsbLte в KeeneticOS. Мягкий reconnect использует штатную CLI-команду interface UsbLte0 tty send: AT+CGATT=0, прошивочная пауза, затем AT+CGATT=1. Telnet negotiation разбирается как непрерывный поток: служебные команды удаляются, фрагменты ответа сохраняются между чтениями, а принятие AT-команды фиксируется только после фактического OK модема. Если сразу после detach мобильный интерфейс отвечает busy, NO CARRIER или другим переходным ответом, attach повторяется раз в секунду до шести раз без повторного detach.

После attach агент выполняет штатный down/up, чтобы Keenetic заново собрал NCM/IP data plane. Ступень «перезагрузка модема» использует встроенный interface UsbLte0 power-cycle 3000, а последняя ступень — встроенный system reboot. Номер порта не участвует в выборе: имя UsbLte/UsbQmi берётся из фактически найденного интерфейса. Перестановка модема в другой совместимый Keenetic не требует правки кода.

Принятие команды роутером не равно восстановлению. После каждого действия ModGuard продолжает внешние HTTP-проверки и закрывает инцидент только после трёх последовательных успехов. В canary-тесте 24.07.2026 простой down/up не вернул трафик за 162 секунды, USB power-cycle — за 177 секунд. Команда interface UsbLte0 connect на KeeneticOS 5.01.B/5.01.C неприменима к UsbLte и отклоняется PPP-подсистемой. AT-detach/attach менял публичный IP и возвращал трафик, но dataplane мог снова деградировать; даже чистый reboot Keenetic дал лишь краткие успешные ответы. Поэтому встроенные примитивы используются каскадом с верификацией, а не считаются безусловно успешным готовым reconnect.

Agent раз в сутки читает running-config локально на ноде, извлекает только привязку и безопасные параметры Ping Check и не отправляет полный конфиг в controller. Если профиль привязан к мобильному интерфейсу, панель показывает предупреждение. На port-222 был настроен ICMP к yandex.ru каждые 10 секунд с max-fails 5. Такая проверка способна отключить интерфейс при фильтрации ICMP или белых списках; привязка на свободном canary снята и сохранена 24.07.2026. После снятия один прогон продержался 9 минут 43 секунды вместо прежних 2,5 минут, однако последующая деградация подтвердила, что Ping Check был дополнительным, но не единственным фактором.

12. Очередь, потоки и расчёт производительности

Настройки по умолчанию на агент: быстрый опрос 5 секунд, пачка до 64 команд, до 32 параллельных действий. Controller хранит до 64 ожидающих команд на агент; технический предел конфигурации — 128. Агент допускает до 64 рабочих потоков.

Требуемая скорость: R = N / T, где N — число модемов, T — период в минутах. Для 300 модемов раз в 5 минут требуется 60 успешных ротаций в минуту; раз в 10 минут — 30.

Оценка параллелизма: W = R × D / 60 × S, где D — средняя длительность команды в секундах, S — запас 1,2–1,5. При D=25 секунд и R=60 нужен пул примерно 30–38 потоков. Поэтому штатные 32 подходят при нормальном времени, а для одного узла с 300 модемами и пятиминутным периодом рекомендуется 48–64 после замера CPU, памяти и длительности reconnect.

Худший сценарий, когда оператор массово возвращает тот же IP до полного таймаута, нельзя исправить только потоками. Его ограничивают retry, reboot Huawei, часовой предел и разнесение ротаций по времени.

Почему нельзя ротировать все одновременно

Одновременный detach сотен модемов создаёт пик на USB, роутерах, мобильной сети и IP-чекерах. Планировщик обслуживает очередь непрерывно небольшими параллельными пачками. При массовом переводе на управление из панели периоды следует распределить с устойчивым фазовым смещением, чтобы 300 таймеров не сработали в одну секунду после перезапуска.

13. Участие модема в production: работа и карантин

Текущих состояний только два: ACTIVE и QUARANTINE. Прежний ручной статус RESERVE упразднён и при запуске автоматически преобразуется в ручной карантин. Источник перехода хранится отдельно, поэтому система различает решение сотрудника и автоматическую изоляцию, не показывая пользователю два одинаковых статуса.

СостояниеНазначениеВозврат
ACTIVE · В работеМодем участвует в KPI парка и может быть кандидатом для размещения клиента при выполнении всех проверок здоровья и ёмкости.Основное рабочее состояние.
QUARANTINE · КарантинРучное исключение пустого или проблемного порта либо автоматическая передача длительно неисправного устройства техническому специалисту. Модем остаётся видимым, диагностируется и не предлагается клиентам.Автоматический карантин снимается после не менее 15 минут непрерывно подтверждённого здоровья. Ручной карантин снимает сотрудник; пустой порт также вернётся сам при обнаружении нового модема.

Автоматический карантин

Controller рассматривает карантин только после не менее трёх часов непрерывного подтверждённого отказа. Перед переходом обязательны ноль активных proxy-назначений на модеме и отсутствие общего сетевого сбоя локации. Поэтому сначала клиенты должны быть безопасно эвакуированы существующим same-location failover, а уже затем неисправное оборудование выводится из production. Нехватка ёмкости блокирует карантин: система не скрывает неисправность ценой оставленного без связи клиента.

Массовые белые списки, outage guard и общий отказ локации не создают автоматический карантин. Ручной перевод в карантин также отклоняется, пока на модеме остаётся хотя бы одно активное клиентское назначение.

Как считаются показатели

Каждый переход сохраняется в modem_production_state_events, а периоды исключения из operational KPI хранятся в modem_production_exclusions. Время после перехода в карантин вычитается и из знаменателя доступности парка, и из модемного простоя. Отказ до перехода не исчезает: например, три часа подтверждённого сбоя до карантина остаются в истории и статистике как реальный production-долг.

Клиентский простой не очищается карантином. Подтверждённые интервалы недоступности публичного proxy хранятся отдельно в proxy_availability_segments. Если клиентский endpoint не работал до пересадки, этот период навсегда остаётся в истории клиента, порта и расследования, даже когда исходный модем позднее отправлен в карантин.

14. Соединения и прокси-порты

Agent читает локальные сокеты 3proxy и связывает их с профилями конкретного modem ID. «Клиентских» — ESTABLISHED только на клиентских профилях. «Всего» включает служебные профили. «Активных клиентских портов» — число профилей, на которых есть хотя бы одно установленное соединение.

Эти числа являются моментальным снимком одновременных TCP-сессий из ss -Htan, а не количеством запросов в минуту. Состояние CLOSE_WAIT считается отдельно и не попадает в ESTABLISHED. Ноль соединений не доказывает, что порт не арендуется: клиент может держать прокси в резерве.

maxconn задаётся для каждого отдельного HTTP/SOCKS listener в 3proxy, а не как общий лимит модема. Поэтому несколько клиентских профилей могут суммарно дать тысячи сессий. Manager показывает два значения, если они расходятся: желаемый лимит из Kraken API и фактический лимит из живого файла /etc/3proxy/modem-id-*.cfg. Расхождение означает, что изменение не дошло до data plane и требует разбора.

Логины и пароли прокси не отправляются на controller и не показываются в интерфейсе.

14a. Архив SMS и очистка памяти модема

Agent читает SMS непосредственно с модема, без Kraken. Huawei HiLink использует локальные API /api/sms/sms-count и /api/sms/sms-list. Fibocom L860 на GoldenOrb читается из атомарно обновляемого штатного кэша /tmp/smstextN: встроенный SMS-демон роутера уже преобразует PDU/UCS-2 в UTF-8, а ModGuard не конкурирует за AT-порт с управлением связью. На Keenetic используется штатный AT-транспорт.

SMS открываются отдельной кнопкой в дашборде модема. На кнопке и внутри окна показывается общий для сотрудников счётчик непрочитанных сообщений. Это состояние ModGuard, а не флаг в памяти модема: Operator или Admin может отметить сообщение прочитанным, снова сделать его непрочитанным для коллеги либо отметить прочитанными все сообщения модема. Система хранит имя сотрудника и время действия в audit log.

HiLink отдаёт текст в Unicode. Fibocom может отдавать UCS-2/UTF-16BE в шестнадцатеричном виде; Agent декодирует отправителя и тело, проверяет управляющие символы и не заменяет исходный текст битой строкой при ошибке декодирования. Для GoldenOrb дополнительно контролируется свежесть штатного кэша. В дашборде показываются отправитель, время по часам модема, текст и состояние очистки.

Почему SMS не потеряется при очистке

  1. Agent читает SMS и вычисляет устойчивый отпечаток из хранилища, индекса, отправителя, времени и текста.
  2. Controller сохраняет сообщение в SQLite и дедуплицирует повторные чтения.
  3. Только после успешной транзакции controller разрешает удалить конкретный индекс с модема.
  4. Agent выполняет удаление, сохраняет результат в локальный spool и отправляет подтверждение controller.
  5. При ошибке архив остаётся, сообщение помечается ошибкой очистки и может быть повторено. Центральная копия кнопкой очистки не удаляется.

Командный статус прочтения и физическая очистка разделены. Удаление после архивации освобождает ограниченную память модема, но не удаляет центральную копию и не сбрасывает отметку сотрудника. Поэтому спам можно убрать из устройства, а важное сообщение сохранить и при необходимости вернуть в непрочитанные.

Автоматическая очистка защищена отдельными параметрами sms_auto_delete_enabled, allowlist модемов, allowlist проверенных драйверов sms_auto_delete_driver_allowlist и обязательным подтверждением sms-archive-delete-v1. Поэтому чтение можно включить на весь парк, а удаление оставить только моделям, прошедшим живую canary-проверку. Перестановка модема в другой порт не обходит это ограничение. Viewer видит только счётчики; тексты и отправители доступны Operator и Admin.

Сканирование выполняется ограниченными пачками, чтобы не перегружать роутеры и канал. Контроллер выдаёт агенту не более 20 команд удаления за один снимок: если в Huawei накопились сотни SMS, они архивируются и очищаются несколькими циклами без шквала запросов к HiLink API. На GoldenOrb удаление конкретного индекса уважает штатные smslock/lockgcom, ограничено по времени и требует ответа OK; ошибочные бесконечные ожидания старой прошивки не наследуются. Полный повторный снимок подтверждает, что SMS действительно исчезла, даже если прежняя SSH-команда оборвалась после выполнения на модеме. Неисправный AT-канал отображается как ошибка и не приводит к удалению вслепую.

15. Данные, хранение и статистика

  • Controller DB: /var/lib/modem-guardian/controller.sqlite.
  • Agent spool и состояние восстановления: /var/lib/modem-guardian/spool.sqlite.
  • Конфигурация controller: /etc/modem-guardian/controller.env.
  • Конфигурация агента: /etc/modem-guardian/agent.json.
  • Токен агента: /etc/modem-guardian/controller.token, режим 0600.
  • Реквизиты MikroTik: таблица node_router_settings; пароль хранится только как Fernet ciphertext и расшифровывается Admin-only API по явному открытию настроек ноды.

Подробные снимки здоровья хранятся 72 часа. Раз в час controller до удаления сворачивает более старые строки в health_sample_hourly и хранит почасовые минимумы, максимумы, средние значения и счётчики 90 дней. Поэтому детальную последовательность отдельных probes видно за трое суток, а семи- и тридцатидневные тренды нагрузки и здоровья не исчезают. Белые списки агрегируются отдельно и не превращаются в отказы. Инциденты, шаги восстановления, outage-сегменты, SMS, замеры скорости, команды, смены IP и audit log этим прореживанием не затрагиваются.

Rollup и удаление выполняются в одной SQLite-транзакции не чаще раза в час; строка maintenance_state.health-sample-rollup-v1 хранит время и итог последнего обслуживания. Удалённые страницы становятся свободными внутри файла. Физическое уменьшение файла выполняется только управляемым WAL checkpoint и VACUUM при остановленном controller после свежего backup, а не во время обычного снимка агента.

Статистика по окнам 24 часа, 7 и 30 дней вычисляется из первичных интервалов и событий при запросе dashboard. Интервалы резерва и карантина вычитаются из operational modem KPI, но только с момента фактического перехода. Подтверждённая доступность клиентских endpoint рассчитывается независимо от modem KPI и не очищается изменением состояния оборудования. В базе не хранится «готовый процент», поэтому изменение границы окна не требует пересчёта исторических строк и не создаёт накопительной ошибки.

Строка локации на главной странице является быстрым фильтром парка: повторное нажатие снимает фильтр. Кнопка «Дашборд» открывает единый профиль модема с текущим здоровьем, аппаратными и SIM-параметрами, сетевым трактом, последними probes, нагрузкой по прокси-портам, доступностью, белыми списками, качеством ротации, инцидентами и проверками за семь дней. Профиль строится атомарно; запоздавший ответ предыдущего запроса не может продублировать или заменить данные другого модема.

16. Безопасность

UI защищён логином, scrypt-паролями, серверными сессиями, HttpOnly/Secure/SameSite cookie и CSRF для изменений. Viewer только читает. Operator выполняет восстановление, ротацию и управляет состоянием участия модема в production. Admin дополнительно управляет пользователями, нодами, установочными токенами и видит внутреннюю Wiki.

Любой активный пользователь может сменить только собственный пароль. Администратор может сменить пароль любого пользователя; остальные сессии этого пользователя при этом отзываются. Генератор создаёт случайный пароль из 22 символов с буквами, цифрами и знаками.

Agent регистрируется одноразовым enrollment token, затем хранит отдельный длинный токен. С VDS к Kraken открывать входящий порт не требуется. Agent инициирует HTTPS наружу.

Controller работает под отдельным пользователем, с systemd sandbox и лимитами ресурсов. Agent работает от root, но также ограничен systemd: защищённая файловая система, закрытый home, ограниченные address families и явные writable paths.

Fail2ban защищает UI и SSH. Правила не должны блокировать штатный NAT-forwarding; перед изменением jail проверяются bind-порты и цепочки NAT.

Пароли клиентских proxy не входят в общий bootstrap менеджера. Bootstrap сообщает только наличие секрета. Operator получает выбранные реквизиты отдельным POST-запросом /api/v1/proxy-manager/credentials после нажатия «Показать», «Копировать», «Скачать» или подготовки восстановления. Ответ имеет Cache-Control: no-store, выдача фиксируется в audit без пароля, а браузер удаляет загруженные пароли из рабочей модели и полей после закрытия окна. За один запрос разрешено не более 200 назначений.

Пароль MikroTik по принятому эксплуатационному правилу показывается открытым в форме ноды, но только роли Admin. Это осознанное отличие от клиентских proxy: общий список нод, dashboard, Operator/Viewer API, agent snapshot и audit не получают пароль. SQLite хранит Fernet ciphertext; ключ остаётся отдельным root-only файлом /etc/modem-guardian/proxy-secret.key. При проверке controller расшифровывает пароль только для команды назначенной Kraken-ноды, а агент передаёт его OpenSSH через временный SSH_ASKPASS, не через командную строку. Ответы API имеют no-store; результат проверки содержит только успех или очищенный текст ошибки.

17. Эксплуатация, логи, backup и релизы

Сервисы

systemctl status modem-guardian-controller
systemctl status modem-guardian-agent
journalctl -u modem-guardian-controller --since today
journalctl -u modem-guardian-agent --since today

Настройки ноды и MikroTik

Admin открывает «Ноды → Редактировать» и меняет понятное название, физическую локацию, локальный адрес MikroTik, SSH-порт, логин и пароль. «Сохранить и проверить SSH» сначала атомарно сохраняет настройки, затем создаёт приоритетную команду test_router_ssh. Проверка выполняется с самой Kraken-ноды, поэтому подтверждает реальный локальный маршрут, а не только доступность публичного проброса с VDS. Agent вызывает штатный OpenSSH без shell, с пятисекундным connect timeout, одним connection attempt и persistent known_hosts в /var/lib/modem-guardian/router-known-hosts. Первый ключ принимается один раз; последующая неожиданная смена host key завершает проверку ошибкой.

В общем списке показываются адрес и последний безопасный результат, но не пароль. Состояния: «не настроен», «не проверен», «проверяем», «SSH доступен» и «SSH недоступен». Команда проверки имеет приоритет над обычной rotation/speed очередью, но не обходит выключенную ноду и не меняет конфигурацию RouterOS. Изменение реквизитов сбрасывает старый зелёный результат до новой проверки.

Релизы

Каждый релиз распаковывается в /opt/modem-guardian/releases/<version-time>. Ссылка /opt/modem-guardian/current переключается атомарно. Предыдущая директория остаётся для отката.

Backup

Перед обновлением выполняется online-backup SQLite с PRAGMA integrity_check. Ежедневный systemd timer хранит на VDS две последние горячие копии с точным именем controller-YYYYMMDDTHHMMSSZ.sqlite.zst. Сначала создаётся и проверяется обычная SQLite-копия, затем она с низким параллелизмом сжимается Zstandard; исходный файл удаляется только после успешного сжатия. Автоматическая ротация намеренно не удаляет ручные и pre-release снимки: они удаляются только отдельной обслуживающей операцией после проверки внешней копии.

Полный снимок прежнего каталога backup от 27.07.2026 сохранён на администраторском Mac в private-backups/VDS-81.200.147.47: 31 файл и 4 896 064 397 исходных байт упакованы в Zstandard и зашифрованы CMS. Каждый SQLite прошёл PRAGMA quick_check, каждый файл имеет SHA-256 в manifest, загруженный архив повторно расшифрован и проверен локально. Приватный ключ хранится отдельно в .secrets, оба каталога исключены из Git. В RESTORE.txt описаны сверка, извлечение и контролируемое восстановление.

29.07.2026 создан свежий комплект private-backups/VDS-81.200.147.47/20260729T081617Z: консистентная БД сжата и зашифрована CMS, controller secrets зашифрованы отдельным потоковым архивом, runtime/NAT/systemd/nginx сохранены отдельно. Серверные и локальные SHA-256 совпали, локально расшифрованная БД прошла полный PRAGMA integrity_check. После этого старые полноразмерные snapshots были адресно удалены, а заполнение VDS снизилось с 71% до 41%.

SQLite содержит зашифрованные proxy credentials, но для расшифровки требуется отдельный /etc/modem-guardian/proxy-secret.key. Поэтому копия БД без отдельно защищённого пакета конфигурации не считается полным disaster-recovery набором. Старые ручные snapshots на VDS не удаляются, пока зашифрованный secret package не сохранён и не проверен отдельно.

Диск и журналы

Политика /etc/systemd/journald.conf.d/50-modguard-retention.conf ограничивает журнал размером 384 МБ, сохраняет минимум 2 ГБ свободного места и не держит записи дольше семи дней. Наличие файла в Git не считается установкой: после deploy проверяются journalctl --disk-usage и фактическая конфигурация VDS. На 26.07.2026 установка политики сократила journal с 1,6 ГБ до 177 МБ и снизила заполнение диска с 66% до 57% без удаления БД и backup.

Admin-only endpoint /api/v1/operations/metrics отдаёт размеры DB/WAL/SHM, логический и освобождаемый объём SQLite, диапазон raw/hourly samples, состояния и возраст старейшего задания в очередях, NAT, активные инциденты, свободное место, число потоков, HTTP p95 и latency/error rate Kraken API. Предупреждение по диску должно срабатывать при 75%, критический уровень — при 85%.

Отказоустойчивость controller

HTTP-очередь ограничена 128 соединениями, одновременно обслуживается не более 48 запросов. Systemd всегда перезапускает упавший процесс с задержкой 3 секунды и ограничивает restart storm. Это повышает живучесть одного экземпляра, но не является HA: SQLite, process-local блокировки proxy lifecycle и один NAT writer не допускают безопасный active-active. Для настоящего HA нужны общая PostgreSQL, persistent reservation/leader lease, fencing token для NAT generation и второй controller за отдельным балансировщиком.

Проверка после релиза

  1. Локальные тесты и compileall.
  2. Контрольная сумма архива.
  3. Backup controller.
  4. Переключение controller и агентов.
  5. Проверка /healthz, версии и SQLite integrity.
  6. Ожидание полного снимка и командного цикла.
  7. Проверка warning/error/traceback в журналах.

17a. Где лежат ModGuard, данные и backup

Это карта production-каталогов. Путь current всегда указывает на активный неизменяемый релиз; данные и секреты находятся вне релиза и не исчезают при откате.

ЧтоГде лежит
Активный controller на VDS/opt/modem-guardian/current — symlink на каталог в /opt/modem-guardian/releases/.
Предыдущие релизы/opt/modem-guardian/releases/<version-time-commit>. Каждый каталог — полный код для отката; это не backup базы.
Активная база controller/var/lib/modem-guardian/controller.sqlite. Файлы -wal и -shm рядом могут появляться во время работы SQLite. modguard.db и modguard.sqlite3 в этом каталоге исторические и не указаны в production MODGUARD_DB_PATH.
Состояние NAT/var/lib/modem-guardian/nat-desired.json и /var/lib/modem-guardian/nat-applied.json. Источник истины для назначений всё равно SQLite.
Настройки controller/etc/modem-guardian/controller.env, секретный Fernet-ключ /etc/modem-guardian/proxy-secret.key, NAT allowlist /etc/modem-guardian/nat-targets.json.
systemd и nginx на VDS/etc/systemd/system/modem-guardian-controller.service, backup service/timer рядом; nginx site — /etc/nginx/sites-available/modguard с symlink в sites-enabled.
Горячие и pre-release backup/var/backups/modem-guardian. Ежедневные проверенные SQLite-копии имеют вид controller-*.sqlite.zst; там же есть ручные pre-release и зашифрованные export.
Старые ручные backup/root/modguard-backups. Это исторический каталог; он не участвует в ежедневном timer.
Локальная внешняя копия на Macprivate-backups/VDS-81.200.147.47 в workspace. Каталог исключён из Git; архивы зашифрованы, manifest и restore-инструкция лежат рядом.
Agent на каждом KrakenКод: /opt/modem-guardian/current и /opt/modem-guardian/releases; конфиг: /etc/modem-guardian/agent.json; токены: /etc/modem-guardian/controller.token и enrollment.token; spool: /var/lib/modem-guardian/spool.sqlite; SSH host keys: /var/lib/modem-guardian/router-known-hosts.
Фактические 3proxy-конфиги на Kraken/etc/3proxy/modem-id-*.cfg. Именно их agent читает для проверки listener, лимитов и загрузки.

Журналы controller и agent не лежат в отдельном application log: их хранит systemd journal. Для просмотра используются journalctl -u modem-guardian-controller на VDS и journalctl -u modem-guardian-agent на Kraken.

18. Разбор типичных проблем

НаблюдениеЧто смотреть
IP старше периода, команды нетЗдоров ли модем, нет ли открытого инцидента, включена ли ротация, известен ли драйвер, не исчерпан ли лимит 10 ошибок/час.
PENDING долго не меняетсяДоступен ли быстрый endpoint, жив ли command-control thread, размер очереди и token auth. Restart-команда 3proxy должна забираться отдельной срочной очередью не позднее следующего 5-секундного poll даже во время долгой ротации.
DELIVERED долго не завершаетсяЛог controller_command агента, SSH роутера, длительность IP verifier и зависший subprocess.
Reconnect completed but IP did not changeЭто не сбой доставки команды. Оператор вернул тот же IP. Проверить следующий retry или переход Huawei к reboot.
Modem is not healthy; reconnect skippedПлановая ротация столкнулась с новым отказом. Основным становится инцидент восстановления.
Много модемов упали одновременноОбщие IP-чекеры, белые списки, VDS, маршрут, оператор и circuit breaker. Не перезагружать парк массово.
Модель неизвестнаДоступен ли роутер, есть ли API модема, правильный ли upstream gateway, виден ли USB/tty.
L860 меняет IP слишком долгоПрошивка, выбранная пауза, AT-порт, WAN-интерфейс, время регистрации после attach и возврат оператором того же IP.
Keenetic показывает Connected, но сайты не открываютсяСмотреть не только radio connection-state, но и link, connected, defaultgw, IPv4 layer и внешние HTTP-пробы. Проверить привязанный Ping Check. Принятый down/up или power-cycle не считается восстановлением без успешного HTTP.

19а. Менеджер proxy, замеры скорости и управляемый ретранслятор

Фактический статус релиза 0.17.10 от 31.07.2026

Менеджер размещён внутри основной ModGuard и доступен ролям Operator и Admin по адресу /proxy-manager/. В production работают создание, удаление, восстановление прежней выдачи с теми же реквизитами, ручная пересадка внутри локации, полный контур замеров скорости и точечный persistent NAT на VDS. Автоматический локальный failover имеет режимы «Стоп», «Наблюдение» и «Активен». Сразу после релиза использовался безопасный режим OBSERVE; после успешного внешнего canary, сверки назначений с NAT и нулевого списка кандидатов 26.07.2026 включён ACTIVE. Admin может в любой момент запретить новые автоматические пересадки глобальной кнопкой «Стоп».

Демо-набор существует только при открытии локального файла прототипа. После загрузки production bootstrap массивы клиентов, модемов, переключений и замеров полностью заменяются ответом controller. Дополнительное правило 0.10.1 запрещает браузеру достраивать отсутствующие номера портов: production candidate pool является точной копией списка реальных modem из bootstrap. Controller, в свою очередь, отдаёт только строки с monitored=1 от явно настроенных Kraken nodes; снятые с мониторинга записи, simulator и случайные исторические строки не попадают ни в ёмкость, ни в автоматический выбор.

Kraken остаётся исполнительным слоем и источником только собственных proxy-профилей, auth-реквизитов, maxconn, backend-порта и связанного modem_id. Его поля conn_inet, operator, rotation и архивные значения не используются для диагноза здоровья. Модель, оператор, наличие устройства, резерв, инциденты и число фактических соединений берутся из локальных ModGuard agents и центральной БД. Это ограничение специально сохраняет возможность позднее заменить Kraken собственным исполнителем.

Двухфазная выдача и проверка proxy

Создание и проверка трафика являются двумя связанными, но независимыми фазами. В первой фазе controller валидирует клиента и выбранные modem, резервирует свободные номера, создаёт auth и proxy через официальный Kraken REST API, перечитывает созданные профили, импортирует назначения в SQLite и после каузально свежего foundation proof применяет persistent NAT. После успешного Kraken read-back, foundation proof и NAT операция CREATE_ASSIGNMENTS получает SUCCEEDED, а окно выдачи открывается менеджеру. Медленный внешний IP-checker больше не удерживает HTTP-запрос создания и не способен превратить уже успешную выдачу в ложный 504.

Операция CREATE_ASSIGNMENTS регистрируется в SQLite до ожидания блокировки конфигурации ноды. Manager видит этапы WAITING_NODE_CONFIG, VALIDATING_SELECTION, READING_KRAKEN, CREATING_KRAKEN_PROFILES, KRAKEN_READBACK, SYNCHRONIZING_ASSIGNMENTS, WAITING_FOUNDATION, RECONCILING_NAT и QUEUING_DATA_PLANE_CHECKS. Новый CREATE требует agent capability до Kraken mutation. После exact read-back controller выдаёт durable challenge, а NAT ждёт следующий agent collection с этим challenge, canonical assignment owner, exact listening customer cfg/port и sole migration-owned CURRENT READY; поздний старый spool snapshot недостаточен. Pending endpoint остаётся PUBLISHING: он резервирует port/modem, но не блокирует unrelated generic NAT. До helper write сохраняются linked attempt и causal generation; только qualified APPLIED/NO_CHANGE атомарно публикует endpoint. Durable claim проверяется вокруг каждого external write, ожидание переживает restart и исходный POST быстро получает 202. Таймаут до первого NAT write не запускает ложный cleanup, ambiguous PENDING остаётся fail-closed, а ошибка после qualified write использует штатную rollback generation. Подтверждённый успех открывает уже созданную группу без повторного POST; терминальная ошибка разрешает новую попытку.

Ожидание node-config lock ограничено 15 секундами. Занятая нода отвечает контролируемым 503 с Retry-After, а не удерживает один из HTTP worker бесконечно. Assignment-lock всегда берётся раньше node-lock; maintenance, ручной lifecycle и автоматика используют один порядок. Фоновая синхронизация ждёт node-lock не более двух секунд и при занятости откладывает только ремонт служебного listener, продолжая обычный импорт. Такой ремонт делает одну ограниченную попытку не дольше 30 секунд за цикл. Синхронизация после клиентской записи импортирует назначения без ремонта служебных 3proxy, поэтому чужой отсутствующий cfg не задерживает выдачу.

Успешный ответ создания содержит только operation ID, созданные endpoint, NAT-результат и очередь проверок. Полный многомегабайтный bootstrap больше не входит в mutation-ответ и загружается отдельным GET после подтверждения. Это уменьшает время удержания мобильного HTTP-соединения и не меняет Kraken, NAT или credentials. Повтор успешного idempotency key также возвращает компактный сохранённый результат без секретов и bootstrap.

Для каждого нового назначения в таблице proxy_verification_jobs создаётся отдельное persistent-задание. Состояния: QUEUED — ожидает свободного worker; RUNNING — выполняется прямой запрос через клиентский endpoint; RETRYING — первая ошибка подтверждается повторно; PASSED — трафик подтверждён; REPAIRED — помог точечный reload 3proxy; MOVED — выполнена и проверена локальная пересадка; CREDENTIALS_ERROR — отклонены реквизиты; ATTENTION — автоматике не удалось закончить восстановление; CANCELLED — назначение уже неактивно. Четыре bounded worker обрабатывают разные порты параллельно. Задание хранит число попыток, срок следующего запуска, безопасный результат и последнюю ошибку; зависшее RUNNING после рестарта controller возвращается в очередь.

Первая ошибка не меняет конфигурацию: она создаёт DATA_PLANE_SUSPECTED и повторную проверку через 30 секунд, чтобы не принять штатную ротацию за отказ. Повторная ошибка входит в тот же automation lock и тот же контур, что регулярный watchdog: точечный parity-safe reload текущего 3proxy, немедленная проверка, затем при необходимости same-location failover с capacity reservation, Kraken read-back, listener confirmation, NAT reconcile и внешним data-plane probe. Отдельного конкурирующего controller нет. Общая авария location, пустая ёмкость, assignment lock и режим «Стоп» продолжают запрещать опасное действие. Временная занятость контура даёт ограниченный повтор задания; подтверждённая позднее регулярным watchdog работоспособность автоматически меняет прежнее ATTENTION на PASSED.

Успешный Kraken API read-back подтверждает назначение, но файл нового target modem-id-N.cfg на ноде иногда появляется на несколько секунд позже. Поэтому точечный restart target до шести раз повторяет только точный ответ «cfg was not found» с пятисекундной паузой; другие ошибки завершают операцию без маскировки. Отсутствующий старый source config не повторяется: после переноса последнего профиля Kraken вправе удалить его. Controller отдельно доказывает отсутствие source orphan на каждом прежнем backend-порту либо ставит найденный orphan за подтверждённым target и переводит его в draining только после успешного внешнего probe. Ручная пересадка и автоматическая maintenance-эвакуация используют один rollback: сначала подтверждается возврат исходного config и владение конкретным клиентским портом, затем безопасно очищается target или доказывается отсутствие его orphan listener.

Окно группы показывает общий прогресс и отдельный статус напротив каждого endpoint. Пока хотя бы одно задание выполняется, технический список раскрывается автоматически. Лёгкий GET /api/v1/proxy-manager/verifications?operation_id=… обновляет только статусы и не перечитывает весь bootstrap каждые полторы секунды; после завершения bootstrap обновляется один раз. Закрытие окна или перезагрузка браузера не отменяет работу: последнее состояние приходит из SQLite. Реквизиты загружаются отдельным защищённым API уже после открытия окна и не задерживают визуальную обратную связь.

Idempotency key меняется только после подтверждённого успеха либо однозначной прикладной ошибки. При сетевом обрыве или ответе reverse proxy без JSON браузер сохраняет прежний ключ и предлагает безопасный повтор. Если первый запрос фактически завершился на controller, повтор возвращает сохранённый результат вместо создания второй пачки. Для base endpoint создания в Nginx оставлен увеличенный timeout как дополнительная страховка, но штатная длительность запроса больше не зависит от последовательных data-plane проверок.

Пакетное удаление использует тот же принцип определённого результата. Один idempotency key сохраняется на весь выбранный набор портов. Controller обновляет безопасные фазы операции VALIDATING_ASSIGNMENTS, DELETING_FROM_KRAKEN, KRAKEN_DELETE_CONFIRMED, SYNCHRONIZING_ASSIGNMENTS и RECONCILING_NAT. Если Nginx вернул 502/504 либо сетевой ответ оборвался, окно не объявляет удаление неудачным: оно читает GET /api/v1/proxy-manager/operations/status по прежнему ключу, показывает живую фазу и ждёт терминальное состояние. SUCCEEDED подтверждает весь пакет, PARTIAL явно показывает число удалённых и сохранённых портов, FAILED оставляет тот же ключ для безопасной проверки или повтора. Статус доступен только тому сотруднику, который запустил операцию, и не возвращает пароли либо общий bootstrap.

В карточке клиента блок доступности перечисляет все активные proxy клиента, а не только когда-либо отказавшие. Текущий диагноз и действие автоматики показываются отдельно от исторической суммы простоя за 24 часа. Зелёный статус означает подтверждённый трафик сейчас; жёлтый — выполняется первичная проверка, 30-секундное подтверждение, проверка после пересадки либо поиск исправного modem после неудачного restart; красный — отказ подтверждён, но конкретное восстановительное действие ещё не завершено либо требуется внимание; серый — свежего data-plane наблюдения пока нет. Историческая строка «Простой за 24ч» не меняет текущий цвет и сама по себе не означает действующий отказ. Порты сортируются сначала по текущей срочности, затем по длительности простоя. Нажатие на строку открывает единую хронологию порта.

Рейтинг «Кого переключает чаще» всегда строится за последние семь суток. Он показывает не только число переключений, но и число и долю затронутых активных портов клиента; период явно написан в заголовке, чтобы manager не принимал семидневную статистику за события текущего дня.

Синхронизация с Kraken

  1. Controller раз в 300 секунд читает POST /api/proxy/list каждого Kraken с пагинацией до последней страницы. Этот фоновый цикл только читает и сверяет профили; изменения выполняются исключительно отдельным подтверждённым запросом manager.
  2. Для каждого профиля разбирается список auth_params. Логины из node-specific allowlist служебных учётных записей, сейчас rooot, не становятся клиентами.
  3. Профиль с ровно одним неслужебным login импортируется. Профиль без клиента считается служебным. Профиль с несколькими клиентскими login, пустым паролем, отсутствующим ID или некорректным портом помещается в счётчик конфликтов и не угадывается автоматически. Если такой Kraken ID уже был импортирован, конфликт не считается доказательством удаления и прежнее назначение сохраняется.
  4. Имена клиентов сравниваются без учёта регистра через casefold. Поэтому одинаковый login на двух Kraken становится одной карточкой клиента, а не двумя клиентами.
  5. Профили одного клиента с одинаковой календарной датой date_create объединяются в одну дневную группу выдачи. Повторная синхронизация не создаёт дубликатов благодаря уникальным ключам node + Kraken proxy ID и client + day + source.
  6. Все корректные профили одной ноды и список исчезнувших профилей применяются одной SQLite-транзакцией: читатель видит либо прежний, либо целиком новый снимок. Единичный active=false для ранее активного профиля считается неподтверждённым наблюдением; DISABLED устанавливается после двух последовательных полных снимков. Аналогично, фоновая синхронизация присваивает REMOVED только после двух последовательных полных снимков без профиля. Подтверждённое удаление через ModGuard записывается сразу после Kraken read-back. Строка, реквизиты и группа не удаляются: менеджер видит её как старую нерабочую выдачу.
  7. Если API одной ноды недоступен, назначения этой ноды не помечаются удалёнными. Sync получает PARTIAL, сохраняет ошибку без token и ждёт следующего цикла.

Фактический снимок хранится в таблицах proxy_clients, proxy_credentials, proxy_orders, proxy_assignments, proxy_sync_runs и proxy_verification_jobs. Каждая попытка записи отдельно фиксируется в proxy_operations с idempotency key, исполнителем, безопасной копией запроса, результатом и состоянием rollback. Синхронизация идемпотентна: повтор одного снимка обновляет last_seen_at и наблюдаемые поля, не создавая второго клиента, заказа или назначения. API token, proxy password и полный JSON с паролем в operation log не записываются.

Operator может запросить один полный внутренний preflight через POST /api/v1/proxy-manager/sync/full. Он последовательно запускает обычный sync, durable lifecycle reconciliation и NAT read-back и возвращает только краткие результаты. READY возможен лишь при sync=OK, завершённой reconciliation и NAT=APPLIED/NO_CHANGE; BUSY, PARTIAL, ошибка или занятая reconciliation остаются NOT_READY. Это не заменяет независимые внешние проверки, не выдаёт handoff admission и не отменяет fencing, owners или lifecycle-инварианты.

Реквизиты и защита секретов

Kraken возвращает пароль внутри auth_params. Controller немедленно шифрует его Fernet-ключом из /etc/modem-guardian/proxy-secret.key; в SQLite хранится ciphertext и keyed fingerprint для дедупликации. Ключ находится вне БД, читается системным пользователем modguard и резервируется отдельно от SQLite. Bootstrap не расшифровывает пароли: выбранные секреты выдаются только по отдельному no-store API после действия manager и фиксируются в audit без значения.

Bootstrap API менеджера доступен только Operator/Admin, отвечает с Cache-Control: no-store и работает через HTTPS/session cookie. Открытые пароли нужны для кнопок «Посмотреть» и «Копировать», поэтому их нельзя выводить в audit, console, URL или error. Viewer не имеет доступа к разделу и API. Пароль Kraken API хранится только в root-owned /etc/modem-guardian/controller.env.

На странице «Парк» Operator/Admin может нажать на клиентский или служебный proxy-профиль. Браузер только в этот момент запрашивает одну пару реквизитов отдельным no-store API, копирует каноническую строку host:port:login:password и показывает локальную подсказку «Прокси скопирован». Полный пароль не входит в bootstrap и не записывается в audit: журнал содержит только node, endpoint, login и тип профиля. Служебные логины, включая rooot, читаются непосредственно из живого Kraken profile и не сохраняются в центральной БД как клиентские credentials.

Что означает карточка клиента

Клиент определяется логином, а не auth ID Kraken и не отдельным портом. Группа выдачи — все профили этого клиента, созданные в один календарный день. Назначение — один публичный endpoint, один Kraken proxy ID и текущий modem ID. Удалённое назначение остаётся в прежней группе и явно помечено нерабочим. Если один login использует несколько паролей, это один клиент с несколькими credential set, а не несколько клиентов.

Клиенты test, legacy и партнёрские логины не архивируются автоматически только из-за имени. Система не знает коммерческий смысл логина. После первичной сверки Operator или Admin может поместить пустую карточку в архив: controller повторно проверяет отсутствие активных назначений, сохраняет историю выдач и записывает действие в audit. Состояние хранится в центральной БД и не теряется после обновления страницы. Новая выдача тому же login может вернуть карточку в рабочий список, но только после явного подтверждения manager. Автоматическое угадывание по словам test или service запрещено.

Поле нового клиента является доступным combobox по активным и архивным карточкам. Точное совпадение с архивным login не создаёт дубликат: интерфейс показывает прежнюю карточку и требует действие «Вернуть и продолжить». Похожее имя с небольшой опечаткой также блокирует переход, пока manager не выберет найденного клиента или явно не подтвердит, что это другой клиент. В запрос передаются одновременно стабильный client_id, login и признак подтверждённого возврата; controller повторно сверяет все три значения. Архив снимается после успешного Kraken read-back, импорта назначения и NAT; последующая data-plane проверка видна отдельно и при проблеме запускает единый recovery-контур. Если control-plane фаза откатывается, карточка остаётся в архиве. Повтор запроса с тем же idempotency key возвращает исходную причину ошибки, а не общий статус rollback.

Занятость и соединения

Столбец основной страницы разделяет соединения на клиентские и служебные. Профиль клиентский, если его порт присутствует в явном списке арендных портов или его login не входит в список служебных. Явный список может быть неполным и больше не способен скрыть нового клиента. Служебный трафик обычно относится к диагностическому профилю rooot; сумма клиентских и служебных равна полному числу установленных TCP-соединений этого модема.

Количество клиентов на модеме считается по активным назначениям разных client login, а не по числу proxy-профилей и не по текущим TCP-соединениям. Глобальная квота download на одно клиентское место настраивается Operator/Admin на вкладке Прокси → Модемы целым числом от 1 до 100 Мбит/с; значение по умолчанию — 10. Рекомендация равна целой части расчётный download / квота, но не меньше одного и не больше десяти мест. При одном-двух успешных полных замерах временно используется медиана и рекомендация помечается предварительной; начиная с трёх замеров используется консервативный P25 за последние 30 дней. Поэтому разовый высокий пик не раздувает вместимость, но modem с одним подтверждённым замером больше не остаётся искусственно одноместным. Работающий modem без истории скорости допускается как базовый одноместный вариант и ранжируется после проверки здоровья, свободного слота и текущих соединений. Квота хранится в SQLite, меняется с audit и одновременно применяется к таблице, общей ёмкости, автоматическому/ручному подбору и финальной серверной проверке выдачи.

Пул публичных IP и потенциал расширения

Дашборд каждого modem строит отдельный блок по центральной истории IP. Полная хронология смен остаётся в public_ip_changes. Для быстрых скользящих окон 24 часа, 7 и 30 дней controller поддерживает компактный индекс public_ip_observations с первым и последним наблюдением каждой комбинации location, modem, operator и IPv4. Индекс обновляется в той же транзакции, что и новое событие; при первом обновлении он идемпотентно строится по прежней истории. Дополнительных запросов к modem, оператору или сторонним базам при открытии окна нет. Показываются число фактически наблюдавшихся уникальных IPv4, число смен, повторных выдач, уникальных подсетей /24 и /16, а также наиболее часто встречавшиеся подсети. Повтором считается смена, после которой уже встречавшийся адрес был выдан снова; это отличается от одновременного дубля на другом modem.

На online-копии production SQLite миграция 120 235 сырых событий в 84 204 компактных наблюдения заняла 4,24 секунды. После однопроходного расчёта окон и SQL-группировки по covering index медиана формирования полной истории сократилась с 3,5–4,1 секунды до 0,90 секунды для Kraken-1 и 1,03 секунды для Kraken-2. Исходная история не удаляется; после миграции отдельно выполняется PRAGMA quick_check.

Данные разделены на три уровня. «Этот modem» относится только к выбранному устройству. «Оператор на локации» объединяет только modem того же оператора на той же физической площадке и является основным срезом для решения о расширении. «Вся локация» объединяет всех операторов и показывается отдельно только как справочный итог. На каждом уровне фактические уникальные IP выводятся отдельно за 24 часа, 7 и 30 дней. Сравнительная таблица location показывает Yota, МТС, МегаФон и другие реально обнаруженные операторы независимо друг от друга.

Начиная с 0.14.7 каждая новая запись смены IP сохраняет нормализованного оператора в момент события. Поэтому последующая перестановка SIM не переносит новую историю в прежний операторский пул. Для записей, накопленных до 0.14.7, оператор однократно заполнен по текущей привязке modem при обновлении; если SIM раньше переставлялась, этот старый участок истории является приближением. API явно сообщает метод атрибуции, а Wiki сохраняет это ограничение.

Показатель «верхняя граница известных /24-корзин» равен 256 × число уже замеченных диапазонов /24 конкретного оператора на location. Деление по /24 здесь является аналитической группировкой первых 24 бит, а не утверждением о реальной маске маршрута: адреса с окончаниями .0 и .255 могут быть легитимны внутри более крупного операторского префикса. Это не фактически полученное число IP и не теоретический полный пул оператора. Даже BGP/WHOIS показывают объявленные сети компании, но не внутренний CGNAT-пул, разрешённый этим SIM.

Для оценки разнообразия controller считает пересечение адресов выбранного modem только с другими modem того же оператора, число новых IP за 7 дней, отношение фактических уникальных IP оператора к числу его активных modem, долю повторов и концентрацию в крупнейшей /24. Прогнозы «добавить 100/200 таких SIM» используют фактический 30-дневный операторский пул при допущении, что он не расширится. Сигнал DIVERSE/WATCH/CONCENTRATED является управленческой подсказкой, а не автоматической командой.

Ёмкость внешних портов

Публичный номер является внутренним ресурсом forwarder-слоя, а не коммерческой ёмкостью парка. Manager видит только число пригодных уникальных modem и фактически доступную выдачу; общий размер диапазонов в обычной карточке не показывается. Если маршрутизация неожиданно становится узким местом, Operator получает короткое сообщение о временном инфраструктурном ограничении, а Admin — точное число свободных endpoint и проблемную location в техническом разделе.

Одна Kraken-нода может иметь несколько непересекающихся public_port_pools. Это позволяет добавлять ёмкость отдельными безопасными блоками, обходя системные listener и диапазоны другой location. Старые public_port_start/public_port_end остаются совместимыми. Controller до запуска проверяет границы и запрещает пересечения диапазонов, использующих один relay host. Перед каждой выдачей свободный endpoint резервируется под lifecycle lock и повторно сверяется с живыми Kraken profile, SQLite assignment и VDS NAT.

Новые relay-ноды должны моделироваться отдельно от Kraken: у каждой свой public host, health, разрешённые диапазоны, applied generation и независимый circuit breaker. Добавление второго relay не должно раздувать цифры в карточке клиента. Для реальной отказоустойчивости он используется как резервный ingress с проверенным маршрутом, а не только как дополнительный список портов. Пока клиентам выдаётся буквальный IPv4, уже выданный endpoint привязан к конкретному relay; бесшовное переключение relay потребует стабильного hostname либо общего floating/anycast IP и отдельного контролируемого этапа миграции.

Стабильность интерфейса

Главная таблица обновляется каждые 10 секунд. Перед автообновлением браузер запоминает ключ первой видимой строки agent_id:modem_id, её координату и горизонтальный scroll. После сортировки свежих данных та же строка возвращается на прежнюю высоту. Ручная смена фильтра или сортировки не восстанавливает старую позицию, потому что пользователь сам запросил новый порядок.

Диалог новой выдачи на desktop имеет отдельную рабочую ширину для таблицы ручного выбора modem. Все семь полей видны без горизонтальной прокрутки; на узком экране строки по-прежнему превращаются в подписанные карточки. Оператор нормализуется на controller и дополнительно в браузере: MTS, MTS RUS, MTS-RUS, МТС и PLMN 25001 являются одним значением МТС во всех фильтрах, рекомендациях и историях.

Расследование жалобы клиента

Поле Прокси → Расследование является редактируемым combobox. При фокусе оно показывает клиентов, при вводе ищет по любой части login и отдельно предлагает найденные proxy-порты. Можно вставить номер порта или полный адрес host:port:login:password: перед запросом он безопасно сокращается до номера порта, а пароль не отправляется как поисковая строка. Стрелки меняют выбранную подсказку, Enter принимает её, Escape закрывает список.

Точное совпадение запускается сразу. Единственное частичное совпадение автоматически превращается в канонический login. При нескольких совпадениях manager обязан выбрать вариант из списка: ModGuard не объединяет истории разных клиентов по догадке. Поиск охватывает активные и удалённые назначения; отсутствие совпадений возвращает отдельный результат «Клиент или порт не найден», а не ошибочный вывод о здоровой работе.

Что реально видел клиент

Backend watchdog проверяет публичный proxy как data plane. Первый сетевой отказ сохраняет подозрение; второй последовательный неавторизационный отказ открывает подтверждённый сегмент недоступности, причём начало ставится на время первого отказа. Успешная проверка, успешный точечный repair или подтверждённая пересадка закрывают сегмент. Ошибка логина не считается поломкой канала и не запускает failover.

Панель «Качество услуги» показывает доступность proxy за 24 часа, число клиентов с подтверждённым простоем, клиентов с недоступными портами прямо сейчас и количество затронутых endpoint. Знаменатель строится только по реально накопленному периоду наблюдения; до появления покрытия интерфейс пишет «Нет покрытия», а не рисует ложные 100%.

Карточка клиента агрегирует доступность его действующих endpoint за 24 часа, 7 и 30 дней и ведёт прямо к затронутым портам. История порта объединяет сегменты доступности, ручные и автоматические пересадки, Kraken read-back, listener repair и результат data-plane проверки. Расследование сначала отвечает на главный вопрос первой линии: был ли подтверждённый отказ именно клиентского proxy. Техническое состояние модема остаётся дополнительным объяснением, а не заменой этого доказательства.

В Park профиль модема строит причинную хронологию «причина → реакция → результат»: сетевой инцидент, reconnect/reboot, пересадка proxy, возвращение связи и переход в карантин показываются одним временным потоком. Это позволяет технику увидеть не только команду, но и какую клиентскую защиту она вызвала и чем закончилась.

Отказы и восстановление данных

СитуацияПоведение и проверка
Один Kraken недоступенSync PARTIAL; данные ноды сохраняются, массового REMOVED нет. Проверить detail последнего sync и доступность API с VDS.
Оба Kraken недоступныМенеджер показывает последний подтверждённый снимок и возраст синхронизации. Мониторинг модемов продолжает работать независимо.
Fernet key не читаетсяController не стартует с manager integration, чтобы не показать повреждённые реквизиты. Проверить owner/mode ключа и отдельную резервную копию.
Пароль не расшифровалсяНазначение остаётся видимым, но secret_errors растёт и копирование неполно. Не удалять БД; восстановить правильный ключ или перечитать профиль из Kraken.
Конфликт authПрофиль не импортируется автоматически. Найти профиль по node и Kraken ID, исправить или явно описать правило; не выбирать клиента по первому login.
БД потеряна, Kraken живСоздать пустую SQLite, вернуть key и node config, запустить read-only sync. Клиенты, credentials, профили и дневные группы восстановятся из API; локальная история ручных действий и удалённых ранее профилей без backup не восстановится.
Kraken и БД потеряныВосстановить ежедневный SQLite backup, отдельный Fernet key, controller.env и agent DB/spool. Wiki описывает модель, но не содержит секреты и не заменяет резервные копии.

Контур операций записи

  1. Operator отправляет запрос с CSRF token и уникальным Idempotency-Key. Повтор успешного запроса возвращает прежний результат и не создаёт второй набор.
  2. Controller сериализует операции записи и непосредственно перед изменением заново читает живые proxy-профили, auth users и список modem выбранных Kraken nodes.
  3. Локальный ID агента не передаётся в Kraken вслепую. ModGuard находит устройство по устойчивой паре node + port-name, требует единственное совпадение и использует свежий Kraken modem ID. Это обязательно: после пересоздания портов локальный ID и REST API ID могут отличаться.
  4. Повторно проверяются monitored, физическое наличие, резерв, ModGuard health, свободный клиентский слот и отсутствие этого же клиента на modem. Kraken не используется как источник диагноза здоровья.
  5. Порт выбирается только из настроенного диапазона location и только если его нет в полном живом списке профилей. Для нового клиента создаётся 16-символьный буквенно-цифровой пароль; существующий auth и его пароль переиспользуются.
  6. Kraken создаёт auto, HTTP или SOCKS-профиль с выбранным maxconn. Controller перечитывает профиль и сверяет ID, порт, modem, login и active, затем запускает синхронизацию и проверяет импорт назначения в центральную БД.
  7. При ошибке удаляются все профили этой операции, затем созданный auth. Если удаление профиля не подтверждено, auth намеренно сохраняется, чтобы не оставить живой proxy с несуществующей авторизацией. Результат получает ROLLED_BACK или ROLLBACK_FAILED.

Удаление управляет отдельными назначениями, а дневная группа остаётся только исторической группировкой. Manager видит все активные порты выбранной выдачи, отмечает один, несколько или «выбрать все», заранее видит число портов, которые останутся работать, и подтверждает действие словом «удалить». Controller получает конкретные assignment_id, проверяет каждый живой Kraken profile до изменения, удаляет выбранные профили официальным API, перечитывает список и только затем архивирует назначения. Неотмеченные порты не затрагиваются. История, endpoint и зашифрованный пароль удалённых строк сохраняются. Если пакет затронул несколько нод и одна из них отказала после успешного удаления на другой, операция честно получает PARTIAL, выполненная часть синхронизируется, а NAT пересобирается по факту.

Восстановление разрешено только для REMOVED. Прежний backend/public port должен быть свободен. ModGuard расшифровывает прежний пароль, переиспользует или создаёт Kraken auth, подбирает работающий modem той же локации, создаёт profile, выполняет read-back и возвращает прежний host:port:login:password. При любой ошибке новый profile удаляется и состояние снова синхронизируется.

Пересадка сохраняет Kraken proxy ID, backend/public port, login, password, тип и maxconn. Controller отправляет полный безопасный payload в официальный /api/proxy/edit/{id}, меняя только modem ID, затем сверяет profile. Ошибка после редактирования запускает обратный edit и возвращает метаданные размещения из снимка до операции.

Изменение лимита соединений доступно из карточки активного proxy и принимает значения от 50 до 2000. ModGuard перечитывает живой профиль, сохраняет endpoint, modem, auth, тип и остальные параметры, меняет только maxconn официальным Kraken API, выполняет read-back и data-plane проверку. При ошибке выполняется обратный edit. Успех записывается событием MAXCONN_CHANGED со старым и новым значением; повтор с тем же idempotency key не меняет профиль второй раз.

Каждая операция имеет отдельный idempotency key, журнал proxy_operations и человеческую хронологию proxy_assignment_events. Долгая пересадка обновляет безопасную фазу выполнения в result_json, поэтому живой процесс отличается от оборванного. После первого полного sync и затем каждые 300 секунд reconciler разбирает RUNNING без прогресса более 15 минут и ROLLBACK_FAILED старше минуты. Он не повторяет старую команду вслепую и не редактирует Kraken: сначала ищет подтверждённое событие этой операции, более новое переключение, затем сверяет текущий assignment, Kraken read-back и фактический data-plane. Итоговые состояния: SUCCEEDED_RECOVERED — событие было записано, но финальный статус потерян; SUPERSEDED — результат уже заменён более новой подтверждённой операцией; RECONCILED — текущее назначение согласовано по всем трём слоям; NEEDS_ATTENTION — обнаружено расхождение; INTERRUPTED — для пакетной операции недостаточно однозначных данных. Обновление терминального состояния выполняется compare-and-swap и пишется в audit. Кнопка интерфейса не является разрешением: capability приходит с controller, поэтому неподключённую операцию нельзя включить только изменением JavaScript.

Публичные реквизиты клиента

Клиент всегда получает endpoint ретранслятора 81.200.147.47:PORT. Прямой IP Kraken или локации считается внутренним backend и не попадает в копирование, TXT или карточку выдачи. В одной строке показывается host:port:login:password; текущие modem, location, оператор и состояние находятся в технической таблице ниже.

Служебные listener 20xxx с общим login rooot доступны на прямых публичных IP соответствующих локаций. На 27.07.2026 широкие диапазоны 20101–20160 и 20161–20300 на VDS-ретрансляторе не опубликованы: их включение требует отдельного подтверждённого firewall-изменения. Поэтому до такого изменения клиентский export через VDS гарантируется для 10xxx, а служебный profile через VDS может не принимать соединение.

Служебный proxy каждого modem

Каждому обнаруженному port-N полагается один локальный служебный 3proxy на порту 20000 + N, например port-120 → 20120. Controller сверяет профили с живым Kraken REST API при каждом proxy-sync. Снимок нового живого modem без служебного профиля будит синхронизацию досрочно; минутный cooldown не даёт серии снимков создать шторм записей в API.

Reconciler только добавляет отсутствующий auto-profile с is_local=true и maxconn=2000. Он не меняет и не удаляет существующие служебные или клиентские профили, не создаёт для 20xxx NAT на ретрансляторе и не считает служебный profile клиентским местом. Если номер занят другим живым profile или API отказал, конфликт сохраняется в журнале, а синхронизация остальной ноды продолжается.

Выдача и выбор модемов

На первом шаге manager выбирает клиента, количество, location кнопками ёмкости, оператора, универсальный SOCKS5/HTTP режим и видимый лимит соединений. Значение maxconn по умолчанию равно 750 для каждого proxy. Новый клиент выбран по умолчанию, чтобы случайно не добавить порты существующему.

Автоматический и ручной режимы используют один список пригодных modem. Исключаются неработающие, резервные, восстанавливаемые и заполненные устройства, а также modem, который этот клиент уже использует. HEALTHY, SELECTIVE_ACCESS_ACTIVE и SELECTIVE_ACCESS_SUSPECTED считаются состояниями с подтверждённой связностью; белые списки не превращают весь парк в несуществующие кандидаты. Отсутствие speed-history само по себе ничего не исключает. Фильтры location и оператора обязательны для обоих режимов. Автоматика ранжирует кандидатов по download, свободным местам, текущим соединениям, числу замеров и отсутствию другой клиентской нагрузки; конкретный список показывается до создания.

Перед обращением к Kraken production backend повторно читает живые данные и выполняет выдачи под глобальной блокировкой записи controller. Уникальный idempotency key защищает от повторной отправки формы, полный список Kraken — от повторного public port, а read-back — от ложного успеха. После create, restore и move controller дополнительно открывает прямой Kraken listener с настоящими client credentials, строит HTTP CONNECT или SOCKS5 tunnel и через TLS обращается к собственному nonce-защищённому /probe/ip. Прямой host берётся из отдельного data_plane_host, а при его отсутствии — из hostname Kraken base_url; публичный public_host ретранслятора для этой проверки не используется, чтобы VDS hairpin не давал ложный отказ. Успех требует ответ именно ModGuard и публичный IPv4. Обычные ошибки завершаются после трёх попыток; только новый listener с Connection refused получает до пяти readiness-попыток. Если трафик не прошёл, операция не выдаётся как успешная и запускает существующий rollback Kraken, БД и NAT.

Ручная пересадка разрешена только внутри location исходного proxy. Список кандидатов содержит только работающие modem со свободным запасом, которых этот клиент ещё не использует; его можно фильтровать по оператору. Cross-location остаётся заблокированным: публичный VDS уже управляется точечно, но backend-порт должен быть гарантированно принят MikroTik другой площадки до снятия этого ограничения.

Вкладка «Модемы» имеет независимые фильтры состояния, location и оператора, а также поиск по номеру порта, modem ID, модели и клиенту. По умолчанию показаны все location и все фактически обнаруженные операторы. После выбора location список операторов пересобирается только из модемов этой площадки; фильтры применяются совместно и не сбрасывают сортировку таблицы.

Контроль переключений

Сводка показывает число переключений клиента не отдельно от размера его парка, а как «затронутые порты / все активные порты» и процент. Нажатие открывает карточку клиента с периодами 24 часа, 7 и 30 дней, раздельными ручными и автоматическими событиями, причинами и точным списком затронутых endpoint. Из списка можно открыть историю конкретного proxy и кнопкой «Назад» вернуться в тот же срез переключений. Поиск клиентов понимает имя, login, endpoint и номер proxy-порта.

Экран замеров скорости

Фактический статус 0.14.0: серверная очередь, одиночный и массовый ручной запуск, расписание и сохранение результатов работают в production. Modem без истории скорости не исключается из выдачи: до трёх успешных замеров действует консервативная базовая ёмкость.

Без выбранных фильтров график показывает дневную медиану download всего парка. Аналитический срез можно последовательно сузить по location, фактически имеющемуся оператору и конкретному modem. Карточки рядом с графиком всегда относятся к текущему срезу и показывают медиану, P25 и число успешных полных замеров. У обоих графиков есть подписанные оси и шкала Мбит/с; наведение показывает значение и размер выборки, нажатие закрепляет подробности. Дневной график дополнительно показывает min–max, почасовой выделяет лучший и худший час, а дни недели считаются по Europe/Moscow.

Планировщик использует только полный Яндекс Интернетометр. Автоматически тестируются только modem без клиентов. Manager может выбрать любое число из 24 часовых слотов по Москве — от одной проверки до режима «каждый час». По умолчанию глобальная очередь допускает до двух тестов одновременно, не больше одного на location, и выдерживает минимум две минуты между стартами с дополнительным jitter. Перед сохранением панель считает тесты на modem и на весь парк, длительность очереди и запас до следующего окна; конфигурация с неизбежным backlog блокируется. Ручной тест занятого modem входит в ту же очередь и требует подтверждения влияния на клиента.

Internetometer запускается на agent Kraken, поэтому скоростной трафик не идёт через controller VDS. Agent получает список CDN-проб из yandex.ru/internet/api/v0/get-probes, привязывает каждый curl к source IPv4 конкретного modem, измеряет latency и jitter, затем восемь секунд параллельно качает полные 50 MB probes с трёх CDN-узлов и восемь секунд выполняет upload. В очередь возвращается только компактный JSON с download, upload, ping, jitter, IP, временем и именами CDN.

Тест не использует клиентский 3proxy, поэтому restart listener сам по себе не обрывает Internetometer. Команды restart выполняются на agent отдельно от других команд. Ротация мобильной сессии конфликтует с замером сильнее: controller не выдаёт speed_test одновременно с rotate_ip, а agent повторяет полный тест один раз, если передача завершилась ошибкой, публичный IP изменился во время окна теста или первый download не превысил 4 Мбит/с. Перед повтором agent ждёт подтверждения доступного публичного IP до 45 секунд. В историю результата сохраняются обе попытки и причина повторения; если оба полных замера показывают низкую скорость, второй считается подтверждённым фактическим результатом.

Ручной диалог ищет по номеру порта, modem ID, модели и оператору, фильтрует по location и сохраняет накопленный выбор при смене поиска или площадки. Можно отметить отдельные строки, выбрать все найденные, очистить выбор и поставить в очередь до 300 modem одним запросом. Свободные и занятые устройства помечены явно; занятость определяется по фактическому числу клиентских профилей в agent snapshot, даже если исторический профиль ещё не сопоставлен с клиентом manager. Наличие хотя бы одного занятого требует общего подтверждения влияния на client traffic. Controller возвращает частичный результат: пригодные тесты ставятся в очередь, а busy/down/invalid строки показываются отдельно и не отменяют всю пачку.

Плановый тест запускается только при нулевом числе client proxy profiles. Массовый ручной запрос также не запускает весь парк одновременно: agent допускает ровно один полный Internetometer на location, а глобальный планировщик соблюдает заданную параллельность и spacing. Расписание хранится в SQLite, использует Europe/Moscow и после перезапуска не теряется. Если modem стал занят после постановки планового задания, agent отменяет тест до передачи тяжёлого трафика.

Каждый запуск сначала сохраняется как QUEUED в speed_test_runs, затем связывается с надёжно доставляемой командой agent. Controller выдаёт каждому agent не более одного speed-test одновременно; остальные задания остаются видимой persistent-очередью в SQLite, а не скрыто ждут внутри потоков agent. Состояние RUNNING ставится только фактически выданному тесту. Успех, ошибка или отмена, контекст FREE/LOADED, число profiles и соединений на момент постановки, все метрики и диагностическая причина сохраняются. Ожидающий тест можно отменить по одному или всей пачкой; уже выданный agent тест не обрывается и безопасно завершает конечный curl. Ожидающее задание старше четырёх часов и выполняющееся дольше 15 минут автоматически переходят в FAILED независимо от того, включено ли расписание. Ошибка или недоступность одного modem не останавливает location: после конечного результата controller выдаёт следующий тест. Production-графики строятся только по сохранённым строкам; демо-профили часов и дней в расчётах не участвуют.

История замеров имеет отдельные фильтры location, оператора и контекста FREE/LOADED плюс поиск по номеру modem. Фильтры истории не меняют аналитический график и наоборот, чтобы оператор мог сравнивать агрегат с другой выборкой сырых запусков.

Фактическая схема ретранслятора

Фактические allocator pools соответствуют подтверждённому ingress каждой площадки. Kraken-1 использует два непересекающихся блока TCP: 10101–10160 и добавленный persistent MikroTik ingress 10301–10999. Kraken-2 использует только подтверждённый блок 10161–10231; адреса 10232–10300 не выдаются до отдельного end-to-end подтверждения маршрута. VDS создаёт точечную desired-привязку каждого активного endpoint к текущему Kraken backend, сохраняет порт, а обратный трафик SNAT-ится адресом ретранслятора.

Размер диапазона не считается товарной ёмкостью и не показывается manager: реальный предел задают пригодные modem, уникальность для клиента и доступные слоты. Широкое расширение вслепую запрещено, поскольку на VDS есть системные listener. Новые блоки добавляются только как явный public_port_pools после проверки MikroTik ingress, VDS DNAT, Kraken listener и внешнего data plane.

Как работает persistent NAT binding

  1. Активное назначение создаёт desired binding public_port → Kraken IP:backend_port в SQLite с уникальными ограничениями на assignment и public port.
  2. Controller атомарно записывает полный snapshot /var/lib/modem-guardian/nat-desired.json, а не отдельную shell-команду.
  3. NAT helper принимает только IPv4 из allowlist и порты 1024–65535, отвергает дубликаты и списки больше 5000 строк.
  4. Helper сначала выполняет nft -c, затем одной транзакцией заменяет только таблицу ip modguard_nat. Остальные nftables/iptables, fail2ban и SNAT не затрагиваются.
  5. Точечная цепочка имеет priority -101 и перекрывает legacy диапазон только для известных активных endpoint. При её отсутствии прежние persistent диапазоны продолжают работать как аварийная подложка.
  6. modguard-nat.service запускает helper после netfilter-persistent при каждом boot. В рабочем режиме controller атомарно записывает snapshot с уникальным generation_id, modguard-nat.path замечает изменение и запускает изолированный oneshot.
  7. Helper работает от непривилегированного пользователя modguard с единственной capability CAP_NET_ADMIN и записывает отдельное подтверждение с тем же generation_id. Controller принимает только совпавшее подтверждение, поэтому запоздалый результат прошлой операции не считается успехом. root, sudo и shell из веб-процесса не используются; NoNewPrivileges=true остаётся включённым.

Состояния PENDING/APPLIED/ERROR и последняя ошибка хранятся в proxy_nat_bindings. Create и restore считаются завершёнными только после успешного применения NAT; локальная move сохраняет endpoint и поэтому при недоступности helper остаётся рабочей, но показывает ошибку NAT для оператора. Одноразовые команды iptables без desired snapshot не считаются управляемым правилом.

UI не получает root shell. Privileged helper принимает только типизированные операции, backend ограничен allowlist зарегистрированных Kraken/location, изменения сериализуются и после каждого шага проверяются. Миграция выполняется по одному endpoint поверх существующих legacy ranges; посторонние filter, fail2ban и NAT-правила не очищаются.

MikroTik

Для текущих same-location операций MikroTik не меняется: каждый разрешённый allocator pool уже постоянно проброшен на свою площадку, а create/delete/restore/move работают только внутри подтверждённого диапазона location. RouterOS API/SSH не открывается в интернет. До включения cross-location требуется read-only аудит обеих площадок, отдельный ограниченный RouterOS user и read-back конкретного правила; без этого ModGuard намеренно отклоняет перенос на другую ноду.

Временная аварийная пересадка

Режим STOPPED запрещает новые автоматические перемещения, но оставляет мониторинг и ручное управление. OBSERVE раз в минуту строит списки кандидатов без изменений. ACTIVE разрешает same-location failover после подтверждённого инцидента длительностью не менее 600 секунд.

Для безопасного canary переменная MODGUARD_PROXY_AUTOMATION_ALLOWLIST_JSON может содержать массив конкретных assignment_id. Когда переменная отсутствует или пуста, область не ограничена; пустой JSON-массив [] запрещает автоматике обрабатывать любые назначения. Непустой массив допускает в candidate и failback только перечисленные назначения, поэтому включение ACTIVE для технического proxy не способно переместить остальных production-клиентов. Это ограничение применяется поверх индивидуального automation_enabled, maintenance allowlist, cooldown, rate limit и outage guard.

Быстрый контроль клиентского data-plane

Modem может иметь подтверждённый интернет, пока отдельный процесс 3proxy, его DNS или входной listener уже не пропускают клиентский трафик. Поэтому при включённой data-plane verification controller пакетно проверяет реальные backend endpoint всех активных assignment; непустой production allowlist может временно сузить scope для canary, пустой список полностью запрещает автоматику. После завершения полного пакета controller ждёт минуту и начинает следующий. Пакет не размазывается по минуте и никогда не перекрывается со следующим. Обычный bounded-пул настраивается через MODGUARD_PROXY_DATA_PLANE_WATCHDOG_WORKERS, production-default — 24 I/O workers. Один probe имеет жёсткие сетевые timeout: до пяти секунд на собственный nonce-защищённый ModGuard endpoint и до шести секунд на региональный fallback Яндекс Интернетометра, поэтому один зависший modem не способен остановить весь обход навсегда.

Первая ошибка получает DATA_PLANE_SUSPECTED, не вызывает действий и ставит только этот assignment в дедуплицированную приоритетную очередь. Через 30 секунд, независимо от полного обхода, один из четырёх отдельных workers перечитывает свежее назначение из БД и повторяет data-plane probe. Успех снимает подозрение: обычная ротация длительностью 20–30 секунд не вызывает restart или пересадку. Повторная ошибка проходит через тот же единственный automation lock, что и обычный цикл: точечный reload текущего 3proxy config ждёт не более 30 секунд и сразу проверяется трафиком. Успех записывается как DATA_PLANE_REPAIRED; если reload не помог, причина PROXY_DATA_PLANE_DOWN в том же цикле передаётся same-location failover. Ожидающая или выполняющаяся reconnect-команда не отключает эту защиту: 30-секундная перепроверка уже является допустимым окном штатной ротации, а более долгий отказ обслуживается как реальная недоступность клиента. Настройки очереди: MODGUARD_PROXY_DATA_PLANE_PRIORITY_RECHECK_SECONDS и MODGUARD_PROXY_DATA_PLANE_PRIORITY_WORKERS. Повтор одного assignment не дублируется, устаревшая задача после ручной или автоматической пересадки отбрасывается по identity назначения.

Подтверждённый одиночный PROXY_DATA_PLANE_DOWN не ждёт освобождения общего часового бюджета обычных пересадок: иначе здоровые возвраты или другие modem-инциденты способны оставить уже доказанно неработающий клиентский endpoint без помощи. Этот аварийный допуск не ослабляет location/common-cause guard, same-location, assignment lock, capacity reservation, Kraken read-back, обязательный listener/data-plane verify и rollback. Для защиты от бесконечного перемещения одного нестабильного proxy действует собственный предел по assignment, по умолчанию три аварийные data-plane пересадки за скользящий час; после него порт остаётся в журнале как персонально ограниченный. Предел задаёт MODGUARD_PROXY_DATA_PLANE_EMERGENCY_MOVES_PER_HOUR. Обычные modem-failover и failback по-прежнему используют центральный предел, сейчас 12 в час.

Пересадка не считается успешной по записи Kraken. Controller выполняет read-back, запускает target-listener, удаляет либо безопасно изолирует source-listener и немедленно пропускает настоящий HTTP/SOCKS-трафик через неизменившийся клиентский endpoint. Только подтверждённый data plane закрывает простой и записывает AUTO_MOVED; ошибка запускает полный rollback. Дополнительная фиксированная пауза 5–10 секунд не используется: agent сообщает готовность конкретного listener, после чего проверка начинается сразу.

Watchdog не путает пароль с отказом канала: ошибка аутентификации получает отдельное событие и не запускает restart или пересадку. Одновременный отказ минимум трёх либо не менее 20% проверяемых assignment одной location включает data-plane common-cause guard и запрещает каскад действий. Проверка ограничена тем же assignment allowlist, а move сохраняет capacity reservation, Kraken read-back, точечный listener reload, NAT reconcile, внешний data-plane verify, rollback и общий rate limit. Публичный relay VDS дополнительно контролируется независимой внешней проверкой: controller не использует небезопасное hairpin-правило к собственному белому IP.

Автоматическая пересадка сохраняет home modem, получает placement_state=FAILOVER, причину возврата и полностью занимает клиентский слот target. Для maintenance-ротации отдельная очередь возврата допускает proxy через пять минут после завершения операции, только если появился свежий здоровый снимок и IP home modem отличается от адреса до reboot. Для recovery maintenance и обычного отказа требуется десять минут стабильности. Эти возвраты имеют собственный часовой бюджет, обрабатываются не более чем по четыре assignment за один цикл и не занимают аварийный бюджет failover. Обычный повторный failover по-прежнему защищён cooldown 1800 секунд. Срок 72 часа является не задержкой перед возвратом, а максимальным окном попыток. Неудачный возврат, включая провал data-plane проверки, полностью откатывается и получает persistent backoff 2, 4, 8, затем максимум 12 часов.

Обслуживание занятого modem перед reboot

Плановый reboot для смены IP и аварийное восстановление нельзя выполнять как независимую от клиентов команду. Начиная с 0.15.0 controller создаёт modem_maintenance_operations вместо немедленного rotate_ip_reboot, если на source modem найден хотя бы один клиентский profile. Мягкий reconnect остаётся быстрым отдельным действием; gate применяется к destructive reboot, который гарантированно создаёт заметный клиенту перерыв.

Занятость вычисляется по максимуму двух независимых наблюдений: свежего agent-разбора фактических modem-id-*.cfg и активных assignment ModGuard, сопоставленных по node + modem name. Локальный modem ID и Kraken config ID не смешиваются. В список служебных login входит только явно настроенный rooot; реальный клиент не может быть объявлен служебным ради исключения из статистики. Ошибка конфигурации, при которой deslamer был внесён в этот список на двух прежних agent, устранена в шаблоне и production-конфигурации.

Числовые идентификаторы modem принадлежат разным пространствам и не обязаны совпадать. Например, Kraken modem_id=49 может описывать port-144, который agent хранит как локальный modem_id=44. Канонический crosswalk строится только по node + port-xxx; Kraken ID используется лишь для официального REST API, а локальный ID — для agent-команд и инцидентов. Если имя отсутствует, ModGuard показывает связь как неопределённую и запрещает управляющее предположение вместо подстановки случайно совпавшего числа.

Режим задаётся MODGUARD_MAINTENANCE_HANDOFF_MODE=off|observe|active. off не запускает planner. observe является production-default первого этапа: он каждые пять секунд рассматривает накопленные intent, рассчитывает полный план, сохраняет причины блокировки и ничего не записывает в Kraken, NAT или command queue. active дополнительно требует включённый автоматический failback; без него состояние становится BLOCKED_FAILBACK, чтобы временное размещение не превратилось в постоянное незаметно.

Planner решает всю задачу расселения modem одновременно, а не выбирает target для каждого proxy независимо. Жёсткие ограничения: только та же location, source исключён, число мест не превышено, один клиент не получает второй обычный proxy на том же modem, target здоров и имеет подтверждённый публичный IPv4. Мягкая стоимость предпочитает того же оператора, большую измеренную download-скорость, меньшую загрузку, меньше активных соединений и равномерное заполнение. Если для всех клиентов нет полного решения, ни один не перемещается, reboot не ставится, операция получает BLOCKED_CAPACITY.

После полного плана каждый target-slot атомарно резервируется в proxy_capacity_reservations. Ручная выдача, ручная пересадка и соседний maintenance видят эти резервы, поэтому параллельные действия не могут обещать одно место двум клиентам. Все assignment source modem блокируются в стабильном порядке. Kraken edit выполняется пакетно, затем одним read-back подтверждается каждое соответствие profile → target. ModGuard синхронизирует назначения и NAT, обязательно точечно перезапускает каждый уникальный target, затем каждый уникальный source config не более одного раза на пакет и только после этого параллельно проверяет каждый неизменившийся публичный endpoint клиента. Первый удачный IP-check до reload не считается доказательством: при SO_REUSEPORT старый и новый процессы способны одновременно принимать соединения. Только состояние HANDOFF_VERIFIED разрешает поставить reboot агенту.

Перед планированием controller повторно проверяет, сохранилась ли причина destructive-действия. Для recovery операция отменяется до handoff, если свежий agent snapshot уже имеет HEALTHY. Для страховочной ротации операция отменяется, если текущий публичный IP уже отличается от адреса, из-за которого был запрошен reboot. Это правило действует и на заявки вне текущего active allowlist: последующее расширение scope не может оживить устаревший PENDING intent и вызвать ненужную пересадку. Отмена сохраняется как CANCELLED с причиной, ожидаемым и фактическим IP; Kraken, NAT, capacity reservations и agent command queue при этом не изменяются. Уже начатые RESERVED/EVACUATING/HANDOFF_VERIFIED операции не отменяются этой быстрой проверкой, чтобы не оставить клиентов на временных target без штатного завершения и failback.

Непосредственно перед destructive-командой controller повторно читает live profiles: source обязан иметь ноль клиентских profiles, а каждое назначение обязано оставаться на своём зарезервированном target. Это закрывает гонку «modem был пуст при планировании, но получил клиента перед reboot». Любая ошибка edit, read-back, NAT или data-plane вызывает обратное изменение всех уже затронутых profiles, возврат строк assignment, повторную сверку и освобождение резервов. Частичный успешный handoff не считается разрешением на reboot.

Основные состояния: PENDING — intent принят; OBSERVED — полный безопасный план рассчитан без записи; CANCELLED — причина исчезла до начала handoff; BLOCKED_MAPPING — agent видит больше клиентов, чем ModGuard способен однозначно сопоставить; BLOCKED_CAPACITY — не хватает безопасных target-slots; RESERVED — все места закреплены; EVACUATING — идёт пакетная пересадка; HANDOFF_VERIFIED — все endpoint подтверждены; ACTION_QUEUED — reboot передан надёжной agent queue; COMPLETED или FAILED — конечный результат. История пересадки получает событие MAINTENANCE_MOVED с maintenance ID, причиной, source, target и методом data-plane подтверждения.

Единый контроллер действий над modem

Регулярный data-plane обход, failback и maintenance больше не запускаются двумя независимыми фоновыми циклами. Их последовательно вызывает один поток proxy-manager-control. Срочная 30-секундная перепроверка остаётся отдельным I/O worker, чтобы не ждать минутного расписания, но любое изменение она передаёт тому же run_automation_cycle, общему automation lock, assignment lock и валидатору ёмкости. Приоритет фиксирован: сначала подтверждение и восстановление клиентского endpoint, затем эвакуация перед ротацией или recovery, затем автоматический возврат на home modem.

Пока data-plane suspect ожидает или проходит срочную перепроверку либо занят automation lock, maintenance не начинает новую эвакуацию и возвращает DEFERRED_CLIENT_RECOVERY. После завершения срочного решения maintenance снова допускается: неисправный endpoint без свободной ёмкости не способен навсегда заблокировать recovery остальных modem. Уже подтверждённая пакетная эвакуация не бросается посередине: её assignment locks и persistent capacity reservations позволяют безопасно завершить начатую фазу. Мягкий reconnect не переселяет клиентов превентивно, потому что обычно укладывается в 20–30 секунд; если трафик не вернулся к контрольной проверке, обычный failover включается без специального исключения. Reboot и router reboot при наличии клиентов по-прежнему разрешены только после полного maintenance handoff.

Ротация IP, восстановление modem и защита клиентского proxy используют один арбитр и один набор правил размещения. Отдельные циклы продолжают обнаруживать разные сигналы, но не принимают независимых destructive-решений. Сигнал превращается в intent, после чего центральный controller проверяет владельца modem, действующую maintenance operation, активную modem-команду, окно планового действия, клиентские назначения, общий сетевой отказ location и пригодность каждой возможной цели. Watchdog видит это же состояние и не запускает параллельную пересадку поверх уже выполняющейся ротации или recovery.

  1. Мягкий reconnect остаётся быстрым действием. Он сериализуется с другими командами этого modem, но не эвакуирует клиентов, потому что измеренный короткий перерыв обычно меньше двух последовательных переключений listener.
  2. Любой запланированный reboot сначала создаёт одну modem_maintenance_operation. Controller одновременно размещает всех клиентов source modem, атомарно резервирует ёмкость, выполняет Kraken handoff, подтверждает target listener и настоящий клиентский data plane. Только после этого reboot разрешается agent.
  3. Подтверждённый отказ отдельного клиентского endpoint входит в тот же automation lock. Последовательность едина: адресная перепроверка через 30 секунд, точечный reload текущего listener, немедленная проверка, затем same-location failover и повторная проверка.
  4. Ручная выдача, ручная пересадка, восстановление архивной выдачи, аварийный failover, failback и maintenance используют общий eligibility gate. Цель обязана быть AVAILABLE, иметь свободный слот, не находиться в карантине и не иметь активной технической блокировки.
  5. Если Kraken не создаёт обязательный target modem-id-N.cfg после шести адресных попыток, либо 3proxy не подтверждает владение нужным listener, modem получает persistent-состояние BLOCKED «Временно не выдавать». Это не диагноз неисправности модема, а защита от выдачи клиенту нематериализовавшегося proxy. Исторические интервалы 30 минут, 2 часа и 12 часов сохраняются в audit как уровни повторной ошибки, но истечение срока больше не снимает запрет. Modem исключается из новых размещений, но остаётся доступен для диагностики и speed test. Каждый proxy-sync перепроверяет живой Kraken config ID, фактический LISTEN служебного и всех назначенных клиентских портов, а затем пропускает реальный HTTPS-трафик через служебный proxy. Только все эти условия вместе автоматически снимают блокировку; ошибка одного modem не останавливает синхронизацию ноды.
  6. Для Huawei есть дополнительный динамический gate: агент обязан подтвердить доступ к HiLink API. Если трафик уже идёт, но API временно недоступен, текущее назначение не разрушается, но modem не участвует в новой выдаче, failover, maintenance-эвакуации и failback. Запрет снимается автоматически по следующему снимку, где identity_device_api=true; ручного карантина не требуется.
  7. Отказ первой выбранной цели не завершает аварийное восстановление клиента. В том же цикле planner пересчитывает фактическую ёмкость и пробует следующую подходящую цель. Уже успешные размещения учитываются немедленно, поэтому параллельные инциденты не переселяют больше клиентов, чем допускает квота.
  8. Каждый переход сохраняется в operation, maintenance, assignment events и audit. По этим журналам можно восстановить сигнал, решение арбитра, выбранную цель, Kraken read-back, listener-команды, data-plane результат, rollback и окончательный статус.

Это один центр принятия решений, но не один тяжёлый последовательный worker. Независимые сетевые проверки выполняются bounded-параллельно и имеют timeout; запись конфигурации конкретного assignment и destructive-действия конкретного modem сериализуются. Такое разделение сохраняет оперативность I/O без гонок между ротацией, recovery и proxy lifecycle.

Защита от массового сетевого события

Controller-side location outage guard независимо смотрит на фактическое здоровье всего modem-парка, а не только на клиентские назначения. Для каждой location учитываются активные, отслеживаемые и не находящиеся в карантине modem. Guard включается, если одновременно отказали минимум три modem и не менее 25% парка, а ошибки затронули минимум двух операторов. При отказе 60% и более распознанный оператор не требуется: это защищает от неполных identity-данных. К common-cause относятся UPSTREAM_TIMEOUT, недоступность API modem, отсутствие маршрута к modem и недоступность router.

Пока guard активен, новые автоматические failover и failback на этой location не выполняются. Ручное управление, мониторинг, история и data-plane диагностика остаются доступны. Состояние видно в блоке автоматики; начало, изменение затронутых location и завершение пишутся как PROXY_OUTAGE_GUARD_STARTED/CHANGED/ENDED. Режим белых списков имеет отдельный код SELECTIVE_ACCESS_ACTIVE, не считается отказом и не запускает recovery. Agent-side fleet circuit breaker дополнительно не даёт массово перезагружать modem при общем upstream-событии.

Перезапуск 3proxy и обязательные listener

Плановый или точечный restart считается успешным только если новый процесс владеет каждым TCP LISTEN-портом из своего modem-id-*.cfg. Проверка выполняется по socket inode из /proc/net/tcp* и файловым дескрипторам конкретного PID; простого существования процесса недостаточно. Если хотя бы один listener не поднялся за две секунды, неполный процесс завершается, а команда возвращает список отсутствующих портов.

Имя Kraken-конфига и локальный modem ID не взаимозаменяемы. Например, локальный port-101 может иметь ID 1, а обслуживаться файлом modem-id-6.cfg. При move controller использует local ID только для здоровья и ожидаемого публичного IP, а точечному restart передаёт свежий Kraken modem/config ID из REST read-back. Сначала полностью поднимается target config с новым назначением, затем перезапускается source config и удаляется его устаревший listener. Такой порядок оставляет краткое безопасное перекрытие вместо окна без listener и уничтожает orphan-процессы прежнего supervisor. Оба шага обязательны после каждой пересадки, даже если первый случайный probe уже показал IP target. При rollback порядок зеркалируется: сначала возвращается исходный source listener, затем очищается бывший target.

Если Kraken сохранил proxy-профиль и старый процесс, но удалил исходный файл конфига, source restart не подменяется догадкой и не запускает произвольный процесс. Controller использует guarded orphan handoff только после точного доказательства replacement: актуальный target-файл существует, ровно один процесс Supervisor владеет клиентским backend-портом, а orphan найден по cmdline отсутствующего source-файла и сам ещё слушает этот порт. Agent атомарно сохраняет PID и параметры в /run/modguard/proxy-orphan-handoffs, ставит orphan на обратимую паузу, после чего controller выполняет внешний probe с числом попыток до пяти. Ошибка вызывает resume; успех переводит процесс в drain. Если controller исчез до решения, agent через пять минут автоматически возобновляет незавершённый paused source как безопасный uptime fallback. Для подтверждённого drain фоновый reaper ждёт не менее 60 секунд, сверяет, что PID всё ещё относится к тому же source cmdline, replacement не исчез и активных TCP-сессий нет, и только затем отправляет SIGTERM. Жёсткий kill в этом протоколе не применяется.

Точечный и полный проход используют один flock. Если fleet restart уже стоит в persistent command queue, точечная команда объединяется с ним и ждёт его подтверждения вместо создания конкурирующего процесса. Если lock удерживает внешний или уже запущенный проход, agent ограниченно ждёт до 330 секунд и каждые 100 мс проверяет именно нужный config: как только новый PID владеет всеми ожидаемыми listener, точечная команда завершается как coalesced, даже если fleet продолжает остальные config. Controller ждёт подтверждение до 360 секунд; незавершённые restart-команды старше 10 минут переводятся в конечную ошибку. Плановый fleet restart не ставится, пока ожидает любая точечная restart-команда.

Периодический fleet restart является аварийным административным инструментом, а не штатным обслуживанием. Каждый проход заменяет все процессы и обрывает открытые клиентские TCP-сессии, даже если новый listener поднимается менее чем за секунду. Расследование 27.07.2026 подтвердило 46 полных проходов Kraken-1 с интервалом 15 минут и одновременный PROBE_ERROR на четырёх клиентских портах во время первого прохода. Поэтому настройка выключена на обеих production-нодах и должна оставаться выключенной по умолчанию. Lifecycle использует точечный restart только для изменённого Kraken config.

Agent отдельно поддерживает инвариант «один config — один принимающий процесс». При включённом proxy_listener_reconcile_enabled фоновый reconciler небольшими партиями ищет исторические transient PID, но действует только когда один Supervisor-процесс уже устойчиво владеет всеми портами config. Старый PID штатно прекращает accept, существующие TCP-сессии дренируются, затем процесс завершается без SIGKILL. Краткое исчезновение socket не считается pause: отсутствие listener должно быть устойчивым 0,75 секунды, иначе выполняется второй parity-toggle. Reconciler использует общий restart-lock и при занятом lock пропускает цикл. Он не меняет Kraken, modem, assignment или NAT и потому является локальным исполнением инварианта, а не вторым центром принятия решений.

Локальный inventory agent перечитывает все /etc/3proxy/modem-id-*.cfg заново перед каждым снимком. Кэш разрешён только внутри одного снимка, чтобы один проход видел согласованный набор файлов. Между проходами кэш обязательно сбрасывается: Kraken меняет состав listener после выдачи, удаления, пересадки и изменения maxconn. Дефект до 0.17.37 сохранял список портов с момента запуска agent, поэтому controller мог видеть старого владельца listener, неверно считать занятость modem и не снимать техническую блокировку, хотя фактический config уже был исправен.

Все операции, меняющие Kraken profile или применяющие 3proxy config, получают единый lease соответствующей ноды. В этот контур входят выдача, удаление, восстановление, пересадка, изменение maxconn, maintenance-handoff и восстановление служебного listener. Внутри ноды эти операции не могут наложиться друг на друга; Kraken-1 и Kraken-2 продолжают работать независимо. Решение принимает controller, REST API Kraken остаётся единственным владельцем желаемой конфигурации, agent является единственным исполнителем точечного reload, а Supervisor — единственным постоянным владельцем процесса.

При включённом MODGUARD_SERVICE_PROXY_RECONCILE_ENABLED controller сверяет служебный профиль 20000 + номер port с новым агентским снимком. Если профиль отсутствует, он создаётся официальным Kraken API. Если профиль есть, но config или listener не материализован, controller повторно сохраняет только этот служебный профиль через API, ждёт появления полного config, точечно reload-ит один Supervisor-процесс и требует все клиентские и служебные listener этого config. Завершение подтверждается HTTPS-запросом через служебный proxy. За один sync ремонтируется не более одного config на ноду; ошибка ставит persistent placement-блокировку, а успешная end-to-end проверка снимает её. Полный fleet restart для этого механизма не используется.

Linux не должен использовать публичные и служебные proxy-порты как случайные исходящие порты. Установщик agent сохраняет net.ipv4.ip_local_reserved_ports=10000-20999 в /etc/sysctl.d/70-modguard-proxy-ports.conf, объединяя диапазон с уже существующей системной резервацией. Проверка после установки: sysctl net.ipv4.ip_local_reserved_ports. Это устраняет наблюдавшийся EADDRINUSE, когда другой 3proxy временно занимал будущий listener исходящим HTTPS-сокетом.

Для Kraken-конфига постоянным владельцем 3proxy является штатный Supervisor. ModGuard не оставляет второй постоянный процесс и не запускает 3proxy дочерним процессом agent. Во время точечного reload сначала через systemd-run поднимается временный bridge с новым файлом, затем Supervisor-процесс штатной парой сигналов SIGCONT переходит в pause и continue с перечитыванием конфига. Только после устойчивого подтверждения всех Supervisor-listener bridge прекращает принимать новые соединения; уже принятые сессии получают короткий drain, после чего transient service завершается. Если Supervisor-процесс отсутствует, ModGuard сначала запускает штатную программу modem-N и лишь затем выводит старый transient из приёма.

Reconciler различает владельцев по Linux cgroup, а не по PID или времени запуска. Не-Supervisor процесс разрешено останавливать только при наличии одного полного Supervisor-listener, который владеет всеми ожидаемыми портами. В 3proxy 0.9.5 SIGCONT является переключателем pause/continue, поэтому ModGuard не предполагает заранее текущее внутреннее состояние процесса. После каждого сигнала он проверяет socket inode конкретного PID; если listener не исчез, разрешена одна повторная попытка. Отсутствие подтверждения после двух попыток останавливает операцию без SIGTERM. Подтверждённо paused-процесс сохраняет уже принятые соединения и получает SIGTERM только после их исчезновения. Если сессии ещё есть, он остаётся в состоянии drain без listener и больше не участвует в распределении новых соединений. Supervisor и его 3proxy на обеих production-нодах подтверждены с LimitNOFILE=1000000.

Текущий data plane использует Linux SO_REUSEPORT. Bridge убирает окно без готового listener, но ядро не гарантирует безошибочное закрытие одного socket для SYN, уже назначенного этому socket. Нагрузочные canary на свободных port-160 и port-171 дали по одному мгновенному reset на 200 последовательных HTTPS-соединений во время окончательного снятия старого listener; остальные 398 запросов прошли с неизменным мобильным IP. Это намного безопаснее hard restart и исключает длительное расщепление трафика между старым и новым modem, но не является математическим zero-drop. Для строгого seamless SLA потребуется отдельный стабильный TCP frontend с двумя независимыми backend-listener и drain-aware переключением.

Контрольный canary после релиза

Скрипт scripts/modguard_proxy_lifecycle_canary.py проверяет живой контур на двух свободных работающих modem одной location: создаёт временного клиента и proxy, подтверждает выходной мобильный IPv4 через публичный endpoint VDS, пересаживает proxy с сохранением endpoint/login/password, удаляет его, восстанавливает с теми же реквизитами, повторно проверяет мобильный IPv4 и всегда выполняет финальную очистку. Проверка использует до четырёх попыток по 20 секунд с паузой 5 секунд, поскольку сразу после смены modem мобильная сессия может кратко перестраиваться.

Успешный production-canary проходит стадии CREATE → MOVE → DELETE → RESTORE → CLEANUP и на каждой активной стадии сверяет credentials отдельным API и фактический мобильный IPv4. Встроенная data-plane проверка использует прямой listener Kraken и поэтому не зависит от VDS hairpin; внешний canary дополнительно доказывает клиентский DNAT через публичный ретранслятор. После cleanup временный клиент помещается в серверный архив, а затем обязательно сверяются active assignments, APPLIED NAT bindings, число правил nft и отсутствие активного canary-клиента. История прогона сохраняется для расследования, но не засоряет рабочий список клиентов.

Проверка выхода в интернет использует два последовательных HTTPS probe в рамках одной попытки. Основной modguard.duckdns.org/probe/ip требует правильный service identity и одноразовый nonce. Если собственный endpoint недоступен по сети во время белых списков, verifier обращается к API Яндекс Интернетометра. Оба ответа обязаны содержать валидный публичный IPv4. При пересадке Kraken read-back должен указывать целевой modem, точечный restart должен подтвердить клиентский listener в конфиге этого modem, а сам proxy обязан провести живой HTTPS-запрос. Совпадение публичного IP со снимком modem сохраняется как диагностика, но не считается обязательным: операторский CGNAT может назначить разные адреса двум соединениям одного modem. Если listener целевого конфига не подтверждён, остаётся строгая проверка совпадения со свежим снимком. Connection refused самого listener не маскируется региональным fallback. В audit сохраняется имя сработавшего probe, но не proxy password.

19. Добавление ноды или нового типа модема

Новая нода

  1. Admin создаёт одноразовый enrollment token в разделе «Ноды».
  2. Панель формирует готовую install-команду с токеном.
  3. Установщик скачивает пакет с этого controller, проверяет SHA-256 и создаёт systemd service.
  4. Сначала нода работает в режиме наблюдения; recovery и rotation включаются только с allowlist и явным acknowledgement.

Новый модем

  1. Добавить read-only идентификацию и тестовые ответы.
  2. Отдельно реализовать reconnect, reboot и проверку результата.
  3. Проверить на свободном порту, не на всём парке.
  4. Добавить recovery driver и rotation driver только после измеренного успешного теста.
  5. Описать модель, команды, тайминги и ограничения в этой Wiki.

20. История изменений Wiki и поведения

17.09.2026 · 0.18.8

Точный живой владелец durable MOVE может публиковать canonical CURRENT READY через более старую пустую заявку PENDING на recovery reboot исходного modem без команды, старта и capacity reservation. После точного завершённого rollback восстановленный source остаётся CURRENT READY при следующих полных sync, пока эта же старая заявка не запущена; durable identity должна совпасть с assignment, source, backend, публичным endpoint и fingerprint credentials. Новая заявка, target, запущенное обслуживание, резервирование и чужой modem lease по-прежнему закрывают rebase. Kraken/NAT и клиентские реквизиты не меняются этим правилом.

14.09.2026 · 0.18.7

Priority recheck data-plane watchdog больше не может неограниченно копить одинаковые queue tickets для одного assignment при занятом worker. Для assignment сохраняется не более одного active/queued ticket и одного deferred retry deadline; после завершения worker переносит только этот один retry. Telemetry показывает отдельные счётчики scheduled, deferred, ready и active; успешное evidence или штатная отмена очищают deferred retry.

13.09.2026 · 0.18.6

Maintenance allowlist со значением * теперь означает все modem соответствующей ноды только в режиме ACTIVE. В OFF, OBSERVE и вне literal allowlist recovery event не создаёт executable maintenance owner, поэтому не блокирует чужую выдачу.

Source health для watchdog и fleet guard определяется по единственному свежему agent evidence config_id → local_modem_id, а не по устаревшему display name Kraken. Exact LOCAL_MODEM_MAPPING_MISSING с единственным unhealthy source попадает в существующую bounded same-node recovery lane; duplicate, stale или ambiguous config evidence остаются fail-closed.

Перед controller rollout дополнительно фиксируются MemoryCurrent, MemoryMax, memory.events и NRestarts. Этот релиз не меняет cgroup limits: OOM diagnosis требует отдельного причинного evidence.

13.09.2026 · 0.18.5

Same-node MOVE rollback больше не становится terminal ROLLED_BACK только по Kraken source read-back и listener receipt. До terminal state нужен fresh exact source CURRENT READY под живым durable rollback claim.

PARTIAL sync, missing/ambiguous mapping или FOUNDATION_CURRENT_TOPOLOGY_CONFLICT сохраняют operation replayable: reconciler освобождает только её exact transient claim/leases, затем продолжает тот же journal без повторного Kraken edit. NAT, placement, agent protocol и cross-location policy не меняются.

Пятисекундный PASS target qualification остаётся предварительным: если modem стал unhealthy до authoritative MOVE preflight, selection CAS освобождается, exact target получает existing durable backoff, а parent ждёт bounded replay без Kraken/NAT write и без выдачи того же modem второму клиенту.

12.09.2026 · 0.18.4

Automatic targeted recovery теперь привязывает durable WAITING_TARGET_QUALIFICATION parent к exact сохранённому qualification batch. Новый heartbeat/snapshot не создаёт второй batch: следующий replay использует тот же свежий QUALIFIED PASS и atomically claim-ит одного кандидата для existing MOVE.

Истёкший EXPIRED batch terminalizes parent clean без requeue и без lease/child; отсутствующий, подменённый или с несовпадающей identity batch становится typed NEEDS_ATTENTION. Никакие Kraken/NAT/cross-location policy или agent protocol этим исправлением не меняются.

12.09.2026 · 0.18.3

Same-node MOVE больше не остаётся в WAITING_NAT только из-за устаревшего helper status. Неизменный route допускается лишь когда ModGuard видит exact applied generation, canonical desired binding, единственный CURRENT READY и совпадающий public/backend tuple; terminal success по-прежнему требует отдельного повторного public data-plane proof.

Durable parent OPERATOR_TARGETED_RECOVERY сохраняет связь с live child OPERATOR_TARGETED_RECOVERY_MOVE и принимает только его terminal outcome. Если ни normal NAT acknowledgement, ни exact unchanged-route proof не получены за 180 секунд, child получает NEEDS_ATTENTION и освобождает только свои fenced modem leases без Kraken/NAT write.

Targeted recovery для ACTIVE assignment с LOCAL_MODEM_MAPPING_MISSING теперь может создать durable child REMATERIALIZE_ASSIGNMENT_PROFILE только после двух разных свежих snapshot/full-sync и immediate Kraken read-back отсутствующего exact profile. Intent сохраняется до POST; lost response допускает только exact read-back adoption без второго POST. Success требует CURRENT READY, unchanged APPLIED NAT и public proof; rollback удаляет только operation-created profile и не трогает shared auth.

12.09.2026 · 0.18.2

Добавлен default-OFF automatic same-node recovery для отдельных assignment из отдельного allowlist. Admission требует два разнесённых во времени PUBLIC_PROBE_FAILED, exact неизменную assignment identity, свежее завершённое full sync, CURRENT READY, NAT APPLIED и отсутствие конфликтующего owner.

Одна атомарная DB-транзакция фиксирует evidence generation и durable parent. Controller restart или новый watchdog probe продолжает тот же idempotency key; одновременно исполняется не более одного automatic parent. Credentials/disabled verifier не считаются outage, stale identity изолируется без остановки unrelated watchdog.

После admission используется прежний operator recovery: bounded five-second same-node qualification, CAS одного PASS target, existing lease/fencing, target backoff и terminal CURRENT/NAT/data-plane barriers. Общий лимит остаётся три MOVE за пять минут и возвращает ATTEMPT_BUDGET_EXHAUSTED. Cross-location policy, Kraken/NAT payload и agent protocol не менялись.

11.09.2026 · 0.18.1

Добавлено точечное operator recovery одного ACTIVE proxy. Оператор передаёт только assignment ID: controller сначала требует две неуспешные проверки текущего endpoint, затем запускает same-node пятисекундную qualification и atomically claim-ит один свежий PASS target. Global automation и cross-location modes не меняются.

Child использует существующий durable MOVE с теми же lease, resource lock, attempt budget, Kraken/listener/CURRENT/NAT/data-plane barriers. Повтор того же idempotency key продолжает тот же lifecycle; BUSY не превращается в обход fencing и ограничен durable 180-секундным cutoff.

Перед qualification controller заново читает durable assignment и свежий agent snapshot: обычный heartbeat между двумя outage probes получает один bounded повтор admission без нового MOVE. Реальная смена identity и повторяющийся snapshot conflict остаются fail-closed; terminal result сохраняет typed nested причину.

Новый RUNNING sync не скрывает последнее свежее завершённое OK поколение: exact sync ID и полный набор nodes повторно проверяются внутри DB transaction. Более новый завершённый PARTIAL, truncated/conflicted или stale sync блокирует qualification.

Пятиминутный move budget считается по assignment и не обнуляется сменой modem identity. Повторный public failure после успешного MOVE поэтому ограничен тремя consumed попытками и затем получает ATTEMPT_BUDGET_EXHAUSTED с retry_after.

11.09.2026 · 0.17.99

Живой Kraken profile больше не остаётся скрытым как REMOVED/SYNC_MISSING только из-за stale derived CURRENT после уже завершённой пересадки. Fresh OK 2/2 sync может атомарно rebase CURRENT и re-adopt assignment лишь по exact terminal SUCCEEDED move provenance, свежему sole local mapping и неизменной client/credential/public/backend identity.

Explicit delete/release, failed или rolled-back move, ambiguity, любой live owner и отдельный cross-location CANDIDATE остаются fail-closed. Reconciliation не пишет Kraken и не применяет NAT напрямую; pending binding обрабатывает прежний typed adapter.

11.09.2026 · 0.17.98

Wave cutover после typed listener restart больше не полагается на periodic inventory, собранный до команды: controller ставит exact claim-fenced read-only probe_proxy_handoff_state и принимает relay generation только после live listener proof.

Service и customer listener с одним config_id и разными портами больше не считаются conflict: ownership доказывается по exact backend port. Wrong config, duplicate port, lost lease и missing live evidence остаются fail-closed; durable error хранит safe typed reason HANDOFF_WAVE_TARGET_LISTENER_EVIDENCE_INVALID без exception text и secrets.

Известная deployment prerequisite: controller-local public probe не проходит через relay prerouting и не является клиентским evidence. До production wave требуется заранее проверенный exact-scope ephemeral path только для controller self-probe через независимый host к реальному public relay; без него wave остаётся NO-GO. NAT/hairpin и критерии public proof релиз не ослабляет, durable external receipt остаётся отдельным follow-up.

11.09.2026 · 0.17.96

Вход ordinary READY cross-location handoff использует свежий server action clock после построения плана: heartbeat, пришедший во время preview/start, больше не выглядит ошибочно «будущим». Явно переданный тестовый момент времени сохраняет строгий future fence. Отказ одного read-only service-probe не скрывает следующий детерминированный PASS-кандидат, а REMOVED assignment больше не занимает live modem capacity.

Добавлен тонкий operator CLI для exact набора assignment одного клиента. Он проверяет версию и двустороннюю qualification, включает только точный manual scope, выполняет ticketless server-qualified start с одной idempotency key и немедленно возвращает scope в STOPPED до polling. Повтор по operation ID работает в status-only режиме. COMPLETED возвращает evidence_required=true: внешний public data-plane CLI не объявляет доказанным.

11.09.2026 · 0.17.95

Scoped handoff CUTOVER/ROLLBACK теперь строит forwarder generation как exact delta поверх текущего immutable APPLIED head. Выбранный endpoint обязан уже находиться в head, сохраняет public tuple и получает только следующую binding version; unrelated routes переносятся byte-exact.

Historical REMOVED/ARCHIVED materialization с ошибочно оставшимися ACTIVE строками больше не возвращается в NAT payload. Head/hash/version drift откатывает всю DB transaction до generation или writer side effect; replay возвращает прежнее поколение. Релиз controller-only, protocol и NAT v2 не меняются.

Sync re-adoption теперь сохраняет public relay в клиентском endpoint, но материализует CURRENT/NAT только на физический data_plane_host. Узкий recovery уже rejected bootstrap разрешён лишь для одного audit-proven endpoint вне immutable APPLIED head: fresh complete sync подтверждает exact non-service profile, helper receipt доказывает pre-claim allowlist rejection с прежним APPLIED fencing token и нулём bindings, foreign owner отсутствует. Rejected generation, её exact lease и relay→physical repair закрываются одной transaction; любой другой token, route delta или identity остаётся fail-closed.

11.09.2026 · 0.17.94

Shared listener cleanup теперь принимает доказанное отсутствие operation-owned target без повторного restart, даже если корректный agent result содержит executed=false. Узкий legacy receipt 0.17.93 с HANDOFF_WAVE_AGENT_IDENTITY_MISMATCH не переписывается: новый controller ставит отдельную claim-fenced read-only verification command и продолжает официальный CANCEL только после exact config/local-modem/sibling evidence.

Verification имеет отдельный agent capability и новый command для каждого execution claim, поэтому rollout остаётся agent-first. Stale/reclaimed claim, старый agent, присутствующий target, drift scope или sibling identity остаются fail-closed; targeted listener restart повторно не выполняется.

10.09.2026 · 0.17.93

Pre-cutover cleanup межлокационного handoff теперь различает exclusive и shared listener config. В shared cfg удаляется только exact operation-owned Kraken candidate; service/foreign profile, shared auth и cfg сохраняются. Agent перед targeted restart повторно проверяет config/local-modem identity, а после restart требует неизменный sibling-набор и реальные LISTEN на всех сохранённых портах.

Cleanup scope хранит exact source IP и secret-safe sibling listener projection, проходит стадии PREPARED → PROFILE_ABSENT → LISTENER_RELOADED → COMPLETED и меняется только под live claim/CAS. Локальный write-ahead fence agent запрещает второй restart после crash: replay только усыновляет доказанный post-state либо возвращает typed uncertain outcome. Новый capability требует agent-first rollout.

10.09.2026 · 0.17.92

Cleanup межлокационного handoff принимает дробный agent collection timestamp внутри той же целой секунды authoritative controller receipt, но остаётся fail-closed для будущего, stale, malformed и naive evidence. Точный live candidate из отменяемой NEEDS_ATTENTION операции может быть принят только cleanup-only путём, связанным с единственным RUNNING CANCEL action, без cutover и удаления shared auth.

Успешный terminal listener-cleanup command повторно используется после reclaim только при совпадении неизменяемой logical identity и полного сохранённого result; незавершённая command lease продолжает блокировать нового executor. Same-node MOVE, ожидающий NAT, атомарно отдаёт continuation reconciler, сохраняя source/target modem leases; fresh process завершает операцию только после CURRENT, NAT и stable data-plane proof и освобождает leases в той же terminal transaction.

10.09.2026 · 0.17.91

Полный sync сохраняет per-node persistence даже при частично недоступной ноде, а повторное принятие tombstone разрешается только после exact завершённого OK 2/2 и совпадения staged profile identity. Re-adoption проверяет assignment, endpoint, CURRENT, NAT и durable ownership, возвращает запись в ACTIVE/NOT_OBSERVED, сохраняет исторические timestamps и применяет pending NAT существующим typed adapter после commit.

Legacy cleanup с конкретным applied forwarder generation теперь сверяет payload/hash, fencing, bindings_json и полный canonical fleet. Повреждённый или неполный payload не проходит старый fallback и остаётся fail-closed без внешнего вызова.

10.09.2026 · 0.17.90

Для legacy target-cleanup операций auth identity восстанавливается только при точном совпадении assignment profile, текущей source binding и сохранённых исходных identity-полей. Cleanup отклоняется при duplicate CURRENT binding, same-modem/other-port профиле или drift; каждый crash/reclaim сохраняет явный TARGET_CLEANUP_CONTINUATION marker. Shared auth siblings не удаляются без доказанного operation ownership.

10.09.2026 · 0.17.89

Независимые внешние public probes больше не получают batch расшифрованных credentials. Для явно allowlisted probe-host создаётся короткая durable session только с hash одноразового token, scope и expiry; claim атомарно выдаёт один endpoint, защищён от replay и не пишет credentials в audit. UI auth/CSRF, CURRENT/NAT/ownership gates и два независимых public цикла сохраняются.

09.09.2026 · 0.17.88

Подготовка межлокационного handoff с новым backend port использует exact target_config_id из durable operation plan и wave claim, поэтому отсутствие самого порта в свежем Kraken snapshot больше не ошибочно считается missing mapping. Existing-port проверка (config_id, port) остаётся строгой, а pre-cutover rollback сохраняет только безопасный typed-код без текста исключения, credentials или секретов.

09.09.2026 · 0.17.87

Планировщик межлокационного handoff теперь исключает из нового backend-port pool все исторические привязки, которые атомарная admission-проверка считает занятыми, включая ROLLED_BACK. История не удаляется, active/reserved/CURRENT/foreign ownership fences не ослабляются, а серверный старт больше не получает гарантированный STALE_HANDOFF_PLAN из-за рассинхрона planner и admission.

09.09.2026 · 0.17.86

Исправлена точность admission для межлокационного нового backend port: snapshot, полученный в той же authoritative секунде, больше не отклоняется только из-за дробной части snapshot_collected_at. Следующая секунда, stale, malformed и snapshot другого поколения или агента остаются fail-closed; listener ownership, CURRENT, capacity, leases и остальные fencing не меняются.

09.09.2026 · 0.17.85

Для межлокационного handoff новый backend port из явно настроенного pool теперь передаётся в durable admission как NEW, а не ошибочно проверяется как уже существующий Kraken listener. Перед reservation controller требует полный свежий snapshot: exact один config_id выбранного local modem и отсутствие candidate port на всей target node. Чужой config, service/customer profile на candidate port, incomplete inventory, capacity/lease/CURRENT/forwarder/capability drift или поздняя materialization по-прежнему дают STALE_HANDOFF_PLAN без Kraken, NAT или endpoint write. Existing-port, same-location и RETURN semantics не меняются.

07.09.2026 · 0.17.84

Qualification межлокационного handoff больше не делает всю локацию NOT_READY из-за первого недоступного service listener: под единым коротким deadline она последовательно проверяет детерминированные eligible candidates и принимает только exact PASSED с ожидаемым outbound IP. В durable evidence не попадают credentials. Ошибка start STALE_HANDOFF_PLAN получила redacted validation_stage: PREVIEW_HASH либо ATOMIC_ADMISSION; это диагностическое поле не ослабляет fences.

07.09.2026 · 0.17.83

У ticket-less server-qualified межлокационного handoff обновление mobile egress target внутри короткого atomic admission window больше не вызывает ложный STALE_HANDOFF_PLAN. SQLite принимает свежий IP как evidence только для remote member и сохраняет именно его для последующей строгой direct data-plane проверки. Node/local/config/backend port, credentials/profile, capability route, forwarder, capacity, leases и fencing остаются exact; для same-node egress по-прежнему является identity fence.

07.09.2026 · 0.17.82

Operator получил отдельный полный internal preflight: один явный вызов последовательно выполняет обычные sync, durable reconciliation незавершённых lifecycle и NAT read-back. READY возвращается только при OK sync, завершённой reconciliation и APPLIED/NO_CHANGE NAT; BUSY, PARTIAL и ошибка остаются fail-closed. Вызов не заменяет независимый внешний data-plane gate и не ослабляет CURRENT, capability, capacity, owner или handoff fencing.

07.09.2026 · 0.17.81

Production release gate больше не может объявить fleet готовой только потому, что совпали active, CURRENT и NAT: проверяемый exact evidence требует равенства ACTIVE = AVAILABLE = CURRENT_READY = NAT_APPLIED и два свежих независимых public data-plane цикла. Старый baseline gate=true остаётся обязательным, поэтому owner, sync, NAT и policy gates не ослабляются.

07.09.2026 · 0.17.80

Ticket-less server-qualified межлокационный handoff теперь внутри той же bounded SQLite admission transaction на три минуты останавливает только новые плановые speed_test на его exact source/target nodes. Ещё не выданный schedule-test терминализируется с audit, но manual, DELIVERED и RUNNING test не отменяются, а recovery и остальные ноды продолжают работать. После terminal handoff окно сразу RELEASED; после crash, lost response или stalled claim оно bounded истекает. В этот контур не добавлено послаблений CURRENT, NAT, capability, capacity, owner или fencing.

07.09.2026 · 0.17.79

Межлокационный start без browser ticket больше не зависит от 30-секундного preview: controller помечает запрос только как server-owned admission, а одна bounded SQLite transaction строит fresh qualification, перепроверяет completed sync всех configured nodes, capability, CURRENT, NAT, capacity, owners и fencing, создаёт operation и сразу потребляет audit ticket. При занятом writer start за 5 секунд fail-closed возвращает HANDOFF_ADMISSION_BUSY без operation, reservation, Kraken или NAT write. Повтор того же idempotency key возвращает прежнюю operation.

06.09.2026 · 0.17.78

Межлокационный preview теперь выдаёт короткоживущий admission token, привязанный к actor, exact plan, completed sync и двусторонней qualification capability. Start потребляет его вместе с durable operation внутри одной SQLite transaction и повторно проверяет CURRENT, capacity, owners и fencing. Истёкший или обновлённый ticket завершает start как STALE_HANDOFF_QUALIFICATION до reservation, Kraken или NAT write; повторный raw agent snapshot больше не создаёт ложный отказ между qualification и start.

06.09.2026 · 0.17.77

Snapshot agent больше не обрывается, когда один recovery request встречает уже занятый durable modem-action lease: controller сохраняет receipt, WAITING_HANDOFF и audit, но не создаёт конкурентную команду и не меняет owner/fencing. Heartbeat, inventory и другие события snapshot продолжают импортироваться; replay не дублирует writer. Incident identity, unassociated command и CREATE conflicts по-прежнему fail-closed.

06.09.2026 · 0.17.76

У межлокационной пересадки смена мобильного внешнего IP target между preview и start больше не даёт ложный STALE_HANDOFF_PLAN: volatile egress observation и производный evidence hash исключены только из admission hash remote member. Сам IP всё равно сохраняется и повторно строго проверяется в atomic reservation, перед cutover и после него публичным data plane. Identity node/local/config/port, credentials/profile, capability, forwarder, capacity, reservations и fencing остались fail-closed; для same-node IP остаётся частью identity.

06.09.2026 · 0.17.75

Один start межлокационной пересадки строит fresh typed admission один раз и передаёт exact canonical plan прямо в атомарную SQLite reservation. Второй non-atomic placement preview убран, поэтому готовая пересадка не получает ложный STALE_HANDOFF_PLAN между двумя внутренними чтениями. Source/CURRENT/forwarder, target config/port, capability, capacity, reservations и fencing всё равно повторно проверяются внутри BEGIN IMMEDIATE; mismatch остаётся fail-closed без external write. Старые operations, claims и leases release не очищает.

06.09.2026 · 0.17.74

Cross-location handoff больше не теряет готовый preview только потому, что completed fleet sync обновил время observation capability. В durable plan входят route, target, NAT, full sync, bilateral agent capability и exact identity, а не технические timestamps renewal. Перед первой записью SQLite всё равно заново требует свежий READY, непросроченный capability, exact CURRENT/NAT, capacity, owners, reservations и fencing; stale agent или любой semantic drift остаётся STALE_HANDOFF_PLAN без Kraken/NAT write.

06.09.2026 · 0.17.73

Подтверждённые отказы нескольких портов одного неисправного модема не считаются массовым отказом фермы при свежем точном CURRENT. Защита от нескольких источников, двух нод и неоднозначной топологии сохраняется. После чистого CURRENT rollback выполняется новая квалификация кандидатов. SQLite ограничивает квалифицированную аварийную эвакуацию, включая возврат домой, тремя попытками за пять минут для неизменной привязки клиента; исчерпание лимита возвращает причину и время повтора. Ремонт исходного модема ждёт подтверждённой эвакуации всех его клиентов.

05.09.2026 · 0.17.72

Локальная пересадка публикует exact CURRENT под собственными durable leases: допускается только живой журнал операции с совпадающими endpoint, credentials, NAT, обоими fencing tokens и свежим agent evidence. Чужое владение, maintenance, reservation и topology conflict остаются блокерами.

Владелец MOVE сам перечитывает полный список профилей своей ноды при новом snapshot, не ожидая фонового sync, которому мешает удерживаемая node lock. Чтение и импорт ограничены текущим deadline; служебные изменения и дополнительные проверки всего парка здесь не запускаются. Fleet PARTIAL не становится OK и по-прежнему не разрешает новую выдачу. Успех пересадки требует повторной публичной проверки и qualified NAT.

05.09.2026 · 0.17.71

Каждый новый CREATE получает отдельную operation-owned группу выдачи ISS-<operation_id>. Следующий Kraken sync сохраняет эту связь и больше не смешивает свежие реквизиты с удалёнными строками прежней дневной импортной группы клиента. Экран результата открывается только по exact order_id и точному множеству выданных assignment.

Удалённые proxy показываются как архивные записи и исключаются из текущего verification summary. В мобильном мастере прокручивается только содержимое, а кнопки действий остаются в видимом footer с учётом visualViewport. При неполном fleet sync вместо ложных нулей отображается обновление данных; форма автоматически перечитывает capacity и разрешает продолжение только после fresh OK 2/2.

05.09.2026 · 0.17.70

Fleet sync сохраняет secret-free список портов всех Kraken profiles под точным sync_id, включая service-only, inactive и malformed customer profiles, которые могут отсутствовать в agent listener snapshot. Bootstrap и CREATE используют только последнее свежее полное поколение OK 2/2; нода с нулём фактически свободных портов больше не участвует в автоматическом выборе.

Новый RUNNING sync сохраняет последнее свежее completed evidence; завершённый PARTIAL, усечённый или stale sync даёт retryable CREATE_FLEET_PROFILE_EVIDENCE_NOT_READY до reservation и внешних write. Выбранный port повторно проверяется внутри BEGIN IMMEDIATE, а live Kraken preflight остаётся последним race barrier. Релиз controller-only и не меняет port ranges, placement, NAT или agent protocol.

05.09.2026 · 0.17.69

Live Kraken preflight больше не отменяет весь CREATE, если быстрый snapshot allocator зарезервировал порт, уже занятый существующим service-only или customer profile. До первого external write controller под live claim атомарно переносит только конфликтующий resource на следующий свободный порт настроенного node pool; public/backend port, semantic fingerprint, operation bootstrap и audit меняются одной transaction.

Остальные порты multi-item заказа сохраняются. Restart продолжает новый durable intent без повторного Kraken write. Active assignment, NAT public port, durable endpoint и чужая CREATE reservation повторно проверяются внутри port CAS; при действительно заполненном пуле сохраняется быстрый полный ROLLED_BACK/CREATE_RESERVED_PORT_IS_NOT_FREE без частичной выдачи.

04.09.2026 · 0.17.63

Automatic local failover сначала запускает один durable source-bound qualification batch: agent параллельно проверяет same-location target modem под общим deadline 5 секунд, а controller принимает только fresh exact PASS, повторно сверяет capacity/identity/ownership и CAS-резервирует одного победителя. Crash продолжает тот же MOVE; параллельных Kraken/NAT writes нет.

CREATE сразу возвращает reserved ports и verification progress. Чужая операция на другом listener той же node больше не даёт terminal NODE_CONFIG_BUSY: exact resource ждёт в WAITING_RESOURCE и автоматически продолжает работу. Recovery action_request в off/observe/out-of-allowlist не создаёт executable PENDING owner и не замораживает unrelated выдачу.

Rollout health проверяет compact JSON семантически. Публичный client data plane считается только по двум полным циклам с независимого внешнего host: controller-local hairpin refusal не является fleet outage и не запускает ложный rollback.

04.09.2026 · 0.17.62

Full sync теперь сохраняет redacted canonical ownership для каждого конфликтующего backend-port. Дубликат допускается к retirement только при exact ACTIVE assignment, свежем Kraken+agent evidence и единственном unreferenced non-service profile; partial sync, service/shared auth, missing local identity или claim drift оставляют endpoint fail-closed.

Retirement выполняется новым durable journal PREPARED → DELETE_INTENT → VERIFYING → COMPLETED под node lock и live claim/CAS. Lost response и restart продолжают exact profile ID по fresh read-back, не удаляют auth/NAT/assignment и завершаются только после unique CURRENT READY. Watchdog продолжает наблюдение конфликтного proxy, подавляет repair только для него и не использует evidence другого endpoint; неполный fleet sync не разрешает local или cross-location write.

04.09.2026 · 0.17.61

Maintenance handoff больше не завершает operation и не ставит destructive source modem action после одного успешного probe. Для всей пачки обязательны exact target CURRENT, повторный NAT reconcile с per-assignment read-back и второй independent public probe. Один mapping/NAT/public blocker сохраняет fail-closed состояние без команды.

Успешный handoff сразу публикует assignment как AVAILABLE и очищает только его stale watchdog/recheck state. Restart recovery восстанавливает target identity из durable reservation и ACTIVE assignment; source-clear без свежей stability оставляет RUNNING/RECOVERY_WAITING_STABILITY. Сохранённый после crash HANDOFF_VERIFIED повторно проверяется непосредственно перед command queue.

04.09.2026 · 0.17.60

Выдача proxy теперь явно проходит путь заказ → выполнение → реквизиты. После подтверждения сразу открывается отдельный execution screen, а текущий этап остаётся видимым в sticky-статусе. Успех автоматически открывает прежний экран партии с полными credentials, копированием, скачиванием и verification; партия сверяется по всем returned assignment ID, а общее сообщение «Готово» не заменяет реквизиты. Закрытие и повторное открытие dialog возвращает к той же operation без второго заказа.

Для пяти proxy на одной Kraken node полный profile list читается 4 раза вместо 12: preflight, общий resource convergence, общий final exact read-back и sync. Per-resource journal/checkpoints, claim/fencing, exact ownership, rollback и crash/replay сохранены; final read-back требует единственного semantic match и exact stored ID до foundation/NAT.

Controller доставляет активные CREATE challenge ID через существующий command poll, а agent durable-сохраняет новый ID и будит обычный fresh full snapshot. Poll не считается evidence, duplicate не создаёт busy-loop. Release требует обновления agent+controller: сначала controller, затем agents по одной node без fleet restart.

03.09.2026 · 0.17.59

Same-location MOVE до первого Kraken write сохраняет durable source/target rollback identity: exact config, authoritative local modem, public endpoint fingerprint и fencing generation без credentials. Если новый snapshot временно теряет или двусмысленно отображает mapping, rollback использует только уже доказанную operation-owned identity под живым lease/CAS.

Controller restart делает fresh Kraken read-back и идемпотентно продолжает ROLLING_BACK_KRAKEN/LISTENERS. Обычный lost response после FORWARD_INTENT не становится ложным FAILED: exact source принимается без повторного Kraken write, exact target возвращается на source, mismatch остаётся NEEDS_ATTENTION. Targeted listener commands имеют deterministic operation/role/attempt identity, а terminal history не скрывает assignment от watchdog.

Rollback listener retries получили durable monotonic attempt: transient missing cfg и точный bridge-retirement race переходят к следующей deterministic command только после terminal предыдущей. Optional target materialization выполняется максимум один раз после durable checkpoint и только для service-profile; customer-profile не редактируется и crash/replay не повторяет Kraken write.

Первый успешный proxy probe больше не завершает MOVE немедленно. После fresh CURRENT и qualified NAT выполняется независимый Kraken/data-plane read-back; повторный failure оставляет RUNNING/WAITING_NAT с WAITING_STABLE_DATA_PLANE. Это снижает failover/failback churn при кратковременных межузловых flap, сохраняя CGNAT listener proof и существующие budgets/backoff.

03.09.2026 · 0.17.58

Новый подтверждённо отказавший failover-target больше не удерживает client proxy обычным 30-минутным cooldown: typed POST_SWITCH_TARGET_FAILED разрешает bounded same-location second-hop в прежних rolling-hour/global budgets. Провалившийся target получает durable idempotent backoff и не участвует в следующем выборе до exact listener/data-plane revalidation.

Automatic placement требует два fresh healthy sample с интервалом не меньше 15 секунд; single, stale, future и transient healthy evidence не допускают target. Final write-time health fence остаётся строгим, а race даёт TARGET_HEALTH_UNSTABLE и переход к следующему допустимому modem без ping-pong.

Derived CURRENT теперь может безопасно заменить устаревший local modem owner только после двух более новых snapshots с одним и тем же sole exact (config_id, backend_port). Первый сохраняет LOCAL_MODEM_MAPPING_MOVED и candidate; same snapshot/time, candidate drift и ambiguity остаются fail-closed. Schema additive, release controller-only; Kraken, NAT, agent protocol, public endpoint и credentials не меняются.

MOVE/AUTO_FAILOVER/AUTO_FAILBACK не завершается успешно по raw DEFERRED или NOT_ACKNOWLEDGED NAT result. Операция остаётся RUNNING/WAITING_NAT, а reconciler повторяет exact CURRENT, target, data-plane и NAT read-back. Для неизменившегося same-location route допускается только EXACT_APPLIED_READBACK: quiescent APPLIED generation, canonical desired/helper v2 evidence, READY CURRENT и APPLIED NAT row обязаны совпасть.

03.09.2026 · 0.17.57

CREATE allocator теперь исключает service-only proxy ports из свежего полного agent snapshot всей Kraken node. Live Kraken profile read-back повторяет этот fence перед первым write: конфликт CREATE_RESERVED_PORT_IS_NOT_FREE одной transaction переводит operation/resources в ROLLED_BACK, отменяет PREPARED challenges и показывает менеджеру понятную причину без auth/profile/NAT side effects.

Консервативный reconciler освобождает старую terminal CREATE reservation только при доказанном отсутствии owned Kraken profile/auth, customer cfg/listener, ACTIVE assignment/backend, NAT publication и forwarder generation. Shared auth с auth_created=0 и endpoint tombstone точной REMOVED assignment сохраняются; неоднозначность остаётся typed NEEDS_ATTENTION. Release controller-only, agent protocol и lifecycle policy не менялись.

Terminal handoff NEEDS_ATTENTION больше не скрывает CURRENT assignment от read-only watchdog: безопасный pre-write CANDIDATE_PROFILE_PARTIAL освобождается одной transaction, а execution-backed случай остаётся mutation-owned и видимым в availability. Same-location failover проверяет target capacity до operation и завершается только после exact CURRENT READY и typed NAT APPLIED/NO_CHANGE; stale binding или DEFERRED вызывает rollback.

При подтверждённом multi-modem public outage node guard по-прежнему запрещает массовый listener reload, но отдельные endpoint могут восстанавливаться на healthy same-location targets в общем rolling-hour emergency budget. Telemetry показывает причину MULTI_MODEM_DATA_PLANE_OUTAGE и candidate/selected/suppressed assignment, поэтому защита парка больше не превращается в полную остановку помощи затронутым клиентам.

02.09.2026 · 0.17.56

Обычная выдача больше не повторно использует публичный TCP-порт, если его tuple уже принадлежит durable-записи proxy_public_endpoints, включая endpoint удалённой или откатившейся выдачи. Такой номер остаётся зарезервированным до отдельной доказуемой процедуры освобождения, поэтому новая выдача выбирает следующий чистый порт и не зависает в WAITING_FOUNDATION из-за конфликта владельца.

Новый CURRENT binding до публикации NAT атомарно получает data_plane_host выбранной Kraken node и сохраняет эту цель во время повторных sync. Публичный адрес VDS больше не может стать backend новой выдачи и сформировать NAT-петлю с CREATE_NAT_OUTCOME_AMBIGUOUS.

Exact helper read-back после restart теперь завершает и forwarder generation, и локальную проекцию proxy_nat_bindings, поэтому уже применённая выдача не остаётся ложным PENDING. Расчёт свободных публичных портов в карточке клиента использует тот же durable endpoint-реестр и больше не завышает возможность выдачи. Исправление controller-only: Kraken, NAT payload, lifecycle timeout и agent protocol не менялись; agents перезапускать не требуется.

02.09.2026 · 0.17.55

Удалённая assignment теперь является durable REMOVED sticky tombstone для той же пары Kraken node/proxy ID. Fresh sync или restart больше не переводит её обратно в ACTIVE, даже если Kraken временно продолжает возвращать старый profile. Такая запись не попадает в CURRENT topology, NAT publication, watchdog или modem maintenance.

Новый proxy с новой Kraken identity и обычные ACTIVE/DISABLED assignments импортируются как прежде. Исправление controller-only: schema, API, placement, credentials, NAT payload, timeout и agent protocol не менялись; agents перезапускать не требуется.

01.09.2026 · 0.17.54

Candidate read-back больше не считает credential-only совпадение ownership anchor. Один client может использовать одинаковые credentials в нескольких proxy; relation доказывается exact target backend port либо уже записанным Kraken proxy/auth ID. Чужой профиль на target port всё ещё даёт CANDIDATE_PROFILE_PARTIAL и останавливает handoff до external write.

Exact pre-write CANDIDATE_PROFILE_PARTIAL без recorded auth/proxy, execution claim, relay phase, command или generation теперь можно отменить официальным CANCEL одной transaction: candidate/member/operation становятся CANCELLED, reservations освобождаются, external writes не выполняются. Любой progressed или ambiguous state сохраняет NEEDS_ATTENTION; direct SQL и blind Kraken cleanup запрещены.

Controller-only hotfix сохраняет same authenticated snapshot и STALE_HANDOFF_PLAN fences, crash-safe WAITING_FOUNDATION/PUBLISHING/ISSUED, migration-owned CURRENT reconciliation и privileged helper v2 exact post-apply read-back. Canonical modguard-agent.tar.gz и modguard-agent.tar.gz.sha256 меняются только из-за package version; agent protocol и behavior не менялись.

CREATE теперь сразу возвращает совместимый 202 с durable operation, зарезервированными портами и отдельным verification status. Kraken create/read-back, challenge, listener foundation, NAT и data-plane выполняет reconciler под persistent claim. Короткий external-write fence отделён от фиксированного 1200-секундного verification deadline; slow/lost response принимается только через exact read-back/adoption без duplicate writes.

Challenge ACK хранит canonical listener evidence и hash, поэтому следующий обычный snapshot не уничтожает уже доказанную причинность; latest exact listener всё равно проверяется отдельно. Assignment import и CURRENT materialization коммитятся одной transaction с causal snapshot identity: старый sync не downgrade-ит более новый foundation, реальный drift остаётся NOT_READY.

Новый rollback flag unified_coordinator_enabled по умолчанию выключен. При включении local failover/failback перестаёт использовать legacy move и проходит через один durable handoff coordinator; cross-location mode/scope/capability gates не ослаблены. Полностью откатанный orphan NAT attempt может стать SUPERSEDED только при отсутствии live lease/topology/cleanup references и показывается как historical terminal, а не pending publication.

31.08.2026 · 0.17.53

Атомарный start локального аварийного handoff теперь принимает единственный exact CURRENT source binding в состоянии NOT_READY. Ранее READY preview видел этот источник, но start-side CAS искал только ACTIVE/READY и ложно возвращал STALE_HANDOFF_PLAN именно для уже недоступного proxy.

Ослабления ownership нет: source binding ID, assignment, public tuple, credentials, target IP/health/capacity/owner и backend port всё ещё сверяются в одной transaction; REMOVED/CANCELLED, duplicate CURRENT и любой identity drift остаются fail-closed до durable operation и external side effects. Schema, API, timeout, placement, cross-location и NAT semantics не менялись.

Release сохраняет crash-safe WAITING_FOUNDATION/PUBLISHING/ISSUED, migration-owned CURRENT reconciliation, same authenticated snapshot fence и privileged helper v2 exact post-apply read-back. Canonical modguard-agent.tar.gz и modguard-agent.tar.gz.sha256 пересобраны только из-за package version; agent behavior и protocol не менялись.

31.08.2026 · 0.17.52

Устранена гонка между READY preview и immediate start локального handoff: receive timestamp позже request-time принимается только для того же same authenticated snapshot, когда modem и authenticated agent имеют точный одинаковый last_seen. Unmatched future, stale и malformed evidence остаются fail-closed.

Unrelated telemetry churn больше не меняет canonical plan hash. Target IP/health/capacity/owner, assignment/backend identity, credential fingerprint и public tuple по-прежнему защищены и дают STALE_HANDOFF_PLAN либо typed ownership conflict до durable operation и side effects. Окно 180 секунд, policy, API и NAT semantics не изменены.

Release сохраняет crash-safe WAITING_FOUNDATION/PUBLISHING/ISSUED, migration-owned CURRENT reconciliation и privileged helper v2 exact post-apply read-back. Canonical modguard-agent.tar.gz и modguard-agent.tar.gz.sha256 пересобраны из exact source только из-за package version; agent behavior и protocol не менялись.

30.08.2026 · 0.17.51

CREATE → foundation → NAT оформлен как отдельная crash-safe release unit. До первого Kraken write controller transactionally сохраняет encrypted resource intent, capacity/public-port reservation, endpoint PUBLISHING и PREPARED challenge. После exact Kraken read-back challenge становится ISSUED; NAT ждёт новый agent collection с exact echo, canonical ACTIVE assignment owner, customer cfg/listener и sole migration-owned CURRENT READY. Операция остаётся RUNNING/WAITING_FOUNDATION и быстро возвращает совместимый 202.

Linked NAT attempt и PENDING generation создаются до helper write; только qualified privileged helper APPLIED/NO_CHANGE с canonical v2 post-apply read-back атомарно публикует endpoint. Pending CREATE исключён из generic NAT, но продолжает резервировать port/modem. Crash, stale claim, lost response и delete retry используют exact read-back/adoption; ambiguous identity сохраняет ownership и NEEDS_ATTENTION без duplicate write или blind cleanup.

Schema additive и проверяется на fresh, populated 0.17.50 и reopened DB. Rollout agent-first: agent 0.17.51 payload совместим с controller 0.17.50; controller 0.17.51 принимает старый snapshot для monitoring/recovery/handoff, но новый CREATE без challenge capability отклоняет до mutation. Release использует canonical modguard-agent.tar.gz и modguard-agent.tar.gz.sha256, сохраняет migration-owned CURRENT reconciliation, modes STOPPED и пустой scope. Technical canary требует отдельного допуска после dark gate.

26.08.2026 · 0.17.50

Первый dark deployment 0.17.49 безопасно остановился до forwarder v2 и activation: fresh full sync обнаружил ACTIVE assignments, чьи public tuples оставались закреплены за historical REMOVED endpoint rows. Controller не изменил Kraken, listeners, client routes или credentials и был возвращён на known-good release.

Post-sync migration-owned reconciliation теперь может atomically передать единственный terminal endpoint свежей ACTIVE assignment. Transfer разрешён только до первой v2 generation, для sole canonical old/new CURRENT и при нулевых handoff members, reservations, capacity owners, modem/maintenance owners и writer lease. Stable endpoint ID/public tuple сохраняются, forwarder retarget-ится на новый CURRENT, а один redacted PROXY_HANDOFF_FOUNDATION_ENDPOINT_TRANSFER audit коммитится в той же BEGIN IMMEDIATE transaction. Exact replay ничего не меняет; ambiguous/active owner или audit failure остаётся fail-closed.

0.17.50 повторяет только dark rollout: modes остаются STOPPED, scope пуст, lifecycle/commands/owners нулевые. GO требует all-active foundation READY, exact privileged v2 post-apply read-back, bilateral READY, realistic capacity и неизменный client baseline. Technical canary по-прежнему требует отдельного подтверждения после dark gate.

26.08.2026 · 0.17.49

Подготовлен только dark bootstrap первого cross-location handoff: installer принимает каноническую пару modguard-agent.tar.gz и modguard-agent.tar.gz.sha256, а self-contained privileged helper публикует canonical v2 evidence лишь после exact post-apply read-back managed chain. Controller остаётся без CAP_NET_ADMIN; missing, duplicate, extra, malformed, stale или future rule/evidence сохраняет typed NOT_READY. Recovery helper различает доказанные *_CONFIRMED состояния и честный NAT_HELPER_STATE_UNCONFIRMED, который запрещает слепой restart/apply.

Post-sync migration-owned CURRENT reconciliation обновляет только sole canonical migration row одной BEGIN IMMEDIATE transaction и сохраняет operation-owned/history byte-for-byte. Несколько customer ports одного Kraken config на одном local modem считаются одним валидным owner; duplicate/split/conflicting/stale identity остаётся fail-closed. Capacity считается строго из node-wide durable CURRENT всех ACTIVE assignments: требуется 70/70 foundation READY на проверенном layout либо exact all-active READY на другом deployment, bilateral READY и realistic nonzero capacity.

Controller 0.17.49 совместим со snapshot agent 0.17.48. Release, full offline fault/restart/redaction gate и green CI не включают activation: manual/automatic modes остаются STOPPED, scope пуст, owners и lifecycle rows не создаются, client baseline не меняется. Dark deployment требует accepted SHA, online sqlite3.Connection.backup(), zero owners, fresh full sync, exact parsed v2 read-back и отдельный rollback plan. Technical canary, OBSERVE/ACTIVE, allowlist и technical assignment требуют нового отдельного approval после dark gate.

22.08.2026 · 0.17.48

Подготовлен fail-closed контур первого межлокационного canary только для нового технического proxy: manual scope по умолчанию пуст, оба cross-location mode остаются STOPPED, а qualification пары строится только из server-derived evidence. Controller принимает старый agent snapshot, но сохраняет направление NOT_READY до последовательного обновления обоих agents до 0.17.48.

One-member перенос между нодами проходит через существующую Task 11 wave v2 CLIENT_ATOMIC. После cutover lifecycle удаляет только exact source profile, точечно перезагружает его config и сохраняет fresh proof отсутствия перенесённого listener; incremental foundation, preflight и conflict audit не переписывают operation-owned CURRENT и не создают новый worker.

Релиз не включает реального клиента, automatic ACTIVE или production deploy. Отдельно подтверждённый rollout обязан использовать короткое manual ACTIVE окно только для durable claim, пять public probes, linked Return, очистку технического assignment и возврат scope/modes к deny-all STOPPED.

19.08.2026 · 0.17.47

Production canary 0.17.46 выявил несовместимый порядок обновления NAT-компонентов: новый controller записал fenced v1 snapshot и ждал точный writer token, тогда как установленный privileged helper вернул status старого формата. Правила были применены, но controller не мог безопасно принять подтверждение, поэтому операция штатно откатилась, а release был возвращён на 0.17.44.

Helper теперь допускает helper-first rollout с прежним controller, принимая только точную четырёхполевую pre-fence v1 схему до первого положительного writer token. После начала fenced epoch старый формат необратимо блокируется; token сохраняет эту границу и при ошибке первой fenced-попытки, поэтому downgrade требует восстановления сохранённого helper при остановленном controller. Fenced v1 и v2 сохраняют обязательные type-exact token/base/hash проверки, смешанные схемы отклоняются до nft. Controller принимает fenced v1 только при точном совпадении generation и integer writer token, не выдавая legacy reconcile за новую handoff generation.

19.08.2026 · 0.17.46

Исправлена совместимость service-listener reconciliation с реальным agent inventory. Один выключенный, отсутствующий или давно не наблюдавшийся service-модем больше не прерывает read-only импорт всех клиентских профилей Kraken-ноды. При неполном либо неоднозначном mapping обе служебные фазы получают явное состояние DEFERRED, а customer inventory и аудит продолжают синхронизироваться.

Fail-closed граница не ослаблена: controller не получает modem lease, не создаёт service profile и не перезагружает listener, пока mapping каждого затронутого live modem не станет свежим и однозначным. При полном mapping сохраняется прежний порядок modem lease → node lock. Изменение не включает автоматические пересадки, не меняет клиентские реквизиты и не добавляет новый worker.

18.08.2026 · 0.17.45

Production-инцидент с клиентским 10124 подтвердил новый вариант рассинхронизации: после Kraken move один backend-порт одновременно оставался в source modem-id-104.cfg и появлялся в target modem-id-120.cfg. Из-за SO_REUSEPORT отдельные соединения проходили через правильный modem, а другие зависали либо выходили с другим IP. Прежний verifier видел удачный target probe и мог ошибочно завершить пересадку.

После каждого same-location move controller теперь через официальный Kraken API повторно материализует освобождённый config, точечно перезагружает target и source и требует, чтобы source больше не подтверждал клиентский порт. Асинхронная генерация cfg получает до шести ограниченных попыток; затем операция fail-closed откатывается. Rollback применяет тот же контракт к бывшему target. Существующий sync-loop без нового worker сканирует свежий agent inventory: один customer port в нескольких config создаёт persistent PROXY_LISTENER_PORT_CONFLICT, блокирует новые пересадки endpoint и снимается только свежим однозначным snapshot. Fleet restart, прямое редактирование cfg и изменение реквизитов клиента не используются.

08.08.2026 · 0.17.44

Production canary 0.17.43 доказал второй слой гонки CREATE. Первый запрос корректно получил retryable 503 и сохранил operation в безопасном FAILED до освобождения ноды. Но повтор с тем же ключом воспроизводил сохранённый текст ошибки как обычный 502, не пытаясь снова получить node lease. Частичного создания proxy не происходило.

Ожидающий CREATE теперь сохраняет явный retry_reason: NODE_CONFIG_BUSY. Повтор с тем же idempotency key атомарно продолжает исходную operation с прежним ID и снова получает lock. Продолжение разрешено только при полном совпадении пользователя, типа операции и нормализованного запроса; изменённый payload либо конкурентный второй владелец не могут выполнить дублирующее создание.

08.08.2026 · 0.17.43

Повторный production lifecycle canary столкнулся не с ошибкой удаления, а с настоящей конкуренцией: сразу после тестовой пересадки на той же Kraken-ноде начался штатный автоматический возврат другого клиентского proxy. Node lease корректно защитил конфигурацию, но общий endpoint удаления сообщил о временной занятости как о постоянной ошибке 502, поэтому canary преждевременно остановился.

Создание, удаление, восстановление и пересадка теперь одинаково возвращают для занятой конфигурации ноды 503, retryable: true и Retry-After: 5. Production canary ограниченно повторяет только такой idempotent запрос с тем же ключом; дубликат операции не создаётся. Ошибки Kraken, NAT, listener или data plane не маскируются автоматическим повтором и сохраняют строгую lifecycle-диагностику.

08.08.2026 · 0.17.42

Lifecycle canary после выпуска 0.17.41 обнаружил ещё один вариант прежней гонки: Kraken уже удалил тестовый proxy, ModGuard подтвердил отсутствие профиля, архивировал assignment и очистил NAT, но занятый общий inventory sync ошибочно завершил операцию как PARTIAL. Восстановление архивной выдачи имело более опасный симметричный путь и могло откатить уже подтверждённый Kraken-профиль только из-за занятого sync.

Удаление и восстановление теперь после собственного Kraken read-back записывают POST_WRITE_SYNC_DEFERRED, будят существующий фоновый sync-loop и завершают lifecycle по фактическому результату. Частичный опрос Kraken также считается deferred и запускает повторный цикл. Kraken read-back, явное состояние assignment в DB, подтверждённое применение NAT и data-plane проверка восстановленного endpoint остаются обязательными; при ошибке firewall удаление получает PARTIAL, а не ложный успех.

08.08.2026 · 0.17.41

Продолжительное production-наблюдение выявило вторую ветку прежней гонки: обычная пересадка уже умела откладывать занятый inventory sync, но пакетная maintenance-эвакуация клиентов перед reboot модема сохраняла строгую пятисекундную зависимость. При длинном фоновом чтении Kraken подтверждённая пересадка ошибочно откатывалась, а повторная синхронизация во время rollback могла дать ложный ROLLBACK_FAILED.

Ручной move, автоматический failover и maintenance-handoff теперь используют одну семантику. После Kraken read-back занятый или временно неуспешный inventory sync фиксируется как POST_WRITE_SYNC_DEFERRED и продолжается фоном; решение об успехе принимают подтверждение listener и реальная проверка клиентского data plane. Обратный edit пакетного rollback дополнительно подтверждается свежим Kraken read-back каждого исходного modem до восстановления DB и listener. Строгая семантика создания, удаления и восстановления proxy не изменена.

08.08.2026 · 0.17.40

Production lifecycle canary после исправления пересадки выявил отдельную конкуренцию удаления с фоновым sync. Удаление одного proxy ошибочно ожидало lease сразу всех Kraken-нод и могло получить retryable 502, если совершенно другая локация в этот момент читала inventory. Такой же слишком широкий lock использовали восстановление архивной выдачи и изменение maxconn.

Эти assignment-операции теперь под существующим lock выбранных assignment читают их текущие ноды и сериализуются только с конфигурацией реально затронутых Kraken. Неизвестные, удалённые и недопустимые assignment по-прежнему проходят прежнюю lifecycle-валидацию; Kraken read-back, NAT, data-plane проверка, idempotency и rollback не ослаблены. Независимая работа локаций больше не блокируется несвязанным изменением.

08.08.2026 · 0.17.39

Исправлена гонка пересадки proxy с фоновым inventory sync. Kraken успевал применить новый modem и подтвердить профиль через REST read-back, но общий sync-lock мог оставаться занят дольше прежнего пятисекундного ожидания. Controller ошибочно возвращал Kraken changes were not synchronized, откатывал рабочую привязку и создавал клиенту дополнительный разрыв.

Для move lifecycle занятый sync теперь получает явное состояние POST_WRITE_SYNC_DEFERRED, будит ближайший фоновый цикл и не является причиной rollback. Чтение и импорт Kraken snapshot используют тот же lease ноды, что и lifecycle write, поэтому уже прочитанный старый профиль не может после пересадки вернуть DB на исходный modem. Assignment обновляется на подтверждённый Kraken target, после чего обязательны точечное подтверждение listener и реальная проверка клиентского data plane. Только их фактический отказ запускает возврат, а обратный Kraken edit обязан пройти свежий read-back исходного modem до восстановления DB и listener. Строгая sync-семантика создания, удаления и восстановления не менялась.

08.08.2026 · 0.17.38

Исправлена зависающая выдача proxy новому клиенту. Production-расследование показало серию HTTP 499: запросы не доходили до регистрации lifecycle-операции, потому что фоновый service-listener sync удерживал общий node lock, а браузер закрывал долгое соединение. Дубликаты и частично созданные профили при этих попытках отсутствовали. После online-backup SQLite перезапущен только controller; Kraken, клиентские 3proxy, NAT и реквизиты не менялись, а оборванный sync получил ABORTED.

Создание теперь сначала сохраняет idempotent operation и живые фазы, затем ограниченно ждёт конфигурацию ноды. UI параллельно отслеживает operation status и восстанавливает результат после сетевого обрыва. Mutation-ответ стал компактным и больше не содержит полный bootstrap. Production lifecycle canary совместим с обоими контрактами: использует вложенный bootstrap старого controller либо отдельный GET после компактного ответа. Устранён обратный порядок assignment/node locks в maintenance; фоновый ремонт служебного listener ограничен одной 30-секундной попыткой и не выполняется внутри клиентского post-write sync. Занятость ноды возвращается как явный retryable 503, повтор уже идущего ключа — как 202. Добавлены конкурентные, idempotency, HTTP и canary-регрессии; полный локальный suite: 387 тестов.

07.08.2026 · 0.17.37

Устранён постоянный кэш локальных 3proxy-конфигов в agent. Production-аудит показал, что Kraken и реальные файлы уже содержали новые назначения, а снимок ModGuard продолжал показывать listener с момента запуска agent. Теперь каждый health-цикл перечитывает cfg; контрольный перезапуск только agent на обеих нодах подтвердил полное совпадение актуальных listener с Kraken без перезапуска клиентских 3proxy.

Изменения Kraken и handoff 3proxy получили общий lease на ноду. Добавлено самовосстановление служебного профиля/config: официальный API read-back, точечный Supervisor reload, подтверждение всех listener и реальный HTTPS probe. Ошибка блокирует только неисправную цель, Kraken-ноды работают независимо, массовый restart не применяется.

Репозиторий Forgejo получил нативный CI workflow и русскоязычный регламент выпуска. CI работает только с mock Kraken и временными БД, не содержит production-секретов и не выполняет команды на нодах. Проверка реального data plane остаётся отдельным обязательным этапом контролируемого релиза.

06.08.2026 · 0.17.36

Срочное восстановление клиентского proxy получило приоритет над возвратом уже работающих портов на домашние модемы. Пока запланирована, ожидает выполнения или выполняется контрольная data-plane проверка, controller не начинает новый failback. За один цикл запускается не более одного возврата после обслуживания. Уже начатая операция Kraken не прерывается: остановка между записью конфигурации и проверкой могла бы оставить порт в неопределённом состоянии.

06.08.2026 · 0.17.35

Production-наблюдение 0.17.34 выявило расхождение между предварительным выбором target и финальной Kraken-проверкой ёмкости: место, сохранённое для автоматического возврата временно переселённого клиента, не отображалось в bootstrap как занятое. В результате data-plane failover повторно выбирал modem с бронью и получал has no free client slots. Теперь фактическая нагрузка и reservedSlots участвуют в одном расчёте bootstrap, manager capacity, failover, maintenance и failback; собственная бронь возвращающегося assignment вычитается только для его проверенного возврата домой.

Если подтверждённый клиентский endpoint не работает, а на его location нет ни одного обычного свободного места, аварийный failover может временно превысить рекомендацию ровно на один слот на наименее загруженном здоровом modem. Modem со вторым сверхлимитным assignment получает состояние OVER и больше не выбирается. Это исключение не применяется к обычной выдаче или ручному размещению и существует только ради восстановления трафика уже оплаченного production proxy.

06.08.2026 · 0.17.34

Журнал пересадок теперь отделяет результат проверки от способа применения конфигурации. Совпадение выходного IP со снимком целевого modem прямо называется успешной проверкой; точечный restart одного экземпляра 3proxy описывается как штатное применение новой привязки с последующим подтверждением клиентского трафика, а не как причина сбоя. Если адрес отличается, интерфейс поясняет допустимые причины: CGNAT либо последующая ротация.

Удаление proxy атомарно закрывает открытый интервал недоступности и переводит availability-state в REMOVED. При запуске controller выполняется безопасная сверка старых удалённых записей, поэтому архивный endpoint больше не может бесконечно увеличивать простой клиента и искажать сводку. Аварийная пересадка на уже восстановившийся домашний modem теперь фиксируется как AUTO_RETURNED, окончательно снимает FAILOVER и не запускает лишний последующий возврат. Target, который при записи оказался заполнен, до конца текущего цикла исключается из дальнейшего выбора.

06.08.2026 · 0.17.33

Расследование жалобы на клиентский 10207 выявило ошибку владения: recovery модема port-196 не нашёл свободного target и остался в BLOCKED_CAPACITY, но сам факт существования этой записи ошибочно выключил data-plane watchdog для исходного модема. Теперь watchdog подавляют только стадии с фактическим владением ресурсом: RESERVED, EVACUATING, HANDOFF_VERIFIED, NO_CLIENTS и ACTION_QUEUED. Заблокированная либо ещё не начатая maintenance больше не может оставить нерабочий client endpoint без повторной проверки, repair и failover.

04.08.2026 · 0.17.32

Автовыбор, failover и failback теперь используют единую консервативную занятость modem: максимум из assignments ModGuard и живых клиентских профилей Kraken. Drift между базой и Kraken больше не заставляет аварийную пересадку последовательно пробовать заведомо занятые modem; одна и та же метрика usedSlots участвует в статусе, запасе парка и projected load.

04.08.2026 · 0.17.31

Повторный обход больше не считает идемпотентный failback со статусом RUNNING неудачей. После restart controller операция сначала завершается или помечается reconciler, а кандидат не получает ложный двухчасовой backoff и событие FAILBACK_DEFERRED. Реальная data-plane ошибка по-прежнему включает persistent backoff.

04.08.2026 · 0.17.30

Production-проверка 0.17.29 выявила голодание maintenance-возвратов: один несвязанный отказ data plane откладывал все готовые failback до следующего обхода. Аварийные failover теперь по-прежнему имеют первый приоритет, но после них в том же цикле обрабатывается независимая очередь до четырёх готовых возвратов. Один и тот же assignment не может одновременно попасть в failover и failback. Обычные долгие возвраты всё ещё уступают любой аварии.

04.08.2026 · 0.17.29

Возврат клиентов после обслуживания разделён по причине. После страховочного reboot ротации отдельная очередь failback ждёт пять минут от завершения maintenance, свежий здоровый снимок home modem и подтверждённый новый IP. После recovery reboot либо обычного отказа применяется десятиминутное окно стабильности. Домашние места временно переселённых клиентов резервируются и не выдаются новым клиентам; аварийный failover сохраняет приоритет над любыми возвратами.

Страховка ротации теперь срабатывает после 15 минут без подтверждённой смены IP. Совпадение IP должно сохраняться минимум три наблюдения и 60 секунд, поэтому один запоздавший снимок не запускает reboot. Huawei-ротация ограничена четырьмя reboot за скользящий час и интервалом 15 минут. Recovery отказа допускает до четырёх modem reboot в час с тем же интервалом; router reboot выполняется по результату каскада без прежнего 12-часового запрета, а операторская заглушка по-прежнему допускает один modem reboot в 30 минут. История proxy показывает, является ли текущий modem домашним или временным и по какому правилу ожидается возврат.

03.08.2026 · 0.17.28

После полного Huawei maintenance-цикла найден один свободный port-173: мобильный трафик работал, но агент не подтверждал HiLink API, поэтому плановый reboot не мог быть выполнен. Любой такой Huawei теперь динамически исключается из новых выдач, ручных и аварийных пересадок, maintenance и failback, но уже работающий клиент не отключается. Подтверждённый следующим снимком API-доступ автоматически возвращает modem в пул. Параллельно исправлено обычное отображение фактического maxconn: все пути читают один и тот же агентский снимок.

Отдельный контролируемый эксперимент проверил гипотезу о длительной выдержке HiLink reconnect. На свободных Huawei обеих локаций выполнены строгие разрывы с 5, 10, 15 и 30 секундами ожидания; ни один не дал новый публичный IP. Часть прошивок вообще не удерживает 902 и самостоятельно возвращается через цепочку 903 → 900 → 901. Во время длинного 902 data plane возобновлял новые TCP-соединения раньше команды включения. Поэтому production-ротация Huawei остаётся на контролируемом reboot с предварительной эвакуацией клиентов; reconnect сохраняется только как ограниченная recovery-диагностика, а не как доказанный способ смены IP.

03.08.2026 · 0.17.27

Живое наблюдение двух maintenance-волн после 0.17.26 подтвердило исправность backpressure, но выявило оставшийся источник downtime: большинство строгих Huawei reconnect вернуло тот же IP, а три клиентских endpoint временно попали в data-plane recovery. Штатная ротация Huawei теперь сразу использует reboot; занятый modem обязательно эвакуирует клиентов до команды. В одном плановом цикле разрешена только одна новая занятая Huawei-maintenance на локацию; свободные модемы и аварийное client recovery этот предел не тормозит.

03.08.2026 · 0.17.26

Production-наблюдение после 0.17.25 показало, что новые страховочные reconnect могли появляться быстрее, чем единый controller завершал уже созданные maintenance-handoff и reboot. Добавлен scope-aware backpressure: пока есть исполняемая эвакуация, действие модема или проверка, новые ротации не создаются. Уже выданные команды, client recovery и same-location failover продолжаются. Записи BLOCKED_CAPACITY, BLOCKED_MAPPING и операции вне maintenance allowlist не блокируют парк бессрочно.

03.08.2026 · 0.17.25

Исправлена семантическая ошибка Huawei-ротации: dataswitch=0 выключает передачу данных, но не доказывает разрыв операторской сессии. Reconnect переведён на /api/dialup/dial и считается выполненным только после строгого перехода 901 connected → 902 disconnected → 901 connected с десятисекундной выдержкой после статуса 902. Аудит обоих Kraken не обнаружил отдельного штатного reconnect в Watchcat: 105 поднятых Huawei-профилей используют ROOter hostless, а Watchcat настроен на ping → reboot. Canary подтвердил, что одинаковый строгий reconnect может как сменить IP, так и вернуть прежний, поэтому после одной попытки без нового адреса следующей стратегией становится контролируемый reboot. Debug-режим и AT-команды в production не используются.

03.08.2026 · 0.17.24

Production-проверка 0.17.23 сняла 20 из 24 старых placement-блокировок после реального service-proxy probe и выявила обратную ошибку прежнего retry_after: до срока запись считалась активной, а после него исчезала из eligibility без доказательства исправления. Теперь «Временно не выдавать» является настоящим persistent gate и снимается только явным listener/data-plane подтверждением. UI больше не обещает фиктивную дату снятия, а показывает «Идёт автопроверка 3proxy».

03.08.2026 · 0.17.23

Короткий трёхсекундный Huawei reconnect удалён: применявшийся на тот момент mobile-dataswitch получил полную десятисекундную выдержку, а reboot остался последующим предохранителем. В 0.17.25 выяснено, что этот сигнал не доказывает разрыв операторской сессии, и метод заменён на строгий /api/dialup/dial. Для каждого нового port-N controller автоматически создаёт отсутствующий локальный служебный proxy 20000 + N; новый живой modem будит sync досрочно с минутным cooldown. Старые BLOCKED «Временно не выдавать» больше не ждут вслепую 12 часов: блокировка снимается только после совпадения config ID, фактических listener и успешного end-to-end HTTPS probe. Ошибка одного служебного proxy изолирована и не обрывает node sync.

03.08.2026 · 0.17.22

Страховочное окно ротации увеличено с 11 до 13 минут, чтобы штатный watchcat с таймером 600 секунд успевал завершить reconnect и передать новый IP. Agent теперь делает одну свежую IP-проверку непосредственно перед разрывом сессии и пропускает reconnect/reboot, если IP уже сменился. Диалог speed test определяет занятость по живым клиентским 3proxy-профилям, поэтому больше не скрывает обязательную галочку для несопоставленного исторического proxy. Таблицы различают заданный Kraken и фактический 3proxy maxconn, счётчик назван «одновременные TCP-сессии», а «Временно не выдавать» показывает понятную причину. В Wiki добавлена отдельная карта production-каталогов, баз, конфигов, журналов и backup.

03.08.2026 · 0.17.21

История клиентского proxy больше не показывает manager внутренние причины PROXY_DATA_PLANE_DOWN, public_ip_stale, duplicate_public_ip и recovery ID как основной текст. События получили понятные названия и короткое описание фактической ситуации: проверка трафика, точечный restart, аварийная пересадка, временная эвакуация перед сменой IP и возврат на домашний modem. Исходная техническая причина сохраняется отдельным полем для глубокого расследования. Production-аудит за 24 часа зафиксировал 348 data-plane failover, 426 maintenance-пересадок перед reboot и 176 возвратов; отдельной задачей остаётся снижение churn страховочной ротации и устранение ошибок владения 3proxy listener.

01.08.2026 · 0.17.20

Продолжение production-наблюдения выявило второй источник длинного точечного reload: один config Kraken-1 накопил девять старых 3proxy-процессов, и их последовательная остановка заняла 75 секунд, хотя фоновый lock уже был свободен. Targeted reload теперь пакетно прекращает приём новых соединений у всех noncanonical-процессов, одним чтением определяет активные TCP-сессии и возвращает управление после запуска актуального listener. Старые процессы с живыми сессиями дренируются фоновым reconciler и больше не задерживают аварийную проверку и failover пропорционально своему числу.

01.08.2026 · 0.17.19

Наблюдение первой production-итерации 0.17.18 подтвердило сокращение duplicate-reconcile на Kraken-2 примерно до одной секунды, но выявило исторический хвост Kraken-1: один конфиг содержал 6–10 старых процессов, которые всё ещё принимали соединения, поэтому их последовательная пауза удерживала listener lock 37–49 секунд. Reconcile теперь получает состояние всех noncanonical-процессов одним чтением /proc/net/tcp* и за один фоновый проход останавливает приём только у одного старого процесса на конфиг. Остальные явно учитываются как deferred и обрабатываются последующими проходами. Медленная очистка остаётся постепенной, а аварийный restart получает lock без длинной очереди.

01.08.2026 · 0.17.18

Страница «Ноды» получила полноценное Admin-only редактирование названия, физической локации и реквизитов MikroTik. Пароль намеренно отображается открытым администратору, но хранится в SQLite как Fernet ciphertext, исключён из общего API и audit. Проверка SSH выполняется новой приоритетной командой непосредственно с соответствующей Kraken-ноды через OpenSSH/SSH_ASKPASS и persistent known-hosts; UI показывает живое состояние и последний результат. Отмена и крестик закрывают окно без HTML-валидации, мобильная форма не имеет горизонтального переполнения. Production-аудит 0.17.17 выявил причину высокого churn: фоновая уборка старых bridge-процессов держала общий listener lock 35–135 секунд, тогда как watchdog признавал точечный restart неудачным через 15 секунд и запускал лишний failover. Теперь под lock остаётся только короткая проверка и остановка приёма новых соединений; drain и пакетное чтение TCP-состояния выполняются после освобождения lock. Верхний предел ожидания штатного restart увеличен до 30 секунд, успешный результат по-прежнему обрабатывается немедленно.

31.07.2026 · 0.17.17

Периодические proxy automation и modem maintenance сведены в один фоновый поток proxy-manager-control с явным приоритетом клиентского uptime. Пока data-plane suspect ожидает или проходит 30-секундную перепроверку либо занят automation lock, новая плановая эвакуация перед rotation/recovery не начинается и получает DEFERRED_CLIENT_RECOVERY. После завершения срочного решения maintenance снова допускается, даже если endpoint потребует дальнейшего технического внимания. Срочный worker остаётся отдельным только для своевременного I/O, но решение выполняет прежний единый automation cycle. Убрано подавление proxy-watchdog на время reconnect-команд: 30 секунд остаются естественным допуском штатной ротации, после чего неработающий клиентский endpoint получает reload и same-location failover. Production-наблюдение 0.17.16 перед изменением подтвердило точные перепроверки через 30–32 секунды и выявило реальное параллельное начало maintenance во время незакрытого suspect; новый арбитраж закрывает именно эту гонку.

31.07.2026 · 0.17.16

Клиентский uptime получил строгий приоритет над автоматическим возвратом размещения. Если data-plane watchdog увидел хотя бы один первый подозрительный endpoint, controller не начинает failback в этом цикле: он освобождает automation lock, чтобы отдельная 30-секундная перепроверка могла вовремя подтвердить восстановление или отказ. При подтверждённом отказе failback также не выполняется до завершения аварийных пересадок. Среди нескольких аварий сначала обслуживаются source-модемы, где reload 3proxy был пропущен как бесполезный либо source-config отсутствует; затем идут случаи, которым мог помочь точечный reload. Это устраняет production-сценарий, где безопасный возврат держал lock несколько минут, пока неработающий клиентский proxy ждал решения.

31.07.2026 · 0.17.15

Production-наблюдение пакетных перепроверок выявило три лишние задержки. Если source modem уже имеет нерабочий health-код, controller больше не тратит время на заведомо бесполезный reload 3proxy: событие получает понятный тип DATA_PLANE_RESTART_SKIPPED, после чего тот же центральный цикл переходит к локальной пересадке. Если исходный Kraken config уже отсутствует, watchdog делает одну проверку вместо шести ожиданий; многим клиентам одного config выполняется один общий reload, а их контрольные proxy-probe идут параллельно. Common-outage guard для срочного batch теперь использует полное число production-assignment ноды, поэтому три клиента одного отказавшего modem не принимаются за отказ всей location. Общая защита location по-прежнему срабатывает при действительно массовой недоступности.

31.07.2026 · 0.17.14

Срочные повторные проверки клиентских proxy, которые созрели одновременно после первого отказа, теперь объединяются в одно решение центрального controller. Он получает один свежий снимок всех назначений, параллельно проверяет их data plane и одним вызовом planner рассчитывает пересадки с общей картиной свободной ёмкости. Поэтому несколько клиентов с одного отказавшего modem или несколько одновременных отказов не конкурируют за одну и ту же цель и не ждут последовательных циклов глобальной automation lock. Общий обход остаётся пакетным раз в минуту, а подозрительные endpoint перепроверяются отдельным приоритетным контуром через 30 секунд. Rotation, recovery и maintenance по-прежнему передают разрушительные действия тому же controller через maintenance handoff; assignment lock, common-outage guard, same-location, target backoff, проверка listener и фактического data plane остаются обязательными.

31.07.2026 · 0.17.13

Целевой 3proxy config после изменения Kraken больше не считается готовым только по факту существования файла. Перед отключением source-контроллер повторяет точечный restart и требует, чтобы ответ агента подтвердил владение каждым переносимым клиентским listener-портом. Правило одинаково для одиночного failover и пакетной эвакуации перед rotation/recovery reboot. Если listener не материализовался за ограниченное число попыток, source не трогается, цель получает общий backoff, а автоматический failover может выбрать следующую цель. Изменение устраняет гонку, при которой Kraken read-back уже показывал новый modem, но 3proxy config догонял его на несколько секунд позже.

31.07.2026 · 0.17.12

Production preflight 0.17.11 обнаружил, что текст старой пакетной ошибки мог относиться только к одной из нескольких target-целей. Исторический backfill усилен третьим доказательством: отсутствующий Kraken config ID обязан совпасть с устойчивым crosswalk node + port name → Kraken modem ID. Crosswalk строится из сохранённых assignment и source-сторон maintenance-планов за 30 дней; Kraken API при старте БД не вызывается. Только после точного совпадения ID, повторения одной цели/config и минимум двух разных source modem создаётся начальный backoff. Live Kraken read-only сверка подтвердила четыре реальные проблемные цели: port-102, port-104, port-112 и port-118.

31.07.2026 · 0.17.11

Ротация, recovery, maintenance и proxy failover получили общий persistent eligibility gate для целевых modem. Если Kraken после ограниченного ожидания не материализует обязательный target config, цель блокируется для всех новых размещений на 30 минут, затем на 2 и 12 часов; успешный listener автоматически снимает блокировку. В manager такой modem имеет отдельное состояние «Временно не выдавать» и не уменьшает фактическую доступную ёмкость. Исторический backfill принимает только повтор одного target/config от двух разных source modem за последние шесть часов и не трактует одиночную неоднозначную ошибку либо штатно удалённый source как поломку цели. Аварийный failover при гонке ёмкости или неисправной первой цели теперь в том же цикле пробует следующую подходящую цель, не завышая projected load после неуспешной попытки. Подробный алгоритм добавлен в раздел единого контроллера.

31.07.2026 · 0.17.10

Production-наблюдение 0.17.9 подтвердило повтор задержанного target config и обнаружило другой штатный случай: после переноса последнего клиентского профиля Kraken удаляет старый source config. Его нельзя ждать как будущий target. Теперь target и source имеют разные правила. Новый target ограниченно ожидается и обязан подтвердить конкретный backend listener. Отсутствующий source немедленно проходит guarded orphan handoff: отсутствие orphan принимается только за подтверждённым target, существующий orphan ставится за replacement и переводится в draining после успешного внешнего data-plane probe. Ошибка probe до finalize возвращает paused orphan и передаёт управление общему rollback. Это убирает ложные откаты уже успешно работающих maintenance-пересадок. Полный suite: 330 тестов прошли, 10 платформенных тестов пропущены в локальной среде.

31.07.2026 · 0.17.9

Наблюдение полного maintenance-контура поймало гонку после успешного Kraken read-back: новый modem-id-N.cfg мог появиться позже первой команды точечного restart. Раньше точный ответ cfg was not found немедленно запускал rollback, а уже исчезнувший target config мог ошибочно дать ROLLBACK_FAILED, хотя клиент был возвращён на source. Теперь controller до шести раз с пятисекундной паузой ожидает только материализацию отсутствующего config; настоящие ошибки агента не повторяются. Ручная пересадка и maintenance используют один общий rollback helper: source listener и конкретный backend-порт подтверждаются первыми, затем target перезапускается либо guarded-проверкой доказывается отсутствие orphan. Полный suite: 328 тестов прошли, 10 платформенных тестов пропущены в локальной среде.

31.07.2026 · 0.17.8

Пять срочных production-долгов сведены в один релиз. Статический proxy automation allowlist удалён: отсутствие переменной защищает все текущие и будущие active assignment. Зависшие после рестарта EVACUATING теперь сверяются с фактическим Kraken mapping, target listener и освобождением source; только полностью подтверждённая эвакуация продолжает reboot, а неподтверждённая fail-closed освобождает reservation. Watchdog знает об ограниченном окне выполняющейся ротации или recovery и не запускает поверх неё вторую пересадку. Новый recovery-request присоединяется к уже выданной disruptive-команде того же modem. Аварийный failover и штатный failback получили независимые часовые бюджеты, поэтому новые отказы не блокируют возврат временных размещений; projected load обновляется после каждого возврата. Allocator приведён к фактическим persistent ingress: Kraken-1 10101–10160 и 10301–10999, Kraken-2 10161–10231. 11-минутная страховка расширена на обе ноды wildcard-разрешением с максимум четырьмя ожидающими и четырьмя выдаваемыми rotation-командами на ноду. Полный suite: 325 тестов прошли, 57 платформенных тестов пропущены на локальной macOS.

31.07.2026 · 0.17.7

Production-развёртывание 0.17.6 выявило старую причину периодических HTTP 502, которая существовала до изменения lifecycle API: unit controller имел MemoryMax=160M, тогда как рабочий процесс при построении manager bootstrap и выполнении proxy-операций стабильно достигал 162–175 МБ RSS. Cgroup OOM killer перезапускал controller; только 31.07 до релиза зафиксировано 12 таких убийств, а preflight старого 0.17.4 показывал 16 автоматических рестартов. Лимит controller повышен до 512M при 1 ГБ RAM VDS; swap не добавлялся и остальные сервисы сохранили собственные ограничения. Полный обратимый production-canary на существующем архивном клиенте подтвердил создание proxy за 8 секунд, фоновую data-plane проверку за 4 секунды, ручную same-location пересадку, удаление, восстановление с теми же endpoint и реквизитами и финальную очистку. Вывод lifecycle-canary теперь принудительно сбрасывается после каждого этапа, чтобы длительная проверка не выглядела зависшей. Unit-файл в Git приведён в соответствие с persistent systemd override на VDS.

31.07.2026 · 0.17.6

Пакетное удаление пяти тестовых proxy фактически завершилось в Kraken, но manager получил ложный HTTP 502 после долгого read-back и синхронизации. Для lifecycle-операций добавлен безопасный read-only статус по idempotency key с ограничением по сотруднику. Удаление сохраняет один ключ для всего выбранного набора и пишет текущую фазу и счётчик выполненных портов в proxy_operations. После 502, 504 либо разрыва сети интерфейс продолжает опрашивать controller, показывает «Удаление выполняется» и получает фактический SUCCEEDED, PARTIAL или ошибку; повтор не может удалить порт дважды. Прямая read-only сверка Kraken подтвердила отсутствие всех пяти выбранных профилей 10129, 10132, 10141, 10142 и 10145. Выборочная внешняя проверка второй location отдельно показала, что публичный адрес отвечает SYN-ACK на множество портов, но тестовый трафик не доходит до Kraken; это не считается доказательством широкого NAT без проверки из VDS либо конфигурации MikroTik.

31.07.2026 · 0.17.5

Выдача proxy разделена на быструю control-plane фазу и persistent data-plane verification. Kraken create/read-back, импорт и NAT по-прежнему обязательны до ответа manager, но последовательные внешние проверки больше не удерживают HTTP-запрос и не создают ложный 504 после фактического успеха. Каждый новый порт получает отдельное задание proxy_verification_jobs, четыре bounded worker, 30-секундное подтверждение первой ошибки и эскалацию в существующую цепочку «точечный reload → проверка → same-location failover → проверка». Интерфейс открывает группу сразу, показывает общий прогресс и живой статус каждого endpoint; состояния сохраняются после перезагрузки страницы. Неопределённый сетевой ответ сохраняет idempotency key, поэтому безопасный повтор не создаёт дубликаты. Nginx long-timeout теперь охватывает и base endpoint создания. Отсутствующий automation allowlist действительно означает весь парк, пустой список запрещает весь scope; новые production-выдачи больше не остаются за пределами watchdog. Очередь verification добавлена в operational metrics, а позднее восстановление общего watchdog закрывает прежнее предупреждение. Карточка клиента крупно показывает login и перечисляет все активные proxy с отдельными текущим статусом, действием автоматики и историческим простоем за 24 часа; прежняя жёлтая кнопка у уже восстановленных портов удалена. Персональная ёмкость разделена на пригодные уникальные modem и фактический минимум; технический размер public pool скрыт от manager и показывается Admin только при реальном дефиците маршрутизации. Node поддерживает несколько непересекающихся public_port_pools; пересечения на одном relay host запрещаются до запуска. Одна операция может выдать до 50 proxy. На MikroTik Kraken-1 после export добавлен persistent ingress TCP 10301-10999 к 192.168.88.20; прежние endpoint не изменены. Рейтинг частых переключений явно подписан как семидневный, мобильные окна выдачи защищены от горизонтального переполнения. Полный suite: 317 тестов прошли, 10 Linux/systemd/nft integration-тестов пропущены на локальной macOS.

30.07.2026 · 0.17.4

Production-наблюдение релиза 0.17.3 подтвердило быстрый пакетный обход 96 endpoint за 16,5 секунды и адресную перепроверку, но выявило следующий ограничитель: после неудачного точечного restart подтверждённая поломка клиента могла ждать освобождения общего лимита 12 failover в час. Для PROXY_DATA_PLANE_DOWN добавлен отдельный аварийный допуск, который действует только после двух адресных ошибок и не обходит защиту общей аварии, ёмкость, блокировки назначения, same-location, Kraken read-back, проверку listener и внешний data-plane rollback. Один flapping assignment ограничен тремя такими перемещениями за скользящий час; обычные modem-failover и возвраты не получили дополнительных разрешений. Настройка и персонально ограниченные кандидаты видны в результате automation-цикла.

30.07.2026 · 0.17.3

Ускорено подтверждение одиночного отказа клиентского proxy без учащения общего мониторинга. Полный data-plane обход по-прежнему выполняется одним пакетом и после завершения ждёт минуту, но bounded-пул увеличен с 4 до 24 I/O workers. Первая ошибка теперь создаёт дедуплицированную адресную перепроверку ровно через 30 секунд в отдельной очереди из четырёх workers; она не ждёт следующего полного обхода. Свежий assignment и его identity перечитываются перед probe, поэтому поздняя задача не применяет restart к уже пересаженному proxy. Повторная ошибка запускает прежний точечный reload, немедленную проверку и same-location failover в одном automation-контуре. Ожидание аварийного listener restart ограничено 30 секундами; после пересадки остаются обязательные Kraken read-back, listener confirmation, внешний data-plane probe и rollback. Пакеты не перекрываются, каждый сетевой probe имеет конечный timeout, common-cause guard и единый automation lock сохранены.

30.07.2026 · 0.17.2

Полный maintenance-rollout выявил две реальные гонки без потери клиентской связности. Долго ожидавшая вне canary операция могла сосуществовать с позднее доставленной обычной rotation-командой; после безопасной эвакуации destructive-команда отклонялась, а handoff откатывался. Теперь maintenance до перемещения клиентов явно ждёт завершения любого уже выданного controller command, планировщик ротации не создаёт новые команды для modem с незавершённой maintenance, а новый recovery-request присоединяется к тому же владельцу вместо второго контура. Проверка остаётся и непосредственно перед постановкой reboot, поэтому поздняя гонка завершается fail-closed.

Второй production-случай подтвердил внешний data plane после пересадки, но agent отклонил rotate_ip_reboot, когда канал source успел перейти из HEALTHY в timeout. Для обычного reconnect требование здоровья сохранено. Уже разрешённый controller reboot HiLink теперь выполняется и при временно нездоровом канале, если modem присутствует, имеет проверенный driver, входит в rotation allowlist и не принадлежит recovery-инциденту. Это позволяет завершить восстановление после эвакуации, не возвращая клиента на проблемный modem. Полный suite: 306 тестов прошли, 54 платформенных и интеграционных теста пропущены в локальной среде.

Controller и обе Kraken-ноды обновлены до 0.17.2, после чего maintenance allowlist расширен на весь поддерживаемый парк обеих локаций. Scope центральной плановой ротации намеренно не расширялся и остался на трёх canary; автоматический failover продолжает защищать все 96 production assignment. Первый полный кейс на свободном port-160 выполнил rotate_ip_reboot за 35 секунд и закрыл maintenance как COMPLETED; во время проверки все 96 клиентских endpoint оставались доступны. Публичный installer bundle, который отстал от живых нод на версии 0.16.4, пересобран из 0.17.2 и опубликован с новым SHA-256, поэтому команда установки в админке больше не понижает agent.

После обычного входа Operator и Admin теперь сразу открывают рабочую страницу «Прокси»: подтверждённый uptime клиентских endpoint является главным эксплуатационным экраном. Viewer остаётся на доступном ему «Парке». Явные ссылки с hash на «Парк», модемы, инциденты, ноды, Wiki или пользователей имеют приоритет над стартовым redirect, поэтому технические закладки и ссылки из расследований продолжают открывать нужный раздел.

30.07.2026 · 0.17.1

Перед расширением безопасной эвакуации клиентов найден пограничный эффект старых maintenance dry-run: состояние OBSERVED правильно никогда не исполнялось после перехода в ACTIVE, но при позднем добавлении того же modem в allowlist могло исключить его assignment из обычного proxy failover. Теперь активный controller терминально закрывает все прежние OBSERVED как CANCELLED, сохраняет отдельный audit MODEM_MAINTENANCE_DRY_RUN_CLOSED и не считает dry-run владельцем modem либо assignment. Исполняемые PENDING, capacity reservation, Kraken read-back, точечный listener handoff, внешний data-plane verifier, групповой rollback и agent-side destructive fence не ослаблены. Maintenance-контур расширяется с восьми canary на весь поддерживаемый парк обеих нод, а центральная ротация сохраняет прежний узкий canary-scope. Автоматический аварийный failover по-прежнему охватывает все 96 production assignment.

Отдельный текущий статус RESERVE упразднён. Все прежние ручные резервы атомарно мигрируют в QUARANTINE с источником manual_quarantine; старые API-команды и браузерный кэш со значением RESERVE принимаются как совместимый псевдоним. В интерфейсе, фильтрах, KPI и текущих записях остаётся один карантин. Автоматически изолированный modem возвращается после 15 минут здоровья, ручной — по команде сотрудника либо после появления нового физического устройства. История и интервалы исключения сохраняются без потери данных. Полный suite: 302 теста прошли, 10 платформенных тестов пропущены.

30.07.2026 · 0.17.0

Ручное исключение и автоматический технический карантин получили единый жизненный цикл участия модема в production. Автоматический карантин разрешён только после трёх часов подтверждённого отказа, полной эвакуации клиентских назначений и при отсутствии общего сетевого сбоя location. После 15 минут непрерывного здоровья автоматически изолированный modem возвращается в работу; ручное исключение сохраняется до решения сотрудника или появления нового устройства. Периоды исключения хранятся отдельно и очищают только будущий operational modem KPI: отказ до карантина и весь подтверждённый простой клиентского proxy сохраняются. Backend watchdog начал вести persistent сегменты доступности публичных endpoint. Manager получил панель фактического клиентского качества, фильтры клиентов с простоем, агрегаты 24 часа/7/30 дней в карточке и приоритетный вывод прямого proxy downtime в расследовании. Профиль modem объединяет причину, действия восстановления, пересадки proxy, результат и карантин в одной технической хронологии. Полный suite: 300 тестов прошли, 10 платформенных тестов пропущены в локальной среде.

30.07.2026 · 0.16.17

Полный production-rollout показал, что общий лимит переключений считал одинаково аварийные пересадки AUTO_MOVED и безопасные возвраты AUTO_RETURNED. Один failover и четыре failback заняли лимит пяти действий в час, поэтому следующий настоящий отказ мог ждать освобождения бюджета, хотя все защитные проверки были готовы. Теперь аварийная пересадка имеет отдельный бюджет и всегда важнее возврата: события возврата не уменьшают доступный аварийный бюджет, а failback вообще не запускается в цикле, где есть хотя бы один аварийный кандидат. Common-outage guard, same-location, assignment lock, cooldown, capacity, Kraken read-back, listener/data-plane verification и rollback остаются обязательными. Часовой предел теперь управляется центральным API в диапазоне 1–100; для текущих 96 production-прокси установлен предел 12. После 0.16.16 controller проверил все 96 endpoint без текущих отказов, девять внешних canary прошли 27 из 27 запросов, на обеих Kraken-нодах нет дублирующих listener, а после релиза не появилось новых операций NEEDS_ATTENTION.

30.07.2026 · 0.16.16

Автоматическая локальная пересадка последовательно расширена с canary до 25, 50 и всех 96 действующих production-прокси. За время расширения controller выполнил пять настоящих аварийных пересадок с Kraken read-back, проверкой единственного listener и внешнего data plane. Наблюдение обнаружило, что два одновременных отказа могли выбрать один и тот же быстрый резервный modem: прежняя сортировка учитывала скорость раньше прогнозируемой загрузки. В одном таком случае Kraken впоследствии удалил общий config, поэтому клиентские 10115 и 10129 были немедленно восстановлены через центральный lifecycle API на двух разных modem; обе операции завершились SUCCEEDED, очереди очистились, а каждый endpoint получил ровно один listener. Теперь planner внутри одного цикла сначала использует разные подходящие modem, сортирует их по совпадению оператора и прогнозируемой доле занятых слотов, а скорость применяет только следующим критерием. Повторное размещение на уже выбранную цель допускается лишь когда свободных альтернатив в текущем цикле больше нет. Регрессионный тест воспроизводит два одновременных отказа и подтверждает распределение по двум разным целям. Полный suite: 295 тестов прошли, 52 platform-теста пропущены в текущей среде.

30.07.2026 · 0.16.15

Pre-expansion аудит действующего canary поймал две неуспешные попытки auto-failover клиентского 10146. Kraken уже удалил исходный modem-id-51.cfg, target modem-id-16.cfg был перезапущен и подтверждён на нужном listener, а исходного orphan-процесса уже не существовало. Прежняя логика ошибочно требовала обязательный orphan pause, затем начала rollback и прекратила его на первом мгновенно отсутствующем source config. Из-за этого target config не успевал перечитать обратное назначение, и до фоновой сверки порт мог временно иметь два принимающих процесса. Теперь отсутствие source orphan считается штатным только при одновременно подтверждённом target-listener; после этого обязательный внешний data-plane probe всё равно решает исход операции. При настоящем rollback controller ограниченно ждёт появления source config и подтверждения именно клиентского порта, сохраняя рабочий target listener на время ожидания. Лишь после готовности source очищается target; если его файл уже исчез, применяется тот же guarded orphan handoff в обратную сторону. Тесты воспроизводят production-сценарий source 51 → target 16, временное отсутствие rollback-config и строгий порядок «сначала восстановить source, потом убрать target». Production allowlist не расширен.

30.07.2026 · 0.16.14

Read-only аудит фактических socket-owner обнаружил, что исторические transient unit прежних версий ModGuard всё ещё принимали новые соединения рядом с Supervisor: 31 config на Kraken-1 и 21 config на Kraken-2. Из-за SO_REUSEPORT корректный controller state не гарантировал, что новый клиент попадёт в актуальный процесс. Canary на 10105/20105 выявил дополнительный дефект: первое переключение parity могло лишь кратко скрыть listener, после чего старый PID снова начинал accept, хотя helper уже сообщил draining. Pause теперь требует устойчивого отсутствия listener, повторно проверяется перед draining и при необходимости делает вторую штатную попытку. Добавлен ограниченный agent-reconciler с общим restart-lock: он сначала устраняет принимающие дубли, сохраняет старые TCP-сессии до естественного завершения и не изменяет Kraken, NAT, assignment или modem. Включение выполняется по одной production-ноде после canary.

30.07.2026 · 0.16.13

Длительное внешнее наблюдение точно измерило штатную ротацию проблемного 10107: четыре неуспешные проверки между 22:37:10 и 22:37:27 UTC, восстановление с новым IP в 22:37:33. Один такой 20–30-секундный перерыв не должен вызывать действий, однако прежняя система не видела бы отдельный продолжительный отказ 3proxy при здоровом modem. Добавлен backend data-plane watchdog, работающий только внутри непустого production assignment allowlist. Первый отказ журналируется без действий; второй цикл начинается спустя минуту и при повторной ошибке запускает безопасный точечный reload конкретного 3proxy; неудачный repair создаёт кандидата PROXY_DATA_PLANE_DOWN для существующей same-location пересадки. Auth-ошибка не вызывает перемещение, а общий отказ минимум трёх или 20% пилотных endpoint одной location включает отдельный guard. Подтверждённый data-plane отказ может обойти switch cooldown, но не assignment lock, capacity reservation, rate limit, location guard, Kraken read-back, NAT reconcile, проверку результата или rollback. Production allowlist не расширен.

После развёртывания 0.16.13 выполнено 2187 внешних проверок девяти разрешённых endpoint. Все 15 кратких ошибок совпали со штатной ротацией, завершились новым публичным IP и не вызвали reload либо пересадку; отдельный backend-прогон с VDS прошёл 27 из 27 раз. Maintenance-canary на port-137 подтвердил единый controller-managed контур: source без активных клиентов получил одну команду reboot, а уже эвакуированный клиент на port-111 прошёл 20 из 20 публичных проверок. Операторская заглушка после reboot сохранилась, поэтому клиент не возвращён на source, один incident остаётся открытым, а следующая попытка выполняется по 30-минутной политике. После итерации agent, speed-test и proxy lifecycle очереди пусты, конкурирующих команд не обнаружено.

30.07.2026 · 0.16.12

Поштучная очистка исторических 3proxy-дублей выявила важную особенность штатного сигнального интерфейса: SIGCONT в 3proxy переключает внутреннее состояние pause/continue, а не является однозначной командой pause. У давно живущего transient-процесса первый сигнал мог оставить listener открытым, хотя следующий корректно прекращал приём новых соединений. Все пути точечного reload, удаления неканонического дубля и guarded orphan handoff теперь используют единый parity-safe helper. После каждого переключения проверяются реальные listening socket inode конкретного PID; при необходимости выполняется ровно одна повторная попытка. Если listener не исчез после двух попыток, операция завершается fail-closed, процесс не получает SIGTERM, а штатный listener продолжает обслуживать трафик. Уже принятые клиентские сессии по-прежнему не обрываются и дренируют естественно. Регрессионный тест воспроизводит инвертированную чётность сигнала. Production allowlist, fleet restart и правила ротации не меняются.

30.07.2026 · 0.16.11

Реальная пересадка клиентского 10120 выявила рассинхронизацию Kraken: профиль и работающий старый 3proxy-процесс существовали, но исходные modem-id-25.cfg и Supervisor definition уже отсутствовали. Обычный target-first reload правильно поднимал новый listener, однако обязательный restart отсутствующего source завершал всю операцию rollback. Добавлен отдельный fail-closed протокол для такого edge case. Agent принимает его только если source-файл действительно отсутствует, исходный процесс по cmdline всё ещё владеет нужным клиентским портом, а другой единственный Supervisor-процесс уже устойчиво обслуживает тот же порт из актуального target-конфига. Сначала старый orphan прекращает принимать новые соединения через штатный 3proxy pause, но сохраняет уже открытые TCP-сессии и локальное состояние для обратного resume. Controller выполняет внешний data-plane probe с числом попыток до пяти на transient readiness; при любой ошибке до фиксации операции listener возобновляется, Kraken и БД откатываются. Незавершённый pause автоматически снимается agent через пять минут при исчезновении controller. Подтверждённое завершение процесса отложено минимум на минуту и повторно проверяет PID по точному cmdline, replacement listener и отсутствие активных сессий, поэтому PID reuse, исчезновение target или сбой agent не превращаются в ошибочный kill. Команды prepare/resume/finalize используют общий listener lock, имеют приоритет над ротацией и speed test и не расширяют production allowlist.

30.07.2026 · 0.16.10

Ночное поэтапное удаление исторических дублей 3proxy выявило дополнительную границу доказательства: полный набор socket у Supervisor подтверждает готовность listener, но не гарантирует, что давно запущенный процесс уже перечитал последнюю версию Kraken-конфига. Порядок точечного reload усилен. Сначала временный bridge поднимается непосредственно из актуального файла и подтверждает все порты, затем Supervisor выполняет штатный pause/continue с перечитыванием конфига и проходит секундную проверку стабильности, и лишь после этого прежние transient и bridge прекращают принимать новые соединения и дренируют уже открытые TCP-сессии. Если bridge или обновлённый Supervisor не доказаны, старые listener не удаляются. Регрессионный тест моделирует одновременно старый Supervisor и более новый transient и требует строгий порядок bridge → Supervisor reload → retire duplicates. Production-наблюдение отдельно отличило 25–30-секундный reconnect с фактической сменой IP от listener-операций: затронутые очисткой endpoint отказов не дали.

30.07.2026 · 0.16.9

Production-аудит процессов выявил системный конфликт владельцев: на Kraken-1 два процесса одновременно обслуживали 55 конфигов, на Kraken-2 — 58. Старый transient ModGuard оставался в собственном systemd cgroup после перезапуска supervisord, новый supervisor-child поднимался рядом, а SO_REUSEPORT распределял новые соединения между ними. Это могло отправлять часть клиентов по устаревшему modem после корректной пересадки «на бумаге». Постоянным владельцем теперь является Supervisor. Точечный reload использует временный bridge, штатный pause/continue с перечитыванием конфига, устойчивую проверку socket inode и graceful drain bridge. Отдельный reconciler удаляет старые дубли только при доказанном полном Supervisor-listener; активные сессии не обрываются принудительно. Canary на свободных port-160 и port-171 подтвердили правильный итоговый PID, мобильный IP и по 199 успешных запросов из 200; единичный reset при закрытии SO_REUSEPORT socket зафиксирован как ограничение архитектуры и основание для будущего стабильного TCP frontend.

30.07.2026 · 0.16.8

Утренний production-аудит подтвердил различие пространств идентификаторов: клиентский 10144 назначен Kraken ID 49 с именем port-144, тогда как agent ID 49 относится к port-149. Управляющие циклы уже сопоставляли устройства по имени, но bootstrap/UI при отсутствующем имени мог угадывать modem по числовому ID и показать неверного оператора, клиента или емкость. Этот fallback удалён: связь без подтверждённого node + port name теперь остаётся неизвестной. Регрессионные тесты покрывают правильный crosswalk Kraken 49 → port-144 → local 44 и fail-closed случай без имени. В том же наблюдении stale reboot-intent port-149 был автоматически отменён после успешного reconnect, а клиентский 10149 подтверждён тремя внешними проверками через Яндекс Интернетометр.

29.07.2026 · 0.16.7

Реальный production failover выявил лишнюю задержку после безопасного rollback: idempotency key автоматической пересадки жил десять минут, поэтому следующая попытка считалась повтором уже завершившейся неудачной операции. Теперь ключ включает выбранный target и минутное retry-окно. Одновременные дубли на тот же target по-прежнему объединяются, другой target можно попробовать сразу, а тот же target после transient race — не позднее следующей минуты. Ограничение успешных переключений в час, assignment-lock, outage guard, Kraken read-back, точечный listener reload, data-plane verification и rollback остаются обязательными.

29.07.2026 · 0.16.6

Ночное production-наблюдение выявило скрытый риск будущего расширения active allowlist: recovery-заявки вне пилота оставались PENDING после фактического восстановления modem. Текущий scope надёжно блокировал их, но позднее расширение могло сначала эвакуировать клиента и только затем получить от agent ответ, что reboot уже не нужен. Теперь controller до scope-фильтра и до любого планирования отменяет stale recovery при свежем HEALTHY, а stale rotation-reboot — при уже изменившемся публичном IP. Операция получает терминальное CANCELLED и диагностическую причину; Kraken, NAT, listener, capacity и command queue не затрагиваются. Состояния после начала handoff намеренно не прерываются этим правилом. Добавлены тесты отмены восстановленного recovery вне active scope, завершённой ротации и сохранения штатной цепочки для всё ещё актуального reboot.

29.07.2026 · 0.16.5

Production-пилот обнаружил, что после перезапуска supervisord прежний orphan 3proxy и новый supervisor-child могут одновременно слушать одинаковые порты с SO_REUSEPORT. Прежний verifier принимал первый удачный выход через target и мог не заметить, что часть следующих соединений попадёт в старый процесс. Теперь любая ручная или автоматическая пересадка и каждый maintenance-handoff детерминированно перезапускают target config, затем source config, и только после этого принимают data-plane результат. Target-first порядок минимизирует accept gap; source reload удаляет stale listener. Добавлен безопасный внешний probe нескольких assignment, который не печатает реквизиты и продолжает проверку при единичном отказе. Во время наблюдения подтверждены короткие разрывы штатной IP-ротации: один неуспешный probe сопровождался фактической сменой IP и восстановлением к следующему циклу; это не считается основанием для двух дополнительных proxy-reload.

29.07.2026 · 0.16.4

Автоматический failback получил idempotency key конкретного failover-эпизода вместо общего 15-минутного временного bucket. Ключ строится из assignment, фактического source-размещения, home-размещения и last_switched_at. Повтор одной и той же попытки по-прежнему безопасно дедуплицируется, но новое maintenance-переселение того же proxy в пределах тех же 15 минут уже создаёт отдельную операцию и возвращается сразу после health grace. Изолированный canary обнаружил прежнюю коллизию после двух последовательных эвакуаций: старый успешный результат повторно читался без нового Kraken move.

29.07.2026 · 0.16.3

Граница dry-run расширена на modem без клиентских назначений. В режиме observe результат «пересадка не требуется» теперь также сохраняется как OBSERVED, а пояснение No active customer assignments use this modem остаётся внутри плана. Ранее такое наблюдение получало исполняемое состояние NO_CLIENTS и могло продолжиться после будущего включения ACTIVE. Новый тест доказывает отсутствие modem-команды и после смены режима. Накопленные до обновления неоднозначные NO_CLIENTS intents должны быть проверены и отменены перед расширением active allowlist.

29.07.2026 · 0.16.2

Dry-run maintenance получил необратимую границу безопасности. Операция в состоянии OBSERVED остаётся только сохранённым планом и не выполняется автоматически после последующего переключения controller в ACTIVE. Для боевого действия должен появиться новый PENDING intent, прошедший актуальное планирование, allowlist и все проверки. Ограничение действует и в maintenance-loop, и в транзакционном резервировании ёмкости. Изменение принято после изолированного production-canary: старый observe-план корректно пересадил только технический assignment и не дал клиентского downtime, но сам факт неявного повышения режима признан небезопасным.

29.07.2026 · 0.16.1

Добавлен независимый allowlist автоматических proxy-назначений MODGUARD_PROXY_AUTOMATION_ALLOWLIST_JSON. Он ограничивает одним списком и аварийную пересадку, и автоматический возврат, включая ранее накопленные failback-кандидаты. Это позволяет включать controller в ACTIVE для специально созданного canary assignment, не открывая запись для production-клиентов. Пустой массив блокирует все назначения, отсутствие переменной сохраняет обычную работу всего парка. Текущая область видна в bootstrap и журналируется вместе с настройками rollout.

29.07.2026 · 0.16.0

Recovery, maintenance handoff, ротация, speed test и proxy failover сведены под единое решение controller. Production-agent с controller_managed_recovery=true больше не выполняет reconnect/reboot самостоятельно: он отправляет типизированный запрос, а controller дедуплицирует его, отменяет конфликтующие ожидающие команды и при destructive-действии запускает групповую эвакуацию всех клиентов. Перед reboot добавлен второй независимый fence по живым локальным 3proxy-конфигам. Recovery-команды имеют приоритет; speed test не ставится при incident, maintenance или незавершённой modem-команде. Точечный restart сериализуется с тем же config ID, fleet restart ждёт все modem-команды. Circuit breaker повторно проверяется непосредственно перед исполнением уже выданного recovery. Режим active требует явный allowlist, что позволяет canary одного modem без открытия всего парка. Автоматический failover игнорирует только те maintenance operations, которыми данный active-scope действительно владеет.

29.07.2026 · 0.15.0

Добавлен Maintenance Orchestrator для безопасного reboot занятых modem. Страховочная ротация больше не отправляет destructive-команду напрямую, если agent или центральные назначения видят клиентские profiles. Появились persistent intent, глобальный min-cost planner расселения, атомарные резервы емкости, batch read-back, параллельная data-plane проверка всех endpoint, дедупликация точечных restart по config и полный rollback. Перед reboot повторно проверяются нулевые live profiles source и сохранность всех target-размещений. Первый production-релиз включается в observe: планы и блокировки видны, но клиенты, Kraken и command queue не изменяются. Исправлена ошибочная классификация login deslamer как служебного; шаблоны agent оставляют служебным только rooot.

29.07.2026 · 0.14.19

Модельный canary свободных устройств подтвердил смену IP на Fibocom L860 с прошивками 18600.5001.00.35.00.02 и 18600.5001.00.35.01.57 за 22,7 и 29,2 секунды, а также router-mediated Huawei за 34,9 секунды. Проверка обнаружила расхождение учёта: Huawei с identity_access_path=router фактически перезагружался под общим действием rotate_ip. Теперь controller сразу классифицирует это действие как rotate_ip_reboot, применяет отдельный предел reboot и показывает фактический метод в интерфейсе. Полный парк не включался; центральная ротация оставалась выключенной во время canary.

29.07.2026 · 0.14.18

После production canary страховочная ротация получила второй уровень защиты от клиентского downtime. Для Huawei введён отдельный предел двух reboot-ротаций за скользящий час; общий предел десяти попыток сохраняется. Повторные неудачи теперь увеличивают cooldown с 60 до 120 и затем до 300 секунд, а успешная смена IP начинает новую серию. В этой версии прямой HiLink reconnect впервые перестал доверять только ответу OK, но переход dataswitch 1→0→1 позднее оказался недостаточным доказательством отключения сессии и был заменён строгим 901→902→901 в 0.17.25. Максимальный backlog ротаций на одну ноду уменьшен с 64 до восьми команд. Перед релизом центральная ротация оставлена выключенной: reconnect/reboot не возобновляются автоматически только от установки новой версии, пока ограничители не пройдут отдельный production canary.

29.07.2026 · 0.14.17

Устранён критический цикл страховочной ротации: часовой предел ранее считал только ошибки и не учитывал успешные reboot, из-за чего отдельные клиентские модемы получали сотни попыток и более ста reboot за сутки. Теперь жёсткий лимит считает каждую созданную попытку, включая PENDING, DELIVERED и успешные COMPLETED. Command plane стал capacity-aware: агент сообщает свободные слоты, controller не переводит лишние команды в DELIVERED, а до восьми независимых modem-команд исполняются параллельно. Listener restart сохраняет отдельную срочную полосу. Глобальное планирование удалено из быстрого ACK и выполняется только после свежего snapshot. Обычное recovery за цикл обслуживает до трёх старейших готовых modem в рамках временного бюджета. Исправлен первый автоматический failover без существующих FAILOVER-размещений; успешные move теперь сразу резервируют расчётный слот, поэтому следующая операция пачки видит уже добавленную нагрузку. Ежедневные SQLite backup после проверки сжимаются Zstandard и хранятся в двух экземплярах. Свежий зашифрованный backup проверен локальным восстановлением, после чего VDS очищен с 71% до 41%.

29.07.2026 · 0.14.16

Разбор задержки production canary подтвердил, что управляемый массовый restart был выключен, cron и systemd timers на обеих нодах отсутствовали. Причиной ожидания стала синхронная обработка controller-команд на агенте: две долгие ротации заняли command-control thread на 153 секунды, поэтому уже созданная точечная команда 3proxy не запрашивалась до их завершения. Диспетчер разделён на срочную однопоточную очередь listener restart и независимую очередь ротаций/Internetometer. HTTP poll продолжает выполняться каждые 5 секунд во время долгих действий; повторно полученная DELIVERED команда не запускается второй раз, а результаты по-прежнему сначала сохраняются в локальный SQLite spool и только затем подтверждаются controller. Добавлен конкурентный тест, доказывающий выполнение listener-команды при заблокированной ротации.

29.07.2026 · 0.14.15

Повторный production canary обнаружил, что операторский CGNAT на MTS способен выдавать разные публичные IPv4 прямой проверке modem и отдельному соединению через клиентский proxy. Побайтное сравнение адресов давало ложный rollback уже применённой пересадки. Проверка теперь опирается на три независимых факта: Kraken read-back указывает target modem; точечный restart подтверждает, что backend-порт слушает процесс целевого modem-id-N.cfg; живой proxy проводит HTTPS-запрос и получает публичный IPv4. Совпадение со снимком IP остаётся диагностикой. Если принадлежность listener целевому конфигу не доказана, строгая проверка по свежему IP сохраняется. Ответы точечного restart дополнены полным списком поднятых listener-портов, включая объединение с уже идущим fleet pass.

29.07.2026 · 0.14.14

Production lifecycle canary выявил, что долгий move мог продолжиться после 60-секундного timeout nginx, а cleanup начинал delete того же assignment до завершения move. Все изменяющие lifecycle-операции одного assignment_id теперь сериализуются общим process lock: move, delete, restore и изменение maxconn не могут пересечься; пакетные операции берут locks в стабильном порядке и не создают deadlock. Для этих четырёх API nginx получил отдельные read/send timeout 480 секунд, соответствующие максимальному ожиданию точечного restart и agent snapshot. Canary-клиент также ждёт до 480 секунд. Другие HTTP-запросы сохраняют короткий общий timeout, поэтому зависший обычный запрос не удерживает worker без необходимости.

29.07.2026 · 0.14.13

Production-восстановление 10222 выявило гонку между data-plane проверкой и ротацией IP target modem. После точечного restart рабочий proxy мог уже показывать новый IPv4, а последний snapshot ещё содержал предыдущий адрес. Verifier теперь ограниченно ждёт до 60 секунд, пока фактический proxy IPv4 появится в независимом agent snapshot target; только подтверждённое совпадение принимается, а отсутствие сходимости по-прежнему вызывает полный rollback. Два последовательных здоровых snapshot после отказа автоматически закрывают зависшие terminal-инциденты BLOCKED_EXTERNAL и NEEDS_HUMAN, сохраняя первым временем восстановления момент первого успешного snapshot и исторический простой. Удалён production HTTP endpoint тестовых инцидентов, а legacy simulator/SIM-001 и его синтетические данные идемпотентно очищаются при migration. Удалённые proxy-назначения больше не сохраняют failback backoff или marker потерянного mapping.

29.07.2026 · 0.14.12

Исправлен аварийный контур локальной пересадки. Если Kraken profile продолжает ссылаться на modem, которого больше нет в полном inventory агента, controller сохраняет момент первого расхождения и число последовательных наблюдений. После двух наблюдений и общего 10-минутного grace такое назначение становится кандидатом MODEM_MAPPING_MISSING без фиктивного modem-инцидента. Ручная и автоматическая пересадка больше не требуют существования исходного local mapping: target всё равно обязан иметь однозначное Kraken-сопоставление, свободный слот, подтверждённое здоровье и публичный IPv4, а результат подтверждается Kraken read-back и фактическим data-plane. Failback заранее отсекает заполненный home, отсутствие IP и повторное размещение того же клиента. Ошибка до первой записи в Kraken учитывается как FAILED, а ROLLED_BACK теперь означает, что запись действительно выполнялась и была возвращена.

28.07.2026 · 0.14.11

Время выполнения Internetometer теперь нормализуется по часам агента: если быстрый отказ или пропуск не содержит отдельного времени старта, старт приравнивается к завершению на ноде, а не к более позднему получению результата контроллером. При запуске controller идемпотентно исправляет уже сохранённые строки, где старт оказался позже завершения. Lifecycle canary использует тот же проверенный data-plane probe с региональным fallback, что и production lifecycle, и поддерживает отдельный режим проверки изнутри relay VDS.

28.07.2026 · 0.14.10

Production canary 0.14.9 выявил ложный rollback автоматической пересадки во время белых списков: строгий data-plane verifier ходил только на собственный VDS endpoint, который был заблокирован мобильным оператором, хотя proxy и Яндекс работали. Добавлен ограниченный по времени HTTPS fallback к API Яндекс Интернетометра с обязательным разбором публичного IPv4 и последующим сравнением с target modem. Локальный Connection refused по-прежнему считается readiness failure и fallback не скрывается. До успешного canary автоматические пересадки штатно переведены в STOPPED; мониторинг, recovery modem и ротация IP продолжают работать.

28.07.2026 · 0.14.9

Устранены три срочных эксплуатационных хвоста. Proxy lifecycle получил безопасные фазы и reconciler: старые RUNNING/ROLLBACK_FAILED завершаются по фактическим событиям, Kraken read-back и data-plane без слепого повтора side effect. Точечный 3proxy restart больше не путает local modem ID с Kraken config ID, объединяется с уже идущим fleet restart, использует общий bounded lock и способен подтвердить замену конкретного listener до окончания полного прохода. Планировщик не ставит fleet поверх ожидающей точечной команды. После расследования клиентских ошибок периодический restart выключен на обеих нодах: проход каждые 15 минут обрывал активные TCP-сессии. IP-аналитика modem dashboard переведена с повторного 30-дневного UNION/GROUP BY сырых событий на транзакционно поддерживаемый компактный индекс наблюдавшихся адресов; сырая история сохранена без сокращения, а измеренная медиана ответа уменьшилась до 0,90–1,03 секунды. Internetometer и ротация больше не выполняются одним пакетом команд; ошибка передачи, смена IP в окне теста или download не выше 4 Мбит/с запускают один повторный полный замер после восстановления доступности, а обе попытки сохраняются в результате. Проверены 210 автоматических тестов.

27.07.2026 · 0.14.8

Исправлена проверка operator-specific IP-пулов на production-данных. Исторические алиасы одного оператора, например MTS RUS и МТС, теперь приводятся к одному имени без потери счётчиков. Диапазоны /24 считаются аналитическими корзинами по 256 адресов, а не подтверждёнными сетевыми масками по 254 host-адреса; поэтому покрытие больше не превышает 100%. Сравнение операторов отдаёт компактные агрегаты без повторной передачи списков подсетей, что уменьшает размер ответа и время открытия modem dashboard.

27.07.2026 · 0.14.7

IP-пул перестал смешивать операторов одной location. Дашборд отдельно показывает фактически замеченные уникальные адреса за 24 часа, 7 и 30 дней для выбранного modem, его оператора на этой location и всей location со всеми операторами. Добавлена сравнительная таблица операторов, операторские подсети, повторы, пересечения, концентрация и прогноз расширения на 100/200 таких SIM. Неоднозначное число «пул location» заменено точными подписями. «Верхняя граница известных /24» отделена от фактического пула и явно не считается гарантией CGNAT. Новые смены IP сохраняют оператора в момент события; старая история однократно классифицируется по текущей привязке modem.

27.07.2026 · 0.14.6

Исправлен отказ выдачи архивному клиенту и непрозрачный повтор rolled-back операции. Поле нового клиента стало combobox с активными и архивными совпадениями, защитой от опечаток и явным транзакционным возвратом: карточка становится активной только после успешного Kraken read-back, NAT и data-plane проверки. Controller сверяет client_id, login и разрешение возврата, а повтор idempotency key показывает исходную причину. Ёмкость выдачи теперь учитывает не только пригодные modem, но и реальные свободные номера публичного диапазона каждой ноды. В дашборд modem добавлен аналитический блок публичного IP-пула: уникальные адреса и подсети за 24 часа/7/30 дней, повторы, пересечения между modem, концентрация, качество выборки и осторожный прогноз расширения location на 100/200 устройств. Расчёт работает по центральной истории и не создаёт дополнительной нагрузки на modem.

27.07.2026 · 0.14.5

Расследование жалоб получило доступный combobox клиентов и proxy-портов: поиск по части login, вставка полного proxy-адреса, клавиатурная навигация, защита от неоднозначного запроса и честный статус отсутствия данных. Диалог новой выдачи расширен на desktop, поэтому таблица ручного выбора modem помещается без горизонтальной прокрутки, сохраняя карточный вид на телефоне. Удаление отвязано от целой дневной группы: manager выбирает один, несколько или все активные порты, видит остаток и подтверждает конкретный набор; невыбранные назначения продолжают работать. Имена MTS, MTS RUS, MTS-RUS, МТС и код 25001 теперь канонизируются в единый оператор МТС на входе в БД, при чтении старых записей и в браузерных фильтрах.

27.07.2026 · 0.14.4

Архив клиентов переведён из локальной имитации браузера в постоянную серверную операцию. Controller запрещает архивировать карточку с активными proxy, хранит состояние в SQLite и записывает архивирование и восстановление в audit; история удалённых выдач остаётся доступной. Lifecycle canary после удаления Kraken profile и NAT binding автоматически архивирует временную карточку, поэтому новые проверки не засоряют рабочий список. Исторические ModGuard Canary и Canary Cycle архивируются только после отдельной сверки нулевого числа активных назначений; клиент с обычным именем test автоматически не затрагивается.

27.07.2026 · 0.14.3

Уведомления и контекстные подсказки теперь учитывают browser top layer: внутри открытого модального окна они создаются в самом dialog, поэтому больше не затемняются backdrop и не прячутся за формой. Проверка скорости показывает ошибку непосредственно рядом с обязательным подтверждением, переводит фокус на галочку и не перекрывает форму дублирующим toast. Исправление применяется ко всем диалогам менеджера proxy; в основном интерфейсе тем же способом защищены подсказки и сообщения копирования.

27.07.2026 · 0.14.2

Полный canary изолированного restart выявил конфиг с директивой proxy вместо Kraken auto. Старый listener безопасно продолжил работу, а проход вернул 57/58. Валидатор теперь извлекает обязательные порты из всех штатных service-директив 3proxy; после патча обе node проходят полную проверку до включения расписания.

27.07.2026 · 0.14.1

Production canary выявил, что запущенные agent процессы 3proxy наследовали cgroup modem-guardian-agent.service, заполняли её лимит tasks и могли завершиться вместе с agent. Управляемая политика была остановлена до исправления. Теперь каждый listener запускается отдельным transient service в system.slice, получает фактический LimitNOFILE и принимается только после проверки всех портов конкретного конфига по /proc. Небезопасного fallback без systemd нет.

27.07.2026 · 0.14.0

Менеджер получил каскадные фильтры location и оператора на вкладке modem, в аналитике скорости и в истории замеров; поиск modem работает по номеру, ID, модели и клиенту. Ручной Internetometer поддерживает накопленный выбор до 300 modem, массовую постановку в persistent-очередь, выбор всех найденных и явное подтверждение занятых устройств. Controller теперь выдаёт ровно один speed-test на location, показывает очередь и её возраст, позволяет отменять ожидающие задания и независимо от расписания закрывает зависшие ожидания через четыре часа, а выполнение через 15 минут. Ошибка одного modem не блокирует последующие. Графики получили шкалы Мбит/с, подписи осей, hover-подсказки, закрепляемую расшифровку, min–max, объём выборки и московские временные срезы; неясный «разброс выборки» заменён объяснённой нестабильностью (P90−P10)/медиана. Добавлена сохраняемая квота download на клиентское место с точностью до 1 Мбит/с: она единообразно пересчитывает рекомендации, запас парка и серверный подбор. Добавлены редактирование maxconn с read-back/data-plane/rollback, относительная статистика переключений с клиентским drill-down, поиск клиента по proxy-порту, возврат из истории proxy и копирование реквизитов из «Парка» отдельным no-store API без паролей в bootstrap или audit. Документирована фактическая граница публичности служебных 20xxx: прямые IP локаций работают, широкие relay-диапазоны VDS пока не включены. После расследования события 26.07 добавлены location outage guard для failover/failback, точная проверка listener после restart 3proxy и persistent-резервация диапазона 10000-20999 от ephemeral-сокетов Linux. Подробный отчёт сохранён в MODGUARD_INCIDENT_2026-07-26.md.

27.07.2026 · 0.13.5

Мобильная шапка manager показывает короткое состояние «Данные актуальны» или «Только чтение», а полный timestamp остаётся на desktop и в tooltip. Статус больше не обрезается и не растягивает первый экран на шесть строк.

27.07.2026 · 0.13.4

Production-аудит автоматического failback выявил повтор одной откатившейся попытки примерно каждые 15 минут. Добавлен persistent backoff 2/4/8/12 часов с причиной и событием FAILBACK_DEFERRED; успех, ручное закрепление и истечение окна возврата сбрасывают состояние. В manager добавлена смена собственного пароля без перехода на главную страницу, а мобильная шапка больше не обрезает состояние синхронизации. Browser smoke подтвердил реальные 124 modem, отсутствие port-238, нового клиента по умолчанию, автоматический подбор существующего modem и 18 кандидатов ручного выбора.

27.07.2026 · 0.13.3

Production canary после 0.13.2 выявил два связанных edge case. Новый Kraken proxy прошёл REST read-back, но listener 3proxy ещё несколько секунд отвечал Connection refused; readiness-проверка теперь делает до пяти попыток с трёхсекундной паузой, при этом реальные timeout и protocol failures по-прежнему обрываются после трёх попыток. Сам rollback корректно удалял профиль из Kraken, но двухснимочная защита 0.13.2 оставляла локальное назначение и NAT rule активными до следующего sync. Create и restore rollback теперь немедленно помечают точно известные успешно удалённые назначения как REMOVED, затем выполняют обычный sync и NAT reconciliation. При неподтверждённом удалении состояние не скрывается и операция остаётся ROLLBACK_FAILED.

27.07.2026 · 0.13.2

Устранён риск промежуточного состояния при синхронизации manager с Kraken. Полный снимок каждой ноды теперь применяется одной транзакцией; ошибка в любой строке откатывает весь нодовый снимок. Одиночный active=false и одиночное исчезновение ранее известного профиля не выключают и не архивируют клиентский порт: отрицательное состояние подтверждается вторым последовательным полным снимком, а явное удаление через lifecycle после Kraken read-back фиксируется немедленно. Конфликтный auth-профиль больше не маскируется под удалённый. Оборванные sync-runs старше 15 минут переводятся из вечного RUNNING в ABORTED; operational metrics показывают состояния и возраст sync queue, а также число активных, отключённых, удалённых и ожидающих подтверждения назначений. Команда controller --check больше не запускает фоновые sync и automation threads: она проверяет конфигурацию и зависимости без скрытого рабочего цикла.

27.07.2026 · 0.13.1

Первый production canary обязательной data-plane проверки обнаружил корректный rollback, но неверный адрес самой проверки: controller пытался открыть public_host ретранслятора с той же VDS и получал hairpin Connection refused. Неуспешная выдача не дошла до менеджера, Kraken profile был удалён, назначение осталось только архивной строкой, NAT-хвостов не возникло. Verifier переведён на отдельный data_plane_host с безопасным fallback к hostname Kraken base_url; relay host больше не используется для внутренней проверки. Canary также переведён со стороннего IP-checker на собственный TLS/nonce endpoint ModGuard.

27.07.2026 · 0.13.0

Формализовано хранение данных и аварийное восстановление. Подробные health samples сокращены до 72 часов с транзакционным почасовым rollup на 90 дней; UI продолжает считать полное число семидневных проверок и отдельно показывает детальную историю. Полный каталог старых backup проверен пофайловыми SHA-256 и SQLite quick_check, сжат, зашифрован и сохранён локально с проверенным restore-runbook; ежедневная VDS-ротация ограничена двумя горячими копиями и не трогает ручные snapshots. Пароли proxy удалены из bootstrap и выдаются только выбранным назначениям через no-store API с audit; браузер очищает секреты после закрытия окна. Create, restore и move теперь завершаются только после HTTP/SOCKS data-plane проверки прямого Kraken listener и откатываются при непрохождении трафика. Добавлены bounded HTTP concurrency, systemd restart policy, admin operational metrics с p95 HTTP/Kraken API и возрастом очередей, dependency lock и CI. Зафиксировано, что это живучий single-controller, а не HA: active-active требует PostgreSQL, leader lease и NAT fencing.

26.07.2026 · 0.12.0

Завершён production lifecycle клиентских proxy: удаление с сохранением истории, восстановление прежних endpoint/login/password, ручная пересадка внутри location с Kraken read-back и rollback, единая хронология выдачи и режим частично выполненной пакетной операции. Добавлен точечный desired-state NAT VDS: типизированный helper с allowlist, nft -c, атомарной таблицей ip modguard_nat, systemd-восстановлением после reboot и аварийной подложкой из прежних persistent iptables ranges. Реализован локальный автоматический failover с глобальной кнопкой «Стоп», режимом наблюдения по умолчанию, 10-минутным grace, cooldown, лимитом операций, circuit breaker, временным размещением и возвратом на восстановленный home modem. Удалена внешняя CDN-зависимость интерфейса; Lucide закреплён внутри релиза, а рабочая страница скрыта до загрузки production bootstrap. Добавлен воспроизводимый внешний canary полного lifecycle, подтверждён живой маршрут через VDS и устранено скрытие пунктов навигации на узком экране. Wiki синхронизирована с фактическими API, таблицами БД, таймингами и границами cross-location.

26.07.2026 · 0.11.0

Исправлен production-подбор modem во время белых списков: менеджер использовал устаревшие несуществующие коды HEALTHY_SELECTIVE_ACCESS/RUSSIAN_ONLY_ACCESS и поэтому помечал весь фактически доступный парк как DOWN. Теперь единый набор из модуля health применяется и при показе кандидатов, и непосредственно перед записью в Kraken. Модем без speed-history остаётся доступным с консервативной ёмкостью. Реализован полный production-контур Яндекс Интернетометра: persistent queue в SQLite, доставка через agent command plane, source-IP binding конкретного modem, три CDN download-потока, upload, ping, jitter, история и реальные агрегаты. Добавлены ручной запуск с подтверждением для занятого modem, московское расписание по выбранным часам, тесты только свободных modem, глобальный предел параллелизма, один тяжёлый тест на location, spacing и 15-минутный expiry зависшей команды. Production-аналитика больше не использует демонстрационные почасовые профили. Canary на свободном port-113 подтвердил прохождение тестового трафика через нужный mobile IP.

25.07.2026 · 0.10.1

Устранено смешивание production и demo в менеджере proxy. Найдена причина несуществующего port-238: браузер достраивал последовательность портов до общего счётчика modem. Теперь production использует только реальные monitored modem настроенных Kraken nodes; simulator, снятые и исторические строки исключаются на controller. Дополнительно canary выявил несовпадение локального modem ID и Kraken REST API ID после перестройки фермы. Создание теперь сопоставляет устройство по node + port-name, получает свежий Kraken ID и запрещает неоднозначное соответствие. Включён ограниченный create-only контур с CSRF, idempotency key, live revalidation, явными диапазонами портов, read-back, повторным импортом и rollback без записи секретов в audit. Canary-профиль прошёл сквозной IP-check через VDS и был полностью удалён. Автоподбор принимает здоровые свободные modem без speed history и честно помечает отсутствие замеров. Speed queue, удаление, восстановление и пересадка остаются выключенными. Из карточки modem можно начать выдачу новому клиенту. Верхняя шапка, текстовая навигация, цвета и мобильное поведение manager приведены к основному ModGuard. Wiki обновлена одновременно с кодом; раздел manager и changelog связаны прямыми и автоматически построенными обратными ссылками.

25.07.2026 · 0.10.0

Менеджер proxy переведён из статического прототипа в первый production-релиз с реальными данными и жёстким read-only гейтом. Добавлена идемпотентная пагинированная синхронизация двух Kraken, объединение клиента по login без учёта регистра, дневные группы выдач, архив исчезнувших профилей, таблица sync runs и зашифрованное хранение proxy password отдельным Fernet key. Служебный rooot исключается из клиентов, неоднозначные auth-профили не угадываются. Kraken не используется как источник здоровья, оператора или модели. Исправлена классификация клиентских соединений при неполном allowlist, подписи клиентского и служебного трафика стали явными. Автообновление парка сохраняет читаемую строку на той же высоте экрана. Основная навигация и manager приведены к одной светлой шапке. Wiki стала гипертекстовой: карта знаний даёт вход по рабочему сценарию, каждый раздел показывает связанные темы и автоматически построенные обратные ссылки.

25.07.2026 · 0.9.1

В менеджере proxy карточки ёмкости location стали основным переключателем выдачи. Реализованы прозрачный автоматический подбор с показом конкретных modem и ручной выбор ровно нужного количества из пригодного pool. maxconn виден на первом шаге и по умолчанию равен 750. Добавлены оператор в таблице клиентских портов, фильтр оператора и явная блокировка location при ручной пересадке. Клиентский экспорт канонизируется через VDS-ретранслятор. Экран скорости теперь показывает медиану всего парка, выбранной location или конкретного modem, почасовой и недельный профиль. Расписание поддерживает любые из 24 часов, прогнозирует суточный объём и блокирует неизбежный backlog; тяжёлые тесты остаются на Kraken agents, а VDS хранит лёгкие агрегаты. Документированы фактические NAT-диапазоны, persistent desired state VDS, второй NAT-слой MikroTik и ограниченный failback без ping-pong.

25.07.2026 · 0.9.0

Прототип менеджера прокси получил общую навигацию ModGuard, рабочую сводку локальных переключений и единый журнал каждого клиентского порта с причиной, исходным и новым модемом и исполнителем. Ручная пересадка показывает только работающие модемы той же локации и отдельно отмечает потерю скорости. Для новых клиентов генерируется 16-символьный пароль только из букв и цифр. Добавлен архив клиентов: пустую карточку можно убрать из рабочего списка, найти фильтром, восстановить вручную или автоматически вернуть при новой выдаче под тем же именем. На телефоне таблицы преобразуются в подписанные карточки без горизонтального переполнения.

25.07.2026 · 0.8.7

Тестовый менеджер прокси доступен ролям «Оператор» и «Администратор», но закрыт для роли «Просмотр». В форме выдачи демонстрационный комментарий убран из значения поля и заменён исчезающей подсказкой «Информация о заказе».

25.07.2026 · 0.8.6

SMS вынесены из длинной истории в отдельное окно дашборда модема. Добавлен общий для сотрудников счётчик непрочитанных сообщений, отметка одного или всех сообщений прочитанными и возможность вернуть сообщение в непрочитанные. Физическая очистка памяти модема остаётся отдельной подтверждаемой операцией и не удаляет центральный архив.

24.07.2026 · 0.8.5

Автоматическая очистка ограничена 20 командами удаления на агента за один снимок. Защитный предел введён после живого прогона: один Huawei выполнил 33 быстрых удаления, после чего HiLink API временно перестал отвечать. Архив не пострадал, а крупные входящие теперь освобождаются за несколько спокойных циклов.

24.07.2026 · 0.8.4

Чтение SMS и очистка памяти разделены на уровне драйвера. Добавлен sms_auto_delete_driver_allowlist: весь парк может автоматически архивироваться, но команды удаления получают только проверенные связки модема и роутера. Huawei HiLink и Fibocom L860 на GoldenOrb прошли canary; два L860 на Keenetic не подтвердили даже AT+CPMS? и остаются read-only.

24.07.2026 · 0.8.3

SMS с пустым телом теперь сохраняются по индексу, отправителю, времени и хранилищу, а затем участвуют в безопасной очистке. Пустой текст является допустимым содержимым и больше не оставляет невидимые сообщения занимать память Huawei.

24.07.2026 · 0.8.2

Статус очистки SMS стал самокорректирующимся: если полный снимок снова обнаруживает сообщение, ранее подтверждённое как удалённое, отметка удаления снимается и точечная очистка ставится в очередь повторно. Это закрывает случаи, когда API модема ответил OK, но сообщение осталось в памяти или появилось снова с тем же отпечатком.

24.07.2026 · 0.8.1

Fibocom на GoldenOrb переведён на пассивное чтение штатного декодированного SMS-кэша без конкуренции за AT-порт. Точечное удаление получило совместимые блокировки, жёсткий тайм-аут, обязательное подтверждение OK и сверку по следующему полному снимку. Диагностика выявила дефект GoldenOrb: штатные gcom-locked и delsms.sh способны ждать занятую блокировку бесконечно.

24.07.2026 · 0.8.0

Добавлен независимый архив SMS для Huawei HiLink и Fibocom L860, корректное декодирование Unicode/UCS-2, просмотр в дашборде модема и подтверждаемая очистка памяти только после транзакционного сохранения. Непрозрачные надписи «Ротация выше нормы» и «Ротация требует внимания» заменены диагнозами о частых проблемах смены IP; Wiki уточняет динамическое сравнение p90/p95.

24.07.2026 · 0.7.6

Статус «Надо пополнить счёт» заменён на точный «Оператор ограничил интернет»: captive portal не доказывает состояние баланса. Добавлена независимая production-политика повторного reboot модема после 300 секунд подтверждения и затем раз в 30 минут до восстановления, с отдельными acknowledgement, allowlist, журналом действий и одним непрерывным инцидентом.

24.07.2026 · 0.7.5

Keenetic recovery переведён на явный каскад встроенных возможностей KeeneticOS: штатный AT-транспорт для мягкого reconnect, штатный USB power-cycle для перезагрузки модема и system reboot для последней ступени. Добавлен read-only аудит привязанного Ping Check. Wiki дополнена измеренными ограничениями canary: принятая команда не считается восстановлением до фактических HTTP-проверок, а Ping Check признан дополнительным, но не единственным источником деградации L860.

24.07.2026 · 0.7.4

Дашборд модема разделён на работоспособность, IP-ротацию и ограничения сети. Уточнены разные случаи одинакового IP, «ожидаемый адрес» переименован в IP до команды, включения белых списков — в отдельные периоды, а reboot для ротации отделён от подтверждённого простоя. Добавлена прямая RCI-идентификация Keenetic Launcher и Fibocom L860, отдельные поля роутера и проверенный мягкий reconnect L860 через Keenetic CLI. Stateful Telnet-транспорт обрабатывает negotiation, фрагментированный баннер и ответы AT-команд без ложного результата; fallback не использует Huawei API, а перенумерованные 3proxy-профили связываются с модемом по исходящему адресу.

24.07.2026 · 0.7.3

Локации стали кликабельными фильтрами парка. Окно «История» заменено полным дашбордом модема: здоровье, доступность, оборудование, SIM, сетевой тракт, probes, клиентская нагрузка, IP и ротация, белые списки, инциденты и хронология проверок. Асинхронный рендер защищён от повторных и запоздавших запросов, вызывавших дублирование блоков.

24.07.2026 · 0.7.2

Оценка качества ротации переведена на динамические p90/p95 за скользящие 24 часа с минимальной выборкой и абсолютными защитными границами. Добавлены поиск по Wiki, доступ к Wiki только для администратора, смена собственного или управляемого пароля, генерация и копирование паролей. Тестовый инцидент убран из рабочего интерфейса.

24.07.2026 · 0.7.1

Суммарные модемо-часы простоя убраны из верхней статистики и заменены доступностью за 24 часа. Статусные показатели стали кликабельными фильтрами парка. Пороги качества ротации откалиброваны по фактическим p90/p95 фермы; добавлена отдельная оценка эффективности reboot.

24.07.2026 · 0.7.0

Добавлены независимые интервалы и статистика белых списков по локациям и модемам за 24 часа, 7 и 30 дней, текущие индикаторы и защита от устаревших данных. Добавлен журнал фактических смен IP, суточные метрики качества ротации, жёлтые/красные пороги, история страховок и reboot. Убрано дублирование состояния плановой ротации из IP-колонки, исправлены подчёркивания и стабилизирована ширина таблицы.

24.07.2026 · 0.6.3

Добавлен второй официальный hostname Яндекс Интернетометра для проверки IP и результата ротации. Краткий timeout yandex.ru больше не оставляет модем без адреса, если тот же сервис доступен через yandex.com.

24.07.2026 · 0.6.2

Добавлено определение IPv4 через Яндекс Интернетометр во время белых списков. Этот режим больше не создаёт инциденты и простой, получает отдельный журнал периодов и не останавливает проверку или ротацию IP. Старые ложные инциденты белых списков автоматически закрываются и исключаются из статистики.

23.07.2026 · 0.6.1

Плановая ротация и обязательная 11-минутная страховка разделены. Значение «Выкл.» стало безопасным режимом по умолчанию и больше не исключает модем из контроля просрочки или дубликатов. Настройка периода вынесена из «Внешнего IP» в отдельный соседний столбец.

23.07.2026 · 0.6.0

Добавлена встроенная Wiki, подробные состояния ротации, очистка устаревшей ошибки после фактической смены IP и отдельный быстрый командный контур. Пул ротации стал настраиваемым: 32 потока по умолчанию, максимум 64.

23.07.2026 · 0.5.6

Параллельная обработка увеличена до 12 команд; проверена пачка ротаций на обоих Kraken.

23.07.2026 · 0.5.5

Добавлены динамический HiLink gateway, точная модель E3372h-153, прошивочные профили L860 и Huawei reboot после двух одинаковых IP.

22–23.07.2026 · 0.4.x

Введены локальный инвентарь без зависимости от Kraken, каскад восстановления, outage-сегменты, резервные порты, полноценная аппаратная страница и защищённое управление нодами.

Пользователи

Роли определяют доступ к просмотру, действиям и администрированию.

Дашборд модема

Модем

Сообщения SIM-карты

SMS

Одноразовый доступ

Добавить ноду

Нода и локальная сеть

Настройки ноды

Роутер MikroTik

Пароль показывается открыто только администратору. В базе он хранится в зашифрованном виде и не попадает в общий список нод или журнал действий.

Доступ ещё не проверялся.

Доступ

Новый пользователь

Сгенерированный пароль показывается открыто, чтобы его можно было передать пользователю.

Только просмотр состояния, истории и статистики.

Безопасность

Сменить пароль