📶

Nostr の Outbox model を4行で説明する

に公開

アウトボックスモデルとは

NostrのアウトボックスモデルはNostrイベントの送信および受信をどのリレーに接続して行うべきかの指標を定めたルールであり、 NIP-65 で定められている通りなのですが、仕様だけ読んでもすぐにはイメージが湧きません。
もう少し噛み砕いて、大まかなルールを以下のように説明することができます。

  1. 自分の署名したイベントは自分のアウトボックスリレーに送信する
  2. 自分に対するイベント(メンション等)は自分のインボックスリレーに見に行く
  3. Aさんの署名したイベントを購読する場合はAさんのアウトボックスリレーに見に行く
  4. Aさんに対してメンションを送る場合はAさんのインボックスリレーに送信する

図にするとこんな感じです[1]

outbox model diagram 1

自分が署名したイベントをどのリレーに送信し、どのリレーで自分に対するイベントをチェックするかを宣言するためのイベントが kind:10002 であり、それぞれ write relays, read relays として定義されますが、これは誤解を招きやすい命名であるため、アウトボックスモデルの場合は outbox relays, inbox relays と表記することが多いです。
本稿でもアウトボックスリレー、インボックスリレーと表記します。

アウトボックスでないモデルは何て呼ぶの?

NIPs でクライアントがリレーに接続するためのルールを定義する仕様は NIP-65 だけで、他に存在しません。接続するリレーセットをユーザーに指定してもらって、そのリレーセットだけに接続する方式が一番シンプルで採用しているクライアントも多いですが、名前が付いていないので本稿では「リレー固定モデル」と呼ぶことにします。

アウトボックスモデルを採用しているNostrクライアントは?

本家のGossipのほか、Android向けのAmethystなどがあります。
拙作のクライアントもアウトボックスモデルを採用していますので以下に紹介します。

Nos Haiku

https://nos-haiku.vercel.app/
Sudo Haikuを模したSNS風クライアントです。

KUCHIYOSE

https://kuchiyose.vercel.app/
ソーシャルブックマークです。

アウトボックスモデルじゃないNostrクライアントはどうしてるの?

以下に紹介するクライアントはアウトボックスモデルではないですが、特徴を紹介したいと思います。

Rabbit

https://rabbit.syusui.net/
ユーザーが指定したリレーセットにのみ接続します。WriteとReadの区別はありません。設定したリレーセットはブラウザに保存され、リレーには送信されません。

nostter

https://nostter.app/
kind:10002 イベントをリレーとやり取りしてWriteリレーとReadリレーを使用しています。ただしアウトボックスモデルではなく、Writeリレーに送信、Readリレーを購読、としたリレー固定モデルとなっています。

lumilumi

https://lumilumi.app/
nostter同様の仕様となっています。

追記

nostter は 4. を(一部を除いて)実装しているそうでした

https://nostter.app/nevent1qyxhwumn8ghj77tpvf6jumt9qys8wumn8ghj7un9d3shjtt2wqhxummnw3ezuamfwfjkgmn9wshx5uqqypjdqewe9u9wqrex7zeq93l276xaqzv3yawwp66mga66nqzhnyxl2m8vyam

アウトボックスモデルとリレー固定モデルのメリット・デメリット

リレー固定モデル

メリット
  • 直感的でわかりやすい
  • 実装が簡単
デメリット
  • 同じリレーを使っていない人同士が繋がれない
  • みんなが同じリレーを使いたがるようになるため中央集権化してしまう
  • 人が増えるほど巨大なリレーを必要とするためスケールできない

アウトボックスモデル

メリット
  • 確実に繋がれるためすれ違いが起こらない
  • 他の人と同じリレーを使う必要が無くリレーが分散され、非中央集権化が促進される
  • 人が増えても分散してリレーを利用すれば良いためスケールしやすい
デメリット
  • 直感的でなく利用者に十分な説明が必要となる
  • NIP-65 をサポートしているクライアントが増えないと意味がない
  • 実装がすごく大変

実際どうやって実装してるの?

アウトボックスモデルを採用したクライアントを実際に作ってみて、多少の知見を得たので共有したいと思います。
最大の難所は フォローTL の構築です。誰かの個人ページでその人の投稿だけを表示するTLの構築はまったく難しくありません。仕様通りにその人のアウトボックスリレーから投稿を取得して表示すればいいだけです。
問題はそれが100人単位のフォローTLになった場合、100人単位分のアウトボックスリレーを購読しなければなりません。重複する分は除けるとしても、1人あたりのアウトボックスリレーが5個も10個もあった場合はリレーの数も100個単位近くなってしまうことは避けられません。

outbox model diagram 2
大変そうな図[2]

ブラウザのWebSocketの接続数の上限は 6~255 程度らしいです。また、それほど多くのリレーに接続していたらパフォーマンスも低下してしまいます。

現実的な解としては、除外しても問題ないリレーを間引くことでしょう。
ここで私は「重複の多いリレーを優先しつつ、各フォローイーのリレーが最低1個含まれるリレーセット」を算出する方式で実装することにしました。
実際のフォローイーの使用リレーを把握したいと思い、こんなツールを作りました。

https://nikolat.github.io/kagenui/

フォローイーのアウトボックスリレー全部の場合とフォローイーにつき最低1個のリレーを選択した場合の表示を切り替えられるようになっています。

これで現実的な数のリレー接続数で実装できる目処が立ちました。

まとめ

  • Nostrをスケールさせるため、リレーの分散を促進するためにアウトボックスモデルは有効
  • 実装は大変だけど、接続しなくてもいいリレーを上手に間引くことで現実的な実装が可能

みなさんもぜひアウトボックスモデルを利用してみてはいかがでしょうか。

脚注
  1. Claudeさんに描いていただきました ↩︎

  2. Claudeさんに描いていただきました ↩︎

Discussion