How S-UI Implements Traffic Routing with the Advanced Interface
S-UI generates dynamic JSON configurations in sub/jsonService.go that merge user-defined rules from the advanced interface with default routing templates, then passes the assembled route object to the Sing-Box engine via core/box.go.
S-UI is a modern user interface for managing Sing-Box proxies that exposes fine-grained traffic control through its advanced configuration panel. Understanding how S-UI implements traffic routing with the advanced interface reveals a sophisticated merge system where UI inputs transform into executable routing rules. This article examines how the alireza0/s-ui repository constructs these configurations and wires them into the underlying proxy engine.
Core Routing Logic in jsonService.go
The traffic routing mechanism originates in sub/jsonService.go, where the service constructs the configuration payload delivered to clients. This file assembles a route object that balances automatic interface detection with explicit user overrides.
Base Route Template
Every generated configuration begins with a foundational route structure hardcoded at lines 65-68:
route := map[string]interface{}{
"auto_detect_interface": true,
"final": "proxy",
"rules": rules_start,
}
This template ensures the proxy engine can automatically select network interfaces while establishing a default outbound for unmatched traffic.
Default Rule Injection
Before processing user input, S-UI prepends two critical rules to ensure baseline functionality. Lines 48-55 initialize rules_start with a traffic sniffing directive and a Clash-mode bypass:
rules_start := []interface{}{
{"action": "sniff"},
{"clash_mode": "Direct", "action": "route", "outbound": "direct"},
}
The sniff rule enables protocol detection, while the Direct mode rule creates an escape hatch for traffic that should bypass the proxy entirely.
Advanced Interface Integration
The advanced interface allows administrators to inject custom JSON routing extensions that override or extend default behavior. These configurations persist in the settings service and merge dynamically during JSON generation.
Merging User-Defined Rules
When the settings service contains a "Sub JSON extension" with custom rules, the system performs a three-way merge at lines 99-102 of sub/jsonService.go:
if settingRules, ok := othersJson["rules"].([]interface{}); ok {
rules := append(rules_start, settingRules...)
route["rules"] = append(rules, rules_end...)
}
This insertion places user rules between the default sniff/direct rules and the final fallback rules, ensuring custom policies take precedence over defaults while maintaining safety fallbacks.
Optional Route Attributes
Beyond rules, the advanced interface supports optional route-level parameters. Lines 96-104 extract and validate these fields:
if _, ok := othersJson["rule_set"]; ok {
route["rule_set"] = othersJson["rule_set"]
}
if defaultDomainResolver, ok := othersJson["default_domain_resolver"].(string); ok {
route["default_domain_resolver"] = defaultDomainResolver
}
These conditionals allow users to specify external rule sets or custom DNS resolvers without modifying core service logic.
Router Initialization and Execution
After assembly, the complete route configuration integrates with the Sing-Box engine. Line 106 of sub/jsonService.go commits the finalized object:
(*jsonConfig)["route"] = route
The actual packet processing begins when core/box.go instantiates the router using route.NewRouter, consuming the auto_detect_interface, final, and nested rules fields to make real-time forwarding decisions for each connection.
Configuration Example
A typical advanced setup generates JSON resembling this structure:
{
"route": {
"auto_detect_interface": true,
"final": "proxy",
"rules": [
{ "action": "sniff" },
{ "clash_mode": "Direct", "action": "route", "outbound": "direct" },
{ "domain": ["example.com"], "outbound": "proxy", "action": "route" },
{ "ip": ["10.0.0.0/8"], "outbound": "direct", "action": "route" },
{ "clash_mode": "Global", "action": "route", "outbound": "proxy" }
],
"rule_set": "custom_provider",
"default_domain_resolver": "8.8.8.8"
}
}
In this example, the middle two rules represent user input from the advanced interface, interpolated between system defaults.
Summary
- S-UI implements traffic routing by generating Sing-Box compatible JSON in
sub/jsonService.gothat combines hardcoded defaults with user extensions. - The advanced interface injects custom rules through the "Sub JSON extension" field, merged at lines 99-102 to maintain correct precedence between sniffing directives and fallback rules.
- Optional parameters like
rule_setanddefault_domain_resolverattach dynamically when present in user configuration. - The assembled route object finalizes at line 106 of
sub/jsonService.gobeforecore/box.goinitializes the actual router engine. - Default protections including automatic interface detection and fallback outbounds ensure connectivity even with incomplete custom rules.
Frequently Asked Questions
Where does S-UI store custom routing rules from the advanced interface?
Custom rules persist in the database through service/setting.go as the "Sub JSON extension" field. During subscription generation, sub/jsonService.go retrieves this value and unmarshals it into the configuration at lines 99-102, inserting the user rules between default sniffing directives and final fallback rules.
Can I use rule sets with the S-UI advanced interface?
Yes. The advanced interface supports the rule_set field. When provided in your JSON extension, lines 96-97 of sub/jsonService.go detect and attach the external reference to the route object, allowing integration with remote rule providers.
What happens if my custom rules conflict with S-UI defaults?
S-UI processes rules in sequence. Your custom rules (injected at lines 99-102) evaluate after the initial sniff and direct-mode rules but before the final global fallback. This ordering ensures protocol detection and explicit direct-mode bypasses execute first, while your domain and IP rules take precedence over the default final outbound.
How does S-UI apply the generated route configuration?
After JSON assembly completes at line 106 of sub/jsonService.go, the configuration propagates to clients. For the server-side implementation in the alireza0/s-ui repository, core/box.go consumes this structure during initialization, calling route.NewRouter with the assembled parameters to instantiate the traffic filtering engine.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →