← 返回信息流
AI 资讯Hacker News·1 小时前

去中心化系统如何设计群聊功能

原标题:How should group chats work in decentralized systems?

速览

本文讨论在去中心化系统中,群聊功能应如何设计才能兼顾隐私、效率与去中心化特性。文章分析了现有集中式群聊的缺陷,提出去中心化群聊可能面临的身份验证、消息排序、共识机制等关键问题,并展望了可能的解决方案。这一话题对分布式社交网络和区块链应用具有参考价值。

AI 深度解读

背景

在去中心化系统中构建群聊功能,远比在中心化服务器上困难得多。中心化服务器天然拥有对群组状态的权威视野:它知道谁在群里、消息的顺序如何、离线期间发生了哪些变化,甚至可以协调端到端加密(E2E)中的密钥分发。Signal、Matrix 等应用尽管保护了消息内容不可读,但依然依赖服务器来维护群组成员列表、排序事件和存储离线消息。

一旦移除服务器,就没有一个“绝对可信的场所”来回答这些问题。作者在开发去中心化 P2P 桌面即时通讯应用 Kiyeovo 时深刻体会到了这一点。在无服务器架构下,群聊的设计必须接受一系列权衡,没有“最佳”方案,只能根据目标做出选择。

核心内容

1. 为什么服务器让群聊变简单

中心化服务器可以统一回答以下关键问题:

  • 当前群组中有哪些成员?
  • 用什么密钥加密消息,谁应该持有它?
  • 消息发生的顺序是什么?
  • 离线期间我错过了什么?

即使是在端到端加密应用中,服务器仍然承担着这些协调职责。例如,Signal 的服务器存储群组名册并转发消息,Matrix 的 homeserver 存储群组状态并排列事件。新增成员会获得完整的群状态,被移除的成员则停止接收更新和消息。内容虽加密,但协调(谁在群里、最新状态、顺序、离线消息)完全由中心化实体完成。

2. 其他去中心化方案的分析与取舍

作者评估了多种可行方案,并逐一指出了它们的缺陷。

(a) 保留协调服务器(Signal、WhatsApp、Matrix 模式)

内容 E2E 加密,但服务器(或 homeserver)依然持有群组所有元数据。密钥方案通常采用 Sender Keys(Signal)或 Megolm(Matrix),每次成员变更时轮转密钥。

弃用原因:包含中心化节点,违背了无服务器的核心要求——网络必须没有“特权点”也能正常工作。

(b) MLS / TreeKEM(RFC 9420)

MLS 是目前最先进的群组加密协议,使用密钥树结构,增删成员只需影响部分树节点,即使上千人的大群也能保持高效。

弃用原因:MLS 需要一个“分发服务”(Delivery Service)来传递密钥变更消息。在无服务器、通过 DHT 和 P2P 网络、且节点可能离线的环境下实现这一服务非常复杂。此外,密钥树在大群中才有优势,而 Kiyeovo 的群组上限仅为 10 人。

(c) 无领导成员管理

允许任何成员添加或移除其他成员,最终通过某种合并逻辑收敛。权力完全去中心化,没有管理员。

弃用原因:当两人同时修改成员时,不同节点会对“谁在群里”产生不同认知。虽然可以最终合并,但合并规则会变得极其复杂。在没有可信时钟的情况下,无法回答顺序问题(例如“X 被踢出发生在 X 发送该消息之前还是之后?”)。考虑过使用工作量证明层来排序,但过于沉重。

(d) 创建时固定成员(如 Briar 的私人群)

一些 P2P 应用完全避开动态成员问题:群组创建后成员固定,无法添加或移除。

弃用原因:最初作者也曾考虑此方案,但无法邀请或踢人是一个重大缺点。尽管作者注意到大多数实际群组很少变更成员,但最终他选择实现了动态成员功能并成功运行。

3. Kiyeovo 的实际实现

设计原则:无服务器,保证收敛简单。

3.1 成员管理:单一创建者

