Files
dotfiles/proxy-client/README.md
T
ddadmin b34eb6ac4c feat(proxy-client): добавлены профили и системная служба
Добавлены независимые пользовательские профили, их выбор в Bash-командах и явная установка службы systemd. Инструкции размещены в README пакета.

Проверка: 67 тестов на Python 3.8 и 3.14, shell-наборы и интеграция Bash с CLI. Загрузка ОС и восстановление настоящей службы требуют отдельной проверки.
2026-11-02 22:16:12 +01:00

636 lines
34 KiB
Markdown
Raw 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
`proxy-client` — переносимый Linux/Bash-интерфейс для запуска команд через
внешний прокси или через управляемый Xray. Он добавляет команды
`proxy_on`, `proxy_off`, `pclaude` и `pcodex`, но не подменяет обычные `claude`
и `codex`.
CLI требует Python 3.8+ и использует только стандартную библиотеку. Он не
запускает Xray и не переключает режим прокси без явной команды пользователя.
## Установка
### Только пакет `proxy-client`
Скопируйте каталог в постоянное место и запустите пакетный установщик. Текущий
рабочий каталог не важен:
```bash
~/src/proxy-client/install.sh
```
Установщик создаёт три ссылки на исходный каталог: CLI, Bash-модуль и каталог
шаблонов. Поэтому исходный каталог после установки нельзя перемещать. Если
какой-то из путей уже занят чужим файлом или ссылкой, установщик ничего не
меняет и перечисляет все такие пути. Повторный запуск безопасен.
По умолчанию установщик дописывает в `~/.bashrc` блок между строками
`# >>> proxy-client >>>` и `# <<< proxy-client <<<`: он добавляет
`~/.local/bin` в `PATH` и подключает Bash-модуль. Если блок уже есть, файл не
меняется. После установки откройте новый Bash или примените блок в текущей
сессии:
```bash
source ~/.bashrc
```
Чтобы оставить `~/.bashrc` без изменений, запустите:
```bash
~/src/proxy-client/install.sh --no-shell-setup
```
В этом случае подготовьте только текущую сессию вручную:
```bash
export PATH="$HOME/.local/bin:$PATH"
source ~/.config/proxy-client/shell.bash
```
Для следующих сессий добавьте эти строки в подходящий Bash-конфиг сами.
Установщик не создаёт машинный `config` и закрытый `default.url`, не
устанавливает Xray и не проверяет сеть. После подключения команд выберите режим
по разделу [«Настройка режима»](#настройка-режима). Без машинного конфига
действует режим Hiddify по умолчанию.
### В составе dotfiles
Корневой установщик dotfiles подключает тот же пакет через Stow и загружает
Bash-функции в новых интерактивных сессиях:
```bash
cd ~/dotfiles
./install.sh
```
Он заранее создаёт `~/.local/bin` и `~/.config/proxy-client`, чтобы Stow
размещал ссылки внутри них, а не превращал сами каталоги в ссылки на
репозиторий. После установки откройте новый Bash или выполните:
```bash
export PATH="$HOME/.local/bin:$PATH"
source ~/.bash_custom
```
Если `~/.local/bin` отсутствует в `PATH` и в новых сессиях, добавьте ту же строку
`export` в свой Bash-конфиг: корневой установщик dotfiles его не изменяет.
## Как выбирается прокси
Без выбора профиля режим определяется файлом
`~/.config/proxy-client/config`:
| Машинный конфиг | Используется | Что запускать |
|-----------------|-------------|---------------|
| Файла нет | Hiddify на `hproxy.local:12334` | Ничего |
| `type = external` | Указанный внешний прокси | Ничего |
| `type = xray` | Управляемый локальный Xray | `proxy-client start` |
Доступность процесса не меняет выбранный режим. Если в конфиге указан Xray, но
он не запущен, `status`, `env`, `run`, `proxy_on`, `pclaude` и `pcodex`
завершатся с ошибкой. Автоматического перехода на Hiddify или другой прокси нет.
Текущий выбор и доступность проверяет команда:
```bash
proxy-client status
```
## Настройка режима
### Hiddify в Windows, команды в WSL
Это режим по умолчанию. Если Hiddify доступен из WSL как
`hproxy.local:12334`, создавать `config` не нужно:
```bash
getent hosts hproxy.local
proxy-client status
pclaude --version
```
#### WSL NAT mode
IP Windows-шлюза меняется при перезапуске WSL, поэтому `hproxy.local` нужно
обновлять. В отдельно скопированном пакете системных помощников нет. Узнайте
адрес шлюза:
```bash
ip route show default | awk 'NR == 1 {print $3}'
```
Затем откройте `/etc/hosts` через `sudoedit` и оставьте одну отдельную строку с
полученным адресом:
```bash
sudoedit /etc/hosts
```
```text
<адрес-шлюза> hproxy.local
```
Не удаляйте другие имена на той же строке: при необходимости перенесите их на
отдельную строку. Повторяйте обновление после перезапуска WSL.
С полным dotfiles запустите корневой `~/dotfiles/install.sh` интерактивно: он
предложит автоматическое обновление при старте WSL. Обновить адрес сразу можно
так:
```bash
sudo ~/dotfiles/scripts/setup-hproxy.sh
# после настройки автозапуска и загрузки общих Bash-функций:
update_hproxy
```
В NAT mode Hiddify должен слушать адрес, доступный через Windows-шлюз, а не
только `127.0.0.1`.
#### WSL mirrored mode
В [mirrored mode](https://learn.microsoft.com/windows/wsl/networking#mirrored-mode-networking)
(`networkingMode=mirrored`) Windows-сервисы доступны из WSL через `127.0.0.1`.
Чтобы сохранить адрес по умолчанию, добавьте алиас в Windows hosts:
```text
# C:\Windows\System32\drivers\etc\hosts
127.0.0.1 hproxy.local
```
Альтернатива — настройте внешний режим по следующему разделу и укажите
`address = 127.0.0.1`. Алиас проверяйте через `getent hosts hproxy.local`, а
активный конфиг — через `proxy-client status`.
### Другой внешний прокси
Скопируйте пример и измените адрес, порт и локальные исключения:
```bash
cp ~/.config/proxy-client/examples/config.external \
~/.config/proxy-client/config
```
```ini
[proxy]
type = external
name = Office proxy
address = proxy.example.internal
port = 12334
no_proxy_extra = .example.internal,git.example.internal
```
Для внешнего прокси на той же Linux-машине можно указать `127.0.0.1` напрямую.
### Локальный Xray
Создайте машинный конфиг:
```bash
cp ~/.config/proxy-client/examples/config.xray \
~/.config/proxy-client/config
```
Закрытый профиль должен содержать ровно одну VLESS-ссылку:
```bash
cp ~/.config/proxy-client/examples/default.url \
~/.config/proxy-client/default.url
$EDITOR ~/.config/proxy-client/default.url
chmod 600 ~/.config/proxy-client/default.url
```
Установите проверенную версию Xray без root:
```bash
proxy-client install
```
Запустите Xray в отдельном терминале или панели tmux:
```bash
proxy-client start
```
Другой файл VLESS-ссылки можно выбрать без замены `default.url`:
```bash
proxy-client start -f ~/.config/proxy-client/work.url
```
Для смены ссылки работающего экземпляра сначала выполните `proxy-client stop`.
Все файлы с настоящими VLESS-ссылками должны иметь права `600` и не должны
попадать в Git.
## Именованные профили
Профиль объединяет настройки локального входа, исключения и, для Xray,
VLESS-ссылку. Без указания имени используется `default`: прежние `config` и
`default.url` остаются на своих местах. Дополнительные профили живут в
`~/.config/proxy-client/profiles/<имя>/`. При заданном `XDG_CONFIG_HOME`
вместо `~/.config` используется этот каталог.
Например, создайте профиль `work`:
```bash
mkdir -p ~/.config/proxy-client/profiles/work
cp ~/.config/proxy-client/examples/config.xray ~/.config/proxy-client/profiles/work/config
$EDITOR ~/.config/proxy-client/profiles/work/config
```
В `config` задайте свободный порт, например `12335`, если основной прокси
занимает `12334`. Поместите VLESS-ссылку в `profiles/work/default.url` и
ограничьте доступ:
```bash
chmod 600 ~/.config/proxy-client/profiles/work/default.url
proxy-client --profile work start
```
Команда остаётся в терминале. В другом терминале можно проверить или
остановить именно этот профиль:
```bash
proxy-client --profile work status
proxy-client --profile work stop
```
Параметр `--profile` ставится перед подкомандой. Неизвестный профиль даёт
ошибку; перехода на `default` нет. Разные профили могут работать одновременно
на разных портах. Остановка одного не затрагивает остальные.
`start -f FILE` переопределяет только файл ссылки выбранного профиля на этот
запуск. Его порт и исключения по-прежнему берутся из `config`.
## Ежедневное использование
| Задача | Команда | Область действия |
|--------|---------|------------------|
| Запустить Claude через прокси | `pclaude [ARG...]` | Только дочерний процесс |
| Запустить Codex через прокси | `pcodex [ARG...]` | Только дочерний процесс |
| Запустить другую команду | `proxy-client run -- COMMAND ARG...` | Только дочерний процесс |
| Включить прокси | `proxy_on` | Текущая Bash-сессия |
| Выключить прокси | `proxy_off` | Текущая Bash-сессия |
| Проверить прокси | `proxy-client status` | Без изменения окружения |
| Получить proxy-переменные | `proxy-client env` | Машинный вывод в stdout |
| Показать внешний IP | `myip` | С учётом текущего окружения |
`run`, `pclaude` и `pcodex` передают дочернему процессу восемь вариантов
`HTTP_PROXY`, `HTTPS_PROXY`, `ALL_PROXY` и `NO_PROXY`, включая имена в нижнем
регистре. После завершения команды переменные не остаются в оболочке.
`proxy_on` применяет те же переменные к текущей Bash-сессии только после
успешной проверки прокси. `proxy_off` удаляет их. Обычные `claude` и `codex` не
обёрнуты и не выполняют предварительную проверку, но после `proxy_on` наследуют
прокси-переменные, как и любой другой дочерний процесс.
Для именованного профиля:
```bash
proxy_on --profile work
pclaude --proxy-profile work
pcodex --proxy-profile work --search
proxy-client --profile work run -- COMMAND ARG...
```
У `pclaude` и `pcodex` параметр `--proxy-profile` должен быть первым.
Обычный `--profile` передаётся запускаемой программе.
`proxy_on --profile work` меняет окружение терминала, но не профиль по
умолчанию: обычный `codex` наследует это окружение, а `pcodex` без параметра
выберет `default`. `proxy_off` только очищает переменные — работающие прокси,
включая системную службу, остаются запущенными. `myip` использует текущее
окружение.
### Жизненный цикл Xray
```bash
proxy-client install [--replace]
proxy-client start [-f PROFILE]
proxy-client status
proxy-client stop
```
`start` сначала проверяет версию и конфигурацию Xray, затем остаётся на переднем
плане. Скрытого запуска из `run` нет; системная служба устанавливается отдельно.
Один loopback-порт принимает HTTP CONNECT и SOCKS5.
Остановить Xray можно двумя способами: `Ctrl+C` в окне `start` или
`proxy-client stop` из любого терминала. Оба гасят Xray и удаляют служебные
файлы. Если процесс `start` был убит (например, закрыли окно tmux), Xray может
остаться работать: `status` и `env` тогда сообщат об ошибке, а `stop` добьёт его
и уберёт файлы. `stop` работает и после изменения машинного типа на `external`.
Второй `start` того же профиля, пока первый работает, завершится ошибкой.
## Системная служба
Этот режим нужен, когда приложения должны получать прокси после загрузки
Linux, до входа пользователя. Требуются systemd, `systemd-sysusers` и права
администратора для установки. Служба одна; именованные профили остаются
пользовательскими.
Сначала подготовьте и проверьте профиль Xray обычным ручным запуском. Перед
запуском службы остановите ручной экземпляр, если он занимает тот же порт.
Установите службу, указав каталог настроек и уже установленный Xray:
```bash
sudo "$HOME/.local/bin/proxy-client" service install \
--config-dir "$HOME/.config/proxy-client" \
--xray "$HOME/.local/bin/xray" --enable --start
```
В качестве `--config-dir` можно указать каталог именованного профиля.
Пути должны быть абсолютными. `--enable` включает запуск при загрузке,
`--start` запускает сейчас. Без этих параметров команда только устанавливает
файлы и регистрирует службу.
Установщик копирует настройки в `/etc/proxy-client/`, а CLI и Xray — в
`/usr/local/lib/proxy-client/`. Служба больше не зависит от домашнего каталога.
Изменения исходного пользовательского профиля на неё не влияют. Системные
настройки имеют владельца `root`, группу `proxy-client` и права `640`;
каталог — `750`. Процесс работает под отдельным пользователем `proxy-client`.
Временные файлы находятся в закрытом `/run/proxy-client/`.
Управление и журнал:
```bash
sudo systemctl status proxy-client
sudo systemctl stop proxy-client
sudo systemctl start proxy-client
sudo systemctl restart proxy-client
sudo journalctl -u proxy-client -b
sudo journalctl -u proxy-client -f
```
Для изменения настроек отредактируйте `/etc/proxy-client/config` или
`/etc/proxy-client/default.url` с правами администратора, затем перезапустите
службу. Сохраняйте владельца, группу и права файлов.
После аварии systemd повторяет запуск с паузой в пять секунд. Явная команда
`stop` оставляет службу остановленной, но не отменяет запуск при следующей
загрузке. Чтобы отключить автозапуск и остановить её сразу:
```bash
sudo systemctl disable --now proxy-client
```
Чтобы включить обратно:
```bash
sudo systemctl enable --now proxy-client
```
При обновлении повторите установку из актуальной копии пакета. Существующие
системные настройки сохраняются; исходный домашний профиль уже не нужен:
```bash
sudo "$HOME/.local/bin/proxy-client" service install \
--config-dir /etc/proxy-client --xray "$HOME/.local/bin/xray"
sudo systemctl restart proxy-client
```
Работающий процесс сам не перезапускается. Чужие или вручную изменённые
исполняемые файлы и unit-файл установщик не заменяет; изменения параметров
systemd делайте через `sudo systemctl edit proxy-client`. Файлы заменяются
по одному атомарно, общего отката всей установки при сбое нет.
Вывод CLI поступает в journald. Сырой вывод Xray в системном режиме подавлен,
чтобы сообщения с фрагментами конфигурации не раскрывали секреты; детальные
сетевые журналы Xray поэтому недоступны. Установщик сообщает настройку хранения
journald, но не меняет её. Сохранение журнала после перезагрузки зависит от
настроек машины; проверить предыдущие загрузки можно через
`sudo journalctl --list-boots`.
Открытый локальный порт не гарантирует доступность удалённого сервера.
На ноутбуке сеть может появиться позже службы. Приложениям без повторения
запросов понадобится отдельная настройка ожидания готовности.
Для `proxy_on`, `pclaude`, `pcodex` и `run` системная служба — внешний прокси:
укажите в нужном пользовательском профиле `type = external`, адрес
`127.0.0.1` и порт службы. Дополнительный профиль типа `xray` запускайте вручную
на другом порту. Пользовательский `stop` системной службой не управляет.
Для проверки размещения файлов без установки на машину есть
`service install --config-dir DIR --xray FILE --root /absolute/staging`.
Этот режим не вызывает systemd, не создаёт системную учётную запись и не
назначает ей владельцев; он несовместим с `--enable` и `--start`.
## Прокси в настройках Codex и Claude Code
Codex и Claude Code умеют брать proxy-переменные из собственных настроек.
Значения оттуда перекрывают окружение, унаследованное от запускающего
приложения, поэтому маршрут не зависит от того, откуда агент запущен:
терминал, IDE или фоновый демон Codex. С такими настройками `pcodex` и
`pclaude` не обязательны.
Установщики эти настройки не трогают. Шаблоны лежат в
`~/.config/proxy-client/examples/`: прокси `127.0.0.1:12334` и короткий
список исключений. Скопируйте нужный и поправьте под свою машину.
Codex читает `~/.codex/.env`:
```bash
cp ~/.config/proxy-client/examples/codex.env ~/.codex/.env
codex app-server daemon restart
```
Запросы к API отправляет общий демон Codex, а он читает `.env` только при
старте. Перезапуск демона прерывает открытые сессии Codex.
Claude Code берёт переменные из блока `env` в `~/.claude/settings.json`.
Перенесите туда блок из `examples/claude-settings.json`; остальные ключи
файла не трогайте. Настройки подхватятся при следующем запуске Claude Code.
Claude Desktop этот блок не использует: само приложение ходит в сеть через
Chromium.
Чтобы агент на машине работал без прокси, даже если его запустило
приложение с прокси в окружении, очистите адреса (`HTTP_PROXY`,
`HTTPS_PROXY`, `ALL_PROXY` в обоих регистрах) и задайте `NO_PROXY=*` и
`no_proxy=*`.
## Диагностика
Начните с проверки выбранного режима:
```bash
proxy-client status
```
Для внешнего Hiddify в WSL проверьте имя и порт:
```bash
getent hosts hproxy.local
curl --proxy http://hproxy.local:12334 https://example.com
curl --proxy socks5h://hproxy.local:12334 https://example.com
```
Для запущенного Xray можно дополнительно проверить созданный JSON:
```bash
xray run -test -c \
"${XDG_STATE_HOME:-$HOME/.local/state}/proxy-client/client.json"
```
Если выбран `type = xray`, но процесс не запущен, запустите его отдельно —
`proxy_on` и `run` этого не делают. Профиль XHTTP с ALPN `h3` требует доступного
UDP-маршрута до сервера; автоматического перехода на H2 нет.
Таймаут предварительной проверки по умолчанию равен двум секундам. Его можно
изменить положительным числом:
```bash
export PROXY_PREFLIGHT_TIMEOUT=5
```
## Справочник
### Ручное подключение без установщика
Если пакетный установщик запустить нельзя, выберите постоянный абсолютный путь
к исходному каталогу и создайте те же ссылки вручную. Команды предполагают, что
назначения ещё не заняты:
```bash
mkdir -p ~/.local/bin ~/.config/proxy-client
ln -s /absolute/path/to/proxy-client/.local/bin/proxy-client \
~/.local/bin/proxy-client
ln -s /absolute/path/to/proxy-client/.config/proxy-client/shell.bash \
~/.config/proxy-client/shell.bash
ln -s /absolute/path/to/proxy-client/.config/proxy-client/examples \
~/.config/proxy-client/examples
chmod 755 /absolute/path/to/proxy-client/.local/bin/proxy-client
```
Подготовьте текущую Bash-сессию:
```bash
export PATH="$HOME/.local/bin:$PATH"
source ~/.config/proxy-client/shell.bash
```
Для следующих сессий добавьте эти две строки в свой Bash-конфиг. Исходный
каталог после этого нельзя перемещать: созданные ссылки зависят от его пути.
### Машинный конфиг
Формат строгий: одна секция `[proxy]` и пять обязательных ключей без повторов и
неизвестных полей:
- `type` — `external` или `xray`;
- `name` — имя для диагностики;
- `address` — IPv4, IPv6 без квадратных скобок или ASCII DNS-имя без схемы и
порта;
- `port` — TCP-порт локального входа;
- `no_proxy_extra` — IP и домены через запятую, значение может быть пустым.
Список **замещает** встроенный `DEFAULT_NO_PROXY_EXTRA` целиком, а не
дополняет его: домены, нужные постоянно, придётся перечислить здесь заново.
Поддержка CIDR в `NO_PROXY` различается между программами, поэтому надёжнее
указывать точные IP и домены. Без машинного файла внешний режим дополнительно
включает `.concept-software.ru`, `dementev.space` и `.dementev.space` в
`NO_PROXY`. Для `type = xray` адрес обязан
быть адресом обратной петли, например `127.0.0.1` или `::1`.
### Поддерживаемые профили
Поддерживаются VLESS TCP/RAW с REALITY и VLESS XHTTP с TLS. Неизвестные,
повторные и несовместимые query-параметры отклоняются. XHTTP поддерживает ALPN
`http/1.1`, `h2` и `h3`; `extra` должен быть JSON-объектом, его содержимое
проверяет `xray run -test`.
Параметры ссылок передаются по [спецификации Xray](https://github.com/XTLS/Xray-core/discussions/716):
| В ссылке | В конфигурации Xray |
|----------|---------------------|
| `ech` | TLS `echConfigList` |
| `pqv`, `spx` | REALITY `mldsa65Verify`, `spiderX` |
| `fm` | `streamSettings.finalmask`, JSON-объект |
| `x_padding_bytes`, `xPaddingBytes` | XHTTP `extra.xPaddingBytes` |
| `scMaxEachPostBytes`, `scMinPostsIntervalMs`, `uplinkChunkSize` | Одноимённые поля XHTTP внутри `extra` |
Для XHTTP пустой или отсутствующий `host` допустим; при отсутствии `path`
используется `/`. Имена `sessionPlacement` и `sessionKey` в `extra`, включая
отдельное соединение скачивания, преобразуются в `sessionIDPlacement` и
`sessionIDKey`. Если старое и новое имя задают разные значения, профиль
отклоняется. Новые `sessionIDTable` и `sessionIDLength` передаются ядру.
Параметры XHTTP с именами как в JSON имеют приоритет над `extra`, а
`extra.xPaddingBytes` — над `x_padding_bytes`.
При отсутствии `encryption` используется `none`, `sni` — адрес сервера,
TLS `fp` — `chrome`. Для REALITY `fp` и `pbk` обязательны, а `flow` и `sid`
могут быть пустыми или отсутствовать; `type=raw` принимается как `type=tcp`.
Пустые `ech`, `pqv`, `spx`, `pcs`, `vcn` допустимы. SHA-256 в `pcs` может
содержать двоеточия и пробелы по краям. Метка `support-x25519mlkem768=true` в REALITY
принимается для совместимости со ссылками 3x-ui и не создаёт поле в JSON Xray.
Кроме `encryption=none`, принимается VLESS-шифрование
`encryption=mlkem768x25519plus.…` из новых ссылок Hiddify; остальную часть
значения проверяет сам Xray. Только с таким шифрованием XHTTP допускает
`flow=xtls-rprx-vision`. Косая черта перед параметрами (`…:443/?type=…`)
допустима, другой путь в ссылке — нет.
Xray 26.10.10 использует `pcs` и `vcn`. Устаревшие `allowInsecure` и `insecure`
принимаются с любым значением, но не переносятся: при значении `1` или `true`
выводится предупреждение, а сертификат проверяется обычным способом.
Последняя версия Xray, где `allowInsecure` ещё применяется, —
[25.12.8](https://github.com/XTLS/Xray-core/releases/tag/v25.12.8); опция
удалена в [26.2.6](https://github.com/XTLS/Xray-core/releases/tag/v26.2.6).
`proxy-client` эти версии не поддерживает и не устанавливает.
### Проверяемая установка Xray
`proxy-client install` устанавливает Xray 26.10.10 в `~/.local/bin/xray`. Для
`x86_64`/`amd64` используется `Xray-linux-64.zip`, для `aarch64`/`arm64` —
`Xray-linux-arm64-v8a.zip`; остальные архитектуры отклоняются.
[Выпуск 26.10.10](https://github.com/XTLS/Xray-core/releases/tag/v26.10.10)
помечен как предварительный. Обновление уже установленного клиента:
```bash
proxy-client install --replace
```
CLI скачивает официальный архив и файл `.dgst`, сверяет SHA-256 и фактическую
версию бинарника, а затем атомарно заменяет назначение. При любой ошибке старый
файл остаётся на месте. Если нужная версия уже установлена, сеть не нужна и
ничего не меняется. Другой файл или симлинк не заменяется без `--replace`
(симлинк заменяется сам, файл по ссылке не трогается); каталог не заменяется
никогда. Если после установки `PATH` находит другой `xray`, `install` сообщает об
ошибке и подсказывает команду.
Устанавливать можно и при работающем Xray: он продолжит работу со старым файлом,
а `install` напомнит перезапустить `start`.
`start` использует первый `xray` из `PATH` и принимает только версию 26.10.10.
### Файлы и безопасность
| Путь | Назначение |
|------|------------|
| `~/.config/proxy-client/config` | Несекретный машинный выбор режима |
| `~/.config/proxy-client/default.url` | Закрытая VLESS-ссылка, права `600` |
| `~/.config/proxy-client/examples/` | Синтетические примеры без секретов, шаблоны для Codex и Claude Code |
| `$XDG_STATE_HOME/proxy-client/` или `~/.local/state/proxy-client/` | Состояние и сгенерированный JSON |
Сводка скрывает UUID и ключи, а для XHTTP показывает только длину `path`.
Если файл профиля доступен другим пользователям, `start` не запустится и
покажет готовую команду `chmod 600` для конечного файла (у симлинка — для файла,
на который он указывает). Служебный каталог закрыт для других пользователей
(`700`, файлы в нём — `600`) и очищается после остановки Xray. Сбой
определения внешнего IP показывается как предупреждение и не останавливает уже
готовый процесс.
## Изменение кода
Перед правкой proxy-client прочитайте
[спецификацию](../docs/specs/2026-07-15-proxy-client-design.md): там сказано,
от чего клиент защищается, от чего нет и почему, и где живут тесты.
Все тесты пакета (из корня dotfiles):
```bash
tests/test_proxy_client_full.sh
```