Schar

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:

  1. Ability to split and merge based on network connectivity.

  2. History synchronization. Every participant must be able to send a message to the entire group.

  3. Keine zentrale Behörde, kann sich nicht auf einen Server verlassen.

  4. Non-repudiation. Devices must be able to verify past messages‘ validity and to replay the entire history.

  5. Perfect Forward Secrecy (PFS) is provided on the transport channels. Storage is handled by each device.

Die Idee ist, einen synchronisierten Merkle-Baum mit den Teilnehmern zu bekommen.

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).

Szenarien

Schaffen Sie eine Schar

Bob will einen neuen Schwarm schaffen.

  1. Bob creates a local Git repository.

  2. Dann erstellt er eine erste unterzeichnete Verpflichtung mit folgenden Worten:

    • Sein öffentlicher Schlüssel in /admins

    • Sein Gerätesertifikaat in ̀ /geräte `

    • Seine CRL in ̀ /crls`

  3. Das Hash des ersten Kommits wird zum ID des Gesprächs.

  4. 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.

Ein Zusatz

Bob adds Alice

  1. Bob adds Alice to the repo:

    • Fügt den eingeladenen URI in /invited hinzu

    • Zugabe der CRL in /crls

  2. Bob sends a request on the DHT.

Einladung zu empfangen

Alice gets the invite to join the previously created swarm

  1. Alice accepts the invite (if she declines, nothing happens; she will remain in the „invited“ list, and will never receive any messages)

  2. A peer-to-peer connection is established between Alice and Bob.

  3. Alice pulls the Git repository from Bob. WARNING this means that messages require a connection, not from the DHT as it is today.

  4. Alice validates the commits from Bob.

  5. To validate that Alice is a member, she removes the invite from /invited directory, then adds her certificate to the /members directory

  6. Once 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.

Eine Nachricht senden

Alice sends a message to Bob

  1. Alice creates a commit message. She constructs a JSON payload containing the MIME type and message body. For example:

{
    "type": "text/plain",
    "body": "hello"
}
  1. 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.

  2. 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).

  3. Alice announces the commit via the DRT with a service message and pings the DHT for mobile devices (they must receive a push notification).

Bemerkung

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.

Eine Nachricht empfangen

Bob receives a message from Alice

  1. Bob performs a Git pull on Alice’s repository.

  2. All incoming commits MUST be verified by a hook.

  3. If all commits are valid, commits are stored and displayed.Bob then announces the message via the DRT for other devices.

  4. If any commit is invalid, pull is aborted. Alice must restore her repository to a correct state before retrying.

Validierung eines Verpflichtungsbestimmungsbereichs

Um zu vermeiden, dass Benutzer einige unerwünschte Kommitte (mit Konflikten, falschen Nachrichten usw.) durchführen, muss jedes Kommitte (von dem ältesten bis zum neuesten) vor der Verschmelzung einer Remote-Fläche validiert werden:

Bemerkung

  1. 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.

  2. 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 each type 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.

Verbot eines Geräts

Wichtig

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:

  1. Untrusted Timestamps: Commit timestamps cannot be relied upon for ordering ban events, as any device can forge or replay commits with arbitrary dates.

  2. 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.

  3. 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.

Ähnliche Systeme (mit verteilten Gruppensystemen) sind nicht so viel, aber hier sind einige Beispiele:

  • [mpOTR definiert nicht, wie man jemanden verbietet]

  • Signal, ohne einen zentralen Server für Gruppenchat (EDIT: Sie ändern diesen Punkt kürzlich), gibt nicht die Möglichkeit, jemanden aus einer Gruppe zu verbieten.

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).

Entfernen Sie ein Gerät aus einem Gespräch

Das ist der einzige Teil, an dem ein Konsens besteht, um eine Spaltung zu vermeiden. Wenn sich zwei Mitglieder aus dem Gespräch treten, was wird dann das dritte sehen?

Dies ist notwendig, um widerrufene Geräte zu erkennen oder einfach zu vermeiden, dass unerwünschte Personen in einem öffentlichen Raum vorhanden sind.

Alice entfernt Bob.

Wichtig

