Страница RoutingPage нигде не создавалась, а её пресеты сайтов и приложений (service_presets, process_presets) читал только старый сборщик build_xray_config, который в рабочем пути не участвует: маршрут задаёт сырой JSON активного конфига. Пресет «Google → напрямую» при этом выглядел как действующее правило и уводил разбор жалоб в сторону. Поля service_routes и process_preset_routes убраны из RoutingSettings; старый профиль читается как прежде, лишние ключи пропадают при сохранении. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
7 KiB
Native Routing Contract
Ownership map
| Layer | Paths | Authority |
|---|---|---|
| Shipped templates | data/templates/sing-box/*.json, data/templates/xray/*.json |
Versioned defaults and reset/import sources |
| Active raw configs | data/configs/sing-box/*.json, data/configs/xray/*.json |
Runtime routing and DNS source of truth |
| Runtime copies | data/runtime/*.json or manager-owned temporary files |
Launch artifacts; do not author product policy here |
| Legacy GUI routing | RoutingSettings, engines/xray/config_builder.py |
Separate tun2socks/legacy path, not raw sing-box/Xray modes |
application/profile_service.py copies a selected or imported template into the corresponding active config. Reset performs the same copy. Saving in the raw editor writes the active config.
The self-updater preserves the installed data/ directory. To deliver template updates through that boundary, build.py generates assets/template-update from the versioned data/templates tree. Before the UI starts, template_sync.py compares each same-path active config with the previously installed template. A JSON-equivalent copy is replaced by the new template; otherwise each top-level section that still equals the previous template follows the new one (merge_stock_sections) and every user-edited section stays verbatim. No section, dns included, is rewritten outside this update merge. The generated asset is transport, not a second authoring source.
The «Маршрутизация» GUI (xray_fluent/ui/configs_page.py, xray_fluent/ui/singbox/) edits the same active JSON: forms are generated from the pinned core schema in assets/sing-box-schema/ and change the parsed document in place, preserving unknown keys, key order and the scalar/list form of Listable fields. It is an editor of native JSON, not a routing layer; the JSON page shows the identical document.
Mode map
| Mode | Routing owner | App-owned runtime work |
|---|---|---|
| System proxy to sing-box | Active sing-box JSON | Replace source proxy/TUN inbounds with public SOCKS/HTTP inbounds; allocate ports |
| sing-box TUN | Active sing-box JSON | Remove proxy inbounds; assign fresh TUN interface name |
| System/manual proxy to Xray | Active Xray JSON | Allocate proxy/API ports and add metrics contract |
| Xray TUN | Active Xray JSON | Add/keep TUN inbound, strip proxy inbounds, bind outbounds to the physical interface when required |
| sing-box hybrid Xray sidecar | Active sing-box JSON at the front | Route only the proxy outbound through the generated sidecar; preserve sing-box direct decisions |
| tun2socks fallback | GUI RoutingSettings and generated Xray config |
Outside raw JSON ownership |
Native sing-box rules
Use route.rules and upstream field names such as:
process_name,process_path,process_path_regex;domain,domain_suffix,domain_keyword,domain_regex;ip_cidr,ip_is_private;rule_set,protocol,inbound;actionandoutbound.
Prefer the current explicit route action:
{
"process_name": [
"Example.exe",
"example-helper.exe"
],
"action": "route",
"outbound": "direct"
}
Do not put Xray values such as domain:example.com, full:example.com, geosite:*, or geoip:* into a sing-box native rule. Use sing-box fields or native rule_set entries.
Keep this normal priority shape:
- non-final
{"action": "sniff"}; - DNS handling such as
{"protocol": "dns", "action": "hijack-dns"}; - narrow protected/direct process rules;
- LAN and other narrow rules;
- broader process/domain/rule-set rules;
route.finalfor unmatched traffic.
Rules are evaluated in order. A final route action stops ordinary matching. Put a forced direct process rule before any broader proxy rule that can match the same traffic.
The runtime may insert narrowly scoped endpoint, metrics, or hybrid-protection rules ahead of source-authored policy. Those rules match app-owned inbounds or the selected proxy endpoint and must not become a second product-policy layer.
Ensure the referenced tag exists, normally:
{
"type": "direct",
"tag": "direct",
"domain_resolver": "bootstrap-dns"
}
Native Xray rules
Use routing.rules with Xray field-rule keys such as:
type: "field";process;domainwith Xray-native items such asdomain:,full:,regexp:, orgeosite:when supported by the bundled data/core;ipwith CIDR,geoip:, or other Xray-native values;network,inboundTag,port;outboundTag.
Example process rule:
{
"type": "field",
"process": [
"Example.exe",
"example-helper.exe"
],
"network": "tcp,udp",
"outboundTag": "direct"
}
Put project policy rules at the beginning of the template's routing.rules, before broad proxy rules. The runtime metrics rule may be inserted ahead of them for a dedicated metrics inbound; it does not match normal application traffic.
Ensure the referenced direct outbound exists, normally:
{
"tag": "direct",
"protocol": "freedom",
"settings": {}
}
Reserved routing tags
- Keep
proxyas the selected-node placeholder when the template expects the application to replace it. - Keep
directfor native direct egress. - Keep
blockfor the engine's native block/blackhole outbound. - Do not rename app-owned tags beginning with
__app_without tracing every runtime consumer.
Files to trace before architecture changes
xray_fluent/application/profile_service.py: template, active-config, reset, and save ownership.xray_fluent/application/template_sync.py: updater-safe delivery of shipped templates and preservation of user-edited active JSON.xray_fluent/engines/singbox/runtime_planner.py: proxy/TUN inbound contracts, selected outbound, bootstrap rule, and hybrid sidecar.xray_fluent/application/xray_runtime_service.py: raw Xray metrics, TUN, selected outbound, and loop-prevention contracts.xray_fluent/ui/configs_page.pyandui/dashboard_page.py: user-facing mode semantics.docs/sing-box/runtime-config.md: current raw-config model and route-order explanation.docs/sing-box/reference/route-rules.md: local upstream-aligned sing-box field reference.docs/template-editor-v1.mdanddocs/sing-box/template-format-v1.md: design context only; do not treat placeholders as implemented runtime behavior without source proof.tests/test_yandex_music_direct_templates.py: concrete cross-core example that checks native process rules and first-match priority in every shipped template.
Verification checklist
- Parse every changed JSON file.
- Assert exact process/domain values, native action fields, and rule priority in a focused unit test.
- Run
sing-box checkfor changed sing-box templates. - Run
xray run -testfor changed Xray templates. - Run the relevant Python suite and
git diff --check. - Test both automatic synchronization of untouched active configs and preservation of user-edited active configs.
- Do not claim build or installed-runtime verification unless each was actually performed.