panel

package
v0.1.7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 3, 2026 License: GPL-3.0 Imports: 28 Imported by: 0

Documentation

Overview

Package panel orchestrates multiple nodes, periodic panel tasks and hot reload. Placeholder — implemented in Phase 1 (doc/03 T11).

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type AutoSpeedLimitConfig

type AutoSpeedLimitConfig struct {
	Limit         int `mapstructure:"Limit"`
	WarnTimes     int `mapstructure:"WarnTimes"`
	LimitSpeed    int `mapstructure:"LimitSpeed"`
	LimitDuration int `mapstructure:"LimitDuration"`
}

AutoSpeedLimitConfig is the T7 dynamic speed-limit policy (doc/09 §5.3). Consumed by the Controller when Limit > 0.

type CertConfig

type CertConfig struct {
	CertMode   string            `mapstructure:"CertMode"`
	CertDomain string            `mapstructure:"CertDomain"`
	CertFile   string            `mapstructure:"CertFile"`
	KeyFile    string            `mapstructure:"KeyFile"`
	Provider   string            `mapstructure:"Provider"`
	Email      string            `mapstructure:"Email"`
	DNSEnv     map[string]string `mapstructure:"DNSEnv"`
}

CertConfig — T10 cert automation policy (doc/09 §5.3). Mapped onto cert.CertConfig by the Controller before AddNode.

type Config

type Config struct {
	Log   LogConfig    `mapstructure:"Log"`
	Nodes []NodeConfig `mapstructure:"Nodes"`

	// --- sing-box passthrough blocks (doc/09 §6.1, §6.2) ---
	//
	// These are NOT parsed by viper: they carry literal sing-box config, which
	// the base decodes itself (core/singbox/passthrough.go). They are held as raw
	// JSON because viper LOWERCASES every map key it reads — harmless for
	// sing-box's own snake_case keys, but it would silently mangle case-sensitive
	// values such as a transport's `headers: {Host: ...}`. loadPassthrough reads
	// them straight from the YAML instead, preserving case exactly.
	//
	// Empty means "not configured": DNS falls back to the Box default, outbounds
	// fall back to a lone direct, and routing is absent (everything egresses via
	// the default outbound).
	DNSRaw      json.RawMessage `mapstructure:"-"`
	OutboundRaw json.RawMessage `mapstructure:"-"`
	RouteRaw    json.RawMessage `mapstructure:"-"`
	// AccessLog is the process-level access-log reporting policy (doc/09 §4.3,
	// doc/11 §13.1). It is the billing/accesslog.Config directly; the Manager's
	// normalize() fills defaults. Enable=false (the zero value) leaves access
	// logging off, so existing configs without an AccessLog block load cleanly.
	AccessLog accesslog.Config `mapstructure:"AccessLog"`
	// ConnectionConfig is node-local connection tuning, applied to every
	// inbound. Absent means "leave every base default alone".
	ConnectionConfig *ConnectionConfig `mapstructure:"ConnectionConfig"`
}

Config is the top-level config.yml shape (doc/09 §3).

func LoadConfig

func LoadConfig(path string) (*Config, error)

LoadConfig reads a YAML config.yml from path (viper), unmarshals it into Config, and fills defaults. Key matching is case-insensitive (mapstructure), matching XrayR semantics.

type ConnLimitConfig

type ConnLimitConfig struct {
	Enable         bool `mapstructure:"Enable"`
	MaxConnPerUser int  `mapstructure:"MaxConnPerUser"`
}

ConnLimitConfig — net-new (doc/09 §5.3): per-user concurrent-connection cap, pushed to the shared ConnLimiter per node tag before AddNode.

type ConnectionConfig added in v0.0.3

