- Зачем: установка с нуля и переход из ручного Xray одной командой. - Что: план с подтверждением или --yes, скрытый ввод ссылки, проверка Xray, перенос настроек, запуск службы и переключение домашнего профиля; безопасное продолжение после сбоя и сохранение существующих настроек. - Проверка: cross-review Claude и исправление находок; по 108 тестов на Python 3.8 и 3.14 под PTY, 18 Bash-проверок. После аудита уточнена фикстура повторного запуска и отдельно проверена на обеих версиях Python. Настоящие службы и настройки машины не изменялись.
224 lines
20 KiB
Markdown
224 lines
20 KiB
Markdown
# Профили proxy-client и системная служба
|
||
|
||
Дата: 2026-11-02
|
||
|
||
Статус: реализовано; запуск после загрузки ОС и восстановление настоящей
|
||
службы systemd требуют отдельной проверки на Linux-ВМ.
|
||
|
||
## Задача и границы
|
||
|
||
На Linux-ВМ, компьютерах и ноутбуках нужен прокси, доступный после загрузки
|
||
без входа пользователя и ручного запуска. Приложения уже знают его адрес.
|
||
Одновременно пользователь должен иметь возможность запустить другой прокси
|
||
на другом порту в открытом терминале.
|
||
|
||
Это расширяет [первоначальный дизайн](2026-07-15-proxy-client-design.md):
|
||
добавляет системную службу и несколько пользовательских профилей.
|
||
Остальные ограничения и принцип личного инструмента сохраняются.
|
||
Термины определены в [CONTEXT.md](../../CONTEXT.md).
|
||
|
||
Не добавляем несколько системных экземпляров, фоновый пользовательский режим,
|
||
TUN, системную маршрутизацию, автоматический выбор прокси или поддержку Windows.
|
||
Для системного режима требуется systemd; ручной режим сохраняет независимость
|
||
от него. Пакет по-прежнему можно использовать отдельно от остальных dotfiles.
|
||
|
||
## Пользовательские профили
|
||
|
||
Профиль по умолчанию сохраняет нынешние файлы
|
||
`~/.config/proxy-client/config` и `default.url`. Отсутствие выбора профиля
|
||
не меняет поведения существующих команд, включая прежний внешний прокси
|
||
при отсутствии конфигурации. Учитываются существующие настройки XDG.
|
||
|
||
Дополнительные профили размещаются в
|
||
`~/.config/proxy-client/profiles/<имя>/`, каждый со своими `config` и
|
||
`default.url`. Имя `default` обозначает существующий профиль по умолчанию.
|
||
Неизвестное имя даёт ошибку, а не переход к другому профилю.
|
||
|
||
Один общий параметр выбора профиля действует на запуск, остановку, проверку,
|
||
вывод окружения и запуск дочерних команд. Существующий выбор файла VLESS-ссылки
|
||
через `start -f` остаётся разовым переопределением ссылки выбранного профиля.
|
||
Bash-команды без явного выбора продолжают использовать профиль по умолчанию.
|
||
|
||
Каждый профиль получает собственные сведения о процессе, блокировку и
|
||
сгенерированный JSON. Запуск и остановка одного не затрагивают остальные.
|
||
Два экземпляра могут работать одновременно, если их локальные порты различны.
|
||
Занятый порт даёт ошибку; чужой процесс не останавливается.
|
||
|
||
Ручной запуск остаётся на переднем плане, с выводом в терминал и остановкой
|
||
через Ctrl+C либо команду остановки выбранного профиля. Само обращение к прокси
|
||
через `run` или `env` ничего не запускает.
|
||
|
||
## Bash-команды
|
||
|
||
`proxy_on` без параметров выбирает профиль по умолчанию, с `--profile` —
|
||
именованный. Он меняет только окружение текущего терминала после успешной
|
||
проверки. `proxy_off` очищает переменные, но не останавливает процессы.
|
||
`myip` использует текущее окружение.
|
||
|
||
`pclaude` и `pcodex` принимают ведущий `--proxy-profile` для выбора профиля.
|
||
Без него остаётся профиль по умолчанию; остальные аргументы и код возврата
|
||
дочерней программы сохраняются. Обычный `--profile` относится к дочерней
|
||
программе. Bash-модуль передаёт выбор CLI и не дублирует правила профилей.
|
||
|
||
Выбор через `proxy_on` не меняет профиль по умолчанию: обычная команда
|
||
наследует окружение, а обёртка с префиксом `p` явно выбирает свой профиль.
|
||
Это различие должно быть показано в README пакета.
|
||
|
||
## Системная служба
|
||
|
||
Одна служба `proxy-client.service` читает один комплект настроек из
|
||
`/etc/proxy-client/`. Ей не нужны именованные профили или домашний каталог
|
||
пользователя. Исполняемые файлы устанавливаются в системное расположение,
|
||
без ссылок на пользовательский каталог исходников. Xray работает под отдельной
|
||
непривилегированной учётной записью и слушает только loopback.
|
||
|
||
Временные файлы процесса, блокировка и сгенерированный JSON находятся в
|
||
закрытом `/run/proxy-client/`. Секреты доступны только администратору и
|
||
служебной учётной записи. Пользовательские команды остановки не управляют
|
||
системным экземпляром: за него отвечает systemd.
|
||
|
||
Установка оформляется одной подкомандой `service install`. В терминале она
|
||
собирает недостающие настройки, показывает план и после подтверждения
|
||
выполняет полный переход на службу. `--yes` (`--auto-yes`) принимает план
|
||
без вопроса. Без терминала для нового подключения нужен закрытый `--file`;
|
||
секрет не передаётся в argv или окружении. В терминале применяется скрытый
|
||
ввод. Новый профиль получает адрес `127.0.0.1` и порт `12334` по умолчанию.
|
||
|
||
Для переноса используются сохранённые `config` и `default.url` выбранного
|
||
профиля; `--file` переопределяет ссылку. Xray ищется в `~/.local/bin`, затем
|
||
в `PATH` и системной установке. Если его нет, скачивается закреплённая версия
|
||
с проверкой SHA-256. Параметр `--xray` позволяет указать бинарник явно.
|
||
|
||
Полный переход состоит из подготовки снимка, проверки Xray, установки
|
||
системных файлов, остановки выбранного ручного процесса и запуска службы.
|
||
Домашний `config` меняется на `external` только после проверки активного
|
||
состояния службы и доступности её порта; прежний файл сохраняется рядом.
|
||
Остановка пользовательского процесса и изменение домашнего профиля
|
||
выполняются без повышения прав. Sudo применяется только к системной части.
|
||
|
||
Закрытый снимок `service-setup.json` в каталоге состояния профиля позволяет
|
||
продолжить незавершённый переход. Он содержит исходный домашний config,
|
||
подготовленный config, VLESS-ссылку и путь для ручного восстановления;
|
||
права файла `600`, каталога `700`. При повторе домашние и системные настройки
|
||
сверяются со снимком до остановки процессов. Изменённые настройки не
|
||
перезаписываются, исходный файл VLESS-ссылки тоже проверяется. Ошибка конфликта
|
||
указывает команду удаления снимка, чтобы начать заново с текущими настройками;
|
||
этот сброс не меняет сами настройки и процессы. После успеха снимок удаляется.
|
||
Ошибка запуска сохраняет
|
||
прежний домашний профиль; восстановление ручного процесса требует явной
|
||
команды, прежние соединения не восстанавливаются.
|
||
|
||
Наличие системных файлов само по себе не означает завершённый переход:
|
||
если домашний профиль ещё `xray`, мастер сверяет его с системным и завершает
|
||
перенос. Другой профиль не заменяет существующие системные настройки.
|
||
После завершённого перехода настройки службы самостоятельны. Обычное
|
||
обновление сохраняет их и включает перезапуск в подтверждаемый план;
|
||
домашний профиль не меняется и может отсутствовать.
|
||
|
||
`--install-only`, `--config-dir` и `--root`
|
||
сохраняют режим установки файлов без мастера. Он требует готовых настроек
|
||
и Xray. Флаги `--enable`, `--start`, `--restart` задают действия явно.
|
||
`--root` не допускает действий с systemd. В этом режиме установка сама
|
||
домашний профиль не меняет и ручной процесс не останавливает.
|
||
Без терминала и без явного выбора режима команда завершается с подсказкой.
|
||
|
||
Systemd отвечает за запуск, остановку и перезапуск после аварии. Явная
|
||
остановка должна оставлять службу остановленной до следующего запуска.
|
||
Если автозапуск включён, после перезагрузки она запускается снова.
|
||
|
||
Отсутствие внешней сети при загрузке не должно приводить к окончательному
|
||
отказу службы. Готовность локального порта и доступность удалённого маршрута
|
||
различаются: автозапуск не гарантирует успешность первого запроса любого
|
||
приложения. Для приложений, не умеющих повторять запрос, порядок запуска и
|
||
ожидание готовности потребуют отдельной настройки.
|
||
|
||
## Журналы и совместная работа
|
||
|
||
Вывод службы направляется в journald. Отдельный текстовый журнал и собственная
|
||
ротация не нужны. Хранение между загрузками зависит от настройки journald;
|
||
установщик проверяет её и сообщает результат, не меняя глобальную политику
|
||
журналирования машины. Секреты не должны попадать в журнал.
|
||
|
||
Если системная служба занимает порт прежнего пользовательского Xray, второй
|
||
запуск на этом порту завершается ошибкой. Чтобы использовать системный прокси
|
||
через пользовательские команды, подходит профиль типа `external`. Для второго
|
||
Xray нужен другой порт. Мастер меняет домашний профиль по подтверждённому
|
||
плану; режим установки только файлов его не меняет.
|
||
|
||
## Устройство модулей
|
||
|
||
Применяем `codebase-design`: выбор профиля и все его пути должны определяться
|
||
в одном месте, чтобы команды не повторяли правила размещения файлов.
|
||
Пути, правила доступа к профилю, вывод Xray и подсказка остановки собраны
|
||
в неизменяемый контекст запуска. Он создаётся при выборе пользовательского
|
||
или системного режима и явно передаётся функциям. Управление процессом
|
||
использует готовые значения, без глобального переключателя режима.
|
||
Системная установка скрывает размещение, права и взаимодействие с systemd
|
||
за небольшим интерфейсом; не копирует разбор VLESS и управление Xray.
|
||
Внутри модуль отделяет подготовку плана от его применения: план хранит
|
||
снимок настроек и исходного процесса, применение управляет порядком
|
||
действий и возобновлением. Проверки проходят через публичный CLI.
|
||
Внешние команды подменяются через `PATH`, терминальный ввод, сокеты и загрузка
|
||
архива — подменой функций стандартной библиотеки в тестовом `sitecustomize`.
|
||
Разбор настроек и работа с файлами выполняются настоящим кодом.
|
||
|
||
Не вводим обобщённую поддержку других менеджеров служб и дополнительные
|
||
слои ради возможного будущего использования. Реально различаются
|
||
пользовательское и системное размещение, а также способ управления процессом.
|
||
|
||
## Проверки
|
||
|
||
Согласованы три интерфейса для новых тестов TDD:
|
||
|
||
1. CLI пользовательских профилей: прежнее поведение без имени, выбор
|
||
именованного профиля, два одновременных процесса, независимая остановка,
|
||
отказ при занятом порте и правильное окружение дочерней команды.
|
||
2. Установка службы через CLI и управление через systemd: размещение файлов,
|
||
сохранение настроек при повторной установке, явное включение автозапуска,
|
||
запуск и остановка, восстановление после аварии и доступность журналов.
|
||
|
||
3. Bash-команды: выбор профиля, передача аргументов и кода возврата,
|
||
сохранение окружения при ошибке и отсутствие остановки процессов.
|
||
|
||
Тесты проверяют наблюдаемое поведение через эти интерфейсы, а не внутренние
|
||
функции и последовательности их вызовов. Идём по одному сценарию:
|
||
падающий тест, минимальная реализация, следующий сценарий.
|
||
|
||
Локальные проверки используют временные каталоги, синтетические профили и
|
||
подставные внешние программы. Это не доказывает работу настоящего systemd.
|
||
Окончательная проверка системного режима нужна в отдельной Linux-среде
|
||
с systemd: запуск до входа пользователя после загрузки, восстановление после
|
||
аварии, явная остановка и одновременная работа ручного экземпляра.
|
||
Изменение текущей машины или её перезагрузка не входят в проверку по умолчанию.
|
||
|
||
Проверка после cross-review 2026-11-04: полный набор под псевдотерминалом
|
||
прошёл на Python 3.8 и 3.14 — по 108 тестов, ещё 12 проверок Bash-команд
|
||
и 6 проверок пользовательского установщика.
|
||
Мастер проверен через CLI: настройка с нуля, перенос, обновление, отмена,
|
||
отказы и продолжение перехода. Скрытый ввод проверен в псевдотерминале.
|
||
Системные действия и скачивание в этих тестах подменены; настоящие службы
|
||
не запускались. Shellcheck в среде отсутствует.
|
||
|
||
При первоначальной реализации отдельно проверены Bash-команды вместе
|
||
с настоящим CLI и двумя локальными тестовыми портами. Unit прошёл статическую
|
||
проверку в изолированном корне с тестовыми базовыми target-файлами.
|
||
Эти результаты не заменяют проверку загрузки ОС.
|
||
|
||
## Документация и альтернативы
|
||
|
||
Подробные инструкции размещены в
|
||
[`proxy-client/README.md`](../../proxy-client/README.md): профили, системная
|
||
установка и обновление, управление, автозапуск, журналы и диагностика.
|
||
В корневом README достаточно существующей ссылки на пакет. Этот документ
|
||
хранит причины решений; рабочие команды и примеры живут в README пакета.
|
||
|
||
Пользовательская служба с linger позволила бы запускать прокси до входа,
|
||
но выбрано явное разделение системных и домашних настроек. Несколько
|
||
именованных системных служб пока не нужны. Запуск в терминале сохраняется
|
||
для дополнительных прокси, но сам по себе не решает задачу загрузки машины.
|
||
|
||
Правила systemd сверены с документацией
|
||
[каталогов службы](https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html#RuntimeDirectory=),
|
||
[перезапуска](https://www.freedesktop.org/software/systemd/man/latest/systemd.service.html#Restart=)
|
||
и [хранения журналов](https://www.freedesktop.org/software/systemd/man/latest/journald.conf.html#Storage=).
|