Skip to content

release: promote актуальный dev в main - #25

Merged
Ap3x0s merged 193 commits into
mainfrom
dev
Sep 19, 2026
Merged

Ap3x0s merged 193 commits into
mainfrom
dev

Conversation

@Ap3x0s

@Ap3x0s Ap3x0s commented Sep 19, 2026 •

Copy link
Copy Markdown
Collaborator

Резюме

Promotion PR после принятия PR #24 в howdeploy/dev. Переносит актуальное состояние dev в стабильную howdeploy/main и объединяет Bash hardening, Electron Desktop GUI, CLI, тесты, CI и синхронизированную документацию.

Источник: howdeploy/dev (42908e0, merge PR #24)
Цель: howdeploy/main (0451a7e на момент подготовки promotion)
Масштаб: 193 commits, 171 files changed относительно старого main.

Что добавлено относительно старого howdeploy/main

Bash/Xray runtime

  • interrupt-safe installer с --check, --resume, --fresh;
  • IPv6-aware DNS и network fallbacks;
  • UFW SSH-port safety и ownership manifest;
  • root-owned manager state, service-account boundary и privilege hardening;
  • safe profile/inbound lifecycle, transaction backups и rollback;
  • HAPP subscription server с token-protected geo assets и managed routing;
  • schema-v3 multi-route HAPP profile: семь маршрутов, включая xhttp-legacy, шесть публикуемых VLESS маршрутов;
  • IPv4 IP-TLS quickstart, domain/local modes, certbot reuse/renewal и nginx rollback;
  • CLI profile management, fingerprint/SNI/port changes, bypass, cascade и self-steal;
  • IPv6-only handling, legacy migrations и удаление deprecated AdGuard Home path.

Electron Desktop GUI

  • новая активная реализация Electron + React + TypeScript в src/;
  • Dashboard, guided Add Server deployment, Server Keys и Server Settings;
  • SSH password/private-key auth, direct root/sudo, host-key TOFU pinning;
  • profile CRUD, transports, fingerprint/SNI/port dialogs, subscription refresh, VLESS links and QR;
  • RU/EN/ZH renderer i18n;
  • PySide6 implementation moved to archival gui-legacy/ with separate tests and packaging.

Tests and CI

  • 24 Bash validation scripts, including readiness, privilege, quickstart, CLI and rollback regressions;
  • 9 Electron Vitest unit files plus typecheck/build checks;
  • legacy PySide6 pytest/ruff workflow;
  • Linux Bash validation workflow;
  • Electron release workflow for Windows, macOS Intel/Apple Silicon and Linux packaging paths.

Documentation

  • README.md, README.ru.md, README.zh-CN.md synchronized with the current runtime;
  • architecture/configuration/security/troubleshooting/testing references synchronized in EN/RU/ZH;
  • new docs/desktop-gui.md, docs/ru/desktop-gui.md, docs/zh-CN/desktop-gui.md;
  • canonical production install/update links standardized on howdeploy/Xrayebator;
  • explicit update-path, HAPP, permission, UFW, OS, CI and legacy GUI limitations.

Compatibility and operational notes

  • The installer requires Bash, root/sudo, apt-based Debian/Ubuntu-like system and systemd. Tested matrix: Debian 12/13 and Ubuntu 22.04/24.04.
  • IP-TLS quickstart is IPv4-only and currently provisions its non-interactive listener on 8443; IPv6-only VPS should use domain mode.
  • xrayebator update, xrayebator update <branch> and xrayebator-update [branch] are distinct commands. Electron GUI uses the manager self-update path, not the full terminal updater.
  • happ-setup is a reduced existing-install path and does not have quickstart's broad migration coverage. Existing subscription markers may be reused and require operator verification.
  • Most manager state is root-protected, but generated metadata/rollback paths can have narrower ownership/mode exceptions.
  • gui-legacy/ is archival; src/ is the active desktop app. gui-v* and v* workflows are distinct.
  • Uninstall cleanup is path/name based for product nginx vhosts, not a universal ownership manifest; domain ACME webroot may remain.
  • Runtime mutations and lifecycle install/update paths have different validation/restart/rollback implementations; verify Xray, DNS and subscription after lifecycle updates.
  • Third-party PR feat(happ): подстановка geo-URL и LastUpdated в переопределении routing #22 and PR feat(happ): split-профиль клиента из тех же наборов, что и bypass #23 are not included in this promotion.

Verification

Local verification on the promotion source:

  • bash -n xrayebator install.sh update.sh uninstall.sh — PASS;
  • 24/24 validation/test-*.sh — PASS locally;
  • npm run typecheck — PASS;
  • npm run build — PASS;
  • Windows npm test: 38/39 due to known POSIX /bin/sh test; Linux workflow is source of truth;
  • git diff --check and stale-claim scan — PASS;
  • PR feat: принять вклад форка в howdeploy/dev #24 CI: Bash validation, Electron tests and Windows/macOS packaging checks green.

This PR promotes the already-reviewed dev branch to main. It intentionally does not merge automatically; maintainer review is required.

Что это добавляет обычному пользователю

Если объяснять без технических терминов, этот PR превращает Xrayebator из большого терминального Bash-скрипта в более цельный продукт, которым можно пользоваться и без постоянной работы в консоли.

Появляется новое полноценное GUI

Добавляется новое десктопное приложение на Electron. В нём можно:

  • увидеть все свои VPS в одном списке;
  • добавить новый сервер через пошаговый мастер;
  • увидеть, доступен ли сервер;
  • получить ссылку подписки и QR-коды;
  • создавать, удалять и просматривать профили;
  • менять SNI, порт и fingerprint;
  • подключаться к серверу по паролю или SSH-ключу;
  • работать напрямую через root или через sudo;
  • выбрать русский, английский или китайский язык интерфейса.

Старое PySide6-приложение не исчезает бесследно, но теперь считается legacy-версией. Основным приложением становится новое Electron GUI.

Сервер становится проще установить и восстановить

Установка теперь лучше переживает реальные проблемы: временный обрыв, повторный запуск, IPv6-only VPS, занятый SSH-порт, недоступный GitHub или сбой выпуска сертификата.

Появились безопасные режимы продолжения установки (--check, --resume, --fresh), проверки конфигурации Xray перед рестартом, резервные копии и возврат к рабочему состоянию при ошибке.

Подписка сразу содержит набор запасных маршрутов

Вместо одной ссылки пользователь получает одну подписку HAPP, внутри которой есть несколько вариантов подключения: XHTTP, TCP Vision, uTLS, gRPC и другие совместимые маршруты.

Если один транспорт заблокирован конкретной сетью, можно переключиться на другой маршрут, не разворачивая сервер заново. Профиль содержит семь маршрутов, а обычная HAPP-выдача публикует шесть наиболее совместимых вариантов.

Исправлены практические баги

В этот promotion входят исправления, которые заметны не только разработчику:

  • установщик больше не ошибается на временном JSON-файле конфигурации;
  • GUI показывает настоящую причину ошибки установки, а не только общий сбой;
  • корректнее обрабатываются нестандартный SSH-порт и правила UFW;
  • снижен риск потерять доступ к серверу при включении firewall;
  • улучшена работа IPv6-only VPS;
  • добавлены проверки и откаты для nginx, certbot и подписки;
  • исправлены переносы профилей между портами;
  • изменения SNI, порта и fingerprint лучше синхронизируются с подпиской;
  • старые и нерабочие маршруты обнаруживаются и не выдаются клиенту;
  • добавлены проверки для каскада, bypass, HAPP и миграций;
  • документация теперь соответствует реальному поведению программы.

Безопасность стала понятнее и строже

Конфигурация, ключи, профили и управляющие скрипты защищены от записи сервисным пользователем Xray. В GUI поддерживается проверка SSH host key: если ключ сервера неожиданно изменился, приложение остановит операцию и попросит подтвердить замену.

Пароли SSH, sudo-пароли и содержимое приватного ключа не сохраняются. При этом сама ссылка подписки и VLESS-ссылки являются чувствительными данными: их нужно защищать, а после утечки отзывать подписку.

Что всё ещё остаётся терминальным

Новое GUI не пытается скрыть все возможности Bash. Расширенные функции — bypass, probe-test, revoke подписки, HAPP setup, cascade, self-steal, сервисные логи и интерактивное меню — по-прежнему выполняются через терминал.

Также остаются операционные ограничения: IP-TLS quickstart рассчитан на IPv4, а после серьёзного lifecycle-обновления рекомендуется проверить состояние Xray, DNS и подписки.

Иными словами, PR #25 приносит в main не только «ещё несколько исправлений», а новый пользовательский слой, более предсказуемую установку, набор резервных маршрутов, управление сервером из GUI и значительно более полную документацию. При этом promotion оставлен открытым для финального review владельца HowDeploy.

howdeploy and others added 30 commits December 7, 2025 11:33
…), новый маршрут для теле2 в белых списках, обновил SNI
- BUG-1: tcp-xudp нормализация в change_port_menu (ложная ошибка "порт занят")
- BUG-2: open_firewall_port при добавлении клиента в existing inbound
- BUG-3: open_firewall_port активирует UFW если он неактивен
- BUG-4: change_port_menu извлекает клиента при shared inbound вместо переноса всего
- BUG-5: delete_profile удаляет UUID только из конкретного inbound по порту
- BUG-6: close_firewall_port не закрывает порт при ошибке jq
- BUG-7: add_inbound проверяет совместимость транспорта
- BUG-8: install.sh проверяет ошибки UFW и валидирует активность
- BUG-9: change_port_menu обновляет fingerprint при переносе в existing inbound

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
CRLF в Windows-чекауте ломал bash-скрипты на сервере (символ \r). .gitattributes нормализует LF для всех скриптов. .gitignore дополнен артефактами сборки (exe/dmg) и служебными файлами.
На Windows-чекауте core.autocrlf превращает LF→CRLF; sftp.put заливал их бинарно, и bash на сервере падал (символ \r, сломанный shebang). Добавлен upload_text() который стрипает \r; deploy.py загружает install.sh/xrayebator через него. TestDeploy FakeSSH расширен upload_text (обновлён контракт).
Unix-only модули grp/pwd импортировались на верхнем уровне service.py, из-за чего любой импорт пакета падал на Windows. Импорты перенесены в authorized_peer() и main() — helper всё равно работает только на Linux, но импорт пакета/тестирование/пакAGING теперь валидны и на Windows.
…/opt)

