The gateway now ships in the
praisonai-bot package. praisonai serve gateway still works exactly as documented here; for a standalone install see praisonai-bot Migration.Quick Start
Simple sliding window
Five turns per minute per identity.
max_requests=0 (default) disables the gate entirely — safe for dev environments.With lockout cooldown
After hitting the ceiling, lock out the identity for an additional cooldown period.
How It Works
| Component | Role |
|---|---|
identity | The caller — authenticated tenant, user ID, or endpoint principal |
scope | Endpoint class, channel name, or tenant token |
now | Current timestamp (seconds) — injected so decisions are pure and testable |
RateLimitDecision | Closed result: allowed bool + optional retry_after_seconds backoff hint |
Configuration Options
SlidingWindowRateLimitPolicy accepts three knobs:
| Option | Type | Default | Description |
|---|---|---|---|
max_requests | int | 0 | Maximum requests allowed per window. 0 disables the gate (legacy pass-through). |
window_seconds | float | 60.0 | Window duration in seconds. Must be > 0 or ValueError is raised. |
lockout_seconds | float | 0.0 | Penalty lockout after exceeding the ceiling. 0 means no lockout. Must be >= 0 or ValueError is raised. |
Decision Table
Per(scope, identity) key:
| Situation | allowed | retry_after_seconds |
|---|---|---|
max_requests == 0 (disabled) | True | None |
Active lockout (now < lockout_until) | False | lockout_until - now |
| New or expired window | True | None |
| Below ceiling in current window | True | None |
Over ceiling, lockout_seconds > 0 | False | lockout_seconds (window dropped; key locked) |
Over ceiling, lockout_seconds == 0 | False | window_seconds - (now - window_start) (window preserved — not reset) |
Choosing Lockout vs. No Lockout
Building a Custom Limiter
Any object with acheck(*, identity, scope, now) method satisfies RateLimitPolicyProtocol. The signature must be keyword-only.
- Keyword-only args —
check(*, identity, scope, now)— positional calls will raiseTypeError. - Return a closed
RateLimitDecision— neverNone. @runtime_checkable—isinstance(policy, RateLimitPolicyProtocol)works at runtime.
State Ownership
Best Practices
Disable in dev, enable deliberately in production
Disable in dev, enable deliberately in production
max_requests=0 (the default) disables rate limiting entirely. This is the safe default for development. Turn on the gate explicitly when you deploy to production — don’t rely on it being on by default.Prefer window-only limiting unless you want an abuse penalty
Prefer window-only limiting unless you want an abuse penalty
lockout_seconds=0 (the default) gives you a strict per-window quota with no extra penalty. The identity is simply denied until the window rolls over. Set lockout_seconds only if you want a cooldown penalty after a burst — e.g. lockout_seconds=300 for a 5-minute cool-down after hitting the ceiling.Implement the protocol for per-tenant or distributed limits
Implement the protocol for per-tenant or distributed limits
SlidingWindowRateLimitPolicy uses in-process state. For per-tenant pricing, multi-process gateways, or Redis-backed distributed limits, implement RateLimitPolicyProtocol yourself — the seam is intentional. The check method is the only surface you need to implement.Keep identity and scope cardinality bounded
Keep identity and scope cardinality bounded
Each
(scope, identity) pair gets its own state entry. If your identity space is unbounded (e.g. raw IPs), entries accumulate without eviction. Either use the gateway’s built-in limiters (which handle reclamation) or implement your own with periodic cleanup.Related
Admission Control
Bound concurrent inbound agent runs with a fair queue and overflow policy
Flow Control
Bounded outbound inboxes and slow-consumer disconnect
Graceful Drain
Drain in-flight turns before shutdown
Gateway Overview
WebSocket control plane for multi-agent coordination

