Documentation
¶
Index ¶
- func BuildDNSOptions(ctx context.Context, raw []byte) (*option.DNSOptions, error)
- func BuildDNSOptionsFromPanel(servers []api.NameServer, strategy string) (option.DNSOptions, error)
- func BuildOutbounds(ctx context.Context, raw []byte) ([]option.Outbound, error)
- func BuildRoute(ctx context.Context, raw []byte) (*option.RouteOptions, error)
- func Context(ctx context.Context) context.Context
- func DirectOutbound(tag string) option.Outbound
- func MinimalVLESSInbound(tag, listen string, port uint16, uuid, flow string) (option.Inbound, error)
- func Translate(ctx context.Context, tag string, info *api.NodeInfo, users []api.UserInfo) (string, any, error)
- func ValidateRouteOutbounds(route *option.RouteOptions, outbounds []option.Outbound) error
- type BillingServer
- type ConnPolicy
- type MieruAuthSnapshot
- type ProxyProtocolConfig
- type Server
- func (s *Server) AddNode(tag string, info *api.NodeInfo) error
- func (s *Server) AddUsers(tag string, users []api.UserInfo) (added int, err error)
- func (s *Server) ClearUserThrottle(tag, key string)
- func (s *Server) Close() error
- func (s *Server) DelNode(tag string) error
- func (s *Server) DelUsers(tag string, users []api.UserInfo) error
- func (s *Server) Detect(tag, destination, key string) bool
- func (s *Server) DrainTraffic(key string, forget bool) (up, down int64)
- func (s *Server) ForgetUsers(tag string, keys []string)
- func (s *Server) GetDetectResult(tag string) ([]api.DetectResult, error)
- func (s *Server) GetUserTraffic(tag string, reset bool) ([]api.UserTraffic, error)
- func (s *Server) MieruAuth() MieruAuthSnapshot
- func (s *Server) OnlineIPs(tag string) ([]api.OnlineUser, error)
- func (s *Server) Protocols() []string
- func (s *Server) ResolveIdentity(tag, name string) (key string, removed bool)
- func (s *Server) SetAccessFile(f *accesslog.FileLogger)
- func (s *Server) SetAccessLog(m *accesslog.Manager)
- func (s *Server) SetConnPolicy(p ConnPolicy)
- func (s *Server) SetNodeAutoLimit(tag string, on bool)
- func (s *Server) SetNodeCert(tag string, cfg cert.CertConfig)
- func (s *Server) SetNodeConnLimit(tag string, max int)
- func (s *Server) SetNodeProxyProtocol(tag string, cfg ProxyProtocolConfig)
- func (s *Server) SetUserThrottle(tag, key string, speed uint64)
- func (s *Server) Start() error
- func (s *Server) Stats() *stats.Registry
- func (s *Server) Type() string
- func (s *Server) UpdateInboundLimiter(tag string, users []api.UserInfo) error
- func (s *Server) UpdateRule(tag string, rules []api.DetectRule) error
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func BuildDNSOptions ¶
BuildDNSOptions parses a raw `dns{}` block (doc/09 §6.1). Returns nil when raw is empty, meaning "leave the Box at its default DNS".
ctx must be the Box context (sing2Context) — DNS transports resolve through a registry just like inbounds do.
func BuildDNSOptionsFromPanel ¶ added in v0.0.2
func BuildDNSOptionsFromPanel(servers []api.NameServer, strategy string) (option.DNSOptions, error)
BuildDNSOptionsFromPanel translates a neutral NameServer list plus an xray-style domain-resolution strategy into native sing-box option.DNSOptions.
strategy accepts the xray DomainStrategy vocabulary (case-insensitive): "" / "AsIs", "UseIP", "UseIPv4", "UseIPv6" — plus the sing-box-native "PreferIPv4" / "PreferIPv6" aliases. Mapping to sing-box strategy:
AsIs → as_is UseIP → prefer_ipv4 (xray "resolve, no family pin"; prefer v4) UseIPv4 → ipv4_only UseIPv6 → ipv6_only PreferIPv4 → prefer_ipv4 PreferIPv6 → prefer_ipv6
An empty server list returns a zero-value option.DNSOptions with no error (the caller then leaves box's dns{} at its default).
func BuildOutbounds ¶ added in v0.0.2
BuildOutbounds parses a raw outbound array (doc/09 §6.2). Returns nil when raw is empty, letting the caller fall back to a lone direct outbound.
func BuildRoute ¶ added in v0.0.2
BuildRoute parses a raw `route{}` block (doc/09 §6.2). Returns nil when raw is empty (no routing — everything takes the default outbound).
func Context ¶ added in v0.0.2
sing2Context is include.Context with Sing2's protocol overrides applied.
It exists because the base fork's Mieru inbound cannot hot-swap users: it drives mieru through the apis/server facade, whose config is frozen after Start. Sing2's replacement (protocol/mieru) talks to mieru's protocol.Mux directly and implements UpdateUsers, so a user change no longer tears down every live connection on the node.
The override is safe and total: inbound.Registry.register is a plain map assignment (adapter/inbound/registry.go), so registering C.TypeMieru again replaces the base's constructor for every code path that resolves through this context — box.New, the dynamic box.Inbound().Create, and option.Inbound's context-aware unmarshaler used by the native translator. Context is sing2Context's exported form: callers that must parse sing-box options (outbounds / DNS / route) before constructing the Server need the registries in hand first (panel.Start, doc/09 §6.2).
func DirectOutbound ¶
DirectOutbound builds a plain direct outbound so smoke traffic has somewhere to go (sing-box uses the first outbound as the default final route).
func MinimalVLESSInbound ¶
func MinimalVLESSInbound(tag, listen string, port uint16, uuid, flow string) (option.Inbound, error)
MinimalVLESSInbound builds a native VLESS inbound with a single user, for the Phase 0 cross-core baseline smoke (Xray-core client → Sing2 server). flow may be "" or "xtls-rprx-vision"; REALITY is layered by the translation layer in Phase 1.
func Translate ¶
func Translate(ctx context.Context, tag string, info *api.NodeInfo, users []api.UserInfo) (string, any, error)
Translate builds the (inboundType, options) pair for info together with its current authoritative user set. users may be empty at AddNode time — users are hot-swapped in later via Inbound.UpdateUsers (see user.go). It is the cert-less public entry (plain-TLS nodes get an SNI-only TLS block); the Server drives the cert-aware path via translateNode.
ctx must be the include.Context-wrapped context the Box was built with: the native dialect (doc/13 T4) resolves inbound options through the registry stored in it. Flat-dialect nodes ignore ctx.
func ValidateRouteOutbounds ¶ added in v0.0.2
func ValidateRouteOutbounds(route *option.RouteOptions, outbounds []option.Outbound) error
ValidateRouteOutbounds cross-checks that every outbound tag a route rule references actually exists.
sing-box would surface this at Start as a bare "outbound not found: xxx" with no indication of which rule is at fault; on a node that is a silent egress misroute waiting to happen, so it is worth catching at config-load time.
Types ¶
type BillingServer ¶
type BillingServer struct {
// contains filtered or unexported fields
}
BillingServer is the per-connection billing tracker (Hinge A). Its RoutedConnection runs the per-connection pipeline limiter(rate/device/online/conn-count) → rule(audit) → stats(inner byte count) → ratelimit(outer token-bucket) and defers the conn-count decrement to the wrapped conn's Close (doc/11 §2.2).
Phase 1 increment A: the pipeline is wired but limiter/rule are not yet populated by the panel (no inbound registered → no throttle / no block); stats and connlimit take effect immediately — the intended "wired-but-unpopulated" state.
func NewBillingServer ¶
func NewBillingServer( lim *limiter.Limiter, rul *rule.Manager, st *stats.Registry, rt *ratelimit.Manager, conns *limiter.ConnLimiter, ) *BillingServer
NewBillingServer wires the five billing components into the tracker.
func (*BillingServer) RoutedConnection ¶
func (b *BillingServer) RoutedConnection( ctx context.Context, conn net.Conn, metadata adapter.InboundContext, matchedRule adapter.Rule, matchOutbound adapter.Outbound, ) net.Conn
RoutedConnection is the TCP injection point (doc/11 §2.2). The pipeline runs conn-count → rate/device/online limiter → audit rule → stats → ratelimit, unwinding the conn-count on any early reject and deferring the decrement to the wrapped conn's Close on success. When access logging is enabled it emits one rejected row per early-reject path and one accepted row on release.
func (*BillingServer) RoutedPacketConnection ¶
func (b *BillingServer) RoutedPacketConnection( ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, matchedRule adapter.Rule, matchOutbound adapter.Outbound, ) N.PacketConn
RoutedPacketConnection is the UDP injection point. UDP is not counted toward the per-user concurrent connection cap; it still gets byte accounting and token-bucket limiting.
type ConnPolicy ¶ added in v0.0.3
type ConnPolicy struct {
// UDPTimeout is how long an idle UDP session is kept. Base default is 5
// minutes for most inbounds. Zero leaves the base default alone.
UDPTimeout time.Duration
// TCPKeepAlive is the idle period before the first keep-alive probe;
// TCPKeepAliveInterval is the gap between probes. Zero means base default.
TCPKeepAlive time.Duration
TCPKeepAliveInterval time.Duration
// DisableTCPKeepAlive turns probing off entirely. Only ever turns it off —
// see applyTo.
DisableTCPKeepAlive bool
}
ConnPolicy is the node-local connection-tuning policy — config.yml's ConnectionConfig block. Like XrayR's it is process-level (one policy, every node), and it lands on every inbound's option.ListenOptions.
This is deliberately NOT a field-for-field port of XrayR's ConnectionConfig ¶
XrayR's five fields (handshake / connIdle / uplinkOnly / downlinkOnly / bufferSize) are xray `policy.levels[0]` settings — see XrayR-master panel/panel.go:251-268, which stuffs them straight into conf.Policy. sing-box has no policy layer at all, and four of the five have no runtime equivalent anywhere in the base:
- handshake — no equivalent. Each protocol handles its own handshake deadline internally; there is no knob.
- uplinkOnly / downlinkOnly — no equivalent, and no need for one: sing uses CloseWrite half-close semantics rather than a post-close grace timer.
- bufferSize — no RUNTIME equivalent. sing's buffer sizes are compile-time constants (sing common/buf/buffer_standard.go:5-8, BufferSize = 32 KiB / UDPBufferSize = 16 KiB); the only lever is the `with_low_memory` build tag, which halves them.
- connIdle — the closest match is per-inbound `udp_timeout`, but mapping it silently would be a REGRESSION: xray's connIdle defaults to 30s and covers TCP too, while sing-box's udp_timeout defaults to 5 minutes. An operator copying `connIdle: 30` across would quietly get UDP sessions dying at 30s.
So Sing2 exposes the knobs the base actually has, and panel/config.go warns — per key — on the XrayR names instead of swallowing them (doc/09 §7).
The TCP side of connIdle's intent (reap dead connections, don't leak fds) is served by TCP keep-alive rather than an idle timer: sing-box never kills an idle-but-live TCP connection, which is the correct behaviour for a proxy.
func (ConnPolicy) IsZero ¶ added in v0.0.3
func (p ConnPolicy) IsZero() bool
IsZero reports whether the policy would change nothing.
type MieruAuthSnapshot ¶ added in v0.0.4
type MieruAuthSnapshot struct {
Stats sing2mieru.AuthStats
// Nodes is how many Mieru inbounds this process serves, Users the total
// across them. mieru's counters are global, so with more than one node the
// per-sweep cost is bounded by the LARGEST node's user count, not the total
// — Users is an upper bound on the estimate, and Nodes says how loose it is.
Nodes int
Users int
// HintMandatoryAll is true only when EVERY Mieru node has
// user_hint_is_mandatory set. Anything less and the expensive
// try-every-user fallback is still reachable, which is the state worth
// reporting.
HintMandatoryAll bool
}
MieruAuthSnapshot pairs mieru's process-wide decryption counters with the node-local context needed to read them (see protocol/mieru/metrics.go for why the counters matter — upstream enfein/mieru#273).
The counters alone are not interpretable: the CPU cost of one full sweep is proportional to the user count, and whether sweeps can happen at all depends on user_hint_is_mandatory. Both live here, not in mieru.
func (MieruAuthSnapshot) Present ¶ added in v0.0.4
func (s MieruAuthSnapshot) Present() bool
Present reports whether this process serves any Mieru node at all. When false the counters are meaningless and callers should stay silent.
type ProxyProtocolConfig ¶ added in v0.0.8
type ProxyProtocolConfig struct {
// Listen/Port is the public address the front listener binds — normally
// 0.0.0.0 and the node's panel-assigned port.
Listen netip.Addr
Port uint16
// Enabled turns the front listener on.
Enabled bool
}
ProxyProtocolConfig is the node-local PROXY protocol policy.
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server is the sing-box-backed Core implementation. It owns the Box, the five billing components (also handed to the Hinge A tracker), and the authoritative per-node user sets driving the whole-set UpdateUsers hot-swap.
func NewServer ¶
NewServer builds a Box from opts, registers the billing ConnectionTracker (Hinge A — Router.AppendTracker, doc/11 §2.1), and returns the wrapper. The caller drives it with Start/Close. include.Context installs the protocol registries the fork's registry-era constructor (and dynamic Create) require.
func NewServerWithContext ¶ added in v0.0.2
NewServerWithContext is NewServer for a caller that already built the Box context via Context() — typically because it had to parse outbound / DNS / route options (which resolve through the same registries) before it could assemble option.Options. Passing a context that did NOT come from Context() will fail at option decoding with "missing … registry in context".
func (*Server) AddNode ¶
AddNode translates info into a dynamic inbound, creates it on the running Box, and registers the node in the limiter (empty user set — users arrive via AddUsers). It is an error to add a tag that already exists.
func (*Server) AddUsers ¶
AddUsers merges users into the node's authoritative set (deduped by join key), re-pushes the complete user list to the live inbound, and syncs the limiter. Returns the count of users newly added (already-present join keys update in place and are not counted).
func (*Server) ClearUserThrottle ¶ added in v0.1.0
func (*Server) Close ¶
Close stops the front listeners before the Box: they hold references to the inbounds the Box is about to tear down, and an accept in flight would otherwise inject into a closing inbound.
func (*Server) DelNode ¶
DelNode removes the dynamic inbound and its limiter/bookkeeping. Removing an unknown tag is a no-op error from the inbound manager but the local state is cleared regardless.
func (*Server) DelUsers ¶
DelUsers removes users (by join key) from the node's authoritative set and re-pushes the remaining list to the inbound.
It deliberately does NOT evict them from the limiter — see ForgetUsers.
func (*Server) Detect ¶ added in v0.0.10
Detect reports whether a destination is blocked for a node, the same query the billing pipeline makes per connection. Exposed alongside UpdateRule / GetDetectResult so callers can verify which rule set a tag is actually running — audit failures are otherwise invisible: nothing errors, traffic that should have been blocked simply flows.
func (*Server) DrainTraffic ¶ added in v0.1.0
DrainTraffic reads and resets one join key's counter directly, bypassing the node's user set. forget=true also drops the entry.
GetUserTraffic can only see keys still in ns.users, so a user the panel just removed becomes unbillable the instant DelUsers runs — including any traffic their still-open sessions keep producing, which on the protocols that do not disconnect on delete is unbounded (doc/07 §3.1, doc/14 C-07). The panel controller keeps draining departed keys through here until they go quiet, then forgets them.
func (*Server) ForgetUsers ¶ added in v0.1.0
ForgetUsers drops the limiter state of users who left the node and have since gone quiet. Pair it with stats.TakeAndDelete (Server.DrainTraffic with forget=true) so a departed user is released from both at the same moment.
Split from DelUsers because a deleted user's sessions outlive the deletion on most protocols; until they end, the user must keep their speed and device limits (doc/14 C-14).
func (*Server) GetDetectResult ¶
func (s *Server) GetDetectResult(tag string) ([]api.DetectResult, error)
GetDetectResult drains the audit hits accumulated for a node since the last call (the rule.Manager clears them on read) and returns them by value. The panel layer filters out local rules (RuleID < 0) before reporting upstream.
func (*Server) GetUserTraffic ¶
GetUserTraffic returns per-user byte counters for the node, keyed by join key against the stats registry. reset=true atomically zeroes each counter after read (reset-then-report, doc/10 §7.1). Only users with non-zero traffic in the period are returned (XrayR reporting convention).
func (*Server) MieruAuth ¶ added in v0.0.4
func (s *Server) MieruAuth() MieruAuthSnapshot
MieruAuth returns the snapshot. Safe to call at any time, including before Start; it takes the node lock only briefly.
func (*Server) OnlineIPs ¶
func (s *Server) OnlineIPs(tag string) ([]api.OnlineUser, error)
OnlineIPs returns the users currently connected on the node (per the limiter's online-device map). Note GetOnlineDevice resets the map as a side effect, so each call reports the devices seen since the previous call.
func (*Server) ResolveIdentity ¶
ResolveIdentity maps an inbound's authenticated user name to the billing join key. It satisfies identityResolver for the Hinge A tracker.
removed=true means the node authenticates on wire names (Mieru/TrustTunnel) and this one is no longer in its user set — the panel deleted the user, and the tracker must refuse the connection rather than bill it under a key nothing downstream knows (doc/14 C-03).
An unknown TAG still passes through: the node is gone entirely, so there is no user set to judge against, and its inbound is gone too — no new connection can reach here.
func (*Server) SetAccessFile ¶ added in v0.0.6
func (s *Server) SetAccessFile(f *accesslog.FileLogger)
SetAccessFile wires (or clears with nil) the local XrayR-format access log. Independent of SetAccessLog: one writes a file, the other reports to the panel. Call before Start; the file's lifetime is owned by the caller.
func (*Server) SetAccessLog ¶
SetAccessLog wires (or clears with nil) the process-level access-log reporter consulted by the Hinge A tracker. Call before Start; the manager itself is started/stopped by the panel orchestrator (doc/11 §13).
func (*Server) SetConnPolicy ¶ added in v0.0.3
func (s *Server) SetConnPolicy(p ConnPolicy)
SetConnPolicy records the process-level connection-tuning policy (config.yml ConnectionConfig). Like the Set*Node* setters it must be called before AddNode: the policy is read when the inbound is created and existing inbounds are not retro-fitted, because changing a listener's socket options would mean rebuilding it — exactly the thing that drops live connections.
func (*Server) SetNodeAutoLimit ¶ added in v0.1.0
SetNodeAutoLimit tells the limiter whether this node runs the dynamic speed limiter, which decides whether unlimited users still get a (rate.Inf) bucket so a later throttle can reach their open connections (doc/14 C-11).
func (*Server) SetNodeCert ¶
func (s *Server) SetNodeCert(tag string, cfg cert.CertConfig)
SetNodeCert records the certificate policy for a node tag. It must be called before AddNode(tag, …) so the translation layer can build the inbound TLS block for a plain-TLS node (REALITY nodes ignore it). Overwrites any prior value for the tag (doc/03 T10).
func (*Server) SetNodeConnLimit ¶
SetNodeConnLimit sets the per-user concurrent-TCP-connection cap for a node tag on the shared ConnLimiter (doc/09 §5.3 ConnLimitConfig, T5). max <= 0 clears the tag's limit. Safe to call before or after AddNode; the panel calls it before AddNode alongside the other per-tag policies.
func (*Server) SetNodeProxyProtocol ¶ added in v0.0.8
func (s *Server) SetNodeProxyProtocol(tag string, cfg ProxyProtocolConfig)
SetNodeProxyProtocol records the PROXY protocol policy for a node tag. Like the other Set*Node* setters it must be called before AddNode, which is where the front listener is bound. cfg.Enabled=false clears it.
func (*Server) SetUserThrottle ¶ added in v0.1.0
SetUserThrottle / ClearUserThrottle carry the T7 dynamic speed limit as an override layer, so a panel-driven whole-list push cannot silently undo it (doc/14 C-10).
func (*Server) Stats ¶ added in v0.1.0
Stats exposes the process-wide per-user byte counters (Hinge B). Reporting goes through GetUserTraffic/DrainTraffic; this is the handle for anything that needs to wrap a connection against a key directly.
func (*Server) UpdateInboundLimiter ¶
UpdateInboundLimiter forwards a user-list speed-limit update to the shared limiter. The panel's dynamic speed-limit driver (T7 AutoLimiter) uses it to throttle/restore users without reaching into billing internals (doc/11 §8).
func (*Server) UpdateRule ¶
func (s *Server) UpdateRule(tag string, rules []api.DetectRule) error
UpdateRule installs the audit rule set for a node. It forwards to the shared rule.Manager (the same instance the Hinge A tracker consults). This is beyond the core.Core interface — the panel orchestration layer calls it on the concrete *Server (doc/11 §5, doc/10 §3.6). RuleNotModified handling is the caller's concern; this always installs the list it is given.