ActivityPub 原理指津

假如你打开一个长毛象站点,并搜索本站的某个 federation 账号(如 @0x0@matterofti.me 也就是我自己),会发现该用户的确存在,个人信息栏也能显示其发表过的文章数量,但个人时间线很有可能是一片空白——看不到该用户的往期文章推送。

起初,我发现这个现象时,困惑了相当长一段时间,还以为是 writefreely 的 bug。但当我关注了自己的博客,新发布的文章又能显示在时间线里了。

前不久,更新 v 0.12.0 后,为了测试新加入的 mention 也就是 @ 功能,我用不同博客分别 @ 了我自己的长毛象账号。那几篇编辑过的文章也奇迹般地出现在了博客的时间线上。

这些奇特的表现其实是 ActivityPub 协议的实现方式所导致的。

ActivityPub 既然是一个「去中心化」协议,顾名思义,它的宇宙分布在不同的私人服务器上。用户要跨服发布/接收内容,就要进行服务器间的通信。

那么,服务器要如何决定推送给哪些服务器/收取哪些服务器的信息呢?最简单的实现是「我全都要」,把宇宙中每一条信息都拷贝到本地,并对全宇宙广播自己的时间线。但很显然,随着宇宙的扩张,服务器之间的联系(完全图的边数)将会爆炸式增长,这会消耗过多的资源,而且也有违「去中心化」的本意——如果接收和储存所有信息,不就相当于每台服务器都在本地做了个宇宙的镜像吗?

所以,每个服务器只应该接收和自己的用户有关的信息,也就是自己用户「订阅」的信息。广播出去的消息,也只应该送到相关的服务器上。

ActivityPub 的具体实现方式是给每个用户(actor)分配了一个 inbox 和一个 sharedInbox,后者通常是本服务器的公共时间线。当用户发布一条内容 (本站的文章,长毛象的嘟文)时,服务器有几种选择:

  1. 私信——分别发送到每个接收者的 inbox
  2. 分组可见——在内容的 tocc 字段里填写上接收者的 handle,并发送到 sharedInbox
  3. 公共——发送到 sharedInbox

不难发现,在 ActivityPub 的世界里私信和普通内容是平等的,因为它们都只是「发给特定收信人的信息」而已。

这么做的结果就是,假如某个服务器没有人订阅本站的博客,他们还是能通过本站的 ActivityPub 端点拿到基本用户信息(如发布的文章数量),但是文章没有被发送到他们的 sharedInbox,所以时间线才会一片空白。

而由于推送是在「创建」或「编辑」时发生的(!!!),用户也无法看到关注之前的内容,因为它们在发布时没有被送到这个服务器。除非两个 ActivityPub 实例都实现了 outbox 相关功能,这样可以从发布内容者的 outbox 里主动拉取内容。

参考资料

#dev