Files
dotfiles/docs/specs/2026-11-02-proxy-client-profiles-service-design.md
ddadmin 203e515479 feat(proxy-client): добавлен мастер установки системной службы
- Зачем: установка с нуля и переход из ручного Xray одной командой.
- Что: план с подтверждением или --yes, скрытый ввод ссылки, проверка Xray,
  перенос настроек, запуск службы и переключение домашнего профиля;
  безопасное продолжение после сбоя и сохранение существующих настроек.
- Проверка: cross-review Claude и исправление находок; по 108 тестов на
  Python 3.8 и 3.14 под PTY, 18 Bash-проверок. После аудита уточнена
  фикстура повторного запуска и отдельно проверена на обеих версиях Python.
  Настоящие службы и настройки машины не изменялись.
2026-11-04 21:15:23 +01:00

20 KiB

Профили proxy-client и системная служба

Дата: 2026-11-02

Статус: реализовано; запуск после загрузки ОС и восстановление настоящей службы systemd требуют отдельной проверки на Linux-ВМ.

Задача и границы

На Linux-ВМ, компьютерах и ноутбуках нужен прокси, доступный после загрузки без входа пользователя и ручного запуска. Приложения уже знают его адрес. Одновременно пользователь должен иметь возможность запустить другой прокси на другом порту в открытом терминале.

Это расширяет первоначальный дизайн: добавляет системную службу и несколько пользовательских профилей. Остальные ограничения и принцип личного инструмента сохраняются. Термины определены в 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: профили, системная установка и обновление, управление, автозапуск, журналы и диагностика. В корневом README достаточно существующей ссылки на пакет. Этот документ хранит причины решений; рабочие команды и примеры живут в README пакета.

Пользовательская служба с linger позволила бы запускать прокси до входа, но выбрано явное разделение системных и домашних настроек. Несколько именованных системных служб пока не нужны. Запуск в терминале сохраняется для дополнительных прокси, но сам по себе не решает задачу загрузки машины.

Правила systemd сверены с документацией каталогов службы, перезапуска и хранения журналов.