CORE_BINARY ссылался на /usr/lib/xrayebator-gui/xray, но install-helper.sh ставит ядро в /opt/xrayebator-gui. unit явно передаёт --core, но ручной запуск helper без --core падал. Теперь константа = /opt.
…сле деплоя

Если D-Bus secret service недоступен (headless Linux), set_password бросал исключение в слоте _deployment_finished — сервер попадал в JSON, но _reload_servers не вызывался, пользователь не видел сервер. Теперь ошибка keyring молча глотается (сервер добавлен, пароль запомнится позже).
result[subscription_url] падал с KeyError если сервер вернул другой JSON-контракт. Теперь .get() — если URL пустой, сервер не добавляется, в лог пишется понятное сообщение.
…ll при фейле загрузки

Если curl для update.sh/uninstall.sh падал, mv не выполнялся, но ln -sf создавал symlink указывающий на несуществующий /usr/local/etc/xray/scripts/{update,uninstall}.sh. Теперь symlink создаётся только если файл есть.
curl|bash @ remove не проверялся — при недоступности GitHub выводило «✓ Xray-core удален» хотя бинарь оставался. Теперь: -c на curl, проверка rc, явное удаление /usr/local/bin/xray + share, drop-in /etc/systemd/system/xray.service.d, symlink xrayebator-update/uninstall, subhttp.sh, xrayebator-sub.service, /var/log/xray, пользователь xray, UFW-правила. НЕ трогаем весь системный журнал — только -u xray.
…н; стирать .warned при отмене; бэкап в backups/

