COMPAX と MATRIX
一つの違いと、
そこから出てくること
Matrix は成熟した連合型メッセンジャーであり、両者を比べて書かれたものの大半は、どちらのコードも使ったことのない人によって書かれています。以下は、その両方を読んで書きました。
2026 年 8 月 27 日に計測。Synapse は Python 246,976 行 — うち保存層だけで 71,349 行。Continuwuity、同じネットワークの二つ目の実装は Rust 95,220 行。このサーバーは 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 は手元で検索します — つまり検索に関して、ここが後れているのは Matrix ではなく Element に対してです。
それぞれが力を注いでいる場所
力の向く先
- ネットワークの分断を生き延びること。あとから 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。操作ごとのトークンバケツ。二台目の端末での履歴 — あちらは鍵のバックアップ、こちらは封をした窓と QR での受け渡し。死んだ相手への再試行では待ち時間を延ばしていくこと。多数のレコードをひとつの要求にまとめること。版の告知を HTTP で行うこと。事業者を遮断すること。受信時の重複除去。添付をイベントログの外に置くこと。Prometheus と警報の規則。そして暗号化された部屋でのサーバー側検索が、どちらの側にもないこと。
つぎに
最初に出てくる
問い
十六の問いに、率直に答えています — 答えが心地よくないほうである二つも含めて。