Skip to content

Instantly share code, notes, and snippets.

@yamarten
Last active February 24, 2024 07:37
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yamarten/3a3e6a6aada3d9030e53019dad8a830c to your computer and use it in GitHub Desktop.
Save yamarten/3a3e6a6aada3d9030e53019dad8a830c to your computer and use it in GitHub Desktop.
atprotoにおけるfederation考察

atprotoにおける「分散」もしくは「フェデレーション」は一意ではない。現状で考えられるアーキテクチャについて妄想してみる。

前提

サーバーの種類

atprotoのfederationにおける登場人物は主に3つ。ユーザー(クライアント)から近い順にPDS・BGS・App Views(appview)の3つ。

詳細は公式ブログ参照。

なお、PDS間の通信も過去には想定されており、BGSやappviewを使う「big-world」と各PDSが直接通信する「small-world」という2種類のfederationが想定されていたが、後者は公式から言及されなくなってしまったのでここでは考慮しない。

PDS

ユーザーデータを管理するサーバー。ユーザーはatprotoに参加するにあたってPDSを一つ選んで所属する(移住可能)。 ユーザー本人すら知らないユーザーデータ署名用秘密鍵を唯一知る存在。

また、ユーザーが直接アクセスするサーバーは自身の所属するPDSのみで、他のサーバー(appview)へのアクセスは所属PDSが行う。

BGS

複数のPDSをクローリングして、更新情報を一つのストリームに集約する。挙動としてはオンラインRSSリーダーみたいなイメージが近い。

ここで流れるデータは入出力ともに、概ねクライアントから投稿した内容に署名が入った程度のものと思ってよい。

基本的にPDSから取得したデータをappviewに流すだけの仲介役だが、FireskyのようにBGSの出力を直接覗くこともできる。

appview

主にBGSから取得したデータをもとに、さまざまな処理を行う。bsky APIの中身は大体ここで実装されている。

タイムライン構築(getTimeline)やフォロワー数カウント(getFollowers)のような複数ユーザーにまたがる処理を行えるのはここだけ。

この文書においては、labelerやfeed generatorもappviewの一種であるとする。bsky appviewの一部という見方でも良いかもしれない。公式では扱いは明言されていないが、そう見做しても問題ないはず。

データ形式

atprotoにおいてユーザーデータの大半はrecordという形式で扱われる。ActivityPubでいうactivityのようなものと思っていい。

federationの話をするにあたって注意すべき性質のみ説明する。

解釈

atprotoでは、recordの中身については特に定義せず、ユーザーが定義できる仕組みを用意している。これもActivityPub風に言うなら、Activity Vocabularyが無いようなもの。

recordの型はlexiconという形式で定義され、どんなフィールドを持つかなどの情報はそこから取得できる。一方、その情報の解釈はクライアントとappviewに委ねられている。PDSは例えばユーザーがフォローしている相手を知っているが、「フォロー」にどんな意味があるかは知らない。[1]

可視性

recordは全て公開情報であり、atprotoユーザーでなくても適切なPDSに問い合わせれば全て取得できる。[2]

そのため、ミュート対象や購読フィードなど非公開にしたい情報はPDSを素通りしてappviewに持たせるという処置が取られている。[3]

フェイズ0: federationしない

PDS・BGS・appviewがそれぞれ一つだけある状態。現状のBluesky Socialはこれ。

諸々の処理を行うためにappviewもユーザーデータを記憶しているだろうことを考慮すると、PDSとBGSはほとんど中継地点でしかない。[4]

260226338 47c2e871 f3b9 4506 9897 9440de0e2e90

PDSが落ちれば当然何もできないが、appviewが落ちた場合もほぼ何もできない。PDSさえ生きていれば書き込み(新規投稿やフォロー)は可能だが、appviewが落ちている間は他ユーザーからは見えない。同じPDSに所属していようと、普通のBlueskyクライアントはappviewを経由してみているためあまり関係無い。[5]

BGSが落ちると復活するまで書き込みは反映されなくなるが、その時点までの情報はappviewに記録されていると思われるため、見る分には特に問題ない。

フェイズ1: PDSの分散

複数のPDSがあり、全てが一つのBGS, appviewに繋がっている状態。現状のsandboxはおそらくこれ。[6]

260226351 7cc24dc8 306a 48e7 94e5 f06160e1013c

公式ブログQiitaの解説記事ではここに重きをおいている。

メリットとして考えられるのは、ユーザー自身がPDSを立てることで署名鍵を管理できるあたりか?データを自分の手元においておけるというのもメリットではあるが、極端な話クライアントから定期バックアップしても大差ない。

PDSが落ちた場合、手元にバックアップデータを持っていれば他PDSに引越すことでほぼ何も変わらずにサービスの利用継続が可能。ただし、引越しはそれなりに重い処理なので、一時的な障害が発生する度に引っ越すことは推奨しない。そもそも現時点では引越しは未実装だが。

また、PDS管理者はモデレーション権限を持つ。特定のコンテンツを削除したり、ユーザーを蹴り出したりできるため、別PDSに退避できることが意味を持つかもしれない。ただし、PDSとは別にappviewでもモデレーションできることに注意。PDSが持つデータを消させることはできなくても、例えばシャドウバンのような処置はできる。

appviewやBGSが落ちた場合は分散していない場合と同様。

フェイズ2: appviewの分散

PDSの分散に加え、現状のbsky appviewのクローンがもう一つできたような状態。

260226390 8341779d de9e 4109 b45f f787ef25691d

