Docs

Connections and room access

Finding a peer and connecting to it are separate steps. Here’s how to handle networks that block direct connections and decide who can join.

When peers cannot connect

First check that both peers use the same app ID, room ID, strategy, password, and compatible library version. Try two tabs on one machine, then devices on different networks. If only some network pairs fail, configure a TURN server to relay their encrypted WebRTC traffic.

const room = joinRoom(
  {
    appId: 'com.example.calls',
    turnConfig: [{
      urls: 'turn:turn.example.com:3478',
      username: turnUsername,
      credential: turnCredential
    }]
  },
  callId,
  {
    onJoinError: ({peerId, error}) => {
      showConnectionError(peerId, error)
    }
  }
)

Fetch your TURN credentials from your app’s backend or credential service. turnConfig adds TURN servers alongside Trystero’s default STUN servers. To replace the full list, set rtcConfig.iceServers instead.

Pass onJoinError in the third argument to joinRoom(). It reports password mismatches, handshake failures, and cases where peers exchange signaling but cannot establish WebRTC. It does not guarantee a callback for every relay outage or an empty room. Give users a visible waiting state as well.

Encryption and access

WebRTC encrypts messages and media between peers, including when TURN relays the traffic. Trystero also encrypts session descriptions on signaling services. By default that key comes from the app and room IDs, so anyone who knows those IDs can derive it.

A shared password adds a secret to the signaling key. All peers must know it ahead of time. This restricts joining to people with the secret; it does not identify an individual user.

const room = joinRoom(
  {appId: 'com.example.calls', password: sharedSecret},
  callId
)

For your own authentication protocol, use onPeerHandshake(peerId, send, receive, isInitiator) in the callbacks object. It runs after transport connection and before the peer becomes active. Resolve to accept or throw to deny; the default timeout is 10 seconds. See the joinRoom reference for the full contract.

Keep rooms small

A room connects peers to each other rather than routing everything through one media server. Connection counts, outgoing bandwidth, and device limits grow with room size. Test on the devices and networks you expect users to have; there is no single room-size limit that suits every app.

Trystero does not provide durable storage, offline delivery, or conflict resolution. Add those in your app when you need them. For large video meetings, evaluate a media server that forwards streams.

Run a backup peer on a server

Node, Bun, and Deno need an RTCPeerConnection implementation passed through rtcPolyfill. Passive rooms stay dormant until they discover an active peer, and do not connect to other passive peers. This can suit a backup peer that implements persistence in your app.

const backup = joinRoom(
  {
    appId: 'com.example.notes',
    passive: true,
    rtcPolyfill: RTCPeerConnectionPolyfill
  },
  documentId
)

See the server-side setup for the polyfill configuration. room.isPassive() reports whether the room was joined in passive mode.