Files
dotfiles/docs/specs/2026-07-15-proxy-client-design.md
ddadmin a30e2eb939 fix(proxy-client): обновлён Xray и исправлен разбор VLESS
Зачем: профиль XHTTP с x_padding_bytes и пустым host не запускался.

Что: Xray обновлён до 26.10.10; добавлены параметры TLS, REALITY и XHTTP,
значения по умолчанию и преобразование старых названий сессии XHTTP.
Обновлены документация и регрессионные тесты.

Проверка: 59 тестов на Python 3.14 и 3.8, Bash-функции и установщик;
16 тестов разбора после уточнения проверки SHA-256. Настоящий Xray
проверил конфигурацию и выполнил HTTPS-запрос через профиль с HTTP 200.
Пользователь подтвердил работу proxy-client start.
2026-10-12 16:21:16 -04:00

228 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# `proxy-client` для агентских CLI
Дата: 2026-07-15
Статус: реализовано; 2026-09-28 упрощено по принципу ниже
Точное поведение, форматы, сообщения и команды описаны в коде,
`proxy-client --help` и [`proxy-client/README.md`](../../proxy-client/README.md).
Здесь — то, чего по коду не видно: задача, границы, выбор и его причины, риски.
## Принцип: личный инструмент
`proxy-client` пользуются владелец и несколько друзей на своих машинах. Код
защищается от реальных ошибок и опасностей:
- скачанный Xray проверяется по SHA-256 и заменяется атомарно;
- секреты профиля не попадают в вывод, служебные файлы закрыты правами 600/700;
- локальный порт Xray слушает только loopback;
- чужие файлы не перезаписываются;
- сигнал уходит только процессам из `state.json`: Xray — если его
`/proc/PID/exe` совпадает с запущенным файлом, супервизору — если Xray
подтверждён и в командной строке супервизора есть `proxy-client`.
Пользователь и его собственные файлы — не противник. Сообщение об ошибке
называет файл полным путём и подсказывает готовую команду.
Новая защита появляется вместе со сценарием, который реально случается у
пользователя, и этот сценарий записывается рядом с ней. Так же проверяется и
прежний код: в 2026-09-28 убраны защиты без такого сценария — повторное
использование PID, подложный `state.json`, zip-бомба в проверенном архиве,
гонка `status` с параллельным `start`.
Тесты проверяют поведение через команду `proxy-client`, разбор ссылки
(`parse_vless` + `build_xray_config`), установку Xray с подставным
скачивателем, функции Bash с поддельным `PROXY_CLIENT_BIN` и установщик пакета
во временном HOME.
## Проблема
На удалённых 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-часть; Windows-часть не меняется.
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).
## Цели
- Управляемый пользовательский Xray на Linux-сервере без systemd и root.
- Явные `pclaude` и `pcodex`, которые нельзя случайно запустить через другой
прокси или без прокси.
- Одна VLESS-ссылка Hiddify превращается в клиентский JSON Xray.
- Внешний Hiddify работает, как раньше.
- Машинный `NO_PROXY` настраивается без правки файлов репозитория.
- Каталог `proxy-client/` можно скопировать отдельно и поставить своим
установщиком, без остальных dotfiles и без Stow.
## Не входит в объём
- Windows, PowerShell, Windows Codex launcher и VS Code Server.
- systemd, автозапуск, sudo, firewall, TUN и системная маршрутизация.
- Автоматический выбор между Xray и Hiddify или запасной прокси.
- Несколько одновременных профилей у одного Unix-пользователя.
- Импорт подписок или полного JSON-экспорта Hiddify.
- Протоколы за пределами двух поддержанных семейств VLESS.
- pip, venv и сторонние Python-библиотеки.
- Сетевая изоляция голых `claude` и `codex`: намерение выражают команды с
префиксом `p`.
## Рассмотренные варианты
- **Только Bash.** URL-кодирование, JSON, контрольные суммы и управление
процессом делают Bash хрупким и плохо тестируемым.
- **Bash-контроллер и Python-преобразователь.** Граница между двумя языками
усложняет ошибки, тесты и единый источник настроек.
- **Один Python CLI и тонкий Bash-модуль.** Выбран. Python отвечает за данные,
установку Xray, процессы и запуск дочерних команд. Bash нужен только там, где
меняется окружение текущей shell-сессии.
- **Без менеджера**: готовые JSON и Xray вручную в tmux. Почти нет кода, но не
решает главного — безопасного `pclaude`/`pcodex` из ссылки Hiddify.
Для отдельно скопированного пакета рассматривались: только инструкция для
ручных ссылок (легко забыть шаги), копирование файлов в `$HOME` (сложно
обновлять), обязательный Stow (лишняя зависимость на серверах) и свой
установщик. Выбран свой установщик, который ставит прямые ссылки: один способ,
нет второй копии файлов.
## Решение
### Компоненты
`proxy-client` — один исполняемый файл на Python 3.8+ без внешних библиотек:
установка Xray, запуск и остановка, проверка доступности, разбор VLESS, запуск
дочерней команды через прокси. Команды: `install`, `start`, `stop`, `status`,
`env`, `run`. Без подкоманды ничего не делается.
`shell.bash` — единственный владелец `proxy_on`, `proxy_off`, `pclaude`,
`pcodex` и `myip`; в `bash/.bash_functions` их нет, чтобы не зависеть от
порядка `source`. Модуль сам конфиг не читает:
- `pclaude` и `pcodex` вызывают `proxy-client run -- COMMAND ARG...`;
- `proxy_on` берёт готовые переменные из `proxy-client env` и применяет их,
только если команда завершилась успешно; `eval` не используется;
- `proxy_off` очищает переменные в обоих регистрах.
`proxy_on`/`proxy_off` больше не запрашивают внешний IP: это замедляло
переключение. Для проверки есть `myip` и `proxy-client status`.
### Настройки
Два файла в `~/.config/proxy-client/`:
- `config` — несекретный машинный INI: тип `xray` или `external`, имя, адрес,
порт, добавка к `NO_PROXY`. Если файла нет, используется прежний Hiddify на
`hproxy.local:12334`.
- `default.url` — секретная VLESS-ссылка с правами 600; `start -f` берёт другой
файл.
Они разделены, потому что первый описывает политику машины, а второй — секрет,
который меняется независимо. Выбран INI, а не TOML: `tomllib` появился только
в Python 3.11.
### Явный тип прокси
`xray` значит: нужен Xray, запущенный через `proxy-client start`. Если его нет,
агент не запускается. `external` значит: проверяется только доступность адреса
и порта. Ни один тип не переходит на другой сам — предсказуемость канала важнее
удобства.
`start` работает только при `type=xray` и loopback-адресе в конфиге. Поэтому
нельзя поднять локальный Xray, оставив `pclaude` направленным во внешний
Hiddify. `stop` от типа не зависит: после переключения на `external` старый
Xray всё равно можно остановить.
Один порт на HTTP и SOCKS, по умолчанию `12334`; порт настраивается, чтобы
разные пользователи одной машины не мешали друг другу.
### VLESS-ссылки
Поддержаны две семьи: TCP/RAW с REALITY и XHTTP с TLS. Неизвестный или
повторный query-параметр — отказ: он может менять смысл соединения, и его
нужно добавить в разбор явно. Остальное проверяет `xray run -test`.
Разбор следует [спецификации ссылок Xray](https://github.com/XTLS/Xray-core/discussions/716):
поддерживает `ech`, `pqv`, `spx`, `fm`, необязательные поля и значения по
умолчанию. Дополнительные параметры XHTTP переносятся внутрь `extra`, чтобы
ядро их не потеряло. Старые `sessionPlacement`/`sessionKey` преобразуются в
`sessionIDPlacement`/`sessionIDKey`, в том числе для отдельного скачивания;
противоречивые значения отклоняются. Полный список — в README пакета.
Hiddify Manager добавляет `allowInsecure`/`insecure` даже к профилям с
нормальным сертификатом
([Hiddify-Manager#5438](https://github.com/hiddify/Hiddify-Manager/issues/5438)),
а Xray [26.2.6](https://github.com/XTLS/Xray-core/releases/tag/v26.2.6) эту
опцию удалил. Поэтому поля принимаются и игнорируются с предупреждением;
TLS-проверку задают `pcs` и `vcn`.
Сводка перед запуском не показывает UUID, ключи REALITY и содержимое XHTTP
`path` (там бывает токен). Ссылка не передаётся аргументом командной строки.
### Установка Xray
`proxy-client install` ставит в `~/.local/bin/xray` официальный архив одного
зафиксированного релиза (сейчас 26.10.10), проверяет SHA-256 по `.dgst` и
заменяет файл атомарно. Официальный установщик Xray не подходит: он требует
root и создаёт systemd-службы. `start` принимает только ту же версию и никогда
ничего не скачивает.
### Жизненный цикл
`start` остаётся на переднем плане как небольшой Python-супервизор с одним
дочерним Xray; ожидаемое место — панель tmux. Отдельный журнал и команда `logs`
не нужны: хватает истории tmux. Второй `start` отсекает блокировка `flock`.
`state.json` хранит PID супервизора и Xray, чтобы `stop` работал из любого
терминала и мог добить Xray, если супервизор убит.
Перед запуском обязательны разбор ссылки, `xray run -test` и открытый порт
после старта. Проверка внешнего IP — только диагностика: её сбой даёт
предупреждение.
`run` никогда не запускает Xray, поэтому у `pclaude` и `pcodex` нет скрытого
автозапуска.
### Доставка
Каталог `proxy-client/` раскладывается в `$HOME` ссылками: CLI в
`~/.local/bin/`, `shell.bash` и `examples/` в `~/.config/proxy-client/`.
Машинный `config` и `default.url` пользователь создаёт сам; в Git они не
входят.
- Отдельно: `proxy-client/install.sh` ставит эти три ссылки и по умолчанию
дописывает блок в `~/.bashrc`. Чужие файлы не трогает. Режим прокси, Xray и
сеть не настраивает — это делают отдельные команды по README.
- В составе dotfiles: корневой `install.sh` подключает тот же пакет через Stow,
а `.bash_custom` загружает `shell.bash`.
## Риски
- Формат ссылок Hiddify расширяется. Строгий отказ на неизвестном параметре
лучше частично неверного JSON, но новые ссылки могут потребовать правки
разбора.
- Открытый порт не доказывает, что маршрут работает. Проверка внешнего IP
отделена от обязательных проверок.
- Loopback без аутентификации открыт другим пользователям того же узла.
Пользователи машины считаются доверенными.
- `NO_PROXY` программы понимают по-разному, особенно CIDR. README советует
точные IP и домены.
- Ссылки установки зависят от пути исходного каталога: переносить его нельзя.
- Поддержка РЕД ОС, Astra Linux и Alt Linux заявляется только после проверки
версии Python и прогона `tests/test_proxy_client_full.sh` на них.
- Шаблоны в `examples/` содержат только вымышленные значения; настоящие ссылки,
UUID и ключи в репозиторий не попадают.