Тавтологичная проверка (-L || -f) приводила к dangling symlink, если stub отсутствовал. Теперь: стираем только если systemd-resolved активен, stub существует и resolv.conf выглядит AdGuard-managed. При отмене чистим и .session, и .warned. BACKUP_DIR -> backups/update_<ts> (как у xrayebator).
Было: quickstart создавал одномаршрутный профиль happ (1 xhttp маршрут), HAPP показывал "route not shown / country n/a". Стало: используется _happ_ensure_default_multiroute_profile() — тот же механизм что happ-setup (общий источник истины). Возвращаем routes=N в JSON.
Собственный Quick Start в README:224 уже говорил про пункт 6, но раздел 45 упоминал 9 (self-steal stub). Исправлено в трёх языках одновременно.
Было: /usr/local/bin/subhttp и backups/<timestamp>/ (подпапки). Стало: /usr/local/bin/subhttp.sh и backups/config_<ts>_<op>.json (плоские файлы) — соответствует реальному поведению backup_config().
test-bbr-removal-migration.sh, test-legacy-udp443-migration.sh, test-main-menu-numbering.sh существовали, но не были задокументированы.
…setup

quickstart используется десктопным GUI (one-shot deploy); happ-setup — повторный вход для HAPP multi-route профиля. Оба возвращают JSON с subscription_url.
Размер 2500→9341 строк. Было «no automated tests» — фактически есть validation/ (16 bash-тестов) и gui/tests/ (15 pytest). Описаны CLI-команды (quickstart/happ-setup). Исправлена ошибка про jq -s (это bash -s, не jq -s).
… quit

