From 716fb7f5dd4ae27e4c6efc3154d66d493aeb4510 Mon Sep 17 00:00:00 2001 From: Dmitry Dementev Date: Thu, 16 Jul 2026 00:06:10 +0300 Subject: [PATCH] =?UTF-8?q?docs(xray-proxy):=20=D0=B4=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D0=B0=20=D1=81=D0=BF=D0=B5=D1=86=D0=B8?= =?UTF-8?q?=D1=84=D0=B8=D0=BA=D0=B0=D1=86=D0=B8=D1=8F=20=D0=BA=D0=BE=D0=BD?= =?UTF-8?q?=D1=81=D0=BE=D0=BB=D1=8C=D0=BD=D0=BE=D0=B3=D0=BE=20=D0=BA=D0=BB?= =?UTF-8?q?=D0=B8=D0=B5=D0=BD=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Зачем: - нужен зафиксированный контракт переносимого Xray-клиента для удалённых Linux-систем. - Что: - описаны Python CLI, отдельный Bash-модуль и совместимость с внешним Hiddify. - закреплены преобразование VLESS-ссылок, версии Xray и обработка TLS. - определены жизненный цикл в tmux, безопасность файлов и проверки реализации. - Проверка: - выполнена проверка `git diff --cached --check`. --- .../2026-07-15-headless-xray-proxy-design.md | 455 ++++++++++++++++++ 1 file changed, 455 insertions(+) create mode 100644 docs/specs/2026-07-15-headless-xray-proxy-design.md diff --git a/docs/specs/2026-07-15-headless-xray-proxy-design.md b/docs/specs/2026-07-15-headless-xray-proxy-design.md new file mode 100644 index 0000000..a682f30 --- /dev/null +++ b/docs/specs/2026-07-15-headless-xray-proxy-design.md @@ -0,0 +1,455 @@ +# Headless Xray-прокси для агентских CLI + +Дата: 2026-07-15 + +Статус: дизайн утверждён, реализация не начата + +## Проблема + +На удалённых Linux-серверах нет GUI-клиента Hiddify. Перед запуском Claude Code +или Codex приходится вручную запускать Xray и выставлять proxy-переменные. +Переменные легко забыть, после чего агент стартует без прокси. + +Нужны привычные команды `pclaude` и `pcodex`, которые не запускают агента, +если явно настроенный прокси недоступен. Решение должно работать как часть этого +репозитория и отдельно от остальных dotfiles. + +## Контекст + +Существующий дизайн +[`pclaude` / `pcodex`](../superpowers/specs/2026-05-19-proxy-cli-launchers-design.md) +задаёт важный контракт: proxy-переменные видны только дочернему процессу, +аргументы и код возврата сохраняются, а недоступный прокси приводит к отказу +до запуска агента. + +Эта спецификация заменяет Linux-часть прежнего дизайна в вопросах расположения +функций, источника настроек и вывода `proxy_on`/`proxy_off`. Windows-часть +прежнего дизайна не меняется. + +Сейчас Bash-функции жёстко используют внешний Hiddify на +`hproxy.local:12334`. Принесённый прототип `scripts/setup-xray-proxy.sh` +умеет установить и запустить Xray из готового JSON, но смешивает установку и +запуск, не разбирает ссылки Hiddify и небезопасно ищет чужие процессы через +`pkill`. + +Xray принимает конфигурационные файлы, а не `vless://`-ссылки. При этом его +SOCKS-вход принимает SOCKS и HTTP на одном порту, поэтому существующая схема +proxy-переменных совместима с локальным Xray без второго порта. См. +[командную строку Xray](https://xtls.github.io/en/document/command.html) и +[SOCKS-вход](https://xtls.github.io/en/config/inbounds/socks.html). + +## Цели + +- Дать headless Linux-серверу управляемый пользовательский Xray без systemd и + прав root. +- Сохранить явные `pclaude` и `pcodex`, которые нельзя случайно запустить через + другой прокси или без настроенного прокси. +- Принимать одну точечную VLESS-ссылку Hiddify и преобразовывать её в клиентский + JSON Xray. +- Сохранить совместимость Bash-функций с внешним Hiddify. +- Позволить настраивать машинный `NO_PROXY` без правки файлов репозитория. +- Поддержать выборочную установку после `git clone`: через stow, ручные + симлинки или копирование. + +## Не входит в объём + +- Windows, PowerShell, Windows Codex launcher и VS Code Server. +- systemd, автозапуск после перезагрузки, sudo, firewall, TUN и изменение + системной маршрутизации. +- Автоматический выбор между Xray и Hiddify или переход на запасной прокси. +- Одновременное управление несколькими профилями одного Unix-пользователя. +- Универсальный импорт подписок или полного JSON-экспорта Hiddify. +- Протоколы и сочетания за пределами двух явно поддержанных семейств. +- pip, venv и любые сторонние Python-библиотеки. +- Сетевая изоляция голых `claude` и `codex`: как и раньше, гарантию намерения + дают явные команды с префиксом `p`. + +## Рассмотренные варианты + +### Только Bash + +Ближе всего к принесённому прототипу, но URL-кодирование, JSON, проверка схемы, +контрольные суммы и безопасное управление процессом делают Bash-реализацию +хрупкой и плохо тестируемой. + +### Bash-контроллер и отдельный Python-преобразователь + +Оставляет жизненный цикл в Bash, а разбор VLESS переносит в Python. Граница +между двумя реализациями усложняет ошибки, тесты и единый источник настроек. + +### Один Python CLI и тонкий Bash-модуль + +Python отвечает за данные, установку Xray, процессы и запуск дочерних команд. +Bash нужен только там, где необходимо изменить окружение текущей shell-сессии. +Это выбранный вариант. + +### Не создавать менеджер + +Можно хранить готовые JSON и вручную запускать Xray в tmux. Вариант почти не +требует кода, но не решает основную боль: безопасный запуск `pclaude` и +`pcodex` из точечной ссылки Hiddify. + +## Рекомендуемое решение + +### Границы компонентов + +`xray-proxy` — один исполняемый файл на Python 3.8+ без внешних библиотек. Он +отвечает за установку Xray, запуск и остановку процесса, диагностику, +преобразование VLESS и запуск произвольной дочерней команды через прокси. + +Переносимый `shell.bash` — единственный владелец `proxy_on`, `proxy_off`, +`pclaude`, `pcodex`, `myip` и внутренних proxy-помощников. При реализации они +удаляются из `bash/.bash_functions`, чтобы результат не зависел от порядка +`source`. `update_hproxy` остаётся там как отдельная WSL-утилита управления +`/etc/hosts`, а не часть переносимого интерфейса прокси. Модуль не разбирает +машинный конфиг самостоятельно: + +- `pclaude` и `pcodex` делегируют запуск форме + `xray-proxy run -- COMMAND ARG...`, где `--` отделяет параметры клиента; +- `proxy_on` получает от `xray-proxy env` проверенный набор proxy-переменных и + применяет его к текущей Bash-сессии; +- `proxy_off` очищает верхний и нижний регистр proxy-переменных. + +Общий `bash/.bash_functions` и модуль Xray не зависят друг от друга. В составе +dotfiles `.bash_custom` подключает их двумя независимыми строками. Без +остальных dotfiles пользователь добавляет только один `source` модуля Xray в +`.bashrc`. Для неинтерактивных сценариев и других оболочек остаётся прямой +`xray-proxy run`. + +`xray-proxy env` сначала выполняет ту же проверку доступности, что и `run`, а +затем печатает восемь строк `KEY=value`: HTTP, HTTPS, ALL и NO_PROXY в верхнем +и нижнем регистре. Переносы строк и NUL в значениях запрещены. `shell.bash` +сначала целиком получает успешный ответ, затем разбирает строки по первому `=`, +проверяет точный белый список ключей и отсутствие повторов, и лишь после этого +экспортирует значения через `printf -v`. `eval` не используется, частичного +изменения окружения при ошибке нет. + +У `env` stdout зарезервирован только для восьми машинно-читаемых строк. +Предупреждения и ошибки идут в stderr; обычная безопасная сводка этой командой +не печатается. + +Совместимость существующих Bash-команд означает сохранение имён функций, +набора переменных, fail-fast перед `pclaude`/`pcodex` и изоляции окружения +дочернего процесса. Автоматический запрос внешнего IP из `proxy_on` и +`proxy_off` удаляется: он замедляет переключение и смешивает изменение +окружения с диагностикой. Для проверки остаются `myip` и `xray-proxy status`. + +### Два независимых файла настройки + +Машинный файл `~/.config/proxy/config` хранит несекретное описание активного +прокси: явный тип `xray` или `external`, отображаемое имя, адрес, порт и +добавку к `NO_PROXY`. Формат — INI из стандартной библиотеки Python. Если файла +нет, прокси-подсистема сохраняет прежний внешний адрес +`hproxy.local:12334` ради обратной совместимости. + +Секретный `~/.config/xray-proxy/default.url` содержит одну VLESS-ссылку. +Команда запуска принимает другой файл через `-f`. Профили можно хранить в +нескольких файлах, но активным у пользователя бывает только один. +CLI следует за симлинком, но проверяет права конечного файла: доступ группы или +остальных пользователей считается ошибкой с подсказкой выполнить `chmod 600`. +Файл должен содержать ровно одну непустую строку без комментариев и служебного +текста. + +Базовый `NO_PROXY` всегда включает loopback-адреса и узел самого прокси. +Машинная добавка расширяет его локальными IP и доменами. Документация должна +предупреждать, что поддержка CIDR в `NO_PROXY` различается между программами. + +### Явная семантика прокси + +Тип `xray` означает, что `pclaude` и `pcodex` требуют запущенный процесс, +которым управляет `xray-proxy`. Если его нет, агент не запускается, а сообщение +предлагает выполнить `xray-proxy start`. + +Тип `external` означает внешний процесс, например Hiddify. Проверяется только +доступность явно заданного адреса и порта. Ни один тип не переходит на другой +автоматически. + +`start` разрешён только при `type=xray`; при `type=external` он завершается с +подсказкой изменить машинный конфиг. Для Xray адрес машинного конфига обязан +быть loopback-адресом, а порт обязан совпадать с создаваемым входом. Так нельзя +случайно поднять локальный Xray, оставив `pclaude` направленным во внешний +Hiddify. + +`status` описывает именно активный тип из машинного конфига: состояние процесса +и порта для Xray либо доступность адреса для external. `stop` от машинного типа +не зависит и завершает записанный управляемый Xray, если он существует. Поэтому +после явного переключения на external старый локальный процесс можно штатно +остановить. + +HTTP, HTTPS и SOCKS используют один локальный порт, по умолчанию `12334`. +Порт остаётся настраиваемым, чтобы разные Unix-пользователи одной машины могли +запускать свои экземпляры без конфликта. + +Локальный вход Xray слушает только loopback: `127.0.0.1` и, если поддержано +выбранной конфигурацией, `::1`. Он никогда не привязывается к `0.0.0.0`, адресу +сетевого интерфейса или имени узла. Это закрывает вход от удалённой сети, но не +изолирует его от других пользователей того же Linux-узла. Первая версия не +добавляет локальную аутентификацию и предполагает доверенных пользователей +машины. + +### Поддерживаемые VLESS-ссылки + +Первая версия поддерживает две семьи: + +- VLESS с TCP/RAW и REALITY; +- VLESS с XHTTP и TLS, включая HTTP/1.1, h2 и h3 через ALPN. + +Преобразователь сохраняет значимые параметры ссылки, проверяет обязательные +поля и отклоняет неизвестное сочетание целиком. Он не должен молча пропускать +неизвестные или несовместимые параметры. Поддержка REALITY опирается на +[официальную схему REALITY](https://xtls.github.io/en/config/transports/reality.html), +а XHTTP с TLS — на +[схему TLS](https://xtls.github.io/en/config/transports/tls.html). + +Для REALITY преобразователь явно сопоставляет UUID с VLESS `id`, узел и порт с +адресом назначения, `type=tcp` с RAW, `flow` с режимом VLESS Vision, `sni` с +`serverName`, `fp` с `fingerprint`, `pbk` с `password`, а `sid` с `shortId`. +`headerType=none` принимается как нейтральное значение, другое значение +отклоняется. `hiddify`, `host` и `alpn` для TCP/REALITY считаются метаданными и +не меняют Xray JSON; безопасная сводка явно помечает `host` и `alpn` как +неиспользованные. Это не скрытая потеря параметра: в целевой +[схеме REALITY Xray 26.3.27](https://github.com/XTLS/Xray-core/blob/v26.3.27/infra/conf/transport_internet.go#L3591-L3636) +нет полей Host или ALPN. Повтор одного query-параметра считается ошибкой. +Остальные правила соответствуют +[стандарту VLESS-ссылок Xray](https://github.com/XTLS/Xray-core/discussions/716). + +Основной сценарий XHTTP/TLS — одиночная ссылка Hiddify с `type=xhttp`, +`security=tls`, `sni`, `alpn`, `path`, `host`, `encryption`, `fp` и, возможно, +`extra`. UUID, узел и порт преобразуются так же, как для REALITY. Остальные +поля сопоставляются явно: + +- `type=xhttp` задаёт транспорт XHTTP; +- `security=tls` включает TLS; +- `sni`, `alpn` и `fp` задают `serverName`, список ALPN и fingerprint; +- `path`, `host` и необязательный `mode` переходят в настройки XHTTP; +- `encryption` задаёт шифрование VLESS и в первой версии должно быть `none`; +- `extra` URL-декодируется, разбирается как JSON-объект без повторяющихся + ключей и целиком передаётся в одноимённую секцию XHTTP; +- `pcs` и `vcn` для Xray 26.3.27 переходят в `pinnedPeerCertSha256` и + `verifyPeerCertByName`; +- `hiddify=1` и `core=xray` принимаются как метаданные источника и не меняют + JSON; +- `headerType=none` принимается как устаревшее нейтральное поле Hiddify, другое + значение отклоняется; +- fragment после `#` используется только как отображаемое имя профиля. + +Если `mode` отсутствует, преобразователь не придумывает его и оставляет выбор +Xray. Параметры query не зависят от порядка, но не могут повторяться. После +перечисленных полей неизвестный параметр считается ошибкой: он может менять +смысл соединения и требует явного добавления в преобразователь. `path`, `host`, +`mode` и `extra` входят в официальный стандарт ссылок Xray; параметры TLS +`pcs`/`vcn` заменяют удалённый `allowInsecure`: +[стандарт VLESS-ссылок](https://github.com/XTLS/Xray-core/discussions/716). + +Перед запуском CLI печатает компактную безопасную сводку: сервер, порт, +транспорт, защиту, SNI, ALPN, Host, путь, fingerprint и локальный порт. Обычный +режим не выводит подробный журнал Xray: `-v` добавляет информационные сообщения, +а `--debug` включает подробную диагностику. UUID, REALITY password/public key, +short ID и другие чувствительные значения выводятся только сокращённо. +В создаваемом JSON журнал доступа отключён, а уровень ошибок по умолчанию — +`warning`. + +Hiddify Manager может добавлять `allowInsecure=true` и `insecure=true` даже к +профилю с действительным публичным сертификатом — этот дефект описан в +[hiddify/Hiddify-Manager#5438](https://github.com/hiddify/Hiddify-Manager/issues/5438). +Современный Xray больше не поддерживает `allowInsecure`; удаление зафиксировано +в [релизе 26.2.6](https://github.com/XTLS/Xray-core/releases/tag/v26.2.6). +Поэтому преобразователь принимает эти поля, но для Xray 26.3.27 не переносит +их в JSON и предупреждает, что сертификат будет проверен обычным способом. +Соединение после этого может успешно заработать или завершиться ошибкой +сертификата; отдельного раннего отказа нет. + +При явно выбранном Xray 25.12.8 преобразователь сохраняет +`allowInsecure=true` и предупреждает одновременно об отключённой проверке +сертификата и об использовании старой совместимой версии. Автоматический откат +с 26.x на 25.12.8 запрещён. Новые `pcs`/`vcn` в режиме 25.12.8 отклоняются как +несовместимые вместо попытки угадать их старое представление. + +### Установка Xray + +`xray-proxy install` — отдельная явная операция. Она устанавливает Xray в +`~/.local/bin`, скачивает официальный архив и файл контрольной суммы, затем +проверяет SHA-256. Версия по умолчанию зафиксирована в коде клиента на текущем +на момент реализации стабильном релизе 26.3.27. Пользователь может явно указать +другую версию через `--version`; произвольная версия не становится заявленной +как поддерживаемая только из-за успешной установки. Официальные релизы +публикуют необходимые файлы `.dgst`: +[релиз 26.3.27](https://github.com/XTLS/Xray-core/releases/tag/v26.3.27). + +Первая версия установщика поддерживает Linux x86_64 и aarch64. Архив +распаковывается во временный пользовательский каталог, проверенный бинарник +сначала запускается с `version`, а затем атомарно переносится в +`~/.local/bin/xray`. Существующий файл другой версии не перезаписывается без +явного `--replace`. После установки CLI проверяет, что `command -v xray` +указывает именно на установленный файл; иначе завершается с подсказкой исправить +`PATH`. `start` всегда показывает фактический путь и версию выбранного бинарника. + +Перед созданием JSON CLI читает фактическую версию из `xray version` и выбирает +только совместимое с ней представление TLS-полей. Версия никогда не выводится +из содержимого VLESS-ссылки. + +Xray 25.12.8 — документированный запасной вариант только для старых Hiddify- +профилей, которым действительно требуется `allowInsecure=true`. Это последняя +версия ветки 25.x, где поле применяется без календарного запрета: +[релиз 25.12.8](https://github.com/XTLS/Xray-core/releases/tag/v25.12.8) и +[реализация TLS 25.12.8](https://github.com/XTLS/Xray-core/blob/v25.12.8/infra/conf/transport_internet.go). + +`start` никогда не скачивает бинарник. Если `xray` отсутствует в `PATH`, команда +завершается с подсказкой выполнить `xray-proxy install`. + +### Жизненный цикл + +CLI предоставляет `install`, `start`, `stop`, `status`, `env` и `run`. Запуск +без подкоманды не устанавливает и не стартует ничего. + +`start` остаётся на переднем плане как небольшой Python-супервизор и занимает +текущую консоль. Он запускает один дочерний Xray без демонизации и без новой +сессии процесса, ждёт его завершения и передаёт ему SIGINT, SIGTERM и SIGHUP. +Ожидаемый способ работы — отдельная панель tmux или другого мультиплексора. +Отсоединение от tmux не останавливает процесс, но закрытие панели или сессии +останавливает Xray. Отдельный постоянный журнал и команда `logs` не нужны: +достаточно истории tmux. + +Супервизор владеет блокировкой, дочерним процессом и служебным каталогом +`~/.local/state/xray-proxy/`. Файл состояния содержит PID и время старта обоих +процессов, путь к бинарнику, порт и безопасный отпечаток профиля, но не VLESS- +ссылку и не её секреты. `status` и `stop` сверяют PID со временем старта и +ожидаемым исполняемым файлом, поэтому повторное использование PID не считается +управляемым процессом. `stop` посылает SIGTERM супервизору, а тот завершает Xray +и очищает состояние. Если супервизор аварийно исчез, `stop` может завершить +только дочерний Xray, идентичность которого подтверждена теми же данными. + +У пользователя может быть только один управляемый экземпляр. Повторный `start` +не заменяет его другим профилем. Для смены профиля нужны явные `stop` и `start`. + +Перед успешным запуском должны пройти разбор ссылки, проверка поддерживаемого +сочетания и `xray run -test`. После запуска процесс должен оставаться живым, а +локальный порт — принимать соединения. Проверка внешнего IP через прокси носит +диагностический характер: её сбой даёт предупреждение, но не останавливает уже +прошедший обязательные проверки Xray. + +Сгенерированный JSON и временные файлы создаются внутри служебного каталога и +удаляются супервизором после завершения Xray. Каталоги создаются с правами +`0700`, файлы с конфигурацией и состоянием — `0600`. Исходная VLESS-ссылка не +попадает в обычный вывод и никогда не передаётся аргументом командной строки. + +`run` никогда не запускает Xray. Для типа `xray` он проверяет PID управляемого +процесса и локальный порт; для типа `external` — только явно настроенный адрес и +порт. Лишь после успешной проверки он выставляет proxy-переменные для дочерней +команды. Поэтому `pclaude` и `pcodex` не имеют скрытого автозапуска. + +### Доставка + +Компоненты образуют отдельный stow-совместимый каталог `xray-proxy/`. Он +доставляет CLI, Bash-модуль и два шаблона: машинного INI и файла VLESS-ссылки. +Реальные пользовательские файлы не управляются stow и не входят в Git. +CLI раскладывается как `~/.local/bin/xray-proxy`, а модуль — как +`~/.config/xray-proxy/shell.bash`. Шаблоны лежат рядом с модулем в подкаталоге +`examples/` и не подменяют настоящие пользовательские файлы. + +В корне этого каталога находится самостоятельный `README.md`. Он является +основной инструкцией клиента и исключается из раскладки stow. README остаётся +рядом с кодом при выборочном клонировании или копировании каталога. Корневой +README репозитория только кратко описывает подсистему и ссылается на него. + +После клонирования репозитория пакет можно подключить отдельно от общего +`install.sh`. README также должен описывать эквивалентные ручные симлинки и +копирование файлов. Полный `install.sh` подключает этот же stow-пакет вместе с +остальными dotfiles и не имеет отдельной реализации. + +## Решения и отклонённые альтернативы + +- Выбран Python 3.8+, потому что стандартной библиотеки достаточно для URL, + JSON, INI, ZIP, SHA-256, HTTP, процессов и тестов. Более новый синтаксис и + сторонние пакеты запрещены ради старых и непредсказуемых серверных систем. +- Выбран INI, а не TOML: модуль + [`tomllib`](https://docs.python.org/3/library/tomllib.html) добавлен только в + Python 3.11, что исключило бы штатный Python 3.10 в Ubuntu 22.04. +- Bash оставлен отдельным тонким модулем, потому что дочерний Python-процесс не + может изменить окружение родительской shell-сессии. +- Машинный прокси и VLESS-ссылка разделены: первый файл описывает локальную + политику shell, второй содержит секрет и меняется независимо. +- Выбран один порт, потому что SOCKS-вход Xray совместим с HTTP и не требует + второго локального слушателя. +- Выбран явный тип прокси вместо автообнаружения. Предсказуемость канала важнее + удобства автоматического перехода. +- Выбрана одна активная конфигурация на пользователя. Именованные процессы и + автоматическое распределение портов отложены до появления реальной нужды. +- Официальный системный установщик отклонён: он требует root и создаёт + systemd-службы. Пользовательская установка соответствует выбранной модели. +- Готовые JSON и полный импорт Hiddify отклонены как основной интерфейс: точечная + VLESS-ссылка проще для пользователя и ограничивает область преобразования. + +## Риски и меры + +- Формат ссылок Hiddify может расширяться. Строгая проверка и явный отказ + безопаснее частично неверного JSON. +- Проверка открытого порта не доказывает работоспособность маршрута. CLI + разделяет обязательную проверку процесса и необязательную внешнюю диагностику. +- VLESS-ссылка содержит чувствительные данные. CLI сокращает секреты в выводе, + не журналирует их и использует закрытые пользовательские файлы. +- Устаревшие Hiddify-поля `allowInsecure` и `insecure` неоднозначны. Xray 26.x + игнорирует их на уровне преобразователя и проверяет сертификат; Xray 25.12.8 + сохраняет их только после явного выбора версии. Оба пути сопровождаются + предупреждением. +- Старые дистрибутивы могут иметь Python ниже 3.8. CLI проверяет версию до + основной работы и сообщает обнаруженную и требуемую версии. +- `NO_PROXY` реализуется программами неодинаково. README рекомендует точные IP + и домены, а CIDR описывает как совместимость best-effort. +- Разные пользователи делят сетевое пространство имён. Настраиваемый порт и + явная ошибка конфликта предотвращают случайный запуск на занятом порту, но + loopback без аутентификации не запрещает намеренное подключение другого + локального пользователя. Это принятое ограничение первой версии. + +## Проверка реализации + +- Модульные тесты разбирают обезличенные ссылки обеих поддерживаемых семей и + все показанные варианты ALPN. Отдельный регрессионный пример повторяет форму + типичной ссылки Hiddify XHTTP/TLS с `hiddify`, `core`, `extra`, + `headerType=none`, `allowInsecure` и `insecure`. +- Неподдерживаемые транспорт, защита, отсутствующие обязательные поля и + неизвестные критичные параметры дают понятный отказ до запуска Xray. +- Сгенерированный JSON проходит `xray run -test` отдельно на Xray 26.3.27 и + 25.12.8 для заявленных совместимыми сочетаний. Интеграционная проверка каждой + версии подтверждает, что один локальный вход принимает HTTP и SOCKS. +- Тесты отдельно проверяют современное удаление устаревших insecure-полей и их + сохранение для явно выбранного Xray 25.12.8. +- Тесты подтверждают сокращение секретов и отсутствие полной VLESS-ссылки в + stdout, stderr, служебных файлах и аргументах дочернего процесса. +- Установка проверяется на корректной и повреждённой SHA-256 без записи вне + пользовательского каталога. +- Управление процессом покрывает повторный запуск, устаревший PID, чужой PID, + повторно использованный PID, передачу сигналов, аварийно исчезнувший + супервизор, очистку файлов, смену профиля, остановку и конфликт порта. +- Сбой внешней проверки IP не отменяет успешный запуск, но отображается отдельно + от состояния процесса и порта. +- `run` сохраняет аргументы и код возврата, не оставляет proxy-переменные в + родительской Bash-сессии и не запускает команду при недоступном прокси. +- `env` не печатает частичный результат при ошибке, а `shell.bash` отклоняет + неизвестные и повторные ключи без `eval` и без частичного экспорта. +- Регрессионные Bash-тесты подтверждают прежнее поведение Hiddify при отсутствии + машинного конфига, оба регистра proxy-переменных, настраиваемый `NO_PROXY` и + отсутствие автоматического запроса внешнего IP в `proxy_on`/`proxy_off`. +- Ручная проверка выполняется минимум на Ubuntu с Python 3.10 и на одной целевой + удалённой системе; отдельным пунктом проверяется доступная версия Python в + РЕД ОС перед заявлением её поддержки. + +## Влияние на документацию + +Основная инструкция создаётся как `xray-proxy/README.md` одновременно с +реализацией. Она должна описывать: + +- различие внешнего Hiddify и управляемого Xray; +- выборочная установка через stow, ручные симлинки и копирование; +- создание машинного INI из шаблона и отдельного файла VLESS-ссылки; +- команды управления, `proxy_on/off`, `pclaude/pcodex`; +- настройка `NO_PROXY`, ограничения CIDR и диагностика; +- поддерживаемые семейства VLESS, современную проверку сертификата и явный + запасной вариант Xray 25.12.8 для `allowInsecure`; +- отсутствие systemd, автозапуска и автоматического перехода между прокси. + +Корневой README получает только короткий обзор, пример подключения пакета и +ссылку на полную инструкцию клиента. + +Шаблоны должны содержать только вымышленные значения. Настоящие VLESS-ссылки, +UUID и ключи никогда не входят в репозиторий.