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

13 KiB
Raw Permalink Blame History

Маршрутизация: структурный редактор 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; текущая страница переключается сразу, анимируется только положение).