每个群组只有一个创建者,由 Ed25519 密钥标识。所有其他成员持有创建者的公钥,用于验证更新确实来自创建者。只有创建者可以邀请和踢人。希望离开的成员发送签名离开请求,由创建者执行移除。

  • 优点:不存在“两个管理员在分区期间踢了不同的人”需要合并的情况——成员状态只有一个合法写入者,收敛极为简单。
  • 缺点:创建者是单点故障。如果创建者丢失设备(身份)或离线,现有成员仍可使用当前密钥继续聊天,但无法增加或移除成员(用户可以自行离开并不再接收消息,但其他成员不会收到验证后的离开通知)。

3.2 密钥:按 epoch 轮转的发送者密钥

一个 epoch 代表“群组的一个版本”,每个 epoch 有一个随机的 32 字节对称密钥供整个群组共享。消息使用 XChaCha20-Poly1305 加密。为了分发新密钥,创建者将密钥用每个成员的公钥加密(公钥加密),封装在签名的控制消息中,发送到各成员的私有收件箱。

  • 优点:重新密钥只需 O(n) 个封装副本。群组上限 10 人,非常轻量。
  • 代价:稳定群组内不会有跨 epoch 的密钥轮转,因此缺乏连续前向保密。

3.3 密钥轮转:成员变更触发新 epoch

加入、离开、踢人都会创建新的 epoch。新成员只能收到加入后那个 epoch 的密钥,无法阅读更早的历史。

在 libp2p 中,topic(主题)类似于“频道”。群组实时消息就是通过 topic 发送的。Topic 名称由群组密钥导出,因此轮转密钥同时也轮转了 topic。被移除的成员无法计算出新 topic,更无法订阅。

3.4 群组状态分发:DHT 中的追加日志

群组元数据(成员表、每个发送者的序列边界等)存储在 DHT 中,包含两种记录:

  • 一个可变的“最新版本”指针
  • 一条由不可变、哈希链式链接的版本记录链——一个追加日志,每条记录由创建者签名,并链接到前一条记录的哈希。

负载(成员更新)使用前述 epoch 密钥加密。

  • 优点:离线成员可以获得群组“演进”的可验证历史。DHT 验证器会拒绝任何不推进群组状态或非创建者签名的记录。
  • 代价:采用“最后写入胜出”策略,由于只有一个写入者,这才安全。

3.5 排序与离线消息:每个发送者的序列号与桶

每个发送者针对每个(群组,topic/epoch)维护一个序列号——表示“这是我的第 N 条消息”。接收者据此检测某个发送者的缺失消息。

当用户离线时,他的消息存储在 DHT 中。但存储不是按接收者划分的桶,而是按发送者划分的桶——这不太常见。每条消息写入发送者自己的桶(对应当前 epoch)。当用户上线时,他必须扫描所有群组 × 成员 × 未关闭的 epoch 的桶,才能获取所有离线消息。

为什么选择这种设计?替代方案更差:

  • 按接收者划分桶:发送消息时,发送方需要写入 群组数 × 成员数 × 未关闭的 epoch 数 个桶,而且还要跟踪每个发送者的未关闭 epoch,带来另一套问题和低效。
  • 共享桶:问题更多,此处未展开。

关键要点

  • 核心矛盾:无服务器群聊必须在完全去中心化与功能简单性之间做出权衡,没有通用最优解。
  • 成员管理:采用单一创建者模式(而非无领导或多个管理员)保证了状态收敛的确定性,代价是创建者成为单点故障。
  • 密钥轮转:基于 epoch 的对称密钥轮转,轻量且满足基本的前向保密(仅当成员变更时),但不提供稳定群组内的连续前向保密。
  • 群组状态存储:利用 DHT 的追加日志结构,由创建者签名、哈希链式链接,使得离线成员可验证群组的完整演进历史。
  • 消息排序与离线消息:采用每个发送者的独立序列号,并按发送者桶存储离线消息,避免了按接收者桶的高写入开销。
  • 群组大小限制:由于当前设计假定群组上限 10 人,因此可以接受 O(n) 的密钥分发复杂度;不适用于大规模群组。
  • 安全性:通过 topic 随密钥轮转,被移除成员无法计算新 topic,从而无法接收
查看原文 →marindedic.com