Rate Limiting
Titan implements Redis-backed rate limiting to protect the API from abuse.
Architecture
How It Works
- Request arrives at middleware (HTTP) or hub filter (SignalR)
- Match endpoint pattern to find applicable policy
- Check token bucket in Redis for remaining requests
- Allow or deny based on bucket state
- Apply timeout if limit exceeded
Configuration
Configure in appsettings.json:
{
"RateLimiting": {
"Enabled": true,
"DefaultPolicy": "Global",
"MetricsCollectionEnabled": false,
"Policies": {
"Global": {
"Rules": [
{ "RequestsPerWindow": 1000, "WindowSeconds": 60 }
]
},
"Auth": {
"Rules": [
{ "RequestsPerWindow": 10, "WindowSeconds": 60 },
{ "RequestsPerWindow": 100, "WindowSeconds": 3600 }
],
"TimeoutSeconds": 300
},
"Admin": {
"Rules": [
{ "RequestsPerWindow": 1000, "WindowSeconds": 60 }
]
}
},
"EndpointMappings": {
"/api/auth/*": "Auth",
"/api/admin/*": "Admin",
"/hubs/admin*": "AdminHub"
}
}
}
Policies
Policy Structure
interface RateLimitPolicy {
name: string;
rules: RateLimitRule[];
timeoutSeconds?: number;
}
interface RateLimitRule {
requestsPerWindow: number;
windowSeconds: number;
}
Multiple Rules
Policies can have multiple rules. All must pass:
{
"Auth": {
"Rules": [
{ "RequestsPerWindow": 10, "WindowSeconds": 60 }, // 10/min
{ "RequestsPerWindow": 100, "WindowSeconds": 3600 } // 100/hour
],
"TimeoutSeconds": 300
}
}
This means: max 10 requests per minute AND max 100 per hour.
Endpoint Mapping
Endpoints are matched to policies using glob patterns:
| Pattern | Matches |
|---|---|
/api/auth/* | /api/auth/login, /api/auth/refresh |
/api/admin/* | /api/admin/users, /api/admin/rate-limit/* |
/hubs/* | All SignalR hub endpoints |
First match wins. Unmatched endpoints use DefaultPolicy.
Client Identification
Rate limits are tracked per-client:
| Authentication State | Identifier |
|---|---|
| Authenticated | User ID (from JWT) |
| Anonymous | IP Address |
Timeout Behavior
When a limit is exceeded with TimeoutSeconds configured:
Admin API
Manage rate limiting via the admin dashboard or API:
Get Configuration
GET /api/admin/rate-limit/config
Authorization: Bearer {admin_token}
Enable/Disable
PUT /api/admin/rate-limit/enabled
Content-Type: application/json
{ "enabled": true }
Upsert Policy
PUT /api/admin/rate-limit/policies
Content-Type: application/json
{
"name": "NewPolicy",
"rules": [
{ "requestsPerWindow": 50, "windowSeconds": 60 }
],
"timeoutSeconds": 120
}
Add Endpoint Mapping
POST /api/admin/rate-limit/mappings
Content-Type: application/json
{
"pattern": "/api/custom/*",
"policyName": "NewPolicy"
}
View Metrics
GET /api/admin/rate-limit/metrics
Returns active buckets, timeouts, and request counts.
Metrics
When MetricsCollectionEnabled is true, the system collects:
- Active buckets: Current token counts per client/endpoint
- Active timeouts: Clients currently in timeout
- Request counts: Requests per policy over time
Access via the admin dashboard for real-time graphs.
Response Headers
Rate-limited responses include headers:
| Header | Description |
|---|---|
Retry-After | Seconds until limit resets |
X-RateLimit-Limit | Max requests in window |
X-RateLimit-Remaining | Requests remaining |
X-RateLimit-Reset | Unix timestamp of reset |