Overview
revolution/laravel-nostr is a package for using the Nostr protocol from Laravel. It provides key generation and conversion, event retrieval and publishing, pool (multi-relay) support, NIP-05 profiles, NIP-17 Private Direct Messages, and Laravel Notifications integration.
Because the Nostr specifications are still evolving, this package is under constant development. However, the notification features are already useful as-is.
Drivers
This package provides two drivers.
| Driver | Description |
|---|
native | Pure PHP implementation using nostr-php. This is sufficient for most use cases. |
node | Depends on an external WebAPI running on Node.js. |
A unique feature of the native driver is the WebSocketHttpMixin implementation. It connects to WebSocket using Laravel’s HTTP client, and disconnects as soon as data is sent and received. You do not need to keep a WebSocket server running, making it accessible to any Laravel user.
The native driver does not support NIP-04.
Setting the default driver
Set the default driver in config/nostr.php or .env.
// config/nostr.php
'driver' => env('NOSTR_DRIVER', 'node'),
If you do not specify a driver, the default will be used.
use Revolution\Nostr\Facades\Nostr;
Nostr::event()->list();
You can also specify the driver explicitly.
use Revolution\Nostr\Facades\Nostr;
Nostr::driver('node')->event()->list();
Nostr::node()->event()->list();
Nostr::driver('native')->event()->list();
Nostr::native()->event()->list();
Installation
Install the package
composer require revolution/laravel-nostr
Publish the configuration file
php artisan vendor:publish --tag=nostr-config
Key management
Generate new keys
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...',
// ]
Convert keys
Convert from nsec:
use Revolution\Nostr\Facades\Nostr;
$response = Nostr::key()->fromNsec(nsec: 'nsec');
$keys = $response->json();
// ['sk' => '...', 'nsec' => '...', 'pk' => '...', 'npub' => '...']
Convert from secret key:
$response = Nostr::key()->fromSecretKey(sk: 'sk');
Convert from npub (public key only):
$response = Nostr::key()->fromNpub(npub: 'npub');
$keys = $response->json();
// ['pk' => '...', 'npub' => '...']
Convert from public key:
$response = Nostr::key()->fromPublicKey(pk: 'pk');
Getting events
Get multiple events
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' => '...'],
// ]
Get one event
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: ""}']
Publishing events
Publish to a single relay
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');
}
Publish to multiple relays (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 is array<string, Response>
// ['wss://relay1' => $response, 'wss://relay2' => $response]
foreach ($responses as $relay => $response) {
if ($response->failed()) {
dump($relay . ' : ' . $response->body());
}
}
Relay server configuration
Which relay servers are used?
When using Nostr::event() alone, the first relay in config/nostr.php is used. When using Nostr::pool(), all relays in the configuration are used.
Change relay server at runtime
use Revolution\Nostr\Facades\Nostr;
$response = Nostr::event()->withRelay('wss://')->...;
use Revolution\Nostr\Facades\Nostr;
$response = Nostr::pool()->withRelays(['wss://', 'wss://'])->...;
NIP-05 profile
use Revolution\Nostr\Facades\Nostr;
$profile = Nostr::nip05()->profile('user@localhost');
// [
// 'user' => 'user@localhost',
// 'pubkey' => 'pk',
// 'relays' => [],
// ]
NIP-17 Private Direct Messages
NIP-17 is only supported in the native driver.
Send a private direct message
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!'
);
Decrypt a received private direct message
use Revolution\Nostr\Facades\Nostr;
$response = Nostr::driver('native')
->nip17()
->decryptDirectMessage(
giftWrap: $receivedGiftWrap,
sk: 'receiver-secret-key'
);
$decryptedMessage = $response->json();
Laravel Notifications
Use NostrChannel to send messages to Nostr through Laravel Notifications.
Notification class
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(
// '#laravel' in content is for human display; the HashTag in tags is for protocol-level filtering
content: 'hello #laravel',
tags: [
HashTag::make(t: 'laravel'),
],
);
}
}
On-demand notifications
use Illuminate\Support\Facades\Notification;
use Revolution\Nostr\Notifications\NostrRoute;
Notification::route('nostr', NostrRoute::to(sk: 'sk'))
->notify(new TestNotification());
User model integration
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());
Relay servers for notifications
By default, all relays in config/nostr.php are used. Specify relays in NostrRoute to change them at runtime.
use Revolution\Nostr\Notifications\NostrRoute;
return NostrRoute::to(sk: 'sk', relays: ['wss://', 'wss://']);