同じ実装のappviewが2つあっても、機能的にはほとんど変わらない。どちらも同じBGSをソースにしているため、ユーザーやPDSは一方のappviewにだけ投稿を渡すといったことはできない点に注意。ただし、ミュート情報などの非公開情報はPDSが指定したappviewにしか届かないため、appviewを変えたら挙動は若干変わるはず。

最も大きな違いはやはりモデレーションで、ある投稿について一方のappviewでは見えないが他方では警告すら出ない、といったパターンもありうる。[7]

メインのappviewが落ちたら予備appviewに切り替えて使うというような使い方もできるかもしれない。あるAPIを呼んだ時にどのappviewに繋がるかは(現状では)PDSが決定するため、この辺りの使い方次第ではPDSの選択に別の意味が生まれる。

少し特殊なパターンとして、appviewを(lexiconが変わらない範囲で)改造して、類似サービスを作ることも考えられる。フォロワー数など特定の情報をマスクするとか。嫌な例としてはブロックを無視して全ての投稿を見せるとか。

フェイズ3: 新サービスの実装

これは他とちょっと毛色が違っており、Bluesky Socialとは機能もデータも異なるサービスを実装する話。新しいlexiconを定義してそのためのappview&クライアントを実装する。PSDやBGSは既存のものに便乗できる(ように将来的にはなるはず)。

260226398 5c69a93d 146f 480b a895 0f81ca8e90f1

とはいえ必ずしもBluesky Socialと完全に非互換になるとは限らない。例えば絵文字リアクション機能を追加するだけなら、同じタイムラインを見ることができる。もちろん完全に異なるサービスを実装することもできる。

感覚的にはActivityPubにおけるサービス(MastodonとかMisskeyとか)の違いに一番近い。lexiconの違いはクライアントの互換性にも影響してくるという意味でも近いか。

フェイズ4: BGSの分散

BGSを複数立て、PDSやappviewはどれを使うかそれぞれ選ぶ。実現可能性も実現後の機能も一番予想がつかないところ。

260226403 7e5521ad ba04 4a9b 84a2 7df97bd7dea7

1つのPDSが複数BGSにデータを渡すことも1つのappviewが複数BGSからデータを取得することもありだと思うが、同じデータが異なるBGSから同じappviewに届く場合、一気に面倒になりそう。[8]

BGSの分け方としては、例えば以下のようなものが考えられる。

対象PDS

BGSごとにクローリング対象が異なるパターン。わかりやすい例としてはリージョンごとに担当BGSがあるとか。この場合はappviewは複数のBGSに接続して情報を統合することになりそう。

サービス

atprotoの上に複数のサービスができていくと、各サービスはユーザーデータのうち一部しか使わないようになる。そこで、データの種類ごとにBGSを分けることで、appviewとBGS両方の通信負荷を下げる。実質的にappviewとBGSがセットになることが想定され、分散のモチベーションとしても割とありえる案だと思う。

ミラーリング

単に冗長性を確保するために、全てのPDSをクローリングするBGSを2つ作る。一見とても素直な案だが、appviewがうまく切替えなり重複削除なりするのは実は大変かもしれない。

コミュニティ

特定のPDSのみを対象とするBGSを作って、そのBGSのみを使うappviewとセットで、閉じたコミュニティを作る。真にクローズドにするためにはPDS&BGS&appviewが他サーバーをブロックする必要がありそうだし、あまり具体的な活用の形も思い浮かばない。atproto向きではないようにも思える。


1. 所属PDSによらずどこでも同じ機能を使えるようにするための仕様だと思われる。PDSはあくまでatprotoレイヤーの処理のみを行い、その上に構築されるBluesky Socialのようなサービスのロジックは完全に分離することでPDSの互換性を高めている。
2. これは、PDSからはデータを渡すべき相手が本質的に決定できないという背景がある。例えば人気ユーザーフィードの一つであるLikesはfeed generatorが直接PDSにアクセスしていると思われるが、同様のフィードを網羅的に列挙することは原理的に不可能。
3. これは実際には少し怪しくて、atprotoリポジトリのmasterブランチではこれらの情報はPDSとappviewの両方が持つ実装になっている。一方で、sandboxで使われるPDS実装では完全にappviewのみが持つ。masterの方がPDSとappviewの分離が不完全であり、将来的にはPDSに保存しなくなると予想している。
4. 正確には、PDSはデータへの署名なども行なっているし、DID/handleの管理はappviewを使わないので、直接appviewにアクセスするような代替はできない。
5. 例えば自分のプロフィールや投稿を見るにしても、フォロワーリストやlike数を表示するには一度appviewを通す必要がある。
6. appviewやBGSも勝手に立てていいと言われているが、実際にやってるという話を聞いたことがない。
7. BGSにもモデレーション権限はあるかもしれないが、少なくとも現状の実装ではPDS単位のブロックしか出来ず、個別のコンテンツやユーザーに関するレポートがBGSに届くことは無い。
8. recordの重複自体はCID見れば簡単に判定して解消できるが、recordの削除や編集を捌くにはcommitの前後関係判定なども必要になるため、可能といえどあまりやりたくはないだろう。
@yamarten
Copy link
Author

背景情報として、Bluesky Socialの実装は元々PDSの中にappviewも内包していて、これを分離しようとしている途中であるというのは置いておいた方がいいかもしれない。

現在もPDSの中にappviewの機能は残っていて、デフォルトの挙動だと外部のappviewにはアクセスしない。クライアントからXRPC叩く時のヘッダに"x-appview-proxy: true"を加えることでappviewを使うように分岐することができる。公式ウェブクライアント(bsky.app)の場合、Settingsで「Experiment: Use AppView Proxy」をトグルすることでこのヘッダを追加できるが、画面サイズによって出たり出なかったりする?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment