urn:oximail:params:jmap:meet. La répartition des rôles est la première chose à comprendre : OxiMail fait autorité sur qui peut rejoindre et qui est présent dans un appel, et il relaie la poignée de main WebRTC entre les participants. Le média lui-même ne touche jamais le serveur.
L’objet de capacité
Lisez la capacité au lieu d’en coder quoi que ce soit en dur : c’est là que les limites livrées sont annoncées.stunServers: [] et turnServers: null sont honnêtes plutôt que vides par omission : le serveur ne fournit aucun serveur ICE. Apportez les vôtres, et attendez-vous à ce que le pair-à-pair échoue entre deux NAT symétriques sans relais TURN.
Objets et méthodes
Une
MeetSession porte id, accountId, chatRoomId, un calendarEventId optionnel, mode, config, participants, created, ainsi que started et ended optionnels. La liste participants est posée par le serveur : elle est chargée depuis le magasin et jamais prise d’un client. La date started est frappée au moment où le deuxième participant rejoint, c’est-à-dire quand une session devient un appel et non plus une invitation.
Un participant est plat sur le fil (accountId, deviceId, displayName), avec un role valant moderator ou participant, et un status que la phase 1 ne met jamais ailleurs qu’à active ou left.
Une session est ancrée sur un salon
chatRoomId est obligatoire, et c’est sur lui que porte l’autorisation : vous pouvez rejoindre une session si vous êtes membre de son salon. Il n’y a aucune liste d’invités séparée à tenir synchronisée avec le salon.
La création diffuse un StateChanged de la collection MeetSession à tous les membres du salon : c’est ainsi qu’un appelé apprend qu’un appel existe, sans type de notification dédié. Cette trame porte un état, pas un identifiant. La seconde moitié de la boucle est donc MeetSession/get avec ids: null (RFC 8620 §5.1 : tous les enregistrements que l’appelant peut voir), qui répond avec les sessions actives de ses salons. Ces deux appels forment la boucle de découverte d’appel entrant ; un client n’a besoin de rien d’autre pour sonner.
L’adhésion est un bail, pas un drapeau
Rejoindre prend un bail indexé par(compte, appareil). Il expire 30 secondes après le dernier join ou refresh, et MeetSession/refresh répond cette fenêtre en expiresIn pour qu’un client sonde confortablement à l’intérieur au lieu de la deviner. Envoyer un signal vaut également preuve de vie et rafraîchit le bail de l’émetteur.
Quand un bail expire, un balayeur bascule ce participant en left dans le magasin et diffuse le changement ; quand plus personne n’est actif, la session se termine d’elle-même. C’est pour cela qu’un onglet de navigateur qui disparaît ne laisse pas de fantôme dans l’appel.
Les baux vivent en mémoire et ne sont jamais persistés. Un redémarrage du serveur les perd donc tous, et la présence se re-dérive des join/refresh frais qui la rétablissent : la même sémantique que la présence du chat, où tout le monde repart hors ligne après un redémarrage.
La signalisation est relayée, jamais stockée
Les quatre méthodesMeetSignal/* prennent sessionId, toAccountId, toDeviceId et fromDeviceId, plus la charge utile du signal (sdp, candidate, reason), transmise telle quelle et jamais lue.
- Rien n’est persisté, et rien n’est journalisé. Il n’y a ni boîte de signalisation, ni file, ni réémission : le relais est transitoire par contrat. Un refus journalise la session et le type de signal, jamais le contenu.
deliveredest un accusé sur des connexions, pas sur des personnes.trueveut dire qu’au moins une WebSocket ouverte du destinataire a reçu la trame.falseveut dire qu’il n’a aucune connexion ouverte à cet instant : ce n’est pas une erreur, et ce n’est pas mis en attente. Traitez-le dans votre interface (« semble hors ligne »), n’y voyez pas un échec à réessayer.- Le serveur possède les champs d’identité de la trame.
type,signaletfromAccountIdsont apposés après l’étalement de la charge utile de l’appelant, si bien qu’un appelant ne peut pas les écraser en plaçant ces clés dans sa propre charge. - Les deux extrémités doivent être des participants actifs. L’émetteur est contrôlé par
(accountId, fromDeviceId)et le destinataire partoAccountId/toDeviceId; tout le reste estforbidden, et un échec de résolution des participants relaie à personne plutôt qu’à tout le monde. - Débit limité par
(tenant, compte, session)à 120 signaux par minute par défaut, une taille pensée pour le trickle ICE, environ deux par seconde. Au-delà du budget, l’erreur de limite de la RFC 8620 §3.6.1 répond.
Ce que la phase 1 ne fait pas
Les restrictions ci-dessous sont un périmètre délibéré, pas des défauts, et chacune s’observe depuis l’objet de capacité ou par une erreur nommée plutôt que par essais :- Pair-à-pair uniquement.
modeest figé à"p2p",sfuAvailablevautfalse, et rejoindre au-delà de trois participants actifs répondtooManyEntries. Il n’existe aucun mode groupe en repli. - Ni enregistrement, ni salle d’attente.
recordingAllowedetwaitingRoomexistent dans la forme deMeetConfigmais sont refusés eninvalidProperties. - Pas d’invités. L’identité d’un participant est membre ou invité au niveau du schéma, et aucun chemin de phase 1 ne crée d’invité.
- Aucune méthode
MeetParticipant/*: ni admission, ni refus, ni exclusion, ni coupure de micro. Les participants se lisent à travers la session. - Un seul modérateur, et c’est le créateur. La modération ne se transfère pas.