RateLimiter
RateLimiter protects your remotes from spam and abuse using a sliding window: it counts how many calls a player made in the last N seconds and rejects the request once that count crosses the limit. It's used as Middleware either globally or on a specific Service.
How it works internally
Player sends a request
│
├── Player banned? > false (warn + seconds remaining)
│
├── Prune timestamps outside the window
├── count >= maxCalls?
│ ├── Penalties enabled > applyPenalty()
│ │ ├── violations >= banThreshold > kick + temporary ban
│ │ ├── violations >= kickThreshold > kick
│ │ └── violations >= warnThreshold > console warn
│ └── Penalties disabled > console warn (with cooldown)
│ └── return false
│
└── Timestamp recorded > return trueAvailable shortcuts
| Function | Description | Penalties |
|---|---|---|
RateLimiter.perPlayer(max, window, label) |
Per-player individual limit | ❌ off by default |
RateLimiter.global(max, window, label) |
Limit shared across every player | ❌ off by default |
RateLimiter.strict(max, window, label) |
Per-player limit + penalties enabled (low thresholds) | ✅ |
Basic usage
-- > // Server: inside Owl.Start(), applied to every remote
Owl.Start({
GlobalMiddleware = {
Inbound = {
Owl.Util.RateLimiter.perPlayer(30, 1, "Global"),
},
},
})
-- > // On a specific Service (stacked on top of the global one)
local ShopService = Owl.CreateService({
Name = "ShopService",
Middleware = {
Inbound = {
Owl.Util.RateLimiter.perPlayer(5, 1, "ShopService"),
},
},
Client = { --[[ ... ]] },
})Progressive penalties (opt-in)
Penalties are disabled by default. Turn them on through the penalty field:
Owl.Util.RateLimiter.perPlayer(10, 1, "CombatService", {
enabled = true,
warnThreshold = 3, -- > // warn after 3 violations
kickThreshold = 8, -- > // kick after 8 violations
banThreshold = 15, -- > // temporary ban after 15 violations
banDuration = 60, -- > // ban duration, in seconds
kickMessage = "You were kicked for spamming.",
banMessage = "You were banned for 60s for spamming.",
})Violations are cumulative over time, not just within a single window. A player spamming in short bursts still eventually reaches the thresholds resetting the window doesn't reset the violation count.
The strict shortcut
For critical remotes (purchases, save data, combat), strict enables penalties with low thresholds out of the box:
local PurchaseService = Owl.CreateService({
Name = "PurchaseService",
Middleware = {
Inbound = {
-- > // Max 3 purchases/s, kick after 5 violations, ban for 60s after 10
Owl.Util.RateLimiter.strict(3, 1, "PurchaseService"),
},
},
Client = { --[[ ... ]] },
})Full configuration
For anything the shortcuts above don't cover, construct a RateLimiter directly:
RateLimiter.new({
maxCalls = 20, -- > // max calls allowed within the window
window = 1, -- > // window duration, in seconds (default: 1)
global = false, -- > // true = limit shared across every player
warnCooldown = 5, -- > // seconds between two console warns (default: 5)
label = "MyRemote", -- > // name shown in warn output
penalty = {
enabled = true,
warnThreshold = 3,
kickThreshold = 10,
banThreshold = 20,
banDuration = 30,
kickMessage = "Kicked for spamming.",
banMessage = "Banned for 30s for spamming.",
},
})Where to go next
- Middleware - how
RateLimiterslots into the global vs per-Service cascade. - TypeChecker - the other built-in middleware, validating argument shapes instead of call frequency.