Skip to main content

Rate Limiting

Titan implements Redis-backed rate limiting to protect the API from abuse.

Architecture​

How It Works​

  1. Request arrives at middleware (HTTP) or hub filter (SignalR)
  2. Match endpoint pattern to find applicable policy
  3. Check token bucket in Redis for remaining requests
  4. Allow or deny based on bucket state
  5. 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:

PatternMatches
/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 StateIdentifier
AuthenticatedUser ID (from JWT)
AnonymousIP 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:

HeaderDescription
Retry-AfterSeconds until limit resets
X-RateLimit-LimitMax requests in window
X-RateLimit-RemainingRequests remaining
X-RateLimit-ResetUnix timestamp of reset