Les éclats

Synospis

L’objectif de ce document est de décrire comment les discussions de groupe (a.k.a. swarm chat) seront mises en œuvre à Jami.

Un swarm est un groupe capable de discuter sans aucune autorité centrale de manière résiliente. En effet, si deux personnes n’ont aucune connectivité avec le reste du groupe (c’est-à-dire une panne d’Internet) mais peuvent se contacter (dans un réseau local par exemple ou dans un sous-réseau), elles seront en mesure d’envoyer des messages les unes aux autres et ensuite, seront en mesure de synchroniser avec le reste du groupe lorsque cela est possible.

Donc, le swarm est défini par:

  1. Capacité de séparation et de fusion à la suite de la connectivité.

  2. Tout le monde doit pouvoir envoyer un message à tout le groupe.

  3. Aucune autorité centrale, aucun serveur.

  4. Les appareils doivent être capables de vérifier la validité des messages anciens et de reproduire l’historique.

  5. Le stockage est géré par l’appareil.

L’idée principale est d’obtenir un arbre de Merkle synchronisé avec les participants.

Nous avons identifié quatre modes de chat en essaim que nous voulons mettre en œuvre:

  • UN_TO_ONE, c’est le cas de notre époque quand on discute avec un ami

  • ADMIN_INVITES_ONLY généralement une classe où l’enseignant peut inviter des personnes, mais pas des étudiants

  • INVITES_ONLY un groupe privé d’amis

  • Public est essentiellement un forum ouvert

Scenarios

Créer une masse

Bob veut créer un nouvel essaim

  1. Bob crée un référentiel de données.

  2. Il crée ensuite un premier engagement signé avec les éléments suivants:

    • Sa clé publique en /admins

    • Son certificat de dispositif en ̀ /devices `

    • Son RRC est en ̀ /crls`

  3. Le hash du premier comit devient l’ID de la conversation.

  4. Bob annonce à ses autres appareils qu’il crée une nouvelle conversation, ce qui se fait par une invitation à rejoindre l’envahisseur envoyé par le DHT à d’autres appareils liés à ce compte.

Ajouter quelqu’un

Alice ajoute Bob

  1. Alice ajoute Bob au référentiel:

    • Ajout de l’URI invité en /invited

    • Ajout de la RLC à /crls

  2. Alice envoie une demande sur le DHT

Recevoir une invitation

Alice est invitée à rejoindre le swarm créé précédemment

  1. Elle accepte l’invitation (si elle refuse, ne fait rien, elle restera dans l’invité et Alice ne recevra jamais de message)

  2. Une connexion entre Alice et Bob est terminée.

  3. Alice tire le repo de Bob. ** AVERTISSEMENT Cela signifie que les messages ont besoin d’une connexion, pas du DHT comme aujourd’hui**

  4. Alice valide les engagements de Bob

  5. Pour valider que Alice est membre, elle supprime l’invitation du répertoire /invited, puis ajoute son certificat dans le répertoire /members

  6. Une fois tous les engagements validés et sur son appareil, les autres membres du groupe sont découverts par Alice. Avec ces pairs, elle construira le DRT (expliqué ci-dessous) avec Bob comme une bande de démarrage.

Envoyer un message

Alice envoie un message

Envoyer un message est assez simple. Alice écrit un message de commande dans le format suivant:

Format TODO non clair

Elle ajoute son appareil et son CRL au référentiel si elles manquent (d’autres doivent être en mesure de vérifier le commit). Les conflits de fusion sont évités parce que nous sommes principalement basés sur des messages de commit, pas sur des fichiers (sauf si des certificats CRLS + mais ils sont localisés).

Pour pinger d’autres appareils, l’expéditeur envoie aux autres membres un message SIP avec mimetype = « application/im-gitmessage-id » contenant un JSON avec le « deviceId » qui envoie le message, le « id » de la conversation liée et le « commit »

Reçoit un message

