Skip to content

Adapter Interfaces ​

The adapter package defines the core interfaces that all components implement. These interfaces form the contract between the orchestration layer (Box, Router) and the protocol implementations.

Source: adapter/

Core Interfaces ​

Inbound ​

go
type Inbound interface {
    Lifecycle
    Type() string
    Tag() string
}

type TCPInjectableInbound interface {
    Inbound
    ConnectionHandlerEx
}

type UDPInjectableInbound interface {
    Inbound
    PacketConnectionHandlerEx
}

An inbound listens for connections and decodes protocol headers. After decoding, it calls the router to route the connection. Injectable inbounds support receiving connections from other inbounds (detour).

Outbound ​

go
type Outbound interface {
    Type() string
    Tag() string
    Network() []string        // ["tcp"], ["udp"], or ["tcp", "udp"]
    Dependencies() []string   // tags of outbounds this depends on
    N.Dialer                  // DialContext + ListenPacket
}

An outbound is fundamentally an N.Dialer — it can dial TCP connections and listen for UDP packets. This means outbounds are composable: a VLESS outbound wraps a direct dialer with protocol encoding.

Endpoint ​

go
type Endpoint interface {
    Lifecycle
    Type() string
    Tag() string
    Outbound  // endpoints are also outbounds
}

Endpoints are dual-role components that act as both inbound and outbound. WireGuard and Tailscale are endpoints — they create virtual network interfaces.

Router ​

go
type Router interface {
    Lifecycle
    ConnectionRouter
    ConnectionRouterEx
    PreMatch(metadata InboundContext, ...) (tun.DirectRouteDestination, error)
    RuleSet(tag string) (RuleSet, bool)
    Rules() []Rule
    NeedFindProcess() bool
    NeedFindNeighbor() bool
    AppendTracker(tracker ConnectionTracker)
    ResetNetwork()
}

type ConnectionRouterEx interface {
    ConnectionRouter
    RouteConnectionEx(ctx context.Context, conn net.Conn, metadata InboundContext, onClose N.CloseHandlerFunc)
    RoutePacketConnectionEx(ctx context.Context, conn N.PacketConn, metadata InboundContext, onClose N.CloseHandlerFunc)
}

The router matches rules and dispatches connections to outbounds. RouteConnectionEx is the non-blocking variant that takes an onClose callback instead of blocking until completion.

ConnectionManager ​

go
type ConnectionManager interface {
    Lifecycle
    Count() int
    CloseAll()
    TrackConn(conn net.Conn) net.Conn
    TrackPacketConn(conn net.PacketConn) net.PacketConn
    NewConnection(ctx context.Context, this N.Dialer, conn net.Conn, metadata InboundContext, onClose N.CloseHandlerFunc)
    NewPacketConnection(ctx context.Context, this N.Dialer, conn N.PacketConn, metadata InboundContext, onClose N.CloseHandlerFunc)
}

The connection manager handles the actual dial + bidirectional copy loop. When an outbound doesn't implement ConnectionHandlerEx directly, the router delegates to the connection manager.

NetworkManager ​

go
type NetworkManager interface {
    Lifecycle
    InterfaceFinder() control.InterfaceFinder
    DefaultNetworkInterface() *NetworkInterface
    AutoDetectInterface() bool
    AutoDetectInterfaceFunc() control.Func
    ProtectFunc() control.Func
    DefaultOptions() NetworkOptions
    NetworkMonitor() tun.NetworkUpdateMonitor
    InterfaceMonitor() tun.DefaultInterfaceMonitor
    PackageManager() tun.PackageManager
    WIFIState() WIFIState
    ResetNetwork()
}

Manages platform network state: interface detection, routing marks, socket protection (Android), WIFI monitoring.

DNSRouter ​

go
type DNSRouter interface {
    Lifecycle
    Exchange(ctx context.Context, message *dns.Msg, options DNSQueryOptions) (*dns.Msg, error)
    Lookup(ctx context.Context, domain string, options DNSQueryOptions) ([]netip.Addr, error)
    ClearCache()
    LookupReverseMapping(ip netip.Addr) (string, bool)
    ResetNetwork()
}

DNSTransportManager ​

go
type DNSTransportManager interface {
    Lifecycle
    Transports() []DNSTransport
    Transport(tag string) (DNSTransport, bool)
    Default() DNSTransport
    FakeIP() FakeIPTransport
    Remove(tag string) error
    Create(ctx context.Context, ...) error
}

