# Admin panel: HY2XS admin ## Цель документа Зафиксировать модель работы с admin-панелью: HY2XS admin является **штатным компонентом HY2XS**, а не внешней зависимостью, которую target server где-то добывает во время установки. ## Место компонента в системе Проект состоит из компонентов двух разных типов: - **Hysteria2** — external runtime dependency. Ванильный upstream-бинарник, который оркестратор забирает из официального источника во время установки. - **HY2XS admin** — native HY2XS component. Исходный код лежит в репозитории, компонент собирается production builder'ом вместе с остальными артефактами HY2XS. Это противопоставление и есть основная архитектурная граница. ## Состав компонента - исходный код admin-компонента хранится в [`apps/`](../apps); - backend реализован на Go; - frontend реализован на Vue/Vite; - frontend-ассеты встраиваются в Go-бинарник; - компонент собирается production builder'ом вместе с остальными артефактами HY2XS; - готовый бинарник едет в install package как `ui/hy2xs-admin/hy2xs-admin`. ## Правила поставки HY2XS admin: - поставляется внутри итогового пакета; - имеет свой install dir; - имеет свой data dir; - запускается отдельным rootless systemd unit (`hy2xs-admin`); - не требует target-side build. Target server **не собирает** admin-компонент из исходного кода и **не скачивает** его из внешнего репозитория. ## Scope панели Панель нужна для: - operator-facing управления; - просмотра статуса; - работы с пользователями / трафиком / сущностями доступа; - удобной админской рутины. Панель не должна: - определять install lifecycle сервера; - превращать систему в сложный control plane; - диктовать scope оркестратора. HY2XS admin работает как надстройка над Hysteria YAML/API-слоем. Это нормально: важно только, чтобы источник истины по runtime-состоянию был понятен и не было двух конкурирующих конфигурационных миров без правил синхронизации. Относительно конфигурации Hysteria панель **read-only**: конфиг генерирует оркестратор. ## Два слоя работы с конфигом Hysteria Это важное архитектурное разделение. | Слой | Назначение | Поведение при неизвестных полях | | --- | --- | --- | | Типизированная модель | отображение известных HY2XS полей в UI | неизвестные поля не отображаются | | Сырой YAML | экспорт и сохранение | неизвестные поля **сохраняются** | Причина: если бы экспорт работал через типизированную модель (`Unmarshal` → структура → `Marshal`), то любое поле, о котором HY2XS ещё не знает, терялось бы при round-trip. Панель незаметно урезала бы современный конфиг. Поэтому: - экспорт читает исходный YAML и сохраняет структуру документа целиком; - будущие версии Hysteria не ломают экспорт только потому, что backend и frontend ещё не научились показывать новый параметр; - это прямое следствие модели «latest stable на сборке»: схема upstream может опережать модель HY2XS. ### Санитайз экспорта Экспортируемый файл покидает сервер, поэтому секреты из него вырезаются: - пароли обфускации (`obfs.*.password`); - `trafficStats.secret`; - `access_token` в auth-URL и учётные данные, встроенные в URL; - `auth.password`, `auth.userpass`; - учётные данные ACME DNS-провайдера; - любые **неизвестные** поля, имя которых содержит `password`, `secret`, `token` или `credential`. Последний пункт — обратная сторона сохранения неизвестных полей: новое upstream-поле с секретом вырезается ещё до того, как HY2XS про него узнает. Пути к файлам (`tls.cert`, `tls.key`, `ech.keyPath`, `tls.clientCA`) секретами не считаются и остаются читаемыми — они нужны для диагностики. ## Модель современной схемы Hysteria Модель админки понимает актуальную серверную схему, даже там, где UI не позволяет ничего включить: `obfs.gecko`, `ech`, `congestion`, `mimic`, `realm`, `tls.clientCA`, `quic.disableStatelessReset`, `bandwidth.disableLossCompensation`, `masquerade.proxy.xForwarded`. Смысл в том, чтобы admin **понимал текущую upstream-схему**, а не считал неизвестными поля собственного конфига. ## Генерация клиентских ссылок - тип обфускации и пароль берутся из фактического конфига одинаково для всех поддерживаемых типов (`gecko`, `salamander`); - неизвестный тип обфускации в ссылку не попадает: лучше отсутствие параметра, чем параметр, который клиент не понимает; - публичный endpoint берётся из `HY2XS_PUBLIC_HOST` + `HY2XS_PUBLIC_PORT`, а не из `listen` или Host-заголовка запроса; - SNI берётся из ACME-домена, затем из `HY2XS_DOMAIN`, затем из `HY2XS_PUBLIC_HOST`; IP-адрес как SNI не используется; - `minPacketSize`/`maxPacketSize` Gecko в ссылку не помещаются — поэтому HY2XS держит их на upstream-defaults `512/1200`. ## Правила ответственности ### Source of truth - runtime transport layer: Hysteria2 - операторский UI layer: HY2XS admin - install lifecycle: оркестратор HY2XS - deploy facts: `post-install.env` ## Production lifecycle Hysteria2 В production package HY2XS admin не скачивает и не обновляет бинарь Hysteria2 самостоятельно. Правильная модель: - Hysteria2 устанавливается install-оркестратором с official upstream; - Hysteria2 запускается отдельным `hysteria-server.service`; - HY2XS admin работает как operator UI и HTTP auth/traffic layer; - HY2XS admin не запускается от root; - смена версии Hysteria2 через UI отключена в baseline; - список upstream releases не является частью operator UI baseline; - port hopping не является частью production path. ### Что нельзя делать - собирать admin-компонент на target server; - скачивать admin-компонент на target из внешнего репозитория; - склеивать unit Hysteria2 и unit HY2XS admin в один сервис; - раздувать оркестратор из-за особенностей панели; - использовать HY2XS admin как updater бинаря Hysteria2; - использовать `JWT_SECRET` как `trafficStats.secret` для Hysteria API; - экспортировать конфиг Hysteria через типизированную модель — так теряются неизвестные upstream-поля; - выгружать конфиг с секретами в открытом виде. ## Что фиксировать в `post-install.env` Минимум: - `HY2XS_ADMIN_ENABLED` - `HY2XS_ADMIN_SOURCE` - `HY2XS_ADMIN_BUILD_ID` - `HY2XS_ADMIN_BIND_HOST` - `HY2XS_ADMIN_PORT` - `HY2XS_ADMIN_INSTALL_DIR` - `HY2XS_ADMIN_DATA_DIR` - `HY2XS_ADMIN_LOG_DIR` ## Инварианты Схема считается корректной, если: 1. HY2XS admin приезжает на target уже в составе пакета 2. target не скачивает и не собирает admin-компонент 3. HY2XS admin работает отдельным сервисом 4. HY2XS admin не меняет install-only scope оркестратора 5. Hysteria2 остаётся внешним vanilla upstream-компонентом 6. HY2XS admin не выступает updater-менеджером Hysteria2 7. `trafficStats.secret` не связан с `JWT_SECRET` 8. экспорт конфига сохраняет неизвестные upstream-поля 9. экспорт конфига не содержит секретов 10. сгенерированная `hysteria2://` ссылка содержит фактический тип обфускации, и совместимый клиент подключается по ней напрямую