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 Working Groupによって勧告として発行されました。

興味のある方はどなたでも、ワーキンググループのIssue tracker で実装やバグ報告、その他コメントを提供できます。 これらはSocial Web Community Group で議論され、将来のこの仕様のバージョンで考慮されます。

ワーキンググループの 実装レポート も参照してください。

この文書はW3Cのメンバー、ソフトウェア開発者、その他W3Cグループや関係者によってレビューされ、ディレクターによりW3C勧告として承認されています。 安定した文書であり、参考資料として利用したり他の文書から引用したりできます。W3Cが勧告を発行する役割は、仕様に注目を集め、その広範な導入を促進する点にあります。これによりWebの機能と相互運用性が向上します。

この文書はW3C パテントポリシーのもとで運営されているグループによって作成されました。 W3Cは、グループの納品物に関連して公開特許開示リストを管理しています。 そのページには、特許開示の方法についての案内も記載されています。自らが実際に把握する特許が 必須クレームに含まれると考える場合は、 W3Cパテントポリシー第6節 に従い情報を開示しなければなりません。

この文書は、2017年3月1日 W3C プロセス文書に従って管理されています。

1. 概要

ActivityPubは2つのレイヤーを提供します:

ActivityPubの実装はこれらのいずれか一方、または両方を実装することができます。 ただし一方を実装したなら他方を実装するのもそれほど多くの手順はなく、どちらも実装することで(サイトを分散型ソーシャルウェブの一部とし、さまざまなソーシャルサービスに共通するクライアントやクライアントライブラリが利用できるなど)多くのメリットがあります。

ActivityPubにおいてユーザーはサーバ上のアカウントを通じて "アクター" として表現されます。 異なるサーバ上のユーザーアカウントは異なるアクターに対応します。 すべてのアクターは以下を持ちます:

inboxとoutboxを持つアクター

これらはエンドポイント、つまり実体としてはActivityPubアクターのActivityStreams記述にリストされるURLです。 (ActivityStreamsについては後述)

以下は友人Alyssa P. Hackerのレコード例です:

Example 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はソーシャルネットワークを流れる全てのアクションやコンテンツを表現するために必要な共通用語を網羅しています。 おそらくActivityStreamsは必要な語彙を既に持っていますが、もしそうでなくても[JSON-LD]を使ってActivityStreamsを拡張できます。 JSON-LDを知っていれば、そのリンクドデータのアプローチの利点を活かせます。 知らなくても大丈夫です、JSON-LD文書やActivityStreamsは普通のシンプルなJSONとして理解できます。 (拡張したい場合にはJSON-LDが強力な助けとなります)

さて、Alyssaは友達とやりとりしたいし、友達もAlyssaとやりとりしたい! そんな時「inbox」と「outbox」が活躍します。 これらはGETとPOSTで動作が異なります。 どのように動くか見てみましょう:

世界からinboxへ、outboxから世界へメッセージが流れるアクター

では要点をまとめます:

もちろん、もし最後の(他人のoutboxからGETするだけ)が唯一のメッセージ閲覧手段だったら、これは効率のよい連合プロトコルになりません。 実際には、連合は通常サーバがアクターのメッセージを他サーバ上のアクターのinboxにPOSTすることで成立します。

例を見てみましょう! Alyssaが友人Ben Bitdiddleと連絡を取りたいとします。 彼女は最近Benに本を貸したので、返してほしいと思っています。 彼女が作成したActivityStreamsオブジェクトは次のようになります:

Example 2
{"@context": "https://www.w3.org/ns/activitystreams",
 "type": "Note",
 "to": ["https://chatty.example/ben/"],
 "attributedTo": "https://social.example/alyssa/",
 "content": "ねえ、貸した本はもう読み終わった?"}

これはBen宛てのNoteです。 彼女はこれを自分のoutboxへPOSTします。

アクターがoutboxへメッセージを投稿する

これはアクティビティ系オブジェクトではないので、サーバはこれは新規作成されたオブジェクトだと認識し、Createアクティビティでラップしてくれます。 (ActivityPubで送受信されるアクティビティはおおむね「誰かアクターが、何かオブジェクトについて、何らかの行為を行う」というパターンをたどります。ここではPersonがNoteオブジェクトをCreateするアクティビティです。)

Example 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アクターオブジェクトとそのinboxエンドポイントを探し、BenのinboxへこのオブジェクトをPOSTします。

サーバがリモートアクターのinboxへPOSTしている

技術的にはこれは2つの独立したステップです……ひとつはクライアントからサーバへの通信、もうひとつはサーバ間の通信(連合)。 ですが、この例では両方使っているので、「outboxからinboxへのスムーズな流れ」と抽象化して考えられます:

あるアクターのoutboxから他アクターのinboxへのNoteの流れ

やったね! しばらくしてAlyssaは新着メッセージを確認します。 彼女のスマホがinboxをGETしてチェックすると、友人たちの猫動画や妹が投稿した甥っ子の写真と一緒に、こんなレスが届いています:

Example 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の投稿に「Like」します:

Example 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"}

Alyssaはこのメッセージを自分のoutboxへPOSTします。 (これはアクティビティなので、サーバ側はCreateでラップする必要がないと判断します。)

満足したAlyssaは、フォロワー全員に向けて公開メッセージを投稿することにします。 まもなく彼女のfollowersコレクション全員に加え、特別なPublicグループも宛先に含まれているため、基本的に誰でも読める形で次のメッセージが配信されます。

Example 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 Working Group

ActivityPubは、Social Web Working Groupが策定する複数の関連仕様の1つです。 他の手法や補完的プロトコルに関心のある実装者は [Micropub] や 概要ドキュメント [Social-Web-Protocols] を確認するとよいでしょう。

2. 適合性

非規範的と明記されたセクション以外にも、この仕様のすべての著述ガイドライン、図、例、および注記は非規範的です。それ以外のこの仕様の内容はすべて規範的です。

この仕様内の MAY, MUST, MUST NOT, SHOULD, SHOULD NOT というキーワードは[RFC2119]で示される意味として解釈されます。

2.1 仕様プロファイル

この仕様は2つの密接に関連し相互に作用するプロトコルを定義します:

クライアントからサーバへのプロトコル、または"Social API"
このプロトコルにより、クライアントはユーザーを代表して動作できます。 例えばモバイルアプリがユーザーアクターのソーシャル・ストリームとやりとりするのに使われます。
サーバ間プロトコル、または"連合プロトコル"
このプロトコルは、異なるサーバ上のアクター間のアクティビティを分配し、同じソーシャルグラフにつなげます。