Bob reçoit le message d’Alice

  1. Bob fait un coup sur Alice

  2. Les engagements doivent être vérifiés par un crochet

  3. Si tous les comités sont valides, les comités sont stockés et affichés.

  4. Si tous les comités ne sont pas valides, pull est annulé. Alice doit rétablir son état à un état correct. Processus TODO

Validation d’un engagement

Pour éviter que les utilisateurs ne poussent certains commissions indésirables (avec des conflits, de faux messages, etc.), voici comment chaque commande (de la plus ancienne à la plus récente) DOIT être validée avant de fusionner une branche distante:

Remarque: si la validation échoue, le tirage est ignoré et nous ne fusionnons pas la branche (et supprimer les données), et l’utilisateur doit être averti Remarque2: Si un tirage est trop grand, il n’est pas fait (** TODO**)

  • Pour chaque engagement, vérifiez que le dispositif qui tente d’envoyer le engagement est autorisé à ce moment-là et que les certificats sont présents (dans les appareils du dispositif et dans les membres ou administrateurs de l’émetteur).

  • Le comité a 2 parents, donc c’est une fusion, rien de plus à valider ici

  • Le compromis a 0 parents, c’est le compromis initial:

    • Vérifiez que le certificat d’administration a été ajouté

    • Vérifiez que le certificat de l’appareil est ajouté

    • L’ajout des LCR de vérification

    • Vérifiez qu” aucun autre fichier n” est ajouté

  • Le comit a 1 parent, le message comit est un JSON avec un type:

    • Si le texte (ou autre type de mime qui ne change pas de fichiers)

      • Contrôle de la signature du certificat dans le repo

      • Vérifiez qu” aucun fichier étrange n” est ajouté en dehors du certificat de périphérique ni supprimé

    • Si vous votez

      • Vérifiez que voteType est pris en charge (interdiction, déinterdiction)

      • Vérifiez que le vote est pour l’utilisateur qui signe le compromis

      • Vérifiez que le vote est d” un administrateur et un appareil présent & non interdit

      • Vérifiez qu” aucun fichier étrange n” est ajouté ou supprimé

    • Si le membre

      • Si ajoutée

        • Vérifiez que le contrat est correctement signé

        • Vérifiez que le certificat est ajouté dans / invité

        • Vérifiez qu” aucun fichier étrange n” est ajouté ou supprimé

        • Si ONE_TO_ONE, vérifiez que nous n’avons qu’un seul administrateur, un seul membre

        • Si ADMIN_INVITES_ONLY, vérifiez que l’invitation est d’un administrateur

      • Si elle est jointe

        • Vérifiez que le contrat est correctement signé

        • Vérifiez que le dispositif est ajouté

        • Vérifiez que l’invitation est transférée aux membres

        • Vérifiez qu” aucun fichier étrange n” est ajouté ou supprimé

      • Si c’est interdit

        • Vérifiez que le vote est valide

        • Vérifiez que l’utilisateur est interdit via un administrateur

        • Vérifiez que le certificat de membre ou de dispositif est déplacé vers interdit/

        • Vérifiez que seuls les fichiers liés au vote sont supprimés

        • Vérifiez qu” aucun fichier étrange n” est ajouté ou supprimé

    • notifier à l’utilisateur qu’il peut être avec une ancienne version ou que le paire a essayé de soumettre des commissions indésirables

Interdiction d’un appareil

Alice, Bob, Carla, Denys sont dans un essaim.

C’est l’un des scénarios les plus difficiles de notre contexte.

  1. Temps de l’engagement généré

  2. Si plusieurs appareils d’administration sont présents et si Alice peut parler à Bob mais pas à Denys et Carla; Carla peut parler à Denys; Denys interdit Alice, Alice interdit Denys, quel sera l’état lorsque les 4 membres fusionneront les conversations.

  3. Un appareil peut être compromis, volé ou son certificat peut expirer. Nous devrions pouvoir interdire un appareil et éviter qu’il mente sur son expiration ou envoie des messages dans le passé (en modifiant son certificat ou le timestamp de son engagement).

