Отзыв секрета не сходился: `auth_id` при смене секрета оставался прежним,
поэтому сессия, установленная по отозванным учётным данным, была неотличима от
законной, и цикл учёта не имел признака, по которому её следовало завершить. У
состояния есть путь без единой неудачи — Hysteria регистрирует соединение в
Traffic Stats API только после возврата backend-auth, поэтому успешный /kick
может пройти мимо. Новое поколение credentials получает новый auth_id, kick идёт
по старому, пережившая сессия становится orphan.
Адрес Traffic Stats API имел два контракта: оркестратор принимал любой IPv4,
админка всегда шла на loopback. Валидная по всем гейтам конфигурация выключала
лимит устройств, учёт трафика и принудительное отключение разом. Адрес
зафиксирован, а расхождение файла с ним админка называет.
Состояние службы стало трёхзначным: util.Exec выбрасывал вывод systemctl при
ненулевом коде, поэтому «остановлена» и «спросить не удалось» приходили одним
значением, а доступность Traffic Stats API выводилась из него же. Журнал
Hysteria разбирается в фактическом формате upstream (time — дробное число),
страница конфигурации показывает файл вместо дефолтов UI и не возит секреты в
браузер, санитайзер выгрузки следует по YAML-якорям.
Разбор: docs/acceptance/2026-09-02-v1.0.0-rc4-preflight-findings.md
Установщик мог повредить работающий сервер до того, как откажется его
трогать: install.sh переписывал /usr/local/lib/hy2xs, раскладывал
runtime-пакет и перезаписывал install-state.json, и только потом
запускал clean-host preflight. При ошибочном запуске поверх старой
установки rollback дополнительно делал stop и disable для работающих
hysteria-server и hy2xs-admin.
Установка разделена на две фазы с жёсткой границей:
PHASE 0 - read only: права, checksums пакета, clean-host preflight
из распакованного архива (новая команда preflight-install)
PHASE 1 - mutation: раскладка оркестратора и сама установка
Граница держится не соглашением, а read-only guard: под ним writeText,
writeTextAtomic и мутирующие раннеры lib/process кидают ошибку.
Внутри install() preflight выполняется раньше первой записи состояния.
Остальное в этом же инварианте:
- clean-host контракт расширен с двух маркеров до четырнадцати, пути
установки и данных берутся из конфигурации, а не захардкожены;
- отсутствие HY2XS_CONFIG_SCHEMA_VERSION трактуется как legacy, а не
как текущая схема: до v1 этого поля не существовало. Тест,
закреплявший прежнее поведение, инвертирован;
- install-state несёт идентификацию поколения (product, release_line,
config_schema_version); reconfigure и repair проверяют её до всего
остального, потому что installed: true мог остаться и от 0.x;
- repair требует явного --allow-partial-state;
- классификация отказа опирается на ownership-флаги, а не на текст
ошибки: раньше сообщение со словом nftables приводило к откату
чужого firewall. stop/disable выполняется только для юнитов,
развёрнутых текущей операцией, а fatal_pre_apply не делает
системного отката и не собирает diagnostics-бандл.
Сквозная миграция HY2XS на современную Hysteria (2.12.2) и переход на v1.
Build:
- версия Hysteria резолвится на этапе сборки из HyNetworks/hysteria и
замораживается в metadata пакета (version + immutable url + sha256);
- compatibility gate: реальный бинарник должен принять канонический конфиг
HY2XS для gecko и salamander до создания пакета;
- сборка прогоняет тесты оркестратора и админки.
Конфигурационный контракт:
- HY2XS_CONFIG_SCHEMA_VERSION=2, чужая схема отклоняется fail-fast;
- obfs стал настоящим union gecko|salamander, gecko — default;
- obfs-блок рендерится оркестратором целиком, два подтипа одновременно
структурно невозможны;
- современный baseline: congestion bbr/standard, disableLossCompensation=false,
disableStatelessReset=false, полный quic-блок.
Исправления:
- share URI для gecko: генератор был завязан на Obfs.Salamander.Password и
выдавал нерабочую ссылку при любой другой обфускации;
- SNI брался только из ACME-блока и уходил пустым при HY2XS_TLS_MODE=file;
- экспорт конфига выносил trafficStats.secret, access_token и obfs-пароль;
- экспорт терял неизвестные upstream-поля при round-trip через типизированную
модель;
- renderRuntimeEnv печатал тип обфускации литералом, расходясь с конфигом;
- namedotcom удалён из ACME-реестра (нет в Hysteria с 2.11.0).
Тесты:
- 95 тестов оркестратора: env, рендер, семантика профиля, резолвер, rollover;
- тесты URI и экспорта в Go;
- tools/test/e2e-hysteria.sh с реальным клиентом Hysteria.
UX:
- подсказки и примеры в форме создания пира.
Прочее: CHANGELOG.md, .gitattributes (LF для target-side файлов),
документация на русском.