From 688548d4ac3293449a88913275f886fd2e103cdf Mon Sep 17 00:00:00 2001 From: bndw Date: Sat, 14 Feb 2026 09:41:18 -0800 Subject: feat: add Prometheus metrics and YAML config file support ## Metrics Package Comprehensive Prometheus metrics for production observability: Metrics tracked: - Request rate, latency, size per method (histograms) - Active connections and subscriptions (gauges) - Auth success/failure rates (counters) - Rate limit hits (counters) - Storage stats (event count, DB size) - Standard Go runtime metrics Features: - Automatic gRPC instrumentation via interceptors - Low overhead (~300-500ns per request) - Standard Prometheus client - HTTP /metrics endpoint - Grafana dashboard examples ## Config Package YAML configuration file support with environment overrides: Configuration sections: - Server (addresses, timeouts, public URL) - Database (path, connections, lifetime) - Auth (enabled, required, timestamp window, allowed pubkeys) - Rate limiting (per-method and per-user limits) - Metrics (endpoint, namespace) - Logging (level, format, output) - Storage (compaction, retention) Features: - YAML file loading - Environment variable overrides (MUXSTR_
_) - Sensible defaults - Validation on load - Duration and list parsing - Save/export configuration Both packages include comprehensive README with examples, best practices, and usage patterns. Config tests verify YAML parsing, env overrides, validation, and round-trip serialization. --- internal/config/README.md | 440 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 440 insertions(+) create mode 100644 internal/config/README.md (limited to 'internal/config/README.md') diff --git a/internal/config/README.md b/internal/config/README.md new file mode 100644 index 0000000..87d6fa1 --- /dev/null +++ b/internal/config/README.md @@ -0,0 +1,440 @@ +# Configuration + +This package provides configuration management for the relay with support for YAML files and environment variable overrides. + +## Overview + +Configuration can be loaded from: +1. **YAML file** - Primary configuration source +2. **Environment variables** - Override file values +3. **Defaults** - Sensible defaults if not specified + +## Usage + +### Load from File + +```go +import "northwest.io/muxstr/internal/config" + +// Load configuration +cfg, err := config.Load("config.yaml") +if err != nil { + log.Fatal(err) +} + +// Use configuration +fmt.Printf("gRPC listening on %s\n", cfg.Server.GrpcAddr) +``` + +### Load with Environment Overrides + +```bash +# Set environment variables +export MUXSTR_SERVER_GRPC_ADDR=":50051" +export MUXSTR_AUTH_REQUIRED=true +export MUXSTR_RATE_LIMIT_DEFAULT_RPS=100 + +# Run relay +./relay -config config.yaml +``` + +Environment variables use the format: `MUXSTR_
_` + +### Use Defaults + +```go +// Get default configuration +cfg := config.Default() +``` + +## Configuration File Format + +### Complete Example + +```yaml +# Server configuration +server: + # gRPC server address + grpc_addr: ":50051" + + # HTTP server address (for Connect and WebSocket) + http_addr: ":8080" + + # Public URL for reverse proxy deployments (optional) + # Example: "relay.example.com" + public_url: "" + + # Read timeout for requests (optional) + read_timeout: "30s" + + # Write timeout for responses (optional) + write_timeout: "30s" + +# Database configuration +database: + # Path to SQLite database file + path: "relay.db" + + # Maximum number of open connections + max_connections: 10 + + # Connection max lifetime + max_lifetime: "1h" + +# Authentication configuration +auth: + # Enable authentication + enabled: false + + # Require authentication for all requests + # If false, authentication is optional (pubkey available if provided) + required: false + + # Timestamp window in seconds for replay protection + timestamp_window: 60 + + # Allowed pubkeys (optional, whitelist) + # If empty, all valid signatures are accepted + allowed_pubkeys: [] + + # Skip authentication for these methods + skip_methods: + - "/grpc.health.v1.Health/Check" + +# Rate limiting configuration +rate_limit: + # Enable rate limiting + enabled: false + + # Default rate limit (requests per second) + default_rps: 10 + + # Default burst size (token bucket capacity) + default_burst: 20 + + # Rate limit for unauthenticated users (per IP) + ip_rps: 5 + ip_burst: 10 + + # Method-specific limits + methods: + "/nostr.v1.NostrRelay/PublishEvent": + rps: 2 + burst: 5 + "/nostr.v1.NostrRelay/Subscribe": + rps: 1 + burst: 3 + + # User-specific limits (VIP/premium users) + users: + "vip-pubkey-here": + rps: 100 + burst: 200 + + # Skip rate limiting for these methods + skip_methods: + - "/grpc.health.v1.Health/Check" + + # Skip rate limiting for these pubkeys (admins) + skip_users: [] + + # Cleanup interval for idle limiters + cleanup_interval: "5m" + + # Max idle time before limiter is removed + max_idle_time: "10m" + +# Metrics configuration +metrics: + # Enable Prometheus metrics + enabled: true + + # Metrics HTTP server address + addr: ":9090" + + # Metrics path + path: "/metrics" + + # Namespace for metrics + namespace: "muxstr" + + # Subsystem for metrics + subsystem: "relay" + +# Logging configuration +logging: + # Log level: debug, info, warn, error + level: "info" + + # Log format: json, text + format: "json" + + # Output: stdout, stderr, or file path + output: "stdout" + +# Storage configuration +storage: + # Enable automatic compaction + auto_compact: true + + # Compact interval + compact_interval: "24h" + + # Maximum event age (0 = unlimited) + max_event_age: "0" +``` + +### Minimal Example + +```yaml +server: + grpc_addr: ":50051" + http_addr: ":8080" + +database: + path: "relay.db" + +metrics: + enabled: true + addr: ":9090" +``` + +## Environment Variables + +All configuration values can be overridden with environment variables using the pattern: + +``` +MUXSTR_
__=value +``` + +Examples: + +| Config Path | Environment Variable | +|-------------|---------------------| +| `server.grpc_addr` | `MUXSTR_SERVER_GRPC_ADDR` | +| `database.path` | `MUXSTR_DATABASE_PATH` | +| `auth.required` | `MUXSTR_AUTH_REQUIRED` | +| `rate_limit.default_rps` | `MUXSTR_RATE_LIMIT_DEFAULT_RPS` | +| `metrics.enabled` | `MUXSTR_METRICS_ENABLED` | + +Complex types: + +```bash +# Lists (comma-separated) +export MUXSTR_AUTH_ALLOWED_PUBKEYS="pubkey1,pubkey2,pubkey3" + +# Durations +export MUXSTR_SERVER_READ_TIMEOUT="30s" +export MUXSTR_DATABASE_MAX_LIFETIME="1h" + +# Booleans +export MUXSTR_AUTH_ENABLED=true +export MUXSTR_METRICS_ENABLED=false +``` + +## Validation + +Configuration is validated on load: + +```go +cfg, err := config.Load("config.yaml") +if err != nil { + // Validation errors include detailed messages + log.Fatalf("Invalid configuration: %v", err) +} +``` + +Validation checks: +- Required fields are present +- Addresses are valid (host:port format) +- File paths are accessible +- Numeric values are in valid ranges +- Durations are parseable + +## Default Values + +If not specified, the following defaults are used: + +```go +Server: + GrpcAddr: ":50051" + HttpAddr: ":8080" + ReadTimeout: 30s + WriteTimeout: 30s + +Database: + Path: "relay.db" + MaxConnections: 10 + MaxLifetime: 1h + +Auth: + Enabled: false + Required: false + TimestampWindow: 60 + +RateLimit: + Enabled: false + DefaultRPS: 10 + DefaultBurst: 20 + IPRPS: 5 + IPBurst: 10 + CleanupInterval: 5m + MaxIdleTime: 10m + +Metrics: + Enabled: true + Addr: ":9090" + Path: "/metrics" + Namespace: "muxstr" + Subsystem: "relay" + +Logging: + Level: "info" + Format: "json" + Output: "stdout" +``` + +## Configuration Precedence + +Values are loaded in this order (later overrides earlier): + +1. **Defaults** - Built-in default values +2. **Config file** - Values from YAML file +3. **Environment variables** - OS environment overrides + +Example: +```yaml +# config.yaml +server: + grpc_addr: ":50051" +``` + +```bash +# Environment override +export MUXSTR_SERVER_GRPC_ADDR=":9000" + +# Result: gRPC listens on :9000 (env var wins) +``` + +## Reloading Configuration + +Configuration can be reloaded without restart (future feature): + +```go +// Watch for changes +watcher, err := config.Watch("config.yaml") +if err != nil { + log.Fatal(err) +} + +for cfg := range watcher.Updates { + // Apply new configuration + updateServer(cfg) +} +``` + +## Best Practices + +1. **Use config files for static settings**: Server addresses, paths, etc. +2. **Use env vars for deployment-specific settings**: Secrets, environment-specific URLs +3. **Keep secrets out of config files**: Use env vars or secret management +4. **Version control your config**: Check in config.yaml (without secrets) +5. **Document custom settings**: Add comments to config.yaml +6. **Validate in CI**: Run `relay -config config.yaml -validate` in CI pipeline +7. **Use different configs per environment**: `config.dev.yaml`, `config.prod.yaml` + +## Example Configurations + +### Development + +```yaml +server: + grpc_addr: ":50051" + http_addr: ":8080" + +database: + path: "relay-dev.db" + +auth: + enabled: false + +rate_limit: + enabled: false + +metrics: + enabled: true + addr: ":9090" + +logging: + level: "debug" + format: "text" +``` + +### Production + +```yaml +server: + grpc_addr: ":50051" + http_addr: ":8080" + public_url: "relay.example.com" + read_timeout: "30s" + write_timeout: "30s" + +database: + path: "/var/lib/muxstr/relay.db" + max_connections: 50 + +auth: + enabled: true + required: false + timestamp_window: 60 + +rate_limit: + enabled: true + default_rps: 10 + default_burst: 20 + methods: + "/nostr.v1.NostrRelay/PublishEvent": + rps: 2 + burst: 5 + +metrics: + enabled: true + addr: ":9090" + +logging: + level: "info" + format: "json" + output: "/var/log/muxstr/relay.log" +``` + +### High-Performance + +```yaml +server: + grpc_addr: ":50051" + http_addr: ":8080" + +database: + path: "/mnt/fast-ssd/relay.db" + max_connections: 100 + max_lifetime: "30m" + +auth: + enabled: true + required: true + timestamp_window: 30 + +rate_limit: + enabled: true + default_rps: 100 + default_burst: 200 + +metrics: + enabled: true + addr: ":9090" + +logging: + level: "warn" + format: "json" +``` -- cgit v1.2.3