El grupo

Sinóspis

El objetivo de este documento es describir cómo se implementarán los chats grupales (también conocido como camaradas de grupo) en Jami.

Un swarm es un grupo capaz de discutir sin ninguna autoridad central de una manera resistente. De hecho, si dos personas no tienen conectividad con el resto del grupo (es decir, apagón de Internet) pero pueden comunicarse entre sí (en una LAN por ejemplo o en una subred), podrán enviar mensajes entre sí y luego, podrán sincronizarse con el resto del grupo cuando sea posible.

Así que, el swarm se define por:

  1. Capacidad para dividirse y fusionarse siguiendo la conectividad.

  2. Sincronización de la historia. Cualquiera debe ser capaz de enviar un mensaje a todo el grupo.

  3. No hay autoridad central, no puede confiar en ningún servidor.

  4. Los dispositivos deben poder verificar la validez de los mensajes antiguos y reproducir todo el historial.

  5. El almacenamiento es gestionado por el dispositivo.

La idea principal es conseguir un árbol de Merkle sincronizado con los participantes.

Identificamos cuatro modos para el chat enjambre que queremos implementar:

  • Uno a uno, básicamente el caso que tenemos hoy cuando hablas con un amigo

  • ADMIN_INVITES_ONLY generalmente una clase donde el profesor puede invitar a la gente, pero no a los estudiantes

  • INVITAS_SOLO un grupo privado de amigos

  • PÚBLICO básicamente un foro abierto

Escenarios

Crear un grupo

Bob quiere crear un nuevo enjambre

  1. Bob crea un repositorio de git local.

  2. Luego, crea un compromiso inicial firmado con lo siguiente:

    • Su llave pública en /admins

    • Su certificado de dispositivo en ̀ /devices`

    • Su RCA en ̀ /crls`

  3. El hash del primer compromiso se convierte en el ID de la conversación

  4. Bob anuncia a sus otros dispositivos que crea una nueva conversación. Esto se hace a través de una invitación para unirse al enjambre enviado a través del DHT a otros dispositivos vinculados a esa cuenta.

Añadir a alguien

Alice agrega a Bob

  1. Alice añade a Bob al reporte:

    • Añade la URI invitada en /invited

    • Añade la RLC a /crls

  2. Alice envía una solicitud sobre el DHT

Recibir una invitación

Alice recibe la invitación de unirse al enjambre creado anteriormente

  1. Ella acepta la invitación (si declina, no hace nada, se quedará en invitado y Alice nunca recibirá ningún mensaje)

  2. Una conexión entre pares entre Alice y Bob está terminada.

  3. Alice saca el repo de Bob. ** AVISO esto significa que los mensajes necesitan una conexión, no de la DHT como hoy**

  4. Alice valida los compromisos de Bob

  5. Para validar que Alice es miembro, elimina la invitación del directorio /invited, luego añade su certificado en el directorio /members

  6. Una vez que todos los compromisos se validan y en su dispositivo, otros miembros del grupo son descubiertos por Alice. con estos compañeros, ella construirá el DRT (explicado a continuación) con Bob como una banda de arranque.

Enviando un mensaje

Alice envía un mensaje

Enviar un mensaje es bastante simple. Alice escribe un mensaje de compromiso en el siguiente formato:

{
    "type": "text/plain",
    "body": "coucou"
}

y añade su dispositivo y CRL al repositorio si faltan (otros deben poder verificar el compromiso). Se evitan conflictos de fusión porque se basan principalmente en mensajes de compromiso, no en archivos (a menos que se encuentren certificados CRLS + pero están localizados).

Para pingar otros dispositivos, el remitente envía a otros miembros un mensaje SIP con mimetype = «aplicación/im-gitmessage-id» que contiene un JSON con el «deviceId» que envía el mensaje, el «id» de la conversación relacionada, y el «compromiso»

Recibir un mensaje