Les systèmes similaires (avec des systèmes de groupe distribués) ne sont pas si nombreux, mais voici quelques exemples:

  • [mpOTR ne définit pas comment interdire quelqu’un]

  • Signal, sans serveur central pour le chat de groupe (EDIT: ils ont récemment changé ce point), ne donne pas la possibilité d’interdire quelqu’un d’un groupe.

Ce système de vote a besoin d’une action humaine pour interdire quelqu’un ou doit être basé sur les informations des RLC du référentiel (car nous ne pouvons pas faire confiance aux RLC externes)

Retirez un appareil d’une conversation

C’est la seule partie où il faut un consensus pour éviter la fracture de la conversation, comme si deux membres se tirent dessus, que verra le troisième?

Ceci est nécessaire pour détecter les appareils révoqués, ou tout simplement pour éviter de faire venir des personnes indésirables dans une salle publique.

Alice enlève Bob

Note: Alice doit être administrateur pour voter

  • Pour ce faire, elle crée le fichier dans /votes/ban/membres/uri_bob/uri_alice (les membres peuvent être remplacés par des appareils pour un appareil, ou invités à des invitations ou des administrateurs pour les administrateurs) et s’engage à

  • Elle vérifie ensuite si le vote est résolu. Cela signifie que > 50% des administrateurs sont d’accord pour interdire Bob (si elle est seule, c’est certainement plus de 50%).

  • Si le vote est résolu, les fichiers dans /votes/ban peuvent être supprimés, tous les fichiers pour Bob dans /membres, /admins, /invités, /CRLs, /appareils peuvent être supprimés (ou seulement dans /appareils si c’est un appareil qui est interdit) et le certificat de Bob peut être placé dans /banned/membres/bob_uri.crt (ou /banned/devices/uri.crt si un appareil est interdit) et engagé à la repo

  • Alice informe ensuite les autres utilisateurs (à l’exception de Bob)

*Alice (administrateur) ajoute à nouveau Bob (membre interdit)

  • Pour ce faire, elle crée le fichier dans /votes/unban/membres/uri_bob/uri_alice (les membres peuvent être remplacés par des appareils pour un appareil, ou invités à des invitations ou des administrateurs pour les administrateurs) et s’engage à

  • Elle vérifie ensuite si le vote est résolu. Cela signifie que > 50% des administrateurs sont d’accord pour interdire Bob (si elle est seule, c’est certainement plus de 50%).

  • Si le vote est résolu, les fichiers dans /votes/unban peuvent être supprimés, tous les fichiers pour Bob dans /membres, /admins, /invités, /CRLs, peuvent être ajoutés à nouveau (ou seulement dans /appareils si c’est un appareil qui est non interdit) et engagés dans le repo

Retirez une conversation

  1. Enregistrer dans convInfos removed=time::now() (comme enleverContact enregistre dans les contacts) que la conversation est supprimée et synchronisée avec les appareils d’autres utilisateurs

  2. Si on reçoit un nouveau commentaire pour cette conversation, on l’ignore.

  3. Si Jami est en activité et que le repo est toujours présent, la conversation n’est pas annoncée aux clients.

  4. Deux cas: a. Si aucun autre membre de la conversation ne nous est possible de supprimer immédiatement le référentiel b. Si d’autres membres restent, engagez-vous à quitter la conversation, et maintenant attendez que au moins un autre appareil synchronise ce message. Cela évite le fait que d’autres membres détecteront toujours l’utilisateur comme un membre valide et envoie encore de nouvelles notifications de message.

  5. Lorsque nous sommes sûrs que quelqu’un est synchronisé, supprimer effacé=time::now() et synchroniser avec les appareils d’autres utilisateurs

  6. Tous les appareils appartenant à l’utilisateur peuvent désormais effacer le référentiel et les fichiers connexes

Comment spécifier un mode

Les modes ne peuvent pas être modifiés au fil du temps. Ou c’est une autre conversation. Donc, ces données sont stockées dans le message de commande initial.

