この記事はドワンゴ Advent Calendar 2017、Mastodon Advent Calendar 2017の三日目の記事です。
本当は「で、結局Mastodonって何だったの?」をアドベントカレンダーに載せたかったのですが、先に公開してしまったものを載せても仕方がないので軽く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つを定義しています。
- サーバー⇔サーバー間の連合プロトコル - 分散SNSの実装がどのようにデータをやりとりするのか
- クライアント⇔サーバー間のプロトコル - ユーザーやその代行者としての(デスクトップ|スマートフォン|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の将来に期待しましょう。