Files
HY2XS_flamy/docs/14-legacy-cleanup.md
T
founder 2259f7c847 firewall guard: барьер покоя fail-closed и явный контракт транзиентного таймера
Барьер, обязанный ДОКАЗАТЬ отсутствие асинхронного исполнителя, в трёх местах
принимал за доказательство отсутствие наблюдения.

- отказ `systemctl` больше не выдаётся за отсутствие guard: вместо `return []`
  введён единый наблюдатель inspectRollbackGuard с исходами quiescent/pending/
  unknown и отдельным типом отказа GuardStateUnknownError;
- покой перечисляется белым списком (inactive, failed): maintenance,
  refreshing и любое незнакомое состояние systemd блокируют операцию;
- у транзиентного таймера явно заданы AccuracySec=1s (умолчание 1min
  превращало обещанные 45 секунд в 45-105) и RemainAfterElapse=no; барьер
  дополнительно опознаёт SubState=elapsed у *.timer как покой;
- команда взведения строится чистой buildArmGuardArgv и выполняется новым
  runMutatingArgv без shell, поэтому её контракт проверяется значением, а не
  грепом по исходнику;
- status перестал листить guard-юниты своей копией кода: без --plain, с
  `|| true` и с трактовкой failed как «вооружён» отчёт вечно противоречил
  барьеру. Добавлены rollback_guard_state и firewall_state=guard_unknown;
- purge-v0.sh пропускал failed-юниты из-за маркера в первой колонке.

Барьер покрыт поведенческими тестами через подставляемый SystemdUnitProbe:
прежние проверки грепом по тексту функции пережили инверсию смысла - строка
`return [];` была на месте, а решение стало неверным.

Документация (README, docs/07, 11, 12, 13, 14, CHANGELOG) приведена к реальному
окну 45-46 секунд и к новому тексту отказа. Отдельно исправлен комментарий
PNPM_AUDIT_LEVEL в versions.env: гейт давно проверяет весь lock-граф.
2026-08-31 16:44:20 +05:00

14 KiB

Очистка сервера от предыдущей установки

Зачем этот документ

HY2XS v1 не поддерживает установку поверх и не мигрирует состояние 0.x. Это осознанное решение продукта, а не временное ограничение: попытка угадать, как устроен произвольный старый сервер, приводит к полурабочим установкам, которые невозможно диагностировать.

Отсюда следует жёсткий системный инвариант:

обнаружена старая установка
        ↓
НОЛЬ изменений на сервере
        ↓
понятный отказ
        ↓
явная очистка (этот документ)
        ↓
установка HY2XS v1 с нуля

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

Как выглядит отказ

Установщик проверяет чистоту хоста в PHASE 0 — до того, как изменит хотя бы один persistent path, включая /usr/local/lib/hy2xs:

[hy2xs-install] PHASE 0: read-only checks (no persistent path is modified)
[hy2xs-install] verifying package checksums
[hy2xs-install] running clean-host preflight from the unpacked package
[hy2xs] ERROR: На сервере обнаружена предыдущая или посторонняя установка.
HY2XS v1 не поддерживает установку поверх и не мигрирует состояние 0.x.
Ни один файл на сервере не изменён.

Найденные маркеры:
  - /etc/hysteria/post-install.env (post-install.env предыдущей установки HY2XS)
  - hy2xs-admin.service (systemd-юнит админки HY2XS)

Очистите сервер и установите HY2XS заново: см. docs/14-legacy-cleanup.md

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

Отдельный случай — конфигурация без маркера схемы:

HY2XS_CONFIG_SCHEMA_VERSION отсутствует в конфигурации.
Похоже на конфигурацию предыдущего поколения (0.x) или на неизвестный формат.

До v1 поля HY2XS_CONFIG_SCHEMA_VERSION не существовало, поэтому его отсутствие трактуется как legacy, а не как «текущая схема по умолчанию».

Что именно проверяется

Контракт чистого хоста объявлен в orchestrator/src/steps/cleanHost.ts и покрыт тестами. Установка отказывается, если найден хотя бы один из объектов:

