xray

package
v1.2.0 Latest Latest
Warning

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

Go to latest
Published: Jul 20, 2026 License: AGPL-3.0 Imports: 24 Imported by: 0

Documentation

Overview

Package xray models the Xray-core config as typed Go structs (so field-name mistakes are compile errors) and supervises the Xray child process.

Engine note: Xray-core is the single proxy engine for all three protocols (VLESS-Vision-TCP-443 with fallbacks, Trojan-WS-443 via fallback, and Hysteria2-UDP-60000).

Index

Constants

View Source
const (
	TagVLESS    = "vless-in"
	TagTrojan   = "trojan-in"
	TagHysteria = "hysteria-in"
	TagReality  = "vless-reality-in"
)

Inbound tags (also the keys the live add/remove-user API addresses).

View Source
const APIPort = 10085

APIPort is the loopback port for the Xray gRPC StatsService.

View Source
const PinnedVersion = "v26.6.27"

PinnedVersion is the Xray-core release auto-installed when no binary is found. Keep in sync with deploy/install-xray.sh and the Dockerfile ARG.

Why pinned: Xray 26.3.27 shipped a broken Hysteria2 server-side auth handshake (26.6.x fixes it). A floating "latest" can silently regress, so we pin an exact release and its checksums.

View Source
const VisionFlow = "xtls-rprx-vision"

VisionFlow is the VLESS flow used for raw-TCP Vision.

Variables

This section is empty.

Functions

func EnabledInboundTags

func EnabledInboundTags(set *model.Settings) []string

EnabledInboundTags lists the inbound tags that currently carry users (the targets for live user removal via `xray api rmu`).

func EnsureBinary

func EnsureBinary(dir string) (string, error)

EnsureBinary returns the path to an Xray binary in dir, downloading the pinned release there if one isn't already present. It lets a fresh box come up without a separate install step when no system Xray (XRAY_BIN / PATH) is found.

func VersionMatchesPinned added in v1.0.0

func VersionMatchesPinned(v string) bool

VersionMatchesPinned reports whether a reported Xray version is the pinned release, tolerating a leading "v": PinnedVersion carries it, but `xray version` output does not (Supervisor.Version parses "26.6.27"), so a plain string compare would spuriously flag every node as version-skewed.

Types

type API

type API struct {
	Tag      string   `json:"tag"`
	Services []string `json:"services"`
}

API exposes gRPC services (StatsService) on the api-tagged inbound.

type Balancer

type Balancer struct {
	Tag         string            `json:"tag"`
	Selector    []string          `json:"selector"`
	Strategy    *BalancerStrategy `json:"strategy,omitempty"`
	FallbackTag string            `json:"fallbackTag,omitempty"`
}

Balancer load-balances across the outbounds whose tag matches a Selector prefix. leastPing + the Observatory route to the fastest live one; FallbackTag is used when none are healthy.

type BalancerStrategy

type BalancerStrategy struct {
	Type string `json:"type"`
}

BalancerStrategy selects the balancing algorithm ("leastPing" | "random" | …).

type Certificate

type Certificate struct {
	CertificateFile string `json:"certificateFile,omitempty"`
	KeyFile         string `json:"keyFile,omitempty"`
}

Certificate points at on-disk PEM files (shared cert for all listeners).

type Config

type Config struct {
	Log         *Log         `json:"log,omitempty"`
	Stats       *Stats       `json:"stats,omitempty"`
	API         *API         `json:"api,omitempty"`
	Policy      *Policy      `json:"policy,omitempty"`
	DNS         *DNS         `json:"dns,omitempty"`
	Inbounds    []Inbound    `json:"inbounds"`
	Outbounds   []Outbound   `json:"outbounds"`
	Routing     *Routing     `json:"routing,omitempty"`
	Observatory *Observatory `json:"observatory,omitempty"`
}

Config is the top-level Xray configuration document.

func Generate

func Generate(set *model.Settings, users []model.User, opts Options, proxies map[string][]model.ProxyEndpoint) (*Config, error)

Generate builds the full Xray config from settings + enabled users.

Layout (all on one box):

  • VLESS-Vision inbound owns :443 + TLS. fallbacks route by path → loopback Trojan-WS inbound; default → the Go panel (decoy/panel/sub).
  • Trojan inbound on 127.0.0.1:<trojan_port>, WS transport, no TLS (the 443 inbound already terminated it).
  • Hysteria2 inbound on :<hysteria_port> (UDP), its own TLS; host nftables redirects the hop range to it.
  • One credential set per user (uuid for VLESS, password for Trojan/Hy2).

