"静かなフェディバース" 問題を解決する二つのアプローチ:会話バックフィリングメカニズム

洪 民憙 (Hong Minhee) @hongminhee@hackers.pub
フェディバース(fediverse)を使ったことがある人なら、一度は経験したことがあるでしょう。興味深い議論が行われているようなのに、実際にその会話を見てみると返信が数個しか表示されなかったり、文脈が分からない返信だけがポツポツと現れる現象です。まるで複数の人が集まって議論しているのに、その中の一部の発言だけが聞こえるように感じられます。
これがまさにフェディバースのユーザーがしばしば経験する 「静かなフェディバース」(quiet fediverse)問題です。2025年2月ブリュッセルのFOSDEMで開催された The Fediverse is Quiet—Let's Fix That! プレゼンテーションは、この問題を正面から取り上げました。
この記事では、フェディバースの会話分断問題がなぜ発生するのか、そしてこれを解決するために開発者たちが提案した二つの主要なアプローチを詳しく見ていきます。各方式の技術的原理から実際の実装事例、そして各々の長所と短所まで、豊富な例とともに説明します。
Note
この記事はNodeBBの @julian 氏が作成した Backfilling Conversations: Two Major Approaches を主要参考資料として、韓国の開発者コミュニティのために韓国語に翻訳し、追加分析を加えた記事です。
原文の構造と核心アイデアをベースにしつつ、技術的概念の説明を補強し、実際の実装事例を追加しました。AIの助けを借りて作成されました。
原作者の @julian 氏と活発な議論に参加してくださったフェディバース開発者コミュニティに感謝いたします。
問題の根本原因:ActivityPubの分散特性
ActivityPubとは?
まずフェディバースの基盤となる ActivityPub プロトコルを理解する必要があります。ActivityPubは分散型ソーシャルネットワークのためのW3C標準プロトコルで、異なるサーバーのユーザー同士が相互作用できるようにします。
ActivityPubではすべての相互作用はアクティビティ(activity)という形で表現されます。例えば、新しい投稿を作成すると Create(Note)
アクティビティが生成され、返信を付けると同じく Create(Note)
アクティビティが生成されて、その投稿への返信であることを示します。詳細は ActivityStreams 2.0仕様 で確認できます。
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Create",
"id": "https://alice.example/activities/create-reply-123",
"actor": "https://alice.example/users/alice",
"published": "2025-06-09T10:30:00Z",
"to": ["https://bob.example/users/bob"],
"object": {
"type": "Note",
"id": "https://alice.example/notes/reply-123",
"content": "本当に興味深い視点ですね!",
"inReplyTo": "https://bob.example/posts/original-post",
"attributedTo": "https://alice.example/users/alice"
}
}
分散のジレンマ
ActivityPubの分散特性がまさに問題の原因です。中央集権型プラットフォーム(X、Facebookなど)と異なり、フェディバースでは会話が複数のサーバーにわたって分散して保存されます。
Alice(alice.example
)が元の投稿を作成し、Bob(bob.example
)がAliceの投稿に返信し、Charlie(charlie.example
)がBobの返信にさらに返信し、Dave(dave.example
)がAliceの元の投稿に直接返信する状況を考えてみましょう:
Aliceの元投稿
├── Bobのコメント
│ └── Charlieのコメント
└── Daveのコメント
このとき、各サーバーは次のような情報だけを持っている可能性があります。alice.example
はAliceの元投稿とBobの返信、Daveの返信は知っていますが、Charlieの返信は知らないかもしれません。bob.example
はAliceの元投稿とBobの返信、Charlieの返信は知っていますが、Daveの返信は知らないかもしれません。結果として、誰も会話全体の完全な全体像を見ることができなくなります。
解決策のための基盤概念:context
属性
二つの主要な解決策を見る前に、核となるcontext
属性について理解する必要があります。ActivityStreams 2.0で定義されたcontext
属性は、関連するオブジェクトをグループ化するために使用されます。しかし仕様ではこれを「意図的に曖昧に」(intentionally vague)定義しているため、実際の実装ではさまざまな方法で活用されています。
実際のcontext
値の形態
1. 単純な識別子
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Note",
"content": "最初のコメントです",
"context": "https://example.com/conversations/abc123"
}
2. Mastodonスタイル(ostatus:conversation
)
MastodonはActivityPub標準のcontext
と共に、OStatus時代から使用してきたconversation
属性を併用しています。
{
"@context": [
"https://www.w3.org/ns/activitystreams",
{
"ostatus": "http://ostatus.org#",
"conversation": "ostatus:conversation"
}
],
"type": "Note",
"content": "これは返信です",
"context": "https://mastodon.social/contexts/abc123",
"conversation": "tag:mastodon.social,2025:objectId=12345:objectType=Conversation"
}
3. 解釈可能なコレクションURL(FEP 7888方式)
この場合、コンテキストURLにGET
リクエストを送ると、その会話のすべての投稿を含むOrderedCollection
が返されます。これはFEP-7888: Demystifying the context propertyで提案された方式です。
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Note",
"content": "スレッド会話の一部です",
"context": "https://forum.example/topics/technology/thread-42",
"inReplyTo": "https://forum.example/posts/789"
}
第一のアプローチ:返信ツリークローリング(reply tree crawling)
概要と歴史
返信ツリークローリング方式はMastodonの @jonnyjonny (good kind) 氏が開拓した方法です。2024年4月15日に初めて提案され、2025年3月12日にMastodonコアにマージされました。
この方式の核心アイデアは「すべての返信を取得する」(fetch all replies)ことです。返信ツリー全体を順次クローリングして、欠落した会話を見つけ出すというものです。
技術的な動作原理
1. 必要な前提条件
この方式が機能するためには、すべてのActivityPubオブジェクトがreplies
コレクションを提供する必要があります。これはActivityPubオブジェクトが受け取った返信のリストを表すコレクションです。これにより、特定の投稿に付いたすべての返信を探索できます。
{
"id": "https://alice.example/posts/1",
"type": "Note",
"content": "どう思いますか?",
"replies": {
"type": "OrderedCollection",
"id": "https://alice.example/posts/1/replies",
"totalItems": 3,
"first": "https://alice.example/posts/1/replies?page=1"
}
}
2. クローリングアルゴリズム
返信ツリークローリングの動作方式は本質的に深さ優先探索(DFS)に類似しています。開始点となる投稿から始めて、すべての返信を見つけて下っていく過程を繰り返します。
具体的なプロセスを見ると、まず開始投稿のreplies
コレクションを確認します。このコレクションにはその投稿に直接付けられた返信のリストが含まれています。次に各返信を一つずつ取得して処理しますが、ここで重要なのは各返信もまた独自のreplies
コレクションを持つ可能性があるという点です。
async function crawlReplyTree(postUrl: URL): Promise<Note[]> {
const post = await fetchNote(postUrl);
const allReplies: Note[] = [];
const replies = await post.getReplies();
if (replies) {
for await (const reply of replies.getItems()) {
if (reply instanceof Note) {
allReplies.push(reply);
const subReplies = await crawlReplyTree(reply.id!);
allReplies.push(...subReplies);
}
}
}
return allReplies;
}
この方式の核心は、各ノード(投稿)が自分に付けられた返信のリストを正確に提供するという前提に基づいている点です。
3. Mastodonの実際の実装
Mastodonでは理論的なアルゴリズムを実際のネットワーク環境に合わせて調整した実装を使用しています。核心的な違いは現実的な制約を考慮している点です。
@jonnyjonny (good kind) 氏の説明によると、現在の実装にはいくつかの実用的な考慮事項が含まれています。拡張された投稿から始めて下に進み、ツリーのどの地点からでもクローリングを開始でき、重複クローリングを防止するクールダウンメカニズムを含みます。
長所
-
汎用性:
inReplyTo
とreplies
属性はほぼすべてのActivityPub実装で普遍的に使用されています。したがって、既存のインフラを大きく変更せずに適用できます。 -
実装間の一貫性:ほとんどのActivityPub実装体でこれらの属性の使用法が大きく異なりません。
-
完全なツリー構成:理想的な場合、すべてのブランチとリーフを含む完全な会話ツリーを得ることができます。
短所
-
ネットワーク脆弱性:返信ツリーの単一ノードが一時的または永続的にアクセス不可能になると、そのノードから派生するすべてのブランチもアクセスできなくなります。
-
線形的な作業量増加:CPU時間、ネットワークリクエストなどの作業量が返信ツリーのサイズに比例して線形的に増加します。大規模な議論では性能問題が発生する可能性があります。
-
再クローリングの必要性:新しいブランチ発見のためには全体の返信ツリーを再度クローリングする必要があります。急速に成長する議論では、クローリング開始時点によって完全なツリーを得られない可能性があります。
-
不完全な実装現実:現実的にすべてのActivityPub実装体が
replies
コレクションを提供しているわけではありません。Mastodonはパフォーマンス上の理由から同じサーバーの返信のみ最大5つまでreplies
コレクションに含め、多くの小規模実装体はパフォーマンス上の理由でこれを省略したり不完全に実装したりしています。
現在の実装状況
現在、Mastodonがこの方式の唯一の完全な実装体です。しかしこの方式はMastodon固有のものではなく、他の実装体も採用することができます。
第二のアプローチ:コンテキスト所有者ベース方式(context owner approach)
概要と背景
コンテキスト所有者方式は複数のFEP[^1]の組み合わせから誕生しました。FEP-7888は「context
属性の明確化」(demystifying the context
property)を扱い、FEP-171bは「会話コンテナ」(conversation containers)を定義し、FEP-f228は上記FEPの統合および拡張を提案しています。
この方式の核心は**「コンテキスト所有者」**(context owner)の概念です。会話の元作成者や指定された主体がその会話のすべての内容を管理する中央集権的なアプローチです。
[^1]: Fediverse Enhancement Proposalの略で、フェディバースの改善事項を提案し議論するための公式文書体系です。新機能やプロトコル拡張を標準化するプロセスで使用されます。
技術的な動作原理
1. コンテキスト所有者の役割
コンテキスト所有者は誰になるのか?一般的にスレッドの最上位投稿(ルートポスト)を作成したユーザーがコンテキスト所有者になります。例えば、Aliceが「今日の天気はどうですか?」という元投稿を作成した場合、Aliceがその会話のコンテキスト所有者になります。
しかしフォーラムやグループ環境では、フォーラム管理者やグループ所有者がコンテキスト所有者の役割を果たすこともあります。核心は誰か一人がその会話の「正規メンバーシップ」を決定する権限を持つという点です。
コンテキスト所有者は自分が管理する会話のすべてのメンバーを含むOrderedCollection
を提供します。
{
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/fep/171b"
],
"type": "OrderedCollection",
"id": "https://alice.example/conversations/tech-discussion",
"attributedTo": "https://alice.example/users/alice",
"collectionOf": "Activity",
"totalItems": 15,
"orderedItems": [
"https://alice.example/activities/add/1",
"https://alice.example/activities/add/2",
"https://alice.example/activities/add/3"
]
}
2. 二段階アクティビティプロセス
この方式ではコメント追加が必ず二段階で行われる必要があります。なぜこのように複雑にする必要があるのでしょうか?
一つ目の理由はモデレーションです。単に返信を作成しただけでは自動的にその会話に含まれるのではなく、コンテキスト所有者の承認を経る必要があります。
二つ目の理由は一貫性です。コンテキスト所有者が管理するコレクションにはAdd
アクティビティのみが入るため、後にこのコレクションを読む他のサーバーが「これらはすべてコンテキスト所有者が承認した内容である」ということを明確に知ることができます。
三つ目の理由は拡散(broadcasting)です。直接コメントだけでなく会話に属するすべてのコメントと返信コメントはすべてコンテキスト所有者に送信されるため、コンテキスト所有者はその会話に含まれるすべてのノードを把握しています。したがって、すべての会話参加者に新しいコメントが追加されたことを通知することができます。
第1段階:返信作成者が一般的なCreate(Note)
アクティビティを送信
BobがAliceの投稿に返信したいと思っています。Bobは通常通りCreate(Note)
アクティビティを生成しますが、Note
オブジェクトのcontext
属性にAliceが管理する会話IDを含めます。
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Create",
"actor": "https://bob.example/users/bob",
"published": "2025-06-09T11:00:00Z",
"to": ["https://alice.example/users/alice"],
"object": {
"type": "Note",
"id": "https://bob.example/notes/reply-456",
"content": "本当に良い指摘ですね!",
"inReplyTo": "https://alice.example/posts/original",
"context": "https://alice.example/conversations/tech-discussion"
}
}
重要な点は、Bobがこのアクティビティをコンテキスト所有者であるAliceに直接送信するということです(to
フィールド参照)。これはAliceがBobの返信を知ることができるようにするためです。
第2段階:コンテキスト所有者(Alice)がAdd(Note)
アクティビティを生成
AliceはBobの返信を受け取り、これが自分の会話に含めるべき内容だと判断します。そこでAliceはAdd(Note)
アクティビティを生成して、Bobの返信を自分の会話コレクションに追加します。
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Add",
"actor": "https://alice.example/users/alice",
"published": "2025-06-09T11:05:00Z",
"object": "https://bob.example/notes/reply-456",
"target": {
"type": "OrderedCollection",
"id": "https://alice.example/conversations/tech-discussion",
"attributedTo": "https://alice.example/users/alice"
},
"to": ["https://www.w3.org/ns/activitystreams#Public"]
}
このAddアクティビティは「AliceがBobの返信を自分の会話に公式に含めた」という意味です。もしAliceがBobの返信がスパムや不適切な内容だと判断した場合、このAdd(Note)
アクティビティを生成しないことができます。
3. バックフィルメカニズム
個別の実装体はコンテキスト所有者に全会話内容をリクエストすることができます。
async function performContextBackfill(contextUrl: URL): Promise<Note[]> {
const collection = await fetchCollection(contextUrl);
const notes: Note[] = [];
for await (const item of collection.getItems()) {
if (item instanceof Add) {
const note = await item.getObject();
if (note instanceof Note) {
notes.push(note);
}
}
}
return notes;
}
長所
-
疑似中央化(pseudo-centralization)の利点:コンテキスト所有者が提供する「単一の真実の源泉」(single source of truth)を通じて一貫した会話状態を維持できます。
-
効率的なネットワーク使用:コンテキスト所有者への一度のリクエストで全体の会話を取得できるため、返信ツリークローリングよりもネットワーク効率が高いです。
-
中間ノード障害の克服:返信ツリークローリングとは異なり、中間ノードがダウンしていてもコンテキスト所有者を通じて全体の会話にアクセスできます。
-
効率的な重複排除:コンテキストレベルでオブジェクトの重複排除が可能で、全体のネットワークリクエスト数とCPU時間を削減できます。
-
同期最適化:ID ハッシュサムを通じた同期方法でネットワークコールをさらに減らすことができます。
短所
-
コンテキスト所有者依存性:最大の弱点はコンテキスト所有者への依存性です。コンテキスト所有者サーバーにアクセスできなくなると、全体の会話バックフィルが不可能になります。
-
限定された可視性:コンテキスト所有者は自分が知っているオブジェクト/アクティビティのみを応答できます。
-
上位伝播欠落問題:核心的な限界として、ルートに再び上位伝播されない下位ブランチはコンテキスト所有者が知ることができません。
-
実装体サポート必要:コンテキスト所有者がこの方式をサポートしている場合のみ機能するため、他のバックフィル戦略と組み合わせる必要があります。
現在の実装状況
NodeBB、Discourse、WordPress、Frequency、Mitra、Streamsが現在この方式を実装しており、LemmyとPiefedが関心を表明しています。
重要な議論ポイント
1. モデレーションパラダイムの衝突
関連NodeBBスレッドで @silverpill 氏が提起した核心的な問題点です。
二つのアプローチが互いに衝突しないと言われていますが、これらの「スレッディングパラダイム」はモデレーションの問題に対して異なる解決策を提示しています。
I don't fully agree with this statement, because these 'threading paradigms' suggest two different solutions to the problem of moderation.
コンテキスト所有者方式のモデレーション
Aliceがスパムコメントに対してAdd(Note)
アクティビティを生成しなければ、そのコメントは会話から除外されます。
返信ツリークローリングのモデレーション
各返信は独立しており、作成者たちが直接返信のみをモデレーションできます。元投稿作成者が全体の会話を制御することはできません。
2. 上位伝播欠落問題の解決策
アドレッシングルールの活用(FEP-171b)
FEP-171bでは「返信のオーディエンスは会話ルートからコピーされなければならない」(The audience of a reply MUST be copied from a conversation root)というルールを提示しています。
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Create",
"actor": "https://charlie.example/users/charlie",
"object": {
"type": "Note",
"content": "Bobのコメントへの返信",
"inReplyTo": "https://bob.example/comments/2",
"context": "https://alice.example/conversations/1",
"to": [
"https://bob.example/users/bob",
"https://alice.example/users/alice"
]
}
}
ハイブリッドバックフィルメカニズム
多くの実装体が複数の方式を組み合わせる方法を採用しています。
async function hybridBackfill(conversationId: URL): Promise<Note[]> {
const strategies = [
() => contextOwnerBackfill(conversationId),
() => replyTreeCrawling(conversationId),
() => mentionBasedDiscovery(conversationId)
];
for (const strategy of strategies) {
try {
const result = await strategy();
if (result.length > 0) return result;
} catch (error) {
console.warn('Strategy failed, trying next:', error);
}
}
return [];
}
追加的なバックフィルメカニズム
-
定期的クローリングバックフィル:この方式は定期的な健康診断のようなものです。システムが決められた周期ごとに活発な会話をチェックして、欠落した返信がないか確認します。
-
ユーザートリガーバックフィル:ユーザーが特定の会話ページにアクセスすると、システムは即座に現在保有しているコンテキストコレクションをレビューし、リアルタイムで欠落した返信を探索します。
-
メンションベースバックフィル:ユーザーが会話で他の人をメンションする自然な行動を通じて、欠落した返信チェーンを発見するメカニズムです。
async function onMentionReceived(activity: Create): Promise<void> { const mention = await activity.getObject(); if (mention.context && mention.replyTargetId) { const missingChain = await traceReplyChain(await mention.getReplyTarget()); await addToContext(mention.context, missingChain); } }
実際の課題
-
循環参照防止:バックフィルプロセスで無限ループに陥るのを防ぐことは非常に重要です。実際の実装では訪問したURLを追跡し、最大探索深度を制限する安全装置を設けます。
-
パフォーマンス最適化:大規模な会話では数百の返信が付くことがあり、これをすべて一度に処理しようとするとサーバーに過度な負荷がかかる可能性があります。バッチ処理(batch processing)は複数の会話を同時に処理する際に小さなグループに分けて順次処理し、各バッチの間に短い休息時間を設ける方式です。
-
エラー処理と回復:分散ネットワーク環境では様々な種類のエラーが発生する可能性があります。実際の実装では複数のバックフィル戦略を順次試みる復元力のあるアプローチを使用します。
標準化の取り組みと将来展望
FEP収束議論
現在フェディバースコミュニティではFEP収束スレッドを通じて複数のFEPを統合する取り組みが進行しています。
この議論で扱われている主要なFEPは、公開的に追加可能なActivityPubコレクションを定義するFEP-400e、曖昧に定義されたcontext
属性に対する具体的な使用法を提示するFEP-7888、中央化された会話管理メカニズムを扱うFEP-171b、そして返信ツリーの全体的な視覚化方法を提案するFEP-76eaです。
実装体間の協力
現在、様々な実装体が実用的な相互運用性のために協力しています。これは完璧な標準が確定するのを待つよりも、現在利用可能な方法を組み合わせて最善の結果を得ようとする実務的なアプローチです。
NodeBBとDiscourseの協力事例
この二つのフォーラムソフトウェアはフォーラムに特化したバックフィルメカニズムを共有しています。フォーラムの特性上、会話が構造化されており長期間継続することが多いため、トピックとカテゴリの概念を活用したコンテキスト管理が特に重要です。
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Note",
"context": "https://community.nodebb.org/topic/18844",
"audience": "https://community.nodebb.org/category/development",
"tag": [
{
"type": "Link",
"href": "https://meta.discourse.org/t/activitypub-support/12345",
"rel": "related"
}
]
}
Mastodonとの互換性考慮
Mastodonは最大のフェディバースプラットフォームであるため、他の実装体はMastodonとの互換性を考慮する必要があります。特にMastodonが使用するostatus:conversation
概念を一緒にサポートするケースが多いです。
{
"@context": [
"https://www.w3.org/ns/activitystreams",
{
"ostatus": "http://ostatus.org#",
"conversation": "ostatus:conversation"
}
],
"type": "Note",
"content": "Mastodon互換返信",
"context": "https://mastodon.social/contexts/abc123",
"conversation": "tag:mastodon.social,2025:objectId=12345:objectType=Conversation"
}
このような下位互換性の維持はフェディバースエコシステムの分裂を防ぎ、ユーザー体験を向上させる上で重要な役割を果たします。
今後の開発方向:ハイブリッドアプローチの標準化
将来的には単一の「正解」を見つけるよりも、複数の方式を体系的に組み合わせる標準化されたアプローチが登場する可能性が高いです。これは各方式の長所を生かしながら短所を補完するbest-of-both-worldsアプローチです。
ベストプラクティスガイドライン
-
複数戦略の実装:決して一つのバックフィル方式だけに依存しないでください。フェディバースの多様性と不確実性を考慮すると、複数の戦略を組み合わせることが不可欠です。各戦略は異なる状況で強みを発揮するため、状況に応じて適切な戦略を選択できる柔軟性を確保する必要があります。
例えば、活発なフォーラム討論ではコンテキスト所有者方式が効果的かもしれませんが、Mastodonの一般的な会話では返信ツリークローリングがより適切かもしれません。
-
リソース管理:バックフィル作業はかなりのサーバーリソースを消費する可能性があります。特に人気のある会話や大規模な議論の場合、数百のネットワークリクエストが必要になることがあります。したがって、適切な制限と調整メカニズムを実装する必要があります。
-
モニタリングとロギング:バックフィルシステムのパフォーマンスと信頼性を継続的にモニタリングすることが重要です。どの方式が最も効果的か、どのような種類のエラーが頻繁に発生するかなどを追跡する必要があります。
結論
「静かなフェディバース」問題は分散型ソーシャルネットワークの根本的な課題です。この記事で検討した二つの主要アプローチ—返信ツリークローリングとコンテキスト所有者方式—はそれぞれ固有の長所と短所を持っています。
核心的洞察
完璧な解決策はありません。 両アプローチとも特定の状況で限界を示します。分散ネットワークの本質的な特性上、100%完璧な会話復元は現実的に難しい場合があります。
ハイブリッドアプローチが現実的です。 ほとんどの成功した実装体は複数のバックフィル戦略を組み合わせて使用しています。一つの方法が失敗しても別の方法で補完できる弾力性が重要です。
標準化が進行中です。 FEPプロセスを通じて相互運用性を高める努力が続いています。しかし完全な標準を待つよりも、現在可能な方法を実用的に組み合わせる方が現実的です。
ユーザー体験が核心です。 技術的完成度も重要ですが、最終的にはユーザーが完全な会話を見ることができるかが鍵となります。技術的エレガンスよりも実用的効果を優先すべきです。
今後の方向性
フェディバースの会話バックフィル問題は単に技術的な問題を超えて、分散型ネットワークにおけるガバナンス、モデレーション、ユーザー体験の複合的な問題です。
特にモデレーションパラダイムの違いは単純な技術的互換性を超える哲学的問題です。コンテキスト所有者が全体の会話を制御できるべきか、それとも各返信作成者が独立してモデレーションできるべきか?こうした問いはフェディバースがどのような種類のソーシャル空間になるべきかという根本的な考察につながります。
2025年はこれらの問題に対する解決策が本格的に展開されテストされる年になると思われます。開発者とユーザーの継続的な関心と参加を通じて、フェディバースがより豊かで接続されたソーシャルネットワークへと発展していくことができるでしょう。
重要なのは完璧さよりも改善です。現在の「静かなフェディバース」問題が完全に解決されなくても、これらの努力を通じてユーザーがより完全な会話を体験できるようになれば、それだけでも意味のある進展だと言えるでしょう。