Documentation
¶
Overview ¶
Package ratelimit owns the cross-surface counter used by S35's rate-limit middleware. It generalizes S05's auth/throttle pattern against the shared rate_limits table so any new surface (API, search, git transports) plugs in with a single Allow() call.
Backend: Postgres-backed fixed-window counter via sqlc UPSERT. At launch scale this is well within Postgres's comfort zone and avoids introducing a Redis dependency. Move only if profiling demands it (S36).
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ClientIP ¶
ClientIP returns the parsed client IP using the same rules as IPKey. Used by signup-throttle's CIDR keying.
func StampHeaders ¶
func StampHeaders(w http.ResponseWriter, d Decision)
StampHeaders writes the standard X-RateLimit-* headers from a Decision. Exposed so handlers that run a manual Allow can apply the same stamp without going through Middleware.
Types ¶
type Decision ¶
type Decision struct {
Allowed bool
Remaining int // hits left in the current window (post-increment)
Limit int // == Policy.Max, surfaced for the X-RateLimit-Limit header
ResetIn time.Duration // wall-clock until the current window rolls over
RetryAfter time.Duration // 0 when Allowed; otherwise the wait the client should respect
}
Decision is the verdict from Allow.
type KeyFunc ¶
KeyFunc derives the per-request rate-limit key. Common choices: remote IP (anon), user id (authed), token id (PAT), repo id+user (per-repo), etc. Returning "" skips the limiter (caller should only return "" when there's nothing meaningful to throttle on).
type Limiter ¶
type Limiter struct {
// contains filtered or unexported fields
}
Limiter is the package's primary handle. Construct with New.
func New ¶
New wires a Limiter against a pool. The pool is required; constructing with nil panics so callers fail at boot, not at first request.
func (*Limiter) Allow ¶
Allow increments the (scope, key) counter and reports whether the caller is under or over the configured Max. Returns the post- increment Remaining + the time until the current window rolls.
On a Postgres error the request is allowed (fail-open). The caller is expected to log the error; refusing service over a transient counter glitch would be worse than the brief over-limit window.
func (*Limiter) AllowSignupIP ¶
func (l *Limiter) AllowSignupIP(ctx context.Context, ip netip.Addr, max int, window time.Duration) (Decision, error)
AllowSignupIP is the inet-keyed sibling of Allow against the signup_ip_throttle table. ip is masked to /24 (v4) or /48 (v6) so a single residential allocation shares one counter — matches GitHub's approach (per-/24 signup throttle).
func (*Limiter) Middleware ¶
Middleware wraps next with a per-request Allow check. On allow, X-RateLimit-* headers are stamped on the response; on deny, the response is 429 with Retry-After.
`name` is the per-route identifier surfaced in logs and lets the admin observability pages disambiguate scopes that share the same counter (e.g. "api:anon" used by both /api/repos and /api/users).