# How S-UI Handles Multi-Protocol Support for VLESS, VMess, Trojan, and Shadowsocks

> Discover how S-UI masterfully handles VLESS VMess Trojan and Shadowsocks multi-protocol support by integrating with the sing box core for streamlined management and configuration.

- Repository: [Alireza Ahmadi/s-ui](https://github.com/alireza0/s-ui)
- Tags: how-to-guide
- Published: 2026-05-22

---

**S-UI enables seamless multi-protocol support by registering VLESS, VMess, Trojan, and Shadowsocks implementations from the sing-box core in [`core/register.go`](https://github.com/alireza0/s-ui/blob/main/core/register.go), then orchestrating inbound management, subscription URI generation, and JSON configuration export through a unified service layer.**

S-UI is a web-based control panel built on the **sing-box** proxy core that abstracts V2Ray-compatible protocol complexity into a streamlined interface. The project achieves robust **S-UI multi-protocol support** through a layered architecture that handles protocol registration, database persistence, and client configuration generation. By directly importing implementations from `github.com/sagernet/sing-box/protocol/*`, S-UI ensures immediate compatibility with upstream sing-box features while providing standardized management for all four major protocols.

## Core Protocol Registration ([`core/register.go`](https://github.com/alireza0/s-ui/blob/main/core/register.go))

At application startup, S-UI imports the official sing-box protocol packages and registers them with centralized inbound and outbound registries. This registration makes VLESS, VMess, Trojan, and Shadowsocks available to the engine before any network traffic is handled.

The `InboundRegistry` and `OutboundRegistry` functions in [`core/register.go`](https://github.com/alireza0/s-ui/blob/main/core/register.go) explicitly wire each protocol into the sing-box lifecycle:

```go
import (
    "github.com/sagernet/sing-box/protocol/vless"
    "github.com/sagernet/sing-box/protocol/vmess"
    "github.com/sagernet/sing-box/protocol/trojan"
    "github.com/sagernet/sing-box/protocol/shadowsocks"
)

func InboundRegistry() *inbound.Registry {
    registry := inbound.NewRegistry()
    vmess.RegisterInbound(registry)
    vless.RegisterInbound(registry)
    trojan.RegisterInbound(registry)
    shadowsocks.RegisterInbound(registry)
    return registry
}

func OutboundRegistry() *outbound.Registry {
    registry := outbound.NewRegistry()
    vmess.RegisterOutbound(registry)
    vless.RegisterOutbound(registry)
    trojan.RegisterOutbound(registry)
    shadowsocks.RegisterOutbound(registry)
    return registry
}

```

Once registered, these protocols can be instantiated dynamically based on database configuration without requiring code changes or redeployment.

## Inbound Service Management ([`service/inbounds.go`](https://github.com/alireza0/s-ui/blob/main/service/inbounds.go))

The inbound service layer determines which protocols support user-based authentication and manages their lifecycle within the sing-box engine. The `InboundService.hasUser` method explicitly whitelists VLESS, VMess, Trojan, and Shadowsocks as requiring client credentials:

```go
func (s *InboundService) hasUser(inboundType string) bool {
    switch inboundType {
    case "mixed", "socks", "http", "shadowsocks", "vmess",
         "trojan", "naive", "hysteria", "shadowtls",
         "tuic", "hysteria2", "vless", "anytls":
        return true
    }
    return false
}

```

When an administrator creates or restarts an inbound, the service constructs a JSON configuration blob containing the protocol-specific type identifier (e.g., `"type": "vless"`) and passes it to `corePtr.AddInbound`. This approach allows the sing-box core to instantiate the correct protocol handler while S-UI manages the surrounding TLS, transport, and user context.

## Subscription Link Generation ([`util/genLink.go`](https://github.com/alireza0/s-ui/blob/main/util/genLink.go))

For client distribution, S-UI converts stored inbound configurations into standard subscription URIs through protocol-specific builder functions. Each protocol has a dedicated generator that formats credentials, transport parameters, and TLS settings according to V2Ray URI standards.

The `vlessLink` function demonstrates the generation pattern used across all four protocols:

```go
func vlessLink(userConfig map[string]interface{}, inbound map[string]interface{},
    addrs []map[string]interface{}) []string {
    uuid, _ := userConfig["uuid"].(string)
    baseParams := getTransportParams(inbound["transport"])
    var links []string
    for _, addr := range addrs {
        if tls, ok := addr["tls"].(map[string]interface{}); ok && tls["enabled"].(bool) {
            getTlsParams(&params, tls, "allowInsecure")
            if flow, ok := userConfig["flow"].(string); ok {
                params = append(params, LinkParam{"flow", flow})
            }
        }
        port, _ := addr["server_port"].(float64)
        uri := fmt.Sprintf("vless://%s@%s:%.0f", uuid, addr["server"].(string), port)
        uri = addParams(uri, params, addr["remark"].(string))
        links = append(links, uri)
    }
    return links
}

```

Parallel implementations exist for **VMess** (`vmessLink`), **Trojan** (`trojanLink`), and **Shadowsocks** (`shadowsocksLink`). These functions:
- Extract protocol-specific credentials (UUID for VLESS/VMess, password for Trojan/Shadowsocks)
- Append transport layers (WebSocket, HTTP/2, gRPC) using `getTransportParams`
- Format the final URI with the appropriate scheme (`vless://`, `vmess://`, `trojan://`, `ss://`)

## URI Parsing and JSON Conversion ([`util/linkToJson.go`](https://github.com/alireza0/s-ui/blob/main/util/linkToJson.go))

When users import subscription links into S-UI, the system parses standard V2Ray URIs back into the JSON structures required by sing-box. The `vless` parser normalizes query parameters into typed configuration objects:

```go
func vless(u *url.URL, i int) (*map[string]interface{}, string, error) {
    query, _ := url.ParseQuery(u.RawQuery)
    security := query.Get("security")
    host, portStr, _ := net.SplitHostPort(u.Host)
    port := 80
    if len(portStr) > 0 {
        port, _ = strconv.Atoi(portStr)
    } else if security == "tls" || security == "reality" {
        port = 443
    }
    tp_type := query.Get("type")
    tag := u.Fragment
    if i > 0 {
        tag = fmt.Sprintf("%d.%s", i, u.Fragment)
    }
    vless := map[string]interface{}{
        "type":        "vless",
        "tag":         tag,
        "server":      host,
        "server_port": port,
        "uuid":        u.User.Username(),
        "flow":        query.Get("flow"),
        "tls":         getTls(security, &query),
        "transport":   getTransport(tp_type, &query),
    }
    return &vless, tag, nil
}

```

Analogous parsers for VMess, Trojan, and Shadowsocks handle their respective URI formats, utilizing shared helpers `getTls` and `getTransport` to ensure consistent handling of security and transport layers across all protocols.

## Outbound Configuration Export ([`util/outJson.go`](https://github.com/alireza0/s-ui/blob/main/util/outJson.go))

When exporting full client configurations (e.g., for "Export Config" functionality), S-UI applies protocol-specific transformations to ensure JSON schema compliance. The `vlessOut` function adjusts the transport field structure to match VLESS specifications:

```go
func vlessOut(out *map[string]interface{}, inbound map[string]interface{}) {
    delete(*out, "transport")
    if transport, ok := inbound["transport"]; ok {
        (*out)["transport"] = transport
    }
}

```

Similar helpers (`trojanOut`, `vmessOut`) ensure that each protocol receives the exact field structure expected by sing-box clients, handling subtle differences in how transport and TLS settings are nested within the configuration object.

## Summary

- **Protocol Registration**: [`core/register.go`](https://github.com/alireza0/s-ui/blob/main/core/register.go) imports and registers VLESS, VMess, Trojan, and Shadowsocks implementations from the sing-box core at application startup, making them available to the engine.
- **Inbound Management**: [`service/inbounds.go`](https://github.com/alireza0/s-ui/blob/main/service/inbounds.go) identifies these protocols as user-authenticated and manages their lifecycle, injecting client credentials into sing-box configurations.
- **Link Generation**: [`util/genLink.go`](https://github.com/alireza0/s-ui/blob/main/util/genLink.go) constructs standard subscription URIs for each protocol, handling protocol-specific credential formats and transport parameters.
- **Configuration Parsing**: [`util/linkToJson.go`](https://github.com/alireza0/s-ui/blob/main/util/linkToJson.go) converts imported V2Ray URIs back into structured JSON, normalizing query parameters into sing-box configuration objects.
- **Export Formatting**: [`util/outJson.go`](https://github.com/alireza0/s-ui/blob/main/util/outJson.go) applies final protocol-specific transformations to ensure outbound configurations match the exact schema required by each protocol implementation.

## Frequently Asked Questions

### Does S-UI support advanced features like XTLS and Reality for these protocols?

Yes. Because S-UI directly imports the official sing-box protocol packages in [`core/register.go`](https://github.com/alireza0/s-ui/blob/main/core/register.go), it inherits full support for advanced features including XTLS flow controls for VLESS, Reality TLS handshakes, and Shadowsocks 2022 ciphers. The link generation logic in [`util/genLink.go`](https://github.com/alireza0/s-ui/blob/main/util/genLink.go) specifically handles the `flow` parameter for VLESS and the `security` parameter for Reality/TLS modes.

### How does S-UI determine which protocols require user credentials?

The source code in [`service/inbounds.go`](https://github.com/alireza0/s-ui/blob/main/service/inbounds.go) contains the `hasUser` method, which explicitly lists VLESS, VMess, Trojan, and Shadowsocks among the protocols requiring client authentication. This whitelist approach allows S-UI to display user management interfaces only for protocols that support multi-user configurations, while hiding them for simple relay protocols.

### Can S-UI handle mixed-protocol subscriptions?

Absolutely. The `SubService.GetSubs` method in [`sub/subService.go`](https://github.com/alireza0/s-ui/blob/main/sub/subService.go) aggregates links from all configured inbounds regardless of protocol type. It calls the appropriate generator from [`util/genLink.go`](https://github.com/alireza0/s-ui/blob/main/util/genLink.go) for each inbound based on its type field, producing a mixed subscription list containing VLESS, VMess, Trojan, and Shadowsocks URIs that clients can import simultaneously.

### What happens when a user imports a protocol URI into S-UI?

When importing, [`util/linkToJson.go`](https://github.com/alireza0/s-ui/blob/main/util/linkToJson.go) auto-detects the protocol from the URI scheme (e.g., `vless://`) and routes it to the specific parser function (e.g., `vless()`). The parser extracts server details, UUIDs or passwords, transport settings, and TLS configurations, then returns a JSON object that is stored as an outbound configuration. This JSON matches the schema expected by the sing-box core, ensuring immediate routing capability.