Accounts & delivery
Protocols
| Protocol | Transports | Auth | Node service |
|---|---|---|---|
| SSH / SSH-WS | Raw TCP, WebSocket, TLS via stunnel | Username + password (Linux user) | sshd/Dropbear plus autoscript-node-ssh-ws and stunnel |
| VMess | ws, httpupgrade, xhttp, grpc | UUID | Xray inbound behind Nginx |
| VLESS | ws, httpupgrade, xhttp, grpc (+ flow, e.g. xtls-rprx-vision) | UUID | Xray inbound behind Nginx |
| Trojan | ws, httpupgrade, xhttp, grpc | Password | Xray inbound behind Nginx |
| ZIVPN | UDP with shared OBFS password | Password from a shared list | zivpn service with /etc/zivpn/config.json |
The xray alias
xray always provisions VLESS. Tick multiple Xray protocols to get sibling rows sharing one UUID.Every Xray inbound binds to 127.0.0.1. Nginx owns the real, Cloudflare-compatible public ports and proxies inward, which is what makes CDN fronting and shared certificates possible.
10001 vmess-ws 10011 vmess-xhttp 10021 vmess-httpupgrade
10002 vless-ws 10012 vless-xhttp 10022 vless-httpupgrade
10003 trojan-ws 10013 trojan-xhttp 10023 trojan-httpupgrade
10085 api-in (stats / gRPC handler API)
base offsets: ws=10000, xhttp=10010, httpupgrade=10020, tcp=10030, grpc=10040
protocol index: vmess=1, vless=2, trojan=3Path convention
- WebSocket:
/vless,/vmess,/trojan. - xHTTP:
/vless-xhand siblings. - HTTPUpgrade:
/vless-huand siblings. - gRPC: a bare serviceName such as
vless-gr, with no leading slash.
Stream tuning applied to every inbound
{
"tcpNoDelay": true,
"tcpFastOpen": true,
"tcpcongestion": "bbr",
"tcpKeepAliveIdle": 30,
"tcpKeepAliveInterval": 15
}Accounting identity
The Xray “email” used for stats is the account’s stable account id, not the username — usernames may legitimately collide across planes, ids never do.
xHTTP mode narrowing
Even when an account or CDN lists several xHTTP modes, the node picks exactly one and prefers stream-up. packet-up and auto generate many small upload POSTs that badly hurt upload throughput through Nginx and CDN edges.
SSH accounts are real Linux users, created by the provisioning script with a hardened sshd drop-in. Understanding this removes most SSH support tickets.
- Expiry padding. Linux account expiry is date-granular and disables at start-of-day, so the script sets the expiry to the day after
EXPIRES. Exact-hour and quota enforcement is done separately by the agent. - Login shell wrapper.
/usr/local/bin/grvpn-motdprints the banner (fetched from the local agent, or/etc/grvpn-ssh-banner.html), strips HTML, then execs a real shell. Many tunnel clients (HTTP Custom, Injector, HA Tunnel, NetMod) need an interactive PTY behind sshd or they see immediate EOF. - Hardening in
sshd_config.d/99-grvpn.conf: password and keyboard-interactive auth on,PermitOpenrestricted to the Cloudflare-supported port list so local forwards cannot pivot to arbitrary internal hosts,PermitListen none(remote-Rforwards fully disabled),GatewayPorts no,X11Forwarding no. - Drop-in repair. Earlier over-restrictive drop-ins (
AllowTcpForwarding no,DisableForwarding yes) are stripped, the managed user is appended to any existingAllowUsers(OpenSSH is first-match) and removed fromDenyUsers. - Password handling. The script hashes with
openssl passwd -6(or Pythoncrypt) and applies it viausermod -p, bypassing PAM cracklib rejecting short or username-like passwords. - Unlock logic.
configure_login_user()creates or updates the user, sets shell, expiry and hash, force-unlocks (usermod -U/passwd -u) and removes/etc/nologin— the usual cause of “account exists but is locked from a previous revoke”.
Node SSH accounts are tunnel-only
- 1Ensure Xray is installed —
setup_xray.shruns if the binary or/usr/local/etc/xray/config.jsonis missing. - 2Add the client to the correct inbound via
xray_client.py add <proto>. - 3Validate with
xray run -testbefore restarting the service. A failed config test is exactly what produces an account stuck inpending. - 4Restart Xray, or for suspensions use the gRPC handler API (
xray api rmu -tag=<inbound> <email>) to kick a single user instantly without dropping everyone else’s sessions.
Xray privileges on the node
The agent writes a systemd override granting Xray CAP_NET_ADMIN, CAP_NET_BIND_SERVICE and CAP_NET_RAW and running it as root. This is required: speed caps rely on setsockopt(SO_MARK), and the upstream unit runs as nobody, which silently drops the mark and makes speed limits a no-op. Log paths /var/log/xray/access.log and error.log are created world-writable so Xray starts even when running unprivileged.
- ZIVPN has no per-user config:
/etc/zivpn/config.jsonholdsauth.config, the array of all valid passwords. With no accounts it becomes["disabled"], which locks out every client. - The OBFS password is fleet-wide and identical on every node, so one client profile works everywhere. The service restarts only when the value actually changes.
- Egress is plane-wide: the outbound chosen for the
zivpnprotocol key becomes the whole node’s ZIVPN upstream (WARP → local SOCKS5, address+port → that SOCKS5, otherwise direct). - Installing the tunnel is a prerequisite — account creation returns 400 until
Tunnels → ZIVPN UDPis installed on the target host.
Per-account outbound does nothing for ZIVPN
outboundId has no effect on the node. Only the plane-wide policy for the zivpn key matters.Each account with a non-zero speed cap gets its own cloned outbound tagged mk-<mark> with a unique sockopt.mark, plus a routing rule sending that user through it. tc then shapes by socket mark instead of client IP — which is what makes caps work behind CDNs, on IPv6 and before the first packet is seen.
- Speed caps work for WARP-routed users too: the mark travels through the WireGuard outbound's own socket.
- Blocking rules are inserted ahead of speed-mark routes, so blocking always wins.
| Situation | Recommendation |
|---|---|
| Cheap mobile tunnel clients, carrier bundles | SSH-WS or SSH over TLS on a CDN-friendly port |
| Best throughput on modern clients | VLESS over WebSocket or HTTPUpgrade behind Nginx |
| Heavily filtered networks | VLESS/Trojan on 443 TLS through a CDN with an SNI override |
| Simple UDP-friendly offering | ZIVPN with the fleet-wide OBFS password |
| Legacy client compatibility | VMess over WebSocket |
