ProtocolProtocole
The wire format between a client and the host: the port, the JSON control messages, the binary frames, the encrypted UDP video, byte by byte.Le format réseau entre un client et l'hôte : le port, les messages de contrôle JSON, les trames binaires, la vidéo UDP chiffrée, octet par octet.
Read from include/xdesk/net/Protocol.hpp, Datagram.hpp and the writers of the encoder. Protocol version 1 (XDESK_PROTOCOL_VERSION, bumped on every incompatible change).
Lu depuis include/xdesk/net/Protocol.hpp, Datagram.hpp et les écritures de l'encodeur. Version du protocole 1 (XDESK_PROTOCOL_VERSION, augmentée à chaque changement incompatible).
1. FramingTrames
One TLS 1.3 port (47800 by default) serves HTTPS (/, /api/info, /app/xdesk.apk, /app/version) and the websocket /ws (RFC 6455); the extra video sockets of the web viewers open /lane.
- a text frame is one JSON object
{"t": "<type>", ...}(control), 64 KiB at most; - a binary frame is
[u8 type][payload], every integer big endian, 32 MiB at most (a full 4K frame); - the LAN discovery is a UDP broadcast on port
47801.
Un seul port TLS 1.3 (47800 par défaut) sert le HTTPS (/, /api/info, /app/xdesk.apk, /app/version) et la websocket /ws (RFC 6455) ; les sockets vidéo supplémentaires des viewers web ouvrent /lane.
- une trame texte est un objet JSON
{"t": "<type>", ...}(contrôle), 64 Kio au plus ; - une trame binaire est
[u8 type][payload], tous les entiers en big endian, 32 Mio au plus (une image 4K complète) ; - la découverte sur le LAN est un broadcast UDP sur le port
47801.
2. HandshakeHandshake
- client → host
TLS 1.3, groupX25519MLKEM768first (native clients: post-quantum groups only), certificate checked against the pinned fingerprint. - client → host
hello:v kind name os device nonce udp. - host → client
challenge:host version nonce salt kdf known. - client → host
auth:method = code(web) orproof(native,HMAC(Argon2id(code, salt), transcript)),device,remember. - host → client
pendingwhile the person at the PC decides (unknown device), thenwelcome(session perms screen capture device proof) ordenied(reason).
- client → hôte
TLS 1.3, groupeX25519MLKEM768en premier (clients natifs : groupes post-quantiques seulement), certificat vérifié avec l'empreinte épinglée. - client → hôte
hello:v kind name os device nonce udp. - hôte → client
challenge:host version nonce salt kdf known. - client → hôte
auth:method = code(web) ouproof(natif,HMAC(Argon2id(code, sel), transcript)),device,remember. - hôte → client
pendingpendant que la personne devant le PC décide (appareil inconnu), puiswelcome(session perms screen capture device proof) oudenied(reason).
3. JSON messagesMessages JSON
| t | direction | fields |
|---|---|---|
hello | C → S | v kind name os device nonce udp (udp: the Android app has its own UDP socket) |
challenge | S → C | host version nonce salt kdf known |
auth | C → S | method = code (web) or proof, device, remember |
pending / welcome / denied | S → C | session perms screen capture device proof / reason |
ping / pong | ↔ | ts |
welcome.udp / udp_off | S → C / C → S | port connection down up (keys of the UDP video) / the viewer could not reach it |
welcome.lanes / lanes | S → C | token max (token of the extra sockets) / n sockets to keep for the video |
lane | C → S (first message of /lane) | session token index |
quality / mode / refresh | C → S | quality scale fps / view or control |
term_open / term_resize / term_close / term_exit | ↔ | id cols rows code |
perms / screen / info / bye / clip | S → C (clip ↔) | perms, w h, text, reason, clipboard text |
focus_request | C → S | control paused by the host panel: given back after 1 min unless refused |
input | S → C | state (loading, ready, paused, none), error, wait, asked |
| t | sens | champs |
|---|---|---|
hello | C → S | v kind name os device nonce udp (udp : l'appli Android a sa propre socket UDP) |
challenge | S → C | host version nonce salt kdf known |
auth | C → S | method = code (web) ou proof, device, remember |
pending / welcome / denied | S → C | session perms screen capture device proof / reason |
ping / pong | ↔ | ts |
welcome.udp / udp_off | S → C / C → S | port connection down up (clés de la vidéo UDP) / le viewer n'a pas pu l'atteindre |
welcome.lanes / lanes | S → C | token max (jeton des sockets supplémentaires) / n sockets à garder pour la vidéo |
lane | C → S (premier message de /lane) | session token index |
quality / mode / refresh | C → S | quality scale fps / view ou control |
term_open / term_resize / term_close / term_exit | ↔ | id cols rows code |
perms / screen / info / bye / clip | S → C (clip ↔) | perms, w h, text, reason, texte du presse-papiers |
focus_request | C → S | contrôle mis en pause par le panneau de l'hôte : rendu après 1 min sauf refus |
input | S → C | state (loading, ready, paused, none), error, wait, asked |
4. Binary messagesMessages binaires
5. Tiles of a FrameTuiles d'une Frame
Each tile of a Frame starts with u16 x, u16 y, u16 w, u16 h, u8 enc, then:
| enc | name | payload |
|---|---|---|
1 | Jpeg | u32 length, JPEG data |
2 | Solid | u8 r, u8 g, u8 b: the whole tile of one color |
3 | Palette | u8 n (1-64), n × (u8 r, g, b), u32 length, zlib stream of w × h u8 indices (lossless; clients listing palette) |
4 | Copy | u16 sx, u16 sy: the w × h rectangle at (sx, sy) of the frame as it was before this message (scroll; clients listing copy) |
Chaque tuile d'une Frame commence par u16 x, u16 y, u16 w, u16 h, u8 enc, puis :
| enc | nom | payload |
|---|---|---|
1 | Jpeg | u32 longueur, données JPEG |
2 | Solid | u8 r, u8 g, u8 b : toute la tuile d'une seule couleur |
3 | Palette | u8 n (1-64), n × (u8 r, g, b), u32 longueur, flux zlib de w × h index u8 (sans perte ; clients annonçant palette) |
4 | Copy | u16 sx, u16 sy : le rectangle w × h en (sx, sy) de l'image telle qu'elle était avant ce message (défilement ; clients annonçant copy) |
6. Encrypted UDP videoVidéo UDP chiffrée
Native viewers and the Android app, on the port of the server. A datagram is a 13-byte header in clear but authenticated, the AES-256-GCM payload and its 16-byte tag. Two random 256-bit keys per session (one per direction) come in the welcome.udp inside TLS; nonce = direction + sequence (never reused); a window of 2048 sequences drops replays; the host only answers an address that sent an authentic datagram.
| field | type | size | description |
|---|---|---|---|
| type | u8 | 1 | 1 Hello ... 7 Pong |
| connection | u32 | 4 | connection id of the session |
| sequence | u64 | 8 | per direction, authenticated |
| payload | AES-256-GCM | var | the fields below |
| tag | bytes | 16 | GCM tag |
| type | name | direction | payload |
|---|---|---|---|
1 | Hello | V → H | empty: the viewer proves the keys from its address |
2 | HelloAck | H → V | empty |
3 | Data | H → V | u32 frame, u16 index (data pieces first, then one parity per group of 8), u16 data count, u32 frame size, bytes (1150 per piece) |
4 | Ack | V → H | u32 frame (complete) |
5 | Nack | V → H | u32 frame, u16 n, n × u16 missing indexes |
6 | Ping | V → H | u64 ms (keeps the NAT open) |
7 | Pong | H → V | u64 ms (the same) |
Back to the websocket when UDP is blocked (2.5 s of unanswered hellos) or silent (4 s); a frame given up after 3 s asks a complete one.
Viewers natifs et appli Android, sur le port du serveur. Un datagramme est un en-tête de 13 octets en clair mais authentifié, le payload AES-256-GCM et son tag de 16 octets. Deux clés aléatoires de 256 bits par session (une par sens) arrivent dans le welcome.udp à l'intérieur de TLS ; nonce = sens + séquence (jamais réutilisé) ; une fenêtre de 2048 séquences écarte les rejeux ; l'hôte ne répond qu'à une adresse qui a envoyé un datagramme authentique.
| champ | type | taille | description |
|---|---|---|---|
| type | u8 | 1 | 1 Hello ... 7 Pong |
| connection | u32 | 4 | id de connexion de la session |
| sequence | u64 | 8 | par sens, authentifiée |
| payload | AES-256-GCM | var | les champs ci-dessous |
| tag | octets | 16 | tag GCM |
| type | nom | sens | payload |
|---|---|---|---|
1 | Hello | V → H | vide : le viewer prouve les clés depuis son adresse |
2 | HelloAck | H → V | vide |
3 | Data | H → V | u32 image, u16 index (morceaux de données d'abord, puis une parité par groupe de 8), u16 nombre de données, u32 taille de l'image, octets (1150 par morceau) |
4 | Ack | V → H | u32 image (complète) |
5 | Nack | V → H | u32 image, u16 n, n × u16 index manquants |
6 | Ping | V → H | u64 ms (garde le NAT ouvert) |
7 | Pong | H → V | u64 ms (le même) |
Retour sur la websocket quand l'UDP est bloqué (2,5 s de hellos sans réponse) ou muet (4 s) ; une image abandonnée après 3 s demande une image complète.
7. CodesCodes
Mouse buttons: 0 left, 1 middle, 2 right, 3 back, 4 forward. Client kinds: 0 gui, 1 tty, 2 web, 3 apk (an unknown kind is treated as web, the most restricted).
Boutons de souris : 0 gauche, 1 milieu, 2 droit, 3 précédent, 4 suivant. Types de client : 0 gui, 1 tty, 2 web, 3 apk (un type inconnu est traité comme web, le plus restreint).