Объект Что это
/etc/hysteria/post-install.env post-install.env предыдущей установки
/etc/hy2xs/hy2xs.env runtime-конфигурация предыдущей установки
/etc/hy2xs/bootstrap-admin.secret bootstrap-секрет администратора
/var/lib/hy2xs/install-state.json маркер состояния установки
/usr/local/lib/hy2xs/package runtime-пакет предыдущей установки
/etc/hysteria/config.yaml сгенерированный серверный конфиг
/usr/local/bin/hysteria уже установленный бинарник Hysteria
/etc/nftables.d/hy2xs.nft nftables-фрагмент HY2XS
hy2xs-admin.service systemd-юнит админки
hysteria-server.service systemd-юнит сервера Hysteria
h-ui.service, /usr/local/h-ui наследие панели поколения 0.x
HY2XS_INSTALL_DIR (по умолчанию /opt/hy2xs-admin) каталог приложения
HY2XS_DATA_DIR (по умолчанию /var/lib/hy2xs-admin) каталог данных и БД

Последние два пути берутся из конфигурации, а не захардкожены: нестандартная установка тоже должна быть обнаружена.

Перед очисткой

Очистка разрушительная. Она удаляет базу админки вместе с учётными записями пиров: выданные пользователям ссылки перестанут работать.

Сохраните то, что вам нужно:

# ссылки и учётные записи пиров (если старая панель ещё работает)
sudo sqlite3 /var/lib/hy2xs-admin/h_ui.db '.dump' > ~/hy2xs-peers-dump.sql

# серверный конфиг Hysteria
sudo cp -a /etc/hysteria/config.yaml ~/hysteria-config.yaml.bak

# post-install-справка предыдущей установки
sudo cp -a /etc/hysteria/post-install.env ~/post-install.env.bak

Файлы содержат секреты. Снимите с них лишние права и не пересылайте как есть:

chmod 600 ~/hy2xs-peers-dump.sql ~/hysteria-config.yaml.bak ~/post-install.env.bak

Для безопасной передачи конфига наружу используйте редактирование секретов:

hy2xs-orchestrator redact-config --config ~/hysteria-config.yaml.bak --out ~/hysteria-config.redacted.yaml

Очистка скриптом

Скрипт tools/legacy/purge-v0.sh лежит в репозитории. Скопируйте его на сервер.

Сначала — план. Без флагов скрипт ничего не меняет:

sudo ./purge-v0.sh

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

Затем — выполнение. Требуются оба флага, --apply без подтверждения не работает:

sudo ./purge-v0.sh --apply --yes-i-know

Возможности «оставить бинарник Hysteria» у скрипта нет намеренно. /usr/local/bin/hysteria входит в clean-host контракт установщика: сервер, где он остался, установку HY2XS v1 не пройдёт. Скрипт, который сохранял бы бинарник и при этом сообщал «хост чист», прямо противоречил бы следующему запуску install.sh. Свежая установка всё равно кладёт собственную версию Hysteria, проверенную по SHA-256 против upstream hashes.txt.

В конце скрипт сам проверяет, что хост стал чистым по тому же контракту, который применяет установщик. Если что-то осталось, он назовёт конкретные объекты и завершится с ошибкой.

Что скрипт делает и чего не делает

Делает:

  1. останавливает и выключает hysteria-server, hy2xs-admin, h-ui;
  2. снимает таймеры отката firewall hy2xs-fw-rollback-* — они переживают неудачную установку и иначе продолжили бы менять ruleset уже после очистки;
  3. удаляет unit-файлы и выполняет daemon-reload;
  4. удаляет каталоги приложения, конфигурации, данных и логов, включая /var/lib/hysteria (там остаётся ACME-состояние и сертификаты Hysteria), /usr/local/lib/hy2xs и symlink /usr/local/bin/hy2xs-orchestrator;
  5. удаляет /usr/local/bin/hysteria;
  6. удаляет артефакты незавершённой операции в /run: каталог отката /run/hy2xs (прежние nftables.conf, hy2xs.nft и маркер срабатывания guard) и замок операций /run/lock/hy2xs-orchestrator.lock, который может пережить убитый kill -9 процесс оркестратора и не дать запуститься следующей установке. /run — tmpfs, и перезагрузка убрала бы оба, но очистка не имеет права требовать перезагрузки;
  7. удаляет *.candidate firewall — они остаются, если операция упала между nft -c и подстановкой файла в production-путь;
  8. удаляет фрагмент /etc/nftables.d/hy2xs.nft и строку include для него из /etc/nftables.conf, после чего перезагружает ruleset;
  9. проверяет чистоту хоста.

