Self-hosted WebSocket blind relay server. Originally built for the SemaBuzz Protocol, it is completely protocol-agnostic and can be used by any application requiring encrypted peer-to-peer pairing.
The relay is a blind pass-through — it pairs two peers by a shared room token and forwards raw binary frames between them. Message content is never read, parsed, logged, or stored by the relay. IP addresses are held in memory only for the duration of an active session.
- .NET 9 SDK (to build from source), or use a pre-built binary from Releases
- A reverse proxy (like Nginx or Caddy) for HTTPS/WSS in production — the relay itself serves plain HTTP/WS.
# Run with defaults (port 7171, max 500 rooms)
dotnet run
# Run on a custom port
dotnet run -- --port 8080
# Limit concurrent rooms (recommended for home servers)
dotnet run -- --max-rooms 20
# Combined
dotnet run -- --port 8080 --max-rooms 20
# Pre-built binary
SemaBuzz-Relay-Windows.exe --port 8080 --max-rooms 20
./SemaBuzz-Relay-Linux --port 8080 --max-rooms 20When launched, the relay prints a summary showing all available connection URIs:
Version 1.2.2
Port 7171
Relay URI ws://localhost:7171/relay
Ethernet ws://192.168.1.x:7171/relay
WiFi ws://192.168.0.y:7171/relay
Public IP ws://203.0.113.z:7171/relay ← share this (requires port forwarding)
Health http://localhost:7171/
Max rooms 20 (global)
Only Ethernet and WiFi adapters are shown (virtual adapters and tunnels are excluded). The public IP is discovered automatically via STUN — no external API key needed.
Self-hosting behind a home router? Port forwarding on port 7171 (TCP) is required before the Public IP URI will work for external peers — similar to Plex or game servers. Client machines connecting to a relay do not need any special firewall configuration.
| Endpoint | Protocol | Description |
|---|---|---|
/relay |
WebSocket | Primary peer-pairing endpoint. Two clients connecting with the same 6-character room token are bridged together and raw binary frames are forwarded blindly between them. |
/file |
HTTP POST | Stage a binary blob (up to 10 MB) for out-of-band transfer. Returns a short-lived token the receiving peer can use to fetch the file. Staged files expire after 10 minutes. |
/file/{token} |
HTTP GET | Retrieve a previously staged file by its token. Once fetched or expired, the file is removed from memory. |
/ |
HTTP GET | Health check. Returns HTTP 200 OK. Useful for uptime monitors and PaaS health probes. |
Note: All HTTP endpoints (
/file,/) support CORS from any origin, so browser-based apps can interact with them directly.
To pair two peers via /relay, each client must open a WebSocket connection and send a 10-byte control packet as its very first message:
| Bytes | Field | Value |
|---|---|---|
| 0–1 | Magic | 0x52 0x4C (ASCII RL) |
| 2 | Version | 0x01 |
| 3 | Type | See table below |
| 4–9 | Token | 6-byte uppercase ASCII room token |
Packet types:
| Value | Name | Direction | Description |
|---|---|---|---|
0x01 |
JoinHost |
Client → Relay | First peer registers the room with a token |
0x02 |
JoinDial |
Client → Relay | Second peer joins the same room by token |
0x03 |
Paired |
Relay → Client | Both peers connected — begin streaming |
0x04 |
RelayError |
Relay → Client | Token not found, room full, or capacity exceeded |
0x05 |
Ping |
Bidirectional | Keep-alive to maintain NAT mappings |
Typical pairing flow:
- Peer A opens
ws://host/relayand sendsJoinHostwith tokenABC123 - Peer B opens
ws://host/relayand sendsJoinDialwith tokenABC123 - The relay sends
Pairedto both clients - Both peers may now stream any binary payload — the relay forwards it blindly
After pairing, no relay framing is applied to data frames — whatever bytes you send are forwarded as-is to the other peer.
All options can be set via CLI argument or environment variable. CLI takes precedence.
| CLI Argument | Env Variable | Description | Default |
|---|---|---|---|
--port / -p |
PORT |
Listening port | 7171 |
--max-rooms |
MAX_ROOMS |
Maximum concurrent relay rooms | 500 |
| (none) | TRUST_PROXY |
Set true to honour X-Forwarded-For behind a reverse proxy |
false |
Recommended values by deployment type:
| Deployment | --max-rooms |
|---|---|
| Home server | 10 – 50 |
| Small VPS (1 vCPU / 1 GB) | 100 – 200 |
| Mid-tier VPS (2 vCPU / 4 GB) | 500 (default) |
The relay works out of the box on Railway, Render, Fly.io, and similar platforms. TLS is terminated by the platform � no extra configuration needed!
- Fork this repo
- Connect it to your Railway / Render / Fly.io project
- Set
TRUST_PROXY=trueif the platform injectsX-Forwarded-For - Optionally set
MAX_ROOMSfor your expected load - Point your application at your relay by updating your WebSocket connection URIs.
You can easily compile the relay into a single, self-contained executable for any platform — meaning the host machine won't even need .NET installed to run it!
# Build for Windows (x64)
dotnet publish -c Release -r win-x64 --self-contained -p:PublishSingleFile=true -o publish/win
# Build for Linux (x64)
dotnet publish -c Release -r linux-x64 --self-contained -p:PublishSingleFile=true -o publish/linux
# Build for macOS (Apple Silicon)
dotnet publish -c Release -r osx-arm64 --self-contained -p:PublishSingleFile=true -o publish/mac
# Build for macOS (Intel)
dotnet publish -c Release -r osx-x64 --self-contained -p:PublishSingleFile=true -o publish/mac-intelYou can easily run the relay using Docker. The following multi-stage Dockerfile is platform-agnostic and will build and run the relay universally (including on Windows hosts via Docker Desktop).
# Stage 1: Build
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
WORKDIR /src
COPY . .
RUN dotnet publish SemaBuzz.Relay.csproj -c Release -r linux-x64 --self-contained false -o /app/publish
# Stage 2: Run
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS runtime
WORKDIR /app
COPY --from=build /app/publish .
EXPOSE 8080
ENTRYPOINT ["dotnet", "SemaBuzz.Relay.dll"]Build and run the container:
docker build -t semabuzz-relay .
docker run -p 7171:7171 -e MAX_ROOMS=50 semabuzz-relay| Limit | Value | Configurable |
|---|---|---|
| Global room cap | 500 rooms (default) | ✅ --max-rooms / MAX_ROOMS |
| Rooms per IP | 2 hosted rooms per IP | No |
| Connections per IP | 5 concurrent | No |
| Bandwidth cap | 2 MB/s per session | No |
| Room TTL (idle) | 10 minutes | No |
The relay is a single self-contained binary — no runtime, no database, no config files. Pick the option that fits you best.
Fly.io's free tier includes 3 shared VMs that stay always-on — no spin-down, Docker-native.
- Install flyctl and run
fly auth login - Clone this repo and
cdinto it - Run
fly launch— accept defaults, choose a region close to your users - Run
fly deploy - Your relay is live at
wss://your-app-name.fly.dev/relay
Set PORT via fly secrets set PORT=7171 if needed (Fly maps internal ports automatically).
Railway deploys directly from GitHub with no config needed. Free trial credit included — no credit card required to start.
- Sign up and create a new project → Deploy from GitHub repo
- Point it at your fork of
skynrlabs/SemaBuzz-Relay - Railway auto-detects the Dockerfile and builds it
- Set the
PORTenvironment variable to match Railway's assigned port (Railway injects$PORTautomatically) - Your relay is live at the generated Railway domain — use
wss://your-app.railway.app/relay
Best for always-on local use. Requires port forwarding on your router.
- Download the latest binary for your platform
- Run
./SemaBuzz.Relay— it will print your public IP as the relay URI - Forward TCP port 7171 on your router to the machine's local IP
- Share
ws://your-public-ip:7171/relaywith your peer
For production use, put Caddy or Nginx in front for WSS (TLS):
# Caddyfile
relay.yourdomain.com {
reverse_proxy localhost:7171
}
# Download and extract
wget https://github.com/skynrlabs/SemaBuzz-Relay/releases/latest/download/SemaBuzz.Relay-linux-x64.tar.gz
tar -xzf SemaBuzz.Relay-linux-x64.tar.gz
chmod +x SemaBuzz.Relay
# Open the port
sudo ufw allow 7171/tcp
# Run (or set up as a systemd service)
./SemaBuzz.Relaysystemd service (/etc/systemd/system/semabuzz-relay.service):
[Unit]
Description=SemaBuzz Relay
After=network.target
[Service]
ExecStart=/opt/semabuzz-relay/SemaBuzz.Relay
Restart=always
Environment=PORT=7171
[Install]
WantedBy=multi-user.targetsudo systemctl enable --now semabuzz-relayJoin the SemaBuzz Discord to ask questions, share what you're building, and follow development.
Contributions are welcome! Please read CONTRIBUTING.md for the branch model, coding standards, and PR process.
SemaBuzz Relay is open-source software licensed under the MIT License. See the LICENSE file for full details.