mastodonがどうやって分散SNSを実現しているのか、その仕組をざっくりと調べてみた

:warning: 間違ってたらごめんなさい :warning:

on shinjukurb#49

mastodon

分散SNSです

分散SNSの実現方法

Based on ActivityStreams, Webfinger, PubsubHubbub and Salmon.

とのことなんで、追って流れを見ていきましょう

超ざっくり

登場人物

  • mastodon instance
    • social network server
    • こいつが誰でも立てられて連携できる
    • pubsubで言うところのsubscriberでありpublisherである
  • user
    • mastodon利用者

アクション

  • to-to
    • tweet on twitter
  • Reply
    • reply on twitter
  • Boost
    • retweet on twitter
  • Favorite
    • favorite on twitter
  • Follow
    • favorite on twitter
  • timeline
    • timeline on twitter

流れ

instance XのUser Aさんの情報をinstance Yで見る

  1. (webfinger) User Aさんのtimelineを見たいので、User Aさんのtimeline取得endpointを探す
  2. (PubSubHubbub) User Aさんをfollow
  3. (activestream) User Aさんのfeed取得
  4. (PubSubHubbub) User Aさんの更新が更に通知される

instance XのUser Aさんにinstance YのUser Bさんがreplyし、それをinstance ZのUser Cさんが閲覧する

  1. (activestream) User Aさんのfeed取得
  2. (activestream) User Aさん向けのreply
  3. (salmon) replyがAさんのmention受付エンドポイントに通知される
  4. (salmon) mentionを他のインスタンスに通知依頼
  5. (PubSubHubbub) インスタンスに展開
  6. User Cさんが会話のやり取りを見る!

要素技術の説明

Webfinger

https://webfinger.net/

WebFingerは、セキュアなトランスポートを介して標準的なHTTP(Hypertext Transfer Protocol)方式を使用してURIで識別される、インターネット上の人または他のエンティティに関する情報を検出するために使用されます。
WebFingerリソースは、照会されるエンティティを記述するJavaScript Object Notation(JSON)オブジェクトを返します。 JSONオブジェクトはJSONリソース記述子(JRD)と呼ばれます。

= Userのfeed discoveryです。

というわけで、webfinger叩いてみる

実際の動き

host-metaからwebfinger情報を取得

https://mstdn.jp/.well-known/host-meta

<?xml version="1.0"?>
<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">
  <Link rel="lrdd" type="application/xrd+xml" template="https://mstdn.jp/.well-known/webfinger?resource={uri}"/>
</XRD>

webfingerにてuserの情報をdiscoveryする

https://mstdn.jp/.well-known/webfinger?resource=acct%3Athreetreeslight%40mstdn.jp

{
  "subject": "acct:threetreeslight@mstdn.jp",
  "aliases": [
    "https://mstdn.jp/@threetreeslight",
    "https://mstdn.jp/users/threetreeslight"
  ],
  "links": [
    {
      "rel": "http://webfinger.net/rel/profile-page",
      "type": "text/html",
      "href": "https://mstdn.jp/@threetreeslight"
    },
    {
      "rel": "http://schemas.google.com/g/2010#updates-from",
      "type": "application/atom+xml",
      "href": "https://mstdn.jp/users/threetreeslight.atom"
    },
    {
      "rel": "self",
      "type": "application/activity+json",
      "href": "https://mstdn.jp/@threetreeslight"
    },
    {
      "rel": "salmon",
      "href": "https://mstdn.jp/api/salmon/140702"
    },
    {
      "rel": "magic-public-key",
      "href": "data:application/magic-public-key,RSA.x6myjejqg0H7pCoE_gX6_AC_sbkSR5C8VKJVHZe1LJqrKjqjb4Wg8AIbztz5qzyAOVF1SW8Uswn9VZRjUd5i1nAiLCioC0nV_-c4EEa5hD4-VVG4zJfKHUPmdZg_rh13r7hpWmJi3VBkPAE3Jo7jSlPoESWROrM_7-397cwqNODRY50K5MDoiZGUihAHamuZNOwjaA7X3yoo7CzQ_q2U6mGtTDFtaG3NtVD48siokEmdhgoDoKDY3onA7qFQVfotZ-CiQNw4m8uXByW6FvWJ1frG9FO5z5jmIRZKBaY0c3OmdB2XB0aRqYLFdgm1CDXawaf4chb6jGUEDIP0NaU50w==.AQAB"
    },
    {
      "rel": "http://ostatus.org/schema/1.0/subscribe",
      "template": "https://mstdn.jp/authorize_follow?acct={uri}"
    }
  ]
}

