main
ratelimit.go
Eric Bower
·
2026-02-25
1package patchbin
2
3import (
4 "fmt"
5 "sync"
6 "time"
7)
8
9// RateLimiter enforces a single global cap on submissions per interval,
10// shared across all users (not keyed by pubkey or IP).
11type RateLimiter struct {
12 mu sync.Mutex
13 max int
14 interval time.Duration
15 count int
16 resetAt time.Time
17}
18
19func NewRateLimiter(max int, interval time.Duration) *RateLimiter {
20 return &RateLimiter{
21 max: max,
22 interval: interval,
23 }
24}
25
26// Allow reports whether a new submission is permitted under the current
27// window, incrementing the window's counter if so.
28func (r *RateLimiter) Allow() bool {
29 r.mu.Lock()
30 defer r.mu.Unlock()
31
32 now := time.Now()
33 if now.After(r.resetAt) {
34 r.count = 0
35 r.resetAt = now.Add(r.interval)
36 }
37
38 if r.count >= r.max {
39 return false
40 }
41
42 r.count++
43 return true
44}
45
46func (r *RateLimiter) Error() error {
47 return fmt.Errorf(
48 "rate limit exceeded: max %d submissions per %s, try again later",
49 r.max,
50 r.interval,
51 )
52}