ActivityPub仕様は、このいずれか片方を実装した後で、もう一方もほとんど手間なく追加できるよう設計されています。 ただしサーバは片方だけ実装しても問題ありません。 このため、以下の3つの適合クラスが定義されます:

ActivityPubに準拠したクライアント
これはクライアントからサーバプロトコルのクライアント部全体を実装したものに該当します。
ActivityPubに準拠したサーバ
これはクライアントからサーバプロトコルのサーバ部全体を実装したものに該当します。
ActivityPubに準拠した連合サーバ
これは連合プロトコル全体を実装したものに該当します。

仕様の一部が連合プロトコルの実装時のみに適用される場合、このことが明確に示されます。 また要件指定時は、それがクライアント/サーバ(クライアントからサーバプロトコル)に該当するのか、 サーバ間プロトコルの送信サーバ/受信サーバに該当するのか明示されます。

3. オブジェクト

オブジェクトは、[ActivityStreams]および ActivityPubが構築される中心概念です。 オブジェクトは多くの場合アクティビティでラップされ、ストリームとしてコレクションに含まれます。そしてコレクション自体もオブジェクトのサブクラスです。 詳細は[Activity-Vocabulary]ドキュメント、特に コアクラスを参照してください。 ActivityPubはこの語彙の割り当てを忠実に踏襲しています。

ActivityPubはActivityStreamsで提供される用語に加えて、いくつかの用語を定義します。 それらはActivityPub JSON-LDコンテキスト https://www.w3.org/ns/activitystreams で提供されます。 実装者はオブジェクト定義にActivityPubコンテキストを含めるべきです。 必要に応じて追加のコンテキストを含めても構いません

ActivityPubは ActivityStreamsと同じURI/IRI慣例 を共有します。

サーバは受信したコンテンツの検証を行うべきです。これはコンテンツのなりすまし攻撃を避けるためです。 (サーバは少なくとも、オブジェクトが送信元で受信時と同じものであるかチェックすべきですが、署名検証などが利用できるなら、そちらの方がより堅牢です。) この文書では検証メカニズムは明示的に定めませんが、セキュリティの考慮事項に良い手法例がありますので参照ください。

例として、example.comが以下のアクティビティを受信した場合
Example 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"
  }
}
受信側はidの参照先にアクセスし、それが存在し有効なオブジェクトか、また内容が偽装されていないかも確認すべきです。 (この例ではMalloryがAliceの名前でオブジェクトを偽装しようとしている可能性があります。)

3.1 オブジェクト識別子

[ActivityStreams]の全オブジェクトはグローバルに一意な識別子を持つべきです。 ActivityPubはこの要件を拡張し、ActivityPubプロトコルで配信される全オブジェクトは、意図的に一時的なもの(チャットやゲーム通知のように参照を意図しない短命アクティビティ)を除き、グローバルに一意な識別子が必須となります。 識別子は以下のいずれかに該当する必要があります:

  1. 公開参照可能なURI、たとえばHTTPS URIで、その所有権が元サーバに属していること。 (公開向けコンテンツはHTTPS URIを使うべきです。)
  2. JSONのnullとして明示的に指定されたID、つまり匿名オブジェクト(親コンテキストの一部)

サーバ間連携で配信されるアクティビティには、意図的に一時的でない限り識別子が必須です。 ただしクライアントからサーバへの通信では、outboxにID指定無しで投稿されたオブジェクトを受信するサーバは、アクターの名前空間内でIDを割り当ててオブジェクトに付与すべきです

全オブジェクトは次のプロパティを持ちます:

id
(一時的オブジェクトでなければ)オブジェクトのグローバル一意識別子
type
オブジェクトの種別

3.2 オブジェクトの取得

