| .forgejo/workflows | ||
| .omp/skills/vless-overlay-dev | ||
| .wiki | ||
| authentik | ||
| cmd | ||
| config | ||
| dashboard@9f9ed49abc | ||
| deploy | ||
| docs/adr | ||
| mgmt | ||
| protocol | ||
| raw/sources | ||
| tools | ||
| vless-client@ea2ce5d0aa | ||
| wiki | ||
| wintun | ||
| xray | ||
| .env.example | ||
| .gitignore | ||
| .gitmodules | ||
| _filter.py | ||
| client.bat | ||
| client.ps1 | ||
| client.sh | ||
| config.go | ||
| CONTEXT.md | ||
| device.go | ||
| device_windows.go | ||
| filter.go | ||
| filter_test.go | ||
| firewall.go | ||
| firewall_test.go | ||
| go.mod | ||
| go.sum | ||
| index.md | ||
| integration_test.go | ||
| ip.go | ||
| ip_test.go | ||
| log.md | ||
| mesh.go | ||
| nat.go | ||
| nat_test.go | ||
| packet.go | ||
| packet_test.go | ||
| README.md | ||
| router.go | ||
| router_test.go | ||
| server.bat | ||
| server.sh | ||
| server_nat.go | ||
| tcpflow.go | ||
| tcpflow_test.go | ||
| transport.go | ||
| transport_test.go | ||
| udp.go | ||
| udp_test.go | ||
| vless-client.exe | ||
| vless.go | ||
| vless_combined.exe | ||
| vless_combined.exe~ | ||
| vless_conn.go | ||
| vless_conn_test.go | ||
| vless_mgmt.exe | ||
| vless_server.go | ||
| vless_server_test.go | ||
| vless_test.go | ||
| vless_tun.exe | ||
| vlessd.exe~ | ||
| wintun.dll | ||
| wintun.zip | ||
VLESS Overlay Network
A Tailscale/Netbird competitor using VLESS as the data plane instead of WireGuard.
Architecture
┌─────────────┐
│ Dashboard │ Next.js 14 (:3000)
│ (Next.js) │
└──────┬──────┘
│ REST API
┌──────┴──────┐
│ Management │ Go API (:8080)
│ API │
└──────┬──────┘
│
┌────────────┴────────────┐
│ VLESS Proxy (:8443) │ Go, tenant-scoped
└────────────┬────────────┘
│
┌─────────────────┼─────────────────┐
│ │ │
┌────┴────┐ ┌────┴────┐ ┌────┴────┐
│ vlessd │ │ vlessd │ │ vlessd │ Daemon (Linux/macOS/Windows)
│ :9070 │ │ :9070 │ │ :9070 │
└────┬────┘ └────┬────┘ └────┬────┘
│ │ │
┌────┴────┐ ┌────┴────┐ ┌────┴────┐
│ CLI │ │ CLI │ │Wails GUI│ Clients
└─────────┘ └─────────┘ └─────────┘
Components
| Component | Directory | Role |
|---|---|---|
| VLESS Proxy | cmd/vless_combined/ |
Handshake + TCP forwarding over VLESS |
| Management API | mgmt/ |
Tenants, inbounds, connections, JWT auth |
| Dashboard | dashboard/ |
Next.js 14 UI for server management |
| vlessd (daemon) | cmd/vlessd/ |
VLESS conn + WinTUN + data plane + local API |
| vless_cli | cmd/vless_cli/ |
Thin CLI → daemon API |
| Wails Client | vless-client/ |
Windows GUI → daemon API |
| VLESS Protocol | vless_tun/ |
Wire protocol, NAT, packet parsing, tests |
Data Flow
OS traffic → WinTUN → raw IP → ParseIPPacket → FlowNAT → VLESS → Proxy → Dest
↓
OS ← WinTUN ← BuildIPv4 ← NAT tunWrite ← TCPFlow ← VLESS ← Proxy response
Multicast/broadcast (mDNS, SSDP, LLMNR) is dropped at the TUN layer.
Production CI/CD
Production deployment is handled by Forgejo Actions rather than by manually starting
server.sh or relying on a one-time git pull. Pull requests run the Go tests and
dashboard production build/typecheck. Successful pushes to main deploy the exact commit on the
vless-production runner, restart the systemd services, check API and dashboard
health, and roll back automatically if deployment fails. Host bootstrap and manual
rollback instructions are in deploy/README.md.
Quick Start
Server
# Test server — on the first run, server.bat creates stable, ignored test
# credentials and a self-signed TLS certificate in .vless-test/.
server.bat
It prints local test credentials for exercising the relay and management processes. The one-URL Wails/CLI flow requires a configured public HTTPS dashboard origin and a management-provided relay profile; the old TLS-only/localhost test bundle is not a supported client deployment.
To create a portable local-test profile for another machine:
server.bat init-client laptop-b
This creates .vless-test\clients\laptop-b\ for lower-level relay tests. The
bundle is not a substitute for the public dashboard URL required by clients;
keep its setup key and certificate private.
# Production example — use a real domain certificate. Device credentials are
# issued after setup-key enrollment, not placed in an environment allowlist.
$env:VLESS_ADMIN_PASSWORD = "use-a-unique-12-plus-character-password"
$env:VLESS_ADMIN_EMAIL = "admin@example.com"
$env:VLESS_JWT_SECRET = "use-a-unique-random-32-plus-character-secret"
$env:VLESS_TLS_CERT_FILE = "C:\path\to\fullchain.pem"
$env:VLESS_TLS_KEY_FILE = "C:\path\to\privkey.pem"
# Build and run behind the public HTTPS dashboard origin (private API :9080 + TLS VLESS :8443)
server.bat
Log in as admin@example.com with VLESS_ADMIN_PASSWORD (set
VLESS_ADMIN_EMAIL to use another email address), open Devices & keys, and create a setup key. The
dashboard shows the setup key exactly once. Clients enter only the public
dashboard URL and choose Browser approval or Setup key. Setup-key enrollment
exchanges the key once over the dashboard HTTPS endpoint; management returns
the device credential and public relay profile.
Setup keys default to one use and expire after 24 hours. A device authorization
defaults to 30 days; disable or extend it from Devices & keys. Once it
expires or is revoked, the relay closes the active connection and the client
must authenticate again: desktop clients use browser approval, while CLI and
automation clients use a valid setup key. For Ansible, create a reusable
key with a maximum-use limit (or 0 for unlimited) and a suitable setup-key
expiry; previously enrolled devices do not depend on that setup key remaining
valid.
Set VLESS_TLS_SERVER_NAME only for the relay certificate when required; it is
not a client-facing dashboard URL.
Reality relay (optional)
The relay uses a pinned Xray sidecar for VLESS + RAW + REALITY. The Windows
client launchers download and checksum-verify the pinned Xray release, then
bundle xray.exe beside the client binaries. Linux deployments should install
the same pinned release beside vlessd; set the Reality variables in .env
from .env.example. Keep the server private key only on the relay; clients
receive the public profile through management enrollment. Generate the private
key with xray x25519 and keep it only on the relay. The client-facing public
key is derived from VLESS_REALITY_PRIVATE_KEY at relay startup, so there is no
second public-key value to keep synchronized.
With the public HTTPS dashboard configured, a new client enters one dashboard URL and chooses Browser approval or Setup key. Browser approval opens the system browser and keeps a copyable approval URL in the same Wails window. The daemon persists the public profile returned by management and uses it as the authoritative relay target. A missing profile is a clear enrollment error, not a silent legacy relay fallback.
The Reality listener and the dashboard are separate trust boundaries. Keep the mesh gateway on its loopback address and do not expose the management API directly.
If a previously enrolled client reports an unsupported Reality fingerprint or an invalid public key, its stored profile is stale. Set the server's matching public values first, then re-enroll the device; do not replace the public key with an arbitrary value on the client.
The management API defaults to 127.0.0.1:9080. Expose the dashboard and API
only through the configured HTTPS reverse proxy/domain; never give the private
management listener or relay address to clients.
Cloudflare and Single-IP Front Door
For one public IP, Xray owns public TCP 443. It admits valid Reality
connections and forwards ordinary TLS connections that fail Reality admission
to an internal Caddy listener. Caddy then routes by hostname and path:
reality.vless.tokoliza.org: DNS-only, handled by Xray Reality.relay.vless.tokoliza.org: proxied, forwarded by Caddy to the VLESS WSS listener.dashboard.vless.tokoliza.org: proxied, forwarded by Caddy to Next.js and/api/*to management.netbird.tokoliza.org: proxied or DNS-only, forwarded by Caddy to the NetBird LXC.firewall.tokoliza.org: optional, forwarded by Caddy to the private OPNsense WebGUI.
The internal flow is:
WAN:443 -> OPNsense NAT -> VLESS/Xray LXC:443
|
+-- valid Reality -> mesh gateway
+-- ordinary TLS -> Caddy:8443
+-- NetBird LXC
+-- dashboard LXC
+-- WSS Xray inbound
The sample Caddy layout is in deploy/Caddyfile.cloudflare.example. Caddy
must listen only on the OPNsense LAN address or a dedicated proxy LXC address;
it must not be another public 443 listener. VLESS_REALITY_TARGET is the
private Caddy address, for example 192.168.10.1:8443.
Use a publicly trusted certificate on Caddy because the Reality hostname is
DNS-only. Cloudflare Full (strict) also accepts that certificate. Enable
Network > WebSockets, keep caching disabled for /api/* and /vless/ws,
and do not put Cloudflare Access in front of the relay path. If you expose the
firewall hostname, put Cloudflare Access with MFA in front of that hostname.
The exact production values for vless.tokoliza.org are in
deploy/vless.tokoliza.org.env.example. The Authentik callback is:
https://dashboard.vless.tokoliza.org/api/v1/auth/oidc/callback
The current client tries Reality first and WSS second. This preserves public
port 443 for both transports without requiring a second public IP.
Public Dashboard URL And Migration
- Set
VLESS_DASHBOARD_ORIGIN=https://dashboard.example.com/panel,NEXT_PUBLIC_BASE_PATH=/panel, andNEXT_PUBLIC_DASHBOARD_ORIGIN=https://dashboard.example.com/panel. - Configure the reverse proxy to serve the dashboard at
/panel, strip the/panelprefix, and forward/panel/api/*as/api/*to the private management listener. SetVLESS_TRUST_PROXY=trueonly when the proxy overwritesX-Forwarded-HostandX-Forwarded-Proto. - Remove
NEXT_PUBLIC_API_ORIGINand any client-facingVLESS_MGMT_ADDRor relay URL. KeepVLESS_MGMT_ADDR=127.0.0.1:9080as an operator-only bind address. - Rebuild the dashboard and restart the server. Existing config retains its legacy
serverfield for inspection, but new connections use the saved management URL and profile. - Existing Wails local storage containing a relay URL is ignored. Enter only the public dashboard URL and choose an explicit auth mode.
Changing the dashboard URL clears the stored identity and profile before the selected mode reauthenticates. Setup keys are never persisted by Wails or the daemon.
Authentik OIDC SSO
The server supports native Authentik Authorization Code + PKCE login. Create a dedicated OAuth2/OpenID provider in the self-hosted Authentik instance running with this VLESS VM. VLESS should use that local instance's issuer, client ID, and client secret:
Copy-Item .env.example .env
# Edit .env with the TLS paths, dashboard URL, local Authentik issuer, client ID,
# client secret, and VLESS_OIDC_OPERATOR_EMAILS.
# server.bat loads it automatically.
Set VLESS_AUTH_MODE=oidc, NEXT_PUBLIC_AUTH_MODE=oidc,
VLESS_OIDC_ISSUER, VLESS_OIDC_CLIENT_ID, VLESS_OIDC_CLIENT_SECRET, and
VLESS_OIDC_REDIRECT_URL. The callback is normally
https://dashboard.example.com/api/v1/auth/oidc/callback. VLESS discovers
Authentik's authorization, token, userinfo, and logout endpoints from the
issuer metadata, exchanges the authorization code with the client secret and
PKCE verifier, reads the verified email from userinfo, and creates an HttpOnly
VLESS session cookie.
The dashboard does not pass a callback URI parameter. VLESS infers the origin
from the request and accepts a direct callback only for a currently assigned
dashboard interface address on VLESS_DASHBOARD_PORT when interface discovery
is enabled. Run the one-shot
cmd/vless_authentik_provision binary with a narrowly scoped local Authentik
API token to reconcile those strict callback and logout URIs automatically.
The deployment script runs it when VLESS_AUTHENTIK_RECONCILE=true. Keep the
token outside the daemon's environment. VLESS_OIDC_REDIRECT_URLS is only for
fixed, non-interface origins that are intentionally managed separately.
The public reverse proxy still routes the dashboard to Next.js and /api/* to
the management API; it does not perform the OIDC login. 0.0.0.0 remains a
bind address only. The provisioner includes the public dashboard URL and the
currently assigned direct interface addresses in the local Authentik provider.
Use the public dashboard URL in the Wails client.
For the initial deployment, leave VLESS_OIDC_OPERATOR_EMAILS unset and set
VLESS_OIDC_BOOTSTRAP_FIRST_USER=true in .env. The first identity accepted
by Authentik becomes the persistent operator; later verified identities are
persisted as user accounts rather than rejected. Operators
can promote, demote, add, or remove assignments from Access control in the
dashboard. The service protects the last persisted operator from removal or
demotion. An explicit VLESS_OIDC_OPERATOR_EMAILS list gives those identities
an operator role when they first sign in; it does not overwrite assignments
that were already managed in the dashboard. Password login is disabled in
native OIDC mode. The older VLESS_AUTH_MODE=oidc-proxy mode remains available
for deployments that intentionally delegate OIDC to a trusted reverse proxy.
Users have My devices self-service rather than tenant or infrastructure access: they can create a one-use setup key for the default tenant, list and revoke only their own keys and devices, and renew one of their own device authorizations for up to 30 days. They cannot create reusable or permanent credentials, see another identity’s records, manage roles, or alter network configuration.
Windows Client (GUI + TUN)
# Right-click → Run as Administrator (or double-click for auto-elevation)
client.bat production
Builds and launches: vlessd (daemon, :9070) + vless-client.exe (Wails GUI).
Use client.bat test only for the checked-out .vless-test environment.
The PowerShell launcher accepts the same explicit choice: .\client.ps1 -Mode production or .\client.ps1 -Mode test.
Linux/macOS Client (CLI only)
./client.sh <dashboard-url> <name> [setup-key]
# Example:
./client.sh https://dashboard.example.com/panel my-laptop vsk_...
Builds and launches: vlessd (daemon) + vless_cli connect.
CLI Commands
vless_cli connect -management-url https://dashboard.example.com/panel -auth-mode setup_key -name my-device -setup-key vsk_...
vless_cli connect -management-url https://dashboard.example.com/panel -auth-mode browser_approval -name my-device
vless_cli status
vless_cli disconnect
Project Structure
vless_tun/
├── cmd/
│ ├── vless_combined/ # Server (API + VLESS proxy)
│ ├── vlessd/ # Client daemon
│ ├── vless_cli/ # CLI client
│ ├── vless_mgmt/ # Standalone management server
│ └── vless_tun/ # Standalone TUN example
├── mgmt/ # Management API package
├── dashboard/ # Next.js 14 dashboard
├── vless-client/ # Wails desktop app (submodule)
├── vless_tun/ # VLESS protocol + NAT + packet
│ ├── nat.go # FlowNAT — per-flow VLESS connections
│ ├── tcpflow.go # TCPFlow — per-TCP-flow state machine
│ ├── packet.go # IP/TCP packet builder
│ ├── ip.go # IP packet parser
│ ├── device.go # Cross-platform TUN device
│ ├── device_windows.go # WinTUN adapter (Windows only)
│ └── *_test.go # 82 tests
├── server.bat # Launch server
├── client.bat # Launch Windows client (auto-admin)
├── client.ps1 # PowerShell client launcher
├── client.sh # Linux/macOS client launcher
└── vlessd.log # Daemon log file
IP Addressing
- The shared default overlay pool is
10.100.0.0/24and starts at10.100.0.2. - Operators can configure an IPv4 IP range for a tenant through the admin tenant API; when configured, that tenant's devices are allocated addresses from its range.
- Tenants without a configured range use the shared default pool.
- Assigned on first VLESS connect (port 0 registration)
Tenant traffic is isolated server-side: cross-tenant overlay frames are dropped before forwarding. This applies to both TCP and UDP mesh forwarding; egress policy is unchanged.
Encoding Git Remote (SSO-gated)
git remote add origin https://github.com/org/repo.git
git push -u origin main
# Username: email, Password: PAT (Personal Access Token)
License
MIT