English Version is avaliable.
Read English versionこの記事はFediverse Advent Calendar 2025の10日目の記事です。
私自身、ActivityPub関連のライブラリをある程度の期間開発してきました。しかし、もしActivityPubを一から実装したいという人がいれば絶対に勧めません。
この記事は筆者独自の見解を含んでいるため、誤った情報を含む可能性があります。
ActivityPubの魅力
まず、ActivityPubはTwitterを筆頭とする従来の中央集権的なSNSと大きく異なる点があります。
それは、開発者が自由にそれに準拠した実装を作成し、他の実装と相互に通信させることができるという点です。しかし、ActivityPubには課題も存在します。
ActivityPubの限界
一方で、ActivityPubは魅力や利点だけではなく、以下のような課題、限界も存在します。
スケールできない
ActivityPubがスケールしない理由の一つは、プッシュ型連合を採用している点だと考えています。
まず、プッシュ型連合であること自体がスケールを阻害します。ActivityPubでは投稿が行われるたびに、フォロワーが存在するすべてのリモートサーバーへ個別にHTTPリクエストを送信します。これは本質的にO(n)の構造であり、フォロワー数やインスタンス数が増えるほど、送信しなければならないリクエストが線形に増えていきます。1
極端な例えになるものの、一人一人異なるインスタンスに1万人のフォロワーを持つユーザーAが投稿を作成した場合、サーバーは1万件のHTTPリクエストを個別に送信する必要があります。当然ながら、これは非常に非効率で、CPU・帯域・キュー処理すべてに過剰な負荷がかかります。
さらに、フォロワー側に負荷が集中するケースも存在します。 たとえば、ユーザーBが1万人のユーザーをフォローしているとします。仮にBがフォローしている1万人がすべて同一のインスタンスにいたとしても、B自身が別サーバーにいる限り、フォロイーが短時間に投稿すれば、Bのサーバーには1万件の連合リクエストが一斉に飛んできます。
フォロー元が単一のサーバーに集中していても、連合は「フォローしている側のサーバー」へリクエストが送られるため、Bのサーバー側には突発的に大量のリクエストが到達します。これは事実上、DoS的なスパイクに近い状態となり、サーバーの性能によっては容易に処理が追いつかず、インスタンスがダウンする可能性があります。
互換性周りで問題が起こりやすい
ActivityPubの仕様は柔軟で拡張性が高い一方、その自由度の高さゆえに、実装間での解釈の違いや互換性の不足が起こりやすくなっています。
- 投稿の編集が正しく反映されない場合がある: Misskey等の一部の実装は投稿の編集をサポートしていません、そのため、編集機能を持つ実装がMisskeyに対してUpdateアクティビティを配送しても反映されず、情報の整合性が保たれない場合があります。
- Client-to-Server APIのサポートがない: ActivityPubではS2S API以外にも、C2S APIも定義されています。しかし、これをサポートする実装はPleroma等、一部にとどまっています。
- 標準語彙以外が使用されている場合がある: ほとんどの場合、ActivityPubで連合するにはAS2のサポートのみでは不十分です。また、実装によっては独自機能を実装するために既存のプロパティやアクティビティを拡張している場合もあります。
Misskeyの場合
Misskeyは引用ノートやMFM (Markup language For Misskey)、絵文字リアクションなど、Mastodonでは実装されていない独自の仕様が存在します。2
(少なくともMisskeyでは)絵文字リアクションは技術的にはLikeアクティビティを拡張したものになっています。
その拡張に使用されているのが_misskey_reactionプロパティです。このプロパティにはunicode絵文字を含む文字列か、コロンから始まり、コロンで終わる文字列が格納されます。後者 (カスタム絵文字)の場合はtagプロパティにそれぞれの絵文字のデータが格納されます。
Likeをベースにしているため、非対応サーバーではこれを「いいね」として処理します。これはActivity自体の互換性は向上させますが、「いいね」として取り扱われることで元のリアクションの意図等も伝わりにくくなってしまいます。
Fediverse Enhancement Proposals (FEP)
Fediverse Enhancement Proposals (FEP)は、Fediverseの相互運用性を向上させることを目的とした文書ですが、すべての実装が準拠しているわけではありません。
例えば、引用投稿の仕様はMastodonが採用するFEP-044fと、Misskeyが従来から使用する方法の2種類が存在し、互換性がありません。2 このように、同じ機能でも実装によって仕様が異なり、相互運用を困難にしています。
仕様が曖昧
ActivityPubの最大の問題点の一つは、仕様の曖昧さや不完全さです。これは単に「読みづらい」というレベルではなく、実装者が正しく解釈して実装しても、他の実装と挙動が一致しないことが普通に起こるという、連合プロトコルとして致命的な性質です。
Activity検証の標準が存在しない
ActivityPubでは、受信したActivityをどのように検証すべきかが明確に規定されていません。
現在、Fediverseの多くの実装は古いドラフト仕様であるdraft-cavage-http-signaturesを事実上の標準として利用していますが、これはすでに放棄されている文書です。
これに対して、これを置き換えるRFC 9421 (draft-ietf-httpbis-message-signatures)が登場しましたが、draft-cavage-http-signaturesとは互換性がなく、サポートもMastodonなどの少数の実装のみにとどまっているため、あまり普及していないのが現状で、新しい仕様で署名したリクエストが拒否された場合は古い仕様で再試行するロジック (ダブルノッキング)の実装が必要です。
さらに、アカウントの削除やリレーに対応しようとするとLinked Data Signatures 1.0の対応も必須になっていきます。これもRsaSignature2017という古い仕様を使用しています。さらにこれに加えて、FEP-8b32: Object Integrity Proofsという仕様も存在します。
代替案
ActivityPubをゼロから実装するのは茨の道ですが、幸いにも「一から自作する以外の道」もいくつか存在します。
ここでは、現実的な選択肢を順に紹介します。
1. 既存のものをフォークする
もしあなたが既存のFediverseソフトウェアに満足していて、それに近いものを作りたい場合、既存実装をフォークして目的に合わせてカスタマイズするのが最も手堅い方法です。
- 大規模な実装ほど、連合処理・署名・キュー処理などの複雑な部分が既に安定している
- セキュリティ面でも、完全自作より圧倒的にリスクが低い
- ActivityPubのクセを既に踏んでいるため、相互運用も問題が出にくい
たとえば:
- MastodonをフォークしてUIや投稿機能を調整する
- Misskeyをフォークして不要な機能を切り捨てる、投稿の編集等の新しい機能を導入する
完全自作と比べれば、既に土台が整っているため1/10以下の労力で済むことだってあり得ます。
2. ActivityPubライブラリ/フレームワークを使う
フォークではなく独自のソフトウェアを作りたい場合でも、連合・署名・Actor管理などの部分だけ既存ライブラリに任せるという選択があります。
- ActorモデルやActivityなどの膨大な数のモデルの構造を自前で定義する必要がない
- HTTP署名やActivityの配送等でのミスを避けやすい
- 連合に関するロジックを気にせず、ソフトウェア固有機能の開発に集中できる
ActivityPubライブラリ/フレームワークの例としては、以下のようなライブラリがあります。
fedify(TypeScript): https://fedify.devgo-ap(Go): https://sr.ht/~mariusor/go-activitypub/apkit(Python): https://fedi-libs.github.io/apkit/
ただし、ライブラリごとに対応できる範囲が異なる場合もあるので、選ぶ前に「どの部分まで任せられるか」を確認しておく必要があります。
3. 最初は対応範囲を絞って、徐々に拡大していく
どうしてもゼロから作りたいならこれが唯一の現実的手段です。
最初から
- 編集
- 署名の互換性
- 絵文字リアクション
- FEPの対応
- Mastodon/Misskey両対応
- 公開リレー
などに最初から全部対応しようとすると確実に破綻します。
最初は Inbox (CreateとDeleteのみ等) + 配信だけに絞る、 もしくは読み取り専用のAP実装にする。
そして段階的に実装する機能を増やしていくのが現実的です。
4. そもそも連合しない
実は「連合したいと思っているけど、本当に必要か?」というケースもあります。
連合には必ずコストがかかるため、要件によっては
- 独自チャットアプリ
- 小規模コミュニティ
- 非公開SNS などは、そもそも連合しない方が幸せになれます。
「後で必要になったら導入する」くらいで十分な場合もあります。
まとめ
ActivityPubは「誰でも実装できて、誰でも連合できる」という自由さを持つ一方で、その自由さゆえの複雑さや曖昧さ、そしてスケール問題など、実装者にとって非常に高いハードルを課すプロトコルでもあります。
特にゼロから実装する場合、
- プッシュ型連合によるスケール問題
- 実装間の非互換
- 署名まわり・検証まわりの仕様の曖昧さ
- 暗黙の前提・デファクト仕様が多すぎる
- 既存実装と同等レベルに追いつくのが非常に困難
といった壁が確実に立ちはだかります。
「自由に実装できる」という魅力は確かに存在しますが、実際に実装するには暗黙の仕様に縛られている部分があったり、他の実装の特徴的な動作に対応する必要が出てくる場合があります。
ActivityPub実装に挑戦する際は、いきなりゼロから始める前に、代替案も踏まえて選択肢を比較してみると良いと思います。
- 既存実装をフォークする
- ActivityPubライブラリを使う
- 最初から全てに対応せず、対応範囲を徐々に増やしていく
- そもそも連合をやめる
これらは決して妥協ではなく、長期的に見れば最も合理的な選択肢になり得ます。
ActivityPubは面白い技術であり、学ぶ価値は大いにあります。しかし、現状のエコシステムにおいては「ゼロから完全な実装」を目指すのは、明らかにコストに見合わないケースが多いです。
この記事が、これから実装に挑戦する人の判断材料になれば幸いです。
Footnotes
実際には、
sharedInboxが存在するため、それを双方がサポートしていればフォロワーが特定のインスタンスに偏っていれば負荷はそこまで増えない場合もあります。 ↩厳密には、Mastodon v4.5からFEP-044f: 同意を尊重した引用投稿がサポートされています。この仕様はMisskeyの引用(
_misskey_quote)と互換性があり、MisskeyからMastodonへ送信された引用は正しく表示されます。しかし、逆方向(MastodonからMisskey)の互換性はありません。 ↩ ↩2