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