はじめに
Nostr(Notes and Other Stuff Transmitted by Relays)は、シンプルかつ柔軟なプロトコルを基盤とした分散型のソーシャルネットワークシステムです。本記事では、Nostrの基本概念から技術的な詳細、実装例に至るまで、あらゆる側面を包括的に解説します。コードスニペットを交えながら、Nostrの仕組みを深く理解していきましょう。
Nostrとは
Nostrは、「Notes and Other Stuff Transmitted by Relays」の略で、検閲抵抗性を持つ分散型のソーシャルネットワークプロトコルです。Twitterのようなマイクロブログサービスに対する代替として設計されており、中央集権的なサーバーに依存せず、ユーザー間で直接データをやり取りすることを目指しています。
Nostrの目的
- 検閲抵抗性: 中央管理者が存在しないため、特定のコンテンツの削除や制限が困難。
- シンプルさ: プロトコル自体が非常にシンプルで、実装が容易。
- 拡張性: NIP(Nostr Implementation Possibilities)を通じて機能拡張が可能。
Nostrの基本構造
Nostrは主に以下の3つのコンポーネントから構成されます。
- イベント(Events)
- クライアント
- リレー(Relays)
イベント(Events)
イベントはNostrプロトコルの基本単位であり、ユーザーのアクション(例:投稿、フォロー)を表現します。イベントはJSON形式で表現され、以下のプロパティを含みます。
-
id
: イベントのユニークな識別子(SHA-256ハッシュ)。 -
pubkey
: イベントの作成者の公開鍵。 -
created_at
: UNIXタイムスタンプ。 -
kind
: イベントの種類(例:1はテキストノート)。 -
content
: イベントの内容。 -
tags
: イベントに関連するメタデータ。 -
sig
: イベントの署名。
イベントの例
{
"id": "4376c65d2f232afbe9b882a35baa4f6fe8667c4e684749af565f981833ed6a65",
"pubkey": "6e468422dfb74a5738702a8823b9b28168abab8655faacb6853cd0ee15deee93",
"created_at": 1673347337,
"kind": 1,
"content": "Walled gardens became prisons, and nostr is the first step towards tearing down the prison walls.",
"tags": [
["e", "3da979448d9ba263864c4d6f14984c423a3838364ec255f03c7904b1ae77f206"],
["p", "bf2376e17ba4ec269d10fcc996a4746b451152be9031fa48e74553dde5526bce"]
],
"sig": "908a15e46fb4d8675bab026fc230a0e3542bfade63da02d542fb78b2a8513fcd0092619a2c8c1221e581946e0191f2af505dfdf8657a414dbca329186f009262"
}
クライアント
クライアントは、ユーザーがNostrプロトコルとやり取りするためのソフトウェアツールです。デスクトップアプリやブラウザベースのアプリケーションが一般的です。クライアントはユーザーの公開鍵を使用してイベントを作成し、リレーと通信します。
リレー(Relays)
リレーは、Nostrイベントを受信、保存、配信するサーバーです。リレーは分散型であり、誰でもホスト可能です。クライアントは複数のリレーに接続することで、データの冗長性と耐障害性を確保します。
Nostrプロトコルの特徴
シンプルさ
Nostrのプロトコルは非常にシンプルです。イベントはJSON形式で表現され、WebSocketを通じてクライアントとリレー間でやり取りされます。プロトコル自体が少ない行数で定義されているため、実装が容易であり、言語やプラットフォームに依存しません。
耐障害性
中央集権的なサーバーに依存しないため、システム全体の耐障害性が高いです。リレーがダウンしても、他のリレーに接続することでサービスを継続できます。また、ユーザー自身がリレーを選択・変更できるため、特定のリレーの障害が全体に影響を与えにくくなっています。
検証可能性
公開鍵暗号を基盤としているため、イベントの真正性が容易に検証できます。各イベントには作成者の公開鍵と署名が含まれており、第三者がイベントの信頼性を確認できます。
NIP(Nostr Implementation Possibilities)
NIPは、Nostrプロトコルの拡張機能を提案・標準化するための仕組みです。NIPを通じて、新しい機能や改善点をコミュニティ全体で共有し、実装の互換性を保ちます。
NIPの役割
- 標準化: 新しい機能やプロトコルの拡張を標準化し、異なるクライアントやリレー間での互換性を確保。
- 協調開発: 開発者間でのコミュニケーションと協力を促進し、Nostrエコシステム全体の発展を支援。
主要なNIPの例
- NIP-01: 基本プロトコルの仕様。
- NIP-02: 公開フォロワーリストとニックネーム。
- NIP-04: プライベートダイレクトメッセージ。
- NIP-05: 「認証済み」アカウント。
- NIP-09: イベントの削除(作成者による)。
Nostrの利用開始方法
鍵の理解と管理
Nostrアカウントは、公開鍵と秘密鍵のペアで管理されます。
-
公開鍵(Public Key): ユーザーの識別子として機能し、他者に公開されます。
npub
で始まる形式で表示されます。 -
秘密鍵(Private Key): ユーザーの署名に使用され、厳重に管理する必要があります。
nsec
で始まる形式で表示されます。
鍵の生成
鍵ペアは、Nostrクライアントが自動的に生成します。以下は、JavaScriptを使用した簡単な例です。
// 使用する暗号ライブラリのインポート
import { generatePrivateKey, getPublicKey, signEvent } from 'nostr-tools';
// 秘密鍵の生成
const privateKey = generatePrivateKey();
console.log('Private Key:', privateKey);
// 公開鍵の取得
const publicKey = getPublicKey(privateKey);
console.log('Public Key:', publicKey);
鍵の保存
秘密鍵はリセット不可であるため、安全な場所に保管することが重要です。推奨される方法は以下の通りです。
- パスワードマネージャー: KeePassや1Passwordなど。
- ハードウェアデバイス: Ledger Nanoなどのハードウェアウォレット。
- ブラウザ拡張機能: Connect、nos2x、Albyなど。
クライアントの選択と設定
Nostrクライアントは多岐にわたり、各クライアントは独自の機能やインターフェースを提供します。以下は、一般的なクライアントの例です。
- Damus: セキュリティに重点を置いたデスクトップアプリ。
- Amber: Android向けのクライアント。
- Specter: ブラウザベースのクライアント。
クライアントの設定例
以下は、ブラウザのJavaScriptコンソールを使用してNostrリレーに接続し、ノートを取得する例です。
// リレーへの接続
const ws = new WebSocket("wss://nostr-pub.wellorder.net");
// 接続が開いたときの処理
ws.addEventListener('open', function (event) {
// サブスクリプションのリクエスト
const subscription = JSON.stringify([
"REQ",
"my-sub",
{ "kinds": [1], "authors": ["35d26e4690cbe1"] }
]);
ws.send(subscription);
});
// メッセージを受信したときの処理
ws.addEventListener('message', function (event) {
const data = JSON.parse(event.data);
if (data[0] === 'EVENT') {
console.log('Note:', data[2]["content"]);
}
});
このスクリプトは、指定した公開鍵の作者によるテキストノート(kind:1
)をリレーから取得し、コンソールに表示します。
Nostrの技術的詳細
WebSocketを用いた通信
NostrはWebSocketを唯一の通信手段として採用しています。これにより、リアルタイムでの双方向通信が可能となり、クライアントとリレー間でのイベントの即時配信が実現します。
WebSocketの基本的な流れ
- 接続確立: クライアントがリレーに接続。
- サブスクリプション: クライアントが興味のあるイベントのフィルターをリレーに送信。
- イベントの受信: リレーがフィルターに合致するイベントをクライアントに送信。
- 切断: 必要に応じて接続を終了。
イベントの生成と検証
各イベントは、作成者の秘密鍵で署名されます。これにより、イベントの真正性が保証されます。
イベントの署名
イベントの署名は、以下の手順で行われます。
- イベントオブジェクトの作成: 必要なプロパティを設定。
- JSON文字列への変換: イベントオブジェクトをJSON形式に変換。
- ハッシュ計算: SHA-256ハッシュを計算。
- 署名生成: 秘密鍵を用いてハッシュに署名。
署名の検証
クライアントやリレーは、公開鍵を使用して署名の有効性を検証します。これにより、イベントが真正な作成者によって生成されたことが確認できます。
実装例
ここでは、簡単なNostrクライアントをJavaScriptで実装し、リレーに接続してデータを取得する方法を紹介します。
簡単なNostrクライアントの作成
以下のスクリプトは、ブラウザのコンソールで実行可能な簡単なNostrクライアントの例です。
// リレーへの接続
const ws = new WebSocket("wss://nostr-pub.wellorder.net");
// イベントを格納する配列
const notes = [];
// 接続が開いたときの処理
ws.addEventListener('open', function (event) {
// サブスクリプションのリクエスト
const subscription = JSON.stringify([
"REQ",
"my-sub",
{ "kinds": [1], "authors": ["35d26e4690cbe1"] }
]);
ws.send(subscription);
});
// メッセージを受信したときの処理
ws.addEventListener('message', function (event) {
const data = JSON.parse(event.data);
if (data[0] === 'EVENT') {
const noteContent = data[2]["content"];
notes.push(noteContent);
console.log('Note:', noteContent);
}
});
リレーへの接続とデータの取得
上記のスクリプトでは、以下の手順でリレーからデータを取得しています。
- WebSocket接続の確立: 指定したリレーURLに接続します。
-
サブスクリプションの送信:
kind:1
のイベント(テキストノート)を指定した公開鍵の作者から取得するようリレーにリクエストします。 - イベントの受信と表示: 受信したイベントの内容をコンソールに表示し、配列に格納します。
このように、Nostrはシンプルなプロトコル設計により、短いコードで基本的な機能を実装できます。
まとめ
Nostrは、そのシンプルさと柔軟性により、分散型ソーシャルネットワークの新たな可能性を切り開いています。公開鍵暗号を基盤とした信頼性の高いイベントシステム、拡張性を持つNIP、そして分散型リレーのアーキテクチャにより、検閲抵抗性と耐障害性を兼ね備えたプラットフォームを実現しています。
本記事では、Nostrの基本概念から技術的な詳細、実装例までを網羅的に解説しました。Nostrのエコシステムは日々進化しており、今後も多くの革新が期待されます。ぜひ、Nostrを活用し、分散型ソーシャルネットワークの未来を共に築いていきましょう。
参考資料
- Nostr公式GitHubリポジトリ
- Awesome Nostr - Nostrに関するリソースのキュレーションリスト
- Nostr-rs-relay - Rustで実装されたNostrリレー
- NIP-01: Basic Protocol
- Relay Wizard - リレーのセットアップスクリプト
- Relay Runner - リレーの詳細なセットアップガイド