Skip to content

Features ​

A detailed look at what VGate provides across its four components.

Proxy protocol & transports ​

The VLESS inbound (vgate-server/) is built on top of xray-core and supports:

  • VLESS protocol
    • Plaintext VLESS (v0) — native.
    • v2 AEAD encryption — enabled via the separate vless.decryption field, not a flow.
    • xtls-rprx-vision — xray's Vision flow, requires outer TLS 1.3 or Reality, and is incompatible with v2 encryption.
  • Transports (via the transport abstraction + registry)
    • tcp — native transport, with TLS / Reality applied through transport/security.Wrap.
    • ws — WebSocket, delegating to xray-core's websocket.ListenWS.
    • xhttp — split-HTTP, delegating to xray-core's splithttp.ListenXH.
  • Multiplexing — TCP / HTTP / WebSocket mux handling.
  • UDP relay — UDP-over-TCP relay and a Vision relay leaf.

A node's listen port and transport/security are delivered by the manager, not written into the server's local config. They're configured per node in the admin console and hot-reloaded.

Virtual nodes & entry-point attribution ​

One physical server can be exposed under many addresses:

  • Virtual (multi-IP child) nodes live alongside real nodes in the manager. A child inherits its parent's transport/security config and carries only its own identity — name, alternate address, optional port override, level, and enabled flag. It never polls and never authenticates.
  • Reality short-ID attribution: every node carries exactly one own reality_sid (auto-generated and kept globally unique when left empty). The whitelist delivered to the server combines the real node's own sid with every virtual child's, subscription links for each entry point advertise that entry point's own sid, and the node maps the handshake's short ID back to the child — so traffic deltas are tagged with the entry point and land in user_node_traffic under the child's ID (inheriting the parent's traffic multiplier). A child's sid has priority over its parent's, which is re-issued on collision.
  • Only the native tcp + reality path is attributable; ws / xhttp and non-reality traffic always book to the real node.
  • Dashboard node counts and Telegram node-up/down alerts cover real nodes only.

Centralized configuration & hot reload ​

The manager owns the canonical configuration. Each server node:

  1. Calls GET /server/config and GET /server/users on a SyncInterval ticker.
  2. Uses HTTP 304 short-circuiting (If-None-Match) so unchanged config costs almost nothing.
  3. Applies changes through UpdateConfig / UpdateUsers without restarting the process.
  4. Reports accumulated per-user, per-entry-point traffic via GetAndResetTraffic → POST /server/traffic.

This means adding a user, revoking a credential, or changing a node's transport takes effect across the fleet with no SSH and no restarts.

Identity & access ​

  • JWT auth for the admin and user APIs. Admin login returns an access token plus a refresh token, and on a 401 the admin console performs one automatic silent refresh; user login returns only an access token (no refresh token — a 401 means re-login).
  • Roles: admin and super_admin. super_admin is required to manage plans and other admins.
  • Node auth: the node data plane uses a separate node token (node_auth middleware), distinct from user/admin JWTs.
  • User portal: access tokens are not refreshable — on a 401 the client clears the token and redirects to /login. Login also supports a Cloudflare Turnstile field and a /verify-email flow.

Users, plans & subscriptions ​

  • Create users and assign them subscription plans.
  • Plans carry quotas and expiry.
  • The user portal and standard client apps consume subscription links (GET /sub/:sub_token).
  • Daily quota reset is run by a manager background ticker.

Billing ​

  • Orders and plans are first-class resources in the manager.
  • A payment notify endpoint (/api/v1/billing/:platform/notify) closes orders on payment for Alipay, WeChat Pay, Stripe, PayPal, and Apple IAP.
  • An expired-order closer runs every 5 minutes.

Traffic accounting ​

  • Proxy nodes maintain per-user, per-entry-point delta traffic counters (traffic.go).
  • GetAndResetTraffic returns accumulated deltas and resets them; each delta carries the entry point's node_id (the real node, or the virtual child whose Reality short ID the client connected with).
  • The manager attributes each delta, records hourly traffic stats as nodes report it, and resets usage counters on the configured quota.reset_day.
  • Admins can view traffic stats per user and per node — including per virtual entry point.

