跳转到内容

matrix-rust-sdk — Matrix 客户端的"共享发动机"

是什么

matrix-rust-sdk 是 Matrix 协议官方的 Rust 客户端 SDK。Matrix 是一个开源的去中心化聊天协议(类似 Slack/WhatsApp,但任何人都能跑自己的服务器,服务器之间互通)。这个 SDK 提供给”想做 Matrix 客户端”的人。

日常类比:像汽车厂的共享发动机。Element 公司原来给 iOS、Android、Web 三个客户端各做了一个发动机,三套代码维护不动;现在用 Rust 写一份发动机,再给 Swift / Kotlin / JS 装上不同的方向盘和外壳——都用同一颗心。

你用它写一个最小客户端(伪代码):

use matrix_sdk::Client;
let client = Client::builder().homeserver_url("https://matrix.org").build().await?;
client.matrix_auth().login_username("alice", "pw").send().await?;
client.sync(Default::default()).await?; // 后台一直拉新消息

它是 Element X(Element 公司新一代 iOS/Android 客户端)的底层心脏,也是社区客户端 Fractal(GTK)、iamb(终端)的依赖。

为什么重要

不理解这个项目,下面这些事都看不清:

  • 为什么 Element X 启动比老 Element 快 6 倍——因为老版每端各自实现状态机,新版共用 Rust 编译的核心
  • 为什么”加密聊天”在每个客户端都对——Olm/Megolm 加密只在 Rust 层实现一次,三端共享,不会出现”iOS 解密对、Android 解密错”
  • 为什么 2020 年后做 Matrix 客户端的人几乎都不再用 matrix-js-sdk——多语言 binding 让 Rust 这份成了上游事实标准
  • Rust 在”跨平台共享逻辑”这条路上的代表作之一,FFI 绑定的工程经验值得抄

核心要点

SDK 把客户端逻辑切成 四层 crate

  1. matrix-sdk-base:协议状态机。读懂服务器返回的 JSON,维护”我加入了哪些房间、每个房间最新消息是什么”。没有网络——纯输入纯输出。

  2. matrix-sdk-crypto:加密状态机。Olm(双人对话密钥交换)+ Megolm(群聊密钥分发)。也没有网络,输入是别人的密钥包,输出是要发出去的加密消息。

  3. matrix-sdk:把上面两个加上 HTTP 客户端、同步循环、事件订阅。这是大多数应用应该依赖的层。

  4. matrix-sdk-ui:再往上一层,提供 RoomList(房间列表抽象)、Timeline(时间线抽象)。Element X 就吃这层,UI 只负责画。

外面再裹一层 FFI binding:UniFFI 把 Rust API 暴露成 Swift/Kotlin,wasm-bindgen 暴露成 JS。一份核心,多端调用。

关键设计:核心层无 I/O。base 和 crypto 都是”纯函数 + 状态”,方便测试,也方便机器人这种”我自己管网络”的场景独立用 crypto。

实践案例

案例 1:Element X iOS——把 RoomList 直接当数据源

let roomList = try await client.roomListService().allRooms()
// SwiftUI 里把 roomList 当 @ObservedObject,房间一变 UI 自动刷

iOS 工程师不用写 sync 循环、不用解析 JSON、不用管加密。matrix-sdk-ui 的 RoomList 已经是”按时间倒序、按未读状态分组”的 Swift 可用对象。

案例 2:Rust 终端客户端 iamb——只用中层

iamb 是 Vim 风格的 Matrix 终端客户端,用 matrix-sdk(不用 ui 层)。它自己用 ratatui 画界面,订阅 SDK 的 sync 事件流,按自己节奏渲染。

let mut sync_stream = client.sync_stream(...).await;
while let Some(response) = sync_stream.next().await {
// 自己决定怎么把 response 翻译到 TUI
}

案例 3:纯加密机器人——只用 matrix-sdk-crypto

use matrix_sdk_crypto::OlmMachine;
let machine = OlmMachine::new(user_id, device_id).await;
// 外部网络层把别人发来的加密事件喂进来
machine.receive_sync_changes(...).await?;
let plaintext = machine.decrypt_room_event(&event, &room_id).await?;

这种姿势没有 HTTP 客户端、没有 sync 循环。适合”我已经有 IM 协议栈,只想接 Matrix 的 E2EE 加密能力”。

踩过的坑

  1. 公共 API 只有三个 cratematrix-sdk / matrix-sdk-ui / matrix-sdk-crypto。其它(matrix-sdk-basematrix-sdk-commonmatrix-sdk-store-encryption 等)随时改,不要直接写进 Cargo.toml 否则升级 SDK 全炸。

  2. UniFFI 不直传 async:Rust 这边是 async fn,到 Swift/Kotlin 经 UniFFI 转成回调或同步阻塞。Element X 团队又自己包了一层 Swift await 把回调转回 async,FFI 边界看着干净背后绕了一圈。

  3. sliding sync 服务端尚不稳定:sliding sync(MSC3575)能让”打开 app 立刻看到第一屏”——只先拉可视范围的房间。但需要专门的 sliding sync 代理(Synapse 的实验功能或独立 proxy),不是所有 homeserver 都支持,老 /sync 仍要保留兼容路径。

  4. 加密 store 必须持久化:crypto store 存所有会话密钥,掉了等于历史消息全部解不开。SDK 默认用 SQLite,但升级 schema 时要做迁移;加密 store 也不能简单 cp 复制(同设备 ID 多实例会让 Megolm 出乱)。

  5. 跨设备 verification 是流程不是函数。要做”扫二维码 / SAS 短串验证”得自己驱动 SDK 的 verification 状态机,不是一次 verify() 调用就结束。

  6. Tokio 运行时绑死:SDK 内部用 tokio 跑异步,集成 async-std 或单线程运行时不友好。FFI 层 binding 也是基于 Tokio 多线程模型设计。

适用 vs 不适用场景

适用

  • 做 Matrix 客户端(iOS/Android/Web/Desktop/TUI 都行)
  • 做 Matrix 机器人/桥接器,需要完整客户端能力(含 E2EE)
  • 想抄”Rust + UniFFI + wasm-bindgen 共享核心”的工程模式

不适用

  • 做 Matrix 服务端——服务端用 Synapse(Python)或 Dendrite(Go),matrix-rust-sdk 是客户端方向
  • 做”只发明文消息的极简机器人”——直接 HTTP 调 Client-Server API 更简单
  • 做完全自定义协议——SDK 强绑定 Matrix 协议本身

历史小故事(可跳过)

  • 2014 年:Matrix 协议诞生于 Element 公司前身(Amdocs Unified Communications)。早期客户端只有 JS(matrix-js-sdk)。
  • 2016-2018 年:Element 给 iOS(Swift)/Android(Kotlin)/JS 各做一份 SDK,三份代码各自实现状态机,bug 不一致。
  • 2020 年:Element 启动”Rust 化”,把客户端核心移到 matrix-rust-sdk,先给 Element Desktop 用。
  • 2022-2023 年:Element X 发布——iOS 和 Android 改成”薄壳套 Rust 核心”,启动速度大幅提升,加密 bug 在三端一次修完。
  • 2024-2026 年:社区 Fractal/iamb 切到 Rust SDK。matrix-js-sdk 仍在维护但优先级下降。

之后做 Matrix 客户端的事实模板就是”Rust 核心 + 平台层壳”。

学到什么

  1. 共享核心 + 多语言壳是 Rust 在客户端的杀手用法——一份逻辑给三个端用,省 60-70% 维护成本
  2. 协议状态机和 I/O 解耦——base/crypto 都是纯函数,让测试和裁剪都方便
  3. UniFFI 是 Rust → 移动端的桥——比手写 JNI/Swift bridge 省一个数量级的胶水代码
  4. 公共 API 范围要划清楚——SDK 故意只暴露三个 crate,避免社区”误用内部细节”导致升级灾难

延伸阅读

关联

  • synapse —— Matrix 服务端,matrix-rust-sdk 客户端连的就是它
  • dendrite —— 另一个 Matrix 服务端,协议另一头
  • diffie-hellman —— Olm/Megolm 加密底层用的密钥交换数学
  • actix-web —— Rust web 框架,sliding sync proxy 也用类似栈
  • axum —— Rust web 框架,机器人/桥接器常用来做 webhook 接入

一句话总结

一份 Rust 核心,三端共享——这是 matrix-rust-sdk 留给整个 Matrix 生态最直接的工程礼物。

反向链接

  • actix-web —— Actix Web — Rust 上长期占据 TechEmpower 榜首的 web 框架
  • axum —— axum — 用 Rust 类型系统当『路由参数表』的 Web 框架
  • dendrite —— Dendrite — Go 写的第二代 Matrix homeserver,组件可拆可合
  • diffie-hellman —— Diffie-Hellman 密钥交换
  • element-android —— Element Android — Matrix 协议官方 Android 客户端(Kotlin + Realm)
  • element-web —— Element Web — Matrix 协议旗舰 web 客户端(React + matrix-js-sdk)
  • embedded-hal —— embedded-hal — 让同一份驱动代码跑在任意芯片上
  • flutter-rust-bridge —— flutter-rust-bridge — Dart 调 Rust 像调本地函数
  • matrix-js-sdk —— matrix-js-sdk — Matrix Web/Node 端的”老大哥”客户端 SDK
  • mumble —— Mumble — 游戏圈用了 20 年的低延迟开源语音
  • signal-android —— Signal Android — 让 Android 上的每条消息都只有两端能看见
  • signal-ios —— Signal iOS — 让 iPhone 上的每条消息都只有两端能看见
  • signal-server —— Signal-Server — 服务端看不到任何明文的即时通信后端
  • synapse —— Synapse — Matrix 协议的参考 homeserver,让聊天像电邮一样能跨服务器互通
  • tauri —— Tauri — Rust 写的 Electron 替代,用系统 webview 打包桌面/移动端应用
  • vodozemac —— vodozemac — Matrix 端到端加密的 Rust 内核