{
    "type": "initial",
    "mode": 0,
}

Pour l’instant, le « mode » accepte des valeurs 0 (ONE_TO_ONE), 1 (ADMIN_INVITES_ONLY), 2 (INVITES_ONLY), 3 (PUBLIC)

Processus pour les essaims 1:1

L’objectif ici est de conserver l’ancienne API (addContact/removeContact, sendTrustRequest/acceptTrustRequest/discardTrustRequest) pour générer un essaim avec un paire et son contact.

Le processus est toujours le même, un compte peut ajouter un contact via addContact, puis envoyer une demande de confiance via le DHT. Mais deux changements sont nécessaires:

  1. La demande de confiance intègre un « conversationId » pour informer le paire de quelle conversation cloner lors de l’acceptation de la demande

  2. TrustRequest est retesté lorsque le contact revient en ligne. Ce n’est pas le cas aujourd’hui (car nous ne voulons pas générer une nouvelle TrustRequest si le pair rejette la première).

Ensuite, lorsqu’un contact accepte la demande, une période de synchronisation est nécessaire, car le contact doit maintenant cloner la conversation.

RemoveContact() supprimera le contact et les conversations 1:1 connexes (avec le même processus que « Remove a conversation »). La seule note ici est que si nous interdisons un contact, nous n’attendons pas la synchronisation, nous supprimons simplement tous les fichiers connexes.

Des scénarios difficiles

Il y a des cas où deux conversations peuvent être créées.

  1. Alice ajoute Bob

  2. Bob accepte

  3. Alice enlève Bob

  4. Alice ajoute Bob

ou

1, Alice ajoute Bob et Bob ajoute Alice en même temps, mais les deux ne sont pas connectés ensemble

