Skip to content

Instantly share code, notes, and snippets.

@yamarten
Last active February 15, 2024 16:23
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yamarten/5c1bb0ac6aa8f89f0cc0630a94085e25 to your computer and use it in GitHub Desktop.
Save yamarten/5c1bb0ac6aa8f89f0cc0630a94085e25 to your computer and use it in GitHub Desktop.
Authenticated Transfer Protocol 覚書

11/13時点のドキュメント準拠

実装はあんまり見ずに、ATPの雰囲気を把握するためのメモ

概観

要素としては以下のような感覚。

  • Repository: 署名つきRDB
    • データ構造がRDB(というか表)という話で、SQL-likeな操作が規定されてるわけではない
    • 他サーバやクライアントが複製する形で通信(兼バックアップする)
    • 1行(Record)はJSON互換のCBORで表される
    • テーブルはCollection、主キーはTID(タイムスタンプ)、スキーマはLexiconに対応
    • 署名はRepository全体(のMST)に対して、更新される毎に行う
  • DID: ユーザID→Repository置場マッピング
    • ユーザIDが直接Repositoryの在処を示さないため、IDそのままで引越せる
    • 公開鍵もあるので、Repositoryの検証ができる
    • 当面は(おそらく唯一の)サーバで運用するdid:plcを用いる
  • XRPC: サーバAPI形式
    • HTTP GET/POST上で定義され、型取得方法やURL決定方法を規定する
    • 具体的なメソッドはLexicon形式のJSONが定める
    • Repository同期のような基本的な操作もやるし、タイムライン取得のようなapp固有の操作も可
    • 普通はクライアントは(特に他人の)Repository全体を持ちたくはないので、固有APIが期待される
  • Lexicon: Repositoryスキーマ・XRPCメソッド定義
    • 逆ドメイン名で名前空間を切って、RecordやXRPCの型をJSONで定義する
    • Lexicon名に対応するドメインからLexiconが取得できることが期待されるが、必須ではない

Activitypubの主体がメッセージであるのに対し、ATPはデータが主体。APでいうなら、所有者のみPOST可なCollectionだけがある感じか?二者間の関係ではなく、誰かが持つ情報を別の誰かが見る形。アクセス制御は無いと考えた方がよさそう(PDS単位でのブロックくらいはできるとは思うが)。

サーバ間通信は現状Repository同期のみ。サーバ間で使えるかはわからないが、一応特定Collectionのみの取得も可。app.bsky.graph.followを元にfetchするようにPDSを実装する必要がありそう。Indexerの話も出ていないし、そもそも現状は単独PDSを主に考えていそうなので、この辺りは今後新要素の登場に期待。

そもそもこの名前が何を指すのかも不明瞭だが、一旦app.bsky.*を一通り使いこなすクライアントと想定し、どのようなサービスになるか考える。

プリミティブなTwitterという感じ。ホームTLが見れてリプライやいいねやrepost(RT)ができる。RTどころか画像投稿もスレッド表示もできるので、初期Twitterより多機能っぽいが。DM、非公開アカウント等のアクセス制限する要素は無い。リスト・ハッシュタグ・検索もおそらく無いが、これらは将来的にはClientやIndexerが解決するかも。

最初はPDS固定になる気がする。単独サーバ内のサービスとして始めて、federation可能にして、もしかしたら汎用クライアントになるかも、程度のつもりでいた方が気楽か。

ページ別メモ

  • CollectionはRepository内の同じ型のRecordをまとめたもの
    • Lexiconによって決まるもので、ユーザが定義するものではない点に注意
  • DIDは不変、usernameは(name serverを確保していれば)変更可
    • DIDメソッド固定と考えるとそこそこ強い制約の気もする
    • DIDとusenameは相互変換可
  • 推定Repository構成はMST(履歴&署名付き)+KVS(TID-Record)
    • MSTについては原論文の図を見るのが手っ取り早い
      • 記載されていないが、keyはTID(Lexicon依存?)、valueはRecordのCID(≒ハッシュ)だと思われる
      • MSTノードもCBORになっており、参照をCIDで表すため、Record操作は常に根まで波及する
      • MSTの根のCIDに署名する(commit)ことで、全てのRecordの改竄が検証可能
      • MST Entryが葉と右隣のサブツリーをセットで表しているのが少しややこしい
    • Record本体の管理方法はおそらく自由で、普通にDBに突っ込むとかになりそう
      • PDSが知らない型のRecordも保持してほしいので若干注意が必要
    • 一度作ったRecordを無かったことにする等は普通にできそうだが、MSTが魚拓になる
  • s2sは何も決まってない
    • Bluesky Socialは当面1PDSのみで運用するつもり?
    • 最低限Repository同期APIだけはあるので全く出来ないわけではないが……
    • イベントベースの通信もしたいという話はあったが予定は不明

