Пользователь 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>
13 KiB
Маршрутизация: структурный редактор 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 и программы собираются вlogicalor: в ядре разные виды условий идут через И. Документ не меняется до «Добавить»; правило встаёт сразу послеsniff/hijack-dns. «Добавить и применить» идёт тем же путём, что «Применить». - Программа «Через VPN» по умолчанию идёт «Кроме российских сайтов»: правило встаёт
выше стокового «РФ напрямую», и без исключения VK, Яндекс, банки этой программы
уходили в туннель, где с зарубежного адреса часто не открываются (16.10.2026).
Исключение — обычное native-правило ядра:
logicalandиз условий пользователя и{"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/RoundMenuqfluentwidgets есть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 на каждом вызове. - Микроанимации: вспышка строки при добавлении/дублировании/перемещении,
пульсирующая точка несохранённых правок, подстраницы въезжают справа, возврат —
слева, разделы появляются снизу (
PopUpAniStackedWidgetqfluentwidgets; текущая страница переключается сразу, анимируется только положение).