zapret-kvn/docs/sing-box/routing-gui.md
loop-uh e26419216a
Some checks failed
Windows project source guards / test (push) Failing after 1m12s
Программа «через VPN» — кроме российских сайтов; диагностика видит решения маршрута
Пользователь 0.8.36: VK и Яндекс перестали открываться, пока не добавил
прямые правила. В диагностике через туннель шли 29–39 % соединений к
российским IP (geoip-ru), но по ней нельзя было понять, каким правилом:
сайдкар видит только IP, а конфиг — уже исправленный. Шаблон и данные
маршрутизации в 0.8.30…0.8.36 не менялись (srs побайтно равны); причина —
правило пользователя, вставшее выше стокового «РФ напрямую».

- «Простое правило»: программа «Через VPN» по умолчанию «Кроме российских
  сайтов» — native logical and с {rule_set geosite-category-ru/geoip-ru (если
  есть в документе), domain_suffix ru/xn--p1ai/su, invert}; российское
  назначение идёт дальше по списку. Без галочки — предупреждение, что VK,
  Яндекс, банки уйдут в туннель. Семантика проверена ядром той же версии:
  `domain_suffix && !(domain_suffix=[ru xn--p1ai su]) => route(...)`.
- diagnostics/route_decisions: сводка решений маршрута по последним четырём
  сессиям (процесс, сайт из двух меток или IP, правило ядра «… => …»/final,
  маршрут, число соединений; отдельно российские сайты в туннеле) из того же
  опроса Clash API /connections, что метрики; журнал живёт у контроллера и
  переживает переподключение.
- runtime_redacted: route_history — правила route последних восьми запусков
  sing-box.
- Тесты: форма правила, explain_route (российские сайты мимо правила к
  стоковому direct), страница (галочка, предупреждение, «Напрямую»), сводка и
  история; docs/sing-box/routing-gui.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 19:05:27 +03:00

