你的社交数据,凭什么归平台所有?用 Cloudflare 搭建去中心化社交应用
一个反复发生的故事
一个平台突然关闭,或者改变商业模式,你多年积累的关注者、帖子、社交图谱,一夜之间或烟消云散,或被锁在另一家公司的服务器里。这件事在互联网历史上发生过很多次,而且还会继续发生。
问题的根源在于:在传统的中心化社交平台上,你的账号和数据属于平台,而不属于你。
ATProto(Authenticated Transfer Protocol,认证传输协议)想从协议层面解决这个问题。Bluesky 是目前最大的 ATProto 应用,但 ATProto 本身并不等于 Bluesky——它是一套开放协议,任何人都可以在上面构建应用。
这篇文章,以 Cloudflare 官方博客中的一个实战项目为线索,讲清楚两件事:ATProto 的核心设计思想是什么,以及如何在 Cloudflare 的无服务器平台上,从零搭建一个真正可用的去中心化社交应用。
ATProto 的基本逻辑:数据归用户,协议做担保
理解 ATProto,需要先放下"平台"的思维框架,换成"协议"的视角。
身份:你的账号是一个域名
在 ATProto 里,用户身份以**句柄(Handle)**的形式存在,格式是一个合法的域名,比如alice.example.com。
这不只是个名字。协议通过 DNS 的 TXT 记录来验证句柄的所有权——你在域名的_atproto子域下放一条特定的 TXT 记录,任何人都可以独立验证"这个账号确实属于这个域名的持有者",不需要依赖任何中心化的认证机构。
句柄背后是一个去中心化标识符(DID),DID 再指向用户的**个人数据服务器(PDS)**的位置。PDS 可以是 Bluesky 官方提供的,也可以是用户自己搭建的,或者任何第三方服务商——这个选择权完全在用户手里。
数据:签名的、可验证的、跨应用互通的
用户发布的所有内容,都存储在自己的**个人仓库(Repository)**里,并且经过密码学签名。这意味着:
- 数据的真实性可以被任何人独立验证,不需要信任某个中心服务;
- 任何应用都可以读取和构建在这些数据之上,无需获得原始应用的许可;
- 即使某个应用下线,数据本身依然存在于用户的仓库中。
所有数据类型通过Lexicon这套类似 JSON Schema 的语言来定义,保证了不同应用之间的数据可以互相理解和复用。
事件流:全局可订阅的社交网络动态
当用户在任意应用中发布内容时,这个变更会触发一个事件,发布到中继节点(Relay),再广播到全局事件流中。任何应用,不管它是不是原始发布者,都可以订阅这个事件流来获取整个网络的实时动态。
这就是去中心化的真正含义:仓库、中继、应用,三者相互独立,可以由不同的运营者运行,却共享同一套数据网络。
Statusphere:最小可用的去中心化社交应用
Cloudflare 博客选用了 ATProto 团队官方的示例项目Statusphere作为演示载体。它的功能极其简单:用户只能发布单个 emoji 表情作为状态更新。
正因为足够简单,Statusphere 反而是理解 ATProto 数据流动方式的最佳起点。它的数据格式(Lexicon 定义)只有两个字段:
{"status":"单个 emoji","createdAt":"发布时间"}这个记录会被写入用户的个人仓库,带上密码学签名,然后广播到全局事件流。任何订阅了这个 Lexicon 类型(xyz.statusphere.status)的应用,都能实时看到这个更新。
在 Cloudflare 上的无服务器实现
Cloudflare 博客的核心工程价值在于:展示了如何不依赖任何自管服务器,用 Cloudflare 的开发者平台组件拼出一个完整的 ATProto 应用。整个项目的所有资源都可以在免费套餐内运行。
Workers:业务逻辑的执行层
Cloudflare Workers 是整个应用的入口,负责处理所有 HTTP 请求:用户登录、数据读取、状态发布。Workers 部署在全球 300 多个节点上,代码距离用户通常不超过 50ms。
KV:缓存身份解析结果
ATProto 的身份解析涉及两步:先通过 DNS 把句柄解析为 DID,再通过 DID 找到用户 PDS 的位置。这两步的结果变化非常不频繁,但每次请求都要用到。
Workers KV 是这类场景的自然选择——全球分布、低延迟读取、没有存储量限制。身份解析结果和用户的 OAuth Session 数据都存在 KV 里,客户端只持有一个不透明的 Session ID。
D1:关系型数据的持久化
用户发布的状态更新,在写入 ATProto 仓库的同时,也会写入 Cloudflare D1(一个基于 SQLite 的分布式数据库),确保页面能立即反映最新状态,不依赖事件流的延迟。
Durable Objects:WebSocket 实时推送
每个打开首页的用户都维持着一个与 Durable Object 的 WebSocket 连接。当有新的状态更新时,Worker 向 Durable Object 发送广播指令,后者遍历所有活跃连接,将更新推送给每一个在线用户。
Durable Objects 有一个重要特性:休眠(Hibernation)。当没有消息需要广播时,Durable Object 可以进入休眠状态,不占用计算资源,但 WebSocket 连接保持活跃。只有收到广播请求时才被唤醒,处理完后再次休眠。这对于控制成本至关重要。
最棘手的工程问题:实时事件流遇上无服务器
前面描述的都是"应用内"的操作——用户通过 Statusphere 本身发布状态,流程是闭环的。
但 ATProto 的设计允许用户通过任何应用发布xyz.statusphere.status类型的记录,这些记录同样应该出现在 Statusphere 的时间线上。为此,应用需要订阅全局事件流服务Jetstream。
问题来了:订阅 Jetstream 需要一个持久的 WebSocket 客户端连接,但无服务器平台的 Worker 不能无限期运行。
这是一个经典的架构矛盾,团队给出了两种解法。
方案一:Cron Trigger 定时轮询(纯无服务器)
用 Cloudflare Workers 的Cron Trigger功能,每分钟触发一次 Worker 执行:
- 从持久存储中读取上次处理到的游标(cursor,一个微秒级时间戳);
- 以这个游标为起点连接 Jetstream,拉取从该时刻起的新事件;
- 处理完比当前时间更新的事件后,主动关闭 WebSocket 连接,Worker 结束执行。
这套方案完全在无服务器模型内运行,无需任何外部进程。代价是更新有最长约一分钟的延迟,对于早期项目和原型来说,这是完全可以接受的折衷。
游标存储在 Durable Object 的持久存储中,即使实例重启,也能从正确的位置续传,不遗漏任何事件。
方案二:轻量监听进程(混合模式)
如果需要真正的实时性,可以在任意一台机器(哪怕是家里的树莓派)上运行一个极简的监听进程:
- 它维持一条到 Jetstream 的持久 WebSocket 连接;
- 收到新事件时,通过 HTTP 请求推送给 Cloudflare Worker;
- 自身不对外暴露任何端口,没有公网 IP,没有数据库。
这个进程的职责单一,几乎不需要维护。等应用规模扩大后,可以将其替换为Cloudflare Queues,获得批处理和失败重试的能力。
这种"混合模式"在工程上是务实的选择——它并没有破坏无服务器的核心价值(无需管理复杂的服务端基础设施),只是用一个最轻量的方式填补了平台目前的能力空缺。
工程选型:为什么用了 Rust + WASM
项目最初尝试用 TypeScript 实现,因为 ATProto 的官方核心库就是用 TypeScript 编写的,Workers 也对 TypeScript 有一流支持。但 ATProto 的 TypeScript 库依赖了一些与 Cloudflare 边缘运行时不兼容的 Node.js API,直接使用行不通。
最终选择了Rust + WASM:将 Rust 代码编译为 WebAssembly,运行在 Workers 上。ATProto 的 Rust 社区库(atrium)虽然还在活跃开发中,但已足够完整,作者基于一个已有的 Rust 版 Statusphere 实现进行改造,较快地完成了可用原型。
对于有意在 Cloudflare Workers 上构建 ATProto 应用的开发者,博客建议:向 TypeScript 官方库贡献 PR,修复其与 Serverless 运行时的兼容性问题——这将使生态的门槛大幅降低。
平台的一个现有限制与未来方向
Durable Objects 目前支持在持有长连接 WebSocket服务端时进入休眠,但暂不支持在持有长连接 WebSocket客户端(比如连接到 Jetstream 的那种)时休眠。这正是 Statusphere 需要 Cron Trigger 方案或外部监听进程来绕过这一限制的原因。
未来如果 Durable Objects 能够支持客户端 WebSocket 连接的休眠,整个 Cron Trigger 方案就可以用一个在 Durable Object 内部运行的 Jetstream 监听器来替代,彻底消除这种绕路设计,让架构更加简洁。
总结
这个项目从表面看是一个 emoji 状态更新的玩具应用,但它真正在演示的,是一种新的应用构建范式:
数据主权层面,ATProto 把账号所有权还给了用户——身份基于 DNS 验证,数据经过密码学签名,任何应用都无法单方面没收用户的社交资产。
基础设施层面,Cloudflare 的 Workers + KV + D1 + Durable Objects 这套组合,让开发者可以在几乎零运维的情况下,构建出具备全球分发、实时推送、持久化存储能力的完整应用——而且免费套餐就够用。
工程思路层面,Cron Trigger 应对实时流的方案是一个很好的示范:承认平台当前的限制,找到在约束内工作的最简洁方案,而不是强行绕开约束引入不必要的复杂度。
项目代码完全开源,一键部署按钮可以直接把整套环境克隆到你自己的 Cloudflare 账号下,是上手 ATProto 开发的实用起点。
参考来源:Cloudflare Blog — “Serverless Statusphere: a walk through building serverless ATProto applications on Cloudflare’s Developer Platform”