Список удаляемых путей и список legacy-маркеров clean-host контракта описывают одну и ту же границу: расхождение между ними ловится приёмкой сборки. Иначе возможен сервер, с которого «всё удалено», но который установщик всё равно считает грязным — или, что хуже, наоборот.

Не делает:

  • не трогает sshd и его конфигурацию;
  • не удаляет /etc/nftables.conf целиком — остальной ruleset принадлежит оператору;
  • не удаляет системные пакеты, установленные ранее;
  • не запускается автоматически из установщика.

Ручная очистка

Если запускать скрипт нежелательно, те же шаги вручную:

sudo systemctl stop hysteria-server hy2xs-admin h-ui
sudo systemctl disable hysteria-server hy2xs-admin h-ui
sudo systemctl reset-failed hysteria-server hy2xs-admin h-ui

# таймеры отката firewall от незавершённой установки.
# --plain обязателен: у юнита в состоянии failed первой колонкой идёт маркер `●`,
# и без флага такой юнит легко пропустить — а это ровно те, что остались после
# аварийной установки.
sudo systemctl list-units --all --plain 'hy2xs-fw-rollback-*'
# для каждого найденного юнита:
#   sudo systemctl stop <unit> && sudo systemctl disable <unit>
#   sudo systemctl reset-failed <unit>
#   sudo rm -f /etc/systemd/system/<unit>

sudo rm -f /etc/systemd/system/hysteria-server.service \
           /etc/systemd/system/hy2xs-admin.service \
           /etc/systemd/system/h-ui.service
sudo systemctl daemon-reload

sudo rm -rf /etc/hy2xs /etc/hysteria /var/lib/hy2xs /var/lib/hy2xs-admin \
            /var/lib/hysteria /var/log/hy2xs /opt/hy2xs-admin \
            /usr/local/lib/hy2xs /usr/local/h-ui
sudo rm -f /usr/local/bin/hysteria /usr/local/bin/hy2xs-orchestrator

# артефакты незавершённой операции в /run (tmpfs)
sudo rm -rf /run/hy2xs
sudo rm -f /run/lock/hy2xs-orchestrator.lock

# candidate-файлы firewall от операции, упавшей до подстановки
sudo rm -f /etc/nftables.conf.candidate /etc/nftables.d/hy2xs.nft.candidate

sudo rm -f /etc/nftables.d/hy2xs.nft
sudo sed -i '/nftables.d\/hy2xs.nft/d' /etc/nftables.conf
sudo nft -c -f /etc/nftables.conf && sudo nft -f /etc/nftables.conf

После очистки

Устанавливайте HY2XS v1 обычным путём. PHASE 0 установщика повторит проверку чистоты хоста и подтвердит, что всё в порядке:

sudo ./install.sh

Незавершённая установка v1 — это другой случай

Если установка HY2XS v1 упала после начала применения изменений, полная очистка не нужна. У такой машины есть корректный маркер состояния текущего поколения, и её чинит repair:

sudo hy2xs-orchestrator repair \
  --package-dir /usr/local/lib/hy2xs/package \
  --config /etc/hy2xs/hy2xs.env \
  --allow-partial-state

Флаг --allow-partial-state обязателен и осознан: без него repair работает только поверх полностью успешной установки. При этом repair всё равно проверяет, что маркер принадлежит текущему поколению (product, release_line, config_schema_version), и откажется чинить чужое состояние.

Отказ вида «install state marker … не относится к текущему поколению HY2XS» означает, что repair неприменим и нужна очистка по этому документу.