El grupo
Importante
El código fuente de Jami tiende a usar los términos (un)ban, mientras que la interfaz de usuario usa los términos (un)block.
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:
Capacidad para dividirse y fusionarse siguiendo la conectividad.
Sincronización de la historia. Cualquiera debe ser capaz de enviar un mensaje a todo el grupo.
No hay autoridad central, no puede confiar en ningún servidor.
Los dispositivos deben poder verificar la validez de los mensajes antiguos y reproducir todo el historial.
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
Bob crea un repositorio Git local.
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`
El hash del primer compromiso se convierte en el ID de la conversación
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
Alice añade a Bob al reporte:
Añade la URI invitada en
/invited
Añade la RLC a
/crls
Alice envía una solicitud sobre el DHT
Recibir una invitación
Alice recibe la invitación de unirse al enjambre creado anteriormente
Ella acepta la invitación (si declina, no hace nada, se quedará en invitado y Alice nunca recibirá ningún mensaje)
Una conexión entre pares entre Alice y Bob está terminada.
Alice actualiza el repositorio Git de Bob. ADVERTENCIA esto significa que los mensajes necesitan una conexión, no del DHT como hoy.
Alice valida los compromisos de Bob
Para validar que Alice es miembro, elimina la invitación del directorio
/invited
, luego añade su certificado en el directorio/members
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 agrega su dispositivo y CRL al repositorio si faltan (otros deben poder verificar la confirmación). Los conflictos de fusión se evitan porque nos basamos principalmente en mensajes de confirmación, no en archivos (a menos que se encuentren CRL + certificados). Luego anuncia el nuevo compromiso a través de DIRT con un mensaje de servicio (explicado más adelante) y hace ping al DHT para dispositivos móviles (deben recibir una notificación push).
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
Bob actualizó Git en Alice
Los compromisos deben verificarse mediante un gancho
Si todos los comités son válidos, los comités se almacenan y se muestran.
Si no son válidas todas las confirmaciones, la unión se cancela. Alice ha de restablecer su estado a uno correcto.
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:
Nota
Si la validación falla, se ignora la recuperación y no fusionamos la rama (y eliminamos los datos), y se debe notificar al usuario.
Si una búsqueda es demasiado grande, no se fusiona.
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
Verifique que el voto sea de un administrador y dispositivo presentes y no esté expulsado
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.
Las fichas temporales de los compromisos generados
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?
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
Importante
Alice DEBE ser una 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 (admin) vuelve a agregar a Bob (miembro expulsado)
Si vota por eliminar a Bob de la prohibición. Para hacer eso, crea el archivo en /votes/unban/members/uri_bob/uri_alice (los miembros se pueden reemplazar por dispositivos para un dispositivo, o invitados para invitaciones o administradores para administradores) y confirma
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
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
Ahora, si se recibe un nuevo compromiso para esta conversación se ignora
Ahora, si Jami está en marcha y el repo todavía está presente, la conversación no se anuncia a los clientes
Dos casos: a. Si no hay otro miembro en la conversación podemos eliminar inmediatamente el repositorio b. Si aún quedan otros miembros, comprométase a que abandonemos la conversación y ahora espere a que al menos otro dispositivo sincronice este mensaje. Esto evita el hecho de que otros miembros sigan detectando al usuario como miembro válido y sigan enviando notificaciones de mensajes nuevos.
Cuando estamos seguros de que alguien está sincronizado, eliminar borrado=time::now() y sincronizar con los dispositivos de otros usuarios
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)
Procesos 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:
La solicitud de confianza incorpora un «conversaciónId» para informar al compañero de la conversación que clonar al aceptar la solicitud
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.
Alice agrega a Bob.
Bob acepta.
Alice elimina a Bob.
Alice agrega a Bob.
o
Alice agrega a Bob y Bob agrega a Alice al mismo tiempo, pero ambos no están conectados entre sí.
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.).
Importante
Después de aceptar la solicitud de una conversación, hay un momento en que el demonio necesita recuperar el repositorio distante. Durante este tiempo, los clientes DEBEN mostrar una vista de sincronización para proporcionar información al usuario. Mientras se sincroniza:
ConfigurationManager::getConversations() devolverá el id de la conversación incluso mientras se sincroniza.
ConfigurationManager::conversationInfos() devolverá {{«sincronización»: «verdadero»}} si se está sincronizando.
ConfigurationManager::getConversationMembers() devolverá un mapa de dos URI (la cuenta actual y el par 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: the conversation ID
from: URI of the sender
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
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).
Ú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
Re-importación de una cuenta (enlace/export)
El archivo DEVE contener conversaciónId para poder recuperar conversaciones en nuevos comités después de una re-importación (porque no hay invitación en este punto).
La conversación está ahí, en este caso, el demonio es capaz de volver a clonar esta conversación
La conversaciónId está faltando, por lo que el demonio pide (a través de un mensaje
{{"aplicación/invita", conversaciónId}}
) una nueva invitación que el usuario necesita (re) aceptar
Importante
A conversation can only be retrieved if a contact or another device is there, else it will be lost. There is no magic.
Protocolos utilizados
Git
¿Por qué esta elección
Each conversation will be a Git repository. This choice is motivated by:
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.
Distribuido por la naturaleza, utilizado masivamente, muchos backends y enchufables.
Puede verificar compromisos a través de ganchos y criptomonedas masivamente utilizadas
Puede almacenarse en una base de datos si es necesario
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
A swarm conversation can have multiple rendez-vous. A rendez-vous is defined by the following URI:
«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?
Avoid Git bombs
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:
Alice añade a Bob a una conversación
Alice genera una invitación: { «application/invite+json» : { «conversationId»: «$id», «miembros»: [{…}] }}
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
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:
Alice añade un mensaje en el repo, dando una identificación
Alice recibe un mensaje recibido (de ella misma) si tiene éxito
En ambos casos se crea un mensaje: { «aplicación/im-gitmessage-id» : «{«id»:»\(convId", "commit":"\)commitId», «deviceId»: «$alice_device_hash»}»}.
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": "host URI",
"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": "member URI",
"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": "member URI",
"action": "ban/unban"
}
¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡
Nota
Following notes are not organized yet. Just some line of thoughts.
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**.
Nota
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:
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).
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).
Esto necesita mover la base de datos del cliente al demonio.
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)