Skip to content

Protocol Overview

sing-box supports 20+ proxy protocols, all following a consistent adapter pattern. Protocol implementations are thin wrappers that delegate to sing-* libraries for the actual wire format handling.

Source: protocol/, include/

Registration Pattern

Every protocol registers itself via the include system:

go
// include/inbound.go
func InboundRegistry() *inbound.Registry {
    registry := inbound.NewRegistry()
    tun.RegisterInbound(registry)
    vless.RegisterInbound(registry)
    vmess.RegisterInbound(registry)
    trojan.RegisterInbound(registry)
    // ...
    return registry
}

Each protocol provides a registration function:

go
// protocol/vless/inbound.go
func RegisterInbound(registry adapter.InboundRegistry) {
    inbound.Register[option.VLESSInboundOptions](registry, C.TypeVLESS, NewInbound)
}

The generic Register function maps: (type string, options type) → factory function.

Inbound Pattern

All inbounds follow this structure:

go
type Inbound struct {
    myInboundAdapter  // embedded adapter with Tag(), Type()
    ctx      context.Context
    router   adapter.ConnectionRouterEx
    logger   log.ContextLogger
    listener *listener.Listener    // TCP listener
    service  *someprotocol.Service // protocol service
}

func NewInbound(ctx, router, logger, tag string, options) (adapter.Inbound, error) {
    // 1. Create protocol service (from sing-* library)
    // 2. Create listener
    // 3. Wire service → router for connection handling
}

func (h *Inbound) Start(stage adapter.StartStage) error {
    // Start listener
}

func (h *Inbound) Close() error {
    // Close listener + service
}

// Called by listener for each new connection
func (h *Inbound) NewConnectionEx(ctx, conn, metadata, onClose) {
    // Protocol-specific decoding happens here
    // Then: h.router.RouteConnectionEx(ctx, conn, metadata, onClose)
}

Outbound Pattern

All outbounds implement N.Dialer:

go
type Outbound struct {
    myOutboundAdapter  // embedded adapter with Tag(), Type(), Network()
    ctx       context.Context
    dialer    N.Dialer           // underlying dialer (may be detour)
    transport *v2ray.Transport   // optional V2Ray transport
    // protocol-specific options
}

func NewOutbound(ctx, router, logger, tag string, options) (adapter.Outbound, error) {
    // 1. Create underlying dialer (default or detour)
    // 2. Create V2Ray transport if configured
    // 3. Configure protocol options
}

func (h *Outbound) DialContext(ctx, network, destination) (net.Conn, error) {
    // 1. Dial transport connection
    // 2. Perform protocol handshake
    // 3. Return wrapped connection
}

func (h *Outbound) ListenPacket(ctx, destination) (net.PacketConn, error) {
    // For UDP-capable protocols
}

Protocol Categories

Proxy Protocols (Client/Server)

ProtocolInboundOutboundLibrary
VLESSYesYessing-vmess
VMessYesYessing-vmess
TrojanYesYestransport/trojan (built-in)
ShadowsocksYesYessing-shadowsocks / sing-shadowsocks2
ShadowTLSYesYessing-shadowtls
Hysteria2YesYessing-quic
TUICYesYessing-quic
AnyTLSYesYessing-anytls
NaiveProxyYesYesBuilt-in
WireGuardEndpointEndpointwireguard-go
TailscaleEndpointEndpointtailscale

Local Proxy Protocols

ProtocolInboundOutbound
SOCKS4/5YesYes
HTTPYesYes
Mixed (SOCKS+HTTP)Yes-
RedirectYes-
TProxyYes-
TUNYes-

Utility Protocols

ProtocolPurpose
DirectDirect outbound connection
BlockDrop all connections
DNSForward to DNS router
SelectorManual outbound selection
URLTestAuto-select based on latency
SSHSSH tunnel
TorTor network

V2Ray Transport Integration

Many protocols support V2Ray-compatible transports:

go
// Create transport from options
transport, err := v2ray.NewServerTransport(ctx, logger, common.PtrValueOrDefault(options.Transport), tlsConfig, handler)

// Or for client side
transport, err := v2ray.NewClientTransport(ctx, dialer, serverAddr, common.PtrValueOrDefault(options.Transport), tlsConfig)

Supported transports: WebSocket, gRPC, HTTP/2, HTTPUpgrade, QUIC.

Multiplex Integration

Outbounds can wrap with multiplex:

go
if options.Multiplex != nil && options.Multiplex.Enabled {
    outbound.multiplexDialer, err = mux.NewClientWithOptions(ctx, outbound, muxOptions)
}

Handler Chain

Inbound Listener → Protocol Decode → Router → Rule Match → Outbound Select
    ↓                                                          ↓
TCP/UDP accept                                          Protocol Encode
    ↓                                                          ↓
Protocol Service                                        Transport Dial
    ↓                                                          ↓
Extract destination                                     Remote Connection
    ↓                                                          ↓
Route to outbound ─────────────────────────────→ ConnectionManager.Copy

Key Differences from Xray-core

AspectXray-coresing-box
Wire formatBuilt-in encodingsing-* library
Inbound modelproxy.Inbound.Process() returns Linkadapter.Inbound → router callback
Outbound modelproxy.Outbound.Process() with LinkN.Dialer interface (DialContext/ListenPacket)
Data flowPipe Reader/WriterDirect net.Conn/PacketConn
MuxBuilt-in mux + XUDPsing-mux library
Vision/XTLSBuilt-in in proxy.goNot supported (different approach)