- Добавлен QProgressBar (indeterminate marquee) — показывается при всех busy-стадиях, чтобы UI не выглядел зависшим на долгих шагах деплоя.
- Статус-метка получила цветовую индикацию (зелёный=connected, красный=error, жёлтый=busy).
- Логи теперь с таймстампами и цветовой маркировкой по ключевым словам.
- Race condition: _toggle_connection/_switch_route/_switch_profile мгновенно блокируют UI до старта OperationThread — двойной клик не создаст два потока.
- _quit() больше не блокирует GUI на 5+ секунд — disconnect идёт в OperationThread.
- Traceback добавляется в error messages (OperationThread.run).
- Убраны утечки секретов при ошибке деплоя (redact_log_line применяется)
- Preflight: проверка bash (sh-only без *{}*), прав root (вывод), ОС (apt-based only), systemd (ясная ошибка для OpenVZ/LXC/Docker).
- apt: ошибки не молча подавляются, предлагается DNS-fix, вывод tail.
- Reality ключи: не перегенерируются если уже существуют (resume-safe).
Добавлена state-машина с маркерами /usr/local/etc/xray/.install_step_N_ok.
При повторном запуске после прерывания install.sh продолжает с того самого шага,
не ломая уже созданное (Reality/VLESS ключи, config.json, Xray-core).

Команды:
  bash install.sh --check   — показать статус всех 10 шагов
  bash install.sh           — auto-resume если половина установлена
  bash install.sh --resume  — принудительно resume без диалога
  bash install.sh --fresh   — сбросить маркеры, начать с нуля
QTextEdit.appendHtml() does not exist in PySide6 (verified empirically:
hasattr == False; only QTextBrowser.append() supports HTML strings).
Any call to _append_log (16 places) would crash with AttributeError
on the very first log message.