Bob recibe el mensaje de Alice

  1. Bob hace un tiro en Alice

  2. Los compromisos deben verificarse mediante un gancho

  3. Si todos los comités son válidos, los comités se almacenan y se muestran.

  4. If all commits are not valid, pull is canceled. Alice must reestablish her state to a correct state.

Validación de un compromiso

Para evitar que los usuarios empujen algunos compromisos no deseados (con conflictos, mensajes falsos, etc.), así es como cada compromiso (desde el más antiguo al más nuevo) DEBE ser validado antes de fusionar una rama remota:

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 Note2: If a fetch is too big, it’s not merged

  • Para cada compromiso, compruebe si el dispositivo que intenta enviar el compromiso está autorizado en este momento y si los certificados están presentes (en los dispositivos del dispositivo y en los miembros o administradores del emisor).

  • El comit tiene 2 padres, así que es una fusión, nada más para validar aquí

  • El compromiso tiene 0 padres, es el compromiso inicial:

    • Compruebe que se ha añadido el certificado de administración

    • Compruebe que el certificado de dispositivo se ha añadido

    • Se añadieron los LCR de verificación

    • Compruebe si no se ha añadido otro archivo

  • El commit tiene 1 padre, el mensaje de commit es un JSON con un tipo:

    • Si el texto (o otro tipo de mime que no cambia archivos)

      • Verificación de la firma del certificado en el repo

      • Compruebe que no se añade ningún archivo extraño fuera del certificado del dispositivo ni se elimina

    • Si se vota

      • Compruebe que voteType es compatible (prohibición, desprohibición)

      • Compruebe que el voto es para el usuario que firma el compromiso

      • Compruebe que el voto es de un administrador y dispositivo presente y no prohibido

      • Compruebe que no se añade o elimina ningún archivo extraño

    • Si el miembro

      • Si se suma

        • Compruebe que el compromiso está firmado correctamente

        • Verifique si el certificado se ha añadido en / invitado

        • Compruebe que no se añade o elimina ningún archivo extraño

        • Si ONE_TO_ONE, comprobar que sólo tenemos un administrador, un miembro

        • Si AdMIN_INVITES_ONLY, comprobar que la invitación es de un administrador

      • Si se une

        • Compruebe que el compromiso está firmado correctamente

        • Compruebe que el dispositivo se ha añadido

        • Compruebe si la invitación se transfiere a los miembros

        • Compruebe que no se añade o elimina ningún archivo extraño

      • Si está prohibido

        • Compruebe si el voto es válido

        • Compruebe que el usuario está prohibido a través de un administrador

        • Verifique si el certificado de miembro o dispositivo se ha cambiado a prohibido.

        • Compruebe que sólo se eliminan archivos relacionados con la votación

        • Compruebe que no se añade o elimina ningún archivo extraño

    • Notificar al usuario que puede estar con una versión antigua o que el compañero intentó enviar compromisos no deseados

Prohíbe un dispositivo

Alice, Bob, Carla, Denys están en un enjambre.

Este es uno de los escenarios más difíciles en nuestro contexto.

  1. Las fichas temporales de los compromisos generados

  2. Conflicto con dispositivos prohibidos. Si hay varios dispositivos de administración y si Alice puede hablar con Bob pero no con Denys y Carla; Carla puede hablar con Denys; Denys prohíbe a Alice, Alice prohíbe a Denys, ¿cuál será el estado cuando los 4 miembros fusionen las conversaciones?

  3. Un dispositivo puede ser comprometido, robado o su certificado puede expirar. Debemos poder prohibir un dispositivo y evitar que mienta sobre su vencimiento o envíe mensajes en el pasado (cambiando su certificado o el sello de tiempo de su compromiso).

Los sistemas similares (con sistemas de grupos distribuidos) no son mucho, pero estos son algunos ejemplos:

  • [mpOTR no define cómo prohibir a alguien]

  • La señal, sin ningún servidor central para el chat de grupo (EDIT: recientemente cambiaron ese punto), no da la capacidad de prohibir a alguien de un grupo.