Speed limiting ​

Nodes, plans, and users can carry upload/download speed caps (bytes/sec, 0 = unlimited):

  • Per-node (global) caps the node's aggregate throughput.
  • Per-plan caps every user on that plan; editing a plan re-applies its caps to all current subscribers.
  • Per-user overrides cap an individual user (effective rate is the minimum of the node-global and per-user limits).

The vgate-server node enforces the limits locally with token buckets (golang.org/x/time/rate) on both the counting connection and the xtls-rprx-vision path. The manager stores the caps and delivers them via the node config and the per-user GET /server/users payload.

Two frontends ​

  • Admin Console — manage nodes, users, plans, orders, traffic, announcements, and system config; view stats.
  • User Portal — customers log in, see their profile, subscribe to plans, view their subscription and usage.

Both are Vue 3 + Vite + TypeScript with Element Plus, Pinia, Vue Router, and Axios. In dev, Vite proxies /api → http://localhost:8081 to avoid CORS. The API base URL is read at runtime from window.__ENV__.API_BASE_URL (injected by public/env.js), so you can repoint the backend after deploy without rebuilding.

Telegram integration ​

The manager can run a Telegram bot for alerts, announcements, and per-account ticket notifications. It is configured through DB-backed telegram.* system-config keys (enabled, bot_token, bot_username, user_bot_enabled, and the alert_* toggles).

  • Users and admins bind a personal account via a /start <code> deep link. The code's u_ (user) or a_ (admin) prefix routes the bind to the right account — admins link from Settings → Telegram in the admin console, users from Settings in the portal.
  • Linked users and admins can receive ticket replies and forwarded announcements over Telegram.
  • Admins can broadcast a message to every linked user (optionally also published as an announcement).

Registration, invites & redemption codes ​

  • Registration modes (DB-backed user.* keys): open registration, invite-gated (user.register_require_invite), or restricted to an email-suffix allowlist (user.register_email_suffix_whitelist). Email verification gates purchases and proxy traffic (not login): an unverified user can browse but cannot order or consume traffic until email_verified is true.
  • Trial accounts (user.trial_enabled + user.trial_quota_bytes / user.trial_duration_days): new users can be granted a short, capped trial automatically.
  • Invite codes (/admin/invites): operators issue codes that gate or credit new registrations; invite.default_user_quota sets the quota granted to invited users.
  • Redemption codes (/admin/redemption-codes, redeemed by users at /redeem): operators issue codes users apply to claim plans or credit.

Traffic reminders ​

DB-backed reminder.* keys let the manager notify users when they approach their quota or their reset date: reminder.enabled (master switch), reminder.pct_threshold (e.g. 80 — warn at 80% of quota_bytes), reminder.days_threshold (warn when ≤ N days remain until reset), and reminder.cooldown_days (minimum gap between reminders per user). Users pick their channel (none / email / telegram) via PUT /api/v1/user/reminder-channel.

Support tickets ​

Tickets are a lightweight support channel between users and admins:

  • Users open tickets, reply to admin responses, and close their own ticket. When opening a ticket they choose how they're notified of replies (none / email / telegram), defaulting to telegram when their account is Telegram-linked, else none.
  • Admins list and view all tickets, reply, and move each through the status machine open → in_progress → resolved → closed. A later user reply reopens a closed ticket.
  • When an admin replies, the ticket owner is notified via the method they chose, and every admin with a linked Telegram account gets an alert on new tickets and replies.

Observability & ops ​

  • Structured logging with configurable log.level / log.format.
  • Configurable CORS allowed_origins, timeouts, and JWT TTLs — these are DB-backed and hot-reloadable via PUT /api/v1/admin/system-config.
  • Auto-migrating database (SQLite by default via glebarez/sqlite, or Postgres via db.dialect / db.dsn).
  • GORM AutoMigrate on startup; first-start admin bootstrap.

Security posture ​

  • Separate auth domains: user JWT, admin JWT, node token.
  • Reality / TLS 1.3 support for the Vision flow.
  • DB-backed system config reduces the need to restart for routine policy changes.

Licensed under AGPL-3.0.