summaryrefslogtreecommitdiffstats
path: root/cmd
Commit message (Collapse)AuthorAgeFilesLines
* refactor: migrate nostr dependency to code.northwest.iobndw20 hours1-1/+1
| | | | Updated all import references from northwest.io/nostr to code.northwest.io/nostr and removed the local replace directive from go.mod. The module is now resolved from the published repository.
* feat: add rate limiting to WebSocket connectionsbndw2 days1-1/+6
| | | | | | | | | | | | | | | WebSocket clients were completely unprotected from abuse. Add RateLimiter interface to WebSocket handler and enforce limits on EVENT and REQ messages. - Add RateLimiter interface with Allow(identifier, method) method - Track client IP in connState (proxy-aware via X-Forwarded-For) - Check rate limits in handleEvent and handleReq - Use authenticated pubkey as identifier, fallback to IP - Share same rate limiter instance with gRPC - Add getClientIP() helper that checks proxy headers first Critical security fix for production deployment. Without this, any client could spam unlimited events/subscriptions via WebSocket.
* refactor: use AuthStore interface and remove type assertionsbndw3 days1-0/+1
| | | | | | | | | | | | | 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
* feat: implement NIP-42 WebSocket authenticationbndw3 days1-0/+9
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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!
* feat: add storage stats and average latency metricsbndw3 days1-0/+18
| | | | | | | | | | | | | | | | | | | | | | 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!
* feat: track WebSocket subscriptions in metricsbndw3 days1-0/+3
| | | | | | | | | | | | | | | | | 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).
* fix: properly chain gRPC interceptors and fix dashboard uptimebndw3 days1-19/+22
| | | | | | | | | | | | | | | | | | | 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
* refactor: serve metrics on main HTTP port instead of separate portbndw3 days1-6/+6
| | | | | | | | | | | | | | | | | | | | | | | 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)
* feat: integrate config system into relay main.gobndw3 days1-28/+75
| | | | | | | | | | | | | | | | 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.
* refactor: remove frivolous comments from auth validation/credentialsbndw3 days1-1/+1
| | | | Also removed internal/nostr package - now using northwest.io/nostr library.
* refactor: rename project from nostr-grpc to muxstrbndw3 days2-9/+9
| | | | | | Update module path from northwest.io/nostr-grpc to northwest.io/muxstr. This includes updating all Go imports, protobuf definitions, generated files, and documentation.
* feat: auto-detect TLS in testclientbndw3 days1-1/+16
| | | | | | | | | | | Add automatic TLS detection for testclient: - Use TLS for port 443 - Use TLS for non-localhost addresses - Use insecure for localhost/127.0.0.1 (development) Now works with both: ./bin/testclient # local ./bin/testclient -addr nostr-grpc.x.bdw.to:443 # production
* fix: use port 443 for gRPC when behind reverse proxybndw3 days1-2/+2
| | | | | | 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.
* fix: remove duplicate protocol prefix in WebSocket URLbndw3 days1-2/+2
| | | | | | | | | | | 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
* feat: add --public-url flag for reverse proxy deploymentsbndw3 days1-4/+19
| | | | | | | | | | | | | | | | 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.
* feat: add HTML index page for browser viewingbndw3 days1-0/+1
| | | | | | | | | | 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
* feat: add Connect (gRPC over HTTP/JSON) supportbndw4 days1-2/+16
| | | | | | | | | | | | | | | | | | | | 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.
* feat: add WebSocket server with full NIP-01 supportbndw4 days1-6/+30
| | | | | | | | | | | | | | | | | | | 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!
* feat: testclient can accept JSON events from stdinbndw4 days1-16/+41
| | | | | | | | Usage: - Standalone: ./bin/testclient (generates event) - With nak: nak event "hello" | ./bin/testclient Compatible with nak CLI for easy event generation
* feat: add relay server and test clientbndw4 days2-0/+141
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)