Dans ce cas, deux conversations sont générées. Nous ne voulons pas supprimer les messages des utilisateurs ou choisir une conversation ici. Donc, parfois deux essaim 1:1 entre les mêmes membres seront montrés. Cela générera quelques bugs pendant le temps de transition (comme nous ne voulons pas casser l’API, la conversation déduite sera l’une des deux conversations montrées, mais pour l’instant c’est « ok-ish », sera corrigé lorsque les clients géreront pleinement la conversationId pour toutes les API (appels, transfert de fichiers, etc.).

Note lors de la synchronisation

Après avoir accepté la demande d’une conversation, il y a un moment où le daemon doit récupérer le référentiel distant. Pendant ce temps, les clients DOIVENT afficher une vue de synchronisation pour donner des informations à l’utilisateur.

  • ConfigurationManager::getConversations() renvoie l’id de la conversation même pendant la synchronisation

  • ConfigurationManager::conversationInfos() renvoie {{« synchronisation »: « true »}} si la synchronisation est effectuée.

  • ConfigurationManager::getConversationMembres() renvoie une carte de deux URI (le compte courant et le paire qui a envoyé la demande)

Les discussions demandent des spécifications

Les demandes de conversation sont représentées par une Map<String, String> avec les touches suivantes:

  • id: le numéro de conversation

  • de: uri de l’expéditeur

  • reçu: timestamp

  • titre: nom (optionnel) de la conversation

  • Description: (facultatif)

  • Avatar: (optionnel)

Synchronisation du profil de la conversation

Pour être identifiable, une conversation a généralement besoin de certaines métadonnées, comme un titre (par exemple: Jami), une description (par exemple: quelques liens, quel est le projet, etc.), et une image (le logo du projet). Ces métadonnées sont facultatives mais partagées entre tous les membres, il faut donc les synchroniser et les intégrer dans les demandes.

Conservation dans le référentiel

Le profil de la conversation est stocké dans un fichier vCard classique à la racine (/profile.vcf) comme:

BEGIN:VCARD
VERSION:2.1
FN:TITLE
DESCRIPTION:DESC
END:VCARD

Synchronisation

Pour mettre à jour la vCard, un utilisateur avec suffisamment d’autorisations (par défaut: =ADMIN) doit modifier /profil.vcf. et engagera le fichier avec le mimetype application/update-profile. Le nouveau message est envoyé via le même mécanisme et tous les pairs recevront le signal MessageReceived du daemon. La branche est abandonnée si le commit contient d’autres fichiers ou trop gros ou si fait par un membre non autorisé (par défaut: <ADMIN).

Dernière mise en valeur

Dans les données synchronisées, chaque appareil envoie aux autres appareils l’état des conversations. Dans cet état, l’état affiché est envoyé. Cependant, comme chaque appareil peut avoir son propre état pour chaque conversation, et sans probablement le même dernier engagement à un moment donné, il existe plusieurs scénarios à prendre en compte:

5 scénarios sont pris en charge:

  • si la dernière affichage envoyé par d’autres appareils est la même que celle actuelle, il n’y a rien à faire.

  • si le dernier message n’est pas affiché pour le dispositif actuel, le message affiché à distance est utilisé.

  • si la télécommande dernière affichée n’est pas présente dans la repo, cela signifie que le comit sera récupéré plus tard, donc cache le résultat

  • si la télécommande est déjà récupérée, nous vérifions que la dernière localisation affichée est précédente dans l’histoire pour la remplacer

  • Enfin, si un message est annoncé par le même auteur, cela signifie que nous devons mettre à jour le dernier message affiché.

Préférences

Chaque conversation a des préférences fixées par l’utilisateur. Ces préférences sont synchronisées sur les appareils de l’utilisateur.

  • « color » - the color of the conversation (#RRGGBB format)

  • « ignorer les notifications » - pour ignorer les notifications de nouveaux messages dans cette conversation

  • « symbole » - pour définir un emoji par défaut.

Ces préférences sont stockées dans un paquet MapStringString, stockées dans accountDir/conversation_data/conversationId/preférences et uniquement envoyées sur les appareils du même utilisateur via SyncMsg.

L’API pour interagir avec les préférences est:

// Update preferences
void setConversationPreferences(const std::string& accountId,
                                const std::string& conversationId,
                                const std::map<std::string, std::string>& prefs);
// Retrieve preferences
std::map<std::string, std::string> getConversationPreferences(const std::string& accountId,
                                                              const std::string& conversationId);
// Emitted when preferences are updated (via setConversationPreferences or by syncing with other devices)
struct ConversationPreferencesUpdated
{
    constexpr static const char* name = "ConversationPreferencesUpdated";
    using cb_type = void(const std::string& /*accountId*/,
                            const std::string& /*conversationId*/,
                            std::map<std::string, std::string> /*preferences*/);
};

Gestion des conflits de fusion

Parce que deux administrateurs peuvent modifier la description en même temps, un conflit de fusion peut survenir sur profile.vcf. Dans ce cas, le commit avec le hash plus élevé (par exemple ffffff > 000000) sera choisi.

Les API

L’utilisateur a 2 méthodes pour obtenir et définir les métadonnées de la conversation:

       <method name="updateConversationInfos" tp:name-for-bindings="updateConversationInfos">
           <tp:added version="10.0.0"/>
           <tp:docstring>
               Update conversation's infos (supported keys: title, description, avatar)
           </tp:docstring>
           <arg type="s" name="accountId" direction="in"/>
           <arg type="s" name="conversationId" direction="in"/>
           <annotation name="org.qtproject.QtDBus.QtTypeName.In2" value="VectorMapStringString"/>
           <arg type="a{ss}" name="infos" direction="in"/>
       </method>

       <method name="conversationInfos" tp:name-for-bindings="conversationInfos">
           <tp:added version="10.0.0"/>
           <tp:docstring>
               Get conversation's infos (mode, title, description, avatar)
           </tp:docstring>
           <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="VectorMapStringString"/>
           <arg type="a{ss}" name="infos" direction="out"/>
           <arg type="s" name="accountId" direction="in"/>
           <arg type="s" name="conversationId" direction="in"/>
       </method>

infos est une map<str, str> avec les touches suivantes:

  • Mode: uniquement en lecture

  • titre

  • détail

  • avatar

Protocoles utilisés

Je suis là.

Pourquoi ce choix?

Chaque conversation sera un référentiel de données.

  1. Nous devons synchroniser et commander les messages. L’arbre de Merkle est la structure parfaite pour cela et peut être linéaire en fusionnant des branches. En outre, parce qu’il est largement utilisé par Git, il est facile de synchroniser entre les appareils.

  2. Distribué par la nature, largement utilisé, beaucoup de backends et pluggable.

  3. Peut vérifier les engagements via des crochets et des cryptos largement utilisés

  4. Peut être stockée dans une base de données si nécessaire

  5. Les conflits sont évités en utilisant des messages de mise en œuvre, pas des fichiers.

Ce que nous devons valider

  • Les performances peuvent être faibles.

  • Crocs dans libgit2

  • Plusieurs tirages en même temps?

Limits

Pour supprimer une conversation, l’appareil doit quitter la conversation et en créer une autre.

Cependant, les messages non permanents (comme les messages lisibles seulement pendant quelques minutes) peuvent être envoyés par un message spécial via le DRT (comme les notifications de typage ou de lecture).

La structure

/
 - invited
 - admins (public keys)
 - members (public keys)
 - devices (certificates of authors to verify commits)
 - banned
   - devices
   - invited
   - admins
   - members
 - votes
    - ban
        - members
            - uri
                - uriAdmin
        - devices
            - uri
                - uriAdmin
    - unban
        - members
            - uri
                - uriAdmin
 - CRLs

Transfert de fichiers

Swarm modifie massivement le transfert de fichiers. Maintenant, toute l’historique est synchronisée, permettant à tous les appareils de la conversation de récupérer facilement des fichiers anciens. Ces changements nous permettent de passer d’une logique où l’expéditeur a poussé le fichier sur d’autres appareils, en essayant de se connecter à leurs appareils (c’était mauvais car il n’était pas vraiment résistant aux changements de connexion / défaillances et avait besoin d’une nouvelle tentative manuelle) à une logique où l’expéditeur permet aux autres appareils de télécharger.

Protocole

L’expéditeur ajoute un nouveau commette à la conversation avec le format suivant:

value["tid"] = "RANDOMID";
value["displayName"] = "DISPLAYNAME";
value["totalSize"] = "SIZE OF THE FILE";
value["sha3sum"] = "SHA3SUM OF THE FILE";
value["type"] = "application/data-transfer+json";

et crée un lien dans ${data_path}/conversation_data/${conversation_id}/${file_id}file_id=${commitid}_${value["tide"]}.${extension}

Ensuite, le récepteur peut maintenant télécharger les fichiers en contactant les appareils hébergeant le fichier en ouvrant un canal avec name="data-transfer://" + conversationId + "/" + currentDeviceId() + "/" + fileId et stocker les informations que le fichier attend dans ${data_path}/conversation_data/${conversation_id}/waiting

Le périphérique recevant la connexion acceptera le canal en vérifiant si le fichier peut être envoyé (si sha3sum est correct et si le fichier existe). Le récepteur conservera le premier canal ouvert, fermera les autres et écrira dans un fichier (avec le même chemin que l’expéditeur: ${data_path}/conversation_data/${conversation_id}/${file_id}) toutes les données entrant.

Lorsque le transfert est terminé ou que le canal est fermé, le sha3sum est vérifié pour valider que le fichier est correct (ou il est supprimé).

En cas d’échec, lorsque l’appareil de la conversation sera redémarré, nous demanderons tous les fichiers d’attente de la même manière.

Appelle le swarm

TODO: partie du serveur de noms

Idée

Une conversation en essaim peut avoir plusieurs rendez-vous.

« accountUri/deviceId/conversationId/confId » où accountUri/deviceId décrit l’hôte.

L’hôte peut être déterminé de deux façons:

  • Dans le métadonnées en essaim, où elles sont stockées comme le titre/desc/avatar de la pièce.

  • Ou le premier appelant.

Lors du démarrage d’un appel, l’hôte ajoutera un nouveau engagement au swarm, avec l’URI à rejoindre (accountUri/deviceId/conversationId/confId).

Ainsi, chaque partie recevra l’information qu’un appel a commencé et pourra y rejoindre en l’appelant.

Des attaques?

  • Évitez les bombes de git

Notes

Le timestamp d’un commit peut être fiable car il est éditable. Seul le timestamp de l’utilisateur peut être fiable.

TLS

Les opérations Git, les messages de contrôle, les fichiers et autres choses utiliseront un lien TLS v1.3 p2p avec seulement des chiffres qui garantissent le PFS.

DHT (udp)

Utilisé pour envoyer des messages pour les mobiles (pour déclencher des notifications push) et pour initier des connexions TCP.

Activité du réseau

Processus d’invitation

Alice veut inviter Bob:

  1. Alice ajoute Bob à une conversation

  2. Alice generates an invite: { « application/invite+json » : { « conversationId »: « $id », « members »: [{…}] }}

  3. Deux possibilités pour envoyer le message a. Si pas connecté, via le DHT b. Autrement, Alice envoie sur le canal SIP

  4. Deux possibilités pour Bob a. Reçoit l’invitation, un signal est émis pour le client b. Non connecté, donc ne recevra jamais la demande car Alice ne doit pas savoir si Bob a simplement ignoré ou bloqué Alice.

Processus pour envoyer un message à quelqu’un

Alice veut envoyer un message à Bob:

  1. Alice ajoute un message dans le repo, donnant une pièce d’identité

  2. Alice reçoit un message reçu (de lui-même) si elle réussit

  3. Dans les deux cas, un message est créé: { « application/im-gitmessage-id » : « {« id »: »$convId », « commit »: »$commitId », « deviceId »: « $alice_device_hash »} »}. a. Si elle n’est pas connectée, via le DHT b. Autrement, Alice envoie sur le canal SIP

  4. 4 possibilités pour Bob: a. Bob n’est pas connecté à Alice, alors s’il fait confiance à Alice, demandez une nouvelle connexion et allez à b. b. Si connecté, appelez Alice et annoncez de nouveaux messages c. Bob ne connaît pas cette conversation. Demandez à travers le DHT d’obtenir une invitation d’abord pour être en mesure d’accepter cette conversation ({« application/invitation », conversationId}) d. Bob est déconnecté (aucun réseau, ou juste fermé). Il ne recevra pas le nouveau message mais tentera de synchroniser lorsque la connexion suivante se produira

Mise en œuvre

! [Diagramme: cours de chat en essaim]

Supported messages

Initial message

{
    "type": "initial",
    "mode": 0,
    "invited": "uri"
}

Represents the first commit of a repository and contains the mode:

enum class ConversationMode : int { ONE_TO_ONE = 0, ADMIN_INVITES_ONLY, INVITES_ONLY, PUBLIC }

and invited if mode = 0.

Text message

{
    "type": "text/plain",
    "body": "content",
    "react-to": "id (optional)"
}

Or for an edition:

{
    "type": "application/edited-message",
    "body": "content",
    "edit": "id of the edited commit"
}

Appels

Show the end of a call (duration in milliseconds):

{
    "type": "application/call-history+json",
    "to": "uri",
    "duration": "3000"
}

Or for hosting a call in a group (when it starts)

{
    "type": "application/call-history+json",
    "uri": "uri of the host",
    "device": "device of the host",
    "confId": "hosted confId"
}

A second commit with the same JSON + duration is added at the end of the call when hosted.

Add a file

{
    "type": "application/data-transfer+json",
    "tid": "unique identifier of the file",
    "displayName": "File name",
    "totalSize": "3000",
    "sha3sum": "a sha3 sum"
}

totalSize is in bits,

Updating profile

{
    "type": "application/update-profile",
}

Member event

{
    "type": "member",
    "uri": "uri of the member",
    "action": "add/join/remove/ban"
}

When a member is invited, join or leave or is kicked from a conversation

Vote event

Generated by administrators to add a vote for kicking or un-kicking someone.

{
    "type": "vote",
    "uri": "uri of the member",
    "action": "ban/unban"
}

!! Le vieux projet!!

Remarque: Les notes suivantes ne sont pas encore organisées.

Des améliorations de la cryptographie.

Pour une fonctionnalité de chat de groupe sérieuse, nous avons également besoin de crypto sérieuse. Avec la conception actuelle, si un certificat est volé comme les valeurs DHT précédentes d’une conversation, la conversation peut être décryptée. Peut-être que nous devons aller à quelque chose comme ** Double ratchet**.

Remarque: il pourrait y avoir une lib pour mettre en œuvre des conversations de groupe.

Il a besoin d’un soutien ECC dans OpenDHT

Utilisation

Ajouter des rôles?

Il existe deux cas d’utilisation principaux pour les discussions de groupe:

  1. Quelque chose comme un Mattermost dans une entreprise, avec des canaux privés, et certains rôles (admin/spectateur/bot/etc) ou pour l’éducation (où seulement quelques-uns sont actifs).

  2. Des conversations horizontales comme une conversation entre amis.

Jami will be for which one?

Idée de mise en œuvre

Un certificat pour un groupe qui signe un utilisateur avec un drapeau pour un rôle.

Joignez-vous à une conversation

  • Uniquement par invitation directe

  • Par un lien/code QR/tout ce qui est

  • Par le nom de la chambre?

Ce dont nous avons besoin

  • Confidentialité: les membres en dehors du chat de groupe ne devraient pas pouvoir lire les messages dans le groupe

  • Confidentialité: si une clé du groupe est compromise, les messages précédents doivent rester confidentiels (le plus possible)

  • Ordonnage des messages: Il faut avoir les messages dans le bon ordre

  • Synchronisation: Il faut aussi s’assurer que tous les messages sont disponibles le plus rapidement possible.

  • Persistance: En fait, un message sur le DHT ne dure que 10 minutes. Parce que c’est le meilleur moment calculé pour ce type de DHT. Pour persister les données, le nœud doit réinsérer la valeur du DHT toutes les 10 minutes. Une autre façon de le faire lorsque le nœud est hors ligne est de laisser les nœuds réinsérer les données. Mais, si après 10 minutes, 8 nœuds sont toujours là, ils feront 64 demandes (et c’est exponentiel). La façon actuelle d’éviter le spam pour cela est interroge. Cela fera toujours 64 demandes mais limitera la redondance maximale à 8 nœuds.

Autres voies distribuées

  • Il faut faire des recherches.

  • J’ai besoin d’une enquête.

  • Il faut enquêter.

En fonction des travaux actuels que nous avons

Le chat de groupe peut être basé sur le même travail que celui que nous avons déjà pour les multiples appareils (mais ici, avec un certificat de groupe).

  1. Il faut déplacer la base de données du client vers le daemon.

  2. Si personne n’est connecté, la synchronisation ne peut pas être faite, et la personne ne verra jamais la conversation

Un autre DHT dédié

Comme un DHT avec un superutilisateur.

Transfert de fichiers

Actuellement, l’algorithme de transfert de fichiers est basé sur une connexion TURN (voir Transfert de fichiers). Dans le cas d’un grand groupe, cela ne sera pas bon. Nous avons d’abord besoin d’un implement p2p pour le transfert de fichiers.

Autre problème: actuellement, il n’existe pas de mise en œuvre de l’assistance TCP pour ICE dans PJSIP.

Les ressources

  • Les résultats de l’enquête ont été publiés sur le site Web de l’Union européenne.

  • Synchronisation répartie robuste des systèmes linéaires en réseau avec des informations intermittentes (Sean Phillips et Ricardo G. Sanfelice)