proxies holds the live upstream proxies of each egress lane, keyed by lane ID.

type DNS

type DNS struct {
	Servers []string `json:"servers,omitempty"`
}

DNS is the Xray DNS block. Servers are upstream resolvers — plain IPs ("1.1.1.1"), DoH URLs ("https://dns.google/dns-query"), or "localhost".

type DokodemoSettings

type DokodemoSettings struct {
	Address string `json:"address"`
}

DokodemoSettings is the settings block for the api inbound.

type Fallback

type Fallback struct {
	Path string `json:"path,omitempty"`
	Dest any    `json:"dest"`
	Xver int    `json:"xver,omitempty"`
}

Fallback routes non-VLESS traffic on the shared port. Dest is an int port or "host:port" string. Used in M2+ to send browser/Trojan traffic to the panel.

type GRPCSettings

type GRPCSettings struct {
	ServiceName string `json:"serviceName"`
}

GRPCSettings configures the gRPC transport.

type HTTPInboundSettings

type HTTPInboundSettings struct {
	Accounts []ProxyUser `json:"accounts,omitempty"`
}

HTTPInboundSettings is the "settings" object for an http forward-proxy inbound.

type HysteriaClient

type HysteriaClient struct {
	Auth  string `json:"auth"`
	Email string `json:"email,omitempty"`
}

HysteriaClient is one Hysteria2 user. Xray's infra/conf parser maps the JSON "auth" field directly to the Account.Auth protobuf field — using "password" silently gives empty auth.

type HysteriaInboundSettings

type HysteriaInboundSettings struct {
	Version int              `json:"version"` // must be 2
	Users   []HysteriaClient `json:"users"`
}

HysteriaInboundSettings is the "settings" object for a Hysteria2 inbound. Per Xray's schema the per-user list is "users" (NOT "clients" like Trojan/ VLESS) — using the wrong key leaves Xray with no users, so traffic isn't attributed to an email (breaking per-user stats and IP/access logging).

type HysteriaSettings

type HysteriaSettings struct {
	Version int `json:"version"` // must be 2
}

HysteriaSettings is the Hysteria2 transport block. Xray models Hysteria2 as a streamSettings transport (network "hysteria"); per-user auth lives in the inbound's settings.clients[].auth.

type Inbound

type Inbound struct {
	Tag            string          `json:"tag,omitempty"`
	Listen         string          `json:"listen,omitempty"`
	Port           int             `json:"port"`
	Protocol       string          `json:"protocol"`
	Settings       any             `json:"settings,omitempty"`
	StreamSettings *StreamSettings `json:"streamSettings,omitempty"`
	Sniffing       *Sniffing       `json:"sniffing,omitempty"`
}

Inbound is one listening proxy endpoint. Settings is protocol-specific.

func UserInbounds

func UserInbounds(set *model.Settings, users []model.User) []Inbound

UserInbounds builds inbound stubs (tag + protocol + clients) for the given users on the enabled protocols. Used by the live add-user API (`xray api adu`) so new users join the running Xray without a restart.

type LevelPolicy

type LevelPolicy struct {
	StatsUserUplink   bool `json:"statsUserUplink"`
	StatsUserDownlink bool `json:"statsUserDownlink"`
	ConnIdle          int  `json:"connIdle,omitempty"`
	BufferSize        int  `json:"bufferSize,omitempty"`
}

LevelPolicy enables per-user traffic stats for a policy level and bounds a connection's memory: ConnIdle reaps idle connections; BufferSize caps the per-connection buffer (KB) so many concurrent flows can't balloon RSS.

type Log

type Log struct {
	Loglevel string `json:"loglevel,omitempty"`
}

Log configures Xray logging.

type Observatory

type Observatory struct {
	SubjectSelector   []string `json:"subjectSelector"`
	ProbeURL          string   `json:"probeUrl,omitempty"`
	ProbeInterval     string   `json:"probeInterval,omitempty"`
	EnableConcurrency bool     `json:"enableConcurrency,omitempty"`
}

Observatory periodically probes the proxy-pool outbounds (subjectSelector tag prefixes) so the balancer's leastPing strategy can skip dead/slow proxies.

type Options

type Options struct {
	// PanelDest is where the VLESS default fallback forwards non-proxy traffic
	// (the Go panel's loopback HTTP address, e.g. "127.0.0.1:8080").
	PanelDest string

	// Groups resolves the "iplist:<source>/<group>" routing entries to their
	// domains/CIDRs. Parsed from the on-disk iplist databases and cached by the
	// caller (they only change on a geo refresh). Nil or missing groups simply
	// drop those rules — see expandGroups.
	Groups geo.GroupSet
}

Options carries non-DB generation parameters.

type Outbound

type Outbound struct {
	Tag      string `json:"tag,omitempty"`
	Protocol string `json:"protocol"`
	Settings any    `json:"settings,omitempty"`
}

Outbound is one egress. Settings is protocol-specific (nil for freedom/blackhole).

type Policy

type Policy struct {
	Levels map[string]LevelPolicy `json:"levels,omitempty"`
	System *SystemPolicy          `json:"system,omitempty"`
}

Policy turns on per-user up/down counters.

type ProxyOutboundSettings

type ProxyOutboundSettings struct {
	Servers []ProxyServer `json:"servers"`
}

ProxyOutboundSettings is the "settings" object for a socks/http proxy outbound.

type ProxyServer

type ProxyServer struct {
	Address string      `json:"address"`
	Port    int         `json:"port"`
	Users   []ProxyUser `json:"users,omitempty"`
}

ProxyServer is one upstream proxy (with optional auth).

type ProxyUser

type ProxyUser struct {
	User string `json:"user"`
	Pass string `json:"pass"`
}

ProxyUser is the username/password for an authenticated proxy.

type RealitySettings

type RealitySettings struct {
	Show        bool     `json:"show"`
	Dest        string   `json:"dest"`        // "host:port" of the borrowed site
	Xver        int      `json:"xver"`        // PROXY protocol version (0 = off)
	ServerNames []string `json:"serverNames"` // accepted SNIs (the borrowed site)
	PrivateKey  string   `json:"privateKey"`  // X25519 private (base64 raw-url)
	ShortIds    []string `json:"shortIds"`
	// MaxTimeDiff is the anti-replay window in ms: a client whose handshake clock
	// differs by more than this is rejected, so a probe can't replay a captured
	// REALITY auth later. 0 (omitted) disables the check.
	MaxTimeDiff int `json:"maxTimeDiff,omitempty"`
}

RealitySettings configures the REALITY security layer. Instead of presenting our own cert, the inbound forwards the TLS handshake of a real site (Dest / ServerNames) and authenticates clients via the X25519 key + shortId.

type RouteRule

type RouteRule struct {
	Type        string   `json:"type"` // always "field"
	InboundTag  []string `json:"inboundTag,omitempty"`
	Domain      []string `json:"domain,omitempty"`
	IP          []string `json:"ip,omitempty"`
	Port        string   `json:"port,omitempty"`
	Network     string   `json:"network,omitempty"` // "tcp,udp" — catch-all matcher
	Protocol    []string `json:"protocol,omitempty"`
	OutboundTag string   `json:"outboundTag,omitempty"`
	BalancerTag string   `json:"balancerTag,omitempty"`
}

RouteRule is one Xray field rule. Same-field values are OR'd; different fields are AND'd. Traffic goes to OutboundTag, or BalancerTag (a proxy pool).

type Routing

type Routing struct {
	DomainStrategy string      `json:"domainStrategy,omitempty"`
	Rules          []RouteRule `json:"rules,omitempty"`
	Balancers      []Balancer  `json:"balancers,omitempty"`
}

Routing holds the ordered field rules. Unmatched traffic uses the first outbound (direct). Balancers group several outbounds into one egress.

type Sniffing

type Sniffing struct {
	Enabled      bool     `json:"enabled"`
	DestOverride []string `json:"destOverride,omitempty"`
}

Sniffing inspects proxied connections to recover their real destination (HTTP host / TLS SNI / QUIC) so domain routing rules can match it.

type Sockopt

type Sockopt struct {
	TrustedXForwardedFor []string `json:"trustedXForwardedFor,omitempty"`
}

Sockopt carries socket-level inbound options. trustedXForwardedFor whitelists the upstream peers whose X-Forwarded-For header Xray will trust.

type SocksInboundSettings

type SocksInboundSettings struct {
	Auth     string      `json:"auth"`
	Accounts []ProxyUser `json:"accounts,omitempty"`
	UDP      bool        `json:"udp"`
}

SocksInboundSettings is the "settings" object for a socks forward-proxy inbound (proxy mode). Auth is "password" when accounts are present, else "noauth".

type Stats

type Stats struct{}

Stats enables the stats engine (marshals to {}).

type StreamSettings

type StreamSettings struct {
	Network          string            `json:"network,omitempty"`  // "tcp" | "ws" | "hysteria" | "grpc"
	Security         string            `json:"security,omitempty"` // "tls" | "reality" | "" (none)
	TLSSettings      *TLSSettings      `json:"tlsSettings,omitempty"`
	WSSettings       *WSSettings       `json:"wsSettings,omitempty"`
	HysteriaSettings *HysteriaSettings `json:"hysteriaSettings,omitempty"`
	RealitySettings  *RealitySettings  `json:"realitySettings,omitempty"`
	GRPCSettings     *GRPCSettings     `json:"grpcSettings,omitempty"`
	Sockopt          *Sockopt          `json:"sockopt,omitempty"`
}

StreamSettings configures transport + transport-level security.

type Supervisor

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

Supervisor owns the Xray child process and the on-disk config.json. It serializes config applies: marshal -> validate -> atomic swap -> restart, and supervises the process — an unexpected exit is auto-restarted with backoff.

If no Xray binary is available (e.g. local dev on macOS), Apply still writes and the panel keeps running; it just logs that Xray isn't being (re)started.

func NewSupervisor

func NewSupervisor(binName, configPath, assetDir string) *Supervisor

NewSupervisor resolves binName (via PATH or as an absolute path) and targets configPath for the generated config. assetDir holds the geo databases.

func (*Supervisor) APIAddr

func (s *Supervisor) APIAddr() string

APIAddr is the loopback address of Xray's gRPC API (StatsService + live user add/remove). The wiring lives here so callers don't rebuild it ad hoc.

func (*Supervisor) AddUsers

func (s *Supervisor) AddUsers(apiAddr string, inbounds []Inbound) error

func (*Supervisor) Apply

func (s *Supervisor) Apply(cfg *Config) error

Apply writes the config atomically (validating first when possible) and restarts Xray.

func (*Supervisor) ApplyRaw added in v1.0.0

func (s *Supervisor) ApplyRaw(data []byte) error

ApplyRaw is Apply for a config that is already marshaled JSON — used by the node agent, which receives the exact config the panel generated and applies it verbatim (after substituting its own cert paths) rather than round-tripping it through the Config struct. Same validate → atomic swap → restart, with the same rollback backup, so a config the node's Xray can't parse never kills it.

func (*Supervisor) AssetDir

func (s *Supervisor) AssetDir() string

AssetDir returns the directory holding the geoip.dat / geosite.dat databases.

func (*Supervisor) BinPath added in v0.15.0

func (s *Supervisor) BinPath() string

BinPath returns the resolved Xray binary, or "" if none was found. The self-test spawns a throwaway Xray client from the same binary, so it needs this.

func (*Supervisor) ConfigBytes

func (s *Supervisor) ConfigBytes() ([]byte, error)

ConfigBytes returns the on-disk config.json currently applied to Xray.

func (*Supervisor) HasBackup

func (s *Supervisor) HasBackup() bool

HasBackup reports whether a rollback config (config.json.bak) is available.

func (*Supervisor) LogTail

func (s *Supervisor) LogTail() []string

LogTail returns the buffered recent Xray log lines.

func (*Supervisor) QueryStats

func (s *Supervisor) QueryStats(apiAddr string) (map[string]Traffic, error)

QueryStats reads per-user traffic counters from the running Xray StatsService (via `xray api statsquery`). Keyed by user email (we use "u<id>").

func (*Supervisor) RemoveUsers

func (s *Supervisor) RemoveUsers(apiAddr string, tags, emails []string) error

RemoveUsers removes users (by email) from each given inbound tag via `xray api rmu` (no restart).

func (*Supervisor) Restart added in v0.13.0

func (s *Supervisor) Restart() error

Restart stops the running Xray and starts a fresh one from the config already on disk. Unlike Apply it neither regenerates nor re-validates the config — it is the operator's "kick it" button for a wedged or misbehaving process, and it also makes a process-level change (e.g. the TZ the child runs in) take effect without waiting for the next config change.

Every live VPN connection is dropped, so this is only ever operator-initiated.

func (*Supervisor) Running

func (s *Supervisor) Running() bool

Running reports whether the Xray child process is currently up. Reflects reality: a crashed process clears s.cur until a restart succeeds.

func (*Supervisor) SetOnAccess

func (s *Supervisor) SetOnAccess(fn func(email, ip string))

SetOnAccess registers a callback invoked for each Xray access-log line that carries a user email + source IP (used to track online status / connections).

func (*Supervisor) SetOnCrash added in v0.7.0

func (s *Supervisor) SetOnCrash(fn func(err error))

SetOnCrash registers a callback invoked when the Xray child exits unexpectedly (a genuine crash, not an intentional Stop/Apply). Used to alert the operator.

func (*Supervisor) SetOnRecover added in v1.2.0

