Rate Limiting
| Field | Value |
|---|---|
| Document ID | ASCEND-ADMIN-001 |
| Version | 1.0.0 |
| Last Updated | December 19, 2025 |
| Author | Ascend Engineering Team |
| Publisher | OW-KAI Technologies Inc. |
| Classification | Enterprise Client Documentation |
| Compliance | SOC 2 CC6.1/CC6.2, PCI-DSS 7.1/8.3, HIPAA 164.312, NIST 800-53 AC-2/SI-4 |
Per-agent and per-tenant rate limiting for AI governance.
Overview
ASCEND rate limiting prevents runaway agents and provides DoS protection using Redis-backed sliding window counters. The implementation uses sorted sets for accurate sliding window calculations that prevent burst attacks at window boundaries.
Requirements
- Redis 7.0+ with TLS encryption (ElastiCache Replication Group recommended)
REDIS_URLconfigured (via Secrets Manager in production)- Database tables provisioned via migrations
Production Infrastructure
ASCEND pilot environment uses the following Redis infrastructure:
| Component | Value |
|---|---|
| Service | AWS ElastiCache Replication Group |
| Endpoint | master.ascend-pilot-redis.klo94w.use2.cache.amazonaws.com:6379 |
| Protocol | rediss:// (TLS encrypted) |
| Node Type | cache.t3.micro |
| Engine | Redis 7.0 |
| Transit Encryption | Enabled (TLS required) |
| At-Rest Encryption | Enabled (AES-256) |
| Network | Private subnets only |
| Security Group | ECS tasks only (port 6379) |
Compliance Certifications:
- SOC 2 Type II (CC6.1 - Encryption)
- HIPAA (164.312(e)(1) - Transmission Security)
- PCI-DSS 4.1 (Strong Cryptography)
- NIST 800-53 (SC-8, SC-28)
Configuration
Environment Variables
# Required for rate limiting (use rediss:// for TLS in production)
REDIS_URL=rediss://master.your-redis.xxxxx.cache.amazonaws.com:6379/0
# Protocol explanation:
# redis:// = Unencrypted (development only)
# rediss:// = TLS encrypted (production required)
# Optional: Disable rate limiting globally (default: true)
ASCEND_RATE_LIMIT_ENABLED=true
# Optional: Config cache TTL in seconds (default: 60)
RATE_LIMIT_CONFIG_CACHE_TTL=60
# Rate limiting is also controlled via database config
# See: org_rate_limit_config table
In production, REDIS_URL is stored in AWS Secrets Manager and injected via ECS task definition, not as a plain environment variable.
Default Limits
| Scope | Limit | Window |
|---|---|---|
| Per-tenant | 1000 requests | 1 minute |
| Per-agent | 100 requests | 1 minute |
Architecture
Request
│
▼
┌─────────────────────┐
│ Rate Limit Check │
│ ┌─────────────────┐ │
│ │ Tenant Counter │ │ Redis: tenant:{org_id}:minute
│ └─────────────────┘ │
│ ┌─────────────────┐ │
│ │ Agent Counter │ │ Redis: agent:{agent_id}:minute
│ └─────────────────┘ │
└─────────────────────┘
│
▼
Limit Exceeded? ─── Yes ──→ 429 Too Many Requests
│
No
│
▼
Process Request
Database Schema
org_rate_limit_config
CREATE TABLE org_rate_limit_config (
id SERIAL PRIMARY KEY,
organization_id INTEGER REFERENCES organizations(id) UNIQUE,
enabled BOOLEAN DEFAULT true,
-- Tenant-wide limits
actions_per_minute INTEGER DEFAULT 1000,
actions_per_hour INTEGER DEFAULT 50000,
actions_per_day INTEGER DEFAULT 500000,
-- Per-agent limits
agent_actions_per_minute INTEGER DEFAULT 100,
agent_actions_per_hour INTEGER DEFAULT 5000,
-- Burst handling
burst_multiplier NUMERIC(3,2) DEFAULT 1.5,
burst_window_seconds INTEGER DEFAULT 10,
-- Response behavior
rate_limit_response_code INTEGER DEFAULT 429,
include_retry_after BOOLEAN DEFAULT true,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
agent_rate_limit_overrides
CREATE TABLE agent_rate_limit_overrides (
id SERIAL PRIMARY KEY,
organization_id INTEGER REFERENCES organizations(id),
agent_id VARCHAR(255) NOT NULL,
custom_limit INTEGER,
priority_tier VARCHAR(20) DEFAULT 'standard',
created_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(organization_id, agent_id)
);
Priority Tiers
| Tier | Multiplier | Use Case |
|---|---|---|
standard | 1x | Normal operations |
elevated | 2x | Batch processing |
critical | 5x | Emergency operations |
Customization
Per-Org Configuration
-- Increase limits for a specific organization
UPDATE org_rate_limit_config
SET actions_per_minute = 2000,
agent_actions_per_minute = 200
WHERE organization_id = 1;
Per-Agent Overrides
-- Give a critical agent higher limits
INSERT INTO agent_rate_limit_overrides
(organization_id, agent_id, custom_limit, priority_tier)
VALUES (1, 'critical-automation-agent', 500, 'critical');
Disable Rate Limiting
-- Disable for a specific org (testing)
UPDATE org_rate_limit_config
SET enabled = false
WHERE organization_id = 1;
Fail-Closed Behavior
IMPORTANT: If Redis is unavailable and rate limiting is enabled, ALL requests are denied. This is by design for security.
# Rate limiter behavior when Redis is down
if redis_unavailable and rate_limit_enabled:
return RateLimitResponse(
allowed=False,
error="rate_limit_exceeded",
message="Rate limit check failed",
retry_after=60
)
To avoid this, ensure Redis is properly provisioned before enabling rate limiting.
API Response (429)
{
"detail": {
"error": "rate_limit_exceeded",
"message": "Rate limit exceeded for agent",
"retry_after": 60,
"limit_type": "agent",
"current_usage": {
"agent_minute": 101,
"tenant_minute": 500
}
}
}
Monitoring
Rate Limit Events Table
SELECT
agent_id,
event_type,
COUNT(*) as count,
MAX(created_at) as last_event
FROM rate_limit_events
WHERE created_at > NOW() - INTERVAL '1 hour'
GROUP BY agent_id, event_type
ORDER BY count DESC;
Current Usage (Redis)
redis-cli GET "tenant:1:minute"
redis-cli GET "agent:my-agent-id:minute"
Headers
Rate limit information is included in response headers:
X-RateLimit-Limit-Agent: 100
X-RateLimit-Remaining-Agent: 42
X-RateLimit-Limit-Tenant: 1000
X-RateLimit-Remaining-Tenant: 500
X-RateLimit-Reset: 1702934520
Retry-After: 60 # Only on 429 responses
Header Descriptions
| Header | Description |
|---|---|
X-RateLimit-Limit-Agent | Per-agent limit for this window |
X-RateLimit-Remaining-Agent | Remaining requests for this agent |
X-RateLimit-Limit-Tenant | Per-tenant limit for this window |
X-RateLimit-Remaining-Tenant | Remaining requests for this tenant |
X-RateLimit-Reset | Unix timestamp when window resets |
Retry-After | Seconds until retry allowed (429 only) |
Redis Setup Options
Option 1: AWS ElastiCache with TLS (Production - Recommended)
ASCEND requires TLS encryption for Redis connections in production (SOC 2, HIPAA, PCI-DSS compliance).
# Step 1: Create subnet group (private subnets only)
aws elasticache create-cache-subnet-group \
--cache-subnet-group-name ascend-redis-subnet-group \
--cache-subnet-group-description "ASCEND Redis - Private Subnets" \
--subnet-ids subnet-private-1 subnet-private-2
# Step 2: Create security group (allow only your application)
aws ec2 create-security-group \
--group-name ascend-redis-sg \
--description "ASCEND Redis - Rate Limiting" \
--vpc-id vpc-xxxxxxxx
# Step 3: Create Replication Group with TLS (NOT cache-cluster)
aws elasticache create-replication-group \
--replication-group-id ascend-redis \
--replication-group-description "ASCEND Redis with TLS - SOC2/HIPAA/PCI compliant" \
--cache-node-type cache.t3.micro \
--engine redis \
--engine-version 7.0 \
--num-cache-clusters 1 \
--cache-subnet-group-name ascend-redis-subnet-group \
--security-group-ids sg-xxxxxxxx \
--transit-encryption-enabled \
--at-rest-encryption-enabled \
--tags Key=Project,Value=ASCEND Key=Compliance,Value=SOC2-HIPAA-PCI
# Step 4: Get endpoint (after ~10 minutes)
aws elasticache describe-replication-groups \
--replication-group-id ascend-redis \
--query 'ReplicationGroups[0].NodeGroups[0].PrimaryEndpoint'
Important: Use rediss:// (double 's') for TLS connections:
# TLS-encrypted connection string
REDIS_URL=rediss://master.ascend-redis.xxxxx.use2.cache.amazonaws.com:6379/0
Compliance Evidence:
| Requirement | Setting |
|---|---|
| SOC 2 CC6.1 | --transit-encryption-enabled |
| HIPAA 164.312(e)(1) | --at-rest-encryption-enabled |
| PCI-DSS 4.1 | TLS required mode |
| NIST 800-53 SC-8 | In-transit encryption |
Option 2: Managed Redis Service
- Upstash: Free tier with TLS (10K commands/day)
- Redis Cloud: 30MB free tier with TLS
- Heroku Redis: TLS available on paid plans
Option 3: Docker (Development Only)
# Development only - NO TLS
docker run -d -p 6379:6379 redis:7-alpine
export REDIS_URL=redis://localhost:6379
Docker Redis without TLS is for development only. Production environments must use TLS-encrypted connections (rediss://).
Compliance
- SOC 2: Availability controls (A1.1)
- NIST 800-53: SC-5 (Denial of Service Protection)
- ISO 27001: A.12.1.3 (Capacity Management)
Troubleshooting
Rate Limit Check Failed
{"error": "rate_limit_exceeded", "current_usage": {"agent_minute": null}}
Cause: Redis connection failed Solution: Check REDIS_URL and Redis availability
All Requests Denied
Cause: Rate limiting enabled but Redis unavailable Solution: Either:
- Provision Redis and set REDIS_URL
- Disable rate limiting:
UPDATE org_rate_limit_config SET enabled = false;
Limits Not Applying
Cause: Config not loaded
Solution: Verify org has config: SELECT * FROM org_rate_limit_config WHERE organization_id = X;