Alice MUST be an admin to vote.

  • Erstens stimmt sie für das Verbot von Bob. Dazu erstellt sie die Datei in /votes/ban/members/uri_bob/uri_alice (Mitglieder können durch Geräte für ein Gerät ersetzt werden, oder für Einladungen oder Admin für Admin eingeladen werden) und verpflichtet sich,

  • Dann überprüft sie, ob die Abstimmung gelöst ist. Das bedeutet, dass >50% der Admin bereit sind, Bob zu verbieten (wenn sie allein ist, ist es sicher mehr als 50%).

  • Wenn die Abstimmung gelöst ist, können Dateien in /votes/ban entfernt werden, alle Dateien für Bob in /members, /admins, /invited, /CRLs, /devices entfernt werden (oder nur in /devices, wenn es sich um ein Gerät handelt, das verboten ist) und Bobs Zertifikat kann in /banned/members/bob_uri.crt (oder /banned/devices/uri.crt, wenn ein Gerät verboten ist) platziert und dem repo übertragen werden

  • Dann informiert Alice andere Benutzer (außer 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

  • Dann überprüft sie, ob die Abstimmung gelöst ist. Das bedeutet, dass >50% der Admin bereit sind, Bob zu verbieten (wenn sie allein ist, ist es sicher mehr als 50%).

  • Wenn die Abstimmung gelöst ist, können Dateien in /votes/unban entfernt werden, alle Dateien für Bob in /members, /admins, /invited, /CRLs, können neu hinzugefügt werden (oder nur in /devices, wenn es sich um ein Gerät handelt, das nicht verboten ist) und dem Repo verpflichtet werden

Entfernen Sie ein Gespräch

  1. Speichern Sie in convInfos removed=time::now() (wie removeKontakt speichert in Kontakten) dass die Konversation entfernt und mit den Geräten anderer Benutzer synchronisiert wird

  2. Wenn ein neuer Kompromiss für dieses Gespräch eingegangen ist, wird er ignoriert.

  3. Wenn Jami noch in Betrieb ist, wird das Gespräch nicht bekannt gegeben.

  4. 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.

  5. Wenn wir sicher sind, dass jemand synchronisiert ist, entfernen wir erledigt=zeit::now() und synchronisieren wir mit den Geräten anderer Benutzer

  6. Alle Geräte des Benutzers können das Repository und die damit verbundenen Dateien löschen

Wie ein Modus angegeben wird

Die Modus können nicht im Laufe der Zeit geändert werden. Oder es ist ein anderes Gespräch. Also werden diese Daten in der ersten Kommit-Nachricht gespeichert.

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

Derzeit akzeptiert „Mode“ Werte 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:

Der Prozess ist immer noch derselbe, ein Konto kann einen Kontakt über addContact hinzufügen und dann eine TrustRequest über die DHT senden.

  1. Die TrustRequest enthält eine „ConversationId“, um dem Peer zu mitteilen, welche Konversation er klonen soll, wenn er die Anfrage akzeptiert

  2. TrustRequest wird erneut versucht, wenn der Kontakt online zurückkehrt. Das ist heute nicht der Fall (da wir keinen neuen TrustRequest erstellen wollen, wenn der Peer den ersten entfernt). Wenn also ein Konto eine Vertrauensanfrage erhält, wird es automatisch ignoriert, wenn die Anfrage mit einem verwandten Gespräch abgelehnt wird (da convRequests synchronisiert werden)

Wenn ein Kontakt die Anfrage akzeptiert, ist eine Synchronisierungsperiode notwendig, denn der Kontakt muss jetzt das Gespräch klonen.

removeContact() wird den Kontakt und die damit verbundenen 1:1-Gespräche entfernen (mit dem gleichen Prozess wie „Entfernen Sie eine Konversation“).

Schwierige Szenarien

Es gibt einige Fälle, in denen zwei Gespräche erfolgen können.

  1. Alice adds Bob.

  2. Bob accepts.

  3. Alice removes Bob.

  4. Alice adds Bob.

oder

  1. 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)).

Wichtig

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() wird {{„Synchronisierung“: „Wahr“}} zurückgeben, wenn sie synchronisiert wird.

  • ConfigurationManager::getConversationMembers() will return a map of two URIs (the current account and the peer who sent the request).

Gespräche verlangen eine Spezifikation

Die Gesprächsanfragen werden durch eine Map<String, String> mit folgenden Tasten dargestellt:

  • id: the conversation ID

  • from: URI of the sender

  • Empfang: Zeitstempel

  • Titel: (optional) Name für das Gespräch

  • Beschreibung: (optional)

  • avatar: (optional) the profile picture

Synchronisierung des Gesprächsprofilen

