COMPAX 与 MATRIX
一个区别,
以及由它引出的一切
Matrix 是成熟的联邦式即时通讯,而拿这两者作比较的文字,大多出自没用过这两份代码里任何一份的人。下面这些是从两边都读出来的。
测量于 2026 年 8 月 27 日。Synapse:246,976 行 Python——光存储层就有 71,349 行。Continuwuity,同一网络的第二个实现,95,220 行 Rust。本服务器:17,041 行。两边都在动,下面有一半说的是今天,而不是事情的本性;属于事情本性的那些地方都标了出来。
分岔点
谁说了算:
什么是一个房间
- 一个房间是事件构成的 DAG,每个事件都带着自己的授权链。
- 服务器之间意见不一致时,由 state resolution v2 定夺。
- 于是有了状态组、前向和后向端点、outlier、backfill——光存储层就是 71,349 行。
- 即使网络断成两半,commit 也会在本地被接受,之后再合并。
- 服务器是一个投递服务。只有一个次序,由房间的归属地持有。
- 比对纪元,在同一把锁下追加。没有什么可分岔的。
- 所以没有 DAG,没有 state resolution,没有 backfill。
- 而一个 commit 需要往归属地跑一个来回,碰上竞争就会拿到一个
409。
这笔交换在两个方向上都是诚实的,而 MLS 在这件事上不留选择:应用一个 commit 会摧毁上一个纪元的秘密。没有可以回滚到的地方。
一个值得说准确的说法
「他们的服务器读你的消息」
在两个方向上都是错的
在一个加密的 Matrix 房间里,消息正文是密文,服务器读不到——Megolm,正如这里的 MLS。真正成立的说法更窄,而且对大多数人来说更糟。
- 加密是房间上的一个开关。公开房间通常没有开,那里的一切都可读。
- 房间状态从不加密:名称、话题、头像、成员、邀请、power levels——哪怕在加密房间里也是明文。
- 这不是疏忽。服务器要对事件施加授权规则,所以它必须能读到它们。
- 谁和谁在同一个房间里,谁邀请了谁,什么时候——对他们的服务器永远可见。
- 在这里,这些活在 MLS 树里。服务器看到的是一个
group_id和一个纪元。 - 还有一个容易忽略的后果:加密房间里的服务端搜索在他们那儿根本不能用。Element 是在本地搜的——所以在搜索这一项上,这里落后的是 Element,不是 Matrix。
各自把力气花在哪里
力气花在哪
- 熬过网络分裂,因为 DAG 事后会合并。
- 十二个房间版本,在运行中的网络上发布并迁移完毕。
- 规模在实战中得到验证:stream writer、实例映射、Redis 复制。
- 纵深的治理手段——管理 API、房间 ACL、媒体隔离、一套 MSC 流程。
- 第三方实现由 Complement 当作黑盒来检验。
- 丢光所有设备之后仍能恢复,靠的是服务端密钥备份。
- 露在外面的是一个
group_id和一个纪元,而排序需要的就这些。 - 身份不是服务器签发的:一把根密钥签署设备证明,每个人在本地各自核验。
- 排序只花一行——一次 compare-and-set,在一个房间里实测每秒 239 次追加。
- 推送什么都不带。他们的带着一个
room_id,所以房间还是到了推送服务商手里。 - 通话继承它所在的房间:媒体密钥来自纪元秘密,所以移除一个成员,顺带就把他从媒体里移了出去。
- 机器人是树上的叶子,不是绕经服务器的岔路——不存在哪里为了机器人而搁着明文。
- 投诉是可以证明的:一份消息证明能指出作者。Megolm 是一把群密钥,什么都证明不了。
- 群体共同持有的钱,而这在 Matrix 里根本不存在。
后果
每种协议
做不到什么
这不是记分牌。每一行都是它上面那个选择的代价,而两栏里都有各自那一方宁可没有的东西。
Matrix
Compax
加密房间里的消息正文
密文。Megolm——服务器读不到。
密文。MLS——一样。
加密有没有开
按房间的一个开关,m.room.encryption。公开房间通常没开,那里服务器什么都读得到。
不是开关。不存在一个未加密的房间可以待。
房间状态——名称、话题、成员、邀请、power levels
明文,始终如此,哪怕在加密房间里。这不是疏忽:服务器要施加授权规则,所以它必须读。
在 MLS 树里。服务器看到的是 group_id 和纪元。
谁和谁在同一个房间里
对服务器可见,始终如此。
不可见。真正的区别是这个,不是消息正文。
加密房间里的服务端搜索
根本不能用——加密事件没有可以建索引的 content.body。Element 在本地搜。
同样不能用,而且是同一个原因。出路和 Element 走的是同一条:把索引放在客户端。
网络分裂期间被接受的 commit
可以。事件在本地被接受,事后由 state resolution 合并。
不可能。应用一个 commit 会摧毁上一个纪元的秘密——同一个纪元里的两个 commit,就是两套互不相容的密钥状态。
丢光所有设备之后的历史记录
在恢复密钥之下的服务端密钥备份——它同样是端到端的。
没了。一个仅凭助记词就能打开的封套,会是谁也撤销不了的永久访问权;这是故意拒绝掉的。
由服务器执行的房间规则
有——power levels,服务器会拒绝违反它们的事件。
没有。策略跑在影子里:它统计分歧,什么也不拒绝,因为一个被拒绝的有效 commit 不是弄坏一个 MLS 群,而是终结它。
链接预览、按内容治理、推送里带文字
只要房间没加密就都可以。
不可能。这不是「还没做」——这是买下其余一切所付的代价。
群体持有的钱
不属于协议的一部分。
五条链,每位签名人的密钥在自己的设备上,付款以一条消息的形式抵达。
以及两边相同的地方
两边一样的东西
要长久的用 Postgres,要快的用 Redis。按动作分的令牌桶。第二台设备上的历史记录——他们那边是密钥备份,这边是封好的窗口和一次二维码传输。对已死的对端重试时退避等待。把许多条记录打包进一个请求。版本通过 HTTP 公布。屏蔽某个服务商。接收时去重。附件放在事件日志之外。Prometheus 和告警规则。以及加密房间里都没有服务端搜索——两边都没有。
接下来
最先冒出来的
那些问题
十六个,直截了当地回答——包括那两个答案并不好听的。