Este sistema de votación necesita una acción humana para prohibir a alguien o debe basarse en la información de los RLC del repositorio (porque no podemos confiar en los RLC externos)

Retirar un dispositivo de una conversación

Esta es la única parte que DEBE tener un consenso para evitar la división de la conversación, como si dos miembros se patean de la conversación, ¿qué verá el tercero?

Esto es necesario para detectar dispositivos revocados, o simplemente evitar que personas no deseadas estén presentes en una sala pública.

Alice elimina a Bob

Nota: Alice debe ser administradora para votar

  • Para ello, crea el archivo en /votes/ban/members/uri_bob/uri_alice (los miembros pueden ser reemplazados por dispositivos para un dispositivo, o invitados para invitaciones o administradores para administradores) y compromete

  • Luego comprueba si el voto está resuelto. Esto significa que >50% de los administradores están de acuerdo en prohibir a Bob (si ella está sola, seguramente es más del 50%).

  • Si se resuelve la votación, los archivos en /votes/ban se pueden eliminar, todos los archivos de Bob en /membres, /admins, /invitados, /CRLs, /dispositivos se pueden eliminar (o sólo en /dispositivos si es un dispositivo que está prohibido) y el certificado de Bob se puede colocar en /banned/membres/bob_uri.crt (o /banned/devices/uri.crt si un dispositivo está prohibido) y comprometido con el repo

  • Luego, Alice informa a otros usuarios (fuera de Bob)

*Alice (administrador) añade a Bob (miembro prohibido)

  • Para ello, crea el archivo en /votes/unban/members/uri_bob/uri_alice (los miembros pueden ser reemplazados por dispositivos para un dispositivo, o invitados para invitaciones o administradores para administradores) y compromete a

  • Luego comprueba si el voto está resuelto. Esto significa que >50% de los administradores están de acuerdo en prohibir a Bob (si ella está sola, seguramente es más del 50%).

  • Si se resuelve la votación, los archivos en /votes/unban pueden ser eliminados, todos los archivos para Bob en /membres, /admins, /invitados, /CRLs, pueden ser re-agregados (o sólo en /dispositivos si es un dispositivo que no está prohibido) y comprometidos con el repo

Eliminar una conversación

  1. Guardar en convInfos removed=time::now() (como eliminarContact guarda en contactos) que la conversación se elimina y se sincroniza con los dispositivos de otros usuarios

  2. Ahora, si se recibe un nuevo compromiso para esta conversación se ignora

  3. Ahora, si Jami está en marcha y el repo todavía está presente, la conversación no se anuncia a los clientes

  4. Dos casos: a. Si no hay otro miembro en la conversación podemos eliminar inmediatamente el repositorio b. Si todavía hay otros miembros, comprométese a que abandonemos la conversación, y ahora espere que al menos otro dispositivo sincronice este mensaje. Esto evita el hecho de que otros miembros aún detectarán al usuario como un miembro válido y aún envíe nuevas notificaciones de mensaje.

  5. Cuando estamos seguros de que alguien está sincronizado, eliminar borrado=time::now() y sincronizar con los dispositivos de otros usuarios

  6. Todos los dispositivos propiedad del usuario ahora pueden borrar el repositorio y archivos relacionados

Cómo especificar un modo

Los modos no se pueden cambiar a través del tiempo. o es otra conversación. Así que, estos datos se almacenan en el mensaje de compromiso inicial. El mensaje de compromiso será el siguiente:

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

Por ahora, «modo» acepta valores 0 (ONE_TO_ONE), 1 (ADMIN_INVITES_ONLY), 2 (INVITES_ONLY), 3 (PUBLIC)

Proceso para enjambres 1:1

El objetivo aquí es mantener la vieja API (addContact/removeContact, sendTrustRequest/acceptTrustRequest/discardTrustRequest) para generar enjambre con un par y su contacto.

El proceso sigue siendo el mismo, una cuenta puede agregar un contacto a través de addContact, luego enviar una solicitud de confianza a través de la DHT. Pero dos cambios son necesarios:

  1. La solicitud de confianza incorpora un «conversaciónId» para informar al compañero de la conversación que clonar al aceptar la solicitud

  2. TrustRequest se vuelve a intentar cuando el contacto vuelve a la red. No es el caso hoy (ya que no queremos generar un nuevo TrustRequest si el compañero descarta el primero).

Luego, cuando un contacto acepta la solicitud, es necesario un período de sincronización, porque el contacto ahora necesita clonar la conversación.

RemoveContact() eliminará el contacto y las conversaciones 1:1 relacionadas (con el mismo proceso que «Remove a conversation»). La única nota aquí es que si prohíbemos un contacto, no esperamos a la sincronización, simplemente eliminamos todos los archivos relacionados.

Escenarios complicados

Hay algunos casos en los que se pueden crear dos conversaciones.

  1. Alice añade a Bob

  2. Bob acepta

  3. Alice se quita de Bob

  4. Alice añade a Bob

o

1, Alice añade Bob y Bob añade Alice al mismo tiempo, pero ambos no están conectados juntos

En este caso, se generan dos conversaciones. No queremos eliminar mensajes de los usuarios o elegir una conversación aquí. Así que, a veces se mostrará dos enjambre 1: 1 entre los mismos miembros. Generará algunos errores durante el tiempo de transición (como no queremos romper API, la conversación inferida será una de las dos conversaciones mostradas, pero por ahora es «ok-ish», se solucionará cuando los clientes manejen completamente la conversación ID para todas las API (llamadas, transferencia de archivos, etc.).

Nota durante la sincronización

Después de aceptar la solicitud de una conversación, hay un tiempo en que el daemon necesita recuperar el repositorio distante. Durante este tiempo, los clientes MUSTEN mostrar una vista de sincronización para dar información al usuario.

  • ConfigurationManager::getConversations() devolverá el ID de la conversación incluso mientras se sincroniza

  • ConfigurationManager::conversationInfos() devolverá {{«sincronización»: «verdadero»}} si se está sincronizando.

  • ConfigurationManager::getConversationMembres() devolverá un mapa de dos URI (la cuenta corriente y el compañero que envió la solicitud)

Las conversaciones solicitan especificación

Las solicitudes de conversaciones se representan con un Map<String, String> con las siguientes teclas:

  • ID: el ID de conversación

  • de: uri del remitente

  • Recibida: sello de tiempo

  • Título: (opcional) nombre de la conversación

  • Descripción: (opcional)

  • avatar: (opcional)

Sincronización de perfil de conversación

Para ser identificable, una conversación generalmente necesita algunos metadatos, como un título (por ejemplo, Jami), una descripción (por ejemplo, algunos enlaces, qué es el proyecto, etc.), y una imagen (el logotipo del proyecto).

Almacenamiento en el depósito

El perfil de la conversación se almacena en un archivo vCard clásico en la raíz (/profile.vcf) como:

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

Sincronización

Para actualizar el vCard, un usuario con suficientes permisos (por defecto: =ADMIN) necesita editar /profile.vcf. y comprometerá el archivo con el tipo de aplicación /update-profile. El nuevo mensaje se envía a través del mismo mecanismo y todos los compañeros recibirán la señal MessageReceived del demonio. La rama se elimina si el commit contiene otros archivos o es demasiado grande o si lo hace un miembro no autorizado (por defecto: <ADMIN).

Última vez mostrada

En los datos sincronizados, cada dispositivo envía a otros dispositivos el estado de las conversaciones. En este estado, se envía el último mostrado. Sin embargo, debido a que cada dispositivo puede tener su propio estado para cada conversación, y probablemente sin el mismo último compromiso en algún momento, hay varios escenarios que tener en cuenta:

Se apoyan cinco escenarios:

  • Si la última muestra enviada por otros dispositivos es la misma que la actual, no hay nada que hacer.

  • si no se muestra la última en el dispositivo actual, se utilizará el mensaje de visualización remota.

  • si el remoto último mostrado no está presente en el repo, significa que el commit se buscará más tarde, por lo que el resultado se almacenará en caché

  • si el control remoto ya está recogido, comprobamos que el local último mostrado es antes en la historia para reemplazarlo

  • Finalmente, si se anuncia un mensaje del mismo autor, significa que necesitamos actualizar el último que se muestra.

Preferencias

Cada conversación tiene adjuntadas preferencias establecidas por el usuario. Estas preferencias se sincronizan a través de los dispositivos del usuario. Este puede ser el color de la conversación, si el usuario quiere ignorar las notificaciones, el límite de tamaño de transferencia de archivos, etc. Por ahora, las claves reconocidas son:

  • «color» - el color de la conversación (formato #RRGGBB)

  • «ignore Notifications» - para ignorar las notificaciones de nuevos mensajes en esta conversación

  • «símbolo» - para definir un emoji predeterminado.

Estas preferencias se almacenan en un paquete MapStringString, almacenado en accountDir/conversation_data/conversationId/preferencias y sólo se envían a través de dispositivos del mismo usuario a través de SyncMsg.

Las API para interactuar con las preferencias son:

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

Gestión de conflictos de fusiones

Debido a que dos administradores pueden cambiar la descripción al mismo tiempo, puede ocurrir un conflicto de fusión en profile.vcf. En este caso, se elegirá el commit con el hash más alto (por ejemplo ffffff > 000000).

Las API

El usuario tiene 2 métodos para obtener y configurar los metadatos de conversación:

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

donde infos es un map<str, str> con las siguientes teclas:

  • Modo: Sólo para leer

  • título

  • Descripción

  • avatar

Protocolos utilizados

Git

¿Por qué esta elección

Cada conversación será un repositorio de git.

  1. Necesitamos sincronizar y ordenar mensajes. El Árbol de Merkle es la estructura perfecta para hacerlo y puede ser linearizado por la fusión de ramas. Además, debido a que es masivamente utilizado por Git, es fácil sincronizar entre dispositivos.

  2. Distribuido por la naturaleza, utilizado masivamente, muchos backends y enchufables.

  3. Puede verificar compromisos a través de ganchos y criptomonedas masivamente utilizadas

  4. Puede almacenarse en una base de datos si es necesario

  5. Los conflictos se evitan mediante el uso de mensajes de compromiso, no archivos.

Lo que tenemos que validar

  • ¿Puede ser bajo el rendimiento?

  • Los ganchos en libgit2

  • ¿Múltiples tiros al mismo tiempo?

Los límites

Para eliminar una conversación, el dispositivo tiene que dejar la conversación y crear otra.

Sin embargo, los mensajes no permanentes (como los mensajes que se pueden leer solo durante algunos minutos) pueden enviarse a través de un mensaje especial a través del DRT (como las notificaciones de mecanografía o lectura).

Estructura

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

Transferencia de archivos

Swarm cambia masivamente la transferencia de archivos. Ahora, todo el historial se sincroniza, permitiendo que todos los dispositivos de la conversación recuperen fácilmente archivos antiguos. Estos cambios nos permiten pasar de una lógica en la que el remitente empujó el archivo en otros dispositivos, a través de tratar de conectarse a sus dispositivos (esto fue malo porque no era realmente resistente a los cambios de conexión / fallos y necesitaba un retiro manual) a una lógica en la que el remitente permite que otros dispositivos descarguen.

Protocolo

El remitente agrega un nuevo compromiso en la conversación con el siguiente formato:

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

y crea un enlace en ${data_path}/conversation_data/${conversation_id}/${file_id} donde file_id=${commitid}_${value["tiempo"]}.${extension}

Luego, el receptor puede descargar los archivos poniéndose en contacto con los dispositivos que alojan el archivo abriendo un canal con name="data-transfer://" + conversationId + "/" + currentDeviceId() + "/" + fileId y almacenar la información que el archivo está esperando en ${data_path}/conversation_data/${conversation_id}/waiting

El dispositivo que recibe la conexión aceptará el canal verificando si el archivo puede ser enviado (si sha3sum es correcto y si el archivo existe). El receptor mantendrá el primer canal abierto, cerrará los demás y escribirá en un archivo (con el mismo camino que el remitente: ${data_path}/conversation_data/${conversation_id}/${file_id}) todos los datos entrantes.

Cuando la transferencia se termina o el canal se cierra, la sha3sum se verifica para validar que el archivo es correcto (o se elimina).

En caso de fallo, cuando un dispositivo de la conversación vuelva a estar en línea, pediremos todos los archivos de espera de la misma manera.

Llamen a un enjambre

La idea

Una conversación en enjambre puede tener múltiples citas.

«accountUri/deviceId/conversationId/confId» donde cuentaUri/deviceId describe al host.

El anfitrión puede determinarse de dos maneras:

  • En el enjambre de metadatos, donde se almacena como el título/desc/avatar de la habitación

  • O el primer llamador.

Al iniciar una llamada, el anfitrión añadirá un nuevo compromiso al enjambre, con el URI para unirse (accountUri/deviceId/conversationId/confId). Esto será válido hasta el final de la llamada (anunciado por un compromiso con la duración que se muestra)

Así que cada parte recibirá la información de que una llamada ha comenzado y podrá unirse a ella llamándola.

¿Los ataques?

  • Evite las bombas de git

Las notas

La timestamp de un commit puede ser de confianza porque es editable. Sólo la timestamp del usuario puede ser de confianza.

TLS

Las operaciones de Git, los mensajes de control, los archivos y otras cosas usarán un enlace TLS v1.3 p2p con sólo cifrados que garantizan PFS. Así que cada clave se renegocia para cada nueva conexión.

DHT (udp)

Se utiliza para enviar mensajes para móviles (para activar notificaciones push) e iniciar conexiones TCP.

Actividad de la red

Proceso para invitar a alguien

Alice quiere invitar a Bob:

  1. Alice añade a Bob a una conversación

  2. Alice genera una invitación: { «application/invite+json» : { «conversationId»: «$id», «miembros»: [{…}] }}

  3. Dos posibilidades para enviar el mensaje a. Si no está conectado, a través de la DHT b. De otro modo, Alice envía en el canal SIP

  4. Bob a. Recibe la invitación, se emite una señal para el cliente b. No está conectado, por lo que nunca recibirá la solicitud porque Alice no debe saber si Bob simplemente ignoró o bloqueó a Alice.

Proceso para enviar un mensaje a alguien

Alice quiere enviar un mensaje a Bob:

  1. Alice añade un mensaje en el repo, dando una identificación

  2. Alice recibe un mensaje recibido (de ella misma) si tiene éxito

  3. En ambos casos se crea un mensaje: { «aplicación/im-gitmessage-id» : «{«id»:»$convId», «commit»:»$commitId», «deviceId»: «$alice_device_hash»}»}.

  4. Bob no está conectado a Alice, así que si confía en ella, pida una nueva conexión y vaya a b. b. Si está conectado, traiga de Alice y anuncie nuevos mensajes c. Bob no conoce esa conversación. Pida a través de la DHT que obtenga una invitación primero para poder aceptar esa conversación ({«aplicación/invite», conversaciónId}) d. Bob está desconectado (ninguna red, o simplemente cerrado).

Aplicación

¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡

Mensajes compatibles

Mensaje inicial

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

Representa el primer commit de un repositorio y contiene el modo:

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

e invitado si el modo = 0.

Mensaje de texto

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

O para una edición:

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

Llamadas

Mostrar el final de una llamada (duración en milisegundos):

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

O para alojar una llamada en un grupo (cuando comienza)

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

Un segundo commit con el mismo JSON + duration se añade al final de la llamada cuando se aloja.

Agregar un archivo

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

totalSize está en bits,

Actualizando perfil

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

Acto de miembro

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

Cuando un miembro es invitado, se une o se va o es expulsado de una conversación

Votar acto

Generado por administradores para añadir un voto para expulsar o no expulsar a alguien.

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

¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡

Nota: Las siguientes notas no están organizadas todavía.

Mejoras en las criptomonedas.

Para una función de chat de grupo seria, también necesitamos criptografía seria. Con el diseño actual, si un certificado es robado como los valores DHT anteriores de una conversación, la conversación puede ser descifrada. Tal vez necesitamos ir a algo como ** Double ratchet**.

Note: a lib might exist to implement group conversations.

Necesita apoyo de ECC en OpenDHT

Uso

¿Añadir papeles?

Hay dos casos de uso principales para las charlas grupales:

  1. Algo como un Mattermost en una empresa, con canales privados, y algunos roles (admin/espectador/bot/etc) o para la educación (donde sólo unos pocos están activos).

  2. Las conversaciones horizontales son como una conversación entre amigos.

¿Para quién será Jami?

Ideas de ejecución

Un certificado para un grupo que firma un usuario con una bandera para un papel.

Únete a una conversación

  • Sólo por invitación directa

  • Por medio de un enlace/Código QR/cualquier

  • ¿A través de un nombre de habitación?

Lo que necesitamos

  • Confidencialidad: los miembros fuera del chat de grupo no deben poder leer mensajes en el grupo

  • Secreto de entrada: si alguna clave del grupo es comprometida, los mensajes anteriores deben permanecer confidenciales (al máximo posible)

  • Ordenación de mensajes: Es necesario tener mensajes en el orden correcto

  • Sincronización: También es necesario asegurarse de tener todos los mensajes lo antes posible.

  • Persistencia: En realidad, un mensaje en el DHT dura solo 10 minutos. Porque es el mejor momento calculado para este tipo de DHT. Para persistir en datos, el nodo debe volver a colocar el valor en el DHT cada 10 minutos. Otra forma de hacer cuando el nodo está fuera de línea es dejar que los nodos vuelvan a colocar los datos. Pero, si después de 10 minutos, 8 nodos siguen aquí, harán 64 solicitudes (y es exponencial). La forma actual de evitar el spamming para eso es consultada. Esto todavía hará 64 solicitudes pero limitará la redundancia máxima a 8 nodos.

Otras formas distribuidas

  • Necesitamos una investigación.

  • Necesitamos una investigación.

  • Necesitamos una investigación.

Basándonos en el trabajo actual que tenemos

El chat en grupo puede basarse en el mismo trabajo que ya tenemos para múltiples dispositivos (pero aquí, con un certificado de grupo).

  1. Esto necesita mover la base de datos del cliente al demonio.

  2. Si nadie está conectado, la sincronización no se puede hacer, y la persona nunca verá la conversación

Otro DHT dedicado

Como un DHT con un superusuario.

Transferencia de archivos

Actualmente, el algoritmo de transferencia de archivos se basa en una conexión TURN (ver Transferencia de archivos). En el caso de un grupo grande, esto será malo. Primero necesitamos un implementador p2p para la transferencia de archivos. Implementar el RFC para la transferencia p2p.

Otro problema: actualmente no existe una implementación de soporte TCP para ICE en PJSIP. Esto es obligatorio para este punto (en pjsip o casero)

Recursos

  • El proyecto de ley de la Unión Europea (UE) no se ha aprobado.

  • Sincronización distribuida robusta de sistemas lineares en red con información intermitente (Sean Phillips y Ricardo G. Sanfelice)