Второй разбор того же слоя, уже по состоянию после 162759c. Тема: границы между
частями access-control. Прошлый проход починил одну операцию отзыва доступа и
оставил остальные; правило доступа при этом продолжало существовать в двух
экземплярах. Проведены три границы: состояние пира -> решение о доступе,
сохранённое изменение -> живая сессия, планировщик -> принадлежащая ему работа.
Правило доступа. Оно было записано двумя разными SQL-условиями: одним в выборке
Hysteria2Auth, другим в выборке cron. Второе не является отрицанием первого, и
расхождение приходилось ровно на границы — quota=0, usage=quota, now=expiresAt,
now=bannedUntil: авторизация отказывала, cron сессию не рвал. Условие cron
требовало СТРОГОГО превышения квоты, а счётчики растут порциями по ответу
Traffic Stats API, поэтому точное равенство — обычный исход очередного сбора.
Пир с исчерпанной квотой не пускался заново, но его живая сессия не разрывалась
никогда. Политика вынесена в peerAccessDenied; авторизация ищет пира только по
secret_digest, cron применяет ту же функцию. quota=-1 — единственный безлимит,
quota=0 — ноль байтов, bannedUntil=now — блокировка уже закончилась. Строка без
решающего поля трактуется как повреждённая и ведёт к отказу.
Операции, оставлявшие живую сессию. DeletePeer состоял из одного dao.DeletePeer:
строка исчезала вместе с auth_id, то есть вместе с единственным, чем эту сессию
можно было завершить, — состояние становилось невосстановимым. Разрыв при
изменении выполнялся только при disabled=1, поэтому мимо проходили смена
секрета, урезание квоты ниже израсходованного, перенос срока в прошлое и
снижение maxDevices. Импорт переписывает auth_id, секрет, квоту, срок и disabled
целиком и не трогал сессий вовсе. Все операции идут теперь через один
reconcileLiveSessions, а он — через disconnectAuthIDs, единственный вход к /kick:
он принимает готовые идентификаторы, дедуплицирует их, разбивает на части и не
обращается к базе. Импорт собирает старые auth_id ВНУТРИ транзакции (после
commit их в базе уже нет) и рвёт ПОСЛЕ commit (до него клиент успел бы
переподключиться к ещё не изменённому пиру). Правило асимметрично намеренно:
ограничение применяется немедленно, послабление — нет.
Цикл учёта. CronHandleAccount запускала горутину, которая запускала ещё две, —
для планировщика джоба заканчивалась почти мгновенно, поэтому StopCron не ждал
настоящей работы: releaseResource закрывал SQLite, а горутины продолжали в неё
писать. Параллельность обеих половин означала ещё и то, что enforcement читал
счётчики до записи снятой дельты. Джоба стала синхронной, под одним мьютексом на
весь цикл, порядок строгий. Закрыты три nil-разыменования — trafficSecretConfig,
item.AuthId и item.Id, — каждое из которых роняло процесс целиком вместе с
обработчиком machine-auth. Гейт Hysteria2IsRunning убран: util.Exec не отличает
«служба неактивна» от «спросить не удалось», и сломанный systemctl при живой
Hysteria молча отключал и учёт, и enforcement. Потеря дельты при отказе SQLite
больше не молчит: чтение /traffic?clear=1 деструктивно, и каждая потеря
считается. Checkpoint accounting в 1.0.0 намеренно не вводится — квота здесь
операционный предел доступа, а не учёт с финансово значимым каждым байтом.
Лимит устройств. Между чтением /online и ответом allow место ничем не
удерживалось: при online=max-1 два одновременных запроса получали разрешение
оба. Мьютекс вокруг /online этого не чинит — ответив allow, админка не создаёт
подключение, и следующий запрос продолжает видеть прежнее число. Появился
process-local учёт выданных, но ещё не проявившихся разрешений: решение по сумме
«подключено плюс зарезервировано», рост online снимает соответствующее их число,
протухшие снимаются по внутреннему TTL. Сеть опрашивается вне блокировки.
Гейты. Проверка «авторизация не возвращает успех из ветки ошибки» была записана
регуляркой err != nil \{[\s\S]*?return \*peer\.Id, а ленивый [\s\S]*? свободно
пересекает границы блоков: она даёт совпадение на коде из HEAD, то есть гейт
нельзя было удовлетворить, не сломав продукт. Тело ветки теперь выделяется по
балансу фигурных скобок, и логика проверена в обе стороны. go test -race стал
обязательным шагом сборки: состояние трекера разрешений и мьютекс цикла учёта
принадлежат процессу, и их корректность не наблюдаема ни в go test, ни в go vet;
пропуск при недоступном компиляторе не предусмотрен.
Панель. importPeerApi не объявлял skipErrorToast, а handleImport не имел ни try,
ни catch: после появления частичного результата отказ уходил бы необработанным
отклонением промиса, список не обновлялся бы при уже изменённой базе, а общий
перехватчик показал бы предупреждение красной ошибкой. Формулировка
peer_disconnect_failed во всех трёх местах сделана operation-neutral: через этот
код отчитываются восемь операций, а для удалённого пира прежняя фраза «новые
подключения пира запрещены» просто бессмысленна.
HY2XS Core
HY2XS — production‑установщик Hysteria2‑сервера с локальной HY2XS admin‑панелью, systemd‑юнитами, nftables‑firewall и воспроизводимой моделью release‑пакета для чистого Debian 13.
Что это · Возможности · Быстрый старт · Конфигурация · Версии · Сборка · Changelog · Лицензия
Что это
HY2XS — это install‑only пакет для развёртывания Hysteria2‑узла на чистом Debian 13 amd64. Проект разделяет сборку и установку: разработчик один раз собирает release‑архив на Debian 13 build‑хосте, а целевой сервер получает уже готовый пакет и запускает install.sh без git clone, go build, bun install, pnpm install и сборки frontend‑ассетов.
HY2XS подходит для сценария, где нужен один production‑сервер с Hysteria2, локальной админ‑панелью, управляемым firewall и предсказуемой конфигурацией. Проект не является универсальным server manager, update manager или панелью для любых Linux‑дистрибутивов.
Ключевая идея: на target‑сервере выполняется только установка готового пакета. Вся тяжёлая сборочная часть остаётся на build‑машине.
Возможности
HY2XS release‑пакет разворачивает и настраивает:
- официальный upstream‑бинарник Hysteria2: последняя стабильная версия выбирается при сборке пакета, закрепляется в его metadata и проверяется по SHA256;
- HY2XS admin — встроенную админ‑панель для управления users/peers, трафиком, конфигурацией, логами и состоянием сервера;
- systemd‑юнит
hysteria-serverдля Hysteria2; - systemd‑юнит
hy2xs-adminдля админ‑панели; - runtime‑конфиг
/etc/hy2xs/hy2xs.env; - Hysteria2‑конфиг
/etc/hysteria/config.yaml; - post‑install snapshot
/etc/hysteria/post-install.env; - bootstrap‑секреты администратора в
/etc/hy2xs/bootstrap-admin.secret; - nftables‑правила с staged apply и rollback guard;
- smoke‑проверки после установки;
- команды диагностики, статуса, реконфигурации и сбора support‑bundle.
HY2XS admin запускается локально по умолчанию на 127.0.0.1:8080. Наружу публикуется Hysteria2‑транспорт, а доступ к панели выполняется через SSH local port forwarding.
Архитектура
graph LR
operator[Оператор на Windows/Linux/macOS] -->|SSH 2323/tcp| ssh[sshd на target]
operator -->|SSH tunnel 127.0.0.1:8080| ui[HY2XS admin 127.0.0.1:8080]
internet[Интернет] -->|443/udp| hy2[Hysteria2 server]
letsencrypt[Let's Encrypt] -->|80/tcp при ACME http| acme[ACME challenge]
hy2 -->|HTTP auth localhost + machine token| ui
ui -->|trafficStats localhost| hy2
hy2 --> cfg["/etc/hysteria/config.yaml"]
ui --> env["/etc/hy2xs/hy2xs.env"]
fw[nftables] --> ssh
fw --> hy2
fw --> acme
В production‑профиле проект работает как IPv4‑only stack. IPv6 намеренно не входит в текущий baseline. Если у домена есть AAAA‑запись и включён строгий режим HY2XS_DNS_AAAA_POLICY=strict, установка останавливается до применения конфигурации.
Поддерживаемые платформы
Target‑сервер
| Компонент | Поддерживается |
|---|---|
| ОС | Debian 13 |
| Архитектура | amd64 / x86_64 |
| Init system | systemd |
| Firewall | nftables |
| Сетевой профиль | IPv4‑only |
| Hysteria2 target | linux‑amd64 |
| Admin UI | локальный bind по умолчанию: 127.0.0.1:8080 |
Build‑машина
| Компонент | Поддерживается |
|---|---|
| ОС | Debian 13 |
| Архитектура | amd64 / x86_64 |
| Git tree | обязателен |
| Dirty tree | запрещён по умолчанию |
| Интернет | нужен для apt, Go, Bun, Node.js, pnpm/npm registry, GitHub и upstream Hysteria2 |
Windows и macOS можно использовать для разработки, редактирования кода и работы с GitHub, но production‑архив должен собираться на Debian 13 amd64.
Что HY2XS не делает
Это важные границы проекта:
- не собирает проект на target‑сервере;
- не поддерживает Debian 12, Ubuntu, CentOS, Alpine и другие ОС;
- не поддерживает arm64 в текущем release‑профиле;
- не включает IPv6‑production baseline;
- не настраивает
sshdавтоматически; - не предоставляет полноценный uninstall/update framework;
- не обновляет Hysteria2 на уже работающем сервере:
reconfigureнамеренно не является Hysteria updater; - не мигрирует установки
0.xна1.0.0— переход выполняется чистой установкой, см. CHANGELOG; - не выполняет сложную миграцию старых неизвестных состояний сервера;
- не реализует Telegram‑бота, port hopping и универсальный access‑delivery workflow;
- не предназначен для установки поверх давно используемого сервера с неизвестными firewall/systemd‑правками.
Release‑пакет
Production‑сборка создаёт один архив:
hy2xs-install-<version>.tar.gz
Внутри архива находятся:
hy2xs-install/
├── install.sh
├── orchestrator/hy2xs-orchestrator
├── ui/hy2xs-admin/hy2xs-admin
├── config/hy2xs.env
├── templates/
├── systemd/
├── docs/
└── metadata/
При запуске install.sh пакет проверяет metadata/checksums.txt и выполняет
read‑only clean‑host preflight из распакованного архива. После этого он
передаёт управление install‑only orchestrator через exec — и больше не делает
ничего: сам install.sh не изменяет на сервере ни одного файла.
Всю раскладку выполняет уже оркестратор: ставит себя в
/usr/local/lib/hy2xs/hy2xs-orchestrator, создаёт symlink
/usr/local/bin/hy2xs-orchestrator, копирует package assets в
/usr/local/lib/hy2xs/package и продолжает установку. Это сделано ради одного
свойства: у изменений сервера ровно один владелец, поэтому при любом отказе
известно, что именно было создано и что откатывать.
Сетевая модель по умолчанию
| Назначение | Значение по умолчанию |
|---|---|
| SSH оператора | 2323/tcp |
| Hysteria2 | 443/udp |
| ACME HTTP challenge | 80/tcp, если HY2XS_TLS_MODE=acme и HY2XS_ACME_TYPE=http |
| HY2XS admin | 127.0.0.1:8080 |
| TrafficStats Hysteria2 | 127.0.0.1:36712 |
| Firewall mode | takeover в packaged baseline |
| Hysteria2 auth | http через локальный HY2XS admin |
| Hysteria2 obfs | gecko (512/1200); salamander доступен как режим совместимости |
| Congestion fallback | bbr, профиль standard |
| QUIC stateless reset | включён |
Важно: HY2XS_SSH_PORT нужен HY2XS для nftables‑правил и проверки доступности SSH‑порта. Сам sshd проект не перенастраивает. SSH на 2323 и вход только по ключу нужно настроить до запуска ./install.sh.
Версионная политика Hysteria2
HY2XS не привязан к конкретному номеру версии Hysteria.
Источник по умолчанию берёт последний стабильный релиз Hysteria, доступный на момент сборки пакета. Разрешённая версия, URL артефакта и контрольная сумма замораживаются в получившемся install‑пакете.
Как это работает:
build machine target server
───────────── ─────────────
определить последнюю стабильную ─┐
взять ожидаемый SHA-256 из │
upstream hashes.txt │
скачать артефакт и сверить его ├─► release‑пакет ──► скачать ровно
проверить, что бинарник принимает │ version + url этот артефакт,
канонический конфиг HY2XS │ + sha256 сверить SHA-256
заморозить version/url/sha256 ─┘ и `hysteria version`
Контрольная сумма берётся из upstream‑ассета hashes.txt, а не считается
только локально: локальный пересчёт подтверждает, что файл не изменился после
скачивания, но не доказывает, что скачан именно ожидаемый upstream artifact.
Что это даёт:
- новая установка получает актуальную Hysteria без ручного обновления version lock;
- если между сборкой пакета и его установкой выйдет новая версия, содержимое установки не изменится;
- повторная установка старого пакета поставит ту же версию, что и в день сборки;
- несовместимый upstream ломает сборку, а не сервер оператора.
Переопределения при сборке:
# по умолчанию: последняя стабильная
./tools/build/build.sh
# закрепить конкретную версию
HYSTERIA_VERSION_OVERRIDE=v2.12.2 ./tools/build/build.sh
# офлайн-сборка по закоммиченному tools/build/hysteria-lock.env
HYSTERIA_CHANNEL=pinned ./tools/build/build.sh
Фактически установленная версия видна в /etc/hysteria/post-install.env (HY2_VERSION), а способ её выбора — в HY2_RESOLUTION.
Обновление Hysteria на уже работающем сервере в текущем релизе не поддерживается: reconfigure намеренно не является Hysteria updater. Это сохраняет immutable‑контракт развёртывания.
Обфускация
Новые установки HY2XS используют Gecko.
Gecko помечен upstream как experimental. Он достраивается поверх Salamander: помимо scramble он дополнительно фрагментирует QUIC handshake на пакеты случайного размера. HY2XS использует upstream‑defaults размеров пакетов 512/1200 как проверенный production‑профиль.
Salamander остаётся поддержанным режимом совместимости. Смена типа обфускации требует соответствующих изменений на клиенте: это изменение wire‑совместимости, а не косметическая настройка.
| Gecko | Salamander | |
|---|---|---|
| Статус upstream | experimental | stable |
| Роль в HY2XS | default для новых установок | режим совместимости |
| Параметр | HY2XS_HYSTERIA_OBFS_TYPE=gecko |
HY2XS_HYSTERIA_OBFS_TYPE=salamander |
| В клиентской ссылке | obfs=gecko |
obfs=salamander |
Экспериментальность upstream остаётся контролируемым риском, потому что одновременно выполняются три условия: Salamander доступен как fallback, каждая разрешённая версия проходит compatibility gate до выпуска пакета, и существующие серверы никогда не переводятся на Gecko молча.
Размеры пакетов Gecko не выносятся в конфигурацию: официальная схема hysteria2:// не умеет их передавать, поэтому нестандартные значения сделали бы клиентскую ссылку неполной.
Быстрый старт для нового сервера
Ниже приведён полный путь для оператора, который работает с Windows и ставит HY2XS на чистый Debian 13 сервер.
В примерах используются условные значения:
| Параметр | Пример |
|---|---|
| IP сервера | SERVER_IP |
| Домен сервера | vpn.example.com |
| Email для ACME | admin@example.com |
| Release‑архив | hy2xs-install-0.2.2.tar.gz |
| SSH‑ключ | id_ed25519_hy2xs |
Замените эти значения на свои.
1. Проверьте DNS и внешние порты
До установки домен должен указывать A‑записью на IPv4‑адрес target‑сервера:
vpn.example.com -> SERVER_IP
Для стандартной установки с HY2XS_TLS_MODE=acme и HY2XS_ACME_TYPE=http снаружи должны быть доступны:
| Порт | Протокол | Назначение |
|---|---|---|
2323 |
TCP | SSH оператора |
80 |
TCP | ACME HTTP challenge |
443 |
UDP | Hysteria2 |
Если у домена есть AAAA‑запись, при строгой политике HY2XS_DNS_AAAA_POLICY=strict установка будет остановлена, потому что текущий production‑профиль HY2XS является IPv4‑only.
A‑запись должна указывать именно на этот сервер, а не просто существовать. Preflight сверяет её с публичными IPv4, назначенными интерфейсам машины, и останавливает установку при расхождении:
DNS IPv4 mismatch for HY2XS_PUBLIC_HOST vpn.example.com:
DNS A records: 185.xxx.xxx.10
server public IPv4: 185.xxx.xxx.27
Update the DNS A record before using this server.
Та же проверка выполняется в reconfigure и doctor, поэтому принудительная смена IPv4 провайдером не остаётся незамеченной. Адрес сервера определяется локально, без обращения к внешним сервисам определения IP.
Если сервер работает за NAT или на floating IP — это топология вне текущего baseline; осознанное решение оформляется значением HY2XS_PUBLIC_ENDPOINT_POLICY=warn.
2. Создайте SSH‑ключ на Windows
Откройте PowerShell:
ssh-keygen -t ed25519 -a 100 -f "$env:USERPROFILE\.ssh\id_ed25519_hy2xs" -C "hy2xs-target-1"
Выведите публичный ключ:
type "$env:USERPROFILE\.ssh\id_ed25519_hy2xs.pub"
Скопируйте всю строку вида:
ssh-ed25519 AAAA... hy2xs-target-1
Именно её нужно добавить на сервер в /root/.ssh/authorized_keys.
3. Войдите на сервер по паролю провайдера
Первый вход обычно выполняется по временному root‑паролю от провайдера:
ssh root@SERVER_IP
На сервере установите базовые пакеты:
apt-get update
apt-get install -y openssh-server sudo ca-certificates curl tar
4. Добавьте публичный ключ на сервер
На сервере замените пример ключа на свой реальный публичный ключ:
install -d -m 700 -o root -g root /root/.ssh
cat > /root/.ssh/authorized_keys <<'EOF'
ssh-ed25519 AAAA_REPLACE_WITH_YOUR_PUBLIC_KEY hy2xs-target-1
EOF
chmod 600 /root/.ssh/authorized_keys
chown -R root:root /root/.ssh
5. Переведите SSH на порт 2323 и отключите парольный вход
Не закрывайте текущую SSH‑сессию, пока не проверите новый вход по ключу.
Сначала посмотрите, какие SSH‑директивы уже заданы провайдером:
grep -RniE '^[[:space:]]*(Include|Port|PermitRootLogin|PasswordAuthentication|KbdInteractiveAuthentication|ChallengeResponseAuthentication|PubkeyAuthentication|AllowTcpForwarding|PermitOpen|GatewayPorts|Match)\b' \
/etc/ssh/sshd_config /etc/ssh/sshd_config.d/*.conf 2>/dev/null || true
OpenSSH для многих параметров использует первое найденное значение. Поэтому поздний файл вида 99-*.conf может не перекрыть ранний provider‑конфиг. Безопаснее сначала закомментировать конфликтующие директивы, затем создать ранний 00-hy2xs-operator.conf.
for f in /etc/ssh/sshd_config /etc/ssh/sshd_config.d/*.conf; do
[ -f "$f" ] || continue
cp -a "$f" "$f.bak.$(date +%Y%m%d-%H%M%S)"
sed -i -E 's/^[[:space:]]*(Port|PermitRootLogin|PasswordAuthentication|KbdInteractiveAuthentication|ChallengeResponseAuthentication|PubkeyAuthentication|AllowTcpForwarding|PermitOpen|GatewayPorts|X11Forwarding|AllowAgentForwarding|MaxAuthTries|LoginGraceTime|ClientAliveInterval|ClientAliveCountMax)[[:space:]]+/# disabled-by-hy2xs-hardening: &/' "$f"
done
cat > /etc/ssh/sshd_config.d/00-hy2xs-operator.conf <<'EOF'
Port 2323
PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no
ChallengeResponseAuthentication no
PermitRootLogin prohibit-password
AllowTcpForwarding local
PermitOpen 127.0.0.1:8080 localhost:8080
GatewayPorts no
X11Forwarding no
AllowAgentForwarding no
MaxAuthTries 3
LoginGraceTime 20
ClientAliveInterval 300
ClientAliveCountMax 2
EOF
sshd -t
systemctl reload ssh || systemctl restart ssh
Проверьте effective‑конфиг:
sshd -T | grep -E '^(port|pubkeyauthentication|passwordauthentication|kbdinteractiveauthentication|permitrootlogin|allowtcpforwarding|permitopen|gatewayports) '
ss -H -ltn | grep -E ':(22|2323) '
Ожидаемо:
port 2323
permitrootlogin without-password
pubkeyauthentication yes
passwordauthentication no
kbdinteractiveauthentication no
gatewayports no
allowtcpforwarding local
permitopen 127.0.0.1:8080 localhost:8080
В списке listen‑портов должен остаться 2323, а 22 не должен слушаться.
6. Проверьте вход по ключу
В новом PowerShell‑окне:
ssh -p 2323 -i "$env:USERPROFILE\.ssh\id_ed25519_hy2xs" root@SERVER_IP
Проверьте, что парольный вход запрещён:
ssh -p 2323 -o PreferredAuthentications=password -o PubkeyAuthentication=no root@SERVER_IP
Правильный результат:
Permission denied (publickey).
После этого можно дополнительно заблокировать пароль root на уровне Linux‑аккаунта:
passwd -l root
passwd -S root
Это не ломает вход по SSH‑ключу, но защищает от случайного возврата парольной SSH‑аутентификации в будущем.
7. Скопируйте release‑архив на сервер
С Windows:
scp -P 2323 -i "$env:USERPROFILE\.ssh\id_ed25519_hy2xs" .\hy2xs-install-0.2.2.tar.gz root@SERVER_IP:/root/
На сервере:
cd /root
tar -xzf hy2xs-install-0.2.2.tar.gz
cd /root/hy2xs-install
8. Создайте конфиг для своего сервера
Не редактируйте исходный шаблон внутри пакета без необходимости. Создайте отдельный source‑config:
cp config/hy2xs.env /root/hy2xs-target.env
nano /root/hy2xs-target.env
Минимально проверьте и измените:
HY2XS_DOMAIN=vpn.example.com
HY2XS_PUBLIC_HOST=vpn.example.com
HY2XS_ACME_EMAIL=admin@example.com
HY2XS_SSH_PORT=2323
HY2XS_UI_BIND_HOST=127.0.0.1
HY2XS_UI_PORT=8080
HY2XS_HYSTERIA_PORT=443
HY2XS_FIREWALL_MODE=takeover
HY2XS_FIREWALL_STAGED_APPLY=true
Для стандартной production‑установки оставьте:
HY2XS_IPV6_ENABLED=false
HY2XS_TLS_MODE=acme
HY2XS_ACME_TYPE=http
HY2XS_HYSTERIA_AUTH_MODE=http
HY2XS_HYSTERIA_OBFS_TYPE=gecko
HY2XS_UI_PUBLIC_ACCESS=false
Если нужен режим совместимости со старыми клиентами, укажите HY2XS_HYSTERIA_OBFS_TYPE=salamander. Подробнее — в разделе Обфускация.
9. Запустите установку
./install.sh --config /root/hy2xs-target.env --non-interactive
Установка идёт в две фазы с жёсткой границей между ними.
PHASE 0 — только чтение. До её успешного завершения на сервере не
изменяется ни один файл, включая /usr/local/lib/hy2xs:
- проверит, что запущено от root;
- проверит checksums release‑пакета;
- запустит clean‑host preflight из распакованного архива: платформа Debian 13 amd64, отсутствие предыдущей установки, валидность конфигурации.
PHASE 1 — применение изменений. Её целиком выполняет оркестратор, которому
install.sh передал управление через exec:
- установит сам себя в
/usr/local/lib/hy2xsи разложит runtime‑пакет; - создаст runtime‑каталоги и service users;
- запишет
/etc/hy2xs/hy2xs.env; - разложит bundled HY2XS admin;
- скачает закреплённый в пакете Hysteria2 binary из upstream, проверит SHA256 и фактическую версию;
- создаст
/etc/hysteria/config.yaml; - установит systemd‑юниты;
- применит nftables‑правила;
- выполнит smoke‑checks;
- зафиксирует успешное состояние в
/var/lib/hy2xs/install-state.json.
Если PHASE 0 не прошла, установщик завершается с ошибкой и сервер остаётся в том же состоянии, в котором был. HY2XS v1 не устанавливается поверх предыдущего поколения и не мигрирует его состояние: очистка старой установки — отдельная явная операция, см. docs/operations/14-legacy-cleanup.md.
10. Получите bootstrap‑пароль админки
cat /etc/hy2xs/bootstrap-admin.secret
Файл содержит:
ADMIN_USER=...
ADMIN_INITIAL_PASSWORD=...
ADMIN_CON_PASS=...
ADMIN_INITIAL_PASSWORD нужен для первого входа в HY2XS admin. После первого входа смените пароль через интерфейс.
ADMIN_CON_PASS — bootstrap‑секрет Hysteria2 peer/auth слоя. Не публикуйте этот файл и не отправляйте его в issue/log без редактирования.
11. Откройте HY2XS admin через SSH‑туннель
На локальной машине:
ssh -p 2323 `
-i "$env:USERPROFILE\.ssh\id_ed25519_hy2xs" `
-N `
-L 127.0.0.1:8080:127.0.0.1:8080 `
root@SERVER_IP
Пример для Linux/macOS (одной строкой):
ssh -p 2323 -i ~/.ssh/id_ed25519_hy2xs -N -L 8080:127.0.0.1:8080 root@SERVER_IP
После этого откройте в браузере:
http://127.0.0.1:8080/
Если порт 8080 на локальной машине занят, можно пробросить другой локальный порт:
ssh -p 2323 `
-i "$env:USERPROFILE\.ssh\id_ed25519_hy2xs" `
-N `
-L 127.0.0.1:18080:127.0.0.1:8080 `
root@SERVER_IP
И открыть:
http://127.0.0.1:18080/
12. Проверьте установку
На сервере:
systemctl status hysteria-server --no-pager
systemctl status hy2xs-admin --no-pager
ss -H -lun | grep ':443'
ss -H -ltn | grep ':8080'
ss -H -ltn | grep ':2323'
nft list ruleset
cat /var/lib/hy2xs/install-state.json
Выполните doctor‑проверку:
hy2xs-orchestrator doctor \
--package-dir /usr/local/lib/hy2xs/package \
--config /etc/hy2xs/hy2xs.env
Проверьте сводный статус:
hy2xs-orchestrator status \
--package-dir /usr/local/lib/hy2xs/package \
--config /etc/hy2xs/hy2xs.env
Конфигурация hy2xs.env
После установки основным редактируемым файлом является:
/etc/hy2xs/hy2xs.env
Менять runtime‑параметры нужно через этот файл и затем применять reconfigure. Редактирование post-install.env не меняет runtime‑состояние.
Основные параметры
| Переменная | Назначение | Значение по умолчанию в packaged baseline |
|---|---|---|
HY2XS_CONFIG_SCHEMA_VERSION |
Версия схемы конфигурации HY2XS. Конфигурация другой схемы отклоняется fail‑fast | 2 |
HY2XS_IPV6_ENABLED |
IPv6‑режим. В production baseline должен быть false |
false |
HY2XS_DOMAIN |
Домен для ACME и deploy‑профиля | fi.api.withen.pro |
HY2XS_DNS_AAAA_POLICY |
Поведение при наличии AAAA‑записи: strict, warn, off |
strict |
HY2XS_PUBLIC_ENDPOINT_POLICY |
Строгость проверки того, что A‑записи публичного endpoint ведут на IPv4 этого сервера: strict, warn, off |
strict |
HY2XS_PUBLIC_HOST |
Публичный host без схемы, порта и path | fi.api.withen.pro |
HY2XS_PUBLIC_PORT |
Публичный порт Hysteria2 endpoint | 443 |
HY2XS_SSH_PORT |
SSH‑порт, который будет разрешён firewall‑правилами | 2323 |
HY2XS_FIREWALL_MODE |
Режим управления nftables: managed, takeover, external, off |
takeover |
HY2XS_FIREWALL_STAGED_APPLY |
Включает staged apply и rollback guard firewall | true |
HY2XS_UI_BIND_HOST |
IPv4 bind HY2XS admin | 127.0.0.1 |
HY2XS_UI_PUBLIC_ACCESS |
Флаг публичного UI‑доступа. В baseline оставляйте false |
false |
HY2XS_UI_PORT |
Порт HY2XS admin | 8080 |
HY2XS_ADMIN_USER |
Bootstrap‑логин администратора | hy2xsadmin |
HY2XS_ADMIN_INITIAL_PASSWORD |
Bootstrap‑пароль администратора; __GENERATE__ генерируется при install |
__GENERATE__ |
HY2XS_ADMIN_CON_PASS |
Bootstrap‑секрет peer/auth слоя; __GENERATE__ генерируется при install |
__GENERATE__ |
HY2XS_FORCE_PASSWORD_CHANGE |
Принудительная смена пароля. UX‑flow пока не включён в production baseline | false |
HY2XS_ALLOW_SELF_SIGNED_DEV |
Разрешает self_signed_dev TLS‑режим |
false |
HY2XS_TLS_MODE |
TLS‑режим: acme, file, self_signed_dev |
acme |
HY2XS_ACME_TYPE |
ACME challenge: http, tls, dns; dns пока не поддержан production‑профилем |
http |
HY2XS_ACME_EMAIL |
Email для ACME | admin@withen.pro |
HY2XS_TLS_CERT_PATH |
Путь к cert при file/self_signed_dev |
/etc/hysteria/server.crt |
HY2XS_TLS_KEY_PATH |
Путь к key при file/self_signed_dev |
/etc/hysteria/server.key |
HY2XS_HYSTERIA_BIND_HOST |
Bind Hysteria2. В production profile фиксируется на 0.0.0.0 |
0.0.0.0 |
HY2XS_HYSTERIA_PORT |
UDP‑порт Hysteria2 | 443 |
HY2XS_HYSTERIA_AUTH_MODE |
Auth mode Hysteria2. Фиксированное значение production‑профиля | http |
HY2XS_HYSTERIA_TRAFFIC_STATS_HOST |
Host trafficStats API | 127.0.0.1 |
HY2XS_HYSTERIA_TRAFFIC_STATS_PORT |
Порт trafficStats API | 36712 |
HY2XS_HYSTERIA_TRAFFIC_STATS_SECRET |
Secret для trafficStats и machine auth | __GENERATE__ |
HY2XS_HYSTERIA_OBFS_TYPE |
Тип обфускации: gecko или salamander. Смена меняет wire‑совместимость |
gecko |
HY2XS_HYSTERIA_OBFS_PASSWORD |
Пароль обфускации; __GENERATE__ генерируется при install |
__GENERATE__ |
HY2XS_HYSTERIA_BANDWIDTH_UP |
Hysteria2 upstream bandwidth | 50 mbps |
HY2XS_HYSTERIA_BANDWIDTH_DOWN |
Hysteria2 downstream bandwidth | 50 mbps |
HY2XS_HYSTERIA_IGNORE_CLIENT_BANDWIDTH |
Игнорировать bandwidth клиента | false |
HY2XS_HYSTERIA_CONFIG_PATH |
Путь Hysteria2 config. В production baseline фиксирован | /etc/hysteria/config.yaml |
HY2XS_INSTALL_DIR |
Каталог установки HY2XS admin | /opt/hy2xs-admin |
HY2XS_DATA_DIR |
Data‑каталог HY2XS admin | /var/lib/hy2xs-admin |
HY2XS_LOG_DIR |
Log‑каталог HY2XS | /var/log/hy2xs |
Режимы firewall
| Режим | Поведение |
|---|---|
managed |
HY2XS управляет nftables, но останавливается при обнаружении чужого /etc/nftables.conf |
takeover |
HY2XS явно берёт управление nftables на себя |
external |
HY2XS не меняет nftables; оператор сам отвечает за firewall |
off |
Firewall‑слой HY2XS отключён |
При managed и takeover генерируется nftables‑конфигурация с default drop policy, разрешением loopback, established/related, SSH‑порта, ACME challenge‑порта, Hysteria2 UDP‑порта и ICMP echo‑request.
Защита от потери доступа при смене firewall
При HY2XS_FIREWALL_STAGED_APPLY=true (значение по умолчанию) перед применением новых правил HY2XS взводит rollback guard — транзиентный systemd‑юнит с окном 45 секунд. Если операция не снимет его вовремя, guard вернёт прежний firewall, и SSH останется доступным.
Таймеру явно задаётся AccuracySec=1s, поэтому «45 секунд» — это реальный контракт, а не приблизительный: по умолчанию systemd.timer разрешает себе сработать в окне [цель; цель + AccuracySec], где AccuracySec — одна минута, и обещанное окно превращалось бы в 45–105 секунд. Вторым свойством задаётся RemainAfterElapse=no: отработавший таймер обязан выгрузиться, иначе он навсегда блокировал бы следующую операцию (см. «Одна операция за раз»).
Окно намеренно короткое и не обязано покрывать smoke‑checks: на медленном сервере они идут дольше. Вместо этого guard оставляет за собой факт срабатывания в /run/hy2xs/rollback/<op-id>/auto-rollback-fired, и операция не имеет права объявить себя успешной, если этот файл появился, — сервер в такой момент работает на прежнем firewall, а не на том, который она сгенерировала. Установка завершится отказом с phase: firewall_guard_fired, и её нужно повторить после устранения причины медленного прохода.
Дополнительно smoke сверяет, что действующий firewall — именно тот, который сгенерирован для текущей конфигурации: разбора /etc/nftables.conf для этого недостаточно, потому что прежний ruleset тоже валиден.
Одна операция за раз
install, reconfigure, repair и doctor сериализованы эксклюзивным замком /run/lock/hy2xs-orchestrator.lock. Вторая операция отказывает сразу и до первой мутации:
another HY2XS operation is already in progress: reconfigure (pid 4242, started at …)
Это не перестраховка: конфиги, unit‑файлы, /etc/nftables.conf и маркер установки — общие, и две одновременные операции записывают их поверх друг друга, после чего откат одной «восстанавливает» состояние поверх изменений другой.
status и diagnostics collect замок не берут — они нужны в том числе во время долгой операции, — но сообщают о ней в своём выводе.
Замок снимается сам при любом завершении держателя, включая Ctrl+C, SIGTERM и обрыв SSH. Если процесс был убит kill -9, следующая операция обнаружит мёртвого держателя и переиспользует замок самостоятельно.
Замка при этом недостаточно: он действует, пока жив процесс‑держатель, а rollback guard firewall — отдельный объект systemd, который свой процесс переживает. Аварийно умершая операция оставляет guard вооружённым, и он способен вернуть прежний firewall уже посреди следующей операции. Поэтому условие старта — не «предыдущая операция мертва», а «у неё не осталось исполнителей, способных изменить систему»:
previous HY2XS operation is no longer running, but its firewall rollback guard
is still armed: hy2xs-fw-rollback-<op-id>.timer (active/waiting)
Ждать в этом случае нужно не дольше 45–46 секунд с момента применения firewall.
Покоем считаются ровно два состояния юнита — inactive и failed: отработавший guard больше ничего не сделает, а отказ по failed заблокировал бы repair, которым чинят последствия. Всё остальное, включая незнакомые барьеру состояния systemd, операцию запрещает.
Отдельный случай — когда состояние guard'а вообще не удалось выяснить:
unable to verify firewall rollback guard state; systemd query failed,
refusing to start a lifecycle operation
Здесь ждать нечего: отсутствие ответа systemd — это отсутствие доказательства, а не доказательство покоя, и разбираться нужно с systemd. Барьер обязан доказать, что у предыдущей операции не осталось исполнителей, способных изменить firewall; молчаливое «наверное, всё в порядке» однажды означало бы срабатывание старого таймера поверх новой операции.
Реконфигурация
После изменения /etc/hy2xs/hy2xs.env сначала выполните dry‑run:
hy2xs-orchestrator reconfigure \
--package-dir /usr/local/lib/hy2xs/package \
--config /etc/hy2xs/hy2xs.env \
--dry-run
Если ошибок нет, примените изменения:
hy2xs-orchestrator reconfigure \
--package-dir /usr/local/lib/hy2xs/package \
--config /etc/hy2xs/hy2xs.env \
--apply
reconfigure --apply выполняет backup, генерирует конфиги, обновляет systemd‑юниты, применяет firewall, выполняет smoke‑checks и при ошибке пытается откатить изменённое состояние.
Важно: HY2XS_ADMIN_INITIAL_PASSWORD и HY2XS_ADMIN_CON_PASS являются install‑only bootstrap‑полями. Их изменение в /etc/hy2xs/hy2xs.env после первичной установки не ротирует уже созданные credentials в SQLite автоматически.
Команды управления
| Команда | Назначение |
|---|---|
hy2xs-orchestrator preflight-install |
Read‑only проверка чистоты хоста; ничего не меняет |
hy2xs-orchestrator status |
Показать состояние платформы, сервисов, firewall и install marker |
hy2xs-orchestrator doctor |
Выполнить preflight и smoke‑checks текущей установки; сервисы не перезапускает |
hy2xs-orchestrator reconfigure --dry-run |
Проверить конфиг без применения |
hy2xs-orchestrator reconfigure --apply |
Применить runtime‑конфигурацию |
hy2xs-orchestrator repair --allow-partial-state |
Довести до конца незавершённую установку текущего поколения |
hy2xs-orchestrator diagnostics collect |
Собрать diagnostic bundle в /var/log/hy2xs/diagnostics |
hy2xs-orchestrator redact-config |
Отредактировать секреты в env/yaml перед публикацией логов |
repair без --allow-partial-state работает только поверх полностью успешной
установки. В обоих режимах он сначала проверяет, что
/var/lib/hy2xs/install-state.json принадлежит текущему поколению продукта
(product, release_line, config_schema_version), и отказывается работать
поверх чужого состояния.
Пример сбора диагностики:
hy2xs-orchestrator diagnostics collect \
--package-dir /usr/local/lib/hy2xs/package \
--config /etc/hy2xs/hy2xs.env
Проверка безопасности после установки
Минимальный набор проверок:
# SSH слушает только ожидаемый порт
ss -H -ltn | grep -E ':(22|2323) '
# password auth отключён
sshd -T | grep -E '^(port|permitrootlogin|pubkeyauthentication|passwordauthentication|kbdinteractiveauthentication|allowtcpforwarding|permitopen) '
# UI не слушает публичный 0.0.0.0
ss -H -ltn | grep ':8080'
# Hysteria2 слушает UDP-порт
ss -H -lun | grep ':443'
# nftables-конфиг валиден
nft -c -f /etc/nftables.conf
# сервисы активны
systemctl is-active hysteria-server hy2xs-admin
Ожидаемые SSH‑значения:
port 2323
permitrootlogin without-password
pubkeyauthentication yes
passwordauthentication no
kbdinteractiveauthentication no
allowtcpforwarding local
permitopen 127.0.0.1:8080 localhost:8080
Troubleshooting
Установка отказывается: обнаружена предыдущая установка
[hy2xs] ERROR: На сервере обнаружена предыдущая или посторонняя установка.
HY2XS v1 не поддерживает установку поверх и не мигрирует состояние 0.x.
Ни один файл на сервере не изменён.
Это ожидаемое поведение, а не сбой. Отказ происходит в PHASE 0, до любой мутации: сервер остался в том состоянии, в котором был.
Что делать:
- сохраните нужные данные (база пиров, конфиг) — см. docs/operations/14-legacy-cleanup.md;
- посмотрите план очистки:
sudo ./purge-v0.sh; - выполните очистку:
sudo ./purge-v0.sh --apply --yes-i-know; - повторите установку.
Отдельный случай — отказ вида
HY2XS_CONFIG_SCHEMA_VERSION отсутствует в конфигурации. Он означает, что
переданный --config относится к предыдущему поколению: до v1 этого поля не
существовало. Создайте конфиг заново по разделу «Создайте конфиг для своего
сервера».
Если установка HY2XS v1 упала после начала применения изменений, полная
очистка не нужна — используйте
hy2xs-orchestrator repair --allow-partial-state.
Установка падает на DNS AAAA
Причина: домен имеет IPv6 AAAA‑запись, а HY2XS production profile является IPv4‑only.
Решения:
- удалить AAAA‑запись у домена;
- либо временно установить
HY2XS_DNS_AAAA_POLICY=warn, если оператор осознанно принимает риск клиентских IPv6‑маршрутов вне текущего baseline.
DNS IPv4 mismatch: DNS ведёт не на этот сервер
Причина: A‑запись публичного endpoint указывает на адрес, которого нет среди публичных IPv4 этого сервера. Типичный случай — провайдер принудительно сменил IP, а DNS остался старым: сервисы на машине живы, но клиентская ссылка отправляет людей на другой адрес.
Решения:
- сверить фактический адрес сервера и обновить A‑запись:
ip -4 addr show scope global
- дождаться истечения TTL и повторить
hy2xs-orchestrator doctor; - если в строке
server public IPv4:пусто — на интерфейсах нет публичного IPv4 (сервер за NAT). Это вне baseline; при осознанном решении установитеHY2XS_PUBLIC_ENDPOINT_POLICY=warn.
Если A‑записей несколько и среди них есть посторонняя, проверка тоже отказывает: HY2XS — single‑host профиль, и второй backend за тем же именем означает, что часть клиентов попадёт не на этот сервер.
Установка падает на проверке SSH‑порта
HY2XS firewall‑слой проверяет, что порт из HY2XS_SSH_PORT уже слушается. Если указано 2323, но sshd продолжает слушать только 22, установка остановится.
Проверьте:
ss -H -ltn | grep -E ':(22|2323) '
sshd -T | grep '^port '
Вход по паролю всё ещё работает
Проверьте ранние provider‑конфиги:
grep -RniE '^[[:space:]]*(PermitRootLogin|PasswordAuthentication|KbdInteractiveAuthentication|PubkeyAuthentication)\b' \
/etc/ssh/sshd_config /etc/ssh/sshd_config.d/*.conf
Если есть ранний файл с PasswordAuthentication yes, он может применяться раньше вашего hardening‑файла. Закомментируйте конфликтующие директивы и создайте 00-hy2xs-operator.conf, как показано в quick start.
UI не открывается в браузере
Проверьте, что SSH‑туннель запущен и сервис слушает localhost:
systemctl status hy2xs-admin --no-pager
ss -H -ltn | grep ':8080'
curl -sS http://127.0.0.1:8080/healthz
На локальной машине проверьте, что порт не занят другим приложением. При необходимости используйте локальный порт 18080 вместо 8080.
Нужно отправить логи без секретов
Соберите диагностику:
hy2xs-orchestrator diagnostics collect \
--package-dir /usr/local/lib/hy2xs/package \
--config /etc/hy2xs/hy2xs.env
Перед публикацией отдельных env/yaml файлов используйте redaction:
hy2xs-orchestrator redact-config \
--config /etc/hy2xs/hy2xs.env \
--out /root/hy2xs.env.redacted \
--format env
Сборка release‑пакета
Обычному пользователю не нужно собирать проект из исходников. Этот раздел нужен maintainer’у, который готовит release‑архив.
Перед работой: сверьте среду с контрактом
./tools/dev/doctor.sh # Linux/macOS
.\tools\dev\doctor.ps1 # Windows (PowerShell 7+)
HY2XS development environment
contract: versions.env (HY2XS 1.0.0, release line 1)
Go:
required: 1.26.7
found: 1.25.6
FAIL — локальный Go собирает не ту stdlib, что уедет в релиз; поставьте 1.26.7
Node:
required: 24.20.0
found: 24.20.0
OK
Скрипт ничего не устанавливает и не меняет — он отвечает на один вопрос: совпадает ли эта машина с контрактом сборки.
Раньше versions.env был контрактом только для сборки: она скачивает Go, Node и
Bun ровно тех версий, что там записаны, сверяя контрольные суммы, а машина
разработчика не проверялась никак. Расхождение обнаруживалось на Debian, внутри
release‑сборки, и выглядело как «у меня работало».
Расхождение не гипотетическое. Директива go в apps/go.mod — это языковой
baseline модуля, а не выбор компилятора, поэтому локальный Go другой минорной
линии собирал проект успешно, пока релизный бинарь компилировался на 1.26.7 и
наследовал её stdlib: проверялся не тот код, который уезжает в production.
Поэтому go.mod теперь объявляет toolchain явно, а doctor показывает
расхождение до сборки, а не после.
Сборка поддерживается на Debian 13 amd64 из чистого git work tree.
apt-get update
apt-get install -y git ca-certificates curl unzip tar xz-utils build-essential pkg-config bash coreutils findutils grep sed gawk openssl
git clone <repo-url>
cd <repo-dir>
git status --short
Подготовьте build env:
Версии и контрольные суммы toolchain не задаются переменными окружения: они
объявлены в корневом versions.env, и сборка берёт их оттуда.
Раньше их приходилось передавать снаружи, из-за чего воспроизводимая сборка в
чистой Debian‑среде требовала предварительного знания четырёх SHA‑256.
export BUILD_ID=prod-$(date -u +%Y%m%dT%H%M%SZ)
# Для переносимости между x86_64-серверами без AVX2 предпочтителен baseline artifact.
# Ожидаемый digest выбирается автоматически: в versions.env зафиксированы обе суммы.
export BUN_FLAVOR=x64-baseline
# Опционально: снимает anonymous rate limit при разрешении upstream-релиза.
export GITHUB_TOKEN=<token>
PACKAGE_VERSION тоже приходит из versions.env (HY2XS_VERSION). Шаг
verify_versions_contract роняет сборку, если версия продукта, схема
конфигурации, целевая платформа или packageManager в package.json
разошлись с контрактом.
Запустите сборку:
./tools/build/build.sh
Сборка последовательно:
- проверяет контракт
versions.env(verify_versions_contract); - прогоняет тесты и типы оркестратора (
bun test,tsc --noEmit); - прогоняет контрактные тесты панели (спрайт иконок, словари локализации, коды ошибок, атрибуция);
- определяет последнюю стабильную версию Hysteria, берёт ожидаемый SHA‑256 из upstream
hashes.txtи сверяет с ним скачанный артефакт; - проходит compatibility gate: реальный бинарник Hysteria должен принять канонический конфиг HY2XS для Gecko и для Salamander;
- собирает orchestrator, frontend и backend, проставляя версию админки из контракта;
- прогоняет
go vetиgo testдля HY2XS admin; - проверяет граф зависимостей на известные уязвимости (
govulncheck ./...иpnpm auditпо всему lock‑графу); - формирует архив и прогоняет acceptance‑проверки.
Любой сбой на шагах 1–8 останавливает сборку до создания пакета.
Тесты и типы (шаги 2, 3 и 7) — такой же обязательный гейт, как проверка
зависимостей: переменной, которая их отключает, не существует. Готовый пакет
объявляет об этом полем tests_gate=true в metadata/package.env, и это
утверждение опирается на фактический прогон, а не на намерение.
Для локальной работы обходить нечего: bun test, bun x tsc --noEmit,
go vet ./..., go test ./... и
bun test tools/test/frontend-sprite.test.ts tools/test/frontend-contract.test.ts
запускаются напрямую и tarball не создают.
Переменные, управляющие выбором версии Hysteria:
| Переменная | По умолчанию | Назначение |
|---|---|---|
HYSTERIA_CHANNEL |
из versions.env (stable) |
stable — разрешить последнюю стабильную; pinned — офлайн‑сборка по tools/build/hysteria-lock.env |
HYSTERIA_VERSION_OVERRIDE |
пусто | Закрепить конкретную версию vX.Y.Z |
HYSTERIA_COMPAT_GATE |
true |
Compatibility gate; для release‑сборок обязателен |
HYSTERIA_VERIFY_UPSTREAM_HASHES |
true |
Сверять артефакт с upstream hashes.txt; отключение — только break‑glass |
HYSTERIA_WRITE_LOCK |
false |
Записать разрешённые значения обратно в lock‑файл |
Проверка зависимостей переменными не управляется: у неё нет аварийного
выхода. Релизный артефакт HY2XS невозможно собрать с непройденным гейтом, и
поле dependency_security_gate в metadata/package.env принимает единственное
значение true.
Раньше здесь были описаны два способа выпустить релиз, зная об уязвимости. Ими
они не являлись: финальная приёмка архива требует буквально
dependency_security_gate=true, поэтому сборка с любым из них проходила весь
цикл и падала на последнем шаге. Документированная операция, которую продукт сам
же запрещает, — хуже отсутствующей.
pnpm audit при этом проверяет весь lock‑граф frontend, а не только
production‑подграф. Причина в том, что build tooling исполняется на build‑машине
и порождает production‑бандл: уязвимость в vite/rollup уезжает в артефакт,
хотя сами они на сервер не копируются. Ровно такой случай и был найден — DOM
clobbering в Rollup затрагивал генерируемый бандл, а проверка по одному
production‑подграфу его не показывала.
Если advisory вышло в неудачный момент, чинится это обновлением графа
(apps/go.sum, apps/frontend/pnpm-lock.yaml) или версии toolchain в
versions.env. Для локальной работы обходить нечего: go test ./...,
govulncheck ./... и pnpm audit запускаются напрямую и tarball не создают.
Проверка versions‑контракта и проверка зависимостей отвечают на разные вопросы. Первая следит, что зафиксированные версии согласованы между собой; вторая — что про эти версии не стало известно плохого. Зафиксированный граф не стареет только на бумаге: advisory по нему выходят и после фиксации, а сборка релиза — единственный момент, когда это расхождение ловится дёшево.
Полный E2E с реальным клиентом Hysteria запускается отдельно (нужен Go: ссылка берётся из production‑генератора, а не из отдельной реализации внутри теста):
HYSTERIA_BIN=/usr/local/bin/hysteria ./tools/test/e2e-hysteria.sh
Результат:
dist/hy2xs-install-<version>.tar.gz
Проверьте архив:
ls -lh dist/hy2xs-install-*.tar.gz
sha256sum dist/hy2xs-install-*.tar.gz
tar -tzf dist/hy2xs-install-1.0.0.tar.gz | grep -E \
'^(hy2xs-install/install.sh|hy2xs-install/orchestrator/hy2xs-orchestrator|hy2xs-install/ui/hy2xs-admin/hy2xs-admin|hy2xs-install/metadata/checksums.txt)$'
Подробная документация по сборочному слою находится в tools/build/README.md.
Структура репозитория
.
├── apps/ # HY2XS admin: Go backend + Vue/Vite frontend
├── orchestrator/ # install-only orchestrator на Bun + TypeScript
├── package/ # skeleton будущего install package
├── tools/build/ # production builder и packaging pipeline
├── tools/dev/ # doctor: сверка среды разработки с versions.env
├── tools/test/ # e2e с реальным клиентом Hysteria и контракты панели
├── tools/legacy/ # purge-v0.sh: очистка сервера от предыдущего поколения
├── docs/ # документация, разложенная по слоям
│ ├── architecture/ # baseline-модель и рамки
│ ├── build/ # builder layer и состав пакета
│ ├── runtime/ # оркестратор, systemd, post-install
│ ├── admin/ # HY2XS admin и контракты панели
│ ├── operations/ # runbook, разбор отказов, очистка 0.x
│ ├── testing/ # набор проверок по слоям
│ └── acceptance/ # отчёты о фактических прогонах приёмки
├── versions.env # контракт продукта, платформы и toolchain
├── CHANGELOG.md
├── README.md
└── LICENSE
Каталог dist/ создаётся builder’ом и не должен храниться в git.
Точка входа в документацию — docs/README.md.
Для кого этот проект
HY2XS рассчитан на операторов, которым нужен воспроизводимый способ поставить Hysteria2‑сервер с локальной панелью управления, не собирая проект на production‑сервере и не открывая admin UI наружу.
Проект особенно полезен, если важны:
- строгая target‑платформа;
- установка из одного release‑архива;
- локальный UI через SSH‑туннель;
- systemd/nftables baseline;
- проверяемая установка со smoke‑checks;
- понятная диагностика при сбоях.
Лицензия
HY2XS распространяется на условиях GNU Affero General Public License v3.0 only (AGPL-3.0-only).
Полный текст лицензии находится в LICENSE.
Сторонние библиотеки и зависимости сохраняют собственные лицензии.
Разработано во Flamy Studio.
