Files
mal/docs/superpowers/specs/2026-05-06-rate-limiter-refactor-design.md

2.0 KiB

Design Spec: Generic Rate Limiter Refactor

Refactor the current global-state rate limiter into a struct-based implementation that supports multiple instances with different configurations, improving testability and flexibility.

1. Core Architecture

Current State

  • pkg/middleware/ratelimit.go uses global variables (visitors, mu, quit).
  • Cleanup logic is started automatically via init().
  • Limits are hardcoded (5 attempts per minute).

Changes

  • Limiter Struct: Encapsulate state within a Limiter struct.
  • Explicit Configuration: Pass limits and windows via a Config struct.
  • Context-aware Cleanup: Use context.Context to manage the cleanup goroutine lifecycle instead of a global quit channel.

2. Component Design

Limiter Struct

type visitor struct {
	attempts int
	lastSeen time.Time
}

type Config struct {
	MaxAttempts int
	Window      time.Duration
}

type Limiter struct {
	mu       sync.Mutex
	visitors map[string]*visitor
	config   Config
}

Initialization

func NewLimiter(cfg Config) *Limiter {
	return &Limiter{
		visitors: make(map[string]*visitor),
		config:   cfg,
	}
}

Middleware Methods

  • Middleware(next http.Handler) http.Handler: Generic rate limiting.
  • AuthMiddleware(next http.Handler) http.Handler: Specific logic for auth (redirects with error params).

3. Integration Plan

  • Instantiate the Limiter in cmd/server/main.go.
  • Pass the Limiter or its middleware to internal/server/NewRouter.
  • Update internal/server/routes.go to use the new instance-based middleware.

4. Verification Plan

Automated Checks

  • Unit Tests: Create pkg/middleware/ratelimit_test.go to verify:
    • Multiple IPs are tracked independently.
    • Limits are enforced correctly.
    • Attempts reset after the window expires.
  • go test ./pkg/middleware/...: Run the new tests.

Manual Checks

  • Verify that exceeding login attempts still triggers the rate_limited redirect.