---
title: "WebRTC Relay Server — Open Source"
description: "A single-file WebSocket relay that introduces two WebRTC peers with a one-time key, then forgets them. One dependency, zero logging, self-hostable."
url: "https://royalsoftworks.com/products/webrtc-relay-server/"
source: "https://royalsoftworks.com"
format: "markdown"
note: "Markdown rendering of the HTML page at `url`. Same content, same canonical URL."
---

[All products](https://royalsoftworks.com/#products)

Open-Source Infrastructure

Live

# WebRTC Relay Server

Introduces two WebRTC peers, then gets out of the way.

A single-file, one-dependency WebSocket relay that solves WebRTC's bootstrapping problem: two peers need to swap connection details before they can talk directly, but have no channel to do it over until they're already connected. WebRTC Relay Server mints a one-time connection key, relays the handshake, and forgets both peers the instant they pair up. It's the signaling server behind Privatta's internet P2P mode — and it's open source, so anyone can run their own instead of trusting ours.

[View source on GitLab](https://gitlab.com/ndagovic/webrtc-relay-server) [See it in Privatta](https://royalsoftworks.com/products/privatta/)

1

Dependency (ws)

32-char

Single-use connection key

5 min

Handshake ceiling

0

IPs, keys, or payloads logged

MATCH MADE LOCAL HEARTS NOT DATA OFFLINE PROFILES ZERO TELEMETRY PRIVATE BY DEFAULT NO CLOUD SWIPE E2E ENCRYPTED GDPR NATIVE MATCH MADE LOCAL HEARTS NOT DATA OFFLINE PROFILES ZERO TELEMETRY PRIVATE BY DEFAULT NO CLOUD SWIPE E2E ENCRYPTED GDPR NATIVE

Core capabilities

## What it does.

01

The bootstrapping problem

### WebRTC peers can't introduce themselves.

Two machines that want to talk directly first have to exchange SDP offers and ICE candidates — but they have no channel to trade them over until they're already connected. WebRTC Relay Server is the one thing standing between them: a WebSocket relay that carries just enough information to get a direct connection started, then has nothing left to do.

- Relays SDP/ICE payloads only — never files, never credentials
- Closes its own connection the moment the data channel opens
- No database, no persistence — the entire state is one in-memory Map

01

02

Matchmaking

### One-time keys instead of accounts.

The initiating peer sends create; the server mints an unguessable 32-character key and hands it back. That peer shares the key with exactly one other person, out of band. The second peer presents it via join — the server checks it against the live room table, and a key it never issued is refused outright. No client ever picks its own key, and no room outlives its 5-minute handshake ceiling.

- Keys are ~190 bits of CSPRNG entropy — unguessable, never client-chosen
- join never creates a room; an unknown or expired key is refused
- A third peer presenting a valid key is turned away without disturbing the pair

02

assistant

$ wscat -c wss://signal.example.com

› { "type": "create" }

▸ { "type": "created", "key": "aX7fQ2mZ..." }

› { "type": "join", "key": "aX7fQ2mZ..." }

▸ { "type": "ready", "initiator": false }

03

Zero-logging, by construction

### It can't log what it has no parameter for.

The logging function takes one fixed string and nothing else — no format argument, no object parameter, no way to pass an IP address, a connection key, or a payload through it even by accident. That's a property of the code, not a promise in a privacy policy, and the file is short enough to read in one sitting to confirm it yourself.

- One dependency (ws), which itself has zero transitive dependencies
- npm ls --all proves the entire audit surface in one command
- Open source — read it, or run your own instance instead of ours

03

example.ts TypeScript

```
1function log(event) {2  if (config.silent) return;3  // fixed string only — no IP, key,4  // or payload can pass through here.5  process.stdout.write(`[signaling] ${event}`);6}
```

More capabilities

## Everything else it does.

### One dependency, forever

Just ws, which has zero transitive dependencies of its own. The entire audit surface is one file plus one well-known library.

### Nothing lingers

A room is deleted the instant either peer disconnects, or after a 5-minute handshake ceiling if nobody ever pairs. No sweep job, no tombstones.

### Deploy your way

One file, no database. Run it via systemd + nginx, Docker, or one-click on Railway, Render, or Heroku.

### Not just for Privatta

It's a generic WebRTC rendezvous point. Any project that needs to introduce two peers and step out of the way can point at it — or fork it.

Also in the suite

[### Privatta Direct, encrypted file transfer with no server in the data path. View product](https://royalsoftworks.com/products/privatta/)

Frequently asked

## Questions we hear often.

01 Do I have to use Royal Softworks' hosted instance?

No. WebRTC Relay Server is open source — audit the code, run your own instance, and point any client (including Privatta, via Settings) at it instead of the default.

02 Is this only useful for Privatta?

No. It's a generic WebRTC signaling relay: mint a key, pair two peers, relay their handshake. Privatta is its first production user, not its only intended one — drop it in front of any WebRTC project that needs two peers to find each other.

03 What does the server actually see?

SDP offers/answers and ICE candidates while a handshake is in progress — never the file or data transfer itself, which happens directly between the two peers once the connection is up. It logs no IP addresses, keys, or payloads; the logging function is structurally incapable of carrying one.

04 What happens if nobody joins?

The room is deleted automatically 5 minutes after creation, whether or not a second peer ever showed up. Keys are single-use and are never revived or reissued.

Our hosted instance [https://signal-server-df8l.onrender.com/](https://signal-server-df8l.onrender.com/)

This is the public signaling server behind Privatta's internet P2P mode. Point any WebRTC client at it — or run your own instance from the source above and use that instead.

## Talk to the team that actually builds the software.

Pilots, licensing, demos, security questionnaires, or a question you are not sure is a question yet. All of it lands with engineers and product leads rather than a routing layer, and none of it starts a drip campaign.

Rather not talk to anyone?[Download it free](https://royalsoftworks.com/download/) [Buy it without a call](https://royalsoftworks.com/pricing/)

Half an hour, no slide deck

A walkthrough with someone who built the thing. Bring the awkward questions.

A pilot in your environment

Every feature unlocked, installed with us on the call, configured for your setup.

Or just email

sales@royalsoftworks.com, answered by a person within one business day.

[sales@royalsoftworks.com](mailto:sales@royalsoftworks.com) [office@royalsoftworks.com](mailto:office@royalsoftworks.com)

### Send us a message

Tell us what you are trying to do. A person reads it and replies within one business day.

Name Email

Company— optional Subject— optional

Message

Send message

Goes straight to our own mail server. No CRM, no tracking pixels, no marketing list.
