- Зачем: - прежнее имя выпячивало Xray, хотя клиент поддерживает также внешний прокси, а два каталога конфигурации были неочевидны. - Что: - пакет, CLI, конфигурация, состояние, тесты и документация переименованы в proxy-client. - машинный конфиг, профиль и два явных примера собраны в одном пользовательском каталоге. - свежая установка защищена от сворачивания каталога конфигурации внутрь репозитория. - Проверка: - bash tests/test_proxy_client_full.sh. - bash tests/test_install.sh. - ручные start, status и xray run -test; незавершённая сетевая приёмка записана в handoff.
181 lines
15 KiB
Markdown
181 lines
15 KiB
Markdown
# 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-proxy-client-design.md` — исходная спека.
|
||
3. `proxy-client/.local/bin/proxy-client`, `proxy-client/.config/proxy-client/shell.bash`,
|
||
`tests/test_proxy_client.py`, `tests/test_proxy_client_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
|
||
|
||
- [x] `VERSION_LINE` принимает только `26.3.27`; параметр `version` убран из
|
||
сигнатур `parse_vless`, `build_xray_config`, `safe_summary`, `warn_legacy`
|
||
и их вызовов; `xray_version` остаётся воротами «поддержанная версия или
|
||
отказ» и по-прежнему показывает фактическую версию в сводке.
|
||
- [x] Ветка эмиссии `allowInsecure` для 25.12.8 и запрет `pcs`/`vcn` для
|
||
25.12.8 удалены. `parse_boolean_fields` (валидация точных `true`/`false`,
|
||
требование согласованности обоих полей, предупреждение при истинном
|
||
значении) сохраняется; `allowInsecure` никогда не попадает в JSON.
|
||
- [x] Тестовая ось версий схлопнута: lifecycle-тест гоняет один прогон
|
||
26.3.27 (обе локации профиля — default и `-f` — сохраняются);
|
||
`test_old_version_preserves_explicit_legacy_insecure` и все
|
||
25.12.8-строки табличных тестов удалены.
|
||
|
||
## B. Срез проверки владения сокетом через /proc/net/tcp
|
||
|
||
- [x] `process_owns_listener`, `process_socket_inodes`, `decode_proc_address`
|
||
и их вызовы (в `verify_managed_state` и readiness-цикле `command_start`)
|
||
удалены. Сохраняются: сверка PID/идентичности процесса, сверка
|
||
адрес/порт машинного конфига со state и `check_endpoint`-проверка
|
||
доступности порта.
|
||
- [x] `test_verified_child_must_own_configured_listener` удалён вместе с
|
||
веткой `FAKE_XRAY_LISTEN_PORT` в фейковом xray;
|
||
`test_substituted_machine_port_fails_closed...` сохраняется и зелёный.
|
||
- [x] Диагностические сообщения, упоминающие проверку владения слушателем
|
||
(в `verify_managed_state` и readiness-цикле `command_start`), обновлены:
|
||
после среза они описывают фактические проверки (идентичность процесса,
|
||
сверка адрес/порт, доступность порта), а не владение сокетом.
|
||
|
||
## C. Диета входной валидации (файлы пишет сам пользователь)
|
||
|
||
- [x] `validate_address`: сначала `ipaddress.ip_address`, иначе минимальная
|
||
проверка «непустое ASCII-имя без пробелов, схемы `://`, `@`, `/` и
|
||
квадратных скобок». RFC-строгость по меткам DNS, лимит 253 и
|
||
сопутствующие тесты удалены. Оборачивание IPv6 в `[]` при сборке URL
|
||
сохраняется.
|
||
- [x] `load_config`: сохраняется отказ на неизвестной секции/ключе и на
|
||
отсутствии обязательного ключа (ловит реальные опечатки) и диапазон
|
||
порта. Удалены: скан NUL, guard UTF-8-декодирования, отдельный запрет
|
||
`[DEFAULT]`, запрет CR/многострочных значений, case-sensitive
|
||
`optionxform`; соответствующие подкейсы тестов удалены.
|
||
- [x] `read_profile`: сохраняются разрешение симлинка, проверка прав
|
||
(group/other → ошибка с подсказкой `chmod 600`) и правило «ровно одна
|
||
непустая строка». Удалены скан NUL и guard UTF-8.
|
||
- [x] `parse_query`: сохраняются отказ на неизвестном и на повторном
|
||
query-параметре. Удалены regex на сырые `%`-последовательности и
|
||
посимвольный запрет управляющих символов.
|
||
|
||
## D. Диета церемоний
|
||
|
||
- [x] `write_private_json`: простая запись файла с правами 0600 внутри
|
||
уже-0700 каталога; O_EXCL/fsync/`os.replace`/PID-суффиксы удалены.
|
||
- [x] Валидация `PROXY_PREFLIGHT_TIMEOUT` и `PROXY_CLIENT_STOP_TIMEOUT` — один
|
||
guard «`float()` успешен и > 0, иначе понятная ошибка»; `math` не
|
||
импортируется; тесты экстремальных значений (nan/inf/1e-7/1e308)
|
||
удалены. Для `PROXY_PREFLIGHT_TIMEOUT` оставить один тест «плохое
|
||
значение → понятный отказ»; для `PROXY_CLIENT_STOP_TIMEOUT` отдельного
|
||
теста нет и добавлять его не нужно — достаточно guard'а.
|
||
- [x] `check_reachable` использует `socket.create_connection(timeout=...)`
|
||
без SIGALRM/setitimer-машинерии. Принятый риск (записан здесь):
|
||
зависший DNS-резолв на external-пути не ограничен таймаутом — для
|
||
личного инструмента пользователь прерывает вручную.
|
||
- [x] `extra` и `alpn` разбираются один раз в `parse_vless`, результат
|
||
сохраняется в профиле и переиспользуется в `build_xray_config` без
|
||
повторного разбора. `parse_json_object` сохраняет оба отказа —
|
||
на повторах ключей и на JSON-константах (`reject_constant` остаётся):
|
||
существующий тест требует отказ на уровне разбора с диагностикой про
|
||
`extra` и без запуска `run -test`, и это дешевле, чем переносить
|
||
ответственность на сериализацию.
|
||
- [x] `process_alive` — только `os.kill(pid, 0)`; чтение
|
||
`/proc/PID/stat` ради zombie-состояния удалено.
|
||
- [x] Отпечаток профиля (`profile_fingerprint`) удалён из state и кода:
|
||
он не участвует ни в одном решении. Смена профиля по-прежнему
|
||
блокируется существованием state. Вместе с ним обновлены `STATE_KEYS` и
|
||
все тестовые фикстуры state (включая точную сверку набора ключей в
|
||
managed-тестах): устаревший ключ в фикстуре не должен ронять сверку
|
||
ключей раньше, чем тест дойдёт до проверяемой идентичности процесса.
|
||
|
||
## E. shell.bash: средняя форма приёма `env`
|
||
|
||
- [x] `_proxy_apply_env_file`: при ненулевом коде возврата CLI окружение не
|
||
меняется вовсе; успешный вывод разбирается построчно по первому `=`;
|
||
применяются (через `printf -v`, без `eval`) только восемь известных
|
||
ключей, прочие строки — ошибка без частичного экспорта. Удалены сканы
|
||
NUL/CR, отдельный детектор повторов и проверка «ровно восемь строк».
|
||
Свойство «нет частичного изменения окружения при ошибке» сохраняется и
|
||
остаётся покрыто тестом.
|
||
- [x] `tests/test_proxy_launchers.sh` приведён к средней форме: проверки
|
||
повторов/CR/NUL/счётчика строк, соответствующие удалённой машинерии,
|
||
убраны или переписаны; проверки «ненулевой код возврата CLI → окружение
|
||
не тронуто», «неизвестная строка → ошибка без частичного экспорта» и
|
||
владения функциями остаются зелёными.
|
||
|
||
## F. Спека и соседние issue приведены к новому объёму
|
||
|
||
- [x] Из спеки удалены: ветка 25.12.8 (все места из секции 2 sweep-файла),
|
||
флаги `-v`/`--debug` (не реализованы), упоминание отпечатка профиля в
|
||
state; описания валидации/атомарности смягчены до фактического
|
||
поведения после этого issue. Формулировка идентичности процессов
|
||
(«PID + время старта + исполняемый файл») в спеке не меняется.
|
||
- [x] Issue 03: добавлен явный мандат «идентичность = PID + время старта
|
||
(`/proc/PID/stat`) + исполняемый файл ребёнка / путь скрипта
|
||
супервизора; текущий argv-обход заменить этим механизмом»; убраны
|
||
унаследованные упоминания срезанных фич, если есть; строка Blocked by
|
||
дополнена: «06 — Упрощение».
|
||
- [x] Issue 05: приёмка сведена к одной версии Xray 26.3.27; упоминания
|
||
25.12.8 удалены. Пункты про РЕД ОС сохранены (решение пользователя
|
||
2026-07-20: целевые системы шире Ubuntu — РЕД ОС, возможно Astra Linux
|
||
и Alt Linux; инструмент могут использовать коллеги).
|
||
- [x] В спеке в раздел ручной проверки добавлены Astra Linux и Alt Linux
|
||
как кандидаты «по мере доступности» с тем же правилом, что для РЕД ОС:
|
||
недоступная среда явно записывается как неподтверждённая, поддержка не
|
||
заявляется без фактической проверки версии Python и прогона.
|
||
- [x] В 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. Сомнения, если есть.
|