Um zu identifizieren, benötigt ein Gespräch im Allgemeinen einige Metadaten, wie einen Titel (z.B. Jami), eine Beschreibung (z.B. einige Links, was das Projekt ist, etc.) und ein Bild (das Logo des Projekts). Diese Metadaten sind optional, werden jedoch an alle Mitglieder geteilt, daher müssen sie synchronisiert und in die Anfragen aufgenommen werden.

Speicherung im Repository

Das Konversationsprofil wird in einer klassischen vCard-Datei an der Basis (/profile.vcf) gespeichert, wie:

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

Synchronisierung

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).

Zuletzt angezeigt

In den synchronisierten Daten sendet jedes Gerät den Status der Gespräche an andere Geräte. In diesem Zustand wird der letzte angezeigte gesendet. Da jedoch jedes Gerät seinen eigenen Zustand für jedes Gespräch haben kann und wahrscheinlich ohne dasselbe letzte Kommit zu einem bestimmten Zeitpunkt, gibt es mehrere Szenarien zu berücksichtigen:

Es werden fünf Szenarien unterstützt:

  • Wenn die letzte von anderen Geräten gesendete Anzeige die gleiche ist wie die aktuelle, ist nichts zu tun.

  • wenn für das aktuelle Gerät keine letzte Anzeige vorhanden ist, wird die Fernanzeigungsnachricht verwendet.

  • Wenn die letzte Remote nicht im Repo vorhanden ist, bedeutet dies, dass das Commit später abgerufen wird, also wird das Ergebnis in Cache gebracht

  • Wenn die Fernbedienung bereits abgerufen ist, überprüfen wir, ob die letzte angezeigte Lokalvorrichtung in der Geschichte ist, um sie zu ersetzen

  • Wenn schließlich eine Nachricht vom gleichen Autor angekündigt wird, bedeutet dies, dass wir die letzte angezeigte Nachricht aktualisieren müssen.

Einstellungen

Jedes Gespräch hat die von dem Benutzer festgelegten Präferenzen angeschlossen. Diese Präferenzen werden über die Geräte des Benutzers synchronisiert. Dies kann die Farbe des Gesprächs sein, wenn der Benutzer Benachrichtigungen ignorieren möchte, Dateigrößenbeschränkung usw.

  • „color“ - the color of the conversation (#RRGGBB format)

  • „ignoreNotifications“ - die Benachrichtigungen für neue Nachrichten in diesem Gespräch zu ignorieren

  • „Symbol“ - um ein Standard-Emoji zu definieren.

Diese Präferenzen werden in einem MapStringString-Paket gespeichert, in accountDir/conversation_data/conversationId/preferences gespeichert und nur über SyncMsg über Geräte des gleichen Benutzers gesendet.

Die API zur Interaktion mit den Präferenzen sind:

// 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*/);
};

Konfliktmanagement durch Fusion

Da zwei Admin die Beschreibung gleichzeitig ändern können, kann auf profile.vcf ein Fusionkonflikt auftreten. In diesem Fall wird das Commit mit dem höheren Hash (z.B. ffffff > 000000) gewählt.

API

Der Benutzer hat 2 Methoden zur Erfassung und Einstellung der Metadaten der Konversation:

       <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>

wo infos eine map<str, str> mit folgenden Tasten ist:

  • Modus: Lese-Nur

  • Titel

  • Beschreibung

  • avatar: the profile picture

Gebrauchsprotokolle

Git

Warum diese Wahl

Each conversation will be a Git repository. This choice is motivated by:

  1. Wir müssen Nachrichten synchronisieren und bestellen. Der Merkle Tree ist die perfekte Struktur dafür und kann durch das Zusammenspielen von Zweigen linearisiert werden. Außerdem, weil es von Git massiv verwendet wird, ist es einfach, zwischen Geräten zu synchronisieren.

  2. Wird in der Natur verteilt, massiv verwendet, viele Backends und Plug-ins.

  3. Kann über Haken und massiv genutzte Krypto-Verbindungen überprüfen

  4. Kann bei Bedarf in einer Datenbank gespeichert werden

  5. Konflikte werden vermieden, indem man mit Kommit-Nachrichten, nicht mit Dateien, arbeitet.

Was wir bestätigen müssen

  • Leistung? git.lock kann niedrig sein

  • Anschläge in Libgit2

  • Mehrfach gleichzeitig?

Grenzen