Also: text was inserted into HTML unescaped, so any <>& in bash/ssh/
paramiko output (e.g. vless:// URL with query params) would be parsed
as rich-text by Qt and mangled visually. html.escape() fixes both.

Impact: GUI was unusable before this commit — crashed on first deploy.
Leftover single-letter command from earlier auto-edit session. Bash treats
'c' as a (non-existent) command; with no set -e this only logs
'bash: c: command not found' to stderr, but pollutes CI logs and
masks real error signals when grep'ing for failures.
Reality keys (.private_key/.public_key) at :618 already had the guard.
VLESS Encryption keys at :733 did NOT — so re-running install.sh
(--resume / --fresh / step 5 retry) would regenerate them and silently
orphan every existing PQ-enabled profile (client encryption string no
longer matches server decryption).

Symmetric with the Reality guard: skip when both files exist and
decryption matches the expected mlkem768x25519plus. prefix.
main_menu runs 17 marker-driven migrations on first interactive launch.
quickstart previously ran only 8 + skipped mlkem_keys_generated,
xhttp_default_2026, subscription_tokens_2026, happ_legacy_xhttp_route_2026,
xhttp_route_path_repair_2026, dead_stealth_routes_pruned_2026,
sni_list_2026, bypass_routing_2026, and all 6 subhttp_* migrations.

Impact: on an EXISTING VPS (no fresh install.sh):
- mlkem_keys_generated missing → add_inbound with pq_enabled=true fails
  → _happ_ensure_default_multiroute_profile partially creates 3-4 of 7
    routes, leaving profile in inconsistent state.
- subscription_tokens_2026 missing → profiles have no sub_token,
  subscription_url broken.

Now quickstart runs the full 17-migration set (still ignores
bypass_routing_2026 + sni_list_2026 since they require interaction,
and the remaining subhttp_* family which is menu-driven).
subscription_url was hardcoded to :8443 without checking the actual
subscription markers. If the VPS had previously been set up with
install_subscription_ip_tls on :443 (or any other port via
_select_subscription_port), quickstart would emit a broken URL — client
gets connection refused.

Now uses the canonical helper _subscription_base_url() that reads
.subscription_port and .subscription_domain, only falling back to
https://<ip>:8443 when markers are absent.
Adds named helpers to install.sh, update.sh, xrayebator. Three identical
inline blocks across install.sh:751, update.sh:44, update.sh:884 are
replaced with \ — kills the naming drift
(local query_strategy vs global QUERY_STRATEGY) and gives future
migrations a single canonical way to query IPv6 availability.

Behavior unchanged — only refactor. Locked separately from the
uninstall_adguard_home / migrate_config_optimization fix to allow
independent revert if the helper itself regresses.
…ator

Mirrors the helpers already added to install.sh/update.sh. Other xrayebator
callers (next commits) will use these instead of hardcoding UseIPv4,
so IPv6-only VPS doesn't break DNS for AdGuard-removal path or future
config migrations.
Ap3x0s and others added 29 commits August 16, 2026 22:36
…не вызывались ни из preload, ни из renderer
…uickstart добирает миграцию subhttp_managed_happ_routing_2026
Вклад в dev: Electron GUI, CLI-команды, аудит и актуализация под new-main владельца
Синхронизация main с dev: влит PR #18 и последующий харднинг SSH-доступа из upstream/dev (shell-command, ssh-access, SshAccessForm, vless + unit-тесты). Содержимое main после мержа побайтово равно dev.
mktemp даёт имя config.json.tmp.XXXXXX, а Xray определяет формат конфига по
последнему расширению файла — там случайный суффикс, не json. Ядро отвечает
«Failed to get format», grep не находит «Configuration OK.», и шаг 6 падает с
кодом 1 на ЛЮБОЙ чистой установке. Проверено на живом сервере: те же байты с
расширением .json проходят, без — нет; поведение одинаково на 25.9.5 и 26.3.27,
то есть это не регрессия нового ядра.

Флаг -format json работает на обоих ядрах и уже является соглашением проекта:
xrayebator → safe_restart_xray (строка 1670) вызывает так же.

Затрагивает только dev: блок предварительной валидации внесён c52d863
(«close dev-to-main readiness blockers», 2026-08-11) и отсутствует в main.
Deployer при коде != 0 брал только stderr. Но install.sh печатает статус и все
«✗ …» в stdout, а в stderr попадает шум: прогресс-бар curl и
«TERM environment variable not set.» от clear(). В итоге вместо
«config.json не прошёл валидацию» пользователь видел бар на 100% и не мог
понять причину.

extractInstallFailure() снимает ANSI, ищет последнюю строку с «✗» в
объединённых stdout+stderr, иначе берёт хвост вывода. Путь quickstart так уже
делал — теперь оба согласованы.
Синхронизация main с dev поверх фикса установки.

В dev: 98eac09 (валидация временного config.json с -format json) и
d3a55a8 (GUI показывает настоящую причину провала install.sh).
Содержимое main после мержа побайтово равно dev.
Включает Electron GUI, CLI, Bash hardening, тесты и синхронизированную документацию.
@Ap3x0s
Ap3x0s merged commit 045f6f0 into main Sep 19, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants