revolution/laravel-nostr は、Nostr プロトコルを Laravel から利用するためのパッケージです。Key の生成・変換、イベントの取得・発行、pool(複数リレー)対応、NIP-05 プロフィール、NIP-17 Private Direct Messages、そして Laravel Notifications との統合を提供します。
Nostr の仕様はまだ進化中のため、このパッケージも継続的に開発されています。通知機能はすでに実用的に使えます。
ドライバー
このパッケージには 2 つのドライバーがあります。
| ドライバー | 説明 |
|---|
native | PHP のみで動作する実装。nostr-php を使用。現在はこちらで十分。 |
node | 外部 WebAPI(Node.js)に依存する実装。 |
native ドライバーのユニークな点は WebSocketHttpMixin の実装です。Laravel の HTTP クライアントで WebSocket に接続し、データを送受信したらすぐに切断します。WebSocket サーバーを起動し続ける必要がなく、Laravel ユーザーなら誰でも使える設計になっています。
native ドライバーは NIP-04 には対応していません。
デフォルトドライバーの設定
config/nostr.php または .env で設定します。
// config/nostr.php
'driver' => env('NOSTR_DRIVER', 'node'),
ドライバーを指定しない場合はデフォルトのドライバーが使われます。
use Revolution\Nostr\Facades\Nostr;
Nostr::event()->list();
ドライバーを明示的に指定することもできます。
use Revolution\Nostr\Facades\Nostr;
Nostr::driver('node')->event()->list();
Nostr::node()->event()->list();
Nostr::driver('native')->event()->list();
Nostr::native()->event()->list();
インストール
パッケージをインストールする
composer require revolution/laravel-nostr
設定ファイルを公開する
php artisan vendor:publish --tag=nostr-config
Key 管理
Key を生成する
use Revolution\Nostr\Facades\Nostr;
use Illuminate\Http\Client\Response;
/** @var Response $response */
$response = Nostr::key()->generate();
$keys = $response->json();
// [
// 'sk' => 'sk...',
// 'nsec' => 'nsec...',
// 'pk' => 'pk...',
// 'npub' => 'npub...',
// ]
Key を変換する
nsec から変換します。
use Revolution\Nostr\Facades\Nostr;
$response = Nostr::key()->fromNsec(nsec: 'nsec');
$keys = $response->json();
// ['sk' => '...', 'nsec' => '...', 'pk' => '...', 'npub' => '...']
Secret Key から変換します。
$response = Nostr::key()->fromSecretKey(sk: 'sk');
npub から変換します(公開鍵のみ)。
$response = Nostr::key()->fromNpub(npub: 'npub');
$keys = $response->json();
// ['pk' => '...', 'npub' => '...']
Public Key から変換します。
$response = Nostr::key()->fromPublicKey(pk: 'pk');
イベントの取得
複数イベントを取得する
use Illuminate\Http\Client\Response;
use Revolution\Nostr\Facades\Nostr;
use Revolution\Nostr\Filter;
use Revolution\Nostr\Kind;
$filter = Filter::make(
authors: ['my pk'],
kinds: [Kind::Text],
limit: 10,
);
/** @var Response $response */
$response = Nostr::event()->list(filter: $filter);
$events = $response->json('events');
// [
// ['id' => '...1', 'kind' => 1, 'content' => '...'],
// ['id' => '...2', 'kind' => 1, 'content' => '...'],
// ]
1 件のイベントを取得する
use Revolution\Nostr\Facades\Nostr;
use Revolution\Nostr\Filter;
use Revolution\Nostr\Kind;
$filter = Filter::make(
authors: ['my pk'],
kinds: [Kind::Metadata],
);
$response = Nostr::event()->get(filter: $filter);
$event = $response->json('event');
// ['id' => '...', 'kind' => 0, 'content' => '{name: ""}']
イベントの発行
単一リレーに発行する
use Revolution\Nostr\Facades\Nostr;
use Revolution\Nostr\Event;
use Revolution\Nostr\Kind;
$event = Event::make(
kind: Kind::Text,
content: 'hello',
created_at: now()->timestamp,
tags: [],
);
$sk = 'my sk';
$response = Nostr::event()->publish(event: $event, sk: $sk);
if ($response->successful()) {
$event = $response->json('event');
}
複数リレーに発行する(pool)
use Revolution\Nostr\Facades\Nostr;
use Revolution\Nostr\Event;
use Revolution\Nostr\Kind;
$event = Event::make(
kind: Kind::Text,
content: 'test',
created_at: now()->timestamp,
tags: [],
);
$responses = Nostr::pool()->publish(event: $event, sk: 'my sk');
// $responses は array<string, Response>
// ['wss://relay1' => $response, 'wss://relay2' => $response]
foreach ($responses as $relay => $response) {
if ($response->failed()) {
dump($relay . ' : ' . $response->body());
}
}
リレーサーバーの設定
使用されるリレーサーバー
Nostr::event() のみを使う場合は config/nostr.php の最初のリレーが使われます。Nostr::pool() を使う場合は設定内のすべてのリレーが対象になります。
実行時にリレーを変更する
use Revolution\Nostr\Facades\Nostr;
$response = Nostr::event()->withRelay('wss://')->...;
use Revolution\Nostr\Facades\Nostr;
$response = Nostr::pool()->withRelays(['wss://', 'wss://'])->...;
NIP-05 プロフィール
use Revolution\Nostr\Facades\Nostr;
$profile = Nostr::nip05()->profile('user@localhost');
// [
// 'user' => 'user@localhost',
// 'pubkey' => 'pk',
// 'relays' => [],
// ]
NIP-17 Private Direct Messages
NIP-17 は native ドライバーのみ対応しています。
プライベートメッセージを送信する
use Revolution\Nostr\Facades\Nostr;
$response = Nostr::driver('native')
->nip17()
->sendDirectMessage(
sk: 'sender-secret-key',
pk: 'receiver-public-key',
message: 'Hello, this is a private message!'
);
プライベートメッセージを復号する
use Revolution\Nostr\Facades\Nostr;
$response = Nostr::driver('native')
->nip17()
->decryptDirectMessage(
giftWrap: $receivedGiftWrap,
sk: 'receiver-secret-key'
);
$decryptedMessage = $response->json();
Laravel Notifications
NostrChannel を使うと、Laravel Notifications から Nostr にメッセージを送信できます。
Notification クラス
use Illuminate\Notifications\Notification;
use Revolution\Nostr\Notifications\NostrChannel;
use Revolution\Nostr\Notifications\NostrMessage;
use Revolution\Nostr\Tags\HashTag;
class TestNotification extends Notification
{
public function via(object $notifiable): array
{
return [
'mail',
NostrChannel::class,
];
}
public function toNostr(object $notifiable): NostrMessage
{
return new NostrMessage(
// content 内の #laravel は表示用、tags の HashTag はプロトコルレベルの分類用
content: 'hello #laravel',
tags: [
HashTag::make(t: 'laravel'),
],
);
}
}
オンデマンド通知
use Illuminate\Support\Facades\Notification;
use Revolution\Nostr\Notifications\NostrRoute;
Notification::route('nostr', NostrRoute::to(sk: 'sk'))
->notify(new TestNotification());
User モデルへの統合
use Illuminate\Notifications\Notifiable;
use Revolution\Nostr\Notifications\NostrRoute;
class User
{
use Notifiable;
public function routeNotificationForNostr($notification): NostrRoute
{
return NostrRoute::to(sk: $this->sk, relays: ['wss://']);
}
}
$user->notify(new TestNotification());
通知で使うリレーサーバー
デフォルトでは config/nostr.php のすべてのリレーが使われます。NostrRoute でリレーを指定すると実行時に変更できます。
use Revolution\Nostr\Notifications\NostrRoute;
return NostrRoute::to(sk: 'sk', relays: ['wss://', 'wss://']);