Install the client
One command, no dependencies. Grabs the right surang-client binary for your OS and arch from GitHub releases (Linux and macOS; on Windows, grab a binary from the releases page).
$ curl https://surang.eulerbutcooler.xyz/install.sh | sh
$ surang-client login
$ surang-client -tunnel web=localhost:3000
Prefer to read before piping? The script is plain POSIX sh, read it here, or pin a version with
curl -fsSL https://surang.eulerbutcooler.xyz/install.sh | sh -s -- v0.2.1.
Run surang-client login
with server address https://api.surang.eulerbutcooler.xyz, pick “Create an account”, then open a tunnel from any local port.
Running your own server instead? The self-host section below covers that.
What is surang?
surang-server runs on a VPS with a wildcard domain. surang-client runs on your machine, dials out to the server, and every request to your assigned subdomain gets routed down the tunnel to your local port.
NAT? No problem
The client dials out and keeps one persistent connection open. No port forwarding, no public IP needed on your side.
Many tunnels, one wire
Multiple tunnels per client ride a single outbound connection. -tunnel api=localhost:4000 is just another flag away.
3-word subdomains
Random hyphenated names like crown-cycle-dimmed, so one wildcard certificate covers every tunnel.
Accounts & tokens
Email + password accounts, API tokens with selectable expiry (1h, 1d, 1w, never). A TUI login saves the token locally.
Auto TLS
One wildcard cert for the domain and *.domain, issued and renewed automatically via Let's Encrypt DNS-01.
Bytes in, bytes out
The tunnel never inspects HTTP. It relays raw bytes both directions over multiplexed streams. Fast and dumb, on purpose.
How it works
A visitor hits your subdomain, the server finds your client's session, opens a new stream, and pipes bytes end to end.
The client dials the server's control port and keeps the connection open. It's an outbound connection, so NAT never gets in the way.
That connection is a yamux session: one TCP connection carrying many streams. The server maps each public
subdomain to a session in an in-memory registry. When a visitor hits a subdomain, the server opens a new stream,
writes a dial frame naming the tunnel label, and relays raw bytes. The client reads the label, dials the local
service, and pipes both directions. The registry dies with the process, so tunnels are ephemeral by design.
Users and tokens live in SQLite on disk, stored as hashes.
Self-host it
A VPS, a domain on Cloudflare DNS, and one Docker command.
Run the server
Add a CF_DNS_API_TOKEN (Zone:DNS:Edit) so Let's Encrypt can do DNS-01 for the wildcard cert.
docker run -d --name surang \ -e CF_DNS_API_TOKEN=your_cloudflare_token \ -p 443:443 -p 80:80 -p 5555:5555 -p 9000:9000 \ -v surang-data:/data \ ghcr.io/eulerbutcooler/surang-server:latest \ -domain surang.xyz -tls-domain surang.xyz \ -tls-email you@example.com -db /data/surang.db
No Docker? Grab a server binary from
releases and run it under
systemd.
Plaintext dev mode is just surang-server -domain surang.xyz -db surang.db.
Point DNS at it
Two A records, DNS-only (grey cloud, since the CF proxy would break TLS termination and the control port):
# type name content
A @ your.server.ip
A * your.server.ip
Open ports 443, 80 and 5555 in the host firewall and cloud security list.
Log in & tunnel
Install the client, then:
surang-client login # server = https://api.your.domain
surang-client -tunnel web=localhost:3000
✓ web → https://crown-cycle-dimmed.surang.xyz
Drop your localhost
into the pipe.
curl https://surang.eulerbutcooler.xyz/install.sh | sh




