innis/nostr-client
最新稳定版本:v0.1.4
Composer 安装命令:
composer require innis/nostr-client
包简介
AMPHP-based async WebSocket client for Nostr protocol
README 文档
README
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
- 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
- PHP 8.3 or higher
innis/nostr-core- Core Nostr protocol entitiesamphp/amp^3.0 - Async runtimeamphp/websocket-client^2.0 - WebSocket clientpsr/log^3.0 - Logging interface
Installation
composer require innis/nostr-client
Quick Start
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
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
$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
$subscriptionId = $client->subscribeMultiple( $relay, [ new Filter(kinds: [EventKind::textNote()], limit: 10), new Filter(kinds: [EventKind::reaction()], limit: 10), ], $handler, );
Connection Management
$client->reconnect($relay); $client->ping($relay); $state = $client->getConnectionStatus($relay);
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
Check relay health without an active connection:
$healthChecker = NostrClientFactory::createHealthChecker(); $result = $healthChecker->checkHealth(RelayUrl::fromString('wss://relay.damus.io'));
See examples/ for complete working examples.
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
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
# 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
MIT License. See LICENSE file for details.
统计信息
- 总下载量: 7
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 5
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-03-24