133 lines
13 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Маршрутизация: структурный редактор sing-box
Раздел «Маршрутизация» в боковом меню редактирует **активный native JSON**
sing-box (`data/configs/sing-box/*.json`). Своей модели маршрутизации у
приложения нет: формы меняют разобранный JSON на месте и записывают его обратно,
страница «JSON» показывает тот же документ.
## Страницы
| Подпункт | Что редактирует |
|---|---|
| Обзор | выбор конфига и шаблона, сравнение со стоком, сброс всего конфига или одного раздела, статус |
| Правила | `route.rules` (сверху вниз, первое совпадение) и `route.final` |
| Наборы правил | `route.rule_set`: local / remote / inline |
| DNS | параметры `dns`, `dns.servers`, `dns.rules` |
| Исходящие | `outbounds` и `endpoints`; `proxy` — слот выбранного сервера, только просмотр |
| Система | `inbounds` (TUN), остальные `route.*`, `log`, `experimental`, прочие секции |
| JSON | весь документ текстом |
Правки живые: «Готово» закрывает подстраницу, «Отменить правки» возвращает объект
к состоянию на момент открытия. На диск пишут только «Сохранить»/«Применить»
в верхней панели; «Проверить» запускает `sing-box check` бандл-ядром по
редактируемому JSON в фоне (служебные подстановки запуска в эту проверку не входят).
## Простое правило и «Проверить сайт»
- «Добавить правило» → «Сайты, IP, программы (просто)» (`ui/singbox/simple_rule_page.py`,
разбор — `singbox_config/simple_rule.py`): куда (через VPN / напрямую / блок) и
списки по одному в строке в синтаксисе v2rayN. `2ip.ru` — `domain_suffix` (сайт и
поддомены), `full:`/`keyword:`/`regexp:`, `geosite:`/`geoip:` — существующие наборы,
`geoip:private` — `ip_is_private`, программа с путём — `process_path`. Сайты/IP и
программы собираются в `logical` `or`: в ядре разные виды условий идут через И.
Документ не меняется до «Добавить»; правило встаёт сразу после `sniff`/`hijack-dns`.
«Добавить и применить» идёт тем же путём, что «Применить».
- Программа «Через VPN» по умолчанию идёт «Кроме российских сайтов»: правило встаёт
выше стокового «РФ напрямую», и без исключения VK, Яндекс, банки этой программы
уходили в туннель, где с зарубежного адреса часто не открываются (16.10.2026).
Исключение — обычное native-правило ядра: `logical` `and` из условий пользователя
и `{"rule_set": [geosite-category-ru, geoip-ru], "domain_suffix": [ru, xn--p1ai, su],
"invert": true}`; наборы берутся, только если есть в документе. Российское
назначение не совпадает с правилом и идёт дальше по списку. Без галочки страница
предупреждает, что российские сайты программы тоже пойдут через VPN.
- Диагностика маршрута: в `runtime_redacted.json` — `route_decisions` (по последним
четырём сессиям: процесс, сайт из двух меток имени или IP, правило ядра
«… => route(…)» или `final`, маршрут и число соединений; отдельно — российские сайты,
ушедшие в туннель) из того же опроса Clash API `/connections`, что метрики, и
`components["sing-box"].route_history` — правила `route` последних восьми запусков.
- «Проверить сайт» над списком (`singbox_config/route_explain.py`,
`application/route_check.py`): порядок ядра — сверху вниз до первого совпадения,
`sniff`/`resolve`/`route-options` не останавливают, иначе `route.final`. Запрос —
браузер: TCP/443/TLS, программа неизвестна; условия вроде процесса дают «может
перехватить», перебор идёт дальше. IP-условия — только в TUN или после `resolve`.
Наборы проверяет само ядро: `sing-box rule-set match -f <format>` (без `-f` ядро
не читает `.srs`), наборы по URL — «не узнать». Проверяется редактируемый документ,
включая несохранённые правки.
## Откуда берутся поля
`assets/sing-box-schema/schema.json` — JSON Schema, которую генерирует само
закреплённое ядро (`sing-box schema`) из своих Go-структур:
`python3 scripts/generate_singbox_schema.py` после смены пина sing-box в
`scripts/core-lock.windows-x64.json`. Форк не умеет описать несколько своих
типов (amnezia-диапазоны endpoints, byte-размеры, providers); генератор
ослабляет только их и пишет пути в `loose-paths.txt`. Тест гарантирует, что их
нет в `route`, `dns`, `inbounds`, `outbounds`, `log` и `experimental`; такие
поля endpoints редактируются как JSON.
`sing-box check` по исходному JSON не видит служебных подстановок запуска.
Поэтому outbound `direct` и DNS-сервер `bootstrap-dns`, на которые запуск
ссылается сам, помечены как нужные приложению, а их удаление требует
подтверждения.
- `xray_fluent/singbox_config/schema.py` — чтение схемы: поля объекта с учётом
дискриминатора (`type`, `action`), `Listable`-поля, ссылки на теги
(`x-tag-reference`).
- `xray_fluent/singbox_config/catalog.py` — только подписи, закреплённые поля и
сводки строк. Поле без подписи всё равно доступно под своим native-ключом.
- `xray_fluent/ui/singbox/form.py` — форма показывает заданные, обязательные и
закреплённые поля; всё остальное, что знает ядро, — в «Добавить параметр».
Смена `type`/`action` убирает поля, которые новый вариант не принимает.
## Инварианты
- Открытие страниц и объектов не меняет JSON (пустые секции создаются только
при первой настоящей правке).
- Неизвестные ядру ключи сохраняются и показываются как «неизвестное поле».
- Одиночное значение `Listable`-поля остаётся скаляром, пока в нём один элемент.
- При ошибке JSON разделы только читают и предлагают исправить текст на странице «JSON».
- Обновление приложения сливает стоковый шаблон посекционно
(`template_sync.merge_stock_sections`): нетронутые разделы следуют за стоком,
изменённые остаются вашими. Принудительной синхронизации DNS больше нет.
- Виджеты редактора удаляются только через `qt_lifecycle.dispose_later`:
скрытие сразу, разрушение на следующем такте при выключенном циклическом GC
(у `ScrollArea`/`PlainTextEdit`/`RoundMenu` qfluentwidgets есть
`SmoothScrollDelegate`, гонка GC с деструкторами роняет процесс, на Windows —
особенно). Меню «Добавить…» строятся по клику и удаляются после закрытия.
- qfluentwidgets 1.11 не освобождает Python-обёртки удалённых стилизованных
виджетов (лямбды на `destroyed` и `qconfig.themeChanged`). Глобально это не
патчится: освобождённые обёртки попадают под ту же гонку GC во всём
приложении. Поэтому редактор бережёт виджеты: строки списков обновляются на
месте, невидимые разделы пересобираются только при показе, повторная загрузка
того же текста ничего не пересобирает, схема ядра грузится при первой сборке
раздела.
- Смена `type`/`action` пересобирает форму на следующем такте: выбор приходит из
всплывающего меню самого комбобокса.
- Незафиксированный ввод (списки и JSON применяются с задержкой) сбрасывается в
документ по потере фокуса, «Готово», переключению раздела и перед
«Сохранить/Применить/Проверить».
## Графика и анимация
- Строки списков несут значок-плитку (`ui/singbox/visuals.py`): тон по смыслу —
прокси = акцент, напрямую = зелёный (`success`), блок = `error`, DNS = `info`,
служебные действия = `warning`. Цвета берутся из `ui/theme.py` в момент
отрисовки, так что следуют теме и акценту; hex-литералов вне `theme.py` нет.
- На странице раздела только заголовок и данные. Пояснения для новичков и живая
иллюстрация открываются ссылкой «Как это б#&^ь работает?» в стоковом окне
`MessageBoxBase` поверх приложения (`ui/singbox/guide.py`: тексты `GUIDES`,
окно удаляется через `dispose_later`). Иллюстрация (`ui/singbox/art.py`,
`QPainter`) рисует конфиг пользователя: «Обзор» — карта, где пакеты уходят в прокси,
напрямую или в блок пропорционально числу правил (`visuals.rule_outcomes`);
«Правила» — зонд идёт сверху вниз и останавливается на первом совпадении;
«DNS» — вращающийся глобус и запрос/ответ; «Исходящие» — хаб с импульсами;
«Система» — туннель TUN; «JSON» — набор текста.
- Анимации работают на `motion.FrameGate`: кадры (25 fps, у JSON 12 fps) идут
только пока виджет виден и в Windows не выключена анимация интерфейса; иначе
рисуется неподвижный кадр. Фаза берётся из реального времени.
- Иконки Fluent рисуются из кэша растров по (иконка, цвет, размер, DPR):
`FluentIcon.render(fill=…)` разбирает SVG на каждом вызове.
- Микроанимации: вспышка строки при добавлении/дублировании/перемещении,
пульсирующая точка несохранённых правок, подстраницы въезжают справа, возврат —
слева, разделы появляются снизу (`PopUpAniStackedWidget` qfluentwidgets; текущая
страница переключается сразу, анимируется только положение).