youros

How it worksUnderneath

Network

A pipe is a channel to another node; a session is the sealed conversation that runs over whichever pipes there are.

Pipes, tried in order

A pipe is reliable or datagram: a WebSocket to a relay (the one that is required), WebTransport, a WebRTC data channel, QUIC. It brings its own reliability and congestion control (TCP, SCTP, QUIC); the core never runs a congestion controller and never retransmits on a working pipe. Pipes are tried in order: the relay at once, then WebTransport straight to the node if it advertised a reachable address, then WebRTC, whose ICE gets through the NATs WebTransport can't (D-9).

A direct pipe today

Today the second pipe is WebRTC, browser to machine. Once a session is open over the relay, the browser offers a data channel inside it: the offer, the answer and every candidate are messages of the session, so the relay forwards them sealed and the DTLS fingerprint in them can't be swapped. The machine answers with str0m (WebRTC as a library that does no I/O of its own, like the core), a UDP socket per address it has, and the address the public STUN servers say it has. Nothing relays the data, so there is no TURN. When the channel opens, both ends say moved on it and acknowledge, as after any pipe change, and every frame of the session goes over it unchanged: the channel is ordered and reliable, and the session's own numbering still holds. Datagrams ride it too until a stream needs them lossy. If it closes or ICE gives up, both ends say moved on the relay again and the browser tries direct later, waiting longer each time. The page shows which pipe a machine is on.

Sessions outlive pipes

A session is a hybrid handshake to a key from the log, with the caller's chain verified once, then keys and a session id that belong to no pipe. Every message is numbered and sealed on its own, {to, session, n, ciphertext}, so it can arrive over any pipe, and a network flap is a new pipe, never a handshake (D-7, D-8).

Noticing a dead pipe

A WebSocket whose network has gone often stays open for minutes: nothing closes it, and frames sent into it vanish. So the browser and the node each send the relay ping every 20 seconds, and at once when the browser comes back online, becomes visible or regains focus after a quiet spell; anything arriving counts as an answer. Five seconds without one closes the socket and dials again straight away, and the sessions resend what is unacknowledged, as after any pipe change. A call still unanswered after a minute fails with a reason instead of waiting for ever, and the page says it is reconnecting while it is.

A machine that restarted knows none of the sessions it had. A session whose machine acknowledges nothing for fifteen seconds while a pipe is up, whether a call, a word said in a room or a log entry went unanswered, is given up and a new one made. Every room the page had joined is joined again on the new one: on losing it a page stops being the room's writer at once, so two never write, and on joining again the machine names the writer, the earliest back. Edits made meanwhile wait in the page and reach the room by the editor's version exchange, which the writer then saves.

Lost packets

Reliable messages are acknowledged up to the highest number held in order; the sender keeps what is unacknowledged and resends it only after a pipe change. Datagrams (a screen, audio, input) are never resent. Each stream picks a channel with the properties it needs, so one never waits behind another.

Where it breaks

The relay is the one place availability depends on, and it sees who, when and how much; you can run your own (Relay). The WebSocket floor is one stream, so large transfers go in chunks there. A browser can't hole-punch for WebTransport. Some networks allow only 443 over TCP. What is unacknowledged at a flap is held until resent, so a long outage needs a bound on that buffer. Expiry needs a clock.