Linux-first Rust port-forwarder with:
- authenticated HTTP API
- SQLite persistence and startup restore
- TCP forwarding with default Tokio copy path
- UDP forwarding with default Tokio listener/session handling
- dual-protocol allocations (
both) sharing one TCP+UDP port - Prometheus listener metrics with per-scrape byte rates
HTTP_LISTEN— HTTP API listen address, e.g.:8080or127.0.0.1:8080PORT_RANGE— default10000-30000AUTH_TOKEN— required bearer tokenSQLITE_PATH— optional, defaultrelayd.sqlite3RUNTIME_APPLY_TIMEOUT_MS— optional, default2000RESTORE_SWEEP_TIMEOUT_MS— optional, default30000UDP_SOCKET_RCVBUF_BYTES/UDP_SOCKET_SNDBUF_BYTES— parsed for compatibility, default8388608
Optional compatibility feature gates are parsed only where they are implemented by the Rust runtime. TCP_SPLICE_ENABLED and FORCE_TCP_COPY_FALLBACK are intentionally ignored because this Rust runtime does not expose the old Linux splice(2) TCP path.
If HTTP_LISTEN is :PORT, relayd binds 127.0.0.1:PORT.
Release builds use cargo-zigbuild and target x86_64-unknown-linux-musl so published artifacts are musl-linked Linux binaries. Install once with:
cargo install cargo-zigbuild --locked
rustup target add x86_64-unknown-linux-muslcargo zigbuild --locked --release --bin relayd --target x86_64-unknown-linux-musl
cargo test --locked
cargo clippy --locked --lib --tests -- -D warningstarget/x86_64-unknown-linux-musl/release/relayd \
--http-listen :8080 \
--auth-token mytokenEnvironment variables remain supported and can be mixed with CLI options; CLI options take precedence. Use relayd --help to see all options and their matching environment variables.
For development:
cargo run --locked --bin relayd -- --http-listen :8080 --auth-token mytokenDocker images package a prebuilt musl binary from dist/relayd; the Dockerfile does not download or run compiler toolchains. For local image builds:
cargo zigbuild --locked --release --bin relayd --target x86_64-unknown-linux-musl
mkdir -p dist
cp target/x86_64-unknown-linux-musl/release/relayd dist/relayd
docker build -t relayd:local .
docker run --rm \
-e AUTH_TOKEN=mytoken \
-e HTTP_LISTEN=0.0.0.0:8080 \
-p 8080:8080 \
-v relayd-data:/data \
relayd:localDocker uses HTTP_LISTEN=0.0.0.0:8080 so the service is reachable through the published port. For local non-container runs, :8080 still maps to 127.0.0.1:8080.
Local e2e runs use scripts/ci/e2e_iperf3.sh to drive real TCP and UDP iperf3 traffic through relayd.
cargo zigbuild --locked --release --bin relayd --target x86_64-unknown-linux-musl
RELAYD_BIN=target/x86_64-unknown-linux-musl/release/relayd ./scripts/ci/e2e_iperf3.shBy default the harness uses AUTH_TOKEN=test-token, HTTP_LISTEN=127.0.0.1:18080, and PORT_RANGE=18100-18120. Override them for local reruns:
HTTP_LISTEN=127.0.0.1:28080 PORT_RANGE=28100-28120 RELAYD_BIN=target/x86_64-unknown-linux-musl/release/relayd ./scripts/ci/e2e_iperf3.shThe harness includes matrix modes for optional benchmark lanes, but the Rust default runtime currently targets main TCP/UDP/both forwarding parity rather than optional fast-path gates.
Canonical API documentation lives in docs/API.md.
Quick example:
curl -sS \
-H "Authorization: Bearer ${AUTH_TOKEN}" \
http://127.0.0.1:8080/v1/allocationsPrimary control-plane resources:
POST /v1/allocations— reserve a relay port without binding it yetPUT /v1/allocations/{id}/binding— attach or replace the upstream targetDELETE /v1/allocations/{id}/binding— detach the upstream target while keeping the allocationGET /v1/ports— compatibility / aggregate read modelGET /metrics— authenticated Prometheus listener metrics;bothallocations emit concretetcpandudpseries for the same port
Legacy write endpoints under /v1/ports* remain available for compatibility, but new clients should prefer the allocation/binding API.