type ConnectionConfig struct {
	UDPTimeout           int  `mapstructure:"UDPTimeout"`           // seconds; base default ~5min
	TCPKeepAlive         int  `mapstructure:"TCPKeepAlive"`         // seconds; idle before first probe
	TCPKeepAliveInterval int  `mapstructure:"TCPKeepAliveInterval"` // seconds; gap between probes
	DisableTCPKeepAlive  bool `mapstructure:"DisableTCPKeepAlive"`

	// --- XrayR compatibility: parsed, warned about, never applied ---
	Handshake    int `mapstructure:"Handshake"`
	ConnIdle     int `mapstructure:"ConnIdle"`
	UplinkOnly   int `mapstructure:"UplinkOnly"`
	DownlinkOnly int `mapstructure:"DownlinkOnly"`
	BufferSize   int `mapstructure:"BufferSize"`
}

ConnectionConfig is config.yml's connection-tuning block (doc/09 §7).

The first four fields are real: they map onto sing-box ListenOptions and take effect on every inbound. The five after them are XrayR's — kept ONLY so that a config carried over from XrayR gets a specific migration message per key instead of being silently ignored. They never change behaviour. See core/singbox.ConnPolicy for the field-by-field reasoning on why sing-box has no equivalent for them.

Durations are whole SECONDS (int), matching ControllerConfig.UpdatePeriodic and XrayR's own convention. A duration string is deliberately not accepted: viper's default decode hook turns a bare `15` into 15 NANOseconds when the target is time.Duration, and a timeout that silently becomes 15ns is worse than one that will not parse.

func (*ConnectionConfig) Policy added in v0.0.3

func (c *ConnectionConfig) Policy() singbox.ConnPolicy

Policy converts the block into the core-side policy.

func (*ConnectionConfig) Warnings added in v0.0.3

func (c *ConnectionConfig) Warnings() []string

Warnings reports the XrayR-only keys present in the config, each with what to do instead. Returning them (rather than logging here) keeps LoadConfig free of logging and lets the tests assert on the text.

This exists because the alternative — accepting the key and doing nothing — is the worst outcome available: the operator believes a timeout is in force, and finds out otherwise only when a node misbehaves under load.

type Controller

type Controller struct {
	// contains filtered or unexported fields
}

Controller drives one node. It satisfies Service.

func NewController

func NewController(server *singbox.Server, apiClient api.API, config *ControllerConfig) *Controller

NewController wires a node orchestrator over a shared Core and a panel client. A dynamic speed limiter is constructed when the node enables one (Limit > 0).

func (*Controller) Close

func (c *Controller) Close() error

Close stops the monitor and removes the node from the Core.

func (*Controller) Start

func (c *Controller) Start() error

Start performs the initial sync and launches the periodic monitor.

type ControllerConfig

type ControllerConfig struct {
	ListenIP       string `mapstructure:"ListenIP"`
	SendIP         string `mapstructure:"SendIP"`
	UpdatePeriodic int    `mapstructure:"UpdatePeriodic"`
	EnableDNS      bool   `mapstructure:"EnableDNS"`
	DNSType        string `mapstructure:"DNSType"`
	// EnableProxyProtocol makes Sing2 own the public listener and accept a
	// PROXY header on it (core/singbox/proxyproto.go), so a node behind
	// haproxy's send-proxy still sees real client addresses. Any sender is
	// believed — keep the port reachable only from the load balancer.
	EnableProxyProtocol bool `mapstructure:"EnableProxyProtocol"`
	EnableFallback      bool `mapstructure:"EnableFallback"`

	// Node-local REALITY: legacy panels keep the private key node-side and do not
	// deliver reality-opts, so the Controller injects REALITYConfigs into the
	// NodeInfo before AddNode when EnableREALITY is set (unless the operator forces
	// the panel copy via DisableLocalREALITYConfig). doc/09 §5.3.
	EnableREALITY             bool          `mapstructure:"EnableREALITY"`
	DisableLocalREALITYConfig bool          `mapstructure:"DisableLocalREALITYConfig"`
	REALITYConfigs            RealityConfig `mapstructure:"REALITYConfigs"`

	// MieruUserHintIsMandatory forces mieru's per-connection user hint on or off
	// for this node, overriding whatever the panel says.
	//
	// Node-local authority is correct here, unlike the panel-wins rule that
	// governs protocol identity (enable_vless and friends): this flag changes
	// nothing about what a subscription contains or how a client is configured
	// — mieru clients ALWAYS emit the hint (mieru pkg/protocol/mux.go:646,661 →
	// addUserHintToNonce, skipped only when the username is empty). It only
	// decides whether the SERVER still falls back to trying every user when the
	// hint misses. That fallback is the node's CPU bill, so the node's operator
	// gets to decide. Same category as the REALITY private key being node-local.
	//
	// nil = defer to the panel. true = require the hint, which removes the
	// try-every-user path entirely (~10x cheaper on junk traffic; see
	// protocol/mieru/metrics.go and upstream enfein/mieru#273) at the cost of
	// locking out mieru clients older than v3.31.0. false = force the fallback
	// back on, the escape hatch when that lockout bites.
	MieruUserHintIsMandatory *bool `mapstructure:"MieruUserHintIsMandatory"`

	// Local control sub-blocks, all consumed by the Controller: AutoSpeedLimit
	// drives the T7 dynamic throttle; GlobalDeviceLimit / ConnLimit are pushed to
	// the shared limiter before AddNode; Cert feeds the T10 inbound TLS build.
	AutoSpeedLimitConfig AutoSpeedLimitConfig `mapstructure:"AutoSpeedLimitConfig"` // T7 dynamic speed limit
	ConnLimitConfig      ConnLimitConfig      `mapstructure:"ConnLimitConfig"`      // per-user concurrent conns
	CertConfig           CertConfig           `mapstructure:"CertConfig"`           // T10 cert automation
}

