| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
|
|
|
|
|
|
|
|
|
| |
Replace runtime type assertions with compile-time safe AuthStore interface.
Add connState struct for cleaner per-connection state management instead
of mutable pointer parameters. Reduce auth challenge TTL from 10min to 2min.
- Add AuthStore interface with CreateAuthChallenge and ValidateAndConsumeChallenge
- Add connState struct for authenticatedPubkey and authChallenge
- Remove fragile type assertion pattern in requireAuth and handleAuth
- Add nil checks for auth store with clear error messages
- Update Handler to have separate auth field
- Wire auth store in main.go when auth is enabled
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Add support for authenticating WebSocket clients using NIP-42 protocol,
enabling auth restrictions for normal Nostr clients.
Storage layer (internal/storage/auth.go):
- CreateAuthChallenge() - Generate random 32-byte challenge with 10min TTL
- ValidateAndConsumeChallenge() - Verify challenge validity and mark as used
- CleanupExpiredChallenges() - Remove old challenges from database
- Uses existing auth_challenges table
WebSocket handler (internal/handler/websocket/handler.go):
- Track authenticatedPubkey per connection
- Track authChallenge per connection
- requireAuth() - Check if operation requires authentication
- handleAuth() - Process AUTH responses (kind 22242 events)
- sendAuthChallenge() - Send AUTH challenge to client
- Enforce auth on EVENT (writes) and REQ (reads) messages
- Support separate read/write allowlists
Main (cmd/relay/main.go):
- Wire auth config from YAML to WebSocket handler
- Pass read/write enabled flags and allowed npub lists
NIP-42 Flow:
1. Client sends EVENT/REQ without auth
2. If auth required, relay sends: ["AUTH", "<challenge>"]
3. Client signs kind 22242 event with challenge tag
4. Client sends: ["AUTH", <signed-event>]
5. Relay validates signature, challenge, and allowlist
6. Connection marked as authenticated
7. Client can now EVENT/REQ
Example config to restrict writes to your npub:
```yaml
auth:
write:
enabled: true
allowed_npubs:
- npub1your-npub-here...
```
WebSocket clients (Damus, Amethyst, etc.) can now authenticate!
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Track and display storage and performance metrics that were previously
showing zeros.
Storage metrics:
- Add GetStats() method to storage returning event count and DB size
- Store database file path for size calculation
- Update metrics every 30 seconds via goroutine in main.go
- Display event count and DB size (MB) in dashboard
Performance metrics:
- Calculate average latency from histogram sum/count metrics
- Display as milliseconds in dashboard
- Formula: (duration_sum / duration_count) * 1000
Missing metrics (deferred):
- Connections: requires connection lifecycle tracking (gRPC + WebSocket)
- Deletions: count would need API change to ProcessDeletion
Dashboard now shows accurate storage stats and request latency!
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Add metrics tracking for WebSocket (NIP-01) subscriptions in addition
to existing gRPC subscription tracking.
Changes:
- Add Count() method to subscription.Manager
- Add MetricsRecorder interface to WebSocket handler
- Update subscription metrics when REQ/CLOSE messages processed
- Wire up metrics to WebSocket handler in main.go
Before: Only gRPC stream subscriptions were counted
After: Both gRPC and WebSocket subscriptions tracked accurately
This fixes the dashboard showing 0 subscriptions when clients connect
via WebSocket (e.g., nak req --stream).
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Two critical fixes for metrics:
1. Fix interceptor chaining
- Changed from mixed grpc.UnaryInterceptor/ChainUnaryInterceptor to
proper chaining with grpc.ChainUnaryInterceptor
- Metrics interceptor now runs first (as intended)
- All interceptors properly chained in order: metrics → auth → ratelimit
- This fixes metrics not being recorded for any requests
2. Fix dashboard uptime calculation
- Changed from page load time to process_start_time_seconds metric
- Uptime now persists correctly across page refreshes
- Uses Prometheus standard process_start_time_seconds gauge
Before: Metrics showed 0 for all requests, uptime reset on refresh
After: Metrics properly record all gRPC requests, uptime shows actual relay uptime
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Move metrics dashboard and Prometheus endpoint to the main HTTP server
for simplified deployment and single ingress configuration.
Changes:
- Add PrometheusHandler() and DashboardHandler() methods to Metrics
- Serve /dashboard on main HTTP port (was root on separate port)
- Serve /metrics on main HTTP port (was /metrics on separate port)
- Remove separate metrics server goroutine
- Update logging to show metrics paths on main HTTP port
Benefits:
- Single port/ingress needed for all HTTP traffic
- Simpler reverse proxy configuration
- Dashboard accessible alongside main relay endpoints
Endpoints on port 8080:
- / - WebSocket/index
- /nostr.v1.NostrRelay/* - Connect (gRPC-Web)
- /dashboard - Metrics dashboard (HTML)
- /metrics - Prometheus metrics (text)
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Add support for loading configuration from YAML file via -config flag.
Wire up auth, rate limiting, and metrics interceptors based on config.
Changes:
- Add -config flag to relay command
- Use config types directly in auth package (AuthOperationConfig)
- Add conversion methods: RateLimitConfig.ToRateLimiter(), MetricsConfig.ToMetrics()
- Add Metrics.Serve() method for prometheus HTTP endpoint
- Update main.go to initialize interceptors from config
- Fix type naming: OperationAuthConfig -> AuthOperationConfig for consistency
Config now supports complete relay setup including auth read/write
allowlists, rate limiting, and prometheus metrics.
|
| |
|
|
|
|
| |
Update module path from northwest.io/nostr-grpc to northwest.io/muxstr.
This includes updating all Go imports, protobuf definitions, generated
files, and documentation.
|
| |
|
|
|
|
| |
When using --public-url, the gRPC endpoint is accessed via the
reverse proxy on standard HTTPS port 443, not the internal port
50051. Update display to show correct public-facing port.
|
| |
|
|
|
|
|
|
|
|
|
| |
The template was hardcoding 'ws://' prefix, but when using
--public-url we were already passing 'wss://'. This caused
the URL to display as 'ws://wss://domain/'.
Fix by:
- Removing 'ws://' prefix from template
- Always including protocol in the variable (ws:// or wss://)
- Also add http:// prefix for local development consistency
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Add flag to specify public-facing domain when relay is behind
a reverse proxy that terminates TLS.
Usage:
./bin/relay --public-url nostr-grpc.x.bdw.to
When set, the index page displays:
- gRPC: nostr-grpc.x.bdw.to:50051
- Connect: https://nostr-grpc.x.bdw.to
- WebSocket: wss://nostr-grpc.x.bdw.to
When not set, falls back to local addresses (:50051, :8080)
for development environments.
|
| |
|
|
|
|
|
|
|
|
| |
Add a beautiful HTML landing page when visiting relay in browser:
- Shows all three protocol endpoints (gRPC, Connect, WebSocket)
- Lists supported NIPs (01, 09, 11)
- Displays relay features and info
- Responsive design with gradient styling
- Serves on GET requests (regular Accept header)
- NIP-11 still served for Accept: application/nostr+json
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Connect integration:
- Buf Connect codegen added to buf.gen.yaml
- Connect handler wraps gRPC server
- Serves on same port as WebSocket (:8080)
- HTTP/2 with h2c for cleartext HTTP/2
Now serving THREE protocols:
1. gRPC (native) on :50051 - binary, high performance
2. Connect on :8080/nostr.v1.NostrRelay/* - HTTP/JSON, browser compatible
3. WebSocket on :8080/ - Nostr standard protocol
All three protocols share:
- Same storage layer
- Same subscription manager
- Same validation logic
Browser-friendly! Call gRPC methods with fetch() or curl.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
WebSocket handler:
- NIP-01 protocol (EVENT, REQ, CLOSE, OK, EOSE, NOTICE)
- JSON envelope parsing
- Shares subscription manager with gRPC (unified event fan-out)
- Standard Nostr client compatibility
Relay now serves dual protocols:
- gRPC on :50051 (binary, high performance)
- WebSocket on :8080 (JSON, Nostr standard)
Both protocols share:
- Same storage layer
- Same subscription manager
- Same validation logic
Compatible with all Nostr clients!
|
|
|
Server (cmd/relay):
- gRPC server on :50051 (configurable)
- SQLite database (default: relay.db)
- Graceful shutdown on SIGTERM/SIGINT
Test client (cmd/testclient):
- Generates key
- Publishes event
- Queries events back
Build:
- make build (relay)
- make build-client (test client)
- make build-all (both)
|