これでどこ見に行けばその人のfeed取得できるか分かる!やったね!

ActivityStreams

ユーザーアクティビティの場合、Atomは、アクティビティ固有のメタデータの大部分を機械で解析可能な形式で表現する能力が欠けています。
アクティビティストリームは、この追加のアクティビティメタデータを既存のAtomエントリおよびフィード内で表現できるように設計されたXML形式です。
- http://activitystrea.ms/specs/atom/1.0/

というわけでAtom拡張しました。
こんな情報でわかります。

Activityはactor、動varb、object、およびtargetで構成されます。

「ジェラルディンは彼女のアルバムに写真を掲載しました」または「ジョンはビデオを共有しました」という物語の上で、またはオブジェクトと一緒に行動する人物の話をします。

拡張されたschema情報をみてみると
https://github.com/activitystreams/activity-schema/blob/master/activity-schema.md

実際の動き

別インスタンスの@trebyとコミュニケーションとったときの@threetreeslight streamをみてみる

https://mstdn.jp/@threetreeslight.atom

<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:activity="http://activitystrea.ms/spec/1.0/" xmlns:poco="http://portablecontacts.net/spec/1.0" xmlns:media="http://purl.org/syndication/atommedia" xmlns:ostatus="http://ostatus.org/schema/1.0" xmlns:mastodon="http://mastodon.social/schema/1.0">
  <id>https://mstdn.jp/users/threetreeslight.atom</id>
  <title>threetreeslight</title>
  <subtitle></subtitle>
  <updated>2017-04-21T13:49:18Z</updated>
  <logo>/avatars/original/missing.png</logo>

  <author>
    <id>https://mstdn.jp/users/threetreeslight</id>
    <activity:object-type>http://activitystrea.ms/schema/1.0/person</activity:object-type>
    <uri>https://mstdn.jp/users/threetreeslight</uri>
    <name>threetreeslight</name>
    <email>threetreeslight@mstdn.jp</email>
    <link rel="alternate" type="text/html" href="https://mstdn.jp/@threetreeslight"/>
    <link rel="avatar" type="" media:width="120" media:height="120" href="/avatars/original/missing.png"/>
    <link rel="header" type="" media:width="700" media:height="335" href="/headers/original/missing.png"/>
    <poco:preferredUsername>threetreeslight</poco:preferredUsername>
    <mastodon:scope>public</mastodon:scope>
  </author>

  <link rel="alternate" type="text/html" href="https://mstdn.jp/@threetreeslight"/>
  <link rel="self" type="application/atom+xml" href="https://mstdn.jp/users/threetreeslight.atom"/>
  <link rel="hub" href="https://mstdn.jp/api/push"/>
  <link rel="salmon" href="https://mstdn.jp/api/salmon/140702"/>

  <entry>
    <id>tag:mstdn.jp,2017-05-24:objectId=13974575:objectType=Status</id>
    <published>2017-05-24T06:31:33Z</published>
    <updated>2017-05-24T06:31:33Z</updated>
    <title>New status by threetreeslight</title>
+    <activity:object-type>http://activitystrea.ms/schema/1.0/comment</activity:object-type>
+    <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
    <content type="html" xml:lang="ru">&lt;p&gt;&lt;span class="h-card"&gt;&lt;a href="https://imastodon.net/@treby" class="u-url mention"&gt;@&lt;span&gt;treby&lt;/span&gt;&lt;/a&gt;&lt;/span&gt; 食べたい&lt;/p&gt;</content>
