Skip to content

Server (Proxy Node)

The vgate-server/ component is a Go binary (cobra CLI) that runs a single VLESS inbound proxy node. It is a stateless worker: it pulls its configuration and authorized user list from the manager, serves proxy traffic, and reports per-user traffic back.

Source: github.com/vgate-project/vgate-server

Build & run

bash
cd vgate-server
go build -o vgate .                 # produces the `vgate` binary
./vgate --config config.yml         # run the proxy (defaults to ./config.yml)

Test & lint:

bash
go test ./...
go vet ./... && gofmt -l .

Local config (what the node owns)

The node's own config.yml holds only manager-connection + sync settings:

yaml
admin_api: http://localhost:8081   # manager base URL
node_id: 1                         # assigned in the admin console
node_token: <NODE_TOKEN>           # assigned in the admin console
sync_interval: 30                  # seconds between syncs
log_level: info

Everything about how the node serves traffic — listen port, transport (tcp/ws/xhttp), TLS/Reality, VLESS flows — is delivered by the manager and hot-reloaded.

Runtime flow

vgate-server/main.gocmd.Execute()run():

  1. Load local viper YAML config.
  2. Create an api.Client pointed at <AdminAPI>/api/v1/server.
  3. Start the VLESS inbound (proxy/vless).
  4. A ticker calls sync() every SyncInterval seconds.

sync()

  • Pulls config + users with HTTP 304 short-circuiting (api/client.go uses If-None-Match; returns api.ErrNotModified).
  • Applies hot-reload via server.UpdateConfig / UpdateUsers (no restart).
  • Reports accumulated per-user traffic (GetAndResetTraffic) back to the manager.

VLESS protocol support

  • Flows: the flow field only takes xtls-rprx-vision (or empty = none), which requires an outer TLS 1.3 or Reality connection. VLESS v2 AEAD is not a flow — it is enabled via the separate vless.decryption field (see below), and is incompatible with xtls-rprx-vision.
  • Transports: native tcp (TLS/Reality via transport/security.Wrap), ws (xray-core websocket.ListenWS), xhttp (xray-core splithttp.ListenXH).
  • Mux: VLESS CommandMux (Mux.Cool) is enabled by default (enableMux: true).
  • UDP: UDP-over-TCP relay; Vision relay leaf.

Flow restriction: flow (e.g. xtls-rprx-vision) is only honored on the tcp transport. When a hot-reload changes the vless block, the node defensively clears flow for ws / xhttp networks and when the security layer is exactly none; an empty/unset security or network (which otherwise resolve to none / tcp) is not matched by that check.

vless block (manager-delivered)

The vless object carries VLESS v2 / obfuscation settings, independent of the transport. All fields are optional:

fieldmeaning
decryption.-separated list of base64url X25519 (or ML-KEM-768) private keys enabling v2 AEAD decryption; "" / none disables it (plaintext VLESS v0).
xor_mode0 off, 1 mask KEM ciphertexts, 2 also wrap the connection in XorConn.
seconds_from / seconds_to0-RTT session-ticket lifetime window in seconds (both 0 = 1-RTT only).
padding.-separated pct-min-max padding spec; empty = library defaults.
flowVLESS flow control, e.g. xtls-rprx-vision (TCP-only, see above).

Optional security fields

Beyond the required settings, the TLS and Reality backends accept additional optional fields (ignored when absent): TLSserver_name, cert_file / key_file (alternatives to cert_pem / key_pem), alpn, min_version, max_version, reject_unknown_sni (if min_version is unset the node enforces TLS 1.2 as a minimum on the native tcp path; for ws / xhttp an unset min_version is left empty and xray-core applies its own default); Realityshow, xver, min_client_ver, max_client_ver, max_time_diff (milliseconds). Reality's target, server_name, and private_key are required — the node rejects a config missing any of them.

Transport abstraction

transport/transport.go defines a Transport interface + registry. The native tcp transport applies TLS/Reality via transport/security.Wrap. ws and xhttp adapters delegate to xray-core listeners, sharing helpers in transport/xraybridge (ChanListener, protojson config decode, TLS/Reality protobuf builders). Transports are registered through anonymous imports in proxy/vless/bootstrap.go.

Inbound internals (proxy/vless/)

FileResponsibility
handler.goVLESS handshake + TCP/UDP forwarder
udp.goUDP-over-TCP relay
vision.goxtls-rprx-vision relay (xray-core leaf)
mux.goTCP / HTTP / WebSocket mux handling
protocol.goVLESS protocol constants / helpers
user.goUser set + connection tracking
traffic.goPer-user delta traffic counters
ratelimit.goPer-user + node-global speed limiting (token buckets)
server.goLifecycle + hot-reload

Speed limiting

A node can cap throughput in both directions, enforced locally with token buckets (golang.org/x/time/rate):

  • Node-global limits (speed_limit_up_bps / speed_limit_down_bps) cap the node's aggregate throughput.
  • Per-user limits (carried on each user in the GET /server/users payload) cap a single user.

The effective rate for a user is the minimum of the node-global and per-user limits; 0 means unlimited. The limiter is applied to both the counting connection and the xtls-rprx-vision path. See proxy/vless/ratelimit.go.

Sizing & deployment

  • One vgate process per node/machine.
  • Tune sync_interval for change-propagation speed vs. request volume (304s keep idle cost low).
  • Run behind the manager for config; expose only the VLESS listen port to clients.

Licensed under AGPL-3.0.