PHPackages                             innis/nostr-client - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [HTTP &amp; Networking](/categories/http)
4. /
5. innis/nostr-client

ActiveLibrary[HTTP &amp; Networking](/categories/http)

innis/nostr-client
==================

AMPHP-based async WebSocket client for Nostr protocol

v0.1.7(1mo ago)09↓90%MITPHPPHP ^8.3

Since Mar 24Pushed 1mo agoCompare

[ Source](https://github.com/johninnis/nostr-client)[ Packagist](https://packagist.org/packages/innis/nostr-client)[ RSS](/packages/innis-nostr-client/feed)WikiDiscussions master Synced 3w ago

READMEChangelogDependencies (16)Versions (9)Used By (0)

innis/nostr-client
==================

[](#innisnostr-client)

**AMPHP-based async WebSocket client for Nostr protocol**

A PHP client library for connecting to Nostr relays over WebSocket, subscribing to events, and publishing. Built with AMPHP for non-blocking concurrent relay connections and clean architecture principles.

---

Features
--------

[](#features)

- **Multi-relay connections** - Connect to multiple relays concurrently
- **AMPHP async** - Non-blocking WebSocket I/O with fibers
- **Subscription management** - Subscribe with single or multiple filters, receive events via handler callbacks
- **Event publishing** - Publish signed events with OK response handling
- **NIP-42 authentication** - Automatic auth challenge handling with transparent publish retry
- **Connection lifecycle** - Automatic state tracking, health checks, reconnection, ping
- **Keep-alive handling** - WebSocket heartbeats and application-level ping responses
- **PSR-3 logging** - Standard logging interface throughout
- **Clean Architecture** - Strict layer separation with domain objects from `innis/nostr-core`

---

Requirements
------------

[](#requirements)

- PHP 8.3 or higher
- `innis/nostr-core` - Core Nostr protocol entities
- `amphp/amp` ^3.0 - Async runtime
- `amphp/websocket-client` ^2.0 - WebSocket client
- `psr/log` ^3.0 - Logging interface

---

Installation
------------

[](#installation)

```
composer require innis/nostr-client
```

---

Quick Start
-----------

[](#quick-start)

### Connect and Subscribe

[](#connect-and-subscribe)

```
use Innis\Nostr\Client\Infrastructure\Factory\NostrClientFactory;
use Innis\Nostr\Core\Application\Port\EventHandlerInterface;
use Innis\Nostr\Core\Domain\Entity\Event;
use Innis\Nostr\Core\Domain\Entity\Filter;
use Innis\Nostr\Core\Domain\ValueObject\Content\EventKind;
use Innis\Nostr\Core\Domain\ValueObject\Protocol\RelayUrl;
use Innis\Nostr\Core\Domain\ValueObject\Protocol\SubscriptionId;

$client = NostrClientFactory::create();

$client->connect(RelayUrl::fromString('wss://relay.damus.io'));
$client->connect(RelayUrl::fromString('wss://nos.lol'));

$handler = new class implements EventHandlerInterface {
    public function handleEvent(Event $event, SubscriptionId $subscriptionId): void
    {
        echo substr((string) $event->getContent(), 0, 100)."\n";
    }

    public function handleEose(SubscriptionId $subscriptionId): void {}
    public function handleClosed(SubscriptionId $subscriptionId, string $message): void {}
    public function handleNotice(RelayUrl $relayUrl, string $message): void {}
};

$filter = new Filter(kinds: [EventKind::textNote()], limit: 10);
$relay = RelayUrl::fromString('wss://relay.damus.io');

$subscriptionId = $client->subscribe($relay, $filter, $handler);

\Amp\delay(5);

$client->unsubscribe($relay, $subscriptionId);
$client->close();
```

### Publish Events

[](#publish-events)

```
use Innis\Nostr\Core\Domain\Factory\EventFactory;
use Innis\Nostr\Core\Domain\ValueObject\Identity\KeyPair;

$keyPair = KeyPair::generate();
$event = EventFactory::createTextNote($keyPair->getPublicKey(), 'Hello Nostr!');
$signedEvent = $event->sign($keyPair->getPrivateKey());

$client->publishEvent($relay, $signedEvent);
```

### Health Checking

[](#health-checking)

```
$results = $client->healthCheck();

foreach ($results as $relayUrl => $result) {
    if ($result->isHealthy()) {
        echo "{$relayUrl}: {$result->getLatencyMs()}ms\n";
    } else {
        echo "{$relayUrl}: {$result->getErrorMessage()}\n";
    }
}
```

### Multiple Filters Per Subscription

[](#multiple-filters-per-subscription)

```
$subscriptionId = $client->subscribeMultiple(
    $relay,
    [
        new Filter(kinds: [EventKind::textNote()], limit: 10),
        new Filter(kinds: [EventKind::reaction()], limit: 10),
    ],
    $handler,
);
```

### Connection Management

[](#connection-management)

```
$client->reconnect($relay);
$client->ping($relay);
$state = $client->getConnectionStatus($relay);
```

### NIP-42 Authentication

[](#nip-42-authentication)

Register an auth handler to sign relay challenges. When `publishEvent()` is rejected with `auth-required`, the client completes the challenge-response flow and retransmits the queued event transparently.

```
use Innis\Nostr\Client\Domain\Service\AuthChallengeHandlerInterface;
use Innis\Nostr\Core\Domain\Factory\EventFactory;

$authHandler = new class($keyPair) implements AuthChallengeHandlerInterface {
    public function __construct(private KeyPair $keyPair) {}

    public function handleAuthChallenge(RelayUrl $relayUrl, string $challenge): ?Event
    {
        $event = EventFactory::createAuth($this->keyPair->getPublicKey(), $relayUrl, $challenge);

        return $event->sign($this->keyPair->getPrivateKey());
    }
};

$client->setAuthHandler($authHandler);
```

### Standalone Health Checker

[](#standalone-health-checker)

Check relay health without an active connection:

```
$healthChecker = NostrClientFactory::createHealthChecker();
$result = $healthChecker->checkHealth(RelayUrl::fromString('wss://relay.damus.io'));
```

See [`examples/`](examples/) for complete working examples.

---

Error Handling
--------------

[](#error-handling)

The client throws on failure. Retry logic belongs in your application layer where you have full business context.

```
try {
    $client->publishEvent($relay, $event);
} catch (\Throwable $e) {
    $this->logger->error('Publish failed', [
        'relay' => (string) $relay,
        'error' => $e->getMessage(),
    ]);
}
```

---

Architecture
------------

[](#architecture)

This package follows Clean Architecture principles:

```
src/
  Application/
    Port/NostrClientInterface        Public API contract
    Port/ConnectionHandlerInterface  Infrastructure port
  Domain/
    Entity/RelayConnection               Connection state and subscriptions
    Entity/RelayConnectionCollection     Typed connection collection
    Enum/ConnectionState                 State machine (connected/disconnected/failed)
    ValueObject/ConnectionConfig         Connection configuration
    ValueObject/HealthCheckResult        Health check outcome
    ValueObject/HealthCheckResultCollection  Typed health result collection
    Service/AuthChallengeHandlerInterface    NIP-42 auth callback (application provides)
    Service/RelayHealthCheckerInterface      Standalone health check contract
    Exception/ClientException            Base exception (extends NostrException)
    Exception/ConnectionException        Connection-specific errors
  Infrastructure/
    Connection/AmphpRelayConnection  WebSocket connection handler (AMPHP)
    Connection/ConnectionFactory     WebSocket connection creation
    Connection/ActiveWebSocket       Active WebSocket holder
    Service/ConnectionManager        Implements NostrClientInterface
    Service/WebSocketHealthChecker   Standalone relay health checker
    Factory/NostrClientFactory       Dependency wiring

```

---

Testing
-------

[](#testing)

```
# Run tests and static analysis
composer test

# Run unit tests only
composer test-unit

# Run PHPStan analysis (level 9)
composer analyse

# Fix code style
composer fix-style
```

---

Licence
-------

[](#licence)

MIT License. See LICENSE file for details.

###  Health Score

39

—

LowBetter than 85% of packages

Maintenance93

Actively maintained with recent releases

Popularity5

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity44

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~8 days

Total

8

Last Release

36d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/d2ca81e761ec3b2d4ce98ac59cd4394bb4d70ebf3d1e620ff154ffcfc7a34bfc?d=identicon)[innis](/maintainers/innis)

---

Top Contributors

[![johninnis](https://avatars.githubusercontent.com/u/242370111?v=4)](https://github.com/johninnis "johninnis (11 commits)")

---

Tags

asyncclientwebsocketprotocolamphpnostr

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/innis-nostr-client/health.svg)

```
[![Health](https://phpackages.com/badges/innis-nostr-client/health.svg)](https://phpackages.com/packages/innis-nostr-client)
```

###  Alternatives

[danog/madelineproto

Async PHP client API for the telegram MTProto protocol.

3.4k885.1k22](/packages/danog-madelineproto)[amphp/http-server

A non-blocking HTTP application server for PHP based on Amp.

1.3k5.9M104](/packages/amphp-http-server)[amphp/websocket-client

Async WebSocket client for PHP based on Amp.

1624.3M54](/packages/amphp-websocket-client)[amphp/http-client

An advanced async HTTP client library for PHP, enabling efficient, non-blocking, and concurrent requests and responses.

7298.5M185](/packages/amphp-http-client)[swow/swow

Coroutine-based multi-platform support engine with a focus on concurrent I/O

1.3k2.2M87](/packages/swow-swow)[ratchet/pawl

Asynchronous WebSocket client

6169.6M233](/packages/ratchet-pawl)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
