Files
dotfiles/.scratch/headless-xray-proxy/issues/06-simplification-pass.md
T
ddadminandClaude Fable 5 98bfd6338a refactor(xray-proxy): срезана accidental-сложность CLI, тестов и спеки
- Зачем:
  - CLI и спека были переусложнены относительно принятой модели угроз
    (один пользователь, доверенная машина): лишняя ось версий Xray и
    защита от входов, которые не возникают при ручном редактировании.
- Что:
  - убрана поддержка Xray 25.12.8 — поддерживается только 26.3.27;
    allowInsecure валидируется, но никогда не эмитится в JSON.
  - удалена проверка владения сокетом через /proc/net/tcp; идентичность
    процесса и сверка адрес/порт со state сохранены.
  - валидация INI/профиля/адреса/query сведена к отказам на реальные
    опечатки; scoped IPv6 отклоняется одинаково на Python 3.8 и 3.9+.
  - удалены fsync/O_EXCL-церемония, SIGALRM-таймер, zombie-инспекция,
    мёртвый отпечаток профиля и двойной разбор extra/alpn.
  - shell.bash принимает вывод env без ре-валидации, сохранив атомарность;
    спека и issue 01–05 приведены к новому объёму (тикет 06, леджер).
- Проверка:
  - python3 -m unittest discover -s tests — 34 теста OK.
  - bash tests/test_proxy_launchers.sh — 8 проверок PASS.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 04:13:54 -04:00

15 KiB
Raw Blame History

06 — Упрощение: срез accidental-сложности спеки и кода

What to build: Один проход упрощения по готовым issue 01–02 и спеке: убрать поддержку второй версии Xray, избыточные защитные проверки вне принятой модели угроз («доверенные пользователи машины», единственный пользователь редактирует свои файлы сам) и церемонии, не нужные личному foreground-инструменту. Поведенческие контракты из раздела «Не трогать» сохраняются без изменений.

Blocked by: 02 — Управляемый Xray: полный штатный путь.

Status: done

Резолюция (2026-07-20): реализовано и принято. Дельта: CLI 1123→976, shell.bash 116→91, тесты 1357→1236 строк; сьюты: 34 unittest OK + 8 shell PASS. Ревью: 1 MAJOR (scoped IPv6 в упрощённом валидаторе — починен с регрессионным тестом под Python 3.8), 1 NIT отклонён аргументированно. Non-gating замечания ревью приняты как риски модели угроз.

Сначала прочитать:

  1. Этот файл целиком, затем /tmp/xray-simplify-Zeo5/06-simplify/sweep.md — там точный line-level blast radius каждого пункта (секция 2 — срезы A/B, секция 3 — SIMP-N). Номера строк — ориентир, сверяй с фактическим кодом.
  2. docs/specs/2026-07-15-headless-xray-proxy-design.md — исходная спека.
  3. xray-proxy/.local/bin/xray-proxy, xray-proxy/.config/xray-proxy/shell.bash, tests/test_xray_proxy.py, tests/test_xray_proxy_managed.py.

Команды проверки (обе обязательны):

  • python3 -m unittest discover -s tests — baseline до изменений: 38 тестов, OK, ~60 с;
  • bash tests/test_proxy_launchers.sh — baseline: 9 проверок PASS.

После изменений тестов станет меньше (удаляются тесты срезанных фич); все оставшиеся зелёные, новые счётчики указать в отчёте.

A. Срез dual-version: остаётся только Xray 26.3.27

  • VERSION_LINE принимает только 26.3.27; параметр version убран из сигнатур parse_vless, build_xray_config, safe_summary, warn_legacy и их вызовов; xray_version остаётся воротами «поддержанная версия или отказ» и по-прежнему показывает фактическую версию в сводке.
  • Ветка эмиссии allowInsecure для 25.12.8 и запрет pcs/vcn для 25.12.8 удалены. parse_boolean_fields (валидация точных true/false, требование согласованности обоих полей, предупреждение при истинном значении) сохраняется; allowInsecure никогда не попадает в JSON.
  • Тестовая ось версий схлопнута: lifecycle-тест гоняет один прогон 26.3.27 (обе локации профиля — default и -f — сохраняются); test_old_version_preserves_explicit_legacy_insecure и все 25.12.8-строки табличных тестов удалены.