形式定義だけなので特に見所は無い

これも形式定義のみなので、意味はGuideの方を見ろということか

defsは何?

  • HTTP GET/PUTとして働く
  • 入力としてParameterとInputの2つがある
  • Lexicon Schemaは必ずしも公開する必要は無い
    • 公開しないとPDSはRecordの検証できない場合が出るので公開しててほしい
  • サーバが特定のmethodを実装しているか、権限的に呼べるかは実際に呼んでみるしかない?
  • 本当にドメインを持っている人だけがそのNSID使おうと言っている?
    • 一度Lexicon作ったら保持し続けないといけないの少し厳しそう
  • 全ATPユーザが同じ(少なくとも一貫した内容の)PLCサーバを参照する
  • PLCサーバはDIDをDIDドキュメント(公開鍵・username・PDSアドレス)に解決する
  • PDSやPLCサーバはsigningKeyの秘密鍵を受け取るが、recoveryKeyは持たないのでまあまあ安全、という思想?
  • usernameはalsoKnownAsで指定
  • 以下の節については現状対応する仕様が無いので注意
    • Federation
    • Achieving scale
    • Algorithmic choice
    • Speech, reach, and moderation
  • "Authenticated Transfer Protocol a.k.a ATP"と言ったそのページ中で別名持ち出すのどうなの?
    • AT Protocolに至っては同じ段落
    • @-protocolはATPのコア部分のみ指すようにも読めなくはないが、多分違う
    • ドメインやリポジトリがatprotoなのはわかるが、URIスキームがatなのは嫌い
  • クライアントはRepositoryを手元にコピーしておくことが推奨される
    • PDSが急に使えなくなっても移行できるバックアップとして働く
    • 逆に言えば、Repositoryにそのユーザの全てが記録されているべき
  • "These IDs should rarely change"ということはDID変更可能にすることも想定している?
  • 良いDIDメソッドが無いので中央集権的だけどdid:plcを使う、とのこと
  • usernameとPDSアドレスは全く異なっていてよい

TIDの定義以外はATPで言ってる内容

  • LexiconはJSONで定義される
  • XRPCメソッド(query/procedure)、Recordの型、定数(token)を定める
  • Recordの$extフィールドはLexiconに無いフィールドの追加を許す?
    • 別で定義したものを付け足すとかでもなさそう
    • かなり気持ち悪い使い方されそう
  • Lexiconは互換性を保つ更新(オプションフィールド追加)のみ可能
    • 追加機能だけ別Lexiconで、とかになる?
  • サーバがどのLexiconを知っている(≒どのメソッドを実装している)べきかは自己判断?

簡単なサンプルコード

  • ブロックチェーンと関係無いと断言するのちょっと安心感ある
    • 現状ちゃんとDIDしようとすると関わる可能性高そうだが、別アプローチに期待している?
  • APに対するATPの強みとしてアカウントの可搬性を挙げている
    • 例えばPDS(Mastodon等でいうインスタンス)を変えてもフォロワーも投稿も維持できる

Lexicons

あまり具体的な記載は無いので推測交えて気になったとこだけ

  • XRPCメソッドの想定権限くらいほしい
  • アカウント作成にemail必須・パスワード認証強制なの微妙に納得いかない
    • せっかく公開鍵あるしそれ使ってもいいのでは、と思ってよく見たらsigningKeyはPDSしか知らないっぽい?
  • invite概念があるらしいがよくわからない
  • com.atproto.syncはサーバ間想定のようだが、クライアントが使う場合もあるはず
  • フォロワーやlike数を正確に把握するの無理では?
  • app.bsky.system.declarationわかってないが、人間/グループ/botみたいな区別に使うものっぽく見える

