A middleware-composable Nostr relay library for Go.
- Middleware Architecture - Compose handlers and middleware to build your relay
- NIP-11 Driven - Built-in middleware maps directly to NIP-11
limitationfields - Pure Go - No cgo dependencies, single binary deployment
- Persistent Storage - Pebble-based LSM-tree storage with MVCC support
- Full-text Search - Bleve-based NIP-50 search with CJK support
- Prometheus Metrics - Built-in instrumentation for relay, router, auth, and storage
- Standard http.Handler - Composes naturally with
net/http.ServeMux
- Go 1.25 or later
GOEXPERIMENT=jsonv2environment variable
export GOEXPERIMENT=jsonv2go get github.com/high-moctane/mocrelayGit hooks are optional. To run go fix and gofmt automatically on staged Go
files before each commit, install lefthook
and run:
lefthook installThe lefthook.yml in the repository configures the hooks. CI runs the same
checks, so skipping local hooks will not bypass them.
The simplest possible relay is a single line:
func main() {
log.Fatal(http.ListenAndServe(":7447", mocrelay.NewRelay(mocrelay.NewNopHandler(), nil)))
}This accepts all events and returns EOSE for all subscriptions — a valid relay with zero storage, in one line.
For a more practical starting point, see the examples.
The cmd/examples/ directory provides three graduated examples:
| Example | Description |
|---|---|
| nop | One-line relay. Demonstrates that a Handler is all you need. |
| minimal | In-memory storage, real-time routing, and basic middleware. A functional relay in ~80 lines. |
| kitchen-sink | Pebble + Bleve + all middleware + Prometheus + ServeMux. Everything mocrelay offers. |
┌──────────────────────────────────────────────┐
│ Relay (http.Handler) │
│ WebSocket lifecycle management │
├──────────────────────────────────────────────┤
│ Middleware Pipeline │
│ Auth → ProtectedEvents → Limits → ... │
├──────────────────────────────────────────────┤
│ MergeHandler │
│ ┌────────────────┬──────────────────┐ │
│ │ StorageHandler │ RouterHandler │ │
│ │ (past events) │ (real-time) │ │
│ └───────┬────────┴────────┬─────────┘ │
├─────────────┼─────────────────┼──────────────┤
│ ┌─────────┴────────┐ ┌─────┴───────┐ │
│ │ CompositeStorage │ │ Router │ │
│ │ ┌──────┬──────┐ │ │ (broadcast) │ │
│ │ │Pebble│Bleve │ │ └─────────────┘ │
│ │ └──────┴──────┘ │ │
│ └──────────────────┘ │
└──────────────────────────────────────────────┘
- Relay serves HTTP/WebSocket and manages connection lifecycles
- Handler processes messages for a single connection
- Middleware wraps a Handler to add cross-cutting concerns
- Storage persists and queries events using Go iterators (
iter.Seq) - Router routes events between connected clients in real-time
| Middleware | NIP-11 Field | Description |
|---|---|---|
MaxSubscriptions |
max_subscriptions |
Limit subscriptions per connection |
MaxSubidLength |
max_subid_length |
Limit subscription ID length |
MaxLimit |
max_limit, default_limit |
Clamp filter limit values |
MaxEventTags |
max_event_tags |
Limit tags per event |
MaxContentLength |
max_content_length |
Limit content length |
CreatedAtLimits |
created_at_lower/upper_limit |
Restrict event timestamps |
KindDenylist |
retention |
Block specific event kinds |
RestrictedWrites |
restricted_writes |
Pubkey allow/deny list |
MinPowDifficulty |
min_pow_difficulty |
Require proof-of-work (NIP-13) |
Auth |
auth_required |
Require authentication (NIP-42) |
Expiration |
- | Handle event expiration (NIP-40) |
ProtectedEvents |
- | Protect events from republishing (NIP-70) |
Multiple middleware are composed into a single pipeline via NewSimpleMiddleware:
handler = mocrelay.NewSimpleMiddleware(
mocrelay.NewMaxSubscriptionsMiddlewareBase(20),
mocrelay.NewMaxLimitMiddlewareBase(500, 100),
mocrelay.NewKindDenylistMiddlewareBase([]int64{4, 1059}),
)(handler)| NIP | Description |
|---|---|
| NIP-01 | Basic Protocol |
| NIP-09 | Event Deletion |
| NIP-11 | Relay Information |
| NIP-13 | Proof of Work |
| NIP-40 | Expiration Timestamp |
| NIP-42 | Authentication |
| NIP-45 | Event Counts |
| NIP-50 | Search Capability |
| NIP-70 | Protected Events |