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>
This commit is contained in:
1 parent
71e16a795a
commit
98bfd6338a
12 files changed
+384
-508
No files matched your search
@@ -9,6 +9,8 @@ Hiddify. Привычные Bash-команды переходят на един
|
||||
|
||||
**Status:** done
|
||||
|
||||
Часть требований пересмотрена в 06 — см. 06-simplification-pass.md.
|
||||
|
||||
- [x] CLI работает на Python 3.8+ без сторонних библиотек, ничего не устанавливает
|
||||
и не запускает без явной подкоманды и выдаёт понятную ошибку на более старом
|
||||
Python.
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
**Status:** done
|
||||
|
||||
Часть требований пересмотрена в 06 — см. 06-simplification-pass.md.
|
||||
|
||||
- [x] `start` разрешён только при явном типе `xray`, требует loopback-адрес и
|
||||
согласованный локальный порт, никогда не скачивает бинарник и выбирает первый
|
||||
`xray` из `PATH`. Один разрешённый путь используется для `version`,
|
||||
|
||||
@@ -4,16 +4,20 @@
|
||||
сигналы, устаревшее состояние и аварийное исчезновение супервизора, не затрагивая
|
||||
чужие процессы и не оставляя секретные или временные данные.
|
||||
|
||||
**Blocked by:** 02 — Управляемый Xray: полный штатный путь.
|
||||
**Blocked by:** 02 — Управляемый Xray: полный штатный путь; 06 — Упрощение.
|
||||
|
||||
**Status:** ready-for-agent
|
||||
|
||||
**Мандат идентичности:** идентичность = PID + время старта (`/proc/PID/stat`) +
|
||||
исполняемый файл ребёнка / путь скрипта супервизора; текущий argv-обход заменить
|
||||
этим механизмом.
|
||||
|
||||
- [ ] Супервизор владеет пользовательской блокировкой, допускает только один
|
||||
управляемый экземпляр и не заменяет запущенный профиль другим без явных
|
||||
`stop` и нового `start`.
|
||||
- [ ] Состояние с правами `0600` хранит PID и время старта супервизора и Xray,
|
||||
путь к бинарнику, порт и безопасный отпечаток профиля, но не ссылку или её
|
||||
секреты; служебные каталоги имеют права `0700`.
|
||||
путь к бинарнику и порт, но не ссылку или её секреты; служебные каталоги имеют
|
||||
права `0700`.
|
||||
- [ ] `status` и `stop` сверяют PID со временем старта и ожидаемым исполняемым
|
||||
файлом, поэтому чужой, устаревший и повторно использованный PID никогда не
|
||||
считается управляемым процессом.
|
||||
|
||||
@@ -19,17 +19,16 @@
|
||||
- [ ] Основная инструкция объясняет внешний Hiddify и управляемый Xray,
|
||||
подготовку двух файлов настройки, все команды управления, `proxy_on/off`,
|
||||
`pclaude/pcodex`, `NO_PROXY`, ограничения CIDR и диагностику.
|
||||
- [ ] Инструкция фиксирует две поддержанные семьи VLESS, современную проверку
|
||||
сертификата, явный запасной Xray 25.12.8 и отсутствие systemd, автозапуска и
|
||||
- [ ] Инструкция фиксирует две поддержанные семьи VLESS, Xray 26.3.27,
|
||||
современную проверку сертификата и отсутствие systemd, автозапуска и
|
||||
автоматического перехода между типами прокси.
|
||||
- [ ] Корневой обзор кратко описывает подсистему, показывает подключение пакета
|
||||
и направляет к полной инструкции, не дублируя её.
|
||||
- [ ] Полный автоматический прогон подтверждает Python 3.8-совместимый синтаксис,
|
||||
внешний Hiddify-сценарий, обе семьи VLESS, обе версии Xray, установку,
|
||||
внешний Hiddify-сценарий, обе семьи VLESS, Xray 26.3.27, установку,
|
||||
жизненный цикл, Bash-интеграцию и отсутствие утечки секретов.
|
||||
- [ ] На доступной Ubuntu с Python 3.10 проверены установка, `xray run -test` и
|
||||
работа HTTP и SOCKS через один локальный вход для заявленных совместимых
|
||||
сочетаний Xray 26.3.27 и 25.12.8.
|
||||
работа HTTP и SOCKS через один локальный вход для Xray 26.3.27.
|
||||
- [ ] На целевой удалённой системе выполнен тот же пользовательский сценарий;
|
||||
версия Python в РЕД ОС проверена до заявления поддержки, а недоступная среда
|
||||
явно записана как неподтверждённая вместо неподкреплённого обещания.
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
# 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
|
||||
|
||||
- [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` и `XRAY_PROXY_STOP_TIMEOUT` — один
|
||||
guard «`float()` успешен и > 0, иначе понятная ошибка»; `math` не
|
||||
импортируется; тесты экстремальных значений (nan/inf/1e-7/1e308)
|
||||
удалены. Для `PROXY_PREFLIGHT_TIMEOUT` оставить один тест «плохое
|
||||
значение → понятный отказ»; для `XRAY_PROXY_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. Сомнения, если есть.
|
||||
@@ -0,0 +1,35 @@
|
||||
# Токен-леджер проекта headless-xray-proxy
|
||||
|
||||
Затраты по этапам конвейера, для калибровки эффорта и планирования лимитов
|
||||
(5-часовые / недельные окна подписок). Прямое сравнение корректно внутри
|
||||
провайдера; между Claude и Codex метрики разные (у Claude-субагентов
|
||||
харнесс даёт один суммарный счётчик, у Codex — раздельные input/cached/
|
||||
output/reasoning), кросс-сравнение — ориентировочное.
|
||||
|
||||
## Задача 06 — упрощение (baseline: реализация на HIGH)
|
||||
|
||||
| Этап | Агент / модель | Effort | Токены | Прочее |
|
||||
|---|---|---|---|---|
|
||||
| KISS-обход спеки и кода | Claude Opus (субагент) | default | 100 025 суммарно | 14 tool uses, 4.5 мин |
|
||||
| Ревью постановки | Codex gpt-5.6-sol | high | in 1 263 557 (кэш 1 174 784 → свежих ~89k), out 16 862 (reasoning 12 817) | 82 команды |
|
||||
| Реализация | Codex gpt-5.6-sol | high | in 6 680 053 (кэш 6 492 928 → свежих ~187k), out 29 064 (reasoning 8 280) | 84 команды, −331 строка |
|
||||
| Саморевью | Codex (resume) | medium | инкремент: свежих in ~155k, out ~4.5k | 8 команд, found 2 fixed 2 |
|
||||
| Слепое ревью «по задаче» | Claude Opus (субагент) | default | 75 216 суммарно | 26 tool uses, 4.3 мин; 1 NIT |
|
||||
| Слепое ревью «по коду» | Codex gpt-5.6-sol (свежий тред) | high | in 2 336 162 (кэш 2 106 112 → свежих ~230k), out 12 434 (reasoning 7 033) | 48 команд; 1 MAJOR |
|
||||
| Триаж/починка | Codex (resume) | medium | инкремент: свежих in ~26k, out ~7.3k | 18 команд; FIXED 1, REJECTED 1 |
|
||||
| Перепроверка CODE-1 | Codex-ревьюер (resume) | medium | инкремент: свежих in ~137k (вкл. оборванный первый заход), out ~1.8k | FIXES_CONFIRMED |
|
||||
|
||||
Итог 06 (high-baseline): свежий input ~735k + output ~97k на весь конвейер
|
||||
(обход, ревью постановки, реализация, саморевью, 2 слепых ревью, триаж,
|
||||
перепроверка); Claude-доля — 175k суммарно (обход 100k + ревью 75k).
|
||||
|
||||
## Задача 03 / 04 (эксперимент: реализация на MEDIUM)
|
||||
|
||||
_Заполняется по ходу; сравнивать построчно с 06._
|
||||
|
||||
## Наблюдения
|
||||
|
||||
- Кэш у Codex покрывает ~93% входа ревью-этапа: длинные треды с большим
|
||||
контекстом дешевле по лимитам, чем кажется по сырому input.
|
||||
- Сравнивать этапы честнее по «свежий input + output (включая reasoning)»,
|
||||
а не по валовому input.
|
||||
Reference in new issue
Block a user