HTTP GETメソッドは、オブジェクトのidプロパティに対して発行することで、そのアクティビティを取得できます。 サーバは、[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エラーで拒否すべきです。 存在自体が秘匿されるべきとサーバが判断した場合は、404 Not Foundでレスポンスしてもよいです。

3.3 sourceプロパティ

[Activity-Vocabulary]で定義されたすべてのプロパティに加えて、ActivityPubはObjectsourceプロパティを拡張します。 sourceは、contentマークアップの由来を示す情報や証跡、将来の編集機能サポートなどを伝える用途のためのものです。 一般に、sourcecontentへの変換はクライアントが担い、逆は行いません。

sourceの値はオブジェクトで、content および mediaType フィールドを使い元情報を与えます。

Example 8
{
  "@context": ["https://www.w3.org/ns/activitystreams",
               {"@language": "en"}],
  "type": "Note",
  "id": "http://postparty.example/p/2415",
  "content": "<p>I <em>really</em> like strawberries!</p>",
  "source": {
    "content": "I *really* like strawberries!",
    "mediaType": "text/markdown"}
}
: クライアントがmediaTypeを扱えない場合どうする?

一般的には、ユーザーが最初に作成した元の投稿を、それと同じソース形式で編集できるのが最適です。 ただし全てのクライアントがあらゆるソースタイプに対して良いUIを提供できるわけではありません。 クライアントはsourcecontentの変換を担う前提なので、あるクライアントが対応しているmediaTypeでも他のクライアントが扱えない場合があります。 クライアントがcontentマークアップのみ編集用に提供しsourceを無視することも可能ですが、その場合、将来の修正時に元のsource形式が失われてしまうことになります。 こうした場合、クライアントは元のソース形式を認識できず無視する旨、できるだけ控えめな警告を表示するのが望ましいです。

例として、Alyssa P. Hackerは自作のEmacsクライアントと Org modeを使い、ActivityPub対応ブログへ投稿しています。 後日、スマホクライアントから編集しようとしますが、そのクライアントはtext/x-orgを知らずHTMLへの変換にも対応していないので、元のcontent編集用テキストボックスのみ表示されます。 編集画面の上部には「この投稿は元々対応していないマークアップ形式で書かれています。編集すると元のソースが失われます」といった警告が表示されます。 Alyssaは小さなタイプミスを直すためだけにorg-modeマークアップを失うのは惜しいと考え、自宅で修正することにしました。

4. アクター

ActivityPubのアクターは一般的には ActivityStreams アクター型 のいずれかですが、必ずしもそうである必要はありません。例えば Profileオブジェクト をアクターとして使ってもいいですし、ActivityStreamsの拡張タイプを使っても構いません。 アクターは、ActivityPub内の他のオブジェクト同様に取得されます。 他のActivityStreamsオブジェクトと同じように、アクターもid(URI)を持ちます。 このidを(例えばログインフォームのようなUIで)直接入力する場合、より簡易な名前表記のサポートが望ましいです。 このため、IDの正規化を以下のように推奨します:

  1. 入力されたIDが有効なURIなら、そのまま使用します。
  2. ユーザーがURIスキームを付け忘れた場合(例: example.org/alice/ など)で、 通常であれば有効URIとなりうる場合、クライアントはデフォルトスキーム(できればhttps)の自動補完を試みてもよい
  3. 上記いずれにも当てはまらない場合、入力値は無効と見なします。

アクターのURIが特定できたら、それをデリファレンス(参照)すべきです。

ActivityPubは「ユーザー」とアクター間の関係性を固定していません。さまざまな構成が可能です。 1人または複数の人間ユーザーや組織が1つのアクターを管理することも、逆に1人の人間や1つの組織が複数のアクターを管理する場合もあります。また、アクターがボットや自動プロセスのようなソフトウェアを表すこともあります。 さらに詳細な「ユーザー」モデル化(たとえば、同じ主体が制御するアクター同士を紐付ける、1つのアクターを複数のプロファイルや側面で提示可能にするなど)は実装の裁量に委ねられます。

4.1 アクターオブジェクト

アクターオブジェクトは3.1 オブジェクト識別子の必須プロパティに加えて、以下のプロパティを必ず持たなければなりません:

inbox
アクターが受信したすべてのメッセージを含む [ActivityStreams] OrderedCollection への参照。詳しくは 5.2 Inbox を参照。
outbox
アクターが生成したすべてのメッセージを含む [ActivityStreams] OrderedCollection への参照。詳しくは 5.1 Outbox を参照。

実装は、さらに次のプロパティも提供することが望ましいです:

following
このアクターがフォローしているアクターの [ActivityStreams] コレクションへのリンク。5.4 フォロー中コレクションを参照。
followers
このアクターをフォローしているアクターの [ActivityStreams] コレクションへのリンク。5.3 フォロワーコレクションを参照。

実装は、さらに次のプロパティも任意で提供できます:

liked
このアクターが「いいね」したオブジェクトの [ActivityStreams] コレクションへのリンク。5.5 Likedコレクションを参照。
Example 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オブジェクト。 これらはこのアクター用、あるいはこのアクターを参照する他者用に有用です。 このマッピングはアクタードキュメントの中に値としてネストされるか、または別のJSON-LDドキュメントへのリンクでもかまいません。

endpointsマッピングには、以下のプロパティを含めてもよいです:

proxyUrl
このアクターのクライアントが認証を必要とするリモートActivityStreamsオブジェクトへアクセスできるようにするエンドポイントURI。 このエンドポイント利用時は、クライアントはx-www-form-urlencodedidパラメータに、取得したいActivityStreamsオブジェクトのidを指定してPOSTします。
oauthAuthorizationEndpoint
クライアントからサーバ連携で OAuth 2.0ベアラートークン [RFC6749][RFC6750] を認証用途で使う場合、 ブラウザ認証済みユーザーが新しい認可を取得できるURIを指定します。
oauthTokenEndpoint
クライアントからサーバ連携で OAuth 2.0ベアラートークン [RFC6749][RFC6750] を認証用途で使う場合、 クライアントがアクセストークンを取得できるURIを指定します。
provideClientKey
Linked Data SignatureやHTTP Signatureで認証・認可を行う場合、クライアントの公開鍵認可のためのURI(クライアントからサーバー連携用、ブラウザ認証ユーザー向け)を指定します。
signClientKey
Linked Data SignatureやHTTP Signatureで認証・認可を行う場合、他サーバとのやり取りでクライアントキーが一時的にアクター代理として署名されるためのURIを指定します。
sharedInbox
公開宛やフォロワー宛アクティビティの広域配信用 のオプションエンドポイントです。 sharedInboxエンドポイントは、Public特別コレクション宛のオブジェクトを収めた 公開読み取り可能なOrderedCollectionオブジェクトとしてもあるべきですsharedInboxへのGETでは、Public宛でないオブジェクトは絶対に表示してはいけません

ActivityPubの上流語彙である [ActivityStreams] の あらゆるプロパティはActivityPubアクターにも利用可能です。 とくにActivityPub実装でどのように使われるか示す目的で、いくつかのActivityStreamsプロパティは強調して紹介されます。

url
アクターの「プロフィールWebページ」へのリンク(idと異なる場合)。
name
アクターの好ましい「ニックネーム」や「表示名」。
summary
ユーザーによる自己紹介や要約。
icon
プロフィール画像(またはImageオブジェクト)へのリンク(サムネイルの場合もある)。

namepreferredUsernamesummary など 自然言語値を含むプロパティは ActivityStreamsで定義された自然言語サポートを利用します。

5. コレクション

[ActivityStreams] はコレクションという概念を定義していますが、ActivityPubは特別な振る舞いを持ついくつかのコレクションも定義します。 ActivityPubは ActivityStreamsのページング を利用して大量のオブジェクトを巡回することができます。

これらコレクションの一部は OrderedCollection 型である必要が明記されていますが、他は Collection または OrderedCollection のいずれかでも構いません。 OrderedCollection は常に逆時系列(新しいものが先)で一貫して提示されなければなりません。

どのプロパティで逆時系列順を判定するかは実装詳細として意図的に規定しません。 多くのSQL型データベースではインクリメントする整数IDを利用し、たいていの場面で挿入順の管理に用いられます。 他のデータベースでは挿入時タイムスタンプの方が好まれるかもしれません。 何を使うかは重要ではありませんが、要素の順序は必ず新しいものが先に来るよう維持しなければなりません。 「最終更新時刻」など頻繁に変化するプロパティは使用しないで下さい。

5.1 Outbox

Outboxはアクターのプロフィールのoutboxプロパティによって発見されます。 outbox必ず OrderedCollectionでなければなりません。

Outboxストリームはユーザーが公開したアクティビティを含みますが、これはリクエスタがそのアクティビティを取得できる権限を持っている場合に限ります(つまり、Outboxの内容は閲覧者の権限によってフィルタされます)。 ユーザーが認可無しでリクエストした場合、サーバはすべての公開宛の投稿を返すべきです。 これは当該ユーザーによる公開済みオブジェクトの全てとなる可能性もありますが、何件返すかはサーバ実装者の裁量に委ねられます。

OutboxはHTTP POSTリクエストも受け付け、その挙動はクライアントとサーバーのやりとりセクションで説明されています。

5.2 Inbox

Inboxはアクターのプロフィールのinboxプロパティによって発見されます。 inbox必ず OrderedCollectionでなければなりません。

Inboxストリームはアクターが受信したすべてのアクティビティを含みます。 サーバはリクエスタの権限に応じて内容をフィルタするべきです。 一般的にInbox所有者はInboxの内容全てにアクセス可能ですが、アクセス制御によって他者には公開/非公開が分かれ、認証無しの利用者にはアクセスできない場合もあります。

サーバはInbox内で返すアクティビティの重複排除を必ず行う必要があります。たとえば1つのアクティビティがアクターのフォロワー全員ならびに特定のアクター(そのアクターもフォロワーである)の両方に宛てられた場合で、サーバが受取人リストの重複排除に失敗した場合などです。 この重複排除は、アクティビティのidを比較し、すでに見たものは除外することで必ず 行います。

連合サーバ上のアクターのInboxはHTTP POSTリクエストも受け付け、その挙動は配信セクションで説明されています。 非連合サーバはPOSTリクエストを受け取った場合405 Method Not Allowedを返すべきです。

5.3 フォロワーコレクション

すべてのアクターフォロワーコレクションfollowersを持つべきです。 これは、そのアクターにFollowアクティビティを送信した全ての人の一覧であり、 副作用として追加されます。 ここでアクターをフォローしている全アクターのリストが取得できます。 followersコレクションは必ず OrderedCollection または Collection でなければならず、さらに認証ユーザの特権や認証なしの場合の適宜な条件でフィルタ可能です。

: 通知配信のデフォルトターゲット

Followアクティビティは一般的にアクターの作成したオブジェクトを見るリクエストとして扱われます。したがって、Followersコレクションは通知配信時のデフォルトターゲットとして適切です。

5.4 フォロー中コレクション

各アクターはfollowingコレクションを持つべきです。 これはアクターがフォローした全員のリストであり、副作用として追加されます。 followingコレクションは必ず OrderedCollection または Collection でなければならず、必要に応じて認証ユーザの特権によるフィルタや認証無し時の適切な制限が施されてもよいです。

5.5 Likedコレクション

各アクターはlikedコレクションを持ってもよいです。 これは、アクターが行った全てのLikeアクティビティの対象オブジェクトのリストであり、 副作用として追加されます。 likedコレクションは必ず OrderedCollection または Collection でなければならず、必要に応じて認証ユーザの特権によるフィルタや認証無し時の制限が加わってもよいです。

5.6 公開宛先

[ActivityStreams]コレクションやオブジェクトに加え、 アクティビティは特別な「公開(public)」コレクションhttps://www.w3.org/ns/activitystreams#Public宛てにも送ることができます。 例:

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

この特別なURI宛てのアクティビティは、すべてのユーザーが認証無しでアクセス可能でなければなりません。 実装は「public」特別コレクションへの配信を行ってはいけません; ここは実際のアクティビティを受理できません。 ただし、アクターは sharedInboxエンドポイントを持てます。 これは公開投稿やフォロワー宛投稿の効率的な共有配信用であり、詳しくは 7.1.3 共有Inbox配信 を参照してください。

ActivityStreamsオブジェクトをActivityStreamsのJSON-LDコンテキストでコンパクト化すると、 https://www.w3.org/ns/activitystreams#Public が単にPublicまたはas:Publicと表現される場合もあります。これらも公開コレクションの有効な表現です。 ActivityStreamsオブジェクトをJSONとして直接扱う実装は、これら3表現すべてに対応できるようにしておくべきです。

5.7 Likesコレクション

すべてのオブジェクトはlikesコレクションを持ってもよいです。 これは、本オブジェクトをobjectとする全てのLikeアクティビティのリストであり、 副作用として 追加されます。 likesコレクションは必ず OrderedCollection または Collection でなければならず、必要に応じて認証ユーザの特権や認証無し時の制限などのフィルタがかかる可能性もあります。

likesコレクションと よく似ているが異なるlikedコレクションを混同しないようご注意ください。 要点をまとめると:

5.8 Sharesコレクション

すべてのオブジェクトはsharesコレクションを持ってもよいです。 これは本オブジェクトをobjectプロパティとする全Announceアクティビティのリストであり、 副作用として 追加されます。 sharesコレクションは必ず OrderedCollection または Collection でなければならず、必要に応じて認証ユーザの特権や認証無し時のフィルタがかかる場合もあります

6. クライアントからサーバーへのやりとり

[ActivityStreams]で定義されたアクティビティは、ソーシャルグラフ内でコンテンツを作成・変更・共有するための中核的な仕組みです。

クライアントからサーバへのやりとりは、クライアントがアクターのoutboxにアクティビティを投稿することで行われます。 これを行うため、クライアントはアクターのプロフィールからoutboxのURLを必ず発見し、そのURLに対して application/ld+json; profile="https://www.w3.org/ns/activitystreams" のContent-TypeでHTTPPOSTリクエストを投げる必要があります。 サーバーは任意で application/activity+jsonというContent-TypeまたはAcceptヘッダーも application/ld+json; profile="https://www.w3.org/ns/activitystreams"と同等として扱っても構いません。 このリクエストは、outboxの所有ユーザーの認証情報を用いた認証を必ず通す必要があります。 POSTリクエストのボディは1つのアクティビティ(埋め込みオブジェクトを含んでいてもよい)または、 サーバーによってCreate activityでラップされる単一の非アクティビティオブジェクトでなければなりません。

Example 11: 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"
}

