ProxyWebSock lets you surface private intranet applications to the public internet without poking inbound firewall holes. It builds a reverse tunnel over WebSockets between a cloud‑facing relay and one or more inside-the-network agents, then exposes both HTTP CONNECT and SOCKS5 proxies for browsers and tooling.
- Architecture
- Features
- Getting Started
- Command Reference
- Dashboard & Monitoring
- Advanced Topics
- Roadmap Ideas
- Support
Browser ──────┐
CLI tools ────┼─HTTP CONNECT / SOCKS5────┐
│ │
│ ┌───▼─────────────────────────┐
│ │ Relay (public) │
│ │ - HTTPS + ACME │
│ │ - WebSocket /tunnel │
│ │ - HTTP CONNECT proxy │
│ │ - SOCKS5 proxy │
│ │ - Dashboard & metrics │
│ └───▲──────────────┬──────────┘
│ │ │
└──────────────────────────┼──────────────┘
│
WebSocket tunnel (WSS)
│
┌─────▼───────┐
│ Agent(s) │
│ - outbound only HTTPS/WSS
│ - TCP dial into intranet
└─────────────┘
The agent maintains a persistent WSS tunnel, multiplexing traffic via JSON frames (register, dial, write, close, err). Destination DNS queries happen inside the intranet, so TLS handshakes preserve SNI end-to-end.
- Single binary (Go 1.22) powered by Cobra with
relayandagentsubcommands. - Reverse tunnel over WebSockets: only outbound HTTPS/WSS is required from the intranet.
- Dual proxies:
- HTTP CONNECT (Basic Auth).
- SOCKS5 with username/password (same credentials).
- Agent ACLs via regex host:port filters.
- Automatic TLS on relay using Let’s Encrypt (ACME HTTP-01).
- Per-agent PAC files for quick browser configuration.
- Dashboard (Tailwind + Chart.js) showing live metrics, resource graphs, stream inventory – auto-refreshes every 3 s.
- Prometheus metrics at
/metricscovering bytes transferred, dial/auth errors, agents connected, etc. - Graceful shutdown on SIGINT/SIGTERM across all listeners.
- Docker & Compose examples with optional Caddy front-end.
- Go 1.22+
- Public domain pointing to the relay host (for ACME)
- Outbound HTTPS/WSS allowed from the intranet host running the agent
make build # builds web assets and the Go binaryOther useful targets:
make lint # golangci-lint with revive/staticcheck/gocritic
make test # go test ./...
make race # go test -race ./...
make bench # go test -bench=.
make fuzz # go test ./internal/protocol -fuzz=FuzzDecodeBinaryFrame
make cover # coverage report (build/coverage.out)./intratun relay \
--proxy-listen=:8080 \
--secure-listen=:443 \
--socks-listen=:1080 \
--agents=myagent:supersecret \
--acl-allow='^.*:443$' \
--acme-host=relay.example.com \
--acme-email=ops@example.com \
--acme-cache=/var/lib/intratun/acme \
--acme-http=:80This exposes:
https://relay.example.com/tunnel– agent WebSocket endpoint (WSS).https://relay.example.com/– real-time dashboard with metrics/charts.https://relay.example.com/status.json– JSON data feed.http://relay.example.com:8080– HTTP CONNECT proxy (Basic auth).relay.example.com:1080– SOCKS5 proxy (username/password).
./intratun agent \
--relay=wss://relay.example.com/tunnel \
--id=myagent \
--token=supersecret \
--dial-timeout-ms=5000 \
--max-frame=32768The agent reconnects automatically with exponential backoff and resolves DNS inside the private network.
Configuration obeys env > file > flags. Both commands accept --config (YAML). Environment variables override file entries and finally CLI flags provide defaults.
| Variable | Purpose | Default |
|---|---|---|
INTRATUN_ENV |
Environment name (log field) | empty |
INTRATUN_SERVICE_NAME |
Service name for logs/tracing | intratun |
INTRATUN_LOG_LEVEL |
debug, info, warn, error |
info |
INTRATUN_JSON_LOGS |
true for JSON logs |
false |
INTRATUN_PID_FILE |
Path for PID file | empty |
INTRATUN_TRACE_ENABLED |
Enable OpenTelemetry tracing | false |
INTRATUN_TRACE_EXPORTER |
stdout, otlp-grpc, otlp-http |
stdout |
INTRATUN_TRACE_ENDPOINT |
Exporter endpoint (host:port or URL) |
empty |
INTRATUN_TRACE_INSECURE |
Disable TLS for OTLP exporters | false |
| Variable | Notes |
|---|---|
INTRATUN_AGENT_CONFIG |
YAML config path |
INTRATUN_AGENT_RELAY |
Relay WSS endpoint |
INTRATUN_AGENT_ID / TOKEN |
Credentials |
INTRATUN_AGENT_DIAL_TIMEOUT_MS |
TCP dial timeout (ms) |
INTRATUN_AGENT_READ_BUFFER |
Stream read buffer bytes |
INTRATUN_AGENT_WRITE_BUFFER |
WebSocket write buffer bytes |
INTRATUN_AGENT_MAX_FRAME |
Max payload per frame |
INTRATUN_AGENT_MAX_INFLIGHT |
Per-stream inflight bytes |
INTRATUN_AGENT_QUEUE_DEPTH |
Per-stream queue depth |
INTRATUN_AGENT_RECONNECT_MIN/MAX |
Durations (e.g. 2s, 30s) |
| Variable | Notes |
|---|---|
INTRATUN_RELAY_CONFIG |
YAML config path |
INTRATUN_RELAY_PROXY_LISTEN |
HTTP CONNECT listener |
INTRATUN_RELAY_SECURE_LISTEN |
HTTPS listener |
INTRATUN_RELAY_SOCKS_LISTEN |
SOCKS5 listener |
INTRATUN_RELAY_AGENT_CONFIG |
Agents definition YAML |
INTRATUN_RELAY_ACL_ALLOW |
Comma/space separated regex list |
INTRATUN_RELAY_MAX_FRAME |
Max frame payload size |
INTRATUN_RELAY_MAX_INFLIGHT |
Client backlog limit bytes |
INTRATUN_RELAY_STREAM_QUEUE_DEPTH |
Client write queue depth |
INTRATUN_RELAY_WS_IDLE |
WebSocket idle timeout (45s) |
INTRATUN_RELAY_DIAL_TIMEOUT_MS |
Dial acknowledgement timeout |
INTRATUN_RELAY_ACME_HOSTS / EMAIL |
ACME config |
INTRATUN_RELAY_ACME_CACHE / ACME_HTTP |
ACME cache/HTTP-01 endpoint |
INTRATUN_RELAY_STREAM_ID_MODE |
uuid (default) or cuid |
Sample YAML snippets live in config/.
| Flag | Description |
|---|---|
--proxy-listen |
HTTP CONNECT listener (:8080 default) |
--secure-listen |
HTTPS listener for /tunnel, dashboard, metrics |
--socks-listen |
Optional SOCKS5 listener |
--agents |
Allowed credentials agentId:token (repeatable) |
--acl-allow |
Regex ACL for host:port destinations |
--acme-host / --acme-email / --acme-cache / --acme-http |
Let’s Encrypt settings |
--max-frame |
Max payload chunk per frame |
--ws-idle |
WebSocket idle timeout |
--dial-timeout-ms |
Agent dial acknowledgement timeout |
| Flag | Description |
|---|---|
--relay |
WSS endpoint of the relay |
--id / --token |
Agent credential pair |
--dial-timeout-ms |
TCP dial timeout |
--max-frame |
Max chunk size to relay |
--read-buf / --write-buf |
Socket and WebSocket buffer sizes |
--max-inflight |
Per-stream backpressure limit |
- Structured logs now include
service,component,trace_id, andspan_id. UseINTRATUN_LOG_LEVEL/INTRATUN_JSON_LOGSfor formatting. - Enable tracing with
INTRATUN_TRACE_ENABLED=true(default exporter prints OTLP spans to stdout). PointINTRATUN_TRACE_EXPORTERto future OTLP exporters when available. - Prometheus metrics remain exposed on the relay at
/metrics.
- Visit
https://relay.example.com/for:- Live summary counters.
- CPU/RSS graphs covering the last seven days (sampled every minute).
- Connected agents, active streams, PAC download links.
https://relay.example.com/status.jsonprovides the raw data; think of it as the REST-ish back-end for the dashboard.https://relay.example.com/metricsexposes Prometheus metrics (intratun_bytes_*,intratun_agents_connected, etc.).
For browser setup:
- HTTP CONNECT: configure proxy
relay.example.com:8080with Basic auth (agentId:token). - SOCKS5: leverage the PAC link (e.g.,
https://relay.example.com/autoconfig/myagent.pac?token=...) and enable “Remote DNS” in the browser so intranet hostnames resolve via the agent.
- Access Control: Use multiple
--agentsentries and tighten ACLs for each relay instance. Future work may scope ACLs per agent. - Scaling Agents: The WebSocket protocol multiplexes streams with
streamId, so a single agent connection can handle many simultaneous tunnels. - High Availability: Run multiple relays behind a load balancer; agents reconnect on failure, and browsers will retry the proxy automatically.
- Extensibility: The JSON frame protocol was designed with room for new message types (e.g., SOCKS5 UDP associate, mTLS, credit systems, audit trails).
- SOCKS5 UDP support.
- mTLS between relay and agents.
- Dynamic per-agent ACLs & rate limiting.
- Pluggable auth providers (OIDC, API tokens).
- Persistent stream/connection analytics store.
Got ideas? Issues and PRs are welcome!
- Repo: github.com/drksbr/ProxyWebSock
- Issues: please open tickets on GitHub with logs and environment details.
Created by Isaac Diniz.
Made with love ❤️

