Single-binary Go replacement for the Outline VPN server stack. Replaces the original 3-process deployment (Node.js shadowbox + outline-ss-server + Prometheus) with one Go binary that uses outline-ss-server as a library.
Supports three protocols: Shadowsocks, AmneziaWG (obfuscated WireGuard with HTTP/3 cover traffic), and VLESS+REALITY (TLS 1.3 camouflage proxy).
# Shadowsocks only
curl -fsSL https://bigbes.sourcecraft.dev/shroud/install.sh | sudo bash
# Shadowsocks + AmneziaWG
curl -fsSL https://bigbes.sourcecraft.dev/shroud/install.sh | sudo bash -s -- --awg --domain vpn.example.com
# Shadowsocks + VLESS+REALITY (auto-detects decoy server)
curl -fsSL https://bigbes.sourcecraft.dev/shroud/install.sh | sudo bash -s -- --vless
The installer builds from source, creates a systemd service, configures firewall rules, and prints the management API URL on completion.
go build ./cmd/shroud/
go build -ldflags='-X main.version=1.0.0' ./cmd/shroud/ # with version
./shroud -c config.example.yaml # start the server
./shroud -c config.yaml -v # with verbose logging
# Server management (no running server required)
shroud server info -c config.yaml
shroud server set-port 50000 -c config.yaml
shroud server set-hostname vpn.example.com -c config.yaml
shroud server set-name "My VPN" -c config.yaml
# Access key management (no running server required)
shroud key list -c config.yaml
shroud key add -n "user1" -c config.yaml
shroud key add -n "user2" -p 51234 --cipher chacha20-ietf-poly1305 -c config.yaml
shroud key remove 1 -c config.yaml
shroud key rename 1 "new-name" -c config.yaml
# VLESS+REALITY management
shroud vless info -c config.yaml
shroud vless keygen # generate x25519 keypair
shroud vless share 0 -c config.yaml # generate share link
shroud vless scan --addr 203.0.113.0/24 # scan for decoy targets
shroud vless autodetect --write -c config.yaml # auto-detect best decoy
# Shell completions
shroud completion bash
shroud completion zsh
See config.example.yaml for the full configuration reference.
server:
name: "My Outline Server"
hostname: "example.com"
api:
listen_addr: ":8081"
metrics:
listen_addr: "127.0.0.1:8082"
node_exporter_collectors:
- cpu
- meminfo
- loadavg
- filesystem
- diskstats
- netdev
shadowsocks:
default_port: 0 # 0 = pick random available port
default_cipher: chacha20-ietf-poly1305
replay_history: 10000
amneziawg:
enabled: false
listen_port: 443
address: "10.14.0.0/24"
domain: "vpn.example.com" # for automatic Let's Encrypt certs
vless:
enabled: false
listen_addr: ":443"
server_names: # SNIs accepted by REALITY handshake
- www.microsoft.com
dest: "www.microsoft.com:443" # decoy forward target
state_file: state.yaml
All management endpoints live under /<secret>/ prefix. The secret is auto-generated on first run and printed to the log.
| Method | Endpoint | Description |
|---|---|---|
GET |
/<secret>/server |
Server info |
PUT |
/<secret>/name |
Rename server |
GET |
/<secret>/access-keys |
List all keys |
POST |
/<secret>/access-keys |
Create key |
GET |
/<secret>/access-keys/{id} |
Get key |
PUT |
/<secret>/access-keys/{id} |
Upsert key |
DELETE |
/<secret>/access-keys/{id} |
Delete key |
PUT |
/<secret>/access-keys/{id}/name |
Rename key |
PUT |
/<secret>/access-keys/{id}/data-limit |
Set data limit |
DELETE |
/<secret>/access-keys/{id}/data-limit |
Remove data limit |
PUT |
/<secret>/server/port-for-new-access-keys |
Set default port |
PUT |
/<secret>/server/hostname-for-access-keys |
Set hostname |
PUT |
/<secret>/server/access-key-data-limit |
Set default data limit |
DELETE |
/<secret>/server/access-key-data-limit |
Remove default data limit |
GET |
/<secret>/metrics/enabled |
Check metrics status |
PUT |
/<secret>/metrics/enabled |
Toggle metrics |
GET |
/<secret>/metrics/transfer |
Per-key byte transfer |
GET |
/<secret>/access-keys/{id}/awg-config |
Download AmneziaWG config |
GET |
/<secret>/access-keys/{id}/outline-config |
Download Outline Smart Dialer config |
Public endpoints (no auth):
| Method | Endpoint | Server | Description |
|---|---|---|---|
GET |
/metrics |
Metrics | Prometheus scrape target |
GET |
/healthz |
Metrics | Health check |
cmd/shroud/main.go CLI entry point, cobra commands, signal handling
|
+-> internal/config/ YAML config loading and validation
+-> internal/store/ YAML file persistence (atomic write via rename)
+-> internal/ssserver/ Wraps outline-ss-server as a library
+-> internal/metrics/ Prometheus registry + TransferTracker
+-> internal/api/ REST API handlers (shadowbox-compatible)
+-> internal/awgserver/ AmneziaWG server + HTTP/3 QUIC cover
+-> internal/vless/ VLESS+REALITY proxy server
+-> internal/reality/ TLS scanner + auto-detection for REALITY decoys
+-> internal/mmdb/ GeoIP database downloader with auto-update
The Shadowsocks proxy is imported as a Go library — not run as a subprocess. When keys change, SyncKeys() rebuilds cipher lists and hot-swaps listeners. The integration surface is ~100 lines in internal/ssserver/server.go.
| Dependency | Version | Purpose |
|---|---|---|
| outline-ss-server | v1.9.3-rc2 | Shadowsocks proxy (used as library) |
| outline-sdk | v0.0.21 | Outline transport primitives |
| amneziawg-go | v1.0.4 | DPI-resistant WireGuard implementation |
| xtls/reality | latest | REALITY protocol for TLS camouflage |
| quic-go | v0.59.0 | QUIC/HTTP3 for AWG cover traffic |
| prometheus/client_golang | v1.23.2 | Go Prometheus client and registry |
| prometheus/node_exporter | v1.10.2 | OS-level metrics collectors (Linux) |
go test ./...