Skip to main content

Rate Limiting

FieldValue
Document IDASCEND-ADMIN-001
Version1.0.0
Last UpdatedDecember 19, 2025
AuthorAscend Engineering Team
PublisherOW-KAI Technologies Inc.
ClassificationEnterprise Client Documentation
ComplianceSOC 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_URL configured (via Secrets Manager in production)
  • Database tables provisioned via migrations

Production Infrastructure

ASCEND pilot environment uses the following Redis infrastructure:

ComponentValue
ServiceAWS ElastiCache Replication Group
Endpointmaster.ascend-pilot-redis.klo94w.use2.cache.amazonaws.com:6379
Protocolrediss:// (TLS encrypted)
Node Typecache.t3.micro
EngineRedis 7.0
Transit EncryptionEnabled (TLS required)
At-Rest EncryptionEnabled (AES-256)
NetworkPrivate subnets only
Security GroupECS 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
Production Configuration

In production, REDIS_URL is stored in AWS Secrets Manager and injected via ECS task definition, not as a plain environment variable.

Default Limits

ScopeLimitWindow
Per-tenant1000 requests1 minute
Per-agent100 requests1 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

TierMultiplierUse Case
standard1xNormal operations
elevated2xBatch processing
critical5xEmergency 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

HeaderDescription
X-RateLimit-Limit-AgentPer-agent limit for this window
X-RateLimit-Remaining-AgentRemaining requests for this agent
X-RateLimit-Limit-TenantPer-tenant limit for this window
X-RateLimit-Remaining-TenantRemaining requests for this tenant
X-RateLimit-ResetUnix timestamp when window resets
Retry-AfterSeconds until retry allowed (429 only)

Redis Setup Options

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:

RequirementSetting
SOC 2 CC6.1--transit-encryption-enabled
HIPAA 164.312(e)(1)--at-rest-encryption-enabled
PCI-DSS 4.1TLS required mode
NIST 800-53 SC-8In-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
warning

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:

  1. Provision Redis and set REDIS_URL
  2. 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;