ActivityPub

W3C 推荐规范

本版本:
https://www.w3.org/TR/2018/REC-activitypub-20180123/
最新发布版本:
https://www.w3.org/TR/activitypub/
最新编辑草案:
https://w3c.github.io/activitypub/
测试套件:
https://test.activitypub.rocks/
实现报告:
https://activitypub.rocks/implementation-report
上一个版本:
https://www.w3.org/TR/2017/PR-activitypub-20171205/
编辑:
Christine Lemmer-Webber
Jessica Tallon
作者:
Christine Lemmer-Webber
Jessica Tallon
Erin Shepherd
Amy Guy
Evan Prodromou
代码仓库:
Git 仓库
问题
提交记录

请查阅 勘误,了解自发布以来报告的所有错误或问题。

另见 翻译


摘要

ActivityPub 协议是一种去中心化的社交网络协议, 基于 [ActivityStreams] 2.0 数据格式。 它提供客户端到服务器的 API,用于创建、更新和删除内容, 还提供服务器到服务器的联邦 API,用于推送通知和内容。

本文档状态

本节描述了本文档在发布时的状态。其他文档可能会取代本文件。当前 W3C 发布文档和本技术报告的最新版本可在 W3C 技术报告索引 https://www.w3.org/TR/ 查询。

本文档由 Social Web 工作组 作为推荐规范发布。

欢迎所有相关方通过工作组的 Issue tracker 提交实现报告、漏洞报告和其他意见。 这些内容将由 Social Web 社区组 讨论,并在本规范后续版本中加以考虑。

请参阅工作组的 实现报告

本文档经 W3C 会员、软件开发者、其他 W3C 组织及相关方审议,由 W3C 董事确认为 W3C 推荐规范。 本文档内容稳定,可作为参考材料或被其他文档引用。W3C 制定推荐规范的角色在于引起对规范的关注并促进其广泛部署,从而提升 Web 的功能和互操作性。

本文档由 遵守 W3C 专利政策 的小组编写。 W3C 维护一份与本组交付物相关的 专利声明公开列表, 该页面也包含披露专利的说明。任何实际知晓含有必要权利要求的专利的人士,须根据 W3C 专利政策第6节披露信息。

本文件受 2017年3月1日 W3C 流程文件管理。

1. 概述

ActivityPub 提供了两层协议:

ActivityPub 的实现可以只实现其中一个,也可以同时实现两者。 不过,一旦你实现了其中一个,距离实现另一个就只差几步,并且两者兼得有很多好处(你的站点能成为去中心化社交网络的一部分,并能使用跨众多社交网站通用的客户端及库)。

在 ActivityPub 中,用户通过其在服务器上的帐号以 "actors"(参与者)表示。 用户在不同服务器上的帐号对应不同的 actor。 每个 Actor 拥有:

Actor with inbox and outbox

它们是端点,实际上只是列在 ActivityPub actor 的 ActivityStreams 描述里的 URL。 (后文将详细介绍 ActivityStreams)。

下面是我们好友 Alyssa P. Hacker 的记录示例:

示例 1
{"@context": "https://www.w3.org/ns/activitystreams",
 "type": "Person",
 "id": "https://social.example/alyssa/",
 "name": "Alyssa P. Hacker",
 "preferredUsername": "alyssa",
 "summary": "Lisp 爱好者,来自 MIT",
 "inbox": "https://social.example/alyssa/inbox/",
 "outbox": "https://social.example/alyssa/outbox/",
 "followers": "https://social.example/alyssa/followers/",
 "following": "https://social.example/alyssa/following/",
 "liked": "https://social.example/alyssa/liked/"}

ActivityPub 使用 [ActivityStreams] 作为其词汇集合。 这很好,因为 ActivityStreams 已包含几乎所有表达社交网络活动和内容流转常见需求的术语。 很可能你所需的词汇已经被定义,但即便没有,还可以通过 [JSON-LD] 扩展。 如果你了解 JSON-LD,就可以用 JSON-LD 提供的强大链接数据手段。 如果不了解也没关系,JSON-LD 文档与 ActivityStreams 可以看作普通 JSON 理解。 (只有你要加扩展时,JSON-LD 的优势才真正体现出来)。

好的。 假如 Alyssa 要和朋友们聊天,朋友也想找她! 幸运的是有了 "inbox" 和 "outbox" 能帮忙。 这两个端点对 GET 和 POST 的行为是不同的。 我们来看看它们如何工作:

Actor with messages flowing from rest of world to inbox and from outbox to rest of world

简单回顾一下:

当然,如果只能靠 GET 别人的 outbox 查看内容,这个联邦协议就太低效了! 实际上,联邦通常是服务器直接将 actor 产生的消息 POST 到其他服务器的 inbox。

看个例子! Alyssa 想联系她的好友 Ben Bitdiddle。 她前不久借给他一本书,现在要确认他能归还。 这就是她作为 ActivityStreams 对象写的消息:

示例 2
{"@context": "https://www.w3.org/ns/activitystreams",
 "type": "Note",
 "to": ["https://chatty.example/ben/"],
 "attributedTo": "https://social.example/alyssa/",
 "content": "嘿,你看完我借你的那本书了吗?"}

这是写给 Ben 的便笺。 她把它 POST 到自己的 outbox。

Actor posting message to outbox

因为这不是一个 activity(活动)对象,服务器识别到这是一条新建的对象,因此会体贴地把它包裹在 Create activity 里。 (ActivityPub 传递的活动通常都是某 actor 对某个对象采取某种 action,这里就是 Person 发布 Create 类型的 Note)。

示例 3
{"@context": "https://www.w3.org/ns/activitystreams",
 "type": "Create",
 "id": "https://social.example/alyssa/posts/a29a6843-9feb-4c74-a7f7-081b9c9201d3",
 "to": ["https://chatty.example/ben/"],
 "actor": "https://social.example/alyssa/",
 "object": {"type": "Note",
            "id": "https://social.example/alyssa/posts/49e2d03d-b53a-4c4c-a95c-94a6abf45a19",
            "attributedTo": "https://social.example/alyssa/",
            "to": ["https://chatty.example/ben/"],
            "content": "嘿,你看完我借你的那本书了吗?"}}

Alyssa 的服务器查询 Ben 的 ActivityStreams actor 对象,找到了他的 inbox 端点,并把对象 POST 到 Ben 的 inbox。

Server posting to remote actor's inbox

技术上来说,这分为两个步骤:一个是客户端到服务器,一个是服务器到服务器(联邦)。 但结合起来看,我们可以把这抽象成一次 outbox 到 inbox 的流畅提交过程:

Note flowing from one actor's outbox to other actor's inbox

很棒! 过了一会,Alyssa 查看自己收到了哪些新消息。 她的手机通过 GET 轮询自己的 inbox,看见好友们发的视频、姐姐发的侄子照片, 还看到了如下内容:

示例 4
{"@context": "https://www.w3.org/ns/activitystreams",
 "type": "Create",
 "id": "https://chatty.example/ben/p/51086",
 "to": ["https://social.example/alyssa/"],
 "actor": "https://chatty.example/ben/",
 "object": {"type": "Note",
            "id": "https://chatty.example/ben/p/51085",
            "attributedTo": "https://chatty.example/ben/",
            "to": ["https://social.example/alyssa/"],
            "inReplyTo": "https://social.example/alyssa/posts/49e2d03d-b53a-4c4c-a95c-94a6abf45a19",
            "content": "<p>啊、好的,不好意思,明天还你。</p>
                        <p>我最近在回顾寄存机那部分内容,已经有段时间没实现了。</p>"}}

Alyssa 如释重负,顺手点赞了 Ben 的帖子:

示例 5
{"@context": "https://www.w3.org/ns/activitystreams",
 "type": "Like",
 "id": "https://social.example/alyssa/posts/5312e10e-5110-42e5-a09b-934882b3ecec",
 "to": ["https://chatty.example/ben/"],
 "actor": "https://social.example/alyssa/",
 "object": "https://chatty.example/ben/p/51086"}

她把这个消息 POST 到了自己的 outbox。 (因为是 activity 她的服务器知道不需要再包一层 Create 对象)。

开心之余,她又发了一条公开消息给所有粉丝。 很快,下面这条消息广播给了全部 followers 集合成员, 由于包含特殊的 Public 组,实际上对任何人可读。

示例 6
{"@context": "https://www.w3.org/ns/activitystreams",
 "type": "Create",
 "id": "https://social.example/alyssa/posts/9282e9cc-14d0-42b3-a758-d6aeca6c876b",
 "to": ["https://social.example/alyssa/followers/",
        "https://www.w3.org/ns/activitystreams#Public"],
 "actor": "https://social.example/alyssa/",
 "object": {"type": "Note",
            "id": "https://social.example/alyssa/posts/d18c55d4-8a63-4181-9745-4e6cf7938fa1",
            "attributedTo": "https://social.example/alyssa/",
            "to": ["https://social.example/alyssa/followers/",
                   "https://www.w3.org/ns/activitystreams#Public"],
            "content": "把书借给朋友很开心,能拿回来更开心!:)"}}

1.1 Social Web 工作组

ActivityPub 是 Social Web 工作组制定的多个相关规范之一。 对其他可选实现方式和补充协议感兴趣的实现者应查阅 [Micropub] 及 总览文档 [Social-Web-Protocols]。

2. 一致性

除了标明为非规范性的部分,所有创作指南、图示、示例和注释都属非规范范畴。本规范剩余所有内容均为规范性。

以下关键词 MAYMUSTMUST NOTSHOULD 以及 SHOULD NOT 的含义应按照 [RFC2119] 定义理解。

2.1 规范配置

本规范定义了两个密切关联并互有交互的协议:

客户端到服务器协议,或 "Social API"
该协议允许客户端代表用户行动。 例如,手机 App 可用该协议与用户的 actor 社交流交互。
服务器到服务器协议,或 "联邦协议"
本协议用以在不同服务器的 actor 之间分发活动,将他们编织进同一社交图谱。

ActivityPub 设计思路是,只要任一协议实现,支持另一个的成本极低。 但服务器可选实现其中任意一个。 因此有三类一致性级别:

ActivityPub 兼容客户端
适用于实现客户端到服务器协议的全部客户端部分的任何实现。
ActivityPub 兼容服务器
适用于实现客户端到服务器协议的全部服务器部分的任何实现。
ActivityPub 兼容联邦服务器
适用于实现全部联邦协议部分的任何实现。

如果某部分只适用于联邦协议实现,会在规范中特别说明。 此外,每当有具体要求时,会说明要求适用于客户端、服务器(针对客户端-服务器协议)或服务器到服务器协议中的发送/接收服务器。

3. 对象

对象是 [ActivityStreams] 和 ActivityPub 的核心概念。 对象常常被包裹在 Activity 中,位于集合的流中,而集合自身又是对象的子类。 具体见 [Activity-Vocabulary] 文档,特别是 核心类别; ActivityPub 与该词汇的映射非常紧密。

除了 ActivityStreams 定义的术语,ActivityPub 还扩展了部分术语。 这些术语都包含在 ActivityPub 的 JSON-LD context https://www.w3.org/ns/activitystreams。 实现方 建议在对象定义中包含 ActivityPub context。 亦 可以按需追加额外 context。

ActivityPub 与 ActivityStreams 的 URI / IRI 约定完全一致。

服务器 建议对收到的内容进行校验,防止内容伪造攻击。 (服务器至少要判断对象内容与源头一致,更好则能校验签名)。 本文档对校验机制未作强制规定,但具体建议和最佳实践请见 安全性注意事项

例如,example.com 收到如下 activity
示例 7
{
  "@context": "https://www.w3.org/ns/activitystreams",
  "type": "Like",
  "actor": "https://example.net/~mallory",
  "to": ["https://hatchat.example/sarah/",
         "https://example.com/peeps/john/"],
  "object": {
    "@context": {"@language": "en"},
    "id": "https://example.org/~alice/note/23",
    "type": "Note",
    "attributedTo": "https://example.org/~alice",
    "content": "I'm a goat"
  }
}
那么它应当去 dereference id,确保该对象真实存在,并判断对象没有被伪造。 (在该例里 Mallory 可能冒充由 Alice 发布的对象)。

3.1 对象标识符

[ActivityStreams] 的所有对象应具唯一全局标识。 ActivityPub 进一步要求,所有通过 ActivityPub 协议分发的对象必须具唯一全局标识, 除非它们有意为临时对象(如某些聊天消息或游戏通知,仅短暂存在,无需可查询)。 标识符需符合如下之一:

  1. 可公开 dereference 的 URI,比如 HTTPS URI,且授权域归属其原服务器。 (对外内容建议使用 HTTPS URI)。
  2. 明确标记为 JSON null,此时是匿名对象(属于父上下文一部分)

服务器间通信时,活动需必须带标识符,除非活动本就为临时事务。 而客户端到服务器通信时,服务器收到未指定 id 的 outbox 对象, 建议给其分配 actor 命名空间下的对象 ID 并附加到对象上。

所有对象都有下列属性:

id
对象的唯一全局标识符(临时对象可省略)。
type
对象的类型。

3.2 获取对象

可用 HTTP GET 方法根据对象的id属性 dereference,获取活动。 服务器可以按 [RFC7231] 所述实现 HTTP 内容协商, 选择响应类型, 但必须application/ld+json; profile="https://www.w3.org/ns/activitystreams" 提供 ActivityStreams 对象的表示, 并且建议application/activity+json 也提供 ActivityStreams 表示。 客户端必须用 Accept 头带上 application/ld+json; profile="https://www.w3.org/ns/activitystreams" 类型以便获取该活动。

服务器可以对未满足上述要求的请求采取其他在合理范围内的行为。 (如实现遗留协议,或对同一 URI 可返回 HTML 及 ActivityStreams 表示)。

服务器可以要求按 B.1 身份验证与授权 的说明进行授权,也可实现自有授权规则。 服务器建议对未通过授权校验的请求返回合适的 HTTP 错误码,若对象为私有可给 403 Forbidden。 若服务器不希望泄漏私有目标 existence,可以直接返回 404 Not Found。

3.3 source 属性

除了 [Activity-Vocabulary] 定义的所有属性,ActivityPub 在 Object 上新增 source 属性。 source 属性用于指示 content 标记是由什么源内容转换而来,作为溯源之用,也便于客户端未来再次编辑。 一般来说,sourcecontent 的转换由客户端完成,反向则不然。

source 的值本身是对象,包含自有的 contentmediaType 字段,表明源内容信息。

示例 8
{
  "@context": ["https://www.w3.org/ns/activitystreams",
               {"@language": "en"}],
  "type": "Note",
  "id": "http://postparty.example/p/2415",
  "content": "<p>我 <em>真的</em>喜欢草莓!</p>",
  "source": {
    "content": "I *really* like strawberries!",
    "mediaType": "text/markdown"}
}
:当客户端无法正确处理 mediaType 时怎么办?

通常最好让用户用自己最初写文章时的源格式进行编辑。 但并非所有客户端都能优雅支持所有源类型,既然由客户端负责从 sourcecontent, 就有可能有些客户端可用的 media type,另一些并不识别。 虽然可直接让用户编辑 content 而忽略 source,不过这样任何将来修订都会丢失原 source 的最好形态。 客户端因此应弹出最小程度的提示,告知原始源格式无法识别,编辑将丢失原格式。

例如 Alyssa P. Hacker 喜欢用她自己写的 Emacs 客户端通过 ActivityPub 博客发表内容,用 Org mode。 后来她换了手机客户端编辑,手机端不认识 text/x-org 也不会渲染,只好用文本框让她直接编辑 content。 编辑区上方弹窗提示:“这段内容原本用我们不支持的标记语言写,若你编辑,原文会丢失!” Alyssa 觉得为修个拼写错误失去 org-mode 标记不值,决定到家后再用 Emacs 修。

4. 参与者(Actors)

ActivityPub 的参与者通常是 ActivityStreams 中的 Actor 类型之一, 但不必然如此。例如, Profile 对象 也可以用作参与者,或者采用 ActivityStreams 扩展中的类型。 参与者的获取方式与 ActivityPub 中其他对象相同。 和其他 ActivityStreams 对象一样,参与者拥有 id,它是一个 URI。 当直接在用户界面输入(如登录表单)时,建议支持简化命名方式。 为此,ID 归一化建议按如下方式执行:

  1. 如果输入的 ID 是有效的 URI,则直接使用即可。
  2. 如果看起来用户只是忘记添加 URI 的协议头,而其它内容无误,例如 example.org/alice/,客户端可以尝试补全默认协议,推荐 https
  3. 否则,应视为无效输入。

一旦确定了参与者 URI,应进行 dereference。

ActivityPub 并未规定“用户”与参与者之间的具体关系,可以有多种配置方式。 一个 Actor 可以由多个用户或组织共同控制;同理,一个用户或组织也可控制多个参与者。Actor 也可以表示一段软件、机器人或自动化进程。 更详细的“用户”建模,例如将归属于同一实体的多个参与者关联在一起,或让一个参与者通过多个不同资料视角展现,都由具体实现决定。

4.1 Actor 对象

除了3.1 对象标识符要求的属性外,Actor 对象必须具备以下属性:

inbox
指向一个 [ActivityStreams] OrderedCollection ,包含该参与者接收到的所有消息;详见 5.2 Inbox
outbox
指向一个 [ActivityStreams] OrderedCollection ,包含该参与者产生的所有消息;详见 5.1 Outbox

实现建议另外提供以下属性:

following
指向一个 [ActivityStreams] 集合,表示本参与者关注的对象; 详见 5.4 Following Collection
followers
指向一个 [ActivityStreams] 集合,表示关注本参与者的人; 详见 5.3 Followers Collection

实现可以再补充下列属性:

liked
指向一个 [ActivityStreams] 集合,包含该参与者点赞过的对象; 详见 5.5 Liked Collection
示例 9
{
  "@context": ["https://www.w3.org/ns/activitystreams",
               {"@language": "ja"}],
  "type": "Person",
  "id": "https://kenzoishii.example.com/",
  "following": "https://kenzoishii.example.com/following.json",
  "followers": "https://kenzoishii.example.com/followers.json",
  "liked": "https://kenzoishii.example.com/liked.json",
  "inbox": "https://kenzoishii.example.com/inbox.json",
  "outbox": "https://kenzoishii.example.com/feed.json",
  "preferredUsername": "kenzoishii",
  "name": "石井健蔵",
  "summary": "この方はただの例です",
  "icon": [
    "https://kenzoishii.example.com/image/165987aklre4"
  ]
}

实现可以进一步提供以下属性:

streams
一个补充集合的列表,可能对用户有参考价值。
preferredUsername
可用来指代该参与者的短用户名,无唯一性保证。
endpoints
一个 json 对象,映射了额外(一般为服务端/域全局)的端点,这些可能对该参与者或引用者有用。 此 mapping 可以嵌入 actor 文档,也可链至带这些属性的 JSON-LD 文档。

endpoints mapping 可以包含如下属性:

proxyUrl
端点 URI,允许该参与者的客户端访问需认证的远程 ActivityStreams 对象。 用法:客户端 POST 一个 x-www-form-urlencoded 格式的 id 参数, 值为请求 ActivityStreams 对象的 id
oauthAuthorizationEndpoint
如果认证使用 OAuth 2.0 令牌 [RFC6749] [RFC6750], 该端点为浏览器认证用户获取授权凭证的 URI,用于 客户端到服务器交互
oauthTokenEndpoint
如果认证使用 OAuth 2.0 令牌 [RFC6749] [RFC6750], 该端点为客户端获取访问令牌的 URI,用于 客户端到服务器交互
provideClientKey
如果使用 Linked Data Signatures 或 HTTP Signatures 做认证/授权,该端点为浏览器认证用户授权客户端公钥提供 URI, 用于 客户端到服务器交互
signClientKey
如果使用 Linked Data Signatures 或 HTTP Signatures 做认证/授权,该端点为客户端密钥由 actor 密钥签名授权一段时间、代表用户操作外部服务器时提供 URI。
sharedInbox
可选端点 用于大范围推送面向公开与粉丝的活动sharedInbox 端点建议也是公开可读 OrderedCollection,只包含指向 Public 特殊集合的对象。 读取 sharedInbox 端点禁止展现非指向 Public 的对象。

作为 ActivityPub 的上游词汇,任何适用的 [ActivityStreams] 属性都可用于 ActivityPub Actor。 这里特别重点列举一些 ActivityStreams 属性,说明其在 ActivityPub 实现中的用法。

url
指向 actor 的“简介网页”,若不同于 id 字段。
name
actor 的“昵称”或“显示名”。
summary
用户自我简介、个人简介。
icon
指向用户头像图片或 Image 对象的链接(可为缩略图)。

包含自然语言值的属性, 如 namepreferredUsernamesummary ,均遵循 ActivityStreams 中定义的自然语言支持机制

5. 集合(Collections)

[ActivityStreams] 定义了 collection(集合)概念; ActivityPub 定义了几种带特殊行为的集合。 注意 ActivityPub 利用 ActivityStreams 分页机制 遍历大对象集。

注意,这些集合有的明确类型为 OrderedCollection ,部分则允许为 Collection OrderedCollection OrderedCollection 必须始终以逆时间顺序展现。

用于排序逆时间顺序的属性定义是实现细节,未固定。许多 SQL 风格数据库会用自增主键作标识,这在多数场合适于插入顺序。 也可在其他数据库用插入时间戳。关键是要保证新项目在前,顺序不乱。 不建议用频繁变动的字段(如“上次更新时间戳”)排序。

5.1 Outbox

outbox 可通过 actor 资料里的 outbox 属性发现。 outbox 字段必须为一个 OrderedCollection

outbox 数据流包含用户已发布的活动,其内容是否可见还受请求者权限影响(即 outbox 的内容必须根据阅读者的权限过滤)。 若某用户未含 授权信息,服务端仅应返回所有 Public 帖子。 这也许就是用户发布所有相关对象,但数量多少由服务器实现自行决定。

outbox 支持 HTTP POST,详细行为见 客户端到服务器交互

5.2 Inbox

inbox 可通过 actor 资料的 inbox 字段发现。 inbox 字段必须 OrderedCollection

inbox 数据流包含参与者接收到的所有活动。 服务器建议根据请求者权限过滤内容。 一般说,inbox 的所有者能访问全部内容。依据访问控制,部分内容对公共公开,部分内容仅授权用户可见。 非 owner 用户按访问权限限制。

服务端必须对返回的 inbox 活动去重。若某活动既发给了 actor 的粉丝,又发给了其中一位关注者且两端都命中,极易产生重复。实现必须按 id 比较,丢弃已收录的活动,避免重复。

联邦服务器的参与者 inbox 支持 HTTP POST,详细行为见 Delivery。 非联邦服务器建议遇 POST 请求返回 405 Method Not Allowed。

5.3 Followers Collection

每个actor建议拥有 followers 集合。 该集合包括所有向该参与者发送过 Follow 活动的用户,作为 副作用添加。 这里可查所有关注该 actor 的人。 followers 集合必须 OrderedCollection Collection ,并可以结合权限过滤。

:通知投递默认目标

follow 行为一般代表希望看到 actor 的动态,因此 Followers 集合是通知 delivery的合理默认目标。

5.4 Following Collection

每个 actor 建议拥有 following 集合。 其列出演员已关注的所有人,均为 副作用追加。 following 集合必须 OrderedCollection Collection可以结合权限过滤。

5.5 Liked Collection

每个 actor 可以拥有 liked 集合。 该集合是所有该 actor Like 活动对象的集合,均为副作用追加。 liked 集合必须 OrderedCollection Collection可以结合权限过滤。

5.6 公开寻址

除了 [ActivityStreams] 集合和对象外, Activity 还可寻址特殊 "public" 集合,其标识为 https://www.w3.org/ns/activitystreams#Public。 例如:

示例 10
{
  "@context": "https://www.w3.org/ns/activitystreams",
  "id": "https://www.w3.org/ns/activitystreams#Public",
  "type": "Collection"
}

发往该特殊 URI 的 Activity 应无认证限制对所有用户可访问。 实现禁止将实际活动直接投递给"public"集合; 该集合不能接收内容。 不过,actor可以定义 sharedInbox 端点,提升公共/粉丝消息的投递效率;详见 7.1.3 共享收件箱投递

用 ActivityStreams 的 JSON-LD context 简化对象时,https://www.w3.org/ns/activitystreams#Public 可被简化为 Publicas:Public,三者均为 Public 集合的有效表示。 只将对象视作普通 JSON 处理而不转换 context 的实现要兼容所有三者写法。

5.7 Likes Collection

每个对象可以likes 集合。 该集合包含所有以该对象为 objectLike 活动,为副作用自动添加。 likes 集合必须 OrderedCollection Collection可以结合权限过滤。

注意不要混淆 likes 与类似命名、但不同的 liked 集合。 总结如下:

5.8 Shares Collection

每个对象可以shares 集合。 这集合包含所有以此对象为 objectAnnounce 活动, 为副作用添加。 shares 集合必须 OrderedCollection Collection可以结合权限过滤。

6. 客户端到服务器交互

由 [ActivityStreams] 定义的 Activity,是在社交图谱中创建、修改和分享内容的核心机制。

客户端与服务器的交互通过客户端将 Activity 提交到参与者的outbox实现。 为此,客户端必须从其个人资料中发现 outbox 的 URL,然后必须对该 URL 发起 HTTP POST请求,Content-Type 为application/ld+json; profile="https://www.w3.org/ns/activitystreams"。 服务器可以将 Content-Type 或 Accept 头为application/activity+json视为等同于application/ld+json; profile="https://www.w3.org/ns/activitystreams"对待。 请求必须用 outbox 所属用户的身份凭证进行认证。 POST请求体必须包含一个 Activity(可以包含嵌套对象),或一个非 Activity 对象(服务器会自动包裹成 Create activity)。

示例 11:提交 Activity 到 Outbox
POST /outbox/ HTTP/1.1
Host: dustycloud.org
Authorization: Bearer XXXXXXXXXXX
Content-Type: application/ld+json; profile="https://www.w3.org/ns/activitystreams"

{
  "@context": ["https://www.w3.org/ns/activitystreams",
               {"@language": "en"}],
  "type": "Like",
  "actor": "https://dustycloud.org/chris/",
  "name": "Chris liked 'Minimal ActivityPub update client'",
  "object": "https://rhiaro.co.uk/2016/05/minimal-activitypub",
  "to": ["https://rhiaro.co.uk/#amy",
         "https://dustycloud.org/followers",
         "https://rhiaro.co.uk/followers/"],
  "cc": "https://e14n.com/evan"
}

如果 Activity 的 id 字段有值,服务器必须忽略该值并为其生成新的 id。 服务器必须返回 201 Created,除非该 activity 是临时的,否则必须Location 头返回新 id

示例 12:Outbox 对 Activity 的响应
HTTP/1.1 201 Created
Location: https://dustycloud.org/likes/345

服务器必须在分发前移除 ActivityStreams 对象的 bto 和/或 bcc 属性,但必须用它们原本保存的地址信息判断 投递时的收件人。

服务器必须把该 Activity 加入outbox集合。 根据 Activity 类型,服务器可能还需要执行其他副作用操作。 (但不能保证 Activity 何时出现在 outbox,中间可能有延迟,也可能随时删除)。 具体副作用见后续各 Activity 说明。

尝试向未实现客户端到服务器支持的服务器提交对象,建议返回 405 Method Not Allowed 响应。

HTTP 缓存机制 [RFC7234] 在客户端与服务器响应中,建议按需支持。

6.1 客户端寻址

客户端负责为新 Activity 合理寻址(指定收件人)。 在一定程度上这取决于客户端具体实现,但需注意服务器只会向tobtoccbccaudience 字段指定的收件人分发 Activity。

Followers 集合和/或Public 集合是新 Activity 默认寻址的好选择。

客户端建议遍历新 Activity 附带对象的 objecttargetinReplyTotag 字段,获取它们的 actorattributedTo 属性,还可以获取其地址信息,并将这些加入新 Activity 的 tocc 字段。 客户端可以递归遍历嵌套对象,如递归时建议设置递归深度限制。 (注意不建议“展开”被寻址的 actor 集合作为单独收件人)。

客户端可以为用户在界面提供修改寻址的机会。

例如 Chris 对 Amy 这篇文章点赞:

示例 13:一篇文章
{
  "@context": ["https://www.w3.org/ns/activitystreams",
               {"@language": "en-GB"}],
  "id": "https://rhiaro.co.uk/2016/05/minimal-activitypub",
  "type": "Article",
  "name": "Minimal ActivityPub update client",
  "content": "Today I finished morph, a client for posting ActivityStreams2...",
  "attributedTo": "https://rhiaro.co.uk/#amy",
  "to": "https://rhiaro.co.uk/followers/",
  "cc": "https://e14n.com/evan"
}

客户端产生如下 Like:

示例 14:对文章点赞
{
  "@context": ["https://www.w3.org/ns/activitystreams",
               {"@language": "en"}],
  "type": "Like",
  "actor": "https://dustycloud.org/chris/",
  "summary": "Chris liked 'Minimal ActivityPub update client'",
  "object": "https://rhiaro.co.uk/2016/05/minimal-activitypub",
  "to": ["https://rhiaro.co.uk/#amy",
         "https://dustycloud.org/followers",
         "https://rhiaro.co.uk/followers/"],
  "cc": "https://e14n.com/evan"
}

接收的 outbox 可据此将消息投递给 Chris(点赞者)的粉丝、Amy、Amy 的粉丝和收到原始文章的 Evan。

客户端向 outbox 提交如下活动时必须在 activity 中提供 object 属性: CreateUpdateDeleteFollowAddRemoveLikeBlockUndo。 另外针对 AddRemove,还必须一同提交 target 属性。

6.2 Create Activity

Create 活动用于发布新对象。 其副作用是 activity 中嵌入的对象(在 object 属性中)被创建。

Create 活动被提交时,活动的 actor建议复制到 objectattributedTo 字段。

Create activity 与包裹对象的地址信息不一致容易让人困惑。因此服务器建议在最初分发时,将 Create activity 的收件人复制到 object,或将 object 的收件人复制给外层 Create。 之后如 object 的收件人发生变更无需同步到 Create 的地址(如后续通过 Update activity)。

6.2.1 无 Create Activity 的对象创建

客户端到服务器时,也可直接提交一个对象,无需外层包裹 activity。 服务器必须接收有效的 [ActivityStreams] 对象,只要它不是 Activity 的子类型,即可直接 POST 至 outbox。 之后服务器必须用该对象作为 Create Activityobject,自动包裹一层。 如非临时对象,服务器还必须为外层 Create 和被包裹对象都分配 id

服务器返回的 Location 应为新的 Create 活动的 URL,而非对象本身。

对象中指定的 tobtoccbccaudience 字段,服务器必须同步复制到新的 Create activity。

示例 15:带 audience 定向属性的对象
{
  "@context": "https://www.w3.org/ns/activitystreams",
  "type": "Note",
  "content": "This is a note",
  "published": "2015-02-10T15:04:55Z",
  "to": ["https://example.org/~john/"],
  "cc": ["https://example.com/~erik/followers",
         "https://www.w3.org/ns/activitystreams#Public"]
}
上例提交后可被转化为:
示例 16:服务器生成的 Create Activity 包裹体
{
  "@context": "https://www.w3.org/ns/activitystreams",
  "type": "Create",
  "id": "https://example.net/~mallory/87374",
  "actor": "https://example.net/~mallory",
  "object": {
    "id": "https://example.com/~mallory/note/72",
    "type": "Note",
    "attributedTo": "https://example.net/~mallory",
    "content": "This is a note",
    "published": "2015-02-10T15:04:55Z",
    "to": ["https://example.org/~john/"],
    "cc": ["https://example.com/~erik/followers",
           "https://www.w3.org/ns/activitystreams#Public"]
  },
  "published": "2015-02-10T15:04:55Z",
  "to": ["https://example.org/~john/"],
  "cc": ["https://example.com/~erik/followers",
         "https://www.w3.org/ns/activitystreams#Public"]
}

6.3 Update Activity

Update 活动用于修改已有对象。 副作用是object必须被修改为更新活动中定义的新结构,前提是 actor 有权限。

6.3.1 部分更新

对于客户端-服务器交互,更新为部分更新;仅替换顶层字段中的任意键值,新值覆盖旧值。 若值为 JSON 的 null,表示该字段应从服务器上的对象表示中删除。

注意此行为仅适用客户端 POST 到服务器。服务器间通信和服务器到客户端推送应携带更新后的完整对象表示。 见 服务器间 Update 活动说明 了解详情。

6.4 Delete Activity

Delete 活动用于删除已有对象。 其副作用是服务器可以object 替换为 Tombstone 对象,以在引用已删除对象的活动中显示。 当请求已删除对象时,若响应体为 Tombstone,服务器建议返回 HTTP 410 Gone,否则返回 404 Not Found。

被删除的对象示例:

示例 17
{
  "@context": "https://www.w3.org/ns/activitystreams",
  "id": "https://example.com/~alice/note/72",
  "type": "Tombstone",
  "published": "2015-02-10T15:04:55Z",
  "updated": "2015-02-10T15:04:55Z",
  "deleted": "2015-02-10T15:04:55Z"
}

6.5 Follow Activity

Follow 活动用于订阅其他用户的动态。

该活动进入outbox的副作用是,当随后收到以该 Follow activity 作为 object 的 Accept activity 时,服务器建议object 添加到 actorfollowing 集合中。

6.6 Add Activity

Add 活动进入outbox时,服务器建议object 添加到 target 字段指定的集合,除非:

6.7 Remove Activity

Remove 活动进入outbox时,服务器建议objecttarget 集合中移除,除非:

6.8 Like Activity

Like 活动表示 actor 点赞了 object

该活动进入outbox的副作用是,服务器建议object 添加到 actorliked 集合中。

6.9 Block Activity

Block 活动表示发布该活动的 actor 不希望 object 属性指向的另一个 actor 能与自己发布的对象互动。 服务器建议阻止被屏蔽用户对 actor 发布对象的任何交互。

服务器不建议object 投递 Block 活动。

6.10 Undo Activity

Undo 活动用于撤销之前的操作。 参见 Activity Vocabulary 文档 逆活动和“Undo”。 例如 Undo 可撤销 LikeFollowBlock。 撤销活动与被撤销操作必须为同一 actor。 应尽可能撤销相应副作用。 比如撤销 Like,那么所有计数应相应减一。

某些情况存在明确“逆操作”活动应优先用之。 基于 Create 的活动用 DeleteAddRemove

6.11 投递

联邦服务器必须outbox 投递规则,对所有发布到outbox的 Activity 进行投递。

6.12 媒体上传

本节为非规范性内容。

服务器可以支持上传可以被活动引用的文档类型(如图片、视频等二进制数据),但具体机制不在本 ActivityPub 版本范围内。 社交 Web 社区组正在 ActivityPub Media Upload report中完善协议。

7. 服务器到服务器交互

服务器通过向参与者的 inbox 端点推送活动,与其它服务器通信并在社交网络内传播信息。 发送到网络上的 Activity 建议带有 id,除非设计为临时(这时 可以省略 id)。

POST 请求(如发送至 inbox)必须带有 Content-Type: application/ld+json; profile="https://www.w3.org/ns/activitystreams"GET 请求(参见 3.2 对象获取)要带 Accept 头: application/ld+json; profile="https://www.w3.org/ns/activitystreams"。 服务器建议将 Content-Type 或 Accept 头为 application/activity+json 视为等同于 application/ld+json; profile="https://www.w3.org/ns/activitystreams" 用于服务器间交互。

为了让更新在社交网络内传播,Activity 会投递给合适的收件人。 首先通过对象间的引用关系递归找到目标 actor,再插入该 actor 的 inbox投递)。 这样收件服务器可以:

常见触发投递的场景包括:

向其它服务器的 inboxsharedInbox 投递 activity 时,服务器必须在 activity 中提供 object 属性: CreateUpdateDeleteFollowAddRemoveLikeBlockUndo。 此外,发送下列活动时,必须一同提供 target 属性:AddRemove

HTTP 缓存机制 [RFC7234] 建议在适当时支持,可用于处理服务器响应和接收。

7.1 投递

下述内容仅适用于实现了服务器间联邦通信的服务器。

活动投递给目标(即actor)时,首先查找目标 actor 的 inbox,然后将 activity post 到该 inbox。 投递目标通过查阅 ActivityStreams 的 audience targeting 字段决定,即 tobtoccbccaudience

获取 inbox 的方法:检索目标 actor 的 JSON-LD 表示,然后查找 inbox 属性。 若收件人为 CollectionOrderedCollection,服务器必须用用户的凭证 dereference 该集合,并为集合内每项查找 inbox。 服务器必须限制集合递归查找的层数,可以只查一层。

服务器必须对最终收件人去重;还必须排除 activity 的 actor 本人,也就是说,不应把 activity 推送给自己。

:静默和私有活动

若无收件人指定该怎么办未明确定义,但建议如无收件人,则对象应完全私有,访问权限应受限。 若对象仅发送到 "public" 集合,则不会投递给任何 actor,但可在 actor 的 outbox 公开浏览。

然后向 inbox 发起带用户认证信息的 HTTP POST,请求体为 Activity。 收件方将该 Activity 作为 inbox OrderedCollection 的 item 添加进去。 向非联邦服务器的 inbox 投递时,建议返回 405 Method Not Allowed

联邦服务器向第三方服务器投递推荐异步进行,如因网络故障导致失败建议自动重试。

注意:同源 actor 之间的 Activity 分发可以选择任何内部机制,不一定使用 HTTP。

:与 Linked Data Notifications (LDN) 关系

虽然不要求理解本规范需先读此内容,但需要说明 ActivityPub 的投递机制与 Linked Data Notifications 存在重叠,两者可以互操作。 特别是 inbox 属性在 ActivityPub 和 LDN 中都是一样的,本规范描述的投递机制同样被 LDN 支持。 LDN 支持多种 RDF 序列化格式,并不要求 ActivityPub 实现支持,但希望更好兼容 LDN 的系统可考虑支持更多格式。

7.1.1 服务器到服务器 outbox 投递要求

当对象被投递到 outbox (适用于既实现客户端-服务器交互 又实现服务器-服务器交互的服务器), 服务器必须投递到:

  • tobtoccbccaudience 字段,且其值为该 actor 拥有的单个或集合。

这些字段已经被客户端寻址逻辑预先填充。

7.1.2 inbox 转发

:为避免“幽灵回复”问题而转发

下节内容为了解决在联邦网络中偶尔出现的“ghost replies(幽灵回复)”问题。 其问题最好通过实际例子说明。

Alyssa 发了个她在会议上成功报告论文的帖子,收件人是她的 followers 集合,包括她的朋友 Ben。 Ben 回帖祝贺她,并同样将 Alyssa 的 followers 集合作为收件人。 但 Ben 无权看到 Alyssa 的 followers 成员,因此他的服务器无法将消息转发给他们。 若无这个转发机制,Alyssa 再回复 Ben,Alyssa 的粉丝就会看到她直接回复 Ben,然而 Ben 的内容根本没显示,这让人很困惑!

当 Activity 到达 inbox 后,服务器需将它转发给原发布方无法直达的收件人。 为此,服务器必须仅在下面三个条件都满足时,针对 tocc 和/或 audience 的值再次投递

  • 这是服务器首次收到该 Activity。
  • tocc 和/或 audience 指向本服务器拥有的集合。
  • inReplyToobjecttarget 和/或 tag 中的对象属于本服务器。 服务器建议递归检查这些字段值,寻找属于本服务器的对象,并建议限制最大递归层数(如话题已深入,粉丝不再关心与自身无关的内容)。 服务器必须仅对原始对象的 toccaudience 转发,不采集递归对象的新收件人(以防收件人为客户端故意更改)。

服务器可以按实现规则过滤投递对象(如垃圾信息过滤)。

7.1.3 共享收件箱投递

对于托管多个 actor 的服务器,向所有粉丝逐个投递会带来大量消息。 有些服务器还需展示“已知网络”中全部公开消息。 因此 ActivityPub 提供了这个可选机制满足两需求。

当对象要发给本 actor 粉丝时,服务器可以把拥有相同sharedInbox的所有粉丝合并,统一将对象投递到该 sharedInbox,以减少实际独立收件人数量。 这种情况下,远端服务器将实际完成具体 inbox 的分发。

另外,如向 Public 特殊集合寻址,服务器可以将对象分发给全网络已知的所有 sharedInbox 端点。

源服务器向 sharedInbox 发送公开活动时,必须保证那些无 sharedInbox 汇总或不能通过 sharedInbox 获取的 actor 及集合,依然可以按 tobtoccbccaudience 单独投递,确保都能收到。

7.2 Create Activity

收到 Create 活动添加到 inbox,理论上副作用极少;活动会显示在 actor 的 inbox,通常服务器会在本地保存活动及对象的表示。 不过这和一般接收 inbox 活动的处理并无不同。

7.3 Update Activity

服务器间的 Update 活动,接收服务器建议用 Activity 中新对象内容替换本地相同 idobject 副本。 与 客户端到服务器交互不同,此为全量替换而非部分更新。

接收服务器必须确保 Update 活动有权修改该 object。最小条件为 Update 及其对象必须同源。

7.4 Delete Activity

收到该活动的副作用为(假定 object 属于发送方 actor / 服务器),接收服务器建议移除等 id 对象的本地副本,并可以Tombstone 替换之。

(注意 ActivityPub 无法强制远端服务器根据活动删除接收对象副本,协议仅限于原始发送服务器→远端)。

7.5 Follow Activity

收到该活动添加到 inbox 时,服务器建议生成 AcceptReject 活动,并将其作为该 Followobject 投递给 Follow 的 actor。 AcceptReject可以自动生成,也可以经用户审核后手动。 服务器可以选择不明确回应 Reject,但实现需知这样会导致提交方服务器处于中间态。例如为了保护用户隐私而不返回 Reject

如果收到一个引用该 Follow 为 object 的 Accept,服务器建议将该 actor 加入 object actor 的Followers 集合。 若收到 Reject,服务器禁止将 actor 添加到 object actor 的Followers 集合

有时 Follow 订阅可能成功,但以后会因为网络原因长时间无法送达 follower。 实现需考虑,并不能保证网络中的 actor 始终可达,应合理处理。 例如长达半年的投递都不可达,服务器可合理地从 followers 清除该订阅者。 所采用策略和时长由服务器自行决定。

7.6 Accept Activity

接收 inbox 中 Accept 的副作用取决于收到的 object 类型,也可用于本文件未描述的类型(如 Offer)。

如果 Acceptobject 为事先发送的 Follow 活动,服务器建议将 actor 加入接收者的Following 集合

7.7 Reject Activity

接收 inbox 中 Reject 的副作用取决于收到的 object 类型,也可用于本文件未描述的类型(如 Offer)。

如果 Rejectobject 为事先发送的 Follow 活动,这意味着收件人未批准 Follow 请求。服务器禁止将 actor 加入接收者的Following 集合

7.8 Add Activity

收到 Add 活动进入 inbox 时,服务器建议object 添加到 target 指定的集合,除非:

7.9 Remove Activity

收到 Remove 活动进入 inbox 时,服务器建议objecttarget 指定集合移除,除非:

7.10 Like Activity

收到该活动进入 inbox 的副作用为,服务器建议增加对象的点赞数,将收到的活动加进 likes 集合(如有)。

7.11 Announce Activity(分享)

收到 Announce 活动进入 inbox 时,服务器建议增加对象 share 次数,将收到的活动加进 shares 集合(如有)。

Announce 活动实际上就是其他社交网络中的“分享”“转发”或“boost”。

7.12 Undo Activity

Undo 活动用于撤销上一次活动的副作用。参见 ActivityStreams 文档 逆向活动与“Undo”Undo 活动的范围和约束与 客户端-服务器交互上下文下的 Undo 活动 一致,但用于联邦场景。

A. 国际化

本节为非规范性内容。

在一个联邦网络中,构建全球用户基础是非常重要的。 ActivityStreams 提供了内容国际化的相关工具,应尽可能加以利用。 不过,实现上有时难以确定为用户生成内容指定哪个 @language 属性W3C 国际化小组 提供了 语言检测的指导

B. 安全性注意事项

本节为非规范性内容。

B.1 认证与授权

ActivityPub 在两个场景下用到身份认证:一是客户端到服务器认证,二是在联邦实现中服务器互相认证。

很遗憾,在标准制定时,并未有广泛认同的认证机制。 更多认证实现方向可参见 Social Web Community Group 关于认证与授权最佳实践报告

B.2 验证

服务器不应信任客户端上传内容,联邦服务器也应对来自非原始来源的内容做某种校验,不能轻信。

服务器应确保新内容确实由声明的 actor 所发,并检查 actor 是否有权限更新声明的资源。 参见 3. 对象 以及 B.1 认证与授权

B.3 访问本地 URI

开发时用本地进程测试很方便。 但生产环境下允许客户端或服务器访问本地 URI 是很危险的。 对本地 URI 做未授权请求可能无意中访问或修改到原本仅本地主机可用的受保护资源。

若你的 ActivityPub 服务器或客户端为开发便捷而允许访问本地 URI,建议将此设置为默认关闭的配置项。

B.4 URI 方案

除了 httphttps,还有许多类型的 URI。 有些库处理不同 URI scheme 时会尝试“聪明地”引用某些不合规范的 scheme,比如 file。 客户端和服务器开发者应仔细检查库处理请求的方式,最好只白名单安全的 URI 类型,如 httphttps

B.5 递归对象

服务器在解析对象时应限制递归深度,或特殊处理带递归引用的 ActivityStreams 对象。 否则可能导致拒绝服务类安全漏洞。

B.6 垃圾信息

任何网络都难免有垃圾信息,在联邦网络中尤为如此。 ActivityPub 未指定防垃圾内容机制,但建议服务器对本地不受信任用户及任何远端用户内容都加某种反垃圾处理。

B.7 联邦拒绝服务攻击

服务器应有机制防范来自其他联邦服务器的拒绝服务攻击(DOS)。 比如可使用限流规则。 对涉及副作用的 activity,服务器应特别注意实现防护。 服务器 建议采用自适应重试(如指数退避)来避免请求过度拖垮目标服务器。

B.8 客户端-服务器限流

服务器应对 API 客户端做限流管理。 这样做有两个好处:

  1. 防止恶意客户端发起拒绝服务攻击服务器。
  2. 保证服务器不会因分发过多 activity 而触发其他服务器的拒绝服务保护

B.9 客户端-服务器响应拒绝服务

为防止客户端被超大集合内容拖垮,服务器应限制返回集合页的大小。 客户端自身也应限制愿意处理的响应内容大小,比如强制超时和报错,以防接入恶意或被攻破的服务器。

B.10 内容净化

任何渲染到浏览器(或富文本应用)的 activity 字段都应规范净化,移除危险标记,防止 XSS 攻击。

B.11 不显示 bto 和 bcc 属性

btobcc 已要求分发前移除, 但服务器可以自行决定如何内部存储。 但由于 btobcc 仅供原作者知晓,服务器在展示内容时也应省略这些属性。

C. 致谢

本节为非规范性内容。

本规范源自多个社区多年在 Web 联邦领域的深耕与经验。 尤其是 OStatusPump API ,以 StatusNet(现 GNU Social)和 Pump.io 为代表,为本规范贡献了大量实践,但最主要的推动者仍是 Evan Prodromou,没有他的持续付出,ActivityPub 不可能有今天的样子。

Erin Shepherd 撰写了初版规范,灵感来自 Pump API 文档,内容几乎完全重写,不过主观点沿用,只是从 ActivityStreams 1 切换到了 ActivityStreams 2。

当标准移交到 W3C Social 工作组后,Jessica Tallon 与 Christine Lemmer-Webber 担任编辑,她们完成了从 Erin Shepherd 版规范到 ActivityPub 现状的大部分结构和文本重构。 本文档大部分内容都在 Working Group 漫长反馈期内重新编写和梳理。

ActivityPub 的最终形态离不开 W3C Social 工作组众多成员的精心投入。 特别感谢 Amy Guy,她为组内文档互相关联和 [Social-Web-Protocols] 的整体梳理做出了最大贡献。 Amy 还与 Christopher Allan Webber 一起,用为期四天的马拉松推进规范大重构,这推动了客户端到服务器与服务器组件之间的分离,以及与 [LDN] 等规范关系的澄清和其他诸多改进。 特别感谢 Benjamin Goering 制作实现报告模板。 也要感谢 mray 绘制的精彩教程插画(与本规范采用同一许可证)。

许多人也通过细致审阅助力了 ActivityPub,特别致谢: Aaron Parecki, AJ Jordan, Benjamin Goering, Caleb Langeslag, Elsa Balderrama, elf Pavlik, Eugen Rochko, Erik Wilde, Jason Robinson, Manu Sporny, Michael Vogel, Mike Macgirvin, nightpool, Puck Meerburg, Sandro Hawke, Sarven Capadisli, Tantek Çelik 和 Yuri Volkov。

本文献给地球上的所有人——你们应该享有沟通自由;希望我们为这项权利和目标的推进尽了绵薄之力。

D. 参考文献

D.1 规范性引用

[Activity-Vocabulary]
Activity 词汇表. J. Snell. ActivityStreams 工作组. 编辑草案. URL: https://www.w3.org/TR/activitystreams-vocabulary/
[ActivityStreams]
Activity Streams 2.0. J. Snell. ActivityStreams 工作组. 编辑草案. URL: https://www.w3.org/TR/activitystreams-core/
[JSON-LD]
JSON-LD 1.0. Manu Sporny; Gregg Kellogg; Markus Lanthaler. W3C. 2014年1月16日. W3C 推荐. URL: https://www.w3.org/TR/json-ld/
[LDN]
链接数据通知 (LDN). Sarven Capadisli; Amy Guy. W3C. 2017年5月2日. W3C 推荐. URL: https://www.w3.org/TR/ldn/
[RFC2119]
RFC 规范性用词需求定义. S. Bradner. IETF. 1997年3月. 最佳当前实践. URL: https://tools.ietf.org/html/rfc2119
[RFC7231]
超文本传输协议 (HTTP/1.1):语义与内容. R. Fielding, Ed.; J. Reschke, Ed.. IETF. 2014年6月. 提案标准. URL: https://tools.ietf.org/html/rfc7231
[RFC7234]
超文本传输协议 (HTTP/1.1):缓存机制. R. Fielding, Ed.; M. Nottingham, Ed.; J. Reschke, Ed.. IETF. 2014年6月. 提案标准. URL: https://tools.ietf.org/html/rfc7234

D.2 补充性引用

[Micropub]
Micropub. Aaron Parecki. W3C. 2017年5月23日. W3C 推荐. URL: https://www.w3.org/TR/micropub/
[RFC6749]
OAuth 2.0 授权框架. D. Hardt, Ed.. IETF. 2012年10月. 提案标准. URL: https://tools.ietf.org/html/rfc6749
[RFC6750]
OAuth 2.0 授权框架:Bearer Token 使用. M. Jones; D. Hardt. IETF. 2012年10月. 提案标准. URL: https://tools.ietf.org/html/rfc6750
[Social-Web-Protocols]
社交 Web 协议. Amy Guy. W3C. 2017年12月25日. W3C 注释. URL: https://www.w3.org/TR/social-web-protocols/