An encrypted SOCKS5/HTTP proxy tunnel over HTTP/SSE.
tunnix routes your SOCKS5 and HTTP(S) proxy traffic through a plain HTTP connection, end-to-end encrypted with ChaCha20-Poly1305. It is designed for environments that serve HTTP but block direct TCP — Cloud Shell, Codespaces, Gitpod, or any host behind a reverse proxy.
- End-to-end encryption — ChaCha20-Poly1305, Argon2id key derivation
- HTTP/SSE transport — no WebSocket required; works wherever plain HTTP works
- Dual-protocol listener — SOCKS5 and HTTP proxy on the same port, auto-detected
- Connection multiplexing — many connections share one SSE stream
- Custom header injection — for cookie-authenticated reverse proxies
- Path prefix support — serve under a sub-path (
/foo/bar/stream/...) to coexist with other apps on the same host - Single binary —
tunnix server/tunnix client
Homebrew (macOS / Linux):
brew tap aeroxy/tunnix https://github.com/aeroxy/tunnix
brew install tunnixCargo:
cargo install tunnixOr download a pre-built binary from the releases page.
# Server
tunnix server --listen 0.0.0.0:8080 --password "your-secret"
# Client
tunnix client \
--server https://your-host.example.com \
--password "your-secret" \
--local-addr 127.0.0.1:7890
# Test
curl -x http://127.0.0.1:7890 https://ifconfig.me
curl --socks5 127.0.0.1:7890 https://ifconfig.meCloud Shell's Web Preview issues a temporary HTTPS URL for your HTTP server. tunnix runs inside Cloud Shell and the client connects using the preview URL with Cloud Shell's authorization cookies.
Server (inside Cloud Shell terminal):
tunnix server --listen 0.0.0.0:8080 --password "your-secret"Open Web Preview on port 8080 to get the preview URL.
Get cookies: Browser DevTools → Network tab → any request to *.cloudshell.dev → copy the Cookie header.
Client (local machine):
tunnix client \
--server "https://8080-cs-XXXX.cs-region.cloudshell.dev" \
--password "your-secret" \
--cookie "CloudShellAuthorization=Bearer ...; CloudShellPartitionedAuthorization=Bearer ..."Codespaces exposes forwarded ports via a GitHub-authenticated HTTPS URL.
Server (inside Codespace terminal):
tunnix server --listen 0.0.0.0:8080 --password "your-secret"In the Ports panel, set port 8080 visibility to Public (or pass a GitHub token).
Client:
tunnix client \
--server "https://your-codespace-name-8080.app.github.dev" \
--password "your-secret"Same pattern as Codespaces. Make the port public in the Gitpod ports UI.
Client:
tunnix client \
--server "https://8080-your-workspace.ws-eu.gitpod.io" \
--password "your-secret"When tunnix shares a host with other services, use path_prefix to scope all its routes under a sub-path. nginx handles TLS; tunnix binds to a local port.
config.toml (server):
[server]
listen = "127.0.0.1:9000"
password = "your-secret"
path_prefix = "/tunnix"nginx snippet:
location /tunnix/ {
proxy_pass http://127.0.0.1:9000;
proxy_http_version 1.1;
proxy_buffering off;
proxy_cache off;
proxy_set_header Connection "";
proxy_set_header X-Accel-Buffering "no";
proxy_read_timeout 3600s;
}Client:
tunnix client --server "https://your-domain.com/tunnix" --password "your-secret"The bare
/healthendpoint always responds regardless of prefix, so load-balancer probes continue to work.
These platforms run long-lived processes and assign a public HTTPS URL. They set a $PORT environment variable.
Dockerfile (minimal):
FROM debian:bookworm-slim
COPY tunnix /usr/local/bin/tunnix
CMD tunnix server --listen "0.0.0.0:$PORT" --password "$TUNNIX_PASSWORD"Set TUNNIX_PASSWORD as an environment secret in the platform dashboard.
Client:
tunnix client \
--server "https://your-app.railway.app" \
--password "your-secret"Vercel is not recommended. Serverless functions have short execution timeouts (10–60 s depending on plan) that are incompatible with long-lived SSE streams. Use a container-based platform instead.
Copy config.example.toml to config.toml and customize:
[server]
listen = "0.0.0.0:8080"
password = "your-secret"
# path_prefix = "/tunnix" # optional; leave empty for root
[client]
server_url = "https://your-host.example.com"
password = "your-secret"
local_addr = "127.0.0.1:7890"
[client.headers]
# Cookie = "..." # only needed for cookie-authenticated hosts
[logging]
level = "info"
# file = "./tunnix.log"Run with a config file:
tunnix server --config config.toml
tunnix client --config config.tomlCLI flags always override config file values. The password can also be supplied via the TUNNIX_PASSWORD environment variable.
Changes to config.toml are picked up automatically every few seconds — no restart needed. Hot-reloadable fields: password, headers, path_prefix, root_redirect, root_html, health_response. Fields that require a restart: listen, local_addr, server_url, logging.level. CLI overrides are never clobbered by file changes.
cargo build --release
# Binary: target/release/tunnixCross-compile for Linux (requires cargo-zigbuild):
cargo zigbuild --release --target x86_64-unknown-linux-gnuOr use make:
make release # native
make release-linux # Linux x86_64
make release-all # bothLocal SOCKS5/HTTP client
│
▼
tunnix client
├── proxy.rs — TCP listener; detects protocol (0x05=SOCKS5, letter=HTTP)
├── socks5.rs — SOCKS5 handshake (RFC 1928, CONNECT only)
├── http_proxy.rs — HTTP CONNECT + plain HTTP forwarding
├── relay.rs — bidirectional relay; connection ID counter
└── tunnel.rs — HTTP/SSE tunnel to server
│
│ POST /[prefix]/send/{session} encrypted binary body
│ GET /[prefix]/stream/{session} SSE text/event-stream
▼
tunnix server
└── server.rs — hyper HTTP/1.1 server; session routing; prefix stripping
│
│ raw TCP
▼
Target (e.g. api.example.com:443)
The client auto-detects the incoming protocol by peeking the first byte:
0x05→ SOCKS5- ASCII letter → HTTP proxy (
CONNECTfor HTTPS, method for plain HTTP)
- Argon2id key derivation from the shared password
- ChaCha20-Poly1305 AEAD, per-message random nonce
- No plaintext payload logging
- Use a strong, randomly generated password — it is the only credential
proxies:
- name: tunnix
type: socks5 # or type: http
server: 127.0.0.1
port: 7890MIT