Rendezvous

WebRTC Signaling Server for P2P Communication

Overview

Rendezvous is a WebSocket-based signaling server that facilitates WebRTC peer-to-peer connections. Peers connect to named rooms, exchange public keys for identity verification, and relay ICE candidates and SDP offers/answers to establish direct connections.

WebSocket API

Connection Endpoint

WS ws(s)://{host}/room/{roomName}/websocket

Connect to a named room. The roomName can be any URL-safe string. Use wss:// for secure connections in production.

Message Flow

  1. Client connects to WebSocket endpoint
  2. Client sends register with public key
  3. Server responds with welcome containing peer ID and list of other peers
  4. Client initiates WebRTC connections to buddies using offer/answer/ice-candidate
  5. Server notifies of peer-joined and peer-left events

Client to Server Messages

register

Client → Server

Register with the signaling server using your public key. The server computes a fingerprint from the key to use as your peer ID.

{
  "type": "register",
  "publicKey": { /* JWK public key */ }
}
FieldTypeDescription
publicKeyJWKYour public key in JWK format (ECDSA, Ed25519, or RSA)

offer

Client → Server → Peer

Send an SDP offer to initiate a WebRTC connection with a specific peer.

{
  "type": "offer",
  "to": "peer-fingerprint",
  "sdp": { /* RTCSessionDescription */ }
}
FieldTypeDescription
tostringTarget peer's fingerprint (peer ID)
sdpRTCSessionDescriptionThe SDP offer

answer

Client → Server → Peer

Send an SDP answer in response to an offer.

{
  "type": "answer",
  "to": "peer-fingerprint",
  "sdp": { /* RTCSessionDescription */ }
}
FieldTypeDescription
tostringTarget peer's fingerprint
sdpRTCSessionDescriptionThe SDP answer

ice-candidate

Client → Server → Peer

Relay an ICE candidate to a peer for NAT traversal.

{
  "type": "ice-candidate",
  "to": "peer-fingerprint",
  "candidate": { /* RTCIceCandidate */ }
}
FieldTypeDescription
tostringTarget peer's fingerprint
candidateRTCIceCandidateThe ICE candidate

Server to Client Messages

welcome

Server → Client

Sent after successful registration. Contains your assigned peer ID and a list of all other peers currently in the room.

{
  "type": "welcome",
  "peerId": "your-fingerprint",
  "peers": [
    { "id": "peer-fingerprint", "publicKey": { /* JWK */ } },
    ...
  ]
}
FieldTypeDescription
peerIdstringYour peer ID (SHA-256 fingerprint of your public key)
peersarrayList of other peers in the room with their IDs and public keys

peer-joined

Server → Client

Broadcast when a new peer joins the room.

{
  "type": "peer-joined",
  "peerId": "new-peer-fingerprint",
  "publicKey": { /* JWK */ }
}

peer-left

Server → Client

Broadcast when a peer leaves the room (disconnects).

{
  "type": "peer-left",
  "peerId": "departed-peer-fingerprint"
}

offer / answer / ice-candidate

Server → Client (relayed)

Relayed messages from other peers. The to field is replaced with from indicating the sender.

{
  "type": "offer",
  "from": "sender-fingerprint",
  "sdp": { /* RTCSessionDescription */ }
}

error

Server → Client

Sent when an error occurs.

{
  "type": "error",
  "message": "Error description"
}

Peer Identity

Peer IDs are computed as the SHA-256 fingerprint of the public key's essential fields. This allows peers to verify each other's identity by comparing fingerprints with a pre-shared buddy list.

Fingerprint Computation

// Extract essential fields based on key type
if (kty === 'EC')  { essential = { kty, crv, x, y } }
if (kty === 'OKP') { essential = { kty, crv, x } }
if (kty === 'RSA') { essential = { kty, n, e } }

// Compute fingerprint
fingerprint = SHA-256(JSON.stringify(essential))