アクティビティがidプロパティ付きで投稿された場合、サーバーはこれを無視し、新たなid必ず生成しなければなりません。 サーバーは必ず201 CreatedのHTTPコードを返し、アクティビティが一時的なものでない限り、新しいidLocationヘッダーに含める必要があります

Example 12: Outboxへのアクティビティ送信後の応答
HTTP/1.1 201 Created
Location: https://dustycloud.org/likes/345

サーバーは配信前に、ActivityStreamsオブジェクトに存在すればbtoおよびbccプロパティを削除しなければなりません。ただし配信先判定には元々bto/bccプロパティに保存されていた宛先指定を必ず用います(配信参照)。

サーバーはその新たなアクティビティをoutboxコレクションへ追加しなければなりません。 アクティビティの種別次第で、さらに副作用的な処理をサーバーが実施する場合があります。 (ただし、アクティビティがoutboxに出現するタイミングは保証されません。遅延後に現れる場合や、逆に消える場合もあります。) こうした副作用は、各アクティビティごとに下記で説明します。

クライアントからサーバ連携未対応サーバーにオブジェクト送信を試みた場合、405 Method Not Allowed応答を返すべきです。

HTTPキャッシュ機構 [RFC7234] は、クライアントがサーバからの応答を受け取る場合もサーバがクライアントへ応答を返す場合も、適切なら尊重すべきです。

6.1 クライアントによる宛先指定

