package ratelimit import "time" // Config configures the rate limiter behavior. type Config struct { // RequestsPerSecond is the default rate limit in requests per second. // This applies to authenticated users (identified by pubkey). // Default: 10 RequestsPerSecond float64 // BurstSize is the maximum burst size (token bucket capacity). // Allows users to make burst requests up to this limit. // Default: 20 BurstSize int // IPRequestsPerSecond is the rate limit for unauthenticated users. // These are identified by IP address. // Typically set lower than authenticated user limits. // Default: 5 IPRequestsPerSecond float64 // IPBurstSize is the burst size for IP-based rate limiting. // Default: 10 IPBurstSize int // MethodLimits provides per-method rate limit overrides. // Key is the full gRPC method name (e.g., "/nostr.v1.NostrRelay/PublishEvent") // If not specified, uses the default RequestsPerSecond and BurstSize. MethodLimits map[string]MethodLimit // UserLimits provides per-user custom rate limits. // Key is the pubkey. Useful for VIP/premium users or admins. // If not specified, uses the default limits. UserLimits map[string]UserLimit // SkipMethods is a list of gRPC methods that bypass rate limiting. // Useful for health checks or public endpoints. // Example: []string{"/grpc.health.v1.Health/Check"} SkipMethods []string // SkipUsers is a list of pubkeys that bypass rate limiting. // Useful for admins or monitoring services. SkipUsers []string // CleanupInterval is how often to remove idle rate limiters from memory. // Limiters that haven't been used recently are removed to save memory. // Default: 5 minutes CleanupInterval time.Duration // MaxIdleTime is how long a limiter can be idle before being cleaned up. // Default: 10 minutes MaxIdleTime time.Duration } // MethodLimit defines rate limits for a specific gRPC method. type MethodLimit struct { RequestsPerSecond float64 BurstSize int } // UserLimit defines custom rate limits for a specific user (pubkey). type UserLimit struct { // RequestsPerSecond is the default rate for this user. RequestsPerSecond float64 // BurstSize is the burst size for this user. BurstSize int // MethodLimits provides per-method overrides for this user. // Allows fine-grained control like "VIP user gets 100 req/s for queries // but still only 5 req/s for publishes" MethodLimits map[string]MethodLimit } // DefaultConfig returns the default rate limit configuration. func DefaultConfig() *Config { return &Config{ RequestsPerSecond: 10, BurstSize: 20, IPRequestsPerSecond: 5, IPBurstSize: 10, CleanupInterval: 5 * time.Minute, MaxIdleTime: 10 * time.Minute, } } // Validate checks if the configuration is valid. func (c *Config) Validate() error { if c.RequestsPerSecond <= 0 { c.RequestsPerSecond = 10 } if c.BurstSize <= 0 { c.BurstSize = 20 } if c.IPRequestsPerSecond <= 0 { c.IPRequestsPerSecond = 5 } if c.IPBurstSize <= 0 { c.IPBurstSize = 10 } if c.CleanupInterval <= 0 { c.CleanupInterval = 5 * time.Minute } if c.MaxIdleTime <= 0 { c.MaxIdleTime = 10 * time.Minute } return nil } // GetLimitForMethod returns the rate limit for a specific method and user. // Precedence: UserLimit.MethodLimit > MethodLimit > UserLimit > Default func (c *Config) GetLimitForMethod(pubkey, method string) (requestsPerSecond float64, burstSize int) { // Check user-specific method limit first (highest precedence) if userLimit, ok := c.UserLimits[pubkey]; ok { if methodLimit, ok := userLimit.MethodLimits[method]; ok { return methodLimit.RequestsPerSecond, methodLimit.BurstSize } } // Check global method limit if methodLimit, ok := c.MethodLimits[method]; ok { return methodLimit.RequestsPerSecond, methodLimit.BurstSize } // Check user-specific default limit if userLimit, ok := c.UserLimits[pubkey]; ok { return userLimit.RequestsPerSecond, userLimit.BurstSize } // Fall back to global default return c.RequestsPerSecond, c.BurstSize } // ShouldSkipMethod returns true if the method should bypass rate limiting. func (c *Config) ShouldSkipMethod(method string) bool { for _, skip := range c.SkipMethods { if skip == method { return true } } return false } // ShouldSkipUser returns true if the user should bypass rate limiting. func (c *Config) ShouldSkipUser(pubkey string) bool { for _, skip := range c.SkipUsers { if skip == pubkey { return true } } return false }