+    <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/person" href="https://imastodon.net/users/treby"/>
+    <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" href="http://activityschema.org/collection/public"/>
    <mastodon:scope>public</mastodon:scope>
    <link rel="alternate" type="text/html" href="https://mstdn.jp/users/threetreeslight/updates/6205287"/>
    <link rel="self" type="application/atom+xml" href="https://mstdn.jp/users/threetreeslight/updates/6205287.atom"/>
    <thr:in-reply-to ref="tag:imastodon.net,2017-05-24:objectId=967970:objectType=Status" href="https://imastodon.net/users/treby/updates/422592"/>
  </entry>

  <entry>
    <id>tag:mstdn.jp,2017-04-21:objectId=2405319:objectType=Status</id>
    <published>2017-04-21T13:59:07Z</published>
    <updated>2017-04-21T13:59:07Z</updated>
    <title>New status by threetreeslight</title>
+    <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
+    <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
    <content type="html" xml:lang="en">&lt;p&gt;しかし横1024だと使うの難しい。&lt;/p&gt;</content>
    <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" href="http://activityschema.org/collection/public"/>
    <mastodon:scope>public</mastodon:scope>
    <link rel="alternate" type="text/html" href="https://mstdn.jp/users/threetreeslight/updates/1202938"/>
    <link rel="self" type="application/atom+xml" href="https://mstdn.jp/users/threetreeslight/updates/1202938.atom"/>
  </entry>

</feed>

各アクション(Activity)が定義できたね!やったね!

replyのやり取りをどうやってとりまとめているかは追いきれませんでした。すいません :swear:

ref

PubSubHubbub

よく噛むので読み方: パブサブハバブ

PubSub with poling is Hard

Publisherが3つあってSubscriberが3つあるととすると常に9箇所ポーリング!

# Publisher Pとしては無意味なpolingが発生されて辛い
Publisher P <-poling- Subscriber X
Publisher P <-poling- Subscriber Y
Publisher P <-poling- Subscriber Z

# Subscriber Xとしては更新されていないかもしれないpolingがつらい
Publisher O <-poling- Subscriber X
Publisher P <-poling- Subscriber X
Publisher Q <-poling- Subscriber X

負荷を捌くためには、PublisherとSubscriberをスケールしなければいけない
じゃあどうするか?

PubSub with poling is Hard

じゃあどうするか?
mailmagzineみたいに更新受取登録しておいて、連絡を受けるようにする。

Subscriberが更新受付たい旨を登録する。
Publisherは更新があったら登録されているsubscriberに更新を通知する。

Hub P <-registered- Subscriber X
Hub P <-registered- Subscriber Y
Hub P <-registered- Subscriber Z

Publisher P -update notify-> Hub P
Publisher P <-get contents-  Hub P

Hub P -post contents-> Subscriber X
Hub P -post contents-> Subscriber Y
Hub P -post contents-> Subscriber Z

salmon

RSS、Atom、PubSubHubbubなどのテクノロジでは、読者にリアルタイムの更新フローが提供されますが、これは会話の断片化につながります。
コメント、評価、およびアノテーションは、アグリゲータでますます発生し、元のソースからは見えません。

サーモンプロトコルは、アグリゲーターとソースが会話を統一できるオープンでシンプルな標準ベースのソリューションです。 当初、パブリックコンテンツを中心とした一般の会話に焦点を当てています。
- http://www.salmon-protocol.org/salmon-protocol-summary

replyの仕組みとそれが各インスタンスに展開される流れ

  1. entryにreply(commnet)する際にはsalmon endpointにも通知する
  2. salmon endpointが受けた通知をサーバー内に保存し、entryと紐付ける
  3. publisherはreply(commnet)をpubsubhubbubを通してsubscriberにrepublishする

ref

ref

だいたいココ読めば分かるのでおすすめです。

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.