Um eine Konversation zu löschen, muss das Gerät die Konversation verlassen und eine andere erstellen.

Nicht dauerhafte Nachrichten (z. B. Nachrichten, die nur für einige Minuten gelesen werden können) können jedoch über eine spezielle Nachricht über die DRT (z. B. Notifikationen zum Tippen oder Lesen) gesendet werden.

Struktur

/
 - 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

Dateifügelübertragung

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.

Protokoll

Der Absender fügt in die Unterhaltung ein neues Commit hinzu:

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

und erstellt einen Link in ${data_path}/conversation_data/${conversation_id}/${file_id} wo file_id=${commitid}_${value["Tid"]}.${extension}

Dann kann der Empfänger nun die Dateien herunterladen, indem er die Geräte kontaktiert, die die Datei beherbergen, indem er einen Kanal mit name="data-transfer://" + conversationId + "/" + currentDeviceId() + "/" + fileId öffnet und die Informationen speichert, dass die Datei in ${data_path}/conversation_data/${conversation_id}/waiting

Das Anschlussgerät akzeptiert den Kanal, indem es überprüft, ob die Datei gesendet werden kann (wenn sha3sum korrekt ist und wenn Datei existiert). Der Empfänger hält den ersten geöffneten Kanal, schließt die anderen und schreibt in eine Datei (mit dem gleichen Weg wie der Absender: ${data_path}/conversation_data/${conversation_id}/${file_id}) alle eingehenden Daten.

Wenn die Übertragung abgeschlossen ist oder der Kanal geschlossen ist, wird die sha3sum überprüft, um zu bestätigen, dass die Datei korrekt ist (andernfalls wird sie gelöscht).

Im Falle eines Ausfalls, wenn ein Gerät des Gesprächs wieder online ist, bitten wir alle Wartedateien auf die gleiche Weise.

Call in Swarm

Idee

A swarm conversation can have multiple rendez-vous. A rendez-vous is defined by the following URI:

„accountUri/deviceId/conversationId/confId“ wo accountUri/deviceId den Host beschreibt.

Der Gastgeber kann auf zwei Arten bestimmt werden:

  • In the swarm metadatas. Where it’s stored like the title/desc/avatar (profile picture) of the room

  • Oder der erste Anrufer.

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)

Jeder Teil wird die Information erhalten, dass ein Anruf gestartet wurde und kann sich daran beteiligen, indem er es anruft.

Angriffe?

  • Avoid Git bombs

Anmerkungen

Der Zeitstempel eines Commits ist zuverlässig, weil er bearbeitet werden kann. Nur dem Zeitstempel des Nutzers kann man vertrauen.

TLS

Git-Operationen, Steuerungsnachrichten, Dateien und andere Dinge verwenden einen P2p TLS v1.3-Link mit nur Verschlüsselungen, die PFS garantieren.

DHT (UDP)

Verwendet zur Absendung von Nachrichten für Mobiltelefone (um Push-Benachrichtigungen auszulösen) und zur Einleitung von TCP-Verbindungen.

Netzwerkaktivität

Ein Einladungsprozess

Alice will Bob einladen:

  1. Alice fügt Bob zu einem Gespräch hinzu.

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

  3. Zwei Möglichkeiten zum Versenden der Nachricht a. Wenn nicht verbunden, über die DHT b. Anders, Alice sendet auf dem SIP-Kanal

  4. Zwei Möglichkeiten für Bob a. Erhält die Einladung, ein Signal wird für den Client b. Nicht verbunden, so wird nie die Anfrage erhalten, weil Alice nicht wissen muss, ob Bob einfach ignoriert oder blockiert Alice.

Prozess, um jemandem eine Nachricht zu senden

Alice will Bob eine Nachricht senden:

  1. Alice fügt eine Nachricht in die Repo hinzu, gibt eine ID

  2. Alice erhält eine Nachricht (von sich selbst) wenn sie erfolgreich ist

  3. In beiden Fällen wird eine Nachricht erstellt: { „Application/im-gitmessage-id“ : „{„id“:“\(convId", "commit":"\)commitId“, „deviceId“: „$alice_device_hash“}“}. a. Wenn nicht verbunden ist, über den DHT b. Anders sendet Alice auf dem SIP-Kanal

  4. Vier Möglichkeiten für Bob: a. Bob ist nicht mit Alice verbunden, also wenn er Alice vertraut, bitten Sie um eine neue Verbindung und gehen Sie zu b. b. Wenn verbunden, holen Sie von Alice und kündigen Sie neue Nachrichten an c. Bob kennt dieses Gespräch nicht. Bitten Sie über die DHT, um zuerst eine Einladung zu erhalten, um dieses Gespräch zu akzeptieren ({„Anwendung/Einladung“, GesprächId}) d. Bob ist getrennt (kein Netzwerk, oder einfach geschlossen). Er wird die neue Nachricht nicht erhalten, sondern versuchen, zu synchronisieren, wenn die nächste Verbindung auftritt

