ActivityPubの概要

この記事は[ドワンゴ Advent Calendar 2017](https://qiita.com/advent-calendar/2017/dwango)、[Mastodon Advent Calendar 2017](https://adventar.org/calendars/2178)の三日目の記事です。
本当は「[で、結局Mastodonって何だったの?](http://blog.nil.nu/entry/2017/12/01/211959)」をアドベントカレンダーに載せたかったのですが、先に公開してしまったものを載せても仕方がないので軽くMastodonの連合の仕組みを説明したいと思います。

# ActivityPub: Mastodonの連合を支えるプロトコル

前述のとおり、Mastodonの最大の特徴は**連合**です。連合とは、Mastodonインスタンスという敷居を跨いだユーザー同士のやりとりを可能にする仕組みのことです。 Mastodonインスタンス同士はOStatusやActivityPubといった特定のプロトコルを介してつながっています。

Mastodonは、バージョン1.6以前はOStatusと呼ばれるGNU social互換のプロトコルのみで他インスタンスとの連合を行っていました。しかし、OStatusでは投稿の公開範囲に関して細かな規定がなされておらず、一人でもGNU socialのインスタンスに所属している人がフォロワーにいた場合、プライベートな投稿の内容がそちら経由で漏れてしまうなどの問題が発生していました。そこで、Mastodon 1.6.0からメインの連合プロトコルをActivityPubへと転換し、2.0.0からはOStatus経由ではプライベートな投稿が連合されなくなりました。

# ActivityPubとは？

ActivityPubは、分散ソーシャルネットワーキングのプロトコルの1つで、以下の2つを定義しています。

1. **サーバー⇔サーバー間の連合プロトコル** - 分散SNSの実装がどのようにデータをやりとりするのか
2. **クライアント⇔サーバー間のプロトコル** - ユーザーやその代行者としての(デスクトップ|スマートフォン|Web)アプリがActivityPub対応のサーバーとどうやり取りするのか

Mastodonでは現状、連合プロトコルにはActivityPubを使用していますが、クライアント⇔サーバー間の通信に関しては独自のAPIを採用しています。

ActivityPubでは、すべてのデータは基本的にはActivity Streams 2.0のオブジェクトとして扱い、サーバー間ではJSON-LD互換の形式にエンコードしてやり取りされています。一時的に必要となるだけの(transientな)オブジェクトを除いて、すべてのオブジェクトにはURI/IRIによるIDが割り振られています。Activity Streams 2.0のオブジェクトには、以下のようなものがあります:

* Object
  * 投稿など、一般的なオブジェクト

        | Type         | 説明                                                                 |
        |--------------|----------------------------------------------------------------------|
        | Article      | ブログ記事など、複数段落にわたるような長い文章                       |
        | Audio        | 音声データ                                                           |
        | Document     | ドキュメント                                                         |
        | Event        | イベント                                                             |
        | Image        | 画像                                                                 |
        | Note         | TwitterやMastodonの投稿など、短い文章                                |
        | Page         | Webページ                                                            |
        | Place        | 論理的(例: 仕事)/物理的(例: 35°41'54.0"N 139°46'18.3"E)な場所      |
        | Profile      | ほかのオブジェクトを説明するコンテンツ                               |
        | Relationship | 2つの個人間の関係を説明するオブジェクト                              |
        | Tombstone    | Collection内などで、削除されたオブジェクトのかわりに置かれる         |
        | Video        | 動画                                                                 |

  * Actor (ユーザーなど)

        | Type         | 説明                         |
        |--------------|------------------------------|
        | Application  | ソフトウェアアプリケーション |
        | Group        | Actorの集合                  |
        | Organization | 組織                         |
        | Person       | 個人                         |
        | Service      | あらゆるサービス             |

  * Activity (新しい投稿をする、投稿をお気に入りに登録する、フォローリクエストするなどアクターの起こしたアクション)

        | Type            | 説明                                                                                                                                                             |
        |-----------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------|
        | Accept          | `actor`が`object`(例: Invite)を承認したことを示す                                                                                                                |
        | Add             | `actor`が`target`に`object`を追加したことを示す                                                                                                                  |
        | Announce        | `actor`が`target`に`object`を見てほしいと思っていることを示す (= ブースト)                                                                                       |
        | Arrive          | `actor`が`location`に到着したことを示す                                                                                                                          |
        | Block           | `actor`が`object`をブロックしたことを示す。ActivityPubではサーバー間で連合されないことになっている                                                               |
        | Create          | `actor`が`object`を作成した(=投稿した)ことを示す                                                                                                                 |
        | Delete          | `actor`が`object`を削除したことを示す                                                                                                                            |
        | Dislike         | `actor`が`object`を「よくないね」と思っていることを示す                                                                                                          |
        | Flag            | `actor`が`object`を通報したことを示す                                                                                                                            |
        | Follow          | `actor`が`object`をフォローしたことを示す                                                                                                                        |
        | Ignore          | `actor`が`object`を無視すること(おそらくミュート)を示す                                                                                                          |
        | Invite          | `actor`が`object`への招待を`target`に送ったことを示す。Offerの特殊版                                                                                             |
        | Join            | `actor`が`object`へ参加したことを示す                                                                                                                            |
        | Leave           | `actor`が`object`から離脱したことを示す (どうやらArrive, Joinの両方の逆の意味を持っているらしい？)                                                               |
        | Like            | `actor`が`object`を「いいね」と思っていることを示す                                                                                                              |
        | Listen          | `actor`が`object`を聞いたことを示す                                                                                                                              |
        | Move            | `actor`が`object`を`origin`から`target`へ移動したことを示す                                                                                                      |
        | Offer           | `actor`が`object`を`target`にオファーしていることを示す                                                                                                          |
        | Question        | 質問を表現している。`anyOf`, `oneOf`で選択肢を提示することができる (両方同時に存在してはならない)                                                                |
        | Reject          | `actor`が`object`を拒絶したことを示す                                                                                                                            |
        | Read            | `actor`が`object`を読んだことを示す                                                                                                                              |
        | Remove          | `actor`が`object`を削除したことを示す。`origin`が指定することによってどこから`object`が削除されるのかを指定することができる                                      |
        | TentativeReject | Rejectの特殊版。`actor`が`object`を仮拒絶したことを示す                                                                                                          |
        | TentativeAccept | Acceptの特殊版。`actor`が`object`を仮承諾したことを示す                                                                                                          |
        | Travel          | `actor`が`origin`から`target`へ旅をしていることを示す                                                                                                            |
        | Undo            | `actor`が`object`をアンドゥしたことを示す。`object`は大体Actorの1つである (例: ふぁぼを取り消す時はLikeアクティビティをUndoする)                                 |
        | Update          | `actor`が`object`を更新したことを示す。Activity Streams 2.0では特段どのように更新されたかなどの取り決めはなされていないが、ActivityPubによって規定がなされている |
        | View            | `actor`が`object`を見たことを示す                                                                                                                                |

  * Collection (投稿やフォロワーの一覧、inboxやoutboxなど)

ここで興味深いのは、ユーザーの起こしたアクションなどもすべてオブジェクトの一種として扱われることです。このオブジェクトとして表現されたアクティビティをあらかじめ定められたルールに従って他インスタンスへ転送することによって、Mastodonは連合という仕組みを実現しています。これらの転送の仕組みを定義しているのがActivityPubです。

では実際、ActivityPubでアクティビティの転送はどのように定義されているのでしょうか。

# ActivityPubによるサーバー間のデータ転送

ActivityPubでは、すべてのActorオブジェクトに`inbox`, `outbox`という2つのプロパティを定義することを要件づけています。`inbox`はActorが受け取ったすべてのアクティビティのOrderdCollection、`outbox`はActorが行ったすべてのアクティビティのOrderedCollectionです。

他インスタンスのアクターへアクティビティを転送する際には、転送先のアクターの`inbox`へHTTP POSTを行うことによって転送を行います。大規模なインスタンスでは複数のサーバーで動いている場合もありますし、ブーストを行う場合など、送信側・受信側のどちらでもない第三のインスタンスのアクティビティを転送する必要がある場合もあるので、Activity StreamsではLinked Data Signatureを用いたオブジェクトへの署名と検証を行うことが推奨されています。

# おわりに

執筆時間が短かったせいもあって、結構駆け足になってしまいました。しかし、ActivityPubでどのようなものが表現できるかもしれないかということについては少しご理解いただけたかと思います。

ActivityPubはまだ未完成な規格です。特に認証周りやコンテンツをどのように表現してやりとりをするか、ユーザーのIDをどのようにルックアップするのかといった部分があまりはっきりと定まっていないイメージが感じられます。Mastodonでは認証周りはほぼ投げてしまっていますし、コンテンツは許可タグ以外のエスケープ処理がなされたHTMLが使われていますし、ユーザーIDはOStatusだった頃の仕様を引きずってWebFingerを用いています。ActivityPubの将来に期待しましょう。