Вышла первая версия Kiyeovo — децентрализованного P2P-мессенджера для десктопа. Автор потратил кучу времени на групповые чаты: именно здесь отсутствие сервера бьёт больнее всего.
С сервером всё просто. Он отвечает, кто в группе, какой ключ шифрует сообщения, в каком порядке они приходят и что пропустили офлайн-пользователи. Даже Signal и Matrix с E2E-шифрованием используют сервер для координации: хранят состав группы, порядок событий и пересылают сообщения. Без сервера нет «места», которое точно знает список участников, нет «секвенсора» для порядка и нет круглосуточного почтового ящика.
Автор рассмотрел существующие подходы и отверг почти все. Полноценный сервер — централизация, а это не подходит. MLS (RFC 9420) требует Delivery Service для обмена ключами, что сложно натянуть на DHT с офлайн-пирами, плюс дерево ключей окупается только в больших группах, а в Kiyeovo лимит — 10 человек. Лидерлесс-модели (когда любой может кикать) ломаются при одновременных изменениях: без общего порядка событий нельзя понять, кого когда исключили. Proof-of-work выглядел слишком тяжёлым. Фиксированный состав (как в Briar) упростил бы всё, но автору нужны были приглашения и исключения.
Итоговое решение жёсткое, зато сходимость тривиальна. У каждой группы один создатель с ключом Ed25519. Только он может приглашать и исключать. Участники шлют подписанный запрос на выход, создатель применяет удаление. Проблема: создатель — единая точка отказа. Если он потеряет устройство или просто офлайн, добавлять и удалять участников нельзя (существующие могут общаться на текущем ключе).
Шифрование: XChaCha20-Poly1305 с ротацией ключей при каждом изменении состава. Эпоха — версия группы с одним 32-байтовым симметричным ключом. При добавлении новый участник получает ключ только для своей эпохи, старые чаты он не прочитает. Имя топика в libp2p выводится из ключа группы, поэтому исключённый не может даже вычислить новый топик.
Метаданные группы хранятся в DHT как append-only лог: изменяемый указатель на последнюю версию и цепочка версий, подписанных создателем. Это даёт офлайн-участникам верифицируемую историю.
Порядок сообщений и офлайн-доставка организованы через per-sender buckets. Каждый отправитель пишет в свой собственный bucket для текущей эпохи. При возвращении в сеть пользователь сканирует все buckets по группам, участникам и не закрытым эпохам, чтобы собрать пропущенные сообщения. Альтернативы (per-recipient или общий bucket) автор посчитал худшими из-за сложности записи и конфликтов.
Главные компромиссы: единая власть создателя (single point of failure), sender keys вместо MLS (нет ратчета внутри эпохи, но для групп до 10 человек нормально), обёртка ключей через ту же пару Ed25519, что и в личных сообщениях, и best-effort порядок без синхронизации часов.