Skip to main content
Meet est la surface d’appel, demandée avec 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éthodes MeetSignal/* 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.
  • delivered est un accusé sur des connexions, pas sur des personnes. true veut dire qu’au moins une WebSocket ouverte du destinataire a reçu la trame. false veut 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, signal et fromAccountId sont 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 par toAccountId/toDeviceId ; tout le reste est forbidden, 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. mode est figé à "p2p", sfuAvailable vaut false, et rejoindre au-delà de trois participants actifs répond tooManyEntries. Il n’existe aucun mode groupe en repli.
  • Ni enregistrement, ni salle d’attente. recordingAllowed et waitingRoom existent dans la forme de MeetConfig mais sont refusés en invalidProperties.
  • 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.
Ce dernier point a une conséquence qui mérite d’être dite : puisqu’une session ne peut pas survivre à son modérateur sous une forme administrable, effacer le compte du créateur supprime la session entière au lieu de laisser derrière elle une session que personne ne peut administrer. Les lignes Meet font partie de ce qu’un effacement de compte retire. La surface de discussion sur laquelle ces sessions s’ancrent est documentée sur la page chat ; les mécaniques de session et de push dont elles dépendent sont sur JMAP Core.