How S-UI Implements the Transparent Proxy Feature: A Code-Level Analysis
S-UI implements transparent proxy (tproxy) functionality by generating a JSON inbound configuration with "type": "tproxy" and delegating packet interception to the underlying sing-box engine, which binds to a specified port using Linux's IP_TRANSPARENT socket option.
The transparent proxy feature in S-UI enables traffic interception at the network layer without requiring application-level proxy configuration. According to the alireza0/s-ui source code, this capability is not implemented natively within S-UI itself but rather orchestrated through a lightweight wrapper around the sing-box proxy engine.
Architecture Overview
S-UI functions as a configuration management layer that prepares and validates JSON definitions before handing them to sing-box. When users enable transparent proxy mode, S-UI constructs an inbound definition that sing-box's InboundRegistry recognizes as a transparent proxy type. This design delegates the complex network-level packet interception to sing-box while S-UI handles the UI, storage, and lifecycle management.
Step-by-Step Implementation Flow
User Configuration and JSON Generation
The process begins when a user selects the Transparent Proxy option in the S-UI interface. In service/inbounds.go, the service.InboundService.Save method unmarshals the submitted configuration into a model.Inbound struct from database/model/inbounds.go. This struct holds the Type, Tag, and network parameters required for the tproxy configuration.
The service layer constructs a JSON object containing the mandatory fields sing-box expects:
inbound := map[string]interface{}{
"type": "tproxy",
"listen": "0.0.0.0",
"listen_port": 12345,
"tag": "transparent-in",
}
Core Registration and InboundRegistry
After validation, the system checks if the sing-box core is active via corePtr.IsRunning(). If running, the code invokes corePtr.AddInbound(inboundConfig) to register the new inbound with the live engine.
The core.NewCore() function in core/main.go initializes the global sing-box context using:
globalCtx = sb.Context(globalCtx,
InboundRegistry(), OutboundRegistry(),
EndpointRegistry(), DNSTransportRegistry(),
ServiceRegistry())
The InboundRegistry() defined in core/registry.go registers built-in inbound types that sing-box supports, including the tproxy type. When AddInbound processes the configuration, sing-box looks up the "type": "tproxy" entry in this registry to instantiate the appropriate listener.
Traffic Interception at the Network Layer
Once registered, sing-box creates a transparent proxy listener that binds to the specified address and port. This listener utilizes Linux's IP_TRANSPARENT socket option to intercept traffic at the network layer while preserving the original client IP address. Incoming packets are then forwarded to the appropriate outbound based on user-defined routing rules without modifying the source address.
Outbound JSON Short-Circuit
Unlike other inbound types that may require additional outbound processing, transparent proxies are handled specially in util/outJson.go. The util.FillOutJson function contains a switch statement that returns early for tproxy configurations:
case "direct", "tun", "redirect", "tproxy":
return nil
This optimization occurs because tproxy inbounds operate as pure traffic interceptors and do not require S-UI to generate complementary outbound JSON payloads. The core handles the entire forwarding logic internally once the inbound is established.
Practical Implementation Example
To add a transparent proxy programmatically through S-UI's service layer:
// Build the tproxy inbound configuration
inbound := map[string]interface{}{
"type": "tproxy",
"listen": "0.0.0.0",
"listen_port": 12345,
"tag": "transparent-in",
}
inboundJSON, _ := json.Marshal(inbound)
// Add to the running sing-box core
if corePtr.IsRunning() {
err := corePtr.AddInbound(inboundJSON)
if err != nil {
logger.Error("failed to add tproxy inbound:", err)
}
}
// Skip outbound JSON processing (no-op for tproxy)
var inboundModel model.Inbound
_ = json.Unmarshal(inboundJSON, &inboundModel)
_ = util.FillOutJson(&inboundModel, "my.hostname.com")
Key Source Files and Responsibilities
core/main.go: Bootstraps the sing-box context and initializes registriescore/registry.go: DefinesInboundRegistry()which registers thetproxytype with sing-boxservice/inbounds.go: ImplementsInboundService.Savefor configuration validation and core communicationutil/outJson.go: Contains the logic that skips unnecessary outbound processing for transparent proxiesdatabase/model/inbounds.go: Defines the data structures storing inbound configuration parameters
Summary
- S-UI delegates transparent proxy functionality to sing-box's native
tproxyinbound rather than implementing packet interception itself - Configuration flows through four layers: UI → Service (
service/inbounds.go) → Registry (core/registry.go) → sing-box Core util.FillOutJsonoptimizes processing by returningnilfor tproxy types, eliminating unnecessary outbound JSON generation- Linux
IP_TRANSPARENTenables true transparency by preserving original client IP addresses during packet interception - The
InboundRegistrypattern allows S-UI to support sing-box's full range of inbound types through a unified registration mechanism
Frequently Asked Questions
What is the difference between redirect and transparent proxy in S-UI?
Redirect mode operates at the application layer and modifies packet destinations, while transparent proxy (tproxy) operates at the network layer using Linux's IP_TRANSPARENT socket option to intercept packets without altering headers. According to the source code in util/outJson.go, both types are treated similarly in terms of outbound JSON processing (both return nil in the switch case), but tproxy preserves the original source IP address whereas redirect does not.
Why does S-UI rely on sing-box instead of implementing transparent proxy natively?
S-UI functions specifically as a management interface rather than a proxy engine. The core/main.go implementation shows that S-UI initializes sing-box's global context and delegates all packet handling to the underlying engine. This architectural decision allows S-UI to leverage sing-box's mature networking stack, IP_TRANSPARENT socket handling, and performance optimizations while focusing the S-UI codebase on configuration management, user interface, and API endpoints.
How does S-UI handle transparent proxy configuration when the core is offline?
The service layer queues the configuration during the save operation. In service/inbounds.go, the Save method checks corePtr.IsRunning() before calling AddInbound(). If the core is not running, the JSON configuration is still persisted to the database through the model.Inbound struct. When the core subsequently starts via NewCore(), the existing configurations are loaded and registered with the InboundRegistry, activating the transparent proxy listeners at that time.
What network privileges does S-UI require for transparent proxy to function?
The sing-box process requires CAP_NET_ADMIN capability or root privileges to create sockets with the IP_TRANSPARENT option. While S-UI itself runs as a management service, the actual binding and packet interception occur within the sing-box core process. The AddInbound call in the source code assumes the core process has sufficient privileges to bind to the specified port and set the necessary socket options for transparent interception.
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 →