Skip to main content

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.
DriverDescription
nativePure PHP implementation using nostr-php. This is sufficient for most use cases.
nodeDepends 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'),
NOSTR_DRIVER=native
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

1

Install the package

composer require revolution/laravel-nostr
2

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://']);
For the latest information, see the GitHub repository.
Last modified on April 29, 2026