diff --git a/README.PROTOCOL b/README.PROTOCOL new file mode 100644 index 0000000..1f59597 --- /dev/null +++ b/README.PROTOCOL @@ -0,0 +1,294 @@ +# Galène's protocol + +Galène's uses a symmetric, asynchronous protocol. In client-server +usage, some messages are only sent in the client to server or in the +server to client direction. + +## Message syntax + +All messages are sent as JSON objects. All fields exctept `type` are +optional; however, there are some fields that are common across multiple +message types. + + - `type`, the type of the message; + - `kind`, the subtype of the message; + - `id`, the id of the object being manipulated; + - `source`, the client-id of the originating client; + - `username`, the username of the originating client; + - `dest`, the client-id of the destination client; + - `privileged`, set by the server to indicate that the orignating client + had the `op` privilege at the time it sent the message. + +## Data structures + +### Group + +A group is a set of clients. It is identified by a human-readable name +that must not start or end with a slash "`/`" and must not have the +substrings "`/../`" or "`/./`". + +### Client + +A client is a peer that may originate offers and chat messages. It is +identified by an id, an opaque string that is assumed to be unique. Peers +that do not originate messages (servers) do not need to be assigned an id. + +### Stream + +A stream is a set of related tracks. It is identified by an id, an opaque +string. Streams in Galène are uniderectional. A stream is carried by +exactly one peer connection (PC) (multiple streams in a single PC are not +allowed). The offerer is also the RTP sender (i.e. all tracks sent by the +offerer are of type `sendonly`). + +## Establishing and maintaining a connection + +The peer establishing the connection (the WebSocket client) sends +a handshake message. The server replies with another handshake message. +The client may wait for the server's handshake, or it may immediately +start pipelining messages to the server. + +```javascript +{ + type: 'handshake', + id: id +} +``` + +If the field `id` is absent, then the peer doesn't originate streams (it +is a server). + +A peer may, at any time, send a `ping` message. + +```javascript +{ + type: 'ping' +} +``` + +The receiving peer must reply with a `pong` message within 30s. + +```javascript +{ + type: 'pong' +} +``` + +## Joining and leaving + +The `join` message requests that the sender join or leave a group: + +```javascript +{ + type: 'join', + kind: 'join' or 'leave', + group: group, + username: username, + password: password +} +``` + +When the sender has effectively joined the group, the peer will send +a 'joined' message of kind 'join'; it may then send a 'joined' message of +kind 'change' at any time, in order to inform the client of a change in +its permissions or in the recommented RTC configuration. + +```javascript +{ + type: 'joined', + kind: 'join' or 'fail' or 'change' or 'leave', + group: group, + username: username, + permissions: permissions, + rtcConfiguration: RTCConfiguration +} +``` + +The `permissions` field is an array of strings that may contain the values +`present`, `op` and `record`. + +## Maintaining group membership + +Whenever a user joins or leaves a group, the server will send all other +users a `user` message: + +```javascript +{ + type: 'user', + kind: 'add' or 'delete', + id: id, + username: username +} +``` + +## Requesting streams + +A peer must explicitly request the streams that it wants to receive. + +```javascript +{ + type: 'request', + request: requested +} +``` + +The field `request` is a dictionary that maps the labels of requested +tracks to the boolean `true`. + +## Pushing streams + +A stream is created by the sender with the `offer` message: + +```javascript +{ + type: 'offer', + kind: '' or 'renegotiate' + id: id, + source: source-id, + username: username, + sdp: sdp, + labels: labels +} +``` + +If kind is the empty string, then this is a new offer that might or might +not replace an existing stream; if a stream with the same id exists, it +must be torn down before the new stream is created. If kind is +`renegotiate`, then a stream with the given id already exists, and the +receiving peer may either tear down the existing stream or merely perform +a renegotiation. + +The field `sdp` contains the raw SDP string (i.e. the `sdp` field of +a JSEP session description). Galène will interpret the `nack`, +`nack pli`, `ccm fir` and `goog-remb` RTCP feedback types, and act +accordingly. + +The field `labels` is a dictionary that maps track mids to one of `audio`, +`video` or `screenshare`. If a track does not appear in the `labels` +dictionary, it should be ignored. + +The receiver may either abort the stream immediately (see below), or send +an answer. + +```javascript +{ + type: 'answer', + id: id, + sdp: SDP +} +``` + +Both peers may then tricke ICE candidates with `ice` messages. + +```javascript +{ + type: 'ice', + candidate: candidate +} +``` + +The answerer may request a new offer of kind `renegotiate` and an ICE +restart by sending a `renegotiate` message: + +```javascript +{ + type: 'renegotiate', + id: id +} +``` + +## Closing streams + +The offerer may close a stream at any time by sending a `close` message. + +```javascript +{ + type: 'close', + id: id +} +``` + +The answerer may request that the offerer close a stream by sending an +`abort` message. + +```javascript +{ + type: 'abort', + id: id +} +``` + +The stream will not be effectively closed until the offerer sends +a matching `close`. + +## Sending messages + +A chat message may be sent using a `chat` message. + +```javascript +{ + type: 'chat', + kind: '' or 'me', + source: source-id, + username: username, + dest: dest-id, + privileged: boolean, + value: message +} +``` + +If `dest` is empty, the message is a broadcast message, destined to all of +the clients in the group. If `source` is empty, then the message was +originated by the server. The message is forwarded by the server without +interpretation, the server only validates that the `source` and `username` +fields are authentic. The field `privileged` is set to true by the server +if the message was originated by a client with the `op` permission. + +A user message is similar to a chat message, but is not conserved in the +chat history, and is not expected to contain user-visible content. + +```javascript +{ + type: 'usermessage', + kind: kind, + source: source-id, + username: username, + dest: dest-id, + privileged: boolean, + value: value +} +``` + +Currently defined kinds include `error`, `warning`, `info`, `clearchat` +(not to be confused with the `clearchat` group action), and `mute`. + +A user action requests that the server act upon a user. + +```javascript +{ + type: 'useraction', + kind: kind, + source: source-id, + username: username, + dest: dest-id, + value: value +} +``` +Currently defined kinds include `op`, `unop`, `present`, `unpresent`, and +`kick`. + +Finally, a group action requests that the server act on the current group. + +```javascript +{ + type: 'groupaction', + kind: kind, + source: source-id, + username: username, + value: value +} +``` + +Currently defined kinds include `clearchat` (not to be confused with the +`clearchat` user message), `lock`, `unlock`, `record`, `unrecord` and +`subgroups`.