Durchführung

! [Diagramm: Schwarm-Chat-Klassen]

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"
}

Anrufe

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"
}

!! Altes Entwurf!!

Bemerkung

Following notes are not organized yet. Just some line of thoughts.

Krypto-Verbesserungen.

Für eine ernsthafte Gruppen-Chat-Funktion brauchen wir auch ernsthafte Krypto. Wenn ein Zertifikat als die vorherigen DHT-Werte einer Konversation gestohlen wird, kann die Konversation entschlüsselt werden. Vielleicht müssen wir zu etwas wie Double Ratchet gehen.

Bemerkung

A lib might exist to implement group conversations.

ECC-Unterstützung in OpenDHT benötigt

Verwendung

Add Roles?

Es gibt zwei wichtige Anwendungsfälle für Gruppenchats:

  1. Etwas wie ein Mattermost in einem Unternehmen, mit privaten Kanälen und einigen Rollen (Admin/Spectator/Bot/etc) oder für Bildung (wo nur wenige aktiv sind).

  2. Horizontale Gespräche wie ein Gespräch zwischen Freunden.

Jami will be for which one?

Umsetzungsinterpretation

Ein Zertifikat für eine Gruppe, die Benutzer mit einer Flagge für eine Rolle unterschreiben.

Komm mit in ein Gespräch.

  • Nur über eine direkte Einladung

  • Über einen Link/QR-Code/was auch immer

  • Über einen Zimmernamen?

Was wir brauchen

  • Vertraulichkeit: Mitglieder außerhalb des Gruppenchats dürfen keine Nachrichten in der Gruppe lesen

  • Weiterhin geheim gehalten: Wenn ein Schlüssel der Gruppe gefährdet ist, sollten frühere Nachrichten vertraulich bleiben (soweit möglich)

  • Nachrichtenordnung: Nachrichten müssen in der richtigen Reihenfolge angeordnet werden

  • Synchronisierung: Es ist auch notwendig, sicherzustellen, dass alle Nachrichten so schnell wie möglich vorhanden sind.

  • Persistenz: Eigentlich ist eine Nachricht auf der DHT nur 10 Minuten lang. Weil es die beste Zeit ist, die für diese Art von DHT berechnet wird. Um Daten zu erhalten, muss der Knoten den Wert auf der DHT alle 10 Minuten neu setzen. Eine andere Möglichkeit, wenn der Knoten offline ist, ist es, Knoten die Daten neu zu setzen. Aber wenn nach 10 Minuten, 8 Knoten sind noch hier, werden sie 64 Anfragen machen (und es ist exponentiell). Der aktuelle Weg, Spamming zu vermeiden, ist abfragt. Dies wird immer noch 64 Anfragen tun, aber die maximale Redundanz auf 8 Knoten begrenzen.

Andere verteilte Wege

  • IPFS: Ich brauche eine Untersuchung.

  • Ich brauche eine Untersuchung.

  • Ich brauche eine Untersuchung.

Auf der Grundlage der aktuellen Arbeit, die wir haben

Gruppen-Chat kann auf der gleichen Arbeit basieren, die wir bereits für mehrere Geräte haben (aber hier mit einem Gruppenzertifikat).

  1. Hier muss die Datenbank vom Client in den Daemon verschoben werden.

  2. Wenn niemand mit dem Netzwerk verbunden ist, kann die Synchronisierung nicht durchgeführt werden, und die Person wird das Gespräch nie sehen

Ein weiterer spezieller DHT

Wie ein DHT mit einem Superbenutzer.

What’s next for file transfers

Currently, the file transfer algorithm is based on a TURN connection (See Dateifügelübertragung). In the case of a big group, this will be bad. We first need a P2P connection for the file transfer. Implement the RFC for P2P transfer.

Other problem: currently there is no implementation for TCP support for ICE in PJSIP. This is mandatory for this point (in PJSIP or homemade)

Ressourcen