Симптом: ci.yml красный с 01.10.2026 (patch failed: proxy/vless/inbound/inbound.go:597 на свежем XTLS), и этого никто не видел. Наблюдатель следил только за candidate.yml, а тот зелёный, пока официальный релиз не вышел. Когда XTLS выпустит релиз, candidate упадёт, и тревога придёт на дни позже, чем можно было узнать. Исправление: оценщик workflow обобщён на таблицу WorkflowWatch (имя условия, заголовок, последствие, действие). Кроме patch_pipeline (candidate.yml) появилось условие patch_ci (ci.yml, VPNBOT_XRAY_ALERT_CI_WORKFLOW_ID): красный последний запуск main — тревога с действием «адаптировать патч», зелёный — восстановление. Старое состояние без patch_ci читается без миграции. Тесты: красный CI — отдельная проблема при зелёном candidate; зелёный CI восстанавливает; collect_conditions читает оба workflow. Claude-Session: 6557d466-b637-49c9-9419-7a5fa7cc1ff7
13 KiB
VPnBot Xray patches
Этот репозиторий хранит только наши изменения для Xray-core. Полной копии
чужого проекта здесь нет: scripts/prepare-source.sh получает точный тег из
официального XTLS/Xray-core, проверяет
его commit и последовательно накладывает три файла из patches/.
Патчи добавляют capability-маркер vpnbot-active-revoke-v3. Он означает, что
после удаления пользователя через Xray API закрываются уже установленные
VLESS, Trojan, VMess и Shadowsocks-сессии этого пользователя. Обычный
официальный Xray запрещает только новые подключения; поэтому до принятия этого
механизма upstream-проектом VPnBot не может безопасно устанавливать чистый
официальный бинарник на платные узлы.
Второй маркер vpnbot-live-user-audit-v1 означает, что команда
xray api vpnbot-audit-users читает всех пользователей всех inbound через
один процесс и одно локальное gRPC-соединение. Команда делает два полных
прохода и сверяет каждый список с GetInboundUsersCount; узловой помощник
сравнивает оба прохода и по-прежнему безопасно отказывает при любой
неоднозначности. На старых узлах без маркера остаётся совместимый, но более
дорогой аудит отдельными командами.
Граница источников
- чужой исходный код загружается только из официального GitHub XTLS;
- Forgejo хранит наши патчи, проверки, сборочные сценарии и готовые релизы;
- тег и commit upstream закреплены в
upstream.envдля ручной сборки, а автоматический выпуск создаёт отдельный build-env с теми же строгими проверками; - SHA-256 каждого патча закреплён в
patches/series; - Actions заново применяет патчи к официальному исходнику, запускает профильные тесты и собирает три Linux-архива, которые используются VPnBot.
Автоматические dev-релизы
Workflow Follow official Xray dev releases каждые шесть часов читает
официальный XTLS/Xray-core/releases.atom, после чего временным shallow
Git-fetch получает ровно найденный тег, его commit, время и go.mod.
GitHub API, API-токен, подвижная ветка main и постоянная локальная копия
Xray-core для discovery не используются. Официальный prerelease/dev-тег для
него является нормальным входом, но не становится стабильным релизом VPnBot
сразу.
Цепочка разделена на два состояния:
- Actions разрешает тег в точный официальный commit, применяет патчи, запускает
unit- и real-process-тесты, собирает архивы и публикует Forgejo prerelease с
суффиксом
-candidate.1. - Root-only таймер на production-хосте устанавливает точный кандидат на выделенный canary-узел, проверяет живую конфигурацию и запускает изолированный VLESS-сценарий с двумя непрерывными потоками. После удаления пользователя A поток A должен закрыться не позднее 10 секунд, а поток B — продолжить работу. Тот же canary до и после удаления проверяет двухпроходную команду live-аудита и не сохраняет пользовательские идентификаторы в proof-файле.
- Только после этого создаётся обычный
proven-релиз. Его файлы копируются из candidate без пересборки и поэтому имеют ровно те же SHA-256.
Стабильные Xray-updater-таймеры на узлах используют канал stable: они
игнорируют prerelease=true и видят только proven-релизы. При конфликте патча,
падении теста, недоступном canary или неуспешном отзыве новый stable-релиз не
создаётся.
Каждый candidate содержит vpnbot-xray-release-manifest.json с официальным
тегом/commit, SHA-256 патчей и архивов. Proven дополнительно содержит
vpnbot-xray-pilot-proof.json, то есть машинно проверяемый отчёт живого пилота.
Для Linux amd64 один выпуск содержит два архива из одного и того же исходника:
совместимый Xray-linux-64.zip (GOAMD64=v1) и оптимизированный
Xray-linux-64-v3.zip (GOAMD64=v3). Манифест схемы 3 закрепляет требования
к процессору, узловой updater выбирает вариант по фактически видимым гостевой
системе CPU flags, а production-canary обязан доказать именно v3-вариант.
Схема 3 дополнительно закрепляет обязательный vpnbot-live-user-audit-v1,
чтобы старый бинарник нельзя было ошибочно продвинуть как новый выпуск с
однопроцессным аудитом.
Полная модель состояний, гонок и rollback описана в
docs/2026-08-07-automatic-dev-release-plan.md.
Production-компонент устанавливается от root:
scripts/install_promoter.sh
Перед включением таймера нужен runtime-файл
/etc/vpnbot-xray-release-promoter.env по примеру из systemd/ и root-only
Forgejo-токен с областью write:repository. Токен, SSH-ключ и состояние пилота
не хранятся в Git.
Тот же установщик добавляет независимый наблюдатель
vpnbot-xray-release-alert.timer. Он раз в пять минут проверяет последний
candidate.yml, возраст опубликованных candidate без proven и root-only
состояние canary. Оператор получает Telegram-сообщение при безопасной остановке
выпуска, редкое напоминание для продолжающейся аварии и отдельное сообщение о
подтверждённом восстановлении. Переходный retry workflow или canary не считается
восстановлением.
Forgejo может показывать публичные Actions-запуски в интерфейсе, но возвращать
пустой workflow_runs через публичный API. В таком случае наблюдатель читает
страницу именно candidate.yml, выбирает последний запуск main и извлекает
только встроенное структурированное JSON-состояние страницы запуска. Он не
угадывает результат по цвету иконки или локализованному тексту. Незавершённый
запуск старше общего stall-интервала становится отдельной аварией runner,
потому что бесконечное waiting иначе оставляло бы сторож молчащим.
Наблюдатель следит и за ci.yml (VPNBOT_XRAY_ALERT_CI_WORKFLOW_ID): красный
CI на свежем официальном исходнике — отдельное условие patch_ci. Оно значит,
что следующий релиз XTLS не соберётся в candidate, и узнаём мы об этом за дни
до candidate.yml (с 01.10.2026 CI был красным незаметно).
Недоступный источник не означает вердикта о выпуске. Ответы 5xx и 429 и
сетевой обрыв после повторов release_pipeline.request_bytes поднимает как
SourceUnavailableError с кодом source_server_error, source_rate_limited
или source_unreachable. Ответ 4xx и битое тело остаются обычным
PipelineError: это поломка контракта. В такой прогон наблюдатель не оценивает
workflow, candidate и canary, поэтому открытый инцидент не закрывается и новый
не открывается. Вместо этого он ведёт условие release_source: молчит, пока
недоступность короче VPNBOT_XRAY_ALERT_SOURCE_OUTAGE_SECONDS (по умолчанию
1800 с), затем шлёт одну тревогу, а потом сообщение о восстановлении.
Промоутер при недоступном источнике на шаге выбора кандидата (до любых
изменений) откладывает прогон и завершается с кодом 0. Повод для этого —
ночная остановка Forgejo на ~90 секунд ради консистентного бэкапа: 28–29.09.2026
она роняла оба юнита ответом 502.
Для наблюдателя нужен /etc/vpnbot-xray-release-alert.env по отдельному примеру
из systemd/. В нём нет токена Telegram: скрипт читает только
VPNBOT_BOT_TOKEN из существующего root-only runtime-env VPnBot и
отправляет обычный HTTPS на api.telegram.org. Своей маршрутизации у него
нет: путь к Telegram принадлежит хосту. Telegram-фронт node manager
сопоставляет имя с loopback-адресом и склеивает соединение со здоровым реле
или прямым адресом. До 03.10.2026 скрипт держал копию политики бота (реле,
резервные IPv4, семейство адресов), а 24.09 не держал никакой и падал с
ENETUNREACH. Отказ доставки печатается typed-кодом:
telegram_connect_failed гарантирует, что запрос не ушёл;
telegram_delivery_ambiguous — запрос ушёл без ответа и в этом запуске
не повторяется. Долговечное
состояние дедупликации хранится в
/var/lib/vpnbot-xray-release-alert/state.json и никак не участвует в решении о
публикации proven.
Режимы ручной проверки после установки:
sudo -n /usr/local/libexec/vpnbot-xray-release-promoter/release_alert_monitor.py --print-status
sudo -n /usr/local/libexec/vpnbot-xray-release-promoter/release_alert_monitor.py --send-test
Первый вызов ничего не отправляет и не меняет incident-state. Второй отправляет одно явно помеченное тестовое сообщение и тоже не создаёт ложную аварию.
Локальная проверка
scripts/test-patches.sh
scripts/build-release.sh ./release-assets
Для подготовки исходного дерева без сборки:
scripts/prepare-source.sh ./xray-with-vpnbot-patches
Каталоги назначения должны отсутствовать или быть пустыми.
Исходный Xray-core и изменённые файлы распространяются по Mozilla Public
License 2.0. Полный текст лицензии находится в LICENSE.