Repository navigation
Merged
Conversation
Фундамент этапа 1: Hysteria 2 и AmneziaWG 2.0 не должны притворяться Xray-путями. Состояние бэкендов — /usr/local/etc/xrayebator/backends.json (BACKENDS_ROOT переопределяем для тестов, паттерн P2-fix); клиентские креденшелы бэкендов будут жить в профиле (.backends) как единый источник для revoke/expire, серверные конфиги регенерируются из профилей. - хелперы реестра: ensure/installed/field/set (мутации через safe_jq_write, jq-опции строго до фильтра), port_busy_udp, unit_state - _backend_status_json + диспетчер CLI backend-status (только JSON на stdout) - _backend_apply_profile_lifecycle — единая точка жизненного цикла (created|deleted|revoked|expired|restored); вызовы подключаются в срезах 3/5 вместе с реальными обработчиками, сейчас безопасный no-op - validation/test-backend-registry.sh: source-режим + временный BACKENDS_ROOT/LOCK_FILE; тест поймал реальный баг set -u unbound variable в local out/prev одной строкой (bash 5.2) — исправлено разнесением - help и CLAUDE.md синхронизированы Меню намеренно не тронуто: пункты 11/12/13 добавляются вместе со своими бэкендами из-за строгой нумерации в test-main-menu-numbering.sh.
… меню 11)
Первый не-Xray backend. Поставка зеркалит update_xray_core: GitHub API HyNetworks/hysteria (тег app/vX.Y.Z, sha256 из поля digest API-ответа) -> /usr/local/bin/hysteria + self-test. Свой юнит hysteria-server.service (не get.hy2.sh): выделенный пользователь hysteria, CAP_NET_BIND_SERVICE, Restart=on-failure — та же модель доверия, что у xray.service; плюс sysctl-буферы QUIC.
TLS адаптивный: LE-серт подписки копируется в backends/hysteria с certbot deploy-hook на продление (клиенты insecure=0), иначе self-signed (insecure=1). masquerade 404 в обеих модах — proxy-маскарад только после живой проверки в срезе 3. Гранты per-profile (userpass) — срез 3, в yaml пока users: {}.
- CLI hysteria2-install [--port P] / hysteria2-uninstall / hysteria2-status: только JSON на stdout (человеческий вывод в stderr), rc=2 от воркера = already/not installed -> {already:true}
- install атомарен: скачивание во tmp + sha256 + self-test, откат артефактов при неудаче старта; реестр пишется последним шагом — записи только у живого бэкенда
- _hysteria2_safe_restart: бэкап server.yaml -> restart -> откат при неудаче (у hysteria нет -test)
- тест поймал потерю полей version/tls_mode в backend-status: статус теперь прокидывает всю запись реестра, state ставится живой проверкой
- меню 11 + осознанное расширение test-main-menu-numbering до 1..11
…лный жизненный цикл
Профиль — единственный источник креденшелов: .backends.hysteria2 {username, password} в profile JSON; server.yaml всегда РЕГЕНЕРИРУЕТСЯ из профилей (_hysteria2_regen_config: компактная users-карта, фильтр expire_disabled), применяется через _hysteria2_apply_config с откатом при неудачном рестарте. Благодаря фильтру события expired/restored — это просто реген: продление срока автоматически возвращает грант, снапшоты не нужны.
Lifecycle-хук подключён во все точки после успешной Xray-части: create_profile и _profile_cli_create_one (created), profile_delete (deleted), profile_revoke (revoked = ротация пароля), _expire_disable/_expire_enable (expired/restored). Массовая выдача: интерактивный вопрос при install, CLI --grant-all; один реген на всех.
Подписка: hysteria2-ссылка (_hysteria2_profile_link_pure, insecure из tls_mode) добавляется в ОБА тела — generic v2rayNG и HAPP (решение пользователя); флаг sub_body в реестре — выключатель без рестарта сервиса (реестр читается на каждый запрос). Миграция subhttp_hysteria2_2026 регенерирует handler на живых серверах, зарегистрирована в main_menu И quickstart (паритет).
Инфраструктурные находки: (1) subhttp работает от пользователя xray — реестр переведён на 644 root (без секретов, читается сервисом), секреты в 750-каталогах; (2) PROFILES_DIR переопределяем для тестов (P2-fix); (3) users-карта в yaml — компактный JSON (jq -sc). hysteria2-grant CLI + пункт меню 'Ссылка hysteria2' с QR. Живой тест HAPP-тела с hysteria2-строкой — срез 7.
…ninstall, меню 12/13) Спайк по README amneziawg закрыл главное допущение: официальный путь — PPA amnezia/ppa + apt install amneziawg (DKMS-модуль + tools), для Debian — ручная строка репо; фолбэк — сборка из исходников, где для ядра >=5.6 нужен ПОЛНЫЙ linux-source (headers недостаточно) с линком kernel-symlink в дерево модуля. Junk-параметры подтверждены по спеке: Jc 1..128, Jmin<Jmax<=1280, S1/S2 15..150 (S1+56!=S2), H1-H4 уникальные 5..2147483647 — генерируются случайно при install и попадают в server-params.json и в каждый клиентский конфиг. - _awg_install: PPA -> ручная сборка -> приёмка modprobe+lsmod+awg show awg0; реестр последним шагом; неудача = чистый откат артефактов (без полу-состояний) - интерфейс awg0 через awg-quick@awg0, конфиг 0600, Address 10.8.1.1/24, MTU 1280, PostUp/PostDown MASQUERADE на iface default-route (детект при install, литералом в конфиг) - ip_forward через /etc/sysctl.d/99-xrayebator-awg.conf; порт — случайный высокий 20000-59999 с проверкой занятости (ss) и конфликта с hysteria-портом - uninstall гасит интерфейс и убирает артефакты, пакеты/модуль остаются в системе (честно пишется в выводе) - меню 12 (AmneziaWG) и 13 (Статус бэкендов) + CLI awg-install/uninstall/status (JSON-гигиена, rc=2 -> already) - test-awg-lifecycle.sh (31-й): junk-параметры в спеке 5/5 прогонов, рендер конфига, keygen через fake awg, реестр/статус, статика диспетчера; полный install с сетью+DKMS — живой спайк среза 7 - нумерация меню расширена осознанно до 1..13
…лный жизненный цикл
Профиль — источник правды: .backends.awg {client_private_key, client_public_key, preshared_key, address} ; awg0.conf [Peer]-секции регенерируются из профилей (фильтр expire_disabled) и применяются рестартом интерфейса с откатом. Осознанный компромисс: рестарт коротко рвёт туннель всех peer-ов — гранты/отзывы редки, для этапа 1 приемлемо; живые awg set-мутации без рестарта — кандидат этапа 2.
- аллокация адресов: первый свободный .2..254 с учётом уже выданных в профилях (.2 -> .3 проверено тестом)
- события через общий lifecycle-хук диспетчеризуются автоматически: created (грант), revoked (ротация ключей с сохранением адреса — старые клиентские конфиги перестают подключаться), deleted/expired/restored (реген; restored возвращает peer после продления)
- клиентский .conf (_awg_client_conf_pure): клиентская тройка, серверный pubkey/endpoint, junk-параметры копируются с сервера (спека: должны совпадать кроме Jc/Jmin/Jmax), AllowedIPs full-tunnel, Keepalive 25
- CLI awg-grant/awg-conf (JSON, conf в поле для GUI) + пункт меню 'Конфиг клиента' с QR; --grant-all при install
- тест расширен до 12 проверок: адреса, peer-секции, форма клиентского конфига, все события; caught: no-op uninstall должен учитывать существование conf-файла
… (GUI-контракт)
GUI (этап 2) будет читать бэкенды из тех же JSON, что уже потребляет: profiles_command добавляет объект .backends профиля (гранты hysteria2/awg — креденшелы уровня оператора, как уже выданный sub_token), inspect_command — секрет-фри сводку реестра {installed, version, port} для импорта существующего сервера. STRICT read-only сохранён: inspect читает реестр только если файл существует и не создаёт его (гвардится test-quickstart-email-and-inspect.sh — прогнан, зелёный). Старые клиенты/GUI поля просто игнорируют — аддитивность.
…сервере
Срез 7, первая половина: сервер Ubuntu 24.04 (ядро 5.15, 16 ядер) — install.sh (канонический main) + перезапись менеджера dev-версией + quickstart --without-email (подписка 8443, IP-серт certbot 5.8, 7-маршрутный профиль). Затем hysteria2-install --grant-all.
Поймано и починено:
1. CHDIR Permission denied юнита: каталог backends/hysteria получал 750, но оставался root:root — добавлен chown root:hysteria на каталог (права группы без смены владельца не работают).
2. 'empty auth userpass': hysteria отвергает ПУСТУЮ userpass-карту — введён placeholder-пользователь _xrayebator_placeholder (случайный пароль в 0600-файле, в карте только пока нет грантов).
3. ГЛАВНОЕ: поле auth-карты называется userpass, а не users — с 'users:' hysteria игнорировал карту и падал с 'empty auth userpass' независимо от содержимого. Проверено живым запуском: server up and running.
4. В HAPP-теле hysteria2-строка слипалась с последним vless:// (command substitution съедает trailing newline) — printf '%s\n' в HAPP-ветке; handler перегенерирован, тела проверены curl'ом.
Результат на сервере: hysteria v2.12.3 активна на UDP/443, TLS=le (IP-серт), users-карта {happ: <грант>}, hysteria2:// строка в обоих телах подписки, флаг sub_body=true.
…сервере AWG установлен и проверен live на Ubuntu 24.04 / ядре 5.15: PPA недоступен для этой серии -> ручная сборка сработала (модуль в lsmod, tools v3.1), интерфейс awg0 поднялся, peer профиля виден в awg show, ip_forward+MASQUERADE работают. Поймано и починено: 1. Юнит awg-quick@awg0 ищет конфиг в /etc/amnezia/amneziawg/ — наш файл линкуется туда symlink'ом (источник правды остаётся в backends/awg/), uninstall снимает линк. 2. Mismatch имён jq-переменных в _backend_set: port/mtu/junk в awg-цепочке (expr \/\/\ против аргументов port/mtu/junk) —Registry-запись молча обрывалась на втором поле. Все 13 мест выверены grep'ом. 3. Откат при неудаче записи реестра оставлял висящий symlink и полу-запись .awg — теперь чистит и то, и другое (проверено: backend-status после отката чист). 4. Диспетчер awg-install не пробрасывал \$@ — CLI-флаги (например --grant-all) молча терялись. 5. _awg_install читал режим грантов из \, хотя у AWG нет аргумента порта — режим всегда падал в prompt. Теперь \ (у hysteria — \, там порт первый). Полный жизненный цикл подтверждён live: revoke ротирует пароль hysteria и ключи AWG peer-а синхронно в обоих конфигах; expire снимает грант и peer (placeholder в userpass-карте держит конфиг валидным); restore возвращает оба; подписка с ротированным токеном отдаёт 200 и hysteria2-строку в обоих телах.
Синхронизация всех доков с мультибэкенд-обновлениями и добавление раздела AWG 3.1 по статье Хабра (meshroom, 'AmneziaWG: от 2.0 к 3.1') с учётом специфики пассивного анализа ТСПУ/DPI. - architecture (en/ru/zh): дерево состояния с /usr/local/etc/xrayebator и новыми юнитами; секция Multi-protocol backends (реестр, lifecycle через профиль как источник правды, hysteria2/awg-модели); hysteria2-строка в обоих телах подписки - configuration (en/ru/zh): меню 1..13 (восстановлен потерянный пункт 7 bypass), все новые CLI-команды, секция Multi-protocol backends с таблицей 'должны совпадать побайтово vs локальные' и правилами миграции на 3.x; отремонтирован сломанный заголовок секции Bypass (висел текст без заголовка во всех трёх языках) - security (en/ru/zh): модель безопасности бэкендов — registry 644 без секретов (читается subhttp от xray), hysteria root:hysteria 750/0640, awg 700/0600, deploy-hook, placeholder-credential, ip_forward/MASQUERADE - testing (en/ru/zh): 28 -> 31 скрипт + три новых сюиты с описанием, граница 'CI с моками vs live-спайк' - troubleshooting (en/ru/zh): четыре новых раздела — Hysteria не стартует (userpass/CHDIR/placeholder), AWG без latest handshake (чек-лист must-match из статьи, версии клиентов AmneziaVPN >= 5.0.1.5), установка AWG (PPA/linux-source/symlink), отсутствие hysteria2-строки в подписке (sub_body) - README (en/ru/zh): чипы hysteria2/amneziawg + абзац о мультипротокольном этапе - CLAUDE.md синхронизирован ранее (31 тест, все команды)
…блер awg-31
Вопрос 'реализован ли 3.1' закрыт: раньше мы ставили бинари поколения 3.1, но генератор эмитил только набор 2.0. По исходникам kernel-модуля (master: 'feat: awg 3.1 features', 'prevent HeaderProtectionKey from setting if any Sx less then 12') модуль поддерживает 3.1 — недостающим был только наш генератор.
- _awg_gen_junk: S1-S4 все >=12 (12..150, уникальные; спека 3.x), HeaderProtectionKey (openssl rand -base64 32, общий секрет сервер/клиенты)
- рендеры server/client: S3/S4 всегда, HPK + RandomTrailers=on при three_enabled, DisableCookies=on опционально (по умолчанию off — сохраняем anti-amplification)
- awg-31 --on|--off [--disable-cookies] + пункт меню 12: тумблер для живых установок; _awg_ensure_31_params догенеряет S3/S4/HPK в старый server-params; смена = реген интерфейса с откатом + явное предупреждение о перевыпуске всех клиентских .conf (AmneziaVPN >= 5.0.1.5)
- live (2.26.125.147): awg-31 --on -> awg0.conf с S3/S4/HPK/RandomTrailers принят модулем (awg show), клиентский .conf содержит те же 3.1-ключи
Поймано и починено:
1. _backend_field с jq '// empty' проглатывал легитимный boolean false (three_enabled=false читался как пусто и дефолтил в true) — заменено на tostring с guard; тест-регресс добавлен
2. условные printf рендера писали в stdout вместо файла конфига
3. парсер awg (kernel UAPI) принимает булевы только on/off — 'RandomTrailers = true' отвергается ('Boolean value is neither on/off nor 0/1'); в доках и CLAUDE.md зафиксировано
4. формат булевых в docs (en/ru/zh) обновлён: реализация 3.1 вместо 'не эмитит 3.x-ключи'
GUI ничего не придумывает про сервер — только вызывает JSON-команды. Новый BackendManager повторяет паттерн ProfileManager (run + extractJson + describeFailure) и даёт IPC-каналы: backends:status (реестр), backends:hysteria2Grant/awgGrant (гранты per-профиль), backends:awgConf (клиентский .conf для AmneziaVPN — секреты уровня оператора), backends:awg31 (тумблер 3.1 с предупреждением о перевыпуске). Модель этапа 2 (согласована): бэкенды живут одновременно, доступ транспорта определяется грантами конкретного профиля, глобальных режимов запуска нет. - shared/types: ProfileBackends в ServerProfile (аддитивно), BackendEntry/BackendStatusResult/AwgConfResult/BackendToggleResult, секция backends в ElectronAPI - чистые парсеры parseBackendStatus/parseAwgConf вынесены отдельно и покрыты тестами (7): полный статус, пустой реестр, ANSI-загрязнение stdout, ошибка реестра, 3.1-ключи в .conf, отсутствие гранта - typecheck 0; vitest: 112 зелёных, единственный красный — задокументированный Windows-only shell-command (/bin/sh), CI на Ubuntu авторитетен - UI-экраны (срезы B/C) — следующие коммиты; renderer пока не менялся
…ings Карточки Hysteria 2 и AmneziaWG 3.1: статус (active/port/версия/режим 3.1), установка и удаление с диалогами подтверждения, тумблер 3.1 (явное предупреждение о перевыпуске .conf), kill-switch hysteria2-строк в подписке. У карточки AWG — пометка пользователя: «работает через клиент AmneziaVPN, а НЕ через V2Ray-клиентов (HAPP)». Серверная часть: новый CLI hysteria2-subbody --on|--off (kill-switch без рестартов — handler перечитывает реестр на каждый запрос); main-адаптеры hysteria2Install/Uninstall, awgInstall/Uninstall, hysteria2Subbody; IPC backends:hysteria2*/awg*; preload-секция расширена. Отказоустойчивость UI: backend-status недоступен/неизвестен на старом сервере -> секция показывает «обновите менеджер», не ломая страницу (load оборачивает загрузку бэкендов в отдельный try). i18n ru/en/zh — 22 новых ключа.
…HAPP) hysteria2-строка в подписке подписывалась голым именем профиля (#happ) и выглядела в HAPP иначе, чем VLESS-маршруты (🇩🇪 Germany · happ-tcp-xudp). _hysteria2_profile_link_pure теперь повторяет именование _generate_vless_url_pure: префикс 🇩🇪 Germany · из geo-маркера (флаг + страна; нет маркера — голое имя, как у VLESS) и URL-кодирование фрагмента через jq @uri. Живой тест: подписка отдаёт #🇩🇪 Germany · happ.
…анты)
Кнопка «Ключи» у каждого профиля в Server Settings открывает диалог: Hysteria 2 — hysteria2-ссылка (Chip HYSTERIA2·UDP, копирование, QR; пометка: работает в HAPP/Hiddify/v2rayN) и AmneziaWG — полный клиентский .conf (Chip AMNEZIAWG 3.1·UDP, pre-блок, копирование, QR; пометка пользователя: «AmneziaVPN — НЕ V2Ray/HAPP, импорт в AmneziaWG/AmneziaVPN, full-tunnel, >= 5.0.1.5»).
Нет гранта — в диалоге кнопка «Выдать грант» (hysteria2-grant/awg-grant) с автоматическим обновлением ключей. Это закрывает цикл выдачи AWG-ключей из GUI без CLI — тестовый путь пользователя для проверки AWG из РФ.
- новый CLI hysteria2-link --name N (JSON {ok,name,link}) — серверный билдер ссылки с флагом страны
- QR рендерится локально (qrcode toDataURL, Esc/клик закрывают) — серверный qrencode не нужен
- путь доставки AWG: GUI-диалог вместо ручного SSH — как договорились (текст+QR, без скачивания файла)
… ключей через GUI Клиентский .conf AWG начинается с шапки-пометки (требование этапа 2): транспорт AmneziaWG работает НЕ через V2Ray-клиенты (HAPP/v2rayNG), а через AmneziaVPN/AmneziaWG; имя профиля и напоминание перекачать конфиг после смены ключей. В приложении AmneziaVPN содержимое конфига видно — пометка едет вместе с файлом; в само чужое приложение мы не лазим. Доки (en/ru/zh, configuration.md): AWG-секция дополнена выдачей ключей через GUI («Ключи» в Server Settings: hysteria2-ссылка и AWG .conf текстом с QR + выдача гранта в один клик). Тест awg-lifecycle проверяет шапку (startswith '# Xrayebator:'). Live: шапка видна в awg-conf --name happ на 2.26.125.147.
Фидбек: карточки были похожи только цветом, между ними торчала полоска-разделитель, QR с AWG-конфигом не импортировался в приложение.
- каждый бэкенд теперь в СВОЁМ боксе (backendCard: bg-2/border/radius-16 — стилистика остальных карточек приложения), сетка auto-fit 340px
- полоска border-top между рядами убрана
- иконки в цветных бейджах (Zap — жёлтый для hysteria, Shield — голубой для AWG) + подзаголовки ('быстрый UDP · QUIC' / 'системный VPN · full-tunnel')
- тексты переписаны короче и по делу; у AWG-ноты явно: клиент AmneziaWG/AmneziaVPN, НЕ V2Ray/HAPP
- QR для AWG теперь из конфига БЕЗ комментариев-шапки: часть приложений спотыкается при импорте с камеры о строки до [Interface] (текст и копирование остаются с шапкой)
typecheck 0, unit-тесты зелёные.
… AWG-конфиги)
Пользователь не видел hysteria2/AWG «в самих ключах» — страница Ключей парсила из подписки только vless://. Теперь:
- subscription.ts: extractHysteria2Links() — hysteria2:// из HAPP- и generic-тел (включая base64-блоки); fetchSubscription возвращает {keys, hysteria2Links}; deployer/inspector-вызовы адаптированы
- страница Ключей: hysteria2-карточки (chip HYSTERIA2 · UDP :порт, копирование+QR — QR сканируется HAPP/Hiddify напрямую) и AWG-карточки per-profile (полный .conf с 3.1-ключами, копирование + QR без комментариев-шапки для совместимости импорта)
- AWG-конфиги тянутся по SSH с доступом из сохранённой карточки сервера (shouldAutoConnectServer гейтит секцию — секрет должен лежать в keychain)
- ServerSettings: карточки бэкендов поставлены одна под одной (фидбек «совместить 1 под 1»), подчёркнутые «ВНИМАНИЕ»-нотки заменены на нейтральные
- i18n keys.* ×3 языка
typecheck 0; vitest: только задокументированный Windows-only shell-command; bash -n чистый
…юч (не конфиг) Фидбек: протоколы грузятся в реальном времени (ожидание), карточки не совмещены, AWG выглядит конфигом, а не ключом; hysteria перекрывается amnezia. - загрузка бэкендов ПАРАЛЛЕЛЬНО с профилями (Promise.all в load) + скелетоны-заглушки вместо пустоты - карточки бэкендов объединены в ОДИН общий бокс (listCard), блоки протоколов один под другим с отступами (полосок нет) - кэш awg-conf в main-process (инвалидация при смене грантов/режима 3.1/uninstall): страница Ключей открывается мгновенно при повторном заходе, как с обычными коннекторами - AWG теперь показывается как КЛЮЧ: поля Endpoint и Адрес (parseAwgClientConf из нового shared/awg.ts), кнопки — копирование конфига и QR (QR без комментариев-шапки для совместимости импорта); простыня конфига убрана - hysteria2-карточки на странице Ключей — из подписки, грузятся вместе с vless (extractHysteria2Links); deployer/inspector адаптированы к новому возврату fetchSubscription - shared/awg.ts + тесты (10/10: shared + backend-manager) typecheck 0; vitest — только задокументированный Windows-only shell-command; bash -n чистый
…айминг с vless)
Фидбек: hysteria/awg подгружаются в реальном времени без сохранения, каждый раз и в разный тайминг; должны — как vless, ~моментально и из приложения.
- Server record: hysteria2Keys (строки из подписки) и awgConfs (per-profile) персистятся в store; normalizeServer даёт дефолты; store.updateBackendKeys — мерж/очистка
- subscription:fetch пишет hysteria2Keys рядом с vless-ключами
- backends:awgConf пишет конфиг в карточку сервера (store вместо in-memory Map); awgGrant/awg31/awgUninstall очищают кэш при ротации
- страница Ключей: мгновенная инициализация из server.hysteria2Keys/awgConfs, затем тихое фоновое обновление; ВСЕ запросы — одним Promise.all (profiles+backends+awgConf×N) — равный тайминг, без каскада
- карточки бэкендов в Server Settings: один общий бокс, блоки протоколов 1-под-1 (фидбек), параллельная загрузка + скелетоны
- AWG как ключ: поля Endpoint/Адрес (shared/awg.ts parseAwgClientConf), QR/копирование — конфиг без комментариев-шапки
- fetchSubscription -> {keys, hysteria2Links}; deployer/inspector адаптированы
typecheck 0; vitest — только задокументированный Windows-only shell-command; bash -n чистый
…вшая пропса) Фидбек: бэкенд-ключи 'не сохраняются после первой загрузки' и грузятся с разнобоем. Корень: ServerKeys инициализировался из пропсы server — устаревшего снимка Dashboard'а, где ещё нет hysteria2Keys/awgConfs, плюс два отдельных эффекта давали каскад. Единый эффект: (1) мгновенная отрисовка из ПЕРСИСТЕНТНОГО стора через servers.get — пропса больше не источник правды; (2) фоновое обновление одним Promise.all: подписка + profiles/backends + awgConf×N параллельно — равный тайминг с vless, без скелетон-мигания при повторных заходах (данные уже в сторе). typecheck 0; vitest — только задокументированный Windows-only shell-command; bash -n чистый
…нальный sync со стором Фидбек: AWG-конф 'не сохраняется' + спиннер висит после загрузки под карточкой. - неудачный refetch больше не стирает сохранённый конфиг (last known good: merge только по успешным) - после пакета загрузки — финальная синхронизация состояния со стором (servers.get): состояние == персистентные данные, расхождений нет - спиннер перенесён СТРАНИЦУ -> ВНУТРЬ карточки AWG: показывается только пока у конкретной карточки нет конфига и идёт загрузка; после загрузки ничего лишнего под карточкой не остаётся typecheck 0; vitest — только задокументированный Windows-only shell-command; bash -n чистый
…kends.status) Фидбек: AWG-конфиг появлялся только после SSH-ответа backends.status+profiles (~2 сек), хотя конфиг уже лежит в сторе. Карточки AWG на странице Ключей теперь строятся из Object.keys(awgConfs) стора напрямую — рисуются с первой отрисовкой (мгновенно), SSH-refresh догоняет тихо. Гейт awgActive убран: факт наличия конфига в сторе достаточен (uninstall очищает конфиги через стор). backends.status со страницы Ключей убран совсем — минус одно SSH-звено; profiles.list остаётся для имён granted-профилей без конфига (спиннер внутри карточки). typecheck 0; vitest — только задокументированный Windows-only shell-command; bash -n чистый
…емнённых под-боксах Фидбек: заголовок/hint секции висели вне бокса, ноты протоколов — просто текстом под статусом. Теперь: заголовок и hint перенесены ВНУТРЬ общего бокса (текста вне боксов нет), описательные ноты Hysteria/Amnezia обёрнуты во внутренний более затемнённый под-бокс (backendInfo, как затемнённые inner-box у ключей) — визуально отделены от действий. Справочно: последняя версия AmneziaWG подтверждена GitHub API — v3.1.20260812 (tools), генерация протокола 3.1; 3.2 не существует. Live-сервер 2.26.125.147 уже на ней. typecheck 0; vitest — только задокументированный Windows-only shell-command; bash -n чистый
Вместо двух маленьких под-боксов под ноты — ОДНА большая затемнённая выемка (backendsInner, стиль inner-box ключей), в которой лежат оба блока протоколов: сверху заголовок/hint секции, под ними выемка с Hysteria и AmneziaWG один под другим. Ноты — обычным текстом внутри выемки. Внешнее округление — существующий сероватый listCard. typecheck 0; vitest — только задокументированный Windows-only shell-command; bash -n чистый
…иагностика AWG) - configuration.md (en/ru/zh): в CLI-таблицах отсутствовали команды, которые есть в диспетчере: hysteria2-subbody, hysteria2-link, awg-31; GUI-секции дополнены панелью бэкендов (установка/ключи/килл-свитч/3.1) - desktop-gui.md (en/ru/zh): страница «Ключи» — карточки бэкендов из персистентного стора (Endpoint/Address полями, QR без служебной шапки, параллельная загрузка); Server Settings — пул бэкенд-действий; блок backend-команд в списке exposed - troubleshooting.md (en/ru/zh): в секции «нет latest handshake» — диагностика сетевого пути: AWG-сокет в ядре невидим для ss, tcpdump -l (без -l блочный буфер), пустой захват = оператор/DPI, пакеты есть а handshake нет = версия приложения или awg-31 --off fallback. Отработано на живом 2.26.125.147 (пробы РФ->45467 дошли) - ru configuration.md: починена оборванная фраза «предоставляет / и группы» - CLAUDE.md: CLI-список Hysteria 2 дополнен subbody/link
Корень: телефон шлёт junk с собственными параметрами приложения (5 пакетов/попытку при наших Jc=10 — tcpdump 2.26.125.147), т.е. сырой .conf при импорте в AmneziaVPN теряет junk-группу. По исходникам amnezia-client (importController.cpp) правильная доставка — контейнер amnezia-awg с last_config, куда импортёр переносит все AWG-ключи (awgProtocolKeys); сырой текст не гарантирует этого пути. - shared/awg.ts: parseAwgConfMap + buildAwgVpnConfig + buildAwgVpnUrl — чистый Builder структуры extractWireGuardConfig (hostName/port/ client_priv_key/psk_key/server_pub_key/allowed_ips + AWG-ключи), vpn:// + base64url без qCompress (импортёр деликатно принимает несжатое тело — проверено по исходникам dev-ветки) - Ключи (ServerKeys) и диалог ключей (ServerSettings): вторая кнопка QR «AmneziaVPN» рядом с QR для AmneziaWG; чип без жёсткой версии - i18n ru/en/zh: keys.backendsQrVpn - vitest: 7 тестов (структура, base64url-чистота, кириллица, минимум)
error 1000 + предупреждение «старая версия»: last_config без явной версии протокола AmneziaVPN трактует как legacy и запускает туннель не тем поколением. Добавлен protocol_version в last_config и контейнер (значения из их protocolConstants: 1.5/2/3.1), версия выводится из содержимого конфига: HPK/RandomTrailers -> 3.1, I1-I5/ContentPadding -> 2, иначе 1.5. detectAwgVersion экспортирован и протестирован.
…amnezia-awg2) По исходникам amnezia-client (containerUtils.cpp + AwgProtocolConfig::toJson): их формат кладёт серверные junk-параметры (Jc/S1/H1/HPK/...) на уровень объекта 'awg' рядом с last_config (модель сервера читает их оттуда), специальный junk I1..I5 пишет безусловно (пустые строки валидны), а новый контейнер называется 'amnezia-awg2'. Наш прошлый формат с junk только внутри last_config оставлял серверную часть модели пустой, из-за чего сервис терял client_ip (error 1000, подтверждено логом телефона).
…qCompress) Эталон — реальный экспорт рабочего docker-сервера (LV): серверные junk на уровне awg + range-таймеры go-туннеля (Rekey/Keepalive/Reject/Max- Handshake 100-120/3-7/150-180/5-15/15-20) + ContentPaddingAddition в JSON (не kernel), client_ip без маски, client_pub_key+clientId в last_config, subnet_address, persistent_keep_alive, qCompress-payload. Симуляция конвейера 5.0.1.5 (import→model→service-config) подтвердила прохождение client_ip; error 1000 у пользователя — следствие отсутствия серверной части модели в прошлых форматах.
Живое подтверждение (телефон, LTE, AmneziaVPN 5.0.1.5): наш кастомный junk (Jc=10, крупные пакеты 130-452, случайные H1-H4) go-туннель приложения применяет не целиком — всплески по 5 пакетов против ожидаемых ~11, handshake не собирается. LV-сервер Amnezia с дефолтным диалектом с того же телефона работает. - _awg_gen_junk: Jc=5, Jmin=10, Jmax=50, H1-H4=1..4 (WG-магия), S1/S2 rand 12..150, S3/S4 rand 12..64 (лимиты их валидатора), уникальность и S1+56≠S2 сохранены; HPK/RandomTrailers без изменений (3.1-маскировка полная) - test-awg-lifecycle: ассерты на дефолтный диалект Amnezia - docs en/ru/zh: junk = дефолты Amnezia; awg-install row; testing.md; новый кейс troubleshooting «AmneziaVPN error 1000 / No value for client_ip» (нативный shape vpn://, QR · AmneziaVPN); CLAUDE.md Своя логика осталась там, где ей место: Hysteria 2 и VLESS/Reality. AWG теперь = 1:1 с Amnezia + наши HPK/RT и выдача ключей через GUI.
Синхронизация с удалением карточки «Уведомления» из настроек сервера. - desktop-gui.md (3 локали): пункт про карточку заменён на явное «алерты осознанно не часть приложения» с причиной; из списка обслуживаемых команд убран `alerts-*` (заголовок стал «Подписка и TLS»), а сами команды добавлены в перечень того, что GUI не предоставляет. - README (3 локали), строка «Настройки сервера»: уведомления больше не в списке возможностей GUI, вместо них — пометка, что алерты живут в терминале и токен не попадает в renderer. - CHANGELOG: два места описывали карточку и «четыре раздела»; разделов снова три, а удаление оформлено отдельным пунктом с причиной. - CLAUDE.md: у команды алертов зафиксировано «terminal-only by design», чтобы карточку не вернули при следующей правке GUI. Проверено скриптами: 0 битых ссылок и якорей, 0 расхождений в таблицах.
Экран настроек совмещал форму создания и список профилей: оператор, которому нужно было просто посмотреть/поправить существующий профиль, каждый раз проходил мимо формы. Теперь это два экрана одного сервера. - `ServerSettings` и `ServerProfiles` — тонкие обёртки над общим `ServerPage` с режимом. Они делят доступ по SSH, загрузку данных и диалоги профиля, поэтому копировать 3-тысячный экран не пришлось. - Вкладки: «Создание профиля» (панель), «Существующие профили» (переход на страницу, со счётчиком), «Доп. протоколы», «Подписка и TLS». Со страницы профилей вкладки возвращают в настройки на нужном разделе. - `App.tsx`: новый view `profiles`; `openServer` умеет открывать его напрямую (пригодится для будущих точек входа на дашборде). - i18n: `settings.sectionCreate`, `profilesHeading`, `profilesSearch`, `profilesCount` в трёх локалях; вкладка профилей теперь называется «Существующие профили». - Тест рендера: панелей по-прежнему три (create/backends/subscription), плюс smoke-тест новой страницы (свой заголовок, тот же гейт подключения). Проверено: typecheck, `npm test` (171/172 — падает известный /bin/sh на win32), `npm run build`. Внешний вид карточек пока прежний — переделка следующим шагом.
Страница «Существующие профили» получила вид, пригодный для просмотра, а не только для действий: у строки появился значок транспорта (xhttp/grpc/tcp), точка состояния «Активен / Отключён» (срок или лимит трафика) и поиск по имени, SNI и ссылке подписки. Заголовок списка стал «Профили сервера», сверху — счётчик показанных, при пустом результате поиска выводится «Ничего не найдено». Из референса осознанно не взято: полоска загрузки (она ничего не измеряет), пагинация (профилей единицы), дублирующий футер с теми же доменом/сроком/ размером — эти данные уже в чипах строки. Проверено: typecheck, `npm test` (171/172 — известный /bin/sh на win32), `npm run build`.
Синхронизация с новым экраном: список профилей больше не живёт внутри настроек. - desktop-gui.md (3 локали): новый раздел «Server profiles» — откуда открывается (строка вкладок, счётчик), что на карточке (иконка транспорта, состояние активно/отключён, бейджи, чипы, ссылка), поиск, и что из референса осознанно не взято (полоска загрузки, пагинация, дублирующий футер). В разделе настроек «получить список профилей» заменено на «список живёт на отдельной странице». - README (3 локали): новая строка таблицы возможностей GUI + правка строки настроек; уточнено, что срок и отзыв подписки теперь в настройках профиля на этой странице, а уведомления Telegram — терминальные. - architecture.md (3 локали): в дереве репозитория у renderer/ появился ServerProfiles. - testing.md (3 локали): строка smoke-теста описывает и рендер новой страницы. - CHANGELOG: пункт про отдельную страницу списка; старый пункт про «три раздела» приведён к фактическому набору вкладок. Проверено скриптами: 0 битых ссылок/якорей, 0 расхождений таблиц, 0 мойибаке, паритет заголовков по локалям.
Три правки по замечаниям к странице профилей. 1. Переключение «создание ↔ профили» больше не повторяет SSH-запрос. Раньше это были два view, и переход размонтировал страницу: она заново поднимала сессию, тянула профили, бэкенды и статус подписки. Теперь один view `server` с полем `mode`: компонент остаётся смонтированным, данные приходят одним запросом при открытии сервера и раскладываются по обоим экранам. Раздел, выбранный вкладкой со страницы профилей, приходит пропсом `initialSection` и применяется без перезагрузки. 2. Со вкладки убран счётчик профилей — она снова просто «Существующие профили». Количество переехало вниз списка строкой «Показано: N», как в референсе. 3. Шапка списка — как на референсе: слева заголовок «Профили сервера» и подпись «Управляйте подписками и настройками подключений», справа поиск (растягивается) и фильтр «Все профили / Активные / Отключённые». Плюс чипы строки получили обозначения: календарь у срока, база у трафика, глобус у домена (SNI). Без них «до 2026-11-16» и «182 KБ» читались как непонятные числа. Проверено: typecheck, `npm test` (171/172 — известный /bin/sh на win32), `npm run build`.
Синхронизация с тремя правками страницы профилей. - desktop-gui.md (3 локали): шапка страницы описана как в интерфейсе — заголовок и подпись слева, поиск и фильтр «все / активные / отключённые» справа; из описания вкладок убран счётчик, добавлена строка «Показано: N» под списком; отдельным абзацем зафиксировано, что переключение экранов не повторяет SSH-работу (один компонент с `mode`, данные загружаются один раз при открытии сервера); у чипов срока, трафика и SNI отмечены значки. - README (3 локали): в строке «Существующие профили» вместо счётчика — поиск и фильтр. - CHANGELOG: пункт про отдельную страницу дополнен поиском/фильтром, значками чипов и тем, что переключение экранов больше не поднимает новую сессию. Проверено скриптами: 0 битых ссылок и якорей, 0 расхождений таблиц, 0 мойибаке, паритет заголовков по локалям.
Иконок оказалось мало: «182 KB» и «www.cloudflare.com» в строке профиля читались как что угодно, пока не догадаешься по формату. Теперь у значения есть подпись: «Трафик: 182 KB / 10 GB», «SNI: www.cloudflare.com», а у срока подпись уже была — «до 2026-11-16», к ней добавился значок календаря. Новый ключ `settings.trafficShort` в трёх локалях; паритет ключей сохранён. Проверено: typecheck, `npm test` (171/172 — известный /bin/sh на win32), `npm run build`.
Уточнение после «182 KB и www.cloudflare.com ни о чём не говорят»: в описании страницы профилей (3 локали) чипы теперь перечислены с подписями — `SNI: <домен>`, `до <дата>`, `Трафик: <сколько>` — вместе со значками, а не только значками.
По замечанию к превью карточки: - у домена убран значок глобуса — осталась подпись «SNI: www.cloudflare.com», как и просили; - значки календаря и базы у срока и трафика увеличены с 12 до 14 px: на 12 они терялись рядом с текстом; - подпись срока стала «действует до 2026-11-16» вместо «до …» (en «valid until …», zh «有效期至 …»); ключ `settings.expireUntil` используется только этой строкой, так что правка нигде больше не всплывает. Проверено: typecheck, `npm test` (171/172 — известный /bin/sh на win32), `npm run build`.
Синхронизация с правкой превью: у `SNI:` больше нет значка, срок подписан «действует до», значки календаря и базы крупнее. Заодно починен абзац про чипы в desktop-gui.md: предыдущая правка вставки подписей разрезала строку по переносу и оставила «`PQ ×N` badges, » плюс пустую строку и обрывок «date (calendar icon) …» в en и ru (в zh — дубль «SNI(地球图标)、`SNI: …`»). Абзац восстановлен целиком во всех трёх локалях.
По замечанию к превью: «SNI:» перед доменом убран, остался чистый домен, как раньше. Чтобы строка метаданных не уезжала на второй ряд, она больше не переносится: чипы (транспорт, порт, срок, трафик) держат свой размер, а сжимается только домен — единственное значение непредсказуемой длины — и обрезается многоточием. Полный домен остаётся в `title` и в ссылке подписки. Проверено: typecheck, `npm run build`, `npm test` (171/172 — известный /bin/sh на win32).
Синхронизация с правкой карточки: у домена нет ни значка, ни подписи «SNI:», а строка чипов не переносится — при нехватке места сжимается только домен и обрезается многоточием. Заодно восстановлен абзац про чипы в desktop-gui.md: две предыдущие правки резали его построчно и оставили обрывки в en, ru и zh. Теперь абзац заменяется целиком и во всех трёх локалях читается связно.
«Показано: N» считало профили, а карточек на экране может быть больше: у профиля с грантом Hysteria 2 и/или AmneziaWG рисуется дополнительная карточка внутри той же итерации. На сервере из трёх профилей с двумя грантами выходило «Показано: 3» при пяти карточках — подпись врала. Теперь строка называется «Профилей: N» (en «Profiles: N», zh 「配置档:N」) и считает именно профили, прошедшие поиск и фильтр; карточки бэкендов к своему профилю относятся и отдельно не считаются. Проверено: typecheck, `npm test` (171/172), `npm run build`.
Я неверно понял предыдущую правку: просили убрать число, а я перенёс его из вкладки в строку под списком («Показано: N», затем «Профилей: N»). Убрано полностью — ни в шапке, ни под списком числа больше нет, остался только заголовок с подписью, поиск и фильтр. Вместе со строкой удалены её CSS-класс и ключ `settings.profilesShown` в трёх локалях (паритет сохранён), а из документации и CHANGELOG убраны упоминания счётчика. Проверено: typecheck, `npm test` (171/172), `npm run build`.
Возвращаю строку под списком, но число теперь соответствует экрану: профиль плюс карточка на каждый выданный грант Hysteria 2 / AmneziaWG. На сервере из трёх профилей, где у одного оба гранта, счётчик показывает 5, как и карточек. Раньше считались только профили (3), из-за чего число не сходилось с тем, что видно. Новый `visibleCardCount` повторяет ровно те условия рендера, по которым рисуются карточки бэкендов (`profile.backends?.hysteria2`, `?.awg`), поэтому разойтись они не могут. Ключ `settings.profilesShown` («Показано: N») и CSS вернулись; документация (3 локали) и CHANGELOG описывают, что именно считает число.
Вкладка была одной длинной карточкой: статус шёл строкой через «·» (режим · домен · провайдер · сертификат), а форма DNS-01 была раскрыта всегда. Теперь это компактная карточка: - шапка-кнопка: плитка с иконкой (ShieldCheck вместо замка), название, домен подписью, режим чипом с тоном (ip_tls/domain_tls — зелёный, http_tls — жёлтый) и шеврон; клик разворачивает и сворачивает форму; - под шапкой — сетка из четырёх подписанных значений (режим, домен подписки, DNS-провайдер, сертификат) вместо склейки через «·»; - предупреждения (деградация до http_tls, staging-сертификат) — отдельными плашками, а не серым текстом в общем потоке; - основная кнопка («Настроить DNS-01» / «Исправить через DNS-01») раскрывает форму; сама форма, поля и поведение не изменились. Ключ `settings.dns01NoDomain` в трёх локалях; CSS-блок новых классов. Проверено: typecheck, `npm test` (171/172 — известный /bin/sh на win32), `npm run build`; отдельно проверено, что в правленых файлах нет мойибаке.
По замечаниям к карточке «Подписка и TLS»: - иконка сменена на глобус (домен/подписка) — щит читался как «безопасность вообще», а карточка про подписку и её домен; - режим (ip_tls / domain_tls / http_tls) вынесен бейджем к заголовку: зелёный для HTTPS-режимов, жёлтый для деградации, нейтральный если сервер не отдал статус. Из сетки значений строка «РЕЖИМ» убрана — она дублировала бейдж; - домен подписки больше не выглядит обычным текстом: это моно-бокс с рамкой и фоном, выровненный по левому краю подписи (`align-self: flex-start`), с полным значением в `title`; - остаток сертификата подсвечивается жёлтым, если до истечения меньше двух недель. Сетка стала из трёх значений (домен, провайдер, сертификат), то есть ровно по ширине карточки без пустой четвёртой колонки. Проверено: typecheck, `npm test` (171/172 — известный /bin/sh на win32), `npm run build`, детектор мойибаке по правленым файлам — чисто.
Домен уже был в боксе, а провайдер и срок сертификата остались обычным текстом: в одной строке соседствовали «значение с рамкой» и «просто текст», из-за чего колонки читались неровно. Теперь все три значения используют один класс `.subValueBox` — моно, рамка, фон, выравнивание по левому краю своей подписи (`align-self: flex-start`) и минимальная ширина, чтобы бокс с «—» не схлопывался в точку. Жёлтый тон срока сохранён и применяется к тексту внутри бокса. Документация (3 локали) и CHANGELOG описывают три бокса вместо одного. Проверено: typecheck, `npm test` (171/172), `npm run build`, мойибаке — чисто.
Узкие боксы «—» и «6 дн.» смотрелись прилипшими к левому краю и не совпадали по форме с широким боксом домена. Теперь все три значения занимают всю ширину своей колонки (`align-self: stretch` + `width: 100%`) и центрируют текст: колонки одинаковы по форме, а короткие значения стоят по центру плашки. Документация (3 локали) уточняет, что бокс занимает колонку и текст в нём центрирован. Проверено: typecheck, `npm test` (171/172), `npm run build`.
Глобус читался как «домен/интернет вообще», хотя карточка про сертификат и его срок. Теперь `FileBadge` — документ с печатью, ближайшее к «сертификату» в lucide (иконки `Certificate` в наборе нет). Заодно вычищены два расхождения в CHANGELOG, которые накопились за итерации: у страницы профилей осталось «Expiry, traffic and SNI now carry calendar/database/globe icons», хотя у SNI значок убран — теперь только calendar/database; в пункте про карточку подписки режим назывался и «chip», и «badge» в одном абзаце — оставлен badge у заголовка. Документация (3 локали) обновлена на «сертификат». Проверено: typecheck, `npm test` (171/172), `npm run build`, мойибаке — чисто.
…st key в шапке Форма развёртывания занимала 1182px при окне 720px: одна колонка полей, кнопка уезжала за экран. Правая часть (шаги и консоль) не тронута — замечание «правая была идеальной по размеру» учтено: шаги с подписью под каждым, пустая консоль «Ожидание вывода…» и растяжение карточки на высоту формы остались как были. Что изменено слева: - IP и SSH-порт в одной строке (у порта узкая колонка 104px); - PQ-маршрут и DNS-01 — одна группа «Дополнительно» с двумя карточками в ряд вместо двух групп со своими заголовками; обе по-прежнему выключены по умолчанию; - подсказка «Host key закрепится при первом входе: подмена заблокирует подключение» переехала из формы в шапку страницы, справа от заголовка; - шаг между строками формы 18 → 14px. Двухколоночную форму входа (пользователь рядом с паролем) откатил: по замечанию поля снова идут построчно, SSH-пароль вернулся под «Аутентификация», как было. Из той правки остался только необязательный пропс showHostKeyNote — им страница развёртывания убирает подсказку из формы, потому что рисует её в шапке. Страница импорта получила ту же строку «host + порт»: поля там те же, и разная раскладка на соседних экранах читалась бы как ошибка. Замеры в собранном бандле (Electron, 1024×720): форма 1182 → 853px, страница 1302 → 973px; шаги 434px и консоль 120px — ровно как до правок. Размеры инпутов, карточек и их тексты не менялись. Ключи i18n: deploy.pq → deploy.pqOn (длинная подпись с «(пост-квантовое шифрование)» больше не нужна, смысл остался в подсказке карточки), deploy.dns01 → deploy.extras. Количество ключей в ru/en/zh совпадает. Проверено: npm run typecheck — чисто, npm test — 171/172 (падает только известный shell-command.test.ts: на win32 нет /bin/sh), npm run build собирается, проверки документов — 0 битых ссылок, 0 расхождений таблиц, 0 мойибаке.
В шапке страницы подсказка висела отдельной строкой в пустоте справа от заголовка — визуально вылипала из страницы. В форме она удлиняла и без того самую длинную колонку. Место нашлось в правой карточке: она растянута на высоту формы, а консоль не растягивается (min 120 / max 240px, дальше свой скролл), поэтому под ней оставалось ~130px мёртвой зоны. Теперь подсказка — футер карточки с margin-top: auto и разделительной линией: прижата к низу, под консолью, и занимает пустоту вместо того, чтобы её создавать. Внутрь чёрного бокса консоли её класть нельзя: у панели user-select: text, и посторонняя надпись попадала бы в копируемый лог. Поведение консоли не менялось. Проверено в собранном бандле при 1024×720 и 800×560: карточка 853px, шаги 434px, консоль 120px, подсказка прижата к низу (2 строки на широком окне, 3 на узком). npm run typecheck, npm test (171/172 — известный shell-command.test.ts без /bin/sh на win32), npm run build.
Накопленный этап перерос линию 0.6.5: к Reality добавились два транспорта (Hysteria 2, AmneziaWG 3.1), PQ стал свойством любого транспорта, появились DNS-01, четыре формата подписки, лимиты трафика, Telegram-алерты и волна GUI. Поэтому минорная версия поднята до 0.7 — вместо «0.6.5 final», который был подготовлен, но не выпускался. Что в коммите: - релиз-ноты docs/releases/v0.7-beta.1.md и .en.md (бывший v0.6.5.md/.en.md, переписаны под 0.7): бета-баннер, раздел «Изменения с 0.6.5-beta.2» на 112 коммитов, полный список «Что нового» на 13 блоков, честные ограничения, обновление и сборка 0.7.0; - следом за кодом поправлены устаревшие цифры в релиз-нотах: 42 скрипта валидации вместо 34 (и «было 28» — это счёт v0.6.0), 25 файлов/172 теста Vitest, снято устаревшее «PQ-маршрут не кладётся в подписку» — кладётся в HAPP-тело и пропускается в base64/Clash/sing-box; - package.json и package-lock.json → 0.7.0-beta.1; - CHANGELOG: содержимое [Unreleased] порезано как [0.7.0-beta.1] - 2026-10-21, сверху остался пустой [Unreleased]. Проверено: bash validation 42/42 в WSL, npm run typecheck — чисто, npm test — 171/172 (единственный сбой shell-command.test.ts: на Windows нет /bin/sh), npm run build собирается, проверки документов — 0 битых ссылок, 0 расхождений таблиц, 0 мойибаке.
На странице «Ключи» карточка «Ссылки для клиентов» печаталась всегда: на каждый профиль приходило по три почти одинаковых URL (обычная подписка, ?target=clash, ?target=singbox), и ключ выглядел задвоенным. Ориентир продукта — HAPP, Hiddify и v2rayNG: им нужна обычная подписка, а Clash/mihomo и sing-box — отдельные клиенты. Теперь это дополнительный вид. По умолчанию страница отдаёт тот же основной набор, что и раньше (подписка HAPP/v2ray + VLESS-ссылки), а форматы включаются переключателем «Форматы Clash / sing-box» в шапке страницы. Выбор пишется в localStorage: он про клиент оператора, а не про конкретный сервер, поэтому не сбрасывается при переходе между серверами. Сам переключатель показывается только если сервер реально вернул ссылки форматов — иначе кнопка была бы пустышкой. localStorage читается через readFormatsPref с try/catch: в SSR-тестах и в приватном режиме хранилища нет, и страница обязана рисоваться без него. Проверено рендером страницы в offscreen-Electron со стабом стора: по умолчанию пять карточек-key и кнопка «Скопировать всё», с включённым переключателем — шесть карточек. npm run typecheck — чисто, npm test — 171/172 (единственный сбой shell-command.test.ts: на win32 нет /bin/sh), npm run build собирается.
…ключателю Полевые заметки в релиз-нотах не пишем: это формат записки, а не релиза. Те же факты (junk — это диалект; первый channel-test платит прогрев; фильтрация :80 видна только снаружи; geo-базы расходятся по ASN; рестарт awg0 роняет туннель всем на секунды) уже лежат в docs/configuration.md, docs/testing.md и docs/troubleshooting.md, так что раздел убран из обеих локалей без потери знаний. Плюс в блок «Подписка: четыре формата из одного токена» добавлено, что в GUI это дополнительный вид по переключателю, а основной набор ключей остаётся прежним — ровно то поведение, что появилось в предыдущем коммите.
Оба дефекта — только в человекочитаемом выводе, JSON всегда был верным.
Найдены прогоном на двух свежих Ubuntu 22.04, развёрнутых через GUI-путь
(настоящий main-process Deployer, не харнесс-копия).
1) channel-test помечал «, PQ» каждый маршрут. Переменная `pq` — строка
"true"/"false", а не флаг, поэтому `${pq:+, PQ}` подставлялась и при "false".
Теперь суффикс только при `pq == "true"`: на сервере с фиксом метку несёт лишь
`happ-xhttp-pq`, на сервере без фикса — все семь маршрутов.
2) Баннер `awg-install` был захардкожен «✓ AMNEZIAWG 2.0 УСТАНОВЛЕН», хотя 3.1
включён по умолчанию (HeaderProtectionKey, RandomTrailers, S3/S4) — на живом
сервере «2.0» стояло прямо над junk-блоком с полями 3.1. Баннер больше не
называет версию, а отдельная строка «Формат:» читает `three_enabled` из реестра
бэкендов — то же поле, по которому отчитывается `awg-status`.
Проверено: bash -n xrayebator; validation/test-channel-test-cli.sh и
test-awg-lifecycle.sh — зелёные; полный набор validation/ — 42/42.
Переводим 0.7 из беты в стабильный релиз: релиз-ноты переименованы в docs/releases/v0.7.0.md (+ .en.md), бета-баннер убран, версия в package.json и package-lock.json — 0.7.0, секция CHANGELOG — [0.7.0] - 2026-10-22. В релиз-нотах добавлен раздел «Стабильность и безопасность»: он собирает уже сделанное по TLS/ACME, доступу, подписке, лимитам, выбору SNI и надёжности миграций, вместо пересказа отдельных правок. Раздел «Насколько это проверено» приведён к фактическому объёму: контрольные развёртывания на четырёх дистрибутивах, два свежих контрольных развёртывания на Ubuntu 22.04 (дефолт GUI и с включённым PQ), проверка всех маршрутов настоящим клиентом Xray, четыре формата подписки, бэкенды Hysteria 2 и AmneziaWG, самообновление. Проверено: bash -n xrayebator, validation/ — 42/42, npm run typecheck — чисто, npm test — 171/172 (известный shell-command.test.ts: на win32 нет /bin/sh), npm run build собирается, проверки документов — 0 битых ссылок и расхождений.
Раздел «Насколько это проверено» перечислял только контрольные стенды и не упоминал рабочий сервер, где маршруты, форматы подписки и оба бэкенда тоже проверены живьём. Формулировка переписана по факту: три развёртывания с живыми замерами — два контрольных с нуля на Ubuntu 22.04 (дефолт GUI и с PQ) и рабочий сервер, развёрнутый ранее, плюс прежняя матрица на четырёх дистрибутивах. Заодно это честнее по отношению к читателю: он видит, что проверка каналов настоящим клиентом Xray прошла на всех трёх (HTTP 200, ≈170–330 мс), а не только на двух свежих стендах. То же обновлено в теле PR и в сводке CHANGELOG. Проверки документов: 0 битых ссылок, 0 расхождений таблиц, 0 мойибаке.
В «Честных ограничениях» оставалась оговорка, что приём PQ- и hysteria2-строк проверен только на сервере, а не на устройстве с установленным HAPP. Проверка сделана вручную: все маршруты всех трёх развёртываний подключаются и работают с телефона и с ПК через HAPP и Hiddify. Поэтому ограничение переписано по факту, а выигрыш вынесен в раздел «Насколько это проверено»: приём ссылок из подписки подтверждён живыми клиентами, а не только сервером. Осталась одна точная оговорка — задержки внутри приложений отдельно не замерялись, цифры в релизе из `channel-test` на сервере. Проверки документов: 0 битых ссылок, 0 расхождений таблиц, 0 мойибаке.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Что изменилось с прошлого мейна (199 коммитов, 109 файлов, +31 191/−9 367)
Весь этап
0.7(первая бета этой линии): к Reality добавились два транспорта, пост-квантовое шифрование стало свойством любого транспорта, появились DNS-01, четыре формата подписки, лимиты трафика и Telegram-алерты. Плюс длинный хвост исправлений, найденных на живых серверах: почти каждый пункт ниже — это баг, который CI не видел, а живой VPS показал.Ставится поверх
0.6.0: миграции доделывают всё сами, перенастраивать серверы не нужно.Мультипротокол: Hysteria 2 и AmneziaWG 3.1
a9ae8b1): состояние бэкендов переехало из/usr/local/etc/xray/в/usr/local/etc/xrayebator/— Xray-каталог остаётся Xray-only. Реестр644 rootбез секретов, поэтому сервисный аккаунтxrayчитает его из подписки.HyNetworks/hysteriaс проверкой sha256, свой юнит сCAP_NET_BIND_SERVICE, QUIC-буферы в sysctl, TLS адаптивный (сертификат подписки с deploy-hook на продление, иначе self-signed). CLIhysteria2-install|uninstall|status|grant|subbody|link.4a402f4): модуль ядра через DKMS, 3.1 по умолчанию (S1–S4,HeaderProtectionKey,RandomTrailers, мусор в диалекте официальных клиентов). Peer-модель на профиль,awg0.confрегенерируется из профилей.vpn://в родном формате AmneziaVPN (13ecdcb,6c6bae0,db028b4,164ad8d): сырой.confих туннель не доезжает, поэтому GUI отдаёт контейнерamnezia-awg2с server-level junk,protocol_versionи range-таймерами.01f6ddc):_backend_apply_profile_lifecycle— единственная точка, где создание/revoke/срок/удаление профиля трогают бэкенд-ключи. Создание профиля ключи не выдаёт: иначе каждый новый профиль получал бы ключи всех установленных бэкендов.PQ (VLESS Encryption) на всех транспортах
a2b75fb:mlkem768x25519plusбыл жёстко прибит к xhttp — и слой шифрования, и проверка инбаунда, и фильтр подписки. Теперьxrayebator profile-pq --name N [--transport tcp|tcp-utls|tcp-xudp|tcp-mux|grpc|xhttp]. VLESSdecryption— уровня инбаунда, поэтому PQ-маршрут получает собственный инбаунд и порт с общим ключом из.vless_decryption; обычные маршруты остаютсяencryption=noneи работают у всех клиентов.profilesотдаёт аддитивныйpq_routes: верхнеуровневыйpq_enabledописывает только первый маршрут и для GUI не годится.Подписка: четыре формата из одного токена
4b061b2): один токен отдаёт четыре тела — HAPP, base64 v2ray, Clash, sing-box; выбор по?target=иUser-Agent, по умолчанию HAPP, чтобы уже выданные ссылки не сломались.1d60f9b):?target=v2rayотдавал HAPP-тело из-за того, что формат выбирался по флагу от User-Agent; правило, выкидывавшее xhttp-маршрут из HAPP, снято — оно молча удалялоxhttp-pq.subscription-userinfoотдаётся во всех форматах; маршруты, которые конкретный клиент не умеет, пропускаются с причиной, а не ломают конфиг целиком.DNS-01 и ACME
7ba2e02): цепочка «существующий серт → http-01 по IP → DNS-01 →http_tls». Это выход для хостеров, фильтрующих:80.repair-dns01(d82c622): перевод уже деградировавшейhttp_tls-подписки на домен, идемпотентно.dns01-test(87017fe): отладка на проде выжигает квоту Let's Encrypt (5 сертификатов на набор идентификаторов в 168 ч), поэтому появился--test-cert-контур и проверочный выпуск со своей линией и без побочных эффектов.Лимиты трафика профилей
c9e52b5): loopbackdokodemo-door, дельты по watermark — рестарт Xray не теряет посчитанное. При превышении клиент снимается с инбаунда и восстанавливается при поднятии лимита; срок и лимит кросс-проверяются.a630f27): чип в карточке профиля и диалог лимита.Мониторинг и Telegram-алерты
d7b97b6:monitor-checkкаждые 15 минут — юниты, установленные бэкенды, приближение и истечение срока, упёршиеся в лимит профили, живость подписки, деградация TLS-режима, ежедневная SNI-проба, ребуты. Дедупликация, ретраи и «не отправлять чаще раза в N» вmonitor-state.json.f464914): поверхность алертов осталась только в терминале — токен бота ходит через один вход, а не через два.channel-test9e3e2dd: проверка каналов настоящим клиентом Xray (SOCKS5 из параметров ссылки + реальный HTTP-запрос через туннель). Не требует Let's Encrypt, покрывает PQ. Заменил догадки измерением: первый прогон после рестарта платит прогрев (~5 с на маршрут), второй — ~150 мс Reality и ~230 мс PQ.UI/UX десктоп-приложения
21bd82f): вместо одной длинной прокрутки — три секции; панели скрываются атрибутом, поэтому введённое не теряется.d9efc21): создание осталось на месте, оба экрана живут в одном компоненте — переключение не поднимает вторую SSH-сессию. Поиск по имени/SNI/домену подписки, фильтр «Все/Активные/Отключённые», счётчик считает карточки, а не профили.5f32d1a): сворачиваемая, иконка-сертификат, режим бейджем, три значения в моно-боксах на всю ширину колонки.ef26266,77b7036): IP и SSH-порт в одной строке, PQ и DNS-01 в одном блоке «Дополнительно», подсказка о host key — футер карточки прогресса. Правая панель (шаги и консоль) не тронута.9b4e32b,6a90a3e): текст, QR и гранты в окне профиля; кнопка «Готово» зелёная, как выполненный шаг деплоя (e0d9401).cbf5a1e): страница «Ключи» больше не печатает на каждый профиль три почти одинаковых URL. По умолчанию — основной набор (подписка HAPP/v2ray + VLESS-ссылки, как раньше), а форматы добавляет кнопка «Форматы Clash / sing-box» в шапке; выбор запоминается вlocalStorage, кнопка появляется только если сервер реально отдал ссылки форматов.Стабильность и безопасность
dns01-testпроверяют домен и токен без расхода прод-квоты Let's Encrypt;dns01-statusопределяет контур по сертификату и понимает обе формы записи хука; короткоживущий IP-сертификат не конфликтует с дистрибутивнымcertbot.timer;repair-dns01не двигает порт подписки.0600root и в GUI не сохраняются; токен Telegram-бота не печатается и настраивается только из терминала.safe_restart_xrayвалидирует конфиг и откатывается при ошибке; UFW-правила и nginx-vhosts ведутся по манифесту.Исправления, найденные на живых серверах
profile-pqвыбрасывал базовый маршрут профиля, который просили расширить: одно-маршрутная форма разворачивается вroutes[]перед добавлением PQ.profile-deleteоставлял осиротевшие инбаунды: набор портов считается как union верхнего.portи всехroutes[].port.quickstart --pqбыл мёртвым кодом: блок стоял внутри ветки ошибки, а статический тест это не ловил.dns01-statusпоказывалdeploy_hook:falseна правильно настроенном домене (renew_hookvsdeploy-hook);repair-dns01молча менял порт подписки; дистрибутивныйcertbot.timerвоевал с короткоживущим IP-сертификатом; certbot выбирался черезcommand -v, а не по наличию нужного плагина.policy.levels."0".statsUserUplink/Downlink; добавлена ремонтная миграция для серверов, где ранний маркер блокировал бэкфилл.modules-load.d), а карточка говорила «активно»;S1 == S2мог уехать в прод; подпись и цвет карточки брались из разных полей.inspect --jsonрапортовал все бэкенды как неустановленные;xrayebator-uninstallоставлял включённые бэкенды.label: $label(ключевое слово) — PQ-маршрут не создавался вообще на Ubuntu 22.04 / Debian 12; добавленtest-jq-keyword-compat.sh.Dropdown; «действует до» приезжало поздно и дёргало карточку; новая строка интерфейса могла оставаться сырым ключом до перезапуска; ссылки форматов исчезали без SSH.Миграции
mlkem_keys,subhttp_formats_2026,subhttp_v2ray_pq_2026,subhttp_hysteria2_2026,subhttp_traffic_2026,traffic_stats_2026,traffic_user_stats_2026,traffic_timer_2026,expire_timer_2026,awg_module_2026,alerts_monitor_2026,bypass_routing_2026. Все идемпотентны и зарегистрированы вmain_menu,quickstartи (где нужно)happ-setup— паритет проверяется статически.Документация
docs/на трёх языках (30 файлов):configuration,desktop-gui,architecture,testing,troubleshooting,security, README (en/ru/zh).docs/releases/v0.7.0.md(+.en.md): разделы «Изменения с 0.6.5-beta.2», «Что нового», «Стабильность и безопасность», «Насколько это проверено»;package.json—0.7.0, CHANGELOG порезан на[0.7.0]+ пустой[Unreleased].uninstall.sh; проверки ссылок, якорей, таблиц и кодировки — в.superpowers/live/.Тесты
pq-routes,subscription-formats,dns01-certbot,quickstart-tls-fallback,traffic-limit-cli,alerts-monitor,hysteria2-lifecycle,awg-lifecycle,backend-registry,channel-test-cli,happ-client-routing,sni-reachability,legacy-profile-port-sync,jq-keyword-compat.Проверки
bash validation/test-*.shв WSL (Linux): 42/42.bash -nдляxrayebator,install.sh,update.sh,uninstall.sh: чисто.npm run typecheck— чисто,npm run build— собирается,npm test— 171/172 (единственный сбой —shell-command.test.ts, который спавнит POSIX/bin/sh; в Windows такого нет).http_tls-фолбэк на хостере с фильтрацией:80. На всех трёх — проверка всех маршрутов настоящим клиентом Xray (HTTP 200, ≈170–330 мс), четырьмя форматами подписки снаружи, установкой и грантами Hysteria 2 / AmneziaWG.Ключевые изменённые файлы
xrayebator(+14 614/−8 451) — два бэкенда, PQ на всех транспортах, DNS-01, форматы подписки, трафик, алерты, 12 миграцийsrc/renderer/src/pages/ServerSettings.tsx(+2 333/−206) — разделы настроек, страница профилей, бэкенд-боксы, диалогиsrc/renderer/src/pages/ServerSettings.module.css(+1 157/−192)CHANGELOG.md(+571/−1)validation/test-dns01-certbot.sh(+502),validation/test-alerts-monitor.sh(+496),validation/test-traffic-limit-cli.sh(+404)docs/{,ru/,zh-CN/}configuration.md(+460…472 в каждой локали)src/shared/types.ts(+389/−2) — контракты новых CLI и IPCsrc/renderer/src/pages/ServerKeys.tsx(+364/−22) — форматы подписки на странице ключейОговорки
shell-command.test.tsлокально на Windows не выполняется: он спавнит POSIX/bin/sh, которого в Windows нет. На Linux выполним, к коду отношения не имеет.channel-testна сервере.