* 本篇博客文章于太平洋时间上午 11:45 更新。旨在澄清此处描述的用例仅为概念验证和个人项目。为了清晰说明问题,已更新某些章节。
Matrix 是去中心化、端到端加密通信的黄金标准。它为全球的政府通信系统、开源社区以及注重隐私的组织提供支持。
然而,对于个人开发人员来说,其吸引力往往更贴近自身功能:将分散的聊天网络(例如 Discord 和 Slack)整合到统一的收件箱中,或者只是确保对话历史记录保存在用户控制的基础设施上。从功能上看,Matrix 作为去中心化、最终一致的状态机运行。家庭服务器不使用中央服务器推送更新,而是通过 HTTP 来交换签名的 JSON 事件,以及使用冲突解决算法将这些数据流合并到房间历史记录的统一视图。
但是,运行 Matrix 需要付出“代价”。过去,运行 Matrix 家庭服务器意味着需要承担繁重的运维工作。用户必须配置虚拟专用服务器 (VPS),调优 PostgreSQL 来处理繁重的写入负载,管理用于缓存的 Redis,配置反向代理,以及处理 TLS 证书的轮换。它有如一头有状态的重量级庞然大物,无论使用频率高低,都需要投入时间和金钱。
我们想了解能否彻底消除这种代价。
剧透一下:我们做到了。在这篇文章中,我们将解释 Cloudflare 如何将 Matrix 家庭服务器移植到 Cloudflare Workers。最终的概念验证成果是一个无服务器架构,这个架构几乎无需运维操作,空闲时成本降至零,并且默认情况下每个连接都受到后量子加密技术的保护。用户可以查看源代码并直接从 Github 部署实例。
我们的切入点是 Synapse,这是基于 Python 的参考 Matrix 家庭服务器,专为传统部署而设计。它使用 PostgreSQL 进行持久化存储,Redis 进行缓存,文件系统用于存储媒体。
将它移植到 Workers 意味着质疑我们过去视为理所当然的每一个存储假设。
挑战在于存储。传统的家庭服务器认为,通过中央 SQL 数据库实现强一致性。Cloudflare Durable Objects 提供强大的替代方案。这个基本组件可提供解析 Matrix 状态所需的强一致性与原子性,并仍然支持应用在边缘运行。
我们使用 Hono 框架,将 Matrix 协议的核心逻辑(事件授权、房间状态解析、加密验证)移植到 TypeScript 中。D1 取代 PostgreSQL;KV 取代 Redis;R2 取代文件系统;Durable Objects 则负责处理实时协调。
映射的工作原理如下:
迁移到 Cloudflare Workers 为开发人员带来诸多优势:部署简单、降低成本、降低延迟和内置安全性。
轻松部署:传统的 Matrix 部署需要配置服务器、管理 PostgreSQL、管理 Redis 集群、更新 TLS 证书、配置负载平衡器、监测基础设施以及轮班值守。
使用 Workers,部署变得非常简单:只需运行 wrangler deploy 命令即可。Workers 会处理 TLS 证书、负载平衡、DDoS 防护和全球分发。
按使用量计费:传统的家庭服务器无论是否有人使用,都需要付费。Workers 采用基于请求的定价,因此,用户只需在使用时付费,而当所有用户均处于睡眠状态时,成本几乎降至零。
降低全球范围内的延迟:位于 us-east-1 的传统 Matrix 家庭服务器会为亚洲或欧洲用户增加 200 毫秒以上的延迟。与此同时,Workers 在全球 300 多个地点运行。当位于东京的用户发送消息时,Worker 会在东京执行。
内置安全性:Matrix 家庭服务器可能成为高价值攻击目标:因为它们负责处理加密通信、存储消息历史记录,以及验证用户身份。传统部署需要精心加固:防火墙配置、速率限制、DDoS 缓解、WAF 规则、IP 信誉过滤。
Workers 默认提供所有这些功能。
2022 年 10 月,Cloudflare 在所有 TLS 1.3 连接中部署了后量子混合密钥协议。每个与 Cloudflare Workers 的连接都会自动协商 X25519MLKEM768,这是结合了经典 X25519 与 ML-KEM(由 NIST 标准化的后量子算法)的混合密钥交换算法。
经典密码学依赖于传统计算机无法解决的数学难题来保证安全性,但运行 Shor 算法的量子计算机却可以轻而易举地解决此类问题。ML-KEM 基于点阵问题,即使是量子计算机也仍然难以解决这些问题。混合密钥意味着只有当两种算法都失效时,才会破坏连接。
理解在哪里执行加密对于安全架构而言至关重要。当有人通过我们的家庭服务器发送消息时,消息的实际传输路径如下所述:
发送者的客户端接收明文消息,并使用 Matrix 的端到端加密算法 Megolm 进行加密。然后,已加密的有效负载会被封装在 TLS 中进行传输。在 Cloudflare 平台中,该 TLS 连接使用 X25519MLKEM768 算法,使其能够抵御量子攻击。
Worker 会终止 TLS,但它接收到的仍然是加密信息,即 Megolm 密文。Cloudflare 将该密文存储在 D1 中,按房间和时间戳对其进行索引,然后将其传递给接收者。但我们永远不会看到明文。“Hello, world”消息仅存在于发送者和接收者的设备上。
当接收者进行同步时,流程步骤则相反。他们通过另一个可抵御量子攻击的 TLS 连接来接收已加密的有效负载,然后使用 Megolm 会话密钥在本地解密。
通过两个独立运行的加密层提供保护:
传输层 (TLS) 保护传输中的数据。数据在客户端加密,然后在 Cloudflare 边缘解密。现在,使用 X25519MLKEM768 算法,传输层能够抵御量子攻击。
应用层 (Megolm E2EE) 保护消息内容。这些内容在发送者设备上加密,并且仅在接收者设备上解密。这使用了经典的 Curve25519 加密技术。
任何 Matrix 家庭服务器运营商(无论是在 VPS 上运行 Synapse 还是在 Workers 上运行这项实施)都可以看到元数据:存在哪些房间,房间里有哪些用户,何时发送了消息。但基础设施链中的任何人都无法看到消息内容,因为端到端加密 (E2EE) 有效负载在到达网络之前已经在发送者设备上加密。Cloudflare 会终止 TLS 连接并将请求传递到 Worker,但两者都只能看到 Megolm 密文。加密房间中的媒体在上传之前会在客户端完成加密,而且私钥永远不会离开用户设备。
在传统的 Matrix 部署中实现后量子 TLS 需要执行以下步骤:将 OpenSSL 或 BoringSSL 升级为支持 ML-KEM 的版本,正确配置密码套件首选项,测试所有 Matrix 应用的客户端兼容性,监测 TLS 协商失败,随着 PQC 标准的演变维持及时更新,以及妥善处理不支持 PQC 的客户端。
如果使用 Workers,上述步骤均自动完成。Chrome、Firefox 和 Edge 都支持 X25519MLKEM768 密钥交换算法。使用平台 TLS 堆栈的移动应用继承了这种支持。随着 Cloudflare PQC 部署的扩展,安全态势有所改善,无需采取任何行动。
移植 Tuwunel 的关键在于,需要确保不同数据之间的一致性。我们充分利用 Cloudflare 的每一个基本组件。
D1 会存储需要在重启后依旧存在且支持查询的所有内容:用户、房间、事件、设备密钥。超过 25 个表格涵盖了完整的 Matrix 数据模型。
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
);
D1 以 SQLite 为基础,也就是说,可以通过最小的改动来移植 Tuwunel 的查询。连接、索引和聚合均能正常发挥作用。
我们吸取了一个惨痛的教训:D1 的最终一致性会打破外键约束。当后续写入事件检查外键时,写入房间的操作可能不可见。因此,我们移除了所有外键,并在应用代码中强制执行引用完整性。
OAuth 授权代码的有效期为 10 分钟,而刷新令牌的有效期为整个会话。
// Store OAuth code with 10-minute TTL
kv.put(&format!("oauth_code:{}", code), &token_data)?
.expiration_ttl(600)
.execute()
.await?;
KV 的全球分布意味着,无论用户身处何地,OAuth 流程都能快速运行。
Matrix 媒体直接映射到 R2,从而让用户上传图像,获取内容寻址的 URL,而且数据出口免费。
某些操作无法容忍最终一致性。当客户端声明一次性加密密钥时,必须以原子方式移除该密钥。如果两个客户端声明相同的密钥,则加密会话建立失败。
Durable Objects 提供单线程、强一致性存储:
#[durable_object]
pub struct UserKeysObject {
state: State,
env: Env,
}
impl UserKeysObject {
async fn claim_otk(&self, algorithm: &str) -> Result<Option<Key>> {
// Atomic within single DO - no race conditions possible
let mut keys: Vec<Key> = self.state.storage()
.get("one_time_keys")
.await
.ok()
.flatten()
.unwrap_or_default();
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)
}
}
我们使用 UserKeysObject 进行 E2EE 密钥管理,使用 RoomObject 处理实时房间事件(例如输入指示器和已读回执),以及使用 UsersyncObject 管理到设备的消息队列。其余消息则流经 D1。
我们的实施支持完整的 Matrix E2EE 堆栈:设备密钥、交叉签名密钥、一次性密钥、备用密钥、密钥备份和脱水设备。
现代 Matrix 客户端使用 OAuth 2.0/OIDC,而不是传统的密码流程。我们实施了一个完整的 OAuth 提供程序,支持动态客户端注册、PKCE 授权、RS256 签名 JWT 令牌、轮换令牌刷新,以及标准 OIDC 发现端点。
curl https://matrix.example.com/.well-known/openid-configuration
{
"issuer": "https://matrix.example.com",
"authorization_endpoint": "https://matrix.example.com/oauth/authorize",
"token_endpoint": "https://matrix.example.com/oauth/token",
"jwks_uri": "https://matrix.example.com/.well-known/jwks.json"
}
将 Element 或任何 Matrix 客户端连接到特定域,它会自动发现所有内容。
传统的 Matrix 同步会在初始连接时传输数兆字节的数据,从而消耗移动设备的电池电量和流量套餐。
Sliding Sync 让客户端可以请求获取其所需的内容。客户端无需下载所有内容,而是获取最近 20 个状态最小化的房间。随着用户滚动浏览,他们可以请求更多范围。服务器会跟踪用户位置,并仅发送房间状态的增量数据。
与边缘执行相结合,即使在网络速度较慢的情况下,移动客户端也能在 500 毫秒内完成连接并渲染房间列表。
对于服务小型团队的家庭服务器,二者之间的对比如下所述:
| 传统 (VPS) | Workers |
|---|
每月费用(闲置) | 20-50 美元 | 不足 1 美元 |
每月费用(活跃) | 20-50 美元 | 3-10 美元 |
全局延迟 | 100-300 毫秒 | 20-50 毫秒 |
部署时间 | 小时 | 秒 |
维护 | 每周 | 无 |
DDoS 防护 | 附加费用 | 已包含 |
后量子 TLS | 复杂的设置 | 自动 |
*根据截至 2026 年 1 月 15 日,DigitalOcean、AWS Lightsail 与 Linode 发布的公开费率和指标。
如果规模化部署,经济效益将进一步提高。传统部署需要容量规划和超额配置。Workers 可以自动扩展。
我们最初将此作为一个实验:Matrix 能否在 Workers 上运行?答案是“能”,而且这种方法也适用于其他有状态协议。
通过将传统的有状态组件映射到 Cloudflare 的基本组件,例如 Postgres 到 D1、Redis 到 KV、mutex 到 Durable Objects,我们可以看到,复杂的应用并不需要复杂的基础设施。我们剥离了操作系统、数据库管理和网络配置,只保留了应用程序逻辑和数据本身。
Workers 让用户拥有数据主权,而无需承担维护基础设施的负担。
我们一直在尝试不同的实施方式,也期待对这类服务感兴趣的其他人踊跃提出建议和做出贡献。
是否准备好在 Workers 上构建强大的实时应用?立即开始使用 Cloudflare Workers 并探索 Durable Objects,构建自定义的有状态边缘应用。欢迎加入我们的 Discord 社区,与在边缘端进行开发的其他开发人员联系和交流。