Relay role and architecture
What a relay does
A Nostr relay is a server that stores signed events and serves them to clients over WebSocket connections. Clients publish events to relays and subscribe to event feeds from relays. Each client chooses which relays to connect to and can connect to multiple relays simultaneously.
The relay does not hold users' private keys. Clients sign events locally and send the signed event to the relay. The relay verifies the signature before storing or serving the event.
System architecture
Nostrfi Relay is a single-process Go application. One HTTP server exposes both the NIP-11 information document and a WebSocket endpoint.
Nostr client
| HTTP GET with Accept: application/nostr+json
| WebSocket: EVENT, REQ, CLOSE, AUTH, NEG-*
v
RelayHandler
|-- connection authentication and subscriptions (in memory)
|-- live event fan-out (in process)
|-- NIP-11 response
+-- Negentropy sessions (in memory)
|
v
RelayService
|
v
DuckDBRepository
db/relay.db
Components
Entry point
cmd/relay/main.go loads configuration, opens the DuckDB database, wires the
repository, service, and handler, and listens on port 8080. SIGINT or
SIGTERM starts a five-second graceful HTTP shutdown before the database is
closed.
Handler
The handler owns WebSocket upgrades, connected-client tracking, per-connection subscriptions, authentication identity, Negentropy sessions, Nostr message parsing, signature verification, and synchronous live fan-out to matching subscriptions.
Service
The service layer defines the boundary between transport logic and storage.
Persistence
A DuckDB repository stores events in two tables: events (identity, author,
timestamp, kind, content, signature, d tag, expiration) and tags (indexed
tag name and first value keyed by event ID).
Scaling
The design is single-instance. Connected clients, subscriptions, authentication, live broadcasts, and Negentropy sessions are local to one process. Horizontal scaling requires architectural work.
Previous
Nostrfi RelayNext
Current limitations