気になること

  • Lexicon間の依存関係はあり?
    • app.bskyの拡張とかしたくなりそう
    • →普通に許されそうだが、依存関係を示す仕組みは無いので注意
  • Collectionの部分公開とかあり?
    • 例えばDM的なものを実装するにあたって、特定ユーザしか見れないRecordを作りたい
    • 公開対象をRecord内で指定しても、複数ユーザを抱えるPDSがそのLexiconの扱いを知らない場合に漏れたりしそう
    • →現状できないが、DMは暗号化によって実現しようとしている 参考
  • Clientが担当のPDSを介さず直接他PDSに問い合わせることは想定している?
    • 例えば、あるユーザのいいね一覧(app.bsky.feed.like)を取得しようと思った時、誰に聞く?
    • PLCサーバに問い合わせられるのは誰か、どんなタイミングでRepository同期が発生するかにも関わる
    • →基本的にはしない想定だと思われるが、それはそれとして認証無しで叩けるAPIもあるので可能ではある 参考
  • PDSはどのように通信先を定める?
    • 現状、あるPDSが取得すべき別PDSのRepositoryを特定できるのはapp.bsky.graph.followだけに見える
    • 同期すべき相手を決めるのは各Lexiconというのはわかるが、それはPDSに全Lexiconのロジック実装を要求するのでは?
    • 基本的なs2sというか、適当な相手とのfetch/push程度はATPのレベルでできてほしい
    • →small-worldの同期はPDSにロジック実装、big-worldは汎用の仕組みを作ってこっちをメインにしたがっているように見える 参考
  • フォーラムを実現するらしい
    • 個人的にtopic-basedなコミュニケーションツールに興味があるため、割と気になる
    • Lemmy的な使い方をするとして、app.bsky上だと確実な投稿検知&投稿の保存先が壁になりそう
    • →bskyの上にフォーラム風のアルゴリズムを実装することで実現しようとしている? 参考
  • 認証周り割と不安
    • OpenID的な仕組みほしい(Lexicon作ればよさそう)
    • signingKeyを(場合によってはrecoveryKeyも)PDSだけが持ってる片手落ち感
    • com.atproto.account.create使う限り既存DIDは使えない
    • →将来的にどうにかしようと考えているらしいので期待……だが、createSession使ってる限りパスワード排除はできなそう

repository構造

ウェブサイトのものから構造が変わっているので注意

figure_atp_repo drawio

federationの雰囲気

big-worldは現状想像だけどおそらく大体こんな感じ。かつこっちがメイン?

Twitterは中身分からんけどfanoutとか考えればこの書き方でも間違ってはいないはず。Zotはまともなドキュメント無いので雰囲気で書いている。

fedprotocols.drawio.png

@yamarten
Copy link
Author

アカウントがサーバに依存しない=引っ越せるというのは自己アピールしている魅力だけど、それとは別にサービスがサーバに依存しない=サービスロジックをクライアントだけで実装できる(ので独立した複数サービスに同じアカウントを使いやすい)のも魅力になりうるので、その辺りが思い込みでないことを願う。

とはいえ仮に仕様上は可能であったとしても、WebUIを主とする人が大多数なら結局PDS依存ということにもなりそうだが。クライアントサイドで処理するのがどれだけ現実的かはわからないし。例えばapp.bsky.feed.getTimelineを使えないPDSを想定したBlueskyクライアントを作るかという話なので。

これについてはアカウントも同じで、1アカウントに複数サービス抱え込めばクライアントがRepository丸ごと持つのが現実的かは怪しくなるかもしれないし、そもそもクライアント使わず外部サービス使わずでバックアップが存在しないユーザは一定数出そう。

@yamarten
Copy link
Author

yamarten commented Dec 9, 2022

データ構造について図に整理したので追加。commitはCIDに署名しているのが肝なのだが、gist上ではアイコン見えてない。

getRepoの実装見たらRecord含め全部履歴持ってるようだが、マイクロブログと食い合わせ悪いのでは?てっきりMSTだけ持って検証だけできるようにするものかと。

@yamarten
Copy link
Author

@-protocolはATPのコア部分のみ指すようにも読めなくはないが、多分違う

旧名称らしい。

bluesky-social/atproto-website@325b22d

  • 認証周り割と不安
    • OpenID的な仕組みほしい(Lexicon作ればよさそう)
    • signingKeyを(場合によってはrecoveryKeyも)PDSだけが持ってる片手落ち感
    • com.atproto.account.create使う限り既存DIDは使えない

どちらかというとOAuth的な話だが、元々UCANで権限付与しようとしていた模様。Rootのauth_tokenがそれで、commitの署名も対応する鍵で行なう実装になっている。既存APIでもcom.atproto.sync.updateRepo(未実装)では使えるかもしれないが、権限制御用のAPIは無い。

PDS引越しを実現するにはDIDに紐づいた署名鍵もユーザが持つべきなので、将来的にはこの辺が整備されるはず。UCANは概ね2つのDIDと権限に署名したものなので、ユーザに対する権限付与もできそう。ただし、引越し時もあくまでPDSが鍵生成する提案もある。

bluesky-social/atproto#385

Record含め全部履歴持ってる

PDSの背後に分散型データストアが来る場合を想定している気がしなくもない。

現状、APIを通じて特定のRecordをPDSから完全に削除する術は無さそうなので、その点は要注意。

@yamarten
Copy link
Author

yamarten commented Mar 4, 2023

プライベートベータで話題になってるようなのだし最近開発も活発なので、lexiconの更新で気になるのをまとめてみる。ドキュメントは更新されてなさそうなので開発リポジトリベース。

メディア周りはあまり興味無いので、大きくモデレーションと同期の2点か。色々整理が入るらしいが概ね改名なので一旦無視。

モデレーション

ブログ記事によるとクローズド期間はモデレーション機能周りを作っている間ということらしいが、これはcom.atproto側に入っている。当然と言えば当然だが、PDSローカルの機能の模様。

ユーザ側はcom.atproto.report.createからrecord/repository単位で報告できる。Twittterと同じノリだが、向こうはAPIとして用意されていなかった気がする。管理者側のアクションも全部com.atproto.adminでAPI化されており、徹底している。現状ブロック(takedown)/警告(flag)/承認(achnowledge)の3択+独自拡張という感じか。

s2s

PDS同期をクライアントから直接トリガするためのAPIを作るつもりは今のところ無さそうなので、PDS依存の実装になりそう。やっぱりPDSの知らないrecordを扱わせるつもりは無いのか?そもそも他PDSに同期しに行く処理は現状実装されてない。想定される手段は以下の3つ。

  • app.bsky.graph.followのrecord追加を検知したPDSが適当なタイミングで同期する。古のコードには実際にそのような処理が入っているが、現在は無さそう。もちろんbsky以外なら同等の何かが別途必要。
  • 環境変数APP_VIEW_REPO_PROVIDERにPDSアドレスを設定するとcom.atproto.sync.subscribeAllReposでそのPDS上の全変更をリアルタイム取得する。app-viewと言っているのでatprotoの基本機能のつもりは無さそう。リポジトリを指定しない点に注意。マージ待ち。
  • com.atproto.sync.requestCrawlでイベントを送りたい相手を登録しておくと、com.atproto.sync.notifyOfUpdateを叩いたタイミングで差分を通知(手段不明)する。crawlと言っているように外部サービス(big-world)向けと思われる。定義のみで未実装。

repository同期

com.atproto.sync.getRepoは廃止となり、commit一覧を得るcom.atproto.sync.getCommitPathと指定commitの中身を取得するcom.atproto.sync.getCheckoutに分解。ついでに用語に変更が入ってroot→headへ。履歴取得可能であることが明示され、よりVCS風味が強くなった。

com.atproto.sync.updateRepoは実装されることなく廃止。クライアントが署名鍵を持たなければ意味ないからという理由で、少なくとも当面UCANが採用される気配は無さそう。

com.atproto.sync.getRecordは指定したkeyに対応したrecordを取ってくるqueryだが、そのkeyに至るまでのMSTパスを全部取ってくるので署名の検証が可能という、コンセプトに忠実な作り。recordとしてツイートのようなものを想定すると少し重い気もするが、その場合は単発で取ることは少ないだろうし、無視したければ各lexiconでquery作ればいいという話か。

@yamarten
Copy link
Author

yamarten commented Mar 4, 2023

周辺の話題だと、フィードやSNSのフォーマット変換を行うgranaryがatprotoをサポートしてるのが面白い。ActivityStreamへの変換例を見るとidが設定されていないので、ActivityPubで通じる形式ではなさそう。ActorだけならDNS nameをそのまま使えそうだが。

ライブラリ実装もいくつか出てきているようだが、現状でまともな実装できるのか……?

@yamarten
Copy link
Author

yamarten commented May 5, 2023

他プロトコルのようにサーバ間通信はsmall-worldがメインでbig-worldは不足機能の補填なんだと思ってたんだけど、実際にはそんなことなくてbig-world主体なんじゃないかと最近思っている。それなら確かにPDSは軽かろう。PDSはあくまでデータ置き場&窓口で、そのデータを使って各々が作ったアルゴリズム(サービス)を使う感じか。

ここでいうアルゴリズムはrepositoryを収集し何かしらのパラメータを受け取ってfeed(app.bsky.feed.defs#feedViewPost)を返すロジックを指すと思われる。つまりBluesky上の話だが、それをここまで押すのはBlueskyをプロトコル全体の核にしようとしているから……?

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