feat(v1): Gecko-обфускация, latest-stable Hysteria на сборке и forward-compatible admin
Сквозная миграция 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 файлов), документация на русском.
This commit is contained in:
@@ -41,9 +41,10 @@ Target layer **не содержит сборщика** и **не выполня
|
||||
- **Hysteria2**
|
||||
- QUIC/UDP
|
||||
- один фиксированный UDP-порт
|
||||
- `Salamander` включён по умолчанию
|
||||
- `Gecko` включён по умолчанию, `Salamander` доступен как режим совместимости
|
||||
- IPv4-only
|
||||
- лимит по умолчанию: 50/50 Mbps на клиента
|
||||
- fallback congestion controller: BBR (профиль `standard`)
|
||||
|
||||
### UI слой
|
||||
- **HY2XS admin** — штатный компонент проекта
|
||||
@@ -65,7 +66,7 @@ Target layer **не содержит сборщика** и **не выполня
|
||||
## Принципы
|
||||
|
||||
### 1. Ядро, UI и оркестратор ведут себя по-разному
|
||||
- Hysteria2: берём свежую upstream-версию при установке
|
||||
- Hysteria2: последнюю стабильную upstream-версию выбирает **сборка пакета**, установка ставит уже замороженный артефакт
|
||||
- HY2XS admin: разрабатываем **внутри проекта** и поставляем его сами
|
||||
- Оркестратор: пишем на **Bun + TypeScript**, но собираем **локально**, а не на target
|
||||
|
||||
|
||||
@@ -43,13 +43,17 @@ Builder не является частью target install flow: на target serv
|
||||
## Что делает builder
|
||||
|
||||
1. Проверяет структуру проекта.
|
||||
2. Собирает / подготавливает HY2XS admin.
|
||||
3. Компилирует оркестратор из Bun/TypeScript в install-артефакт.
|
||||
4. Копирует артефакты UI в package staging directory.
|
||||
5. Кладёт entrypoint, templates, docs и service files.
|
||||
6. Формирует итоговый install package.
|
||||
7. При необходимости считает manifest/checksum.
|
||||
8. Выдаёт один переносимый результат для target machine.
|
||||
2. Прогоняет тесты и типы оркестратора.
|
||||
3. Разрешает upstream-версию Hysteria и проходит compatibility gate.
|
||||
4. Компилирует оркестратор из Bun/TypeScript в install-артефакт.
|
||||
5. Собирает / подготавливает HY2XS admin.
|
||||
6. Прогоняет тесты HY2XS admin (после сборки frontend: `go:embed all:dist` требует готовых ассетов).
|
||||
7. Копирует артефакты UI в package staging directory.
|
||||
8. Кладёт entrypoint, templates, docs и service files.
|
||||
9. Формирует итоговый install package.
|
||||
10. Считает manifest/checksum.
|
||||
11. Проверяет архив и прогоняет acceptance-проверки.
|
||||
12. Выдаёт один переносимый результат для target machine.
|
||||
|
||||
## Что builder не делает
|
||||
|
||||
@@ -119,14 +123,76 @@ project/
|
||||
Сам бинарь Hysteria2 **не вендорится** в install package как baseline-правило.
|
||||
|
||||
Причина:
|
||||
- ядро Hysteria рассматривается как stable upstream component
|
||||
- целевая установка должна брать его с official upstream на момент развёртывания
|
||||
- ядро Hysteria рассматривается как stable upstream component;
|
||||
- целевая установка скачивает его с official upstream, но **строго по замороженным координатам**.
|
||||
|
||||
### Разрешение версии на сборке
|
||||
|
||||
```text
|
||||
SOURCE
|
||||
│
|
||||
▼
|
||||
resolve latest stable (HyNetworks/hysteria, только теги app/vX.Y.Z)
|
||||
│
|
||||
▼
|
||||
resolve exact release asset (hysteria-linux-amd64)
|
||||
│
|
||||
▼
|
||||
download + compute SHA-256
|
||||
│
|
||||
▼
|
||||
compatibility gate (реальный бинарник принимает канонический конфиг HY2XS)
|
||||
│
|
||||
▼
|
||||
PACKAGE METADATA
|
||||
version = vX.Y.Z
|
||||
exact_url = <immutable release asset>
|
||||
sha256 = <...>
|
||||
resolution = latest-stable | pinned | override
|
||||
│
|
||||
▼
|
||||
TARGET SERVER
|
||||
скачивает уже конкретный неизменяемый артефакт
|
||||
```
|
||||
|
||||
Так одновременно выполняются оба требования: «по умолчанию брать последнюю стабильную» и «production-установка должна быть детерминированной и проверяемой».
|
||||
|
||||
Переменные builder:
|
||||
|
||||
| Переменная | Значение по умолчанию | Назначение |
|
||||
| --- | --- | --- |
|
||||
| `HYSTERIA_CHANNEL` | `stable` | `stable` — разрешить последнюю стабильную через upstream API; `pinned` — взять `tools/build/hysteria-lock.env` без сети |
|
||||
| `HYSTERIA_VERSION_OVERRIDE` | пусто | Закрепить конкретную версию `vX.Y.Z` |
|
||||
| `HYSTERIA_COMPAT_GATE` | `true` | Compatibility gate; для release-сборок обязателен |
|
||||
| `HYSTERIA_WRITE_LOCK` | `false` | Записать разрешённые значения обратно в `tools/build/hysteria-lock.env` |
|
||||
| `GITHUB_TOKEN` | пусто | Опционально, чтобы не упереться в anonymous rate limit |
|
||||
|
||||
Дополнительно:
|
||||
- версия Hysteria2 фиксируется в metadata install package (`metadata/hysteria.version`);
|
||||
- URL артефакта и SHA256 фиксируются в metadata install package (`metadata/hysteria.url`, `metadata/hysteria.sha256`);
|
||||
- версия, URL и SHA256 фиксируются в metadata install package (`metadata/hysteria.version`, `metadata/hysteria.url`, `metadata/hysteria.sha256`);
|
||||
- способ выбора версии фиксируется в `metadata/hysteria.resolution` и `metadata/package.env`;
|
||||
- runtime `reconfigure` не обновляет и не откатывает бинарник Hysteria2;
|
||||
- install flow валидирует SHA256 и фактическую версию установленного бинарника.
|
||||
- install flow валидирует SHA256 и фактическую версию установленного бинарника;
|
||||
- install-time код не обращается к upstream API и не использует moving `latest` — это проверяется тестами и acceptance-шагом сборки.
|
||||
|
||||
### Compatibility gate
|
||||
|
||||
Gate защищает от ситуации, когда upstream меняет схему конфигурации, а builder молча собирает неработающий HY2XS.
|
||||
|
||||
Порядок:
|
||||
|
||||
1. скачать артефакт и сверить SHA-256;
|
||||
2. сверить `hysteria version` с разрешённой версией;
|
||||
3. отрендерить канонический конфиг HY2XS тем же кодом, что работает на target (`orchestrator/tools/render-canonical-config.ts`);
|
||||
4. запустить реальный бинарник Hysteria с этим конфигом — для Gecko и для Salamander;
|
||||
5. только после этого собирать release package.
|
||||
|
||||
При несовместимости ломается сборка:
|
||||
|
||||
```text
|
||||
BUILD FAILED: unsupported Hysteria stable v2.13.0
|
||||
```
|
||||
|
||||
Это осознанно: ошибка должна проявиться на build machine, а не на сервере оператора.
|
||||
|
||||
## Инварианты
|
||||
|
||||
@@ -137,4 +203,6 @@ project/
|
||||
3. на сервере нет отдельного build step
|
||||
4. bundled UI уже находится внутри пакета
|
||||
5. оркестратор authored as Bun/TypeScript, но на target приходит как готовый install-артефакт
|
||||
6. Hysteria2 подтягивается install layer'ом с upstream, а не собирается на target из исходников
|
||||
6. Hysteria2 подтягивается install layer'ом с upstream по замороженным координатам, а не собирается на target из исходников
|
||||
7. выход новой версии Hysteria после сборки не меняет содержимое уже собранного пакета
|
||||
8. несовместимый upstream ломает сборку, а не установку у пользователя
|
||||
|
||||
+146
-18
@@ -18,13 +18,53 @@ Hysteria2 — основной транспортный компонент се
|
||||
|
||||
## Версионная политика
|
||||
|
||||
С учётом выбранной модели «берём свежее из upstream» фиксируется такая практика:
|
||||
Ключевое правило: **«последняя стабильная» определяется на этапе сборки пакета, а не на целевом сервере.**
|
||||
|
||||
- по умолчанию install layer тянет **свежий upstream release / install source**
|
||||
- фактически установленная версия обязательно записывается в `post-install.env`
|
||||
- бинарник Hysteria2 устанавливается только на этапе `install`
|
||||
- версия, URL и SHA256 фиксируются в metadata install package
|
||||
- `reconfigure` не обновляет и не откатывает бинарник Hysteria2
|
||||
Не «HY2XS использует Hysteria vX.Y.Z», а:
|
||||
|
||||
> HY2XS по умолчанию берёт последний стабильный релиз Hysteria, доступный на момент сборки пакета. Разрешённая версия, URL артефакта и контрольная сумма замораживаются в получившемся install package.
|
||||
|
||||
Практика:
|
||||
|
||||
- builder обращается к каноническому upstream `HyNetworks/hysteria`;
|
||||
- принимаются только стабильные релизы с тегом вида `app/vX.Y.Z`, без draft и prerelease;
|
||||
- берётся ровно один артефакт `hysteria-linux-amd64`, URL используется в том виде, в каком его отдал upstream API;
|
||||
- SHA-256 считается локально от скачанного артефакта, а не берётся из стороннего файла;
|
||||
- версия, URL и SHA-256 фиксируются в metadata install package;
|
||||
- на target-сервере **никогда** не используется moving `latest`;
|
||||
- бинарник Hysteria2 устанавливается только на этапе `install`;
|
||||
- фактически установленная версия записывается в `post-install.env`;
|
||||
- `reconfigure` не обновляет и не откатывает бинарник Hysteria2.
|
||||
|
||||
Следствие: если между сборкой пакета и его установкой выйдет новая версия Hysteria, содержимое установки **не изменится под ногами**. Повторная установка старого пакета поставит ту же версию, что и в день сборки.
|
||||
|
||||
Переопределения builder:
|
||||
|
||||
```bash
|
||||
HYSTERIA_CHANNEL=stable # по умолчанию: разрешить последнюю стабильную
|
||||
HYSTERIA_CHANNEL=pinned # взять закоммиченный tools/build/hysteria-lock.env, без сети
|
||||
HYSTERIA_VERSION_OVERRIDE=v2.12.2 # закрепить конкретную версию
|
||||
```
|
||||
|
||||
## Compatibility gate
|
||||
|
||||
Автоматический выбор «последней стабильной» без проверки опасен: upstream может изменить схему конфигурации, и builder молча соберёт неработающий HY2XS.
|
||||
|
||||
Поэтому до создания release-пакета builder:
|
||||
|
||||
1. скачивает артефакт и сверяет SHA-256;
|
||||
2. сверяет `hysteria version` с разрешённой версией;
|
||||
3. рендерит канонический конфиг HY2XS **тем же кодом**, который работает на target-сервере;
|
||||
4. запускает реальный бинарник Hysteria с этим конфигом — отдельно для Gecko и для Salamander;
|
||||
5. только после этого формирует пакет.
|
||||
|
||||
Если upstream несовместим, ломается **сборка**:
|
||||
|
||||
```text
|
||||
BUILD FAILED: unsupported Hysteria stable v2.13.0
|
||||
```
|
||||
|
||||
а не production-сервер оператора.
|
||||
|
||||
## Платформа
|
||||
|
||||
@@ -48,14 +88,55 @@ Hysteria2 — основной транспортный компонент се
|
||||
|
||||
## Обфускация
|
||||
|
||||
В baseline включается:
|
||||
- `obfs.type: salamander`
|
||||
- `obfs.password`
|
||||
Новые установки HY2XS используют **Gecko**.
|
||||
|
||||
Gecko помечен upstream как **experimental**. Он достраивается поверх Salamander: помимо scramble он дополнительно фрагментирует QUIC handshake на пакеты случайного размера. HY2XS использует upstream-defaults размеров пакетов `512/1200` как проверенный production-профиль.
|
||||
|
||||
**Salamander остаётся полностью поддержанным режимом совместимости.** Смена типа обфускации требует соответствующих изменений на клиенте: это изменение wire-совместимости, а не косметическая настройка.
|
||||
|
||||
Baseline:
|
||||
|
||||
```yaml
|
||||
obfs:
|
||||
type: gecko
|
||||
gecko:
|
||||
password: "<сгенерированный пароль>"
|
||||
minPacketSize: 512
|
||||
maxPacketSize: 1200
|
||||
```
|
||||
|
||||
Режим совместимости:
|
||||
|
||||
```yaml
|
||||
obfs:
|
||||
type: salamander
|
||||
salamander:
|
||||
password: "<сгенерированный пароль>"
|
||||
```
|
||||
|
||||
Правила:
|
||||
- пароль должен быть сильным
|
||||
- пароль должен фиксироваться в конфигурационном контуре
|
||||
- значение должно быть доступно оператору через runtime config и `post-install.env`
|
||||
- тип выбирается через `HY2XS_HYSTERIA_OBFS_TYPE` (`gecko` | `salamander`);
|
||||
- пароль должен быть сильным, генерируется автоматически при `__GENERATE__` или пустом значении;
|
||||
- пароль фиксируется в конфигурационном контуре и доступен оператору через runtime config и `post-install.env`;
|
||||
- `obfs`-блок формируется оркестратором целиком, а не собирается из отдельных placeholders внутри YAML — комбинация вида `type: gecko` рядом с блоком `salamander` структурно невозможна.
|
||||
|
||||
### Почему размеры пакетов Gecko не вынесены в env
|
||||
|
||||
Официальная схема `hysteria2://` передаёт только тип обфускации и пароль. `minPacketSize` и `maxPacketSize` в ссылку не помещаются.
|
||||
|
||||
Если разрешить оператору произвольные значения, сгенерированная клиентская ссылка перестанет полностью описывать подключение и потребуется отдельный формат — выгружаемый клиентский профиль. Пока такой задачи нет, фиксация `512/1200` даёт корректную ссылку и воспроизводимое поведение.
|
||||
|
||||
Валидация (на случай будущего расширения) централизована в оркестраторе: `min > 0`, `max >= min`, `max <= 2048`.
|
||||
|
||||
## Версия схемы конфигурации
|
||||
|
||||
```bash
|
||||
HY2XS_CONFIG_SCHEMA_VERSION=2
|
||||
```
|
||||
|
||||
Пакет понимает только свою версию схемы. Конфигурация с другой версией отклоняется fail-fast, а не применяется частично.
|
||||
|
||||
HY2XS `v1` **не мигрирует установки `0.x` на месте**: между `0.x` и `1.0.0` изменились схема конфигурации, тип обфускации по умолчанию и контракт выбора версии Hysteria. Переход выполняется чистой установкой.
|
||||
|
||||
## TLS
|
||||
|
||||
@@ -72,8 +153,9 @@ Hysteria2 — основной транспортный компонент се
|
||||
- `acme` block обязан содержать `type: http|tls` из runtime env (`HY2XS_ACME_TYPE`);
|
||||
- `HY2XS_ACME_TYPE=dns` в production-профиле запрещён до отдельной реализации;
|
||||
- `HY2XS_HYSTERIA_AUTH_MODE` зафиксирован в `http` и валидируется fail-fast;
|
||||
- `HY2XS_HYSTERIA_OBFS_TYPE` зафиксирован в `salamander` и валидируется fail-fast;
|
||||
- блок `masquerade` в baseline не задаётся (допустимо, но приводит к `404 Not Found` на обычный HTTP трафик);
|
||||
- `HY2XS_HYSTERIA_OBFS_TYPE` принимает `gecko` (default) или `salamander` и валидируется fail-fast;
|
||||
- блок `masquerade` в baseline не задаётся: при включённой обфускации сервер и так перестаёт быть обычным HTTP/3 endpoint, поэтому masquerade не даёт выигрыша, а `404 Not Found` на обычный HTTP-трафик — ожидаемое поведение;
|
||||
- `ech` в baseline не включается: при включённой обфускации соединение целиком перестаёт выглядеть как обычный QUIC, поэтому ECH не даёт дополнительной выгоды (он полезен в bare-режиме);
|
||||
- `file` -> только `tls.cert`/`tls.key` block;
|
||||
- `self_signed_dev` -> только dev сценарии.
|
||||
|
||||
@@ -91,11 +173,55 @@ Hysteria2 — основной транспортный компонент се
|
||||
Серверная baseline policy:
|
||||
- `bandwidth.up = 50 mbps`
|
||||
- `bandwidth.down = 50 mbps`
|
||||
- `bandwidth.disableLossCompensation = false`
|
||||
- `ignoreClientBandwidth = false`
|
||||
- `congestion.type = bbr`
|
||||
- `congestion.bbrProfile = standard`
|
||||
|
||||
Важно:
|
||||
- эти параметры сами по себе не исчерпывают speed policy
|
||||
- корректный лимит ожидается только в паре с совместимым клиентским конфигом
|
||||
- эти параметры сами по себе не исчерпывают speed policy;
|
||||
- корректный лимит ожидается только в паре с совместимым клиентским конфигом;
|
||||
- `congestion` — это **fallback** controller: он применяется, когда Brutal bandwidth не согласован сторонами. Подробнее — в [06-speed-limits-and-congestion.md](06-speed-limits-and-congestion.md).
|
||||
|
||||
## QUIC stateless reset
|
||||
|
||||
```yaml
|
||||
quic:
|
||||
disableStatelessReset: false
|
||||
```
|
||||
|
||||
Начиная с Hysteria 2.12.1 сервер отправляет stateless reset, чтобы клиент со stale-соединением после перезапуска сервера или сна устройства переподключался сразу, а не по таймауту. В 2.12.2 появилась возможность это отключить.
|
||||
|
||||
Для VPN-подобного применения HY2XS быстрый reconnect — плюс, поэтому механизм остаётся включённым, а значение фиксируется в конфиге явно.
|
||||
|
||||
## Возможности вне default-профиля
|
||||
|
||||
HY2XS обязан **понимать** современную схему Hysteria, но не обязан включать всё подряд. Разделяются три уровня:
|
||||
|
||||
| Возможность | Генерирует HY2XS | Читает и сохраняет | Отдельный профиль |
|
||||
| --- | :-: | :-: | :-: |
|
||||
| Gecko | да | да | — |
|
||||
| Salamander | fallback | да | — |
|
||||
| BBR / bbrProfile | да | да | — |
|
||||
| Loss compensation | да | да | — |
|
||||
| QUIC stateless reset | да | да | — |
|
||||
| ECH | нет | да | позже |
|
||||
| Mimic | нет | да | позже |
|
||||
| Realms | нет | да | позже |
|
||||
| Port hopping | нет | да | позже |
|
||||
| ACME DNS | нет | да | позже |
|
||||
| Masquerade | нет | да | позже |
|
||||
|
||||
Причина не в качестве этих возможностей, а в том, что каждая меняет соседнюю подсистему:
|
||||
|
||||
- **Mimic** — привилегии, eBPF/XDP, сторонний бинарник, требования к клиенту; текущий systemd-контракт намеренно запускает Hysteria под непривилегированным пользователем с `CapabilityBoundingSet=CAP_NET_BIND_SERVICE`, поэтому Mimic несовместим с ним по построению и требует отдельного security-профиля;
|
||||
- **Realms** — сетевая топология (STUN/hole punching вместо публичного IPv4 и own nftables);
|
||||
- **Port hopping** — nftables и capabilities; официально несовместим с Mimic;
|
||||
- **ECH** — жизненный цикл ключей и распространение конфигурации клиентам (Hysteria не генерирует ECH keypair сама);
|
||||
- **ACME DNS** — учётные данные провайдера и работа с секретами;
|
||||
- **Masquerade** — дополнительное web/proxy-поведение.
|
||||
|
||||
Ни одна из них не должна включаться toggle'ом, который незаметно меняет systemd capabilities или топологию firewall.
|
||||
|
||||
## Рекомендуемые пути
|
||||
|
||||
@@ -108,8 +234,8 @@ Hysteria2 — основной транспортный компонент се
|
||||
|
||||
После установки должно быть верно:
|
||||
|
||||
1. Hysteria2 получена из official upstream
|
||||
2. фактическая версия отражена в `post-install.env`
|
||||
1. Hysteria2 получена из official upstream по замороженному в пакете URL и SHA-256
|
||||
2. фактическая версия совпадает с версией из metadata пакета и отражена в `post-install.env`
|
||||
3. конфиг валиден
|
||||
4. сервис стартует через systemd
|
||||
5. нужный UDP-порт реально слушается
|
||||
@@ -118,3 +244,5 @@ Hysteria2 — основной транспортный компонент се
|
||||
8. `trafficStats.secret` отдельный от `JWT_SECRET`
|
||||
9. IPv6 listen не используется
|
||||
10. публичные клиентские endpoint/URL берутся из `HY2XS_PUBLIC_HOST` + `HY2XS_PUBLIC_PORT`, а не из `listen`/request-host
|
||||
11. сгенерированная `hysteria2://` ссылка содержит фактический тип обфускации и пароль, и совместимый клиент подключается по ней напрямую
|
||||
12. SNI в ссылке берётся из ACME-домена, затем из `HY2XS_DOMAIN`, затем из `HY2XS_PUBLIC_HOST`; IP-адрес как SNI не используется
|
||||
|
||||
+54
-1
@@ -51,6 +51,54 @@ Target server **не собирает** admin-компонент из исход
|
||||
|
||||
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
|
||||
@@ -81,7 +129,9 @@ HY2XS admin работает как надстройка над Hysteria YAML/AP
|
||||
- склеивать unit Hysteria2 и unit HY2XS admin в один сервис;
|
||||
- раздувать оркестратор из-за особенностей панели;
|
||||
- использовать HY2XS admin как updater бинаря Hysteria2;
|
||||
- использовать `JWT_SECRET` как `trafficStats.secret` для Hysteria API.
|
||||
- использовать `JWT_SECRET` как `trafficStats.secret` для Hysteria API;
|
||||
- экспортировать конфиг Hysteria через типизированную модель — так теряются неизвестные upstream-поля;
|
||||
- выгружать конфиг с секретами в открытом виде.
|
||||
|
||||
## Что фиксировать в `post-install.env`
|
||||
|
||||
@@ -107,3 +157,6 @@ HY2XS admin работает как надстройка над Hysteria YAML/AP
|
||||
5. Hysteria2 остаётся внешним vanilla upstream-компонентом
|
||||
6. HY2XS admin не выступает updater-менеджером Hysteria2
|
||||
7. `trafficStats.secret` не связан с `JWT_SECRET`
|
||||
8. экспорт конфига сохраняет неизвестные upstream-поля
|
||||
9. экспорт конфига не содержит секретов
|
||||
10. сгенерированная `hysteria2://` ссылка содержит фактический тип обфускации, и совместимый клиент подключается по ней напрямую
|
||||
|
||||
@@ -13,12 +13,27 @@
|
||||
|
||||
Это неверная модель.
|
||||
|
||||
## Три разные вещи, которые нельзя смешивать
|
||||
|
||||
Это главный источник путаницы в теме скоростей Hysteria.
|
||||
|
||||
| Механизм | Что это | Где задаётся |
|
||||
| --- | --- | --- |
|
||||
| **Политика HY2XS 50/50** | продуктовое решение проекта, сколько давать клиенту | `HY2XS_HYSTERIA_BANDWIDTH_UP` / `_DOWN` |
|
||||
| **Brutal bandwidth** | режим Hysteria, работающий по согласованным сторонами значениям полосы | `bandwidth.up` / `bandwidth.down` на сервере + hints на клиенте |
|
||||
| **Fallback congestion controller** | что делает Hysteria, когда Brutal не применяется | `congestion.type` / `congestion.bbrProfile` |
|
||||
|
||||
`50 mbps` здесь — **не** «оптимальная скорость Hysteria» и не свойство протокола. Это политика HY2XS.
|
||||
|
||||
## Что зафиксировано в baseline
|
||||
|
||||
### На сервере
|
||||
- `bandwidth.up = 50 mbps`
|
||||
- `bandwidth.down = 50 mbps`
|
||||
- `bandwidth.disableLossCompensation = false`
|
||||
- `ignoreClientBandwidth = false`
|
||||
- `congestion.type = bbr`
|
||||
- `congestion.bbrProfile = standard`
|
||||
|
||||
### На клиенте
|
||||
Совместимый клиентский конфиг должен задавать соответствующие bandwidth hints:
|
||||
@@ -29,13 +44,30 @@
|
||||
|
||||
Ожидаемый 50/50 Mbps contract считается корректным только тогда, когда сервер и клиентская конфигурация согласованы.
|
||||
|
||||
Логика выбора внутри Hysteria:
|
||||
|
||||
- когда стороны согласовали Brutal bandwidth — используется Brutal;
|
||||
- когда это не применяется — используется выбранный fallback congestion controller.
|
||||
|
||||
Поэтому BBR тоже является частью явного baseline HY2XS, а не «настройкой по умолчанию, о которой можно не думать».
|
||||
|
||||
## Loss compensation
|
||||
|
||||
```yaml
|
||||
bandwidth:
|
||||
disableLossCompensation: false
|
||||
```
|
||||
|
||||
Компенсация потерь (появилась в Hysteria 2.10.0) позволяет отправлять быстрее заданной полосы, чтобы компенсировать потерю пакетов. В baseline HY2XS она **включена**, а значение фиксируется в конфиге явно — проект про воспроизводимое поведение, а не про молчаливое следование upstream-дефолтам.
|
||||
|
||||
## Что делать с host-level BBR
|
||||
|
||||
`net.ipv4.tcp_congestion_control=bbr` можно оставить как общий системный тюнинг, но:
|
||||
|
||||
- это не главный механизм speed policy Hysteria2
|
||||
- это не замена клиентским bandwidth hints
|
||||
- это не центр документации по лимитам
|
||||
- это не главный механизм speed policy Hysteria2;
|
||||
- это не замена клиентским bandwidth hints;
|
||||
- это **не то же самое**, что `congestion.type: bbr` в конфиге Hysteria — у Hysteria собственный congestion-control контур поверх QUIC;
|
||||
- это не центр документации по лимитам.
|
||||
|
||||
## Что фиксировать в `post-install.env`
|
||||
|
||||
@@ -43,8 +75,11 @@
|
||||
- `HY2_BANDWIDTH_UP`
|
||||
- `HY2_BANDWIDTH_DOWN`
|
||||
- `HY2_IGNORE_CLIENT_BANDWIDTH`
|
||||
- `HY2_DISABLE_LOSS_COMPENSATION`
|
||||
- `HY2_CONGESTION_TYPE`
|
||||
- `HY2_BBR_PROFILE`
|
||||
|
||||
Дополнительно фиксируется `HY2_VERSION` как фактически установленная версия Hysteria2.
|
||||
Дополнительно фиксируется `HY2_VERSION` как фактически установленная версия Hysteria2 и `HY2_RESOLUTION` как способ её выбора при сборке пакета.
|
||||
|
||||
## Что нельзя писать в проектных доках
|
||||
|
||||
@@ -52,6 +87,8 @@
|
||||
- «лимит задаётся только на сервере, клиент не важен»
|
||||
- «любой URI достаточно для полной speed policy»
|
||||
- «host BBR и есть логика Hysteria»
|
||||
- «50 mbps — оптимальная скорость Hysteria» (это политика HY2XS, а не свойство протокола)
|
||||
- «Brutal и congestion controller — одно и то же»
|
||||
|
||||
## Правильная baseline-формулировка
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
- `ORCH_ENTRYPOINT`
|
||||
|
||||
### Общие
|
||||
- `HY2XS_CONFIG_SCHEMA_VERSION`
|
||||
- `DEPLOY_DOMAIN`
|
||||
- `PUBLIC_HOST`
|
||||
- `PUBLIC_PORT`
|
||||
@@ -71,7 +72,8 @@
|
||||
|
||||
### Hysteria
|
||||
- `HY2_SOURCE=official-upstream`
|
||||
- `HY2_VERSION`
|
||||
- `HY2_VERSION` — фактически установленная версия
|
||||
- `HY2_RESOLUTION` — как версия была выбрана при сборке: `latest-stable`, `pinned` или `override`
|
||||
- `HY2_TLS_MODE`
|
||||
- `HY2_ACME_EMAIL`
|
||||
- `HY2_TLS_CERT_PATH`
|
||||
@@ -81,11 +83,17 @@
|
||||
- `HY2_AUTH_MODE`
|
||||
- `HY2_AUTH_URL`
|
||||
- `HY2_TRAFFIC_STATS_LISTEN`
|
||||
- `HY2_OBFS_TYPE`
|
||||
- `HY2_OBFS_TYPE` — `gecko` или `salamander`
|
||||
- `HY2_OBFS_PASSWORD`
|
||||
- `HY2_GECKO_MIN_PACKET_SIZE`
|
||||
- `HY2_GECKO_MAX_PACKET_SIZE`
|
||||
- `HY2_BANDWIDTH_UP`
|
||||
- `HY2_BANDWIDTH_DOWN`
|
||||
- `HY2_DISABLE_LOSS_COMPENSATION`
|
||||
- `HY2_IGNORE_CLIENT_BANDWIDTH`
|
||||
- `HY2_CONGESTION_TYPE`
|
||||
- `HY2_BBR_PROFILE`
|
||||
- `HY2_DISABLE_STATELESS_RESET`
|
||||
- `HY2_CONFIG_PATH`
|
||||
|
||||
### HY2XS admin
|
||||
|
||||
@@ -4,6 +4,24 @@
|
||||
|
||||
Зафиксировать checklist для новой двухслойной схемы.
|
||||
|
||||
## Как запускать тесты
|
||||
|
||||
```bash
|
||||
# Юнит-тесты и типы оркестратора
|
||||
cd orchestrator && bun install --frozen-lockfile && bun run check && bun test
|
||||
|
||||
# Тесты и статический анализ HY2XS admin
|
||||
cd apps && go vet ./... && go test ./...
|
||||
|
||||
# Полный E2E с реальным клиентом Hysteria (Debian 13 amd64)
|
||||
HYSTERIA_BIN=/usr/local/bin/hysteria ./tools/test/e2e-hysteria.sh
|
||||
|
||||
# Production-сборка: прогоняет тесты, резолвер и compatibility gate
|
||||
./tools/build/build.sh
|
||||
```
|
||||
|
||||
`build.sh` останавливается, если падают тесты оркестратора, тесты админки или compatibility gate.
|
||||
|
||||
## A. Builder layer tests
|
||||
|
||||
### Проверяем
|
||||
@@ -16,6 +34,81 @@
|
||||
7. builder сам доставляет отсутствующие build-зависимости
|
||||
8. builder проверяет версии Go/Bun/Node.js/pnpm
|
||||
9. builder пишет версии toolchain в metadata
|
||||
10. builder прогоняет `bun test` и `go test` до упаковки
|
||||
|
||||
## A1. Latest-stable resolver
|
||||
|
||||
Фикстуры и ожидаемое поведение (`orchestrator/test/hysteria-release.test.ts`):
|
||||
|
||||
| Сценарий | Ожидание |
|
||||
| --- | --- |
|
||||
| stable `app/v2.12.2` | выбирается |
|
||||
| prerelease `app/v2.13.0` | игнорируется |
|
||||
| draft `app/v2.14.0` | игнорируется |
|
||||
| чужое семейство тегов (`core/`, `docs/`) | игнорируется |
|
||||
| тег без префикса `app/` | игнорируется |
|
||||
| `app/v2.9.10` против `app/v2.9.2` | выбирается `2.9.10` (числовое сравнение, не строковое) |
|
||||
| отсутствует `hysteria-linux-amd64` | ошибка |
|
||||
| дублирующийся `hysteria-linux-amd64` | ошибка, а не случайный выбор |
|
||||
| non-https URL артефакта | ошибка |
|
||||
| невалидный semver в теге | игнорируется |
|
||||
| пустой список релизов | понятная ошибка |
|
||||
| несовпадение SHA-256 | сборка падает |
|
||||
| сетевая ошибка / rate limit | понятная ошибка с подсказкой про `GITHUB_TOKEN` и `HYSTERIA_CHANNEL=pinned` |
|
||||
|
||||
Отдельно проверяется, что **`latest stable` — это именно stable, а не максимальная строка или самый свежий тег**.
|
||||
|
||||
## A2. Release rollover
|
||||
|
||||
Ключевой acceptance-критерий модели «latest на сборке»:
|
||||
|
||||
```text
|
||||
Сегодня: latest = 2.12.2 → пакет A закрепляет 2.12.2
|
||||
Завтра: latest = 2.12.3 → пакет B закрепляет 2.12.3
|
||||
|
||||
Повторная установка пакета A всё равно ставит 2.12.2
|
||||
```
|
||||
|
||||
Проверяется на двух уровнях:
|
||||
- резолвер даёт разный результат на разных снимках upstream (`orchestrator/test/release-rollover.test.ts`);
|
||||
- install-time код не импортирует резолвер, не обращается к `api.github.com` и не использует moving `latest` — это утверждение проверяется тестом и acceptance-шагом сборки.
|
||||
|
||||
## A3. Compatibility gate
|
||||
|
||||
1. скачанный артефакт проходит проверку SHA-256;
|
||||
2. `hysteria version` совпадает с разрешённой версией;
|
||||
3. реальный бинарник принимает канонический конфиг HY2XS для Gecko;
|
||||
4. то же для Salamander;
|
||||
5. при несовместимости падает **сборка** с сообщением `BUILD FAILED: unsupported Hysteria stable vX.Y.Z`, а не установка у пользователя.
|
||||
|
||||
## A4. Конфигурационный контракт (unit)
|
||||
|
||||
Таблица `orchestrator/test/env.test.ts`:
|
||||
|
||||
| Вход | Ожидание |
|
||||
| --- | --- |
|
||||
| значение не задано | `gecko` |
|
||||
| `gecko` | принято |
|
||||
| `salamander` | принято |
|
||||
| неизвестный тип | отклонено |
|
||||
| `Gecko` (регистр) | отклонено |
|
||||
| gecko `max < min` | отклонено |
|
||||
| gecko `max > 2048` | отклонено |
|
||||
| gecko `max == 2048` | принято |
|
||||
| неположительный/нецелый `min` | отклонено |
|
||||
| пустой obfs-пароль | автогенерация, а не пустое значение в конфиге |
|
||||
| `HY2XS_CONFIG_SCHEMA_VERSION=1` | отклонено с указанием на чистую установку |
|
||||
|
||||
Отдельно — round-trip `parse(render(config)) == config`. Этот тест ловит класс ошибок «в рендер runtime-конфига попал литерал вместо значения из конфигурации».
|
||||
|
||||
Рендер конфига (`orchestrator/test/render-config.test.ts`):
|
||||
|
||||
- Gecko рендерит **только** gecko-подблок;
|
||||
- Salamander рендерит **только** salamander-подблок;
|
||||
- в конфиге никогда нет двух подтипов obfs одновременно;
|
||||
- шаблон не содержит захардкоженного типа обфускации;
|
||||
- пароль с пробелами и спецсимволами экранируется;
|
||||
- YAML-инъекция через пароль отклоняется даже в обход env-валидации.
|
||||
|
||||
## B. Target install tests
|
||||
|
||||
@@ -56,6 +149,89 @@
|
||||
21. `/etc/hysteria/config.yaml` имеет `0640 hysteria:hy2xs-admin`
|
||||
22. `hy2xs-admin` может читать `/etc/hysteria/config.yaml`, но не может писать
|
||||
|
||||
## C1. Семантический smoke конфига
|
||||
|
||||
Недостаточно `grep` по YAML: он не отличит нужное поле от такой же строки в другой секции и не заметит оставшийся рядом лишний подблок.
|
||||
|
||||
Smoke разбирает `/etc/hysteria/config.yaml` и сверяет с production-профилем:
|
||||
|
||||
```text
|
||||
effective Hysteria version == версия из metadata пакета
|
||||
|
||||
obfs:
|
||||
type == HY2XS_HYSTERIA_OBFS_TYPE
|
||||
ровно один подблок, соответствующий type
|
||||
password непустой
|
||||
для gecko: minPacketSize == 512, maxPacketSize == 1200
|
||||
|
||||
bandwidth:
|
||||
up/down == runtime env
|
||||
disableLossCompensation == false
|
||||
|
||||
congestion:
|
||||
type == bbr
|
||||
bbrProfile == standard
|
||||
|
||||
quic:
|
||||
disableStatelessReset == false
|
||||
окна и таймауты == baseline
|
||||
|
||||
trafficStats:
|
||||
listen == runtime env
|
||||
secret непустой
|
||||
|
||||
auth:
|
||||
type == http
|
||||
url содержит machine access_token
|
||||
|
||||
TLS:
|
||||
acme-режим не содержит секции tls
|
||||
file-режим не содержит секции acme
|
||||
```
|
||||
|
||||
## C2. End-to-end с реальным клиентом
|
||||
|
||||
`tools/test/e2e-hysteria.sh`, отдельно для Gecko и Salamander:
|
||||
|
||||
1. сервер принимает сгенерированный конфиг и стартует;
|
||||
2. TLS handshake;
|
||||
3. handshake с обфускацией;
|
||||
4. HTTP auth HY2XS: разрешённый пир принят;
|
||||
5. HTTP auth HY2XS: неразрешённый пир отклонён;
|
||||
6. клиент подключается **именно по сгенерированной `hysteria2://` ссылке**;
|
||||
7. TCP forwarding;
|
||||
8. UDP forwarding;
|
||||
9. `trafficStats` с валидным secret;
|
||||
10. `trafficStats` с невалидным secret отклоняется;
|
||||
11. per-peer accounting содержит аутентифицированного пира;
|
||||
12. перезапуск сервера;
|
||||
13. быстрое переподключение клиента (поведение stateless reset).
|
||||
|
||||
Пункт 6 — тот самый, который ловит класс ошибок, неизбежный при наивном включении Gecko: сервер работает, ссылка формально валидна, а клиент по ней не подключается.
|
||||
|
||||
## C3. Share URI (unit)
|
||||
|
||||
`apps/service/hysteria2_api_test.go`:
|
||||
|
||||
- Gecko URI содержит `obfs=gecko` и `obfs-password`;
|
||||
- Salamander URI содержит `obfs=salamander` и `obfs-password`;
|
||||
- конфиг без обфускации даёт ссылку без `obfs`;
|
||||
- неизвестный тип обфускации в ссылку не попадает;
|
||||
- обфускация без пароля в ссылку не попадает;
|
||||
- SNI: ACME-домен → `HY2XS_DOMAIN` → `HY2XS_PUBLIC_HOST`, IP не используется;
|
||||
- спецсимволы в credentials и obfs-пароле переживают round-trip: `+`, пробел, `#`, `@`, `/`, `?`, `&`, `=`, `%`, кириллица;
|
||||
- литеральный `+` кодируется как `%2B` и не схлопывается с пробелом (регрессия на upstream-баг 2.9.3).
|
||||
|
||||
## C4. Экспорт конфига (unit)
|
||||
|
||||
`apps/service/hysteria2_export_test.go`:
|
||||
|
||||
- неизвестные upstream-секции переживают экспорт целиком, включая вложенные карты и списки;
|
||||
- операционные поля остаются читаемыми;
|
||||
- вырезаются: obfs-пароль, `trafficStats.secret`, `access_token`, `auth.userpass`, учётные данные ACME DNS, пароли outbound;
|
||||
- вырезается **неизвестное** поле с секретным именем;
|
||||
- пути к файлам (`tls.key`, `ech.keyPath`, `clientCA`) остаются видимыми.
|
||||
|
||||
## D. Negative tests
|
||||
|
||||
1. не Debian 13
|
||||
@@ -68,6 +244,9 @@
|
||||
8. install flow прерван посередине
|
||||
9. попытка использовать `HY2XS_IPV6_ENABLED=true`
|
||||
10. `HY2XS_PUBLIC_HOST=0.0.0.0`
|
||||
11. неизвестный `HY2XS_HYSTERIA_OBFS_TYPE`
|
||||
12. конфигурация со схемой `HY2XS_CONFIG_SCHEMA_VERSION` из линейки `0.x`
|
||||
13. upstream `latest` несовместим с шаблоном HY2XS — падает сборка, не установка
|
||||
|
||||
## E. Fix20 production matrix (обязательные сценарии)
|
||||
|
||||
@@ -119,3 +298,22 @@
|
||||
13. production build verify падает, если `config/hy2xs.env` содержит placeholder-значения
|
||||
14. production build verify падает при dirty git tree (кроме `ALLOW_DIRTY_BUILD=true`)
|
||||
15. metadata содержит `source_git_commit`, `dirty_tree`, `build_profile=production`
|
||||
16. builder без override на сегодняшний день автоматически выбирает последнюю стабильную версию Hysteria
|
||||
17. собранный пакет содержит **точные** версию, URL и SHA-256
|
||||
18. выход новой версии Hysteria после сборки не меняет содержимое старого пакета
|
||||
19. новая установка генерирует Gecko
|
||||
20. Gecko использует `512/1200`
|
||||
21. установленная Hysteria реально принимает сгенерированный YAML
|
||||
22. сервис запускается под существующим непривилегированным пользователем `hysteria`
|
||||
23. созданный пользователь получает `hysteria2://` с `obfs=gecko` и `obfs-password`
|
||||
24. совместимый клиент Hysteria подключается напрямую по этой ссылке
|
||||
25. после перезапуска Hysteria клиент быстро восстанавливает соединение
|
||||
26. режим `HY2XS_HYSTERIA_OBFS_TYPE=salamander` полностью работоспособен
|
||||
27. admin читает Gecko-конфиг без ошибок
|
||||
28. экспорт не уничтожает современные и неизвестные upstream-поля
|
||||
29. экспорт не содержит секретов
|
||||
30. frontend отображает Gecko
|
||||
31. `namedotcom` удалён, актуальные ACME-провайдеры отражены
|
||||
32. документация нигде не утверждает, что Salamander — фиксированный инвариант
|
||||
33. документация не фиксирует конкретный номер версии как «текущую версию», а объясняет latest-stable build policy
|
||||
34. форма создания пира содержит примеры значений и пояснения для полей «Пир», «Комментарий» и «Секрет»
|
||||
|
||||
+7
-2
@@ -24,10 +24,12 @@
|
||||
Здесь нет сборщика. Здесь запускается только итоговый install package / orchestrator, который:
|
||||
- ставит системные зависимости
|
||||
- разворачивает **встроенный HY2XS admin**
|
||||
- забирает **свежую Hysteria2 из официального upstream**
|
||||
- забирает **закреплённую в пакете Hysteria2 из официального upstream** и сверяет её по SHA-256 и версии
|
||||
- создаёт конфиги, systemd unit-файлы и `post-install.env`
|
||||
- выполняет базовую настройку сервера
|
||||
|
||||
Версия Hysteria2 выбирается **на builder layer**: последняя стабильная разрешается при сборке и замораживается в metadata пакета. Target layer никогда не обращается к moving `latest`.
|
||||
|
||||
## Базовые правила
|
||||
|
||||
1. Hysteria2 не вендорится и не собирается как часть HY2XS.
|
||||
@@ -52,6 +54,9 @@
|
||||
10. [10-access-layer-out-of-scope.md](10-access-layer-out-of-scope.md)
|
||||
11. [11-testing-and-acceptance.md](11-testing-and-acceptance.md)
|
||||
12. [12-operations-and-troubleshooting.md](12-operations-and-troubleshooting.md)
|
||||
13. [13-production-runbook.md](13-production-runbook.md)
|
||||
|
||||
История изменений проекта — в [CHANGELOG.md](../CHANGELOG.md).
|
||||
|
||||
## Жёсткие рамки baseline
|
||||
|
||||
@@ -72,4 +77,4 @@
|
||||
|
||||
Правильная baseline-модель теперь такая:
|
||||
|
||||
**Локальный builder собирает install package с HY2XS admin и Bun/TypeScript оркестратором; серверный install-only orchestrator ставит этот пакет на чистый Debian 13, тянет свежую Hysteria2 из upstream, разворачивает HY2XS admin, создаёт systemd + nftables + post-install env и подготавливает рабочее серверное окружение.**
|
||||
**Локальный builder разрешает последнюю стабильную Hysteria2, проверяет её на совместимость с конфигом HY2XS и собирает install package с HY2XS admin и Bun/TypeScript оркестратором; серверный install-only orchestrator ставит этот пакет на чистый Debian 13, скачивает ровно закреплённую Hysteria2, разворачивает HY2XS admin, создаёт systemd + nftables + post-install env и подготавливает рабочее серверное окружение.**
|
||||
|
||||
Reference in New Issue
Block a user