Matrix 是一个去中心化的即时通讯协议,在隐私保护和去中心化领域算是公认的标杆。很多政府机构、开源社区和注重隐私的组织都在用它。
对于个人开发者来说,用 Matrix 的理由往往更加朴素:把 Discord、Slack 这些分散的聊天工具桥接到一个收件箱,或者单纯想让自己的聊天记录跑在自己控制的服务器上。
但运行一个 Matrix 服务器,一直有一个难以回避的"税"要交。
原文链接:https://blog.cloudflare.com/serverless-matrix-homeserver-workers/
传统部署:一头永远饥渴的怪兽
传统的 Matrix 服务端(比如官方的 Synapse)需要:
- 一台 VPS,或者自己的服务器
- PostgreSQL,专门为大量写入做调优
- Redis,用于缓存
- 反向代理(Nginx/Caddy 等)
- TLS 证书的申请和定期续期
- 监控告警体系
- 随时待命处理故障
这套东西不只是搭起来麻烦,更麻烦的是它一直在烧钱。不管有没有人在用,这台服务器都在运行,都在计费。
Cloudflare 的一位工程师想验证一件事:能不能把这个负担彻底消掉?
结论是:可以。
一次迁移,把所有基础设施换掉
迁移的起点是 Synapse——Matrix 官方的 Python 参考实现。整个移植过程的核心工作,是把传统架构里每一个有状态的组件,换成 Cloudflare 边缘平台上对应的原语。
对应关系如下:
| 传统组件 | Cloudflare 替代 | 用途 |
|---|---|---|
| PostgreSQL | D1 | 持久化存储,用户/房间/消息等 |
| Redis | KV | 缓存和短生命周期状态 |
| 文件系统 | R2 | 媒体文件存储 |
| 互斥锁/分布式锁 | Durable Objects | 强一致性的原子操作 |
协议核心逻辑——事件授权、房间状态解析、密码学验证——用 TypeScript 加 Hono 框架重写,跑在 Workers 上。
部署方式从原来的一堆运维操作,变成了一条命令:
wrangler deployTLS、负载均衡、DDoS 防护、全球分发,全部由平台处理,不需要做任何配置。
一个意外收获:后量子加密,自动生效
Cloudflare 在 2022 年把后量子混合密钥协商部署到了所有 TLS 1.3 连接上。这意味着,任何运行在 Workers 上的服务,所有连接都自动使用X25519MLKEM768——一种结合了经典 X25519 和后量子算法 ML-KEM 的混合方案。
这里先解释几个概念:
为什么需要后量子加密?
当前主流的非对称加密依赖的数学难题(比如大数分解),对量子计算机来说并不难。理论上,足够强大的量子计算机运行 Shor’s 算法,可以破解现在广泛使用的 RSA 和 ECC 加密。ML-KEM 基于格问题(Lattice Problem),目前被认为对量子计算机也是困难的。
混合方案的逻辑
X25519MLKEM768 同时用了经典算法和后量子算法。两者必须同时被破解,连接才会被攻破。这是一种稳健的过渡方案——在后量子算法被充分验证之前,经典算法仍然作为保底。
用在 Matrix 上意味着什么?
Matrix 本身支持端对端加密(E2EE),消息内容在离开发送方设备之前已经被加密,服务器只存储密文,看不到明文内容。
整个加密链路分为两层,彼此独立:
- 传输层(TLS):保护数据在网络上传输的安全,在客户端加密,在 Cloudflare 边缘解密。Workers 上这层现在是后量子的。
- 应用层(Megolm E2EE):保护消息内容本身,在发送方设备加密,只有接收方设备能解密。服务器拿到的始终是密文。
也就是说,即使有人截获了传输中的数据包,也只能看到已经被 Megolm 加密过的密文。即使服务器被入侵,也看不到任何消息内容。
如果想在传统 Matrix 部署上达到同样的后量子保护,需要手动升级 OpenSSL 或 BoringSSL、配置密码套件偏好、测试各客户端的兼容性,并持续跟进 PQC 标准演进。在 Workers 上,这些都是默认行为,不需要任何操作。
存储架构的几个关键决策
D1:用 SQLite 建整个数据模型
D1 是 Cloudflare 基于 SQLite 的分布式数据库。Matrix 的数据模型——用户、房间、事件、设备密钥——全部存在 D1 里,超过 25 张表。
事件表的结构大致如下:
CREATETABLEevents(event_idTEXTPRIMARYKEY,room_idTEXTNOTNULL,senderTEXTNOTNULL,event_typeTEXTNOTNULL,state_keyTEXT,contentTEXTNOTNULL,origin_server_tsINTEGERNOTNULL,depthINTEGERNOTNULL);迁移过程中踩了一个坑:D1 的最终一致性破坏了外键约束。对 rooms 表的写入,可能在后续对 events 表做外键检查时还不可见,导致约束失败。解决方案是删掉所有外键,在应用层代码里自行保证引用完整性。
KV:处理有过期时间的短生命周期数据
OAuth 授权码只需要存活 10 分钟,refresh token 存活一个会话。这类数据完全不需要强一致性,KV 的 TTL 功能天然适合:
// 存储 OAuth code,10 分钟后自动过期 kv.put("oauth_code:{code}", token_data) .expiration_ttl(600)KV 的全球分布还意味着 OAuth 流程无论用户在哪里,响应都很快。
Durable Objects:处理不能有并发问题的操作
Matrix 的端对端加密依赖一次性密钥(One-Time Key)。每个密钥只能被一个客户端认领一次。如果两个客户端同时认领了同一个密钥,加密会话的建立就会失败。
这种操作不能容忍任何并发冲突,必须是原子的。Durable Objects 提供单线程、强一致性的存储,天然解决了这个问题:
asyncfnclaim_otk(&self,algorithm:&str)->Result<Option<Key>>{// 在单个 DO 内是原子的,不可能有竞争条件letmutkeys=self.state.storage().get("one_time_keys").await...;ifletSome(idx)=keys.iter().position(|k|k.algorithm==algorithm){letkey=keys.remove(idx);self.state.storage().put("one_time_keys",&keys).await?;returnOk(Some(key));}Ok(None)}除了密钥管理,Durable Objects 还用于处理打字指示、已读回执等实时房间事件,以及用户间的消息队列。其余数据流经 D1。
两个附加能力
Sliding Sync:移动端不再苦等
传统 Matrix 同步,初次连接会下载大量数据,严重消耗移动端的流量和电量。Sliding Sync 允许客户端只请求需要的内容——比如最近 20 个房间的最小状态集。用户往下滚动时,再请求更多数据。
结合边缘执行,移动客户端连接并渲染房间列表的时间,即使在慢速网络下也能控制在 500ms 以内。
完整的 OAuth 2.0/OIDC 支持
现代 Matrix 客户端使用 OAuth 2.0/OIDC 代替老式的密码登录。这个实现包含了完整的 OAuth 提供方:动态客户端注册、PKCE 授权、RS256 签名的 JWT Token、Token 轮换刷新,以及标准的 OIDC 发现端点。把 Element 或任何 Matrix 客户端指向这个域名,会自动发现所有配置,无需手动设置。
数字对比
对于一个服务小团队的 Matrix 服务器:
| 传统 VPS 部署 | Workers | |
|---|---|---|
| 月费(闲置) | $20-50 | <$1 |
| 月费(活跃使用) | $20-50 | $3-10 |
| 全球延迟 | 100-300ms | 20-50ms |
| 部署时间 | 数小时 | 数秒 |
| 日常维护 | 每周 | 几乎为零 |
| DDoS 防护 | 额外付费 | 默认包含 |
| 后量子 TLS | 复杂手动配置 | 自动生效 |
规模越大,传统部署的劣势越明显——它需要提前做容量规划、预留冗余资源。Workers 自动扩缩,不需要这些。
这件事背后更大的意义
这个项目最初是一个实验性的个人项目,但它揭示了一种可以推广的工程思路。
Matrix 不是一个简单的应用。它是一个有状态的、对一致性有明确要求的分布式协议,带有复杂的密码学验证逻辑。如果它能在 Serverless 上跑,意味着很多同类协议也可以。
核心映射关系是通用的:把中心化数据库换成 D1,把缓存换成 KV,把互斥锁换成 Durable Objects,把文件存储换成 R2。这套思路适用于任何需要从传统有状态架构向边缘迁移的复杂应用。
结果是:你保留了对自己数据的完全控制权,但不再需要承担对基础设施的运维责任。这两件事过去是捆绑在一起的,现在可以分开了。
源代码已开源,可以一键部署到自己的 Cloudflare 账户:
https://github.com/nkuntz1934/matrix-workers