ControllerConfig is the node-level local control policy (doc/09 §5.3). Only ListenIP and UpdatePeriodic are load-bearing for increment D; the rest are parsed so configs round-trip and later increments can wire them.

type LogConfig

type LogConfig struct {
	Level      string `mapstructure:"Level"`
	AccessPath string `mapstructure:"AccessPath"`
	ErrorPath  string `mapstructure:"ErrorPath"`
	// Timezone stamps the access log. Empty = the machine's local time.
	// Accepts a fixed offset ("+08:00") or an IANA name ("Asia/Shanghai");
	// the offset form always works, the name needs system tzdata.
	Timezone string `mapstructure:"Timezone"`
}

LogConfig mirrors XrayR LogConfig (doc/09 §4.1).

type NodeConfig

type NodeConfig struct {
	PanelType        string           `mapstructure:"PanelType"`
	ApiConfig        api.Config       `mapstructure:"ApiConfig"`
	ControllerConfig ControllerConfig `mapstructure:"ControllerConfig"`
}

NodeConfig is one entry of Nodes[] (doc/09 §5): panel type + panel connection (api.Config) + local control policy.

type Panel

type Panel struct {
	// contains filtered or unexported fields
}

Panel owns the shared Core and the per-node services.

func New

func New(config *Config) *Panel

New constructs a Panel from a loaded Config. It does not start anything.

func (*Panel) Close

func (p *Panel) Close() error

Close stops every service in reverse order, then the Core. Safe to call more than once, and safe to call CONCURRENTLY — see closeMu.

func (*Panel) Start

func (p *Panel) Start() error

Start builds and starts the shared Core, then constructs and starts one Controller per node. On any failure it unwinds what it already started.

type RealityConfig

type RealityConfig struct {
	Show             bool     `mapstructure:"Show"`
	Dest             string   `mapstructure:"Dest"`
	ProxyProtocolVer uint64   `mapstructure:"ProxyProtocolVer"`
	ServerNames      []string `mapstructure:"ServerNames"`
	PrivateKey       string   `mapstructure:"PrivateKey"`
	MinClientVer     string   `mapstructure:"MinClientVer"`
	MaxClientVer     string   `mapstructure:"MaxClientVer"`
	MaxTimeDiff      uint64   `mapstructure:"MaxTimeDiff"`
	ShortIds         []string `mapstructure:"ShortIds"`
}

RealityConfig is the node-local REALITY server config (doc/09 §5.3), mirroring api.REALITYConfig. Fields map to the REALITYConfigs YAML block.

type Service

type Service interface {
	Start() error
	Close() error
}

Service is the lifecycle contract every node orchestrator satisfies (ported from XrayR service.Service). The panel starts and closes them uniformly.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL