人们自建 Matrix 是为了不让某家公司掌握自己的对话记录,这一点确实名副其实。但让人意外的是后来才发现的真相:homeserver 并不是一个恰好能聊天的私人盒子,它是公共网络里的一个复制节点,联邦机制的运作方式更像发布协议,远超大多数新手管理员的预期。
这不是反对自建的理由,而是应该刻意地自建的理由。你能获得的隐私是真实的,但也是具体的:数据托管权转移到你手中,账号不会被别人关闭,法律问题也会落在你自己选择的司法管辖区,而不是某家公司替你选的地方。你得不到的隐私同样具体,几乎全部藏在“消息已加密”和“没人能看出谁在跟谁说话”之间的鸿沟里。本指南会讲清这两方面,再讲决定服务器一年后是否依然健康的运维细节。
自建 homeserver 到底改变了什么
先把不同的威胁分开看,因为 homeserver 能完全解决一部分威胁,部分解决另一部分,还有一部分完全解决不了。下表是这套说辞的诚实版本,值得在你挑硬件之前而不是之后读一遍。
| 你担心的问题 | 自建 homeserver 能解决吗 |
|---|---|
| 某家公司读取你的消息内容 | 端到端加密在私密房间里已经覆盖了这一点——没错,自建之后连这家公司本身也一并去掉了 |
| 某家公司分析你和谁聊、什么时候聊 | 部分解决。你不再把数据喂给同一个中心化运营商,但这份记录现在改为存在你自己的服务器上 |
| 账号被别人关闭或封禁 | 能。这是整件事里最明确的收获,也是讨论最少的一点 |
| 有人对你的数据提出法律请求 | 请求不会消失,只是转移了目标。请求现在会送到你手上,适用你自己选定国家的法律 |
| 其他人摸清你的社交关系图谱 | 解决不了。房间里只要有成员在的每一台服务器,收到的成员数据和你收到的一样多 |
| 把服务器本身的存在藏起来 | 藏不住。联邦机制需要一个公开的名字和一个可达的端口,这跟“隐藏”正好相反 |
最后两行要仔细读,因为期望通常就是在这两点上落空的。如果你的目标是让任何人都无法确认某个服务的存在,Matrix 就选错了工具,onion 隐藏服务才更接近你想要的东西。如果你的目标是数据托管权、控制权和司法管辖权,homeserver 就是一件出色的工具,本指南接下来要讲的就是如何把它运维好。

联邦机制:披着聊天协议外衣的复制协议
这里有个机制能解释大多数人的意外之处。当你的某个用户加入一个托管在别处的房间时,你的服务器并不像邮件客户端那样按需拉取消息,而是作为参与者加入一个分布式事件图,然后拉取并存储一份副本,包括房间的事件、成员情况,以及验证后续内容所需的足够多的状态历史。从那一刻起,你的机器就持有了一份副本,其他每一台参与的服务器也各自持有一份。
这件事的后果是双向的,而且都不太符合直觉。你的用户产生的数据——显示名称、头像、加入和退出记录、时间戳、表情回应——会被复制到房间里有成员的每一台服务器上,并且会一直留在对方的数据库里,无论你之后在自己这边删不删。撤回消息只是向对等服务器发出的一个请求,而不是一道命令。在一群各自独立运营的联邦服务器之间,根本没有真正的“撤回”,把它当成撤回是关于这个协议最常见的误解。
反过来看,加入大型公开房间意味着把别人的历史记录导入到你自己的硬盘里。这就是为什么一台只有三个用户的全新 homeserver 也能背上几十 GB 的数据库:不是因为这三个用户写了多少内容,而是因为他们加入了拥有五万成员、积累多年状态的房间。有意识地选择加入哪些房间,既是隐私决策,也是容量决策。
加密覆盖了什么,又有什么留在明文里
Matrix 用 Megolm 加密消息内容,在私密房间里默认就是开启的。这保护了大家最在意的那部分,而且确实管用——你的服务器存的是它自己都读不了的密文,当服务器是租来的硬件时,这是一个真实且有用的特性。但消息外层的“信封”是另一回事,这中间的落差比大多数介绍文章承认的要大得多。
| 信号 | 是否加密 | 谁能看到 |
|---|---|---|
| 消息文本和文件内容 | 是 | 仅限房间成员中已验证的设备 |
| 谁在房间里,以及每一次加入或退出 | 否 | 该房间里有成员的每一台 homeserver |
| 时间戳、发消息频率、活跃时段 | 否 | 每一台参与联邦的 homeserver |
| 显示名称、头像、在线状态与输入提示 | 否 | 每一台参与联邦的 homeserver |
| 房间名称、主题和头像 | 否 | 每一台参与联邦的 homeserver |
| 附件大小和传输时间 | 否 | 每一台参与联邦的 homeserver |
| 你服务器的域名和 IP 地址 | 否 | 整个联邦网络——这是设计使然 |
实际的理解是:加密保护的是说了什么,联邦机制公开的是谁在什么时候、多频繁地说。对大多数社区来说,这笔交换完全可以接受,坦白本身就是重点所在。但如果你的威胁模型里社交关系图谱本身才是敏感信息,联邦协议在结构上就是错误的形状,任何配置开关都改变不了这一点。
Synapse、Dendrite 还是 Conduit——到底该跑哪个
实际有意义的实现只有三个,选哪个主要是资源上的取舍,而不是理念之争。
- Synapse 是参考实现,用 Python 写成,也是唯一一个所有功能从第一天起就能用的版本。它同时也是最耗资源的:内存占用会随着用户加入的房间数量和规模增长,繁忙的服务器迟早需要拆分成 worker 进程。如果你需要 spaces、审核工具、bridge 和管理 API 完全按文档所述工作,就选它。
- Dendrite 是用 Go 重写的版本,比 Synapse 明显更轻,完全能撑起一台小型服务器,代价是部分功能滞后。当 Synapse 对你实际的用户规模来说显得太重时,它是个合理的折中选择。
- Conduit 及其持续开发的分支
conduwuit用 Rust 写成,以内嵌数据库的单一二进制文件形式发布。用来跑一个家庭或小型社区服务器,放在我们卖的最小套餐上也毫无压力。代价是生态更小:部分管理工具和一些 bridge 默认只考虑 Synapse。
如果你是第一次搭建、用户不多,Conduit 系软件配一台小 VPS,是最省心也最省钱的起步路径。如果你预期会增长——公开社区、公司内部、带 bridge 的项目——那就直接从 Synapse 起步,省得以后迁移,因为日后在不同实现之间搬家,是一次导出再重建的工程,不是改个配置那么简单。
几乎所有人都会搞错的委派设置
Matrix 把用户 ID 里的名字和真正提供服务的机器分开处理,把这件事搞反是自建过程中最常见、也最无法挽回的错误。你的 server_name 是出现在你服务器上每一个用户 ID 冒号之后的那个域名。第一条事件一经签名,它就成了你在联邦网络里身份的一部分,此后无法再更改,除非你放弃这台机器上的所有账号和房间。
你几乎总是想要这样的搭法:server_name 用你的裸域名,软件本身跑在一个子域名上,两者之间靠委派连接起来,方式有两种。简单的一种是在裸域名的 /.well-known/matrix/server 路径下放一个静态 JSON 文件,写明真正的主机和端口。另一种是用 DNS 记录 _matrix._tcp,指向同一个地方。客户端那一侧的文件也要放在 /.well-known/matrix/client,这样应用只凭一个地址就能找到 homeserver。
先定好名字,再安装任何东西。因为软件恰好跑在子域名上就把 server_name 设成子域名,是最典型的错误,而且不可逆:每一个用户 ID、房间 ID 和已签名事件都会永远带着它。选一个你愿意印在名片上的域名,把它委派到进程实际监听的地方,并且给两个域名都配上有效的 TLS——委派主机上的证书一旦失效,联邦就会中断,哪怕本地看起来一切正常。
老老实实地估算规格
Matrix 在正常运行时不怎么吃 CPU,真正的瓶颈是内存和数据库的行为方式。Synapse 官方公布的数字是一个有用的下限:起步大约 2 GB 内存,十到五十个活跃用户时约 4 GB,超过一百人则要 8 GB 甚至更多。Conduit 系服务器所需远低于这些数字。这些数字没说清楚的是,占用量跟着加入的房间数走,而不是注册的人数——一百个大型公开房间里的五个用户,比几个私密房间里的五十个用户耗费的资源要多得多。
由此可以得出两条实用的规则。把数据库放在速度快的存储上,并留出增长空间,因为写入模式是持续的小写入,而不是突发写入。另外,不要按今天的用户数来定规格:要按这些用户第一个月会加入哪些房间来定规格,意外通常就藏在这里。我们的入门套餐能轻松承载一台小型 Conduit 或 Dendrite 服务器,而给真实社区用的 Synapse 实例则该放在中档套餐或以上——聊天托管页面列出了针对每种规模我们推荐的档位。
这里的正常运行时间比大多数工作负载都更重要,因为聊天服务器一旦掉线,不只是暂时不可用——它会悄无声息地错过一些事件,对等服务器会重试一阵子,然后就不再投递了。联邦机制能容忍几分钟的中断,但对以天计的中断毫不留情。
媒体存储是一颗慢动作的硬盘炸弹
凡是经过你用户所在房间的图片、视频和文件,都可能最终缓存在你的硬盘上,包括你自己的用户从未打开过的远程媒体。Synapse 的默认保留策略是无限期保留。结果是可以预见却依然会让人栽跟头:数据库很稳定,媒体目录却在悄悄变大,直到把空间占满,而这时的症状不是“磁盘已满”,而是“服务器表现得很奇怪”。
从第一天就设置好远程媒体的保留策略,而不是等第一次故障之后再补。Synapse 在 homeserver.yaml 里提供了保留设置,还有用于清理旧历史和缓存文件的管理接口;synapse-compress-state 能从老服务器的 state 表里回收出一大截意外的空间。数据库和媒体路径都要盯着,报警要设在剩余空间上,而不是设在服务挂掉上——后一个症状往往在前一个之后好几天才出现。
有一个设置值得你主动做决定,而不是照默认值来。URL 预览会让你的服务器去抓取房间里发出的任何链接,也就是说只要有人贴出一个链接,你服务器的 IP 地址就会立刻向第三方发出一次出站请求——包括那些特意用来试探是谁上钩的链接。如果你的 homeserver 藏在前置代理后面、真实地址很重要,就要仔细权衡这一点;我们关于隐藏源站 IP的指南更详细地讲了这一类泄露。
开放注册、垃圾账号,以及你会继承的域名声誉
公开 homeserver 上的开放注册是一张请柬,而且不是你想发的那种。自动化注册几天之内就能把一台小服务器变成垃圾源头,后果还不止于本地:其他 homeserver 会把你的域名加进访问控制列表,一旦你的名字上了足够多的名单,你那些正常用户就再也没法参加别处的房间了。挽回一个已经烧坏的域名声誉,比一开始就避免它难得多,这一点和邮件送达率是一个道理。
站得住脚的默认设置很简单。私人服务器就把 enable_registration 关掉,自己手动发账号。如果你想开放注册,就设个门槛:registration_requires_token 能在不借助任何第三方服务的情况下,把注册变成邀请制,验证码则能挡住问题里比较粗糙的那一部分。对于你自己管理的房间,Mjolnir 和 Draupnir 系的审核机器人能让你把封禁名单和房间 ACL 一次性应用到整个社区,而不用一个房间一个房间地做。
反过来也有一点值得知道:我们的地址段并没有出现在 homeserver 之间流传的 Matrix ACL 黑名单上,所以新服务器起步时声誉是干净的。之后这份声誉会变成什么样,取决于你怎么运营注册,而不取决于机器放在哪里。
Bridge,以及随之而来的元数据账单
很多人留在 Matrix 上的真实理由是 bridge:一个客户端就能覆盖住在其他网络上的房间。但 bridge 也会以一种很容易被忽略的方式改变你服务器的安全态势。bridge 持有远程账号的凭据,而且在两种协议交汇的边界上,它必然要以能够转换的形式处理消息——也就是说,对于在两端都是端到端加密的流量,bridge 进程本身看到的是明文。
这不是要你避开 bridge 的理由,而是要你把跑 bridge 的主机当成敏感基础设施来对待:这台机器一旦被攻破,它所代管的那些账号就都暴露了。每加一个 bridge,小型服务器的内存占用大致会翻一倍,容量要按这个来规划;给它选址时也要花和给 homeserver 本身选址一样多的心思——我们司法管辖区指南里的那套推理,用在一台同时握着好几个网络凭据的机器上分量更重。
让它活下去:密钥、备份与升级
Matrix 服务器有一个文件,一旦丢失就无法挽回,而且这跟数据量大小完全无关。签名密钥——Synapse 里的 signing.key——是你的服务器用来证明“声称来自你域名的事件确实来自你”的凭证。丢了它,你就没法再可信地做自己的服务器了;对等服务器会拒绝那些由一个偷了你名字的陌生人签名的事件。要把它单独备份,副本放在这台机器之外。
把密钥和数据库都备份好,并且理解为什么只恢复其中一个是危险的。把 Matrix 数据库回滚到一个较旧的快照,会让你的服务器停在一个对等服务器早已越过的状态,由此产生的分歧比干脆重建一遍还难修复。用 pg_dump 做一致性的转储,把它们存到机器之外,并且记住在这个平台上没有服务商备份可以兜底——终止服务后什么都不会保留,这正是整套安排的意义所在,我们的备份指南里有详细说明。
升级是常规操作,但不是可选项。homeserver 的每个版本都可能带着 schema 迁移,跳过太多版本会把一次五分钟的升级变成耗掉一下午的工程。升级前先读发行说明,升级要够勤,让每一步都小一点,再照上线第一小时加固清单把主机的基本卫生做到位——聊天服务器是一个长期在线、面向公网、还挂着数据库的服务,理应得到同等的对待。
服务器放在哪里,依然决定最终结果
以上都是配置层面的事。配置改变不了的是哪一套法律体系会收到关于你用户的请求,而对一台通讯服务器来说,这个问题的分量比对一个普通网站要重得多。即便消息正文是加密的,homeserver 仍然以明文形式保存着成员记录、时间戳和社交关系图谱数据——所以托管它的司法管辖区,就是决定谁能访问这些记录的司法管辖区。
这就是为什么选址应该是刻意为之,而不是只看延迟。我们一共运营七个司法管辖区,它们之间的取舍写在司法管辖区指南和机房列表页面里。同一个问题的另一半,是服务商到底知道你是谁:一个没有绑定身份的账号,交不出它从未收集过的身份证件,这也是无需 KYC 的托管和自建通讯服务总是一起被提起的直接原因。但这两者都挡不住一个已经知道你名字的法院,我们的OpSec 指南对这条界限说得很直白。
浓缩版
如果只从这篇文章里记住六件事,记住这些:
- 先定好
server_name再装任何东西——这是唯一一个你以后永远改不了的决定。 - 用
/.well-known/matrix/server或 SRV 记录做委派,并且给两个域名都配上有效的 TLS。 - 按用户将来会加入哪些房间来定规格,而不是按你有多少用户来定。
- 第一天就设好媒体保留策略,URL 预览要不要开也自己拿主意,别照单全收默认值。
- 让注册保持关闭或凭令牌开放;烧坏的域名声誉代价很高,很难挽回。
- 把
signing.key单独备份,绝不要让数据库回滚到落后于对等服务器的状态。
做到这些,服务器就会平平无奇——这正是聊天服务器该有的样子。作为交换你得到的东西也值得看清楚:不是隐形,也不是一个能替你隐藏谁在跟谁说话的协议,而是内容归你自己的对话、一个别人关不掉的账号,以及一台放在你自己刻意选定的法律体系之下的机器。把 homeserver 放在你自己选的地方,然后让联邦网络自己找过来。