Files
HY2XS_flamy/docs/architecture/06-speed-limits-and-congestion.md
founder c0a43ae915 fix(admin): закрыть обещания панели, которые продукт не выполнял
Девятый проход, по итогам приёмки v1.0.0-rc1 на живом Debian 13. Общая тема:
интерфейс обещал оператору то, что продукт умел, но до чего не доходило
управление.

Секрет пира. Подпись под полем предлагала оставить его пустым, сервер умел его
сгенерировать, и генерация была недостижима: в go-playground/validator тег
omitempty НЕ пропускает правило, если поле объявлено указателем и указатель не
nil — hasValue считает указатель на пустую строку «значением». Правило min=6
применялось к пустой строке и отказывало. Ловушка закрыта общим шагом
нормализации DTO, а не тегом на одном поле: та же ловушка ломала фильтр списка
пиров, где очищенный крестиком el-input отправляет `?name=`. Граница проходит по
каждому полю отдельно — у remark пустая строка означает «убрать пометку», у
disabled ноль означает «включён».

Отказы. Любая ошибка любого поля превращалась в слово `invalid`, а слой vo
определял код ответа СРАВНЕНИЕМ текста сообщения — тот же антипаттерн, который
запрещён панели, только на сервере. Ответ несёт errors[{code, field, message,
params}]; панель выбирает фразу по коду и подставляет причины под поля.

Сессия. Ветка «войдите заново» была недостижима дважды: сервер отвечает HTTP 200
на любой отказ, поэтому обработчик ошибок axios не вызывался, а условие в нём
проверяло code === "A0230" и поле msg, которых в этом API никогда не было.
Истёкший токен вдобавок уезжал с кодом системной ошибки.

Иконки. Контракт currentColor был объявлен в двух местах и не действовал: восемь
ассетов несли литеральный fill="#000000" на <path>, а атрибут представления
перебивает унаследованное CSS-свойство. Под это попадали все семь иконок
бокового меню на фоне #181818.

Имя пира. Два правила на одном поле противоречили друг другу (min=1 против
6-32), а копия набора символов в слое контроллеров несла неэкранированный дефис
и впускала `, - . / : ; <` — через панель проходило имя peer/name, которое
импорт того же пира отклонял. Набор символов ЛОГИНА сознательно не сужен и
закреплён тестом: он приходит из HY2XS_ADMIN_USER и оркестратором не
ограничивается.

Добавлены подпись «Разработано во Flamy» с адресом, принадлежащим приложению, и
контрактные тесты панели как обязательный шаг сборки. Их исполняет Bun, а не
vitest: jsdom не вычисляет currentColor и визуальной корректности не доказал бы,
зато vitest привёл бы в граф pnpm audit сотню транзитивных зависимостей.

docs/ разложена по слоям, 11-testing-and-acceptance.md (117 КБ) разбит на пять
частей, добавлен docs/acceptance/ с отчётом о прогоне rc1 и перечнем дефектов.
Обход документации в приёмке стал рекурсивным: плоский docs/*.md после
разнесения по каталогам совпадал бы ровно с одним файлом.
2026-09-01 07:27:15 +05:00

5.1 KiB
Raw Permalink Blame History

Speed limits and congestion policy

Цель документа

Зафиксировать корректную speed policy без неточных упрощений.

Что нельзя считать правильной схемой

Нельзя описывать baseline так:

  • на сервере включили host BBR
  • выдали какой-то URI
  • автоматически получили строгий лимит 50 Mbps на клиента

Это неверная модель.

Три разные вещи, которые нельзя смешивать

Это главный источник путаницы в теме скоростей 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:

  • up_mbps = 50
  • down_mbps = 50

Практический смысл

Ожидаемый 50/50 Mbps contract считается корректным только тогда, когда сервер и клиентская конфигурация согласованы.

Логика выбора внутри Hysteria:

  • когда стороны согласовали Brutal bandwidth — используется Brutal;
  • когда это не применяется — используется выбранный fallback congestion controller.

Поэтому BBR тоже является частью явного baseline HY2XS, а не «настройкой по умолчанию, о которой можно не думать».

Loss compensation

bandwidth:
  disableLossCompensation: false

Компенсация потерь (появилась в Hysteria 2.10.0) позволяет отправлять быстрее заданной полосы, чтобы компенсировать потерю пакетов. В baseline HY2XS она включена, а значение фиксируется в конфиге явно — проект про воспроизводимое поведение, а не про молчаливое следование upstream-дефолтам.

Что делать с host-level BBR

net.ipv4.tcp_congestion_control=bbr можно оставить как общий системный тюнинг, но:

  • это не главный механизм speed policy Hysteria2;
  • это не замена клиентским bandwidth hints;
  • это не то же самое, что congestion.type: bbr в конфиге Hysteria — у Hysteria собственный congestion-control контур поверх QUIC;
  • это не центр документации по лимитам.

Что фиксировать в post-install.env

Минимум:

  • HY2_BANDWIDTH_UP
  • HY2_BANDWIDTH_DOWN
  • HY2_IGNORE_CLIENT_BANDWIDTH
  • HY2_DISABLE_LOSS_COMPENSATION
  • HY2_CONGESTION_TYPE
  • HY2_BBR_PROFILE

Дополнительно фиксируется HY2_VERSION как фактически установленная версия Hysteria2 и HY2_RESOLUTION как способ её выбора при сборке пакета.

Что нельзя писать в проектных доках

Не писать:

  • «лимит задаётся только на сервере, клиент не важен»
  • «любой URI достаточно для полной speed policy»
  • «host BBR и есть логика Hysteria»
  • «50 mbps — оптимальная скорость Hysteria» (это политика HY2XS, а не свойство протокола)
  • «Brutal и congestion controller — одно и то же»

Правильная baseline-формулировка

Пер-клиентный лимит 50/50 Mbps обеспечивается согласованной серверной и клиентской конфигурацией. Install baseline отвечает за серверную часть этого контракта; конкретный delivery/access слой в этот документ не входит.