func (s *Supervisor) SetOnRecover(fn func())

SetOnRecover registers a callback invoked when Xray comes back after a crash.

func (*Supervisor) StartedAt

func (s *Supervisor) StartedAt() int64

StartedAt returns the unix start time of the current Xray process, or 0 if it's down. It changes on every (re)start, so clients can detect a config reload.

func (*Supervisor) Stop

func (s *Supervisor) Stop()

Stop terminates the Xray process and prevents further (re)starts (used on panel shutdown).

func (*Supervisor) SubscribeLogs

func (s *Supervisor) SubscribeLogs() (<-chan string, func())

SubscribeLogs returns a channel of new Xray log lines and an unsubscribe func.

func (*Supervisor) UptimeSeconds

func (s *Supervisor) UptimeSeconds() int64

UptimeSeconds reports how long the current Xray process has been running, or 0 if it's down.

func (*Supervisor) Version

func (s *Supervisor) Version() string

Version returns the Xray version string (e.g. "26.6.1"), cached after the first call. Empty when no binary is available.

func (*Supervisor) WriteConfig

func (s *Supervisor) WriteConfig(cfg *Config) error

WriteConfig atomically writes config.json WITHOUT validating or restarting Xray. Used after live user add/remove so a crash-restart (the monitor reloads from disk) preserves the change. The content is generated by trusted code, so validation (which would reparse the geo DBs and cost seconds) is skipped.

type SystemPolicy

type SystemPolicy struct {
	StatsInboundUplink    bool `json:"statsInboundUplink"`
	StatsInboundDownlink  bool `json:"statsInboundDownlink"`
	StatsOutboundUplink   bool `json:"statsOutboundUplink"`
	StatsOutboundDownlink bool `json:"statsOutboundDownlink"`
}

SystemPolicy enables system-wide traffic stats.

type TLSSettings

type TLSSettings struct {
	ServerName       string        `json:"serverName,omitempty"`
	RejectUnknownSni bool          `json:"rejectUnknownSni,omitempty"`
	ALPN             []string      `json:"alpn,omitempty"`
	MinVersion       string        `json:"minVersion,omitempty"`
	Certificates     []Certificate `json:"certificates,omitempty"`
}

TLSSettings configures the TLS layer for an inbound.

type Traffic

type Traffic struct {
	Up   int64
	Down int64
}

Traffic is a per-user uplink/downlink counter snapshot.

type TrojanClient

type TrojanClient struct {
	Password string `json:"password"`
	Email    string `json:"email,omitempty"`
}

TrojanClient is one Trojan user.

type TrojanInboundSettings

type TrojanInboundSettings struct {
	Clients []TrojanClient `json:"clients"`
}

TrojanInboundSettings is the "settings" object for a Trojan inbound.

type VLESSClient

type VLESSClient struct {
	ID    string `json:"id"`             // UUID
	Flow  string `json:"flow,omitempty"` // "xtls-rprx-vision"
	Email string `json:"email,omitempty"`
}

VLESSClient is one VLESS user.

type VLESSInboundSettings

type VLESSInboundSettings struct {
	Clients    []VLESSClient `json:"clients"`
	Decryption string        `json:"decryption"` // always "none" for VLESS
	Fallbacks  []Fallback    `json:"fallbacks,omitempty"`
}

VLESSInboundSettings is the "settings" object for a VLESS inbound.

type WSSettings

type WSSettings struct {
	Path string `json:"path,omitempty"`
	// AcceptProxyProtocol lets the loopback Trojan inbound read the real client
	// IP from the PROXY-protocol header the VLESS fallback prepends (xver).
	AcceptProxyProtocol bool `json:"acceptProxyProtocol,omitempty"`
}

WSSettings configures the WebSocket transport (Trojan-WS).

type WireGuardPeer

type WireGuardPeer struct {
	PublicKey  string   `json:"publicKey"`
	Endpoint   string   `json:"endpoint"`
	AllowedIPs []string `json:"allowedIPs,omitempty"`
}

WireGuardPeer is one WireGuard peer (Cloudflare's WARP endpoint).

type WireGuardSettings

type WireGuardSettings struct {
	SecretKey string          `json:"secretKey"`
	Address   []string        `json:"address"`
	Peers     []WireGuardPeer `json:"peers"`
	Reserved  []int           `json:"reserved,omitempty"`
	MTU       int             `json:"mtu,omitempty"`
}

WireGuardSettings is the "settings" object for a wireguard outbound (used for the Cloudflare WARP egress).

Jump to

Keyboard shortcuts

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