B. Срез проверки владения сокетом через /proc/net/tcp

  • process_owns_listener, process_socket_inodes, decode_proc_address и их вызовы (в verify_managed_state и readiness-цикле command_start) удалены. Сохраняются: сверка PID/идентичности процесса, сверка адрес/порт машинного конфига со state и check_endpoint-проверка доступности порта.
  • test_verified_child_must_own_configured_listener удалён вместе с веткой FAKE_XRAY_LISTEN_PORT в фейковом xray; test_substituted_machine_port_fails_closed... сохраняется и зелёный.
  • Диагностические сообщения, упоминающие проверку владения слушателем (в verify_managed_state и readiness-цикле command_start), обновлены: после среза они описывают фактические проверки (идентичность процесса, сверка адрес/порт, доступность порта), а не владение сокетом.

C. Диета входной валидации (файлы пишет сам пользователь)

  • validate_address: сначала ipaddress.ip_address, иначе минимальная проверка «непустое ASCII-имя без пробелов, схемы ://, @, / и квадратных скобок». RFC-строгость по меткам DNS, лимит 253 и сопутствующие тесты удалены. Оборачивание IPv6 в [] при сборке URL сохраняется.
  • load_config: сохраняется отказ на неизвестной секции/ключе и на отсутствии обязательного ключа (ловит реальные опечатки) и диапазон порта. Удалены: скан NUL, guard UTF-8-декодирования, отдельный запрет [DEFAULT], запрет CR/многострочных значений, case-sensitive optionxform; соответствующие подкейсы тестов удалены.
  • read_profile: сохраняются разрешение симлинка, проверка прав (group/other → ошибка с подсказкой chmod 600) и правило «ровно одна непустая строка». Удалены скан NUL и guard UTF-8.
  • parse_query: сохраняются отказ на неизвестном и на повторном query-параметре. Удалены regex на сырые %-последовательности и посимвольный запрет управляющих символов.

D. Диета церемоний

  • write_private_json: простая запись файла с правами 0600 внутри уже-0700 каталога; O_EXCL/fsync/os.replace/PID-суффиксы удалены.
  • Валидация PROXY_PREFLIGHT_TIMEOUT и XRAY_PROXY_STOP_TIMEOUT — один guard «float() успешен и > 0, иначе понятная ошибка»; math не импортируется; тесты экстремальных значений (nan/inf/1e-7/1e308) удалены. Для PROXY_PREFLIGHT_TIMEOUT оставить один тест «плохое значение → понятный отказ»; для XRAY_PROXY_STOP_TIMEOUT отдельного теста нет и добавлять его не нужно — достаточно guard'а.
  • check_reachable использует socket.create_connection(timeout=...) без SIGALRM/setitimer-машинерии. Принятый риск (записан здесь): зависший DNS-резолв на external-пути не ограничен таймаутом — для личного инструмента пользователь прерывает вручную.
  • extra и alpn разбираются один раз в parse_vless, результат сохраняется в профиле и переиспользуется в build_xray_config без повторного разбора. parse_json_object сохраняет оба отказа — на повторах ключей и на JSON-константах (reject_constant остаётся): существующий тест требует отказ на уровне разбора с диагностикой про extra и без запуска run -test, и это дешевле, чем переносить ответственность на сериализацию.
  • process_alive — только os.kill(pid, 0); чтение /proc/PID/stat ради zombie-состояния удалено.
  • Отпечаток профиля (profile_fingerprint) удалён из state и кода: он не участвует ни в одном решении. Смена профиля по-прежнему блокируется существованием state. Вместе с ним обновлены STATE_KEYS и все тестовые фикстуры state (включая точную сверку набора ключей в managed-тестах): устаревший ключ в фикстуре не должен ронять сверку ключей раньше, чем тест дойдёт до проверяемой идентичности процесса.