クライアントは新しいアクティビティの宛先指定を適切に行う責任が有ります。 どのように宛て先を決定するかはクライアントの実装によりますが、サーバーが新しいアクティビティを転送するのはtobtoccbccaudienceフィールド内の宛先だけである、ということをクライアントは理解しておかなければなりません。

フォロワーコレクション公開コレクションは、新規アクティビティのデフォルトの宛先として適切な選択肢です。

クライアントは新しいアクティビティのobjecttargetinReplyTotagフィールドに付いているオブジェクトを調べ、それぞれのactorまたはattributedToプロパティや、さらに各オブジェクトの宛先指定プロパティを取得し、新しく作るアクティビティのtoccフィールドに追加することが望ましいです。 クライアントは再帰的にオブジェクトをたどっても構いませんが、その際は再帰の深さ制限を設けるべきです。 (ただし、「宛先アクターのコレクション」をリスト内で個別に分解して受信者とする必要がある、という意味ではありません。)

クライアントはUIでユーザーが宛名を修正する機会を与えても構いません

例えば、ChrisがAmyの記事に「いいね」した場合は:

Example 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は:

Example 14: 記事へのLike
{
  "@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(Likeした本人)のフォロワーに加え、Amy本人、Amyのフォロワー、また元記事を受信したEvanにもdeliveryできます。

クライアントがoutboxへ以下のアクティビティを投稿する際はobjectプロパティを必ず指定しなければなりません: Create, Update, Delete, Follow, Add, Remove, Like, Block, Undo。 またAddRemove投稿時はtargetプロパティの指定も必須です

6.2 Createアクティビティ

Createアクティビティは新しいオブジェクトを投稿するときに用います。 これには、そのアクティビティに埋め込まれた(objectプロパティ内の)オブジェクトを新規作成する副作用があります。

Createアクティビティを投稿した際は、そのactorフィールドを objectattributedToフィールドにコピーすべきです

Createアクティビティとそのobjectの宛先指定が一致していないと混乱のもとになります。 そのため、サーバーは初回配信時にCreateアクティビティの宛先指定をobjectにコピーしたり、逆にobjectからラップしているCreateアクティビティへコピーすべきです。 ただし、objectの宛て先が後で変更されてもCreate側の宛て先指定は変更される必要はありません(例:Updateアクティビティによる場合)。

6.2.1 Createアクティビティ無しでのオブジェクト作成

クライアントからサーバーへオブジェクトを投稿する際、アクティビティでラップせずにオブジェクト単体を投稿することも可能です。 サーバーは、POSTされた内容がActivityのサブタイプでない有効な[ActivityStreams]オブジェクトの場合は必ず受け付けなければなりません。 その場合、サーバーはこのオブジェクトをCreateアクティビティobjectとしてラップし、非一時的オブジェクトなら Create自体とラップされたObject両方へid必ず付与します。

サーバーが返すLocationの値は新しく生成されたCreateアクティビティ(オブジェクトそのものではない)のURLであるべきです。

オブジェクトに指定されたtobtoccbccaudience各プロパティは、サーバーが新しいCreateアクティビティへコピーしなければなりません

Example 15: 宛先指定付きオブジェクト
{
  "@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"]
}
上記は次のように変換されます:
Example 16: サーバーが生成するCreateアクティビティのラッパー
{
  "@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アクティビティ

Updateアクティビティは、既存のオブジェクトを更新する際に使います。 これにより、objectは、更新アクティビティが定義する新しい構造で必ず書き換えられます(そのアクターに更新権限がある場合)。

6.3.1 部分更新

クライアントからサーバーのやりとりでは、更新は部分的なフィールドごとに行われます。 すなわち、ドキュメント全体を置き換えるのではなく、指定されたキー値ペアのみ既存の値が新しい値に置換されます。 これは更新対象オブジェクトのトップレベルフィールドのみに適用されます。 例外として、jsonのnull型が指定されている場合、そのフィールドはサーバ側のオブジェクト表現から除去します。

ただし、この動作は「クライアント→サーバ通信」(クライアントがサーバへ投稿するだけの場合)のものです。 サーバ間通信やサーバからクライアントへの更新では、部分更新後の完全な新オブジェクト表現が含まれるべきです。 詳しくはサーバ間やりとりでのUpdateアクティビティ説明を参照してください。

6.4 Deleteアクティビティ

Deleteアクティビティは既存オブジェクトを削除するために使います。 この副作用として、サーバはobjectを、そのオブジェクトのTombstoneで置き換えても構いません(他のアクティビティが削除済みオブジェクトを参照する際に表示されます)。 削除済みオブジェクトがリクエストされた場合、サーバはTombstoneオブジェクトをレスポンスボディに返すならHTTP 410 Goneで返すべき、それ以外は404 Not Foundで返します。

削除済みオブジェクトの例:

Example 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アクティビティ

Followアクティビティは他のアクターのアクティビティ購読時に使用します。

outboxでこれを受信した場合、 サーバーはAcceptアクティビティが、このFollowアクティビティをobjectとして受信するまでは objectactorfollowingコレクション追加すべきでありません

6.6 Addアクティビティ

outboxAddアクティビティが届いたら、サーバーは objecttargetで指定されたコレクションに追加すべきですが、以下の場合は除きます:

6.7 Removeアクティビティ

outboxRemoveアクティビティが届いたら、サーバーは objecttargetで指定されたコレクションから削除すべきですが、以下の場合は除きます:

6.8 Likeアクティビティ

Likeアクティビティはactorobjectを「いいね」したことを示します。

outboxでこれを受信した場合、 サーバーはobjectactorlikedコレクション追加すべきです

6.9 Blockアクティビティ

Blockアクティビティは、投稿者アクターが(objectフィールドで指定する)他のアクターに対し、自分の投稿したオブジェクトとやり取りできないようにしたい時に使います。 サーバーはブロックされたユーザーが投稿者アクターのオブジェクトとやり取りできないように防ぐべきです

サーバーはBlockアクティビティをそのobjectで指定された相手へ配信すべきでありません

6.10 Undoアクティビティ

Undoアクティビティは以前のアクティビティを取り消すために使われます。 詳細はActivity Vocabularyの 逆アクティビティと「Undo」を参照。 例えばLikeFollowBlockなどを取り消すのに使います。 Undoアクティビティも元アクティビティも必ず同じアクターでなければなりません。 副作用は出来る限り元に戻してください。 例えばLikeのUndoであれば、以前インクリメントされたカウンターは適切にデクリメントするなど。

ただし、明示的な「逆アクティビティ」がある場合はそちらを用いるべきです。 CreateベースアクティビティならDeleteAddアクティビティならRemoveを使ってください。

6.11 配信

連合型サーバーはoutboxに投稿されたすべてのアクティビティについて、 outbox配信に従い必ず配信しなければなりません

6.12 メディアのアップロード

このセクションは規範的ではありません。

サーバーは画像・動画などのバイナリデータを活動で参照できるようアップロード機能を提供しても構いませんが、その具体的な仕組みはこのActivityPubバージョンの範囲外です。 Social Web Community Groupは ActivityPubメディアアップロード報告書 でプロトコル改訂議論を行っています。

7. サーバー間のやりとり

サーバーは他のサーバーと通信し、アクターのinboxエンドポイントにアクティビティを投稿することで、ソーシャルグラフ全体に情報を伝播させます。 ネットワーク上で送信されるアクティビティは、意図的に一時的でない限りidを持つべきですが、一時的な場合はid省略可能です。

POSTリクエスト(例:inbox宛)は必ず application/ld+json; profile="https://www.w3.org/ns/activitystreams" をContent-Typeとして送信し、GETリクエスト(3.2 オブジェクトの取得も参照) はAcceptヘッダーでapplication/ld+json; profile="https://www.w3.org/ns/activitystreams"を付けます。 サーバーは、サーバー間通信において application/activity+jsonのContent-TypeまたはAcceptヘッダーを application/ld+json; profile="https://www.w3.org/ns/activitystreams"と同等に扱うべきです

ソーシャルグラフ全体に更新を伝播するため、アクティビティは適切な受信者へ送信されます。 まず、該当オブジェクト間のリンクをたどってアクターまで到達し、そのアクターのinboxにアクティビティが挿入されます(配信)。 これにより受信サーバーは以下のことができます:

配信は通常、例えば次の場合にトリガされます:

他サーバー上のアクターのinboxまたはsharedInboxプロパティへ配信を行うサーバーは、 以下のアクティビティにobjectプロパティを必ず含める必要があります: CreateUpdateDeleteFollowAddRemoveLikeBlockUndo。 加えて、サーバー間でAddRemoveアクティビティの配信を行う場合はtargetプロパティも必要です

HTTPキャッシュ機構 [RFC7234] は、他サーバーからの応答受信・他サーバーへの応答送信どちらでも、適切なら尊重すべきです。

7.1 配信

以降は連合型サーバー間の通信についてのみ必要となる規定です。

アクティビティはそのターゲット(アクター)のinboxを参照し、 そのinboxへ投稿することで届けられます。 ターゲットは ActivityStreamsの宛先指定 、つまりアクティビティのtobtoccbccaudienceフィールドで決定します。

inboxはまず ターゲットアクターのJSON-LD表現を取得し 、そのinboxプロパティを参照することで特定されます。 受信者がCollectionまたはOrderedCollectionの場合、サーバーは必ず(ユーザーの認証情報で)そのコレクションをデリファレンスし、各アイテムのinboxを探します。 サーバーはコレクションを通じた間接的なたどりの階層数に上限を設ける必要があり、その上限は1階層でもかまいません

サーバーは最終的な受信者リストを重複排除しなければならず、配信対象アクティビティのactor自身はリストから除外する必要があります。すなわち、アクターは自身のアクティビティを自分自身に配信しないで下さい。

: 非公開アクティビティ・サイレント配信について

宛先が指定されていない場合の動作は未規定ですが、推奨としては、宛先がなければそのオブジェクトは完全非公開としアクセス制御で保護すべきです。 単に「public」コレクション宛の場合、そのオブジェクトはアクターのoutboxで公開表示されますが、いずれのアクターにも届けられません。

その後、(送信者認証つきで)inboxへHTTP POSTリクエストが発行され、ボディにアクティビティが含まれます。 このアクティビティは受信側によりinboxOrderedCollectionitemとして追加されます。 非連合サーバーのinboxに配信しようとした場合は405 Method Not Allowed が返るべきです。

連合サーバーが第三者サーバーへ配信を行う場合、配信は非同期で実施すべきです。またネットワークエラーなどで失敗した場合には配信を再試行すべきです。

注: 同一オリジン間のアクター同士の配信は任意の内部メカニズムを用いても良く、HTTPを使う必要はありません。

: Linked Data Notificationsとの関係

この仕様の理解に必須ではありませんが、ActivityPubの宛先指定・配信機構は Linked Data Notifications 仕様と重複します。両者は相互運用可能です。 特にinboxプロパティは両者共通であり、本仕様書で記述される宛先指定・配信はLDNでもサポートされています。 また、JSON-LDでコンパクト化されたActivityStreams文書に加え、Linked Data NotificationsはActivityPub実装で必須でない様々なRDFシリアライゼーションもサポートします。 より広範な互換性を目指すActivityPub実装は、他のRDF表現のサポートも検討できます。

7.1.1 サーバー間のOutbox配信要件

(クライアントからサーバー、サーバー間双方をサポートするサーバの)outboxでオブジェクトを受信した場合、 サーバは以下の宛先へ配信しなければなりません

  • tobtoccbccaudienceのいずれかのフィールドの値が、そのアクター自身の所有する個人またはコレクションの場合

これらのフィールドは クライアントにより適切に設定されているはずです。

7.1.2 Inboxからの転送

: ゴーストリプライ問題の回避のための転送

この節は、連合ネットワーク上で時折問題となる「ゴーストリプライ」問題の対策です。 この問題は例を使うと最もよく分かります。

Alyssaが学会発表成功の投稿をfollowersコレクションに送り、その中にはBenも入っています。 BenはAlyssaへ返信し、そのフォロワーコレクションも宛先に含めます。 しかしBenはAlyssaのfollowersコレクションの中身にアクセスできず、自身のサーバからfollowersメンバーのinboxへは転送できません。 この転送機構が無いと、AlyssaがBenに再返信した場合、Alyssaのフォロワー達はAlyssaが突然Benに返事しているように見え、Benの発言を見ないままになるので混乱します。

inboxでアクティビティを受信したサーバーは、元サーバーが配信できなかった受信者に対してこれを転送する必要があります。これを行うには、サーバーは以下の全てを満たすときに限り、 toccaudience値をターゲットとし配信しなければなりません

  • サーバがこのアクティビティを初めて受信した場合
  • toccaudienceの値内に、サーバ自身が所有するコレクションが含まれている
  • inReplyToobjecttargettag の値がサーバ所有のオブジェクトの場合。 サーバはこれら値を再帰的に辿り自分の所有オブジェクトか調べすべき、ただし再帰の深さには上限有り。 但し新しい宛先は拾わず、オブジェクトのtoccaudienceのみ配信ターゲットとする(クライアントorユーザーで意図的に宛先を追加・修正している場合があるため)。

サーバは実装固有のルール(例:スパムフィルタ等)で配信ターゲットをフィルタしてもよいです。

7.1.3 共有Inbox配信

多数のアクターを抱えるサーバーでは、全フォロワーへの配信は膨大な数のメッセージ送信につながることがあります。 一部サーバーでは「既知ネットワーク」へ公開投稿されたすべてのメッセージの一覧も表示したいと考えています。 そこでActivityPubはこれら2点に対応するオプション機構を定めます。

オブジェクトを元アクターのフォロワーに配信する場合、 サーバは同じsharedInboxを持つ全フォロワーへの配信をまとめて、 受信対象が個別受信者からsharedInboxへの配信に集約しても構いません。 この場合はリモート受信側サーバーが宛先判定・最終配信を担います。

また、オブジェクトがPublic特別コレクション宛であれば、 サーバーはネットワーク上の全てのsharedInboxエンドポイントにそのオブジェクトを配信してもよいです。

公開宛アクティビティをsharedInboxに送信する場合も tobtoccbccaudienceによる宛先指定で、sharedInbox非対象・本来受信できないアクターやコレクションがあれば そこにも必ず配信してください。

7.2 Createアクティビティ

inboxCreateを受信した際の副作用は意外と少なく、そのアクティビティはアクターのinboxに現れるべきですが、サーバー側でそのアクティビティや付随オブジェクトのローカル表現を保存したくなる可能性が高いです。 ただし、このことは全般的にinboxに配信されたアクティビティを処理する際には常に行われます。

7.3 Updateアクティビティ

サーバー間通信では、Updateアクティビティを受信したサーバーは、そのidが一致する objectのコピーをUpdateで送られてきた内容で上書きすべきです。 なお、 クライアント→サーバでのUpdateアクティビティ と違い、サーバ間では部分更新ではなくオブジェクト全体が置き換えられます。

受信サーバはこのUpdateが対象objectの更新権限を持つことを厳重に確認すべきです。 最低でもUpdateobjectが同じオリジンであることを確認しましょう。

7.4 Deleteアクティビティ

これを受信した場合の副作用は、(objectが送信アクター/サーバー所有である場合)受信側サーバは id一致のobject表現を削除すべきであり、Tombstoneオブジェクトに置換しても構いません。

(なお、ActivityPubで発信元サーバからリモートサーバにアクティビティが送信された後は、オブジェクトの表現のリモート削除強制を義務付けるものはありません。)

7.5 Followアクティビティ

inboxでこれを受信した場合、 サーバーはobjectにこのFollowアクティビティを指定した AcceptまたはRejectアクティビティを生成し、そのFollowactorに配信すべきです。 受理Acceptも拒否Rejectも自動生成またはユーザー操作で生成できます(レビュー後の遅延発行も可)。 サーバーはFollowリクエストに明示的なRejectの送信をしない 選択も可能ですが、リクエスト送信側サーバが中間状態に留まるリスクには注意しましょう。 (例:ユーザー保護のためRejectedの通知を控える…など)

AcceptでこのFollowアクティビティがobjectとなっている場合、サーバーは actorを目的アクターのFollowers Collection追加すべきです。 Rejectの場合は絶対に目的アクターのFollowers Collectionに追加してはいけません。

Follow購読が一度成功したとしても、将来長期間にわたりフォロワーへの配信が失敗することがあります。 実装者はネットワーク上のアクターが常に到達可能と限らないことを想定し、適切な対策を施してください。 例えば半年等、配信に何度も失敗しフォロワーが長期間到達不能となれば、 配信側サーバーはその購読者をfollowersリストから除外しても合理的です。 到達不能アクターへの対応方針と期限は配信サーバー実装側の裁量に委ねます。

7.6 Acceptアクティビティ

inboxでこれを受信した際の副作用はobjectの種別によって決まります。 本書で述べていない型(例: Offer)も受理可能です。

inboxAcceptobjectが、以前受信者から送信されたFollowアクティビティであれば、 サーバーはactorを受信者のFollowing Collection追加すべきです。

7.7 Rejectアクティビティ

inboxでこれを受信した際の副作用はobjectの種別によって決まります。 本書で述べていない型(例: Offer)も拒否可能です。

inbox宛のRejectobjectが、以前受信者から送信されたFollowアクティビティであった場合、 これは受信者がFollow要求を認可しなかったことを意味します。サーバーはactorを受信者のFollowing Collection絶対に追加してはいけません

7.8 Addアクティビティ

inboxAddアクティビティを受信した際は、サーバーは objecttargetプロパティのコレクションに追加すべきですが、以下の場合を除きます:

7.9 Removeアクティビティ

inboxRemoveアクティビティを受信した場合、サーバーは objecttargetプロパティのコレクションから削除すべきですが、以下の場合を除きます:

7.10 Likeアクティビティ

inboxでこれを受信した副作用として、サーバーは likesコレクションが有る場合は アクティビティを追加しobjectのLike数をインクリメントすべきです。

7.11 Announceアクティビティ(共有)

inboxAnnounceアクティビティを受信した際は、サーバーは sharesコレクションが有る場合は アクティビティを追加しobjectの共有数をインクリメントすべきです。

Announceアクティビティは、他のSNSで言うところの「共有」「リポスト」「ブースト」にあたります。

7.12 Undoアクティビティ

Undoアクティビティは直前のアクティビティ副作用を取り消すために利用されます。 詳細はActivityStreamsの 逆アクティビティと「Undo」 を参照してください。 Undoアクティビティの効力および制約範囲は クライアントからサーバー時のUndoアクティビティ と同様ですが、ここでは連合サーバー間で適用されます。

A. 国際化

このセクションは規範的ではありません。

連合ネットワークで国際的なユーザーベースを築くことは重要です。 ActivityStreamsはコンテンツの国際化対応ツールを提供しており、 可能な限りこれを利用すべきです。 しかし、実装でユーザー投稿コンテンツにどの @languageプロパティ を指定すべきか判断するのは難しい場合もあります。 W3C 国際化グループ は、 言語検出に関するガイドラインを提供しています。

B. セキュリティに関する考慮事項

このセクションは規範的ではありません。

B.1 認証と認可

ActivityPubは2つの目的で認証を使用します。1つ目はクライアントによるサーバー認証、2つ目は連合実装でサーバー同士の認証です。

標準化時点では、認証に関して強く合意された仕組みは残念ながら存在しません。 認証のありうる方向性として Social Web Community Groupのベストプラクティス報告書 でいくつか指針が示されています。

B.2 検証

サーバーは、クライアントから投稿されたコンテンツを信用すべきではありませんし、 連合サーバーも、そのコンテンツの発信元でない別サーバーから受け取ったものは、なんらかの検証無しに信用すべきではありません。

サーバーは、新規コンテンツが本当にその主張するアクターから投稿されたものか、またアクターが主張するリソースを更新する権限があるか、慎重に検証すべきです。 詳しくは 3. オブジェクト および B.1 認証と認可も参照してください。

B.3 localhost URIへのアクセス

開発中はlocalhostで動作するプロセスへテストアクセスするのが便利ですが、本番クライアントやサーバーでlocalhostへのリクエストを許可するのは危険な場合があります。 認証が不要なlocalhostのURIへリクエストを投げると思わぬリソースにアクセスしたり、localhost専用の想定だったリソースが操作されてしまうことがあります。

ActivityPubサーバーやクライアントが開発目的でlocalhost URIへのリクエストを許可する場合、デフォルトでオフになる設定項目とすることを検討してください。

B.4 URIスキーム

httphttps以外にも多様なURIタイプがあります。 URIスキームごとにリクエスト処理するようなライブラリの中には、望ましくないスキーム、例えばfileを参照しようとするものもあるかもしれません。 クライアント・サーバー実装者は、自分のライブラリがどのようなリクエスト動作になるか十分検証し、httphttpsなど安全なスキームだけをホワイトリストすることも検討すべきです。

B.5 再帰オブジェクト

サーバーは、オブジェクト解決時の再帰の深さ上限を設けたり、再帰参照を持つActivityStreamsオブジェクトを特別に処理するよう工夫すべきです。 これを怠るとDoS(サービス拒否)セキュリティ脆弱性につながる恐れがあります。

B.6 スパム

スパムはどんなネットワークでも問題ですが、連合型ネットワークでは特に問題が顕著になります。 ActivityPub自体はスパム対策の具体的仕組みは提供しませんが、サーバーは信頼できないローカルユーザーやリモートユーザーからの受信コンテンツを、何らかのスパムフィルターで検査することが推奨されます。

B.7 連合型DoS対策

サーバーは、他の連合サーバーからのDoS攻撃対策を実装すべきです。 たとえばレート制限機構を使うなどの方法があります。 とくに副作用を伴うアクティビティではこの対策を厳重に実装すべきです。 サーバーはまた自身が提出するリクエストで他のサーバーを過負荷にしないよう、指数バックオフなどの戦略で抑制すべきです。

B.8 クライアント→サーバのレート制限

サーバーはAPIクライアントからの投稿をレート制限すべきです。 その目的は2つあります:

  1. 悪意あるクライアントによるサーバーへのDoS攻撃の防止
  2. サーバーがあまり大量のアクティビティを配信してしまい、他サーバーのDoS防御機構を発動させてしまうのを防ぐ

B.9 クライアント→サーバ応答型DoS

大きすぎるコレクションの返却でクライアントが過負荷にならないよう、サーバーは返すコレクションページサイズを制限すべきです。 クライアントもまた、悪意ある/乗っ取られたサーバーに接続した場合に備えて、処理可能な応答のサイズを制限する備えが必要です(たとえばタイムアウトやエラー生成など)。

B.10 コンテンツのサニタイズ

ブラウザやリッチテキスト対応アプリ向けにアクティビティのフィールドを描画する場合、マークアップを含むフィールドのクロスサイトスクリプティング(XSS)対策としてサニタイズ処理を必ず行ってください。

B.11 bto/bccプロパティ非表示

btoおよびbcc配信時に削除すべきですが、 サーバーが自身のストレージ上でどう表現するかは自由です。 ただしbtobccはオブジェクト/アクティビティの元作成者のみに知れている/見えている前提なので、表示時にもこれらプロパティは省略すべきです。

C. 謝辞

このセクションは規範的ではありません。

この仕様はWeb連合分野を探究する多くのコミュニティの長年の努力と経験から生まれました。 特に本仕様の多くは OStatus および Pump API (StatusNet(現GNU Social)およびPump.ioが先駆)に影響を受けています。 これらイニシアチブはいずれも多くの開発者の貢献から生まれましたが、なかでもEvan Prodromou氏はこの分野で一貫したリーダーであり、彼の情熱がなければ現在のActivityPubが存在していたかどうか分かりません。

Erin Shepherdは最初期の仕様バージョンを構築しました。これは Pump API ドキュメントのアイデアを元に、ほとんどを完全新規に書き下ろしたものですが、主要なアイデアを共有しつつActivityStreams 1から2へ切り替えています。

Jessica TallonとChristine Lemmer-Webberは標準化がW3C Social Working Groupに移った際に編集を引き継ぎ、Erin Shepherdのドキュメントから現在のActivityPubへの移行の大半を担いました。 この文書の多くはSocial Working Groupの長いフィードバックプロセスの中で書き換えや再編成が行われています。

ActivityPubはW3C Social Working Groupの多くのメンバーの丁寧な意見を元に作られています。 なかでもAmy Guyには特大の感謝があります。彼女は[Social-Web-Protocols]により、 異なるSocial Working Group文書の内容を調和的に体系化してくれました。 またAmyはChristopher Allan Webberと4日間のスプリントでActivityPub仕様の重要なリファクタ案を作成し、 そのおかげでクライアント・サーバー・連合間の構造がより明確になり、[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 Vocabulary. J. Snell. ActivityStreams Working Group. Editors Draft. URL: https://www.w3.org/TR/activitystreams-vocabulary/
[ActivityStreams]
Activity Streams 2.0. J. Snell. ActivityStreams Working Group. Editors Draft. 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]
Linked Data Notifications. Sarven Capadisli; Amy Guy. W3C. 2017年5月2日. W3C 勧告. URL: https://www.w3.org/TR/ldn/
[RFC2119]
Key words for use in RFCs to Indicate Requirement Levels. S. Bradner. IETF. 1997年3月. Best Current Practice. URL: https://tools.ietf.org/html/rfc2119
[RFC7231]
Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content. R. Fielding, Ed.; J. Reschke, Ed.. IETF. 2014年6月. 提案標準. URL: https://tools.ietf.org/html/rfc7231
[RFC7234]
Hypertext Transfer Protocol (HTTP/1.1): Caching. 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]
The OAuth 2.0 Authorization Framework. D. Hardt, Ed.. IETF. 2012年10月. 提案標準. URL: https://tools.ietf.org/html/rfc6749
[RFC6750]
The OAuth 2.0 Authorization Framework: Bearer Token Usage. M. Jones; D. Hardt. IETF. 2012年10月. 提案標準. URL: https://tools.ietf.org/html/rfc6750
[Social-Web-Protocols]
Social Web Protocols. Amy Guy. W3C. 2017年12月25日. W3C Note. URL: https://www.w3.org/TR/social-web-protocols/