docs(xray-proxy): добавлена спецификация консольного клиента
- Зачем: - нужен зафиксированный контракт переносимого Xray-клиента для удалённых Linux-систем. - Что: - описаны Python CLI, отдельный Bash-модуль и совместимость с внешним Hiddify. - закреплены преобразование VLESS-ссылок, версии Xray и обработка TLS. - определены жизненный цикл в tmux, безопасность файлов и проверки реализации. - Проверка: - выполнена проверка `git diff --cached --check`.
This commit is contained in:
1 parent
3b7ebd29c8
commit
716fb7f5dd
1 file changed
+455
@@ -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 и ключи никогда не входят в репозиторий.
|
||||||
Reference in new issue
Block a user