Les éclats
A swarm (group chat) is a set of participants capable of resilient, decentralized communication. For example, if two participants lose connectivity with the rest of the group (e.g., during an Internet outage) but can still reach each other over a LAN or subnetwork, they can exchange messages locally and then synchronize with the rest of the group once connectivity is restored.
A swarm is defined by the following properties:
Ability to split and merge based on network connectivity.
History synchronization. Every participant must be able to send a message to the entire group.
Aucune autorité centrale, aucun serveur.
Non-repudiation. Devices must be able to verify past messages” validity and to replay the entire history.
Perfect Forward Secrecy (PFS) is provided on the transport channels. Storage is handled by each device.
L’idée principale est d’obtenir un arbre de Merkle synchronisé avec les participants.
We identified four modes for swarms that we want to implement:
ONE_TO_ONE: A private conversation between two endpoints—either between two users or with yourself.
ADMIN_INVITES_ONLY: A swarm in which only the administrator can invite members (for example, a teacher-managed classroom).
INVITES_ONLY: A closed swarm that admits members strictly by invitation; no one may join without explicit approval.
PUBLIC: A public swarm that anyone can join without prior invitation (For example a forum).
Scenarios
Créer une masse
Bob veut créer un nouvel essaim
Bob creates a local Git repository.
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`
Le hash du premier comit devient l’ID de la conversation.
Bob announces to his other devices that he created a new conversation. This is done via an invite to join the group sent through the DHT to other devices linked to that account.
Ajouter quelqu’un
Bob adds Alice
Bob adds Alice to the repo:
Ajout de l’URI invité en
/invited
Ajout de la RLC à
/crls
Bob sends a request on the DHT.
Recevoir une invitation
Alice gets the invite to join the previously created swarm
Alice accepts the invite (if she declines, nothing happens; she will remain in the « invited » list, and will never receive any messages)
A peer-to-peer connection is established between Alice and Bob.
Alice pulls the Git repository from Bob. WARNING this means that messages require a connection, not from the DHT as it is today.
Alice validates the commits from Bob.
To validate that Alice is a member, she removes the invite from
/invited
directory, then adds her certificate to the/members
directoryOnce all commits are validated and syncronized to her device, Alice discovers other members of the group. with these peers, she will then construct the DRT with Bob as a bootstrap.
Envoyer un message
Alice sends a message to Bob
Alice creates a commit message. She constructs a JSON payload containing the MIME type and message body. For example:
{
"type": "text/plain",
"body": "hello"
}
Alice ensure her device credentials are present. If Alice’s device certificate or its associated CRL isn’t already stored in the repository, she adds them so that other participants can verify the commit.
Alice commits to the repository (Because Jami relies primarily on commit-message metadata rather than file contents, merge conflicts are rare; the only potential conflicts would involve CRLs or certificates, which are versioned in a dedicated location).
Alice announces the commit via the DRT with a service message and pings the DHT for mobile devices (they must receive a push notification).
Note
To notify other devices, the sender transmits a SIP message with type: application/im-gitmessage-id
.
The JSON payload includes the deviceId (the sender’s), the conversationId and the reference (hash) of the new commit.
Reçoit un message
Bob receives a message from Alice
Bob performs a Git pull on Alice’s repository.
All incoming commits MUST be verified by a hook.
If all commits are valid, commits are stored and displayed.Bob then announces the message via the DRT for other devices.
If any commit is invalid, pull is aborted. Alice must restore her repository to a correct state before retrying.
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:
Note
If the validation fails, the fetch is ignored and we do not merge the branch (and remove the data), and the user should be notified.
If a fetch is too big, it’s not merged.
For each incoming commit, ensure that the sending device is currently authorized and that the issuer’s certificate exists under /members or /admins, and the device’s certificate under /devices.
Then handle one of three cases, based on the commit’s parent count:
Merge Commit (2 parents). No further validation is required, merges are always accepted.
Initial Commit (0 parents). Validate that this is the very first repository snapshot:
Admin certificate is added.
Device certificate is added.
CRLs (Certificate Revocation Lists) are added.
No other files are present.
Ordinary Commit (1 parent). The commit message must be JSON with a top‑level
type
field. Handle eachtype
as follows:If
text
(or any non–file‑modifying MIME type)Signature is valid against the author’s certificate in the repo.
No unexpected files are added or removed.
If
vote
voteType
is one of the supported values (e.g. « ban », « unban »).The vote matches the signing user.
The signer is an admin, their device is present, and not themselves banned.
No unexpected files are added or removed.
If
member
If
adds
Properly signed by the inviter.
New member’s URI appears under
/invited
.No unexpected files are added or removed.
If ONE_TO_ONE, ensure exactly one admin and one member.
If ADMIN_INVITES_ONLY, the inviter must be an admin.
If
joins
Properly signed by the joining device.
Device certificate added under
/devices
.Corresponding invite removed from
/invited
and certificate added to/members
.No unexpected files are added or removed.
If
banned
Vote is valid per the
vote
rules above.Ban is issued by an admin.
Target’s certificate moved to /banned.
Only files related to the ban vote are removed.
No unexpected files are added or removed.
Fallback. If the commit’s type or structure is unrecognized, reject it and notify the peer (or user) that they may be running an outdated version or attempting unauthorized changes.
Interdiction d’un appareil
Important
Jami source code tends to use the terms (un)ban, while the user interface uses the terms (un)block.
Alice, Bob, Carla, Denys are in a swarm. Alice issues a ban against Denys.
In a fully peer‑to‑peer system with no central authority, this simple action exposes three core challenges:
Untrusted Timestamps: Commit timestamps cannot be relied upon for ordering ban events, as any device can forge or replay commits with arbitrary dates.
Conflicting bans: In cases where multiple admin devices exist, network partitions can result in conflicting ban decisions. For instance, if Alice can communicate with Bob but not with Denys and Carla, while Carla can communicate with Denys, conflicting bans may occur. If Denys bans Alice while Alice bans Denys, the group’s state becomes unclear when all members eventually reconnect and merge their conversation histories.
Compromised or expired devices: Devices can be compromised, stolen, or have their certificates expire. The system must allow banning such devices and ensure they cannot manipulate their certificate or commit timestamps to send unauthorized messages or falsify their expiration status.
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.
This voting system needs a human action to ban someone or must be based on the CRLs info from the repository (because we can not trust external CRLs).
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
Important
Alice MUST be an admin to vote.
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 (admin) re-adds Bob (banned member)
If she votes for unbanning Bob. To do that, she creates the file in /votes/unban/members/uri_bob/uri_alice (members can be replaced by devices for a device, or invited for invites or admins for admins) and commits
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
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
Si on reçoit un nouveau commentaire pour cette conversation, on l’ignore.
Si Jami est en activité et que le repo est toujours présent, la conversation n’est pas annoncée aux clients.
Two cases: a. If no other member in the conversation we can immediately remove the repository b. If still other members, commit that we leave the conversation, and now wait that at least another device sync this message. This avoids the fact that other members will still detect the user as a valid member and still sends new message notifications.
Lorsque nous sommes sûrs que quelqu’un est synchronisé, supprimer effacé=time::now() et synchroniser avec les appareils d’autres utilisateurs
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)
Processes for 1:1 chats
The goal here is to keep the old API (addContact/removeContact, sendTrustRequest/acceptTrustRequest/discardTrustRequest) to create a chat with a peer and its contact. This still implies some changes that we cannot ignore:
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:
La demande de confiance intègre un « conversationId » pour informer le paire de quelle conversation cloner lors de l’acceptation de la demande
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.
Alice adds Bob.
Bob accepts.
Alice removes Bob.
Alice adds Bob.
ou
Alice adds Bob and Bob adds Alice at the same time, but both are not connected together.
In this case, two conversations are generated. We don’t want to remove messages from users or choose one conversation here. So, sometimes two conversations between the same members will be shown. It will generate some bugs during the transition time (as we don’t want to break API, the inferred conversation will be one of the two shown conversations, but for now it’s « ok-ish », will be fixed when clients will fully handle conversationId for all APIs (calls, file transfer, etc)).
Important
After accepting a conversation’s request, there is a time the daemon needs to retrieve the distant repository. During this time, clients MUST show a syncing view to give informations to the user. While syncing:
ConfigurationManager::getConversations() will return the conversation’s id even while syncing.
ConfigurationManager::conversationInfos() renvoie {{« synchronisation »: « true »}} si la synchronisation est effectuée.
ConfigurationManager::getConversationMembers() will return a map of two URIs (the current account and the peer who sent the request).
Les discussions demandent des spécifications
Les demandes de conversation sont représentées par une Map<String, String> avec les touches suivantes:
id: the conversation ID
from: URI of the sender
reçu: timestamp
titre: nom (optionnel) de la conversation
Description: (facultatif)
avatar: (optional) the profile picture
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
To update the vCard, a user with enough permissions (by default: =ADMIN) needs to edit /profile.vcf
and will commit the file with the mimetype application/update-profile
.
The new message is sent via the same mechanism and all peers will receive the MessageReceived signal from the daemon.
The branch is dropped if the commit contains other files or too big or if done by a non-authorized member (by default: <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>
où infos
est une map<str, str>
avec les touches suivantes:
Mode: uniquement en lecture
titre
détail
avatar: the profile picture
Récupération de l’importation d’un compte (lien/exportation)
L’archivage DOIT contenir conversationId pour pouvoir récupérer les conversations sur de nouveaux comités après une réimportation (parce qu’il n’y a pas d’invitation à ce stade).
La conversation est là, dans ce cas, le démon est capable de cloner cette conversation
La conversationId est manquante, donc le daemon demande (via un message
{{"application/invite", conversationId}}
) une nouvelle invitation que l’utilisateur doit (re) accepter
Important
A conversation can only be retrieved if a contact or another device is there, else it will be lost. There is no magic.
Protocoles utilisés
Je suis là.
Pourquoi ce choix?
Each conversation will be a Git repository. This choice is motivated by:
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.
Distribué par la nature, largement utilisé, beaucoup de backends et pluggable.
Peut vérifier les engagements via des crochets et des cryptos largement utilisés
Peut être stockée dans une base de données si nécessaire
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
This new system overhauls file sharing: the entire history is now kept in sync, so any device in the conversation can instantly access past files. Rather than forcing the sender to push files directly—an approach that was fragile in the face of connection drops and often required manual retries—devices simply download files when they need them. Moreover, once one device has downloaded a file, it can act as a host for others, ensuring files remain available even if the original sender goes offline.
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}
où 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.
Call in Swarm
Idée
A swarm conversation can have multiple rendez-vous. A rendez-vous is defined by the following URI:
« accountUri/deviceId/conversationId/confId » où accountUri/deviceId décrit l’hôte.
L’hôte peut être déterminé de deux façons:
In the swarm metadatas. Where it’s stored like the title/desc/avatar (profile picture) of the room
Ou le premier appelant.
When starting a call, the host will add a new commit to the repository, with the URI to join (accountUri/deviceId/conversationId/confId). This will be valid till the end of the call (announced by a commit with the duration to show)
Ainsi, chaque partie recevra l’information qu’un appel a commencé et pourra y rejoindre en l’appelant.
Des attaques?
Avoid Git bombs
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:
Alice ajoute Bob à une conversation
Alice generates an invite: { « application/invite+json » : { « conversationId »: « $id », « members »: [{…}] }}
Deux possibilités pour envoyer le message a. Si pas connecté, via le DHT b. Autrement, Alice envoie sur le canal SIP
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:
Alice ajoute un message dans le repo, donnant une pièce d’identité
Alice reçoit un message reçu (de lui-même) si elle réussit
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 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": "host URI",
"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": "member URI",
"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": "member URI",
"action": "ban/unban"
}
!! Le vieux projet!!
Note
Following notes are not organized yet. Just some line of thoughts.
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**.
Note
A lib might exist to implement group conversations.
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:
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).
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).
Il faut déplacer la base de données du client vers le daemon.
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.
Other problem: currently there is no implementation for TCP support for ICE in PJSIP. This is mandatory for this point (in PJSIP or homemade)
Les ressources
Synchronisation répartie robuste des systèmes linéaires en réseau avec des informations intermittentes (Sean Phillips et Ricardo G. Sanfelice)