COMPAX

COMPAX と MATRIX

一つの違いと、
そこから出てくること

Matrix は成熟した連合型メッセンジャーであり、両者を比べて書かれたものの大半は、どちらのコードも使ったことのない人によって書かれています。以下は、その両方を読んで書きました。

2026 年 8 月 27 日に計測。Synapse は Python 246,976 行 — うち保存層だけで 71,349 行。Continuwuity、同じネットワークの二つ目の実装は Rust 95,220 行。このサーバーは 17,041 行。どちらも動き続けていますし、以下の半分は物事の性質ではなく今日についての話です。性質の話である箇所には印をつけてあります。

分かれ道

部屋とは何かを
決めるのは誰か

Matrix — サーバー
  • 部屋はイベントの DAG であり、そのひとつひとつが自分の認可の連鎖を持ちます。
  • サーバーどうしの見解が割れたときは、state resolution v2 が決着をつけます。
  • だから状態グループ、前方と後方の端点、outlier、backfill があり — 保存層だけで 71,349 行になります。
  • ネットワークが割れているときでも commit はローカルで受け入れられ、あとから統合されます。
Compax — MLS の木
  • サーバーは配達係です。順序はひとつだけで、それを持つのは部屋の実家です。
  • エポックを比べ、同じロックの下で追記する。分かれようがありません。
  • だから DAG もなく、state resolution もなく、backfill もありません。
  • そして commit は実家との往復を必要とし、競合すれば 409 が返ります。

この取引はどちらの向きにも正直で、MLS はそこに選択の余地を残しません。commit を適用すると前のエポックの秘密が壊れます。巻き戻す先がないのです。

正確に言うだけの値打ちがある主張

「あちらのサーバーはあなたのメッセージを読む」は
どちらの向きにも誤りです

暗号化された Matrix の部屋では、メッセージの本文は暗号文であり、サーバーはそれを読みません。Megolm、ここでの MLS とまったく同じです。本当のところはもっと狭く、そして多くの人にとってはもっと悪い話です。

実際に開いているもの
  • 暗号化は部屋に付いたスイッチです。公開の部屋はたいてい入っておらず、そこでは何もかもが読めます。
  • 部屋の状態は決して暗号化されません。名前、話題、アイコン、参加者、招待、power levels — 暗号化された部屋の中でも平文です。
  • これは見落としではありません。サーバーはイベントに認可の規則を当てるので、それらを読めなければならないのです。
だから本当の違いはグラフのほうです
  • 誰と誰が同じ部屋にいるか、誰が誰を招いたか、いつか — 相手のサーバーには常に見えています。
  • ここではそれが MLS の木の中に住んでいます。サーバーに見えるのは group_id とエポックだけです。
  • そして見落としやすい帰結がひとつ。暗号化された部屋でのサーバー側検索は、あちらではまったく動きません。Element は手元で検索します — つまり検索に関して、ここが後れているのは Matrix ではなく Element に対してです。

それぞれが力を注いでいる場所

力の向く先

Matrix — 到達範囲と運用
  • ネットワークの分断を生き延びること。あとから DAG が統合されるからです。
  • 十二の部屋バージョンを、稼働中のネットワーク上で出荷し移行してきたこと。
  • 現場で証明された規模。stream writer、インスタンスマップ、Redis のレプリケーション。
  • 深い運営 — 管理 API、部屋の ACL、メディアの隔離、MSC のプロセス。
  • 第三者の実装を Complement がブラックボックスとして検査していること。
  • 端末をすべて失ったあとの復旧。サーバー側の鍵バックアップによるもの。
Compax — サーバーにできないこと
  • 外に出ているのは 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 と警報の規則。そして暗号化された部屋でのサーバー側検索が、どちらの側にもないこと。

つぎに

最初に出てくる
問い

十六の問いに、率直に答えています — 答えが心地よくないほうである二つも含めて。