E. shell.bash: средняя форма приёма env

  • _proxy_apply_env_file: при ненулевом коде возврата CLI окружение не меняется вовсе; успешный вывод разбирается построчно по первому =; применяются (через printf -v, без eval) только восемь известных ключей, прочие строки — ошибка без частичного экспорта. Удалены сканы NUL/CR, отдельный детектор повторов и проверка «ровно восемь строк». Свойство «нет частичного изменения окружения при ошибке» сохраняется и остаётся покрыто тестом.
  • tests/test_proxy_launchers.sh приведён к средней форме: проверки повторов/CR/NUL/счётчика строк, соответствующие удалённой машинерии, убраны или переписаны; проверки «ненулевой код возврата CLI → окружение не тронуто», «неизвестная строка → ошибка без частичного экспорта» и владения функциями остаются зелёными.

F. Спека и соседние issue приведены к новому объёму

  • Из спеки удалены: ветка 25.12.8 (все места из секции 2 sweep-файла), флаги -v/--debug (не реализованы), упоминание отпечатка профиля в state; описания валидации/атомарности смягчены до фактического поведения после этого issue. Формулировка идентичности процессов («PID + время старта + исполняемый файл») в спеке не меняется.
  • Issue 03: добавлен явный мандат «идентичность = PID + время старта (/proc/PID/stat) + исполняемый файл ребёнка / путь скрипта супервизора; текущий argv-обход заменить этим механизмом»; убраны унаследованные упоминания срезанных фич, если есть; строка Blocked by дополнена: «06 — Упрощение».
  • Issue 05: приёмка сведена к одной версии Xray 26.3.27; упоминания 25.12.8 удалены. Пункты про РЕД ОС сохранены (решение пользователя 2026-07-20: целевые системы шире Ubuntu — РЕД ОС, возможно Astra Linux и Alt Linux; инструмент могут использовать коллеги).
  • В спеке в раздел ручной проверки добавлены Astra Linux и Alt Linux как кандидаты «по мере доступности» с тем же правилом, что для РЕД ОС: недоступная среда явно записывается как неподтверждённая, поддержка не заявляется без фактической проверки версии Python и прогона.
  • В issue 01 и 02 добавлена одна строка после Status: «Часть требований пересмотрена в 06 — см. 06-simplification-pass.md»; чекбоксы 01 и 02 не редактируются (issue 01 описывает строгую приёмку env, которую этот issue смягчает, — без пометки он противоречил бы коду).

G. Не трогать (границы)

  • Строгий отказ на неизвестном/повторном query-параметре VLESS.
  • Точечный stop только по проверенному PID; никакого pkill.
  • Foreground-супервизор, проброс сигналов, очистка state.
  • Изоляция окружения run (proxy-переменные только у ребёнка), сохранение argv и кода возврата, ---разделитель.
  • Fail-fast pclaude/pcodex, обратная совместимость: без машинного конфига — внешний hproxy.local:12334.
  • Проверка прав файла профиля (chmod 600) и loopback-only вход.
  • Авто-диагностика внешнего IP на старте (несмертельная) — остаётся.
  • Секреты: полная VLESS-ссылка, UUID, ключи не появляются в stdout/stderr/ argv — все существующие тесты на это остаются зелёными.
  • bash/.bash_functions, установщик, README — не входят в этот issue.
  • Никаких коммитов.

Отчёт исполнителя

  1. Изменённые файлы со строкой «зачем» на каждый.
  2. Команда тестов и счётчик (все зелёные, указать новое число).
  3. Чекбоксы: закрыт / не закрыт и почему.
  4. Итоговая дельта wc -l по CLI, shell.bash и обоим тест-файлам против baseline (1123 / 116 / 467 / 890).
  5. Сомнения, если есть.