InboundContext — The Metadata Object ​

InboundContext is the central metadata struct that flows through the entire pipeline:

go
type InboundContext struct {
    // Identity
    Inbound     string         // inbound tag
    InboundType string         // inbound type (e.g., "vless")
    Network     string         // "tcp" or "udp"
    Source      M.Socksaddr    // client address
    Destination M.Socksaddr    // target address
    User        string         // authenticated user
    Outbound    string         // selected outbound tag

    // Sniffing results
    Protocol     string        // detected protocol (e.g., "tls", "http")
    Domain       string        // sniffed domain name
    Client       string        // detected client (e.g., "chrome")
    SniffContext any
    SniffError   error

    // Routing cache
    IPVersion            uint8
    OriginDestination    M.Socksaddr
    RouteOriginalDestination M.Socksaddr
    DestinationAddresses []netip.Addr     // resolved IPs
    SourceGeoIPCode      string
    GeoIPCode            string
    ProcessInfo          *ConnectionOwner
    SourceMACAddress     net.HardwareAddr
    SourceHostname       string
    QueryType            uint16
    FakeIP               bool

    // Route options (set by rule actions)
    NetworkStrategy     *C.NetworkStrategy
    NetworkType         []C.InterfaceType
    FallbackNetworkType []C.InterfaceType
    FallbackDelay       time.Duration
    UDPDisableDomainUnmapping bool
    UDPConnect                bool
    UDPTimeout                time.Duration
    TLSFragment               bool
    TLSFragmentFallbackDelay  time.Duration
    TLSRecordFragment         bool

    // Rule matching cache (reset between rules)
    IPCIDRMatchSource            bool
    IPCIDRAcceptEmpty            bool
    SourceAddressMatch           bool
    SourcePortMatch              bool
    DestinationAddressMatch      bool
    DestinationPortMatch         bool
    DidMatch                     bool
    IgnoreDestinationIPCIDRMatch bool
}

Context Threading ​

InboundContext is stored in Go context:

go
// Store in context
func WithContext(ctx context.Context, inboundContext *InboundContext) context.Context

// Retrieve from context
func ContextFrom(ctx context.Context) *InboundContext

// Clone and store (for sub-pipelines)
func ExtendContext(ctx context.Context) (context.Context, *InboundContext)

Handler Interfaces ​

Handlers process incoming connections:

go
// TCP connection handler
type ConnectionHandlerEx interface {
    NewConnectionEx(ctx context.Context, conn net.Conn, metadata InboundContext, onClose N.CloseHandlerFunc)
}

// UDP packet connection handler
type PacketConnectionHandlerEx interface {
    NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, metadata InboundContext, onClose N.CloseHandlerFunc)
}

The onClose callback pattern is central to sing-box's design — it enables non-blocking connection handling. When a connection completes (success or error), onClose is called exactly once.

Upstream Adapters ​

Utility wrappers that adapt between handler types:

go
// Route handler — wraps a ConnectionRouterEx to act as an upstream handler
func NewRouteHandlerEx(metadata InboundContext, router ConnectionRouterEx) UpstreamHandlerAdapterEx

// Upstream handler — wraps connection/packet handlers
func NewUpstreamHandlerEx(metadata InboundContext, connHandler, pktHandler) UpstreamHandlerAdapterEx

These are used by protocol implementations to chain handlers together. For example, a VLESS inbound decodes the header, then uses NewRouteHandlerEx to pass the decoded connection to the router.

Manager Pattern ​

All managers follow the same pattern:

go
type XxxManager interface {
    Lifecycle
    Xxxs() []Xxx              // list all
    Get(tag string) (Xxx, bool)  // get by tag
    Remove(tag string) error     // dynamic removal
    Create(ctx, ..., tag, type, options) error  // dynamic creation
}

Managers own their registries and handle creation via the registry pattern:

go
// Registry creates instances from type + options
type XxxRegistry interface {
    Create(ctx, ..., tag, type string, options any) (Xxx, error)
}

Connection Tracker ​

Used by Clash API and V2Ray API for connection monitoring:

go
type ConnectionTracker interface {
    RoutedConnection(ctx, conn, metadata, matchedRule, matchOutbound) net.Conn
    RoutedPacketConnection(ctx, conn, metadata, matchedRule, matchOutbound) N.PacketConn
}

The tracker wraps connections to count bytes and track active connections.