# shroud Single-binary Go replacement for the [Outline VPN](https://getoutline.org/) server stack. Replaces the original 3-process deployment (Node.js shadowbox + outline-ss-server + Prometheus) with one Go binary that uses [outline-ss-server](https://github.com/Jigsaw-Code/outline-ss-server) as a library. Optionally includes [AmneziaWG](https://docs.amnezia.org/documentation/amnezia-wg/) support with HTTP/3 QUIC cover traffic on port 443. ## Features - **Single binary** — no Node.js, no child processes, no sidecar Prometheus - **Shadowbox-compatible REST API** — drop-in replacement for existing Outline clients - **Hot-reload** — adding/removing keys swaps cipher lists and listeners without downtime - **AmneziaWG** — obfuscated WireGuard VPN with automatic TLS certificates and HTTP/3 cover server - **Built-in metrics** — Prometheus endpoint with optional node_exporter collectors (Linux) - **CLI management** — manage keys and server config without a running server - **Atomic state persistence** — single YAML file with safe write-via-rename ## Quick Start ### Build from source ```bash go build ./cmd/shroud/ ``` ### Run ```bash # Start the server ./shroud -c config.example.yaml # With verbose logging ./shroud -c config.yaml -v ``` ### Install on Ubuntu/Debian ```bash curl -fsSL https://sourcecraft.dev/bigbes/shroud/raw/branch/main/install.sh | sudo bash ``` The installer builds from source, creates a systemd service, configures firewall rules, and prints the management API URL on completion. ## CLI Usage ```bash # 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 # Shell completions ./shroud completion bash ./shroud completion zsh ``` ## Configuration See [`config.example.yaml`](config.example.yaml) for the full configuration reference. ```yaml server: name: "My Outline Server" hostname: "example.com" api: listen_addr: ":8081" metrics: listen_addr: "127.0.0.1:8081" 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 state_file: state.yaml ``` ## REST API All management endpoints live under `//` prefix. The secret is auto-generated on first run and printed to the log. | Method | Endpoint | Description | |--------|----------|-------------| | `GET` | `//server` | Server info | | `PUT` | `//name` | Rename server | | `GET` | `//access-keys` | List all keys | | `POST` | `//access-keys` | Create key | | `GET` | `//access-keys/{id}` | Get key | | `PUT` | `//access-keys/{id}` | Upsert key | | `DELETE` | `//access-keys/{id}` | Delete key | | `PUT` | `//access-keys/{id}/name` | Rename key | | `PUT` | `//access-keys/{id}/data-limit` | Set data limit | | `DELETE` | `//access-keys/{id}/data-limit` | Remove data limit | | `PUT` | `//server/port-for-new-access-keys` | Set default port | | `PUT` | `//server/hostname-for-access-keys` | Set hostname | | `PUT` | `//server/access-key-data-limit` | Set default data limit | | `DELETE` | `//server/access-key-data-limit` | Remove default data limit | | `GET` | `//metrics/enabled` | Check metrics status | | `PUT` | `//metrics/enabled` | Toggle metrics | | `GET` | `//metrics/transfer` | Per-key byte transfer | | `GET` | `//access-keys/{id}/awg-config` | Download AmneziaWG config | Public endpoints (no auth): | Method | Endpoint | Server | Description | |--------|----------|--------|-------------| | `GET` | `/metrics` | Metrics | Prometheus scrape target | | `GET` | `/healthz` | Metrics | Health check | ## Architecture ``` 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 ``` 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`. ## Building with version info ```bash go build -ldflags='-X main.version=1.0.0' ./cmd/shroud/ ``` ## Testing ```bash go test ./... ```