Skip to content

skynrlabs/SemaBuzz-Relay

📡 SemaBuzz Relay

License: MIT .NET 9.0 Contributions Welcome Discord

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.

🛠️ Requirements

  • .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.

⚡ Quick Start

# 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 20

🖥️ Startup Banner

When 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.

🔌 Endpoints

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.

🔗 Wire Protocol

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:

  1. Peer A opens ws://host/relay and sends JoinHost with token ABC123
  2. Peer B opens ws://host/relay and sends JoinDial with token ABC123
  3. The relay sends Paired to both clients
  4. 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.

⚙️ Environment Variables & CLI Arguments

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 1050
Small VPS (1 vCPU / 1 GB) 100200
Mid-tier VPS (2 vCPU / 4 GB) 500 (default)

☁️ Deploying to a PaaS

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!

  1. Fork this repo
  2. Connect it to your Railway / Render / Fly.io project
  3. Set TRUST_PROXY=true if the platform injects X-Forwarded-For
  4. Optionally set MAX_ROOMS for your expected load
  5. Point your application at your relay by updating your WebSocket connection URIs.

📦 Building Standalone Binaries

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-intel

🐳 Docker

You 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

🚥 Rate Limits

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

🌐 Hosting Guide

The relay is a single self-contained binary — no runtime, no database, no config files. Pick the option that fits you best.


🪂 Fly.io (free)

Fly.io's free tier includes 3 shared VMs that stay always-on — no spin-down, Docker-native.

  1. Install flyctl and run fly auth login
  2. Clone this repo and cd into it
  3. Run fly launch — accept defaults, choose a region close to your users
  4. Run fly deploy
  5. 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

Railway deploys directly from GitHub with no config needed. Free trial credit included — no credit card required to start.

Deploy on Railway →

  1. Sign up and create a new project → Deploy from GitHub repo
  2. Point it at your fork of skynrlabs/SemaBuzz-Relay
  3. Railway auto-detects the Dockerfile and builds it
  4. Set the PORT environment variable to match Railway's assigned port (Railway injects $PORT automatically)
  5. Your relay is live at the generated Railway domain — use wss://your-app.railway.app/relay

🏠 Home server / Raspberry Pi

Best for always-on local use. Requires port forwarding on your router.

  1. Download the latest binary for your platform
  2. Run ./SemaBuzz.Relay — it will print your public IP as the relay URI
  3. Forward TCP port 7171 on your router to the machine's local IP
  4. Share ws://your-public-ip:7171/relay with your peer

For production use, put Caddy or Nginx in front for WSS (TLS):

# Caddyfile
relay.yourdomain.com {
    reverse_proxy localhost:7171
}

☁️ Any Linux VPS (DigitalOcean, Hetzner, Linode, etc.)

# 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.Relay

systemd 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.target
sudo systemctl enable --now semabuzz-relay

💬 Community

Join the SemaBuzz Discord to ask questions, share what you're building, and follow development.

🤝 Contributing

Contributions are welcome! Please read CONTRIBUTING.md for the branch model, coding standards, and PR process.

⚖️ License

SemaBuzz Relay is open-source software licensed under the MIT License. See the LICENSE file for full details.

About

Self-hosted blind WebSocket relay server for .NET 9. Protocol-agnostic peer pairing via shared room tokens — no message content is ever read or stored.

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors