Initial commit

This commit is contained in:
Zhongwei Li
2025-11-29 18:52:31 +08:00
commit 7f9211b22c
16 changed files with 1217 additions and 0 deletions

View File

@@ -0,0 +1,15 @@
{
"name": "api-rate-limiter",
"description": "Implement rate limiting with token bucket, sliding window, and Redis",
"version": "1.0.0",
"author": {
"name": "Jeremy Longshore",
"email": "[email protected]"
},
"skills": [
"./skills"
],
"commands": [
"./commands"
]
}

3
README.md Normal file
View File

@@ -0,0 +1,3 @@
# api-rate-limiter
Implement rate limiting with token bucket, sliding window, and Redis

View File

@@ -0,0 +1,655 @@
---
description: Add rate limiting to API endpoints
shortcut: ratelimit
---
# Add Rate Limiting to API Endpoints
Implement production-ready rate limiting with token bucket, sliding window, or fixed window algorithms using Redis for distributed state management.
## When to Use This Command
Use `/add-rate-limiting` when you need to:
- Protect APIs from abuse and DDoS attacks
- Enforce fair usage policies across user tiers
- Prevent resource exhaustion from runaway clients
- Comply with downstream API rate limits
- Implement freemium pricing models with usage tiers
- Control costs for expensive operations (AI inference, video processing)
DON'T use this when:
- Building internal-only APIs with trusted clients (use circuit breakers instead)
- Single-user applications (no shared resource contention)
- Already behind API gateway with built-in rate limiting (avoid double limiting)
## Design Decisions
This command implements **Token Bucket algorithm with Redis** as the primary approach because:
- Allows burst traffic while maintaining average rate (better UX)
- Distributed state enables horizontal scaling
- Redis atomic operations prevent race conditions
- Standard algorithm with well-understood behavior
**Alternative considered: Sliding Window**
- More accurate rate limiting (no reset boundary issues)
- Higher Redis memory usage (stores timestamp per request)
- Slightly higher computational overhead
- Recommended for strict compliance requirements
**Alternative considered: Fixed Window**
- Simplest implementation (single counter)
- Burst at window boundaries (2x limit possible)
- Lower memory footprint
- Recommended only for non-critical rate limiting
**Alternative considered: Leaky Bucket**
- Constant output rate (smooths bursty traffic)
- Complex to explain to users
- Less common in practice
- Recommended for queuing systems, not APIs
## Prerequisites
Before running this command:
1. Redis server installed and accessible (standalone or cluster)
2. Node.js/Python runtime for middleware implementation
3. API framework that supports middleware (Express, FastAPI, etc.)
4. Understanding of your API usage patterns and SLO requirements
5. Monitoring infrastructure to track rate limit metrics
## Implementation Process
### Step 1: Choose Rate Limiting Strategy
Select algorithm based on requirements: Token Bucket for user-facing APIs, Sliding Window for strict compliance, Fixed Window for internal APIs.
### Step 2: Configure Redis Connection
Set up Redis client with connection pooling, retry logic, and failover handling for high availability.
### Step 3: Implement Rate Limiter Middleware
Create middleware that intercepts requests, checks Redis state, and enforces limits with proper HTTP headers.
### Step 4: Define Rate Limit Tiers
Configure different limits for user segments (anonymous, free, premium, enterprise) based on business requirements.
### Step 5: Add Monitoring and Alerting
Instrument rate limiter with metrics for blocked requests, Redis latency, and tier usage patterns.
## Output Format
The command generates:
- `rate-limiter.js` or `rate_limiter.py` - Core rate limiting middleware
- `redis-config.js` - Redis connection configuration with failover
- `rate-limit-tiers.json` - Tiered limit definitions
- `rate-limiter.test.js` - Comprehensive test suite
- `README.md` - Integration guide and configuration options
- `docker-compose.yml` - Redis setup for local development
## Code Examples
### Example 1: Token Bucket Rate Limiter with Express and Redis
```javascript
// rate-limiter.js
const Redis = require('ioredis');
class TokenBucketRateLimiter {
constructor(redisClient, options = {}) {
this.redis = redisClient;
this.defaultOptions = {
points: 100, // Number of tokens
duration: 60, // Time window in seconds
blockDuration: 60, // Block duration after limit exceeded
keyPrefix: 'rl', // Redis key prefix
...options
};
}
/**
* Token bucket algorithm using Redis
* Returns: { allowed: boolean, remaining: number, resetTime: number }
*/
async consume(identifier, points = 1, options = {}) {
const opts = { ...this.defaultOptions, ...options };
const key = `${opts.keyPrefix}:${identifier}`;
const now = Date.now();
// Lua script for atomic token bucket operations
const luaScript = `
local key = KEYS[1]
local capacity = tonumber(ARGV[1])
local refill_rate = tonumber(ARGV[2])
local requested = tonumber(ARGV[3])
local now = tonumber(ARGV[4])
local ttl = tonumber(ARGV[5])
-- Get current state or initialize
local tokens = tonumber(redis.call('HGET', key, 'tokens'))
local last_refill = tonumber(redis.call('HGET', key, 'last_refill'))
if not tokens then
tokens = capacity
last_refill = now
end
-- Calculate tokens to add since last refill
local time_passed = now - last_refill
local tokens_to_add = math.floor(time_passed * refill_rate)
tokens = math.min(capacity, tokens + tokens_to_add)
last_refill = now
-- Check if we can fulfill request
if tokens >= requested then
tokens = tokens - requested
redis.call('HMSET', key, 'tokens', tokens, 'last_refill', last_refill)
redis.call('EXPIRE', key, ttl)
return {1, tokens, last_refill}
else
redis.call('HMSET', key, 'tokens', tokens, 'last_refill', last_refill)
redis.call('EXPIRE', key, ttl)
return {0, tokens, last_refill}
end
`;
const refillRate = opts.points / opts.duration;
const result = await this.redis.eval(
luaScript,
1,
key,
opts.points,
refillRate,
points,
now,
opts.duration
);
const [allowed, remaining, lastRefill] = result;
const resetTime = lastRefill + (opts.duration * 1000);
return {
allowed: allowed === 1,
remaining: Math.floor(remaining),
resetTime: new Date(resetTime).toISOString(),
retryAfter: allowed === 1 ? null : Math.ceil((opts.duration * 1000 - (now - lastRefill)) / 1000)
};
}
/**
* Express middleware factory
*/
middleware(getTier = null) {
return async (req, res, next) => {
try {
// Determine identifier (user ID or IP)
const identifier = req.user?.id || req.ip;
// Get tier configuration
const tier = getTier ? await getTier(req) : 'default';
const tierConfig = this.getTierConfig(tier);
// Consume tokens
const result = await this.consume(identifier, 1, tierConfig);
// Set rate limit headers
res.set({
'X-RateLimit-Limit': tierConfig.points,
'X-RateLimit-Remaining': result.remaining,
'X-RateLimit-Reset': result.resetTime
});
if (!result.allowed) {
res.set('Retry-After', result.retryAfter);
return res.status(429).json({
error: 'Too Many Requests',
message: `Rate limit exceeded. Try again in ${result.retryAfter} seconds.`,
retryAfter: result.retryAfter
});
}
next();
} catch (error) {
console.error('Rate limiter error:', error);
// Fail open to avoid blocking all traffic on Redis failure
next();
}
};
}
getTierConfig(tier) {
const tiers = {
anonymous: { points: 20, duration: 60 },
free: { points: 100, duration: 60 },
premium: { points: 1000, duration: 60 },
enterprise: { points: 10000, duration: 60 }
};
return tiers[tier] || tiers.free;
}
}
// Usage example
const redis = new Redis({
host: process.env.REDIS_HOST || 'localhost',
port: process.env.REDIS_PORT || 6379,
retryStrategy: (times) => Math.min(times * 50, 2000),
maxRetriesPerRequest: 3
});
const rateLimiter = new TokenBucketRateLimiter(redis);
// Apply to all routes
app.use(rateLimiter.middleware(async (req) => {
if (req.user?.subscription === 'enterprise') return 'enterprise';
if (req.user?.subscription === 'premium') return 'premium';
if (req.user) return 'free';
return 'anonymous';
}));
// Apply stricter limit to specific expensive endpoint
app.post('/api/ai/generate',
rateLimiter.middleware(() => ({ points: 10, duration: 3600 })),
handleGenerate
);
module.exports = TokenBucketRateLimiter;
```
### Example 2: Sliding Window Rate Limiter in Python with FastAPI
```python
# rate_limiter.py
import time
import redis.asyncio as aioredis
from fastapi import Request, Response, HTTPException
from typing import Optional, Callable
import asyncio
class SlidingWindowRateLimiter:
def __init__(self, redis_client: aioredis.Redis, window_size: int = 60, max_requests: int = 100):
self.redis = redis_client
self.window_size = window_size
self.max_requests = max_requests
self.key_prefix = "rate_limit"
async def is_allowed(self, identifier: str, tier_config: dict = None) -> dict:
"""
Sliding window algorithm using Redis sorted set
Each request is a member with score = timestamp
"""
config = tier_config or {'max_requests': self.max_requests, 'window_size': self.window_size}
now = time.time()
window_start = now - config['window_size']
key = f"{self.key_prefix}:{identifier}"
# Redis pipeline for atomic operations
pipe = self.redis.pipeline()
# Remove old entries outside the window
pipe.zremrangebyscore(key, 0, window_start)
# Count requests in current window
pipe.zcard(key)
# Add current request
pipe.zadd(key, {str(now): now})
# Set expiration
pipe.expire(key, config['window_size'] + 10)
results = await pipe.execute()
request_count = results[1]
if request_count >= config['max_requests']:
# Get oldest request in window to calculate retry time
oldest = await self.redis.zrange(key, 0, 0, withscores=True)
if oldest:
oldest_time = oldest[0][1]
retry_after = int(config['window_size'] - (now - oldest_time)) + 1
else:
retry_after = config['window_size']
return {
'allowed': False,
'remaining': 0,
'reset_time': int(now + retry_after),
'retry_after': retry_after
}
remaining = config['max_requests'] - request_count - 1
reset_time = int(now + config['window_size'])
return {
'allowed': True,
'remaining': remaining,
'reset_time': reset_time,
'retry_after': None
}
def middleware(self, get_tier: Optional[Callable] = None):
"""FastAPI middleware factory"""
async def rate_limit_middleware(request: Request, call_next):
# Get identifier (user ID or IP)
identifier = getattr(request.state, 'user_id', None) or request.client.host
# Get tier configuration
tier_config = None
if get_tier:
tier_config = await get_tier(request)
# Check rate limit
result = await self.is_allowed(identifier, tier_config)
# Always set rate limit headers
response = None
if result['allowed']:
response = await call_next(request)
else:
response = Response(
content=f'{{"error": "Rate limit exceeded", "retry_after": {result["retry_after"]}}}',
status_code=429,
media_type="application/json"
)
response.headers['X-RateLimit-Limit'] = str(tier_config['max_requests'] if tier_config else self.max_requests)
response.headers['X-RateLimit-Remaining'] = str(result['remaining'])
response.headers['X-RateLimit-Reset'] = str(result['reset_time'])
if not result['allowed']:
response.headers['Retry-After'] = str(result['retry_after'])
return response
return rate_limit_middleware
# Usage in FastAPI
from fastapi import FastAPI
from contextlib import asynccontextmanager
@asynccontextmanager
async def lifespan(app: FastAPI):
# Startup
app.state.redis = await aioredis.from_url("redis://localhost:6379")
app.state.rate_limiter = SlidingWindowRateLimiter(app.state.redis)
yield
# Shutdown
await app.state.redis.close()
app = FastAPI(lifespan=lifespan)
async def get_user_tier(request: Request) -> dict:
"""Determine user tier from request"""
user = getattr(request.state, 'user', None)
if not user:
return {'max_requests': 20, 'window_size': 60} # Anonymous
elif user.get('subscription') == 'enterprise':
return {'max_requests': 10000, 'window_size': 60}
elif user.get('subscription') == 'premium':
return {'max_requests': 1000, 'window_size': 60}
else:
return {'max_requests': 100, 'window_size': 60} # Free tier
# Apply rate limiting middleware
app.middleware("http")(app.state.rate_limiter.middleware(get_user_tier))
```
### Example 3: DDoS Protection with Multi-Layer Rate Limiting
```javascript
// advanced-rate-limiter.js - Multi-layer protection
const Redis = require('ioredis');
class MultiLayerRateLimiter {
constructor(redisClient) {
this.redis = redisClient;
}
/**
* Layered rate limiting strategy:
* 1. IP-based (DDoS protection)
* 2. User-based (fair usage)
* 3. Endpoint-specific (expensive operations)
*/
async checkLayers(req) {
const layers = [
// Layer 1: IP-based rate limiting (DDoS protection)
{
name: 'ip',
identifier: req.ip,
limits: { points: 1000, duration: 60 }, // 1000 req/min per IP
priority: 'high'
},
// Layer 2: User-based rate limiting
{
name: 'user',
identifier: req.user?.id || `anon:${req.ip}`,
limits: this.getUserTierLimits(req.user),
priority: 'medium'
},
// Layer 3: Endpoint-specific limiting
{
name: 'endpoint',
identifier: `${req.user?.id || req.ip}:${req.path}`,
limits: this.getEndpointLimits(req.path),
priority: 'low'
}
];
for (const layer of layers) {
const result = await this.checkLimit(layer);
if (!result.allowed) {
return {
blocked: true,
layer: layer.name,
...result
};
}
}
return { blocked: false };
}
async checkLimit(layer) {
const key = `rl:${layer.name}:${layer.identifier}`;
const now = Date.now();
const count = await this.redis.incr(key);
if (count === 1) {
await this.redis.expire(key, layer.limits.duration);
}
const ttl = await this.redis.ttl(key);
const allowed = count <= layer.limits.points;
return {
allowed,
remaining: Math.max(0, layer.limits.points - count),
resetTime: now + (ttl * 1000),
retryAfter: allowed ? null : ttl
};
}
getUserTierLimits(user) {
if (!user) return { points: 20, duration: 60 };
const tiers = {
free: { points: 100, duration: 60 },
premium: { points: 1000, duration: 60 },
enterprise: { points: 10000, duration: 60 }
};
return tiers[user.subscription] || tiers.free;
}
getEndpointLimits(path) {
const expensiveEndpoints = {
'/api/ai/generate': { points: 10, duration: 3600 }, // 10/hour
'/api/video/render': { points: 5, duration: 3600 }, // 5/hour
'/api/export/large': { points: 20, duration: 3600 } // 20/hour
};
return expensiveEndpoints[path] || { points: 1000, duration: 60 };
}
middleware() {
return async (req, res, next) => {
try {
const result = await this.checkLayers(req);
if (result.blocked) {
res.set({
'X-RateLimit-Layer': result.layer,
'X-RateLimit-Remaining': result.remaining,
'Retry-After': result.retryAfter
});
return res.status(429).json({
error: 'Rate limit exceeded',
layer: result.layer,
retryAfter: result.retryAfter,
message: `Too many requests. Please retry after ${result.retryAfter} seconds.`
});
}
next();
} catch (error) {
console.error('Multi-layer rate limiter error:', error);
next(); // Fail open
}
};
}
}
module.exports = MultiLayerRateLimiter;
```
## Error Handling
| Error | Cause | Solution |
|-------|-------|----------|
| "Redis connection failed" | Redis server unreachable | Check Redis server status, verify connection string, implement connection retry |
| "Rate limiter fail-closed" | Redis timeout, middleware blocking all traffic | Implement fail-open strategy with circuit breaker pattern |
| "Inconsistent rate limits" | Clock skew across servers | Use Redis time (`TIME` command) instead of server time |
| "Memory exhaustion" | Too many keys, no TTL set | Always set TTL on rate limit keys, use key expiration monitoring |
| "False positives from NAT" | Multiple users behind same IP | Use authenticated user IDs when available, consider X-Forwarded-For |
## Configuration Options
**Rate Limit Algorithms**
- **Token Bucket**: Best for user-facing APIs with burst allowance
- **Sliding Window**: Most accurate, higher memory usage
- **Fixed Window**: Simplest, allows boundary bursts
- **Leaky Bucket**: Constant rate, complex UX
**Tier Definitions**
```json
{
"anonymous": { "points": 20, "duration": 60 },
"free": { "points": 100, "duration": 60 },
"premium": { "points": 1000, "duration": 60 },
"enterprise": { "points": 10000, "duration": 60 }
}
```
**Redis Configuration**
- **Connection pooling**: Minimum 5 connections
- **Retry strategy**: Exponential backoff up to 2s
- **Failover**: Redis Sentinel or Cluster for HA
- **Persistence**: AOF for rate limit state recovery
## Best Practices
DO:
- Return standard rate limit headers (X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset)
- Implement graceful degradation (fail open on Redis failure)
- Use user ID over IP when authenticated (avoids NAT issues)
- Set TTL on all Redis keys to prevent memory leaks
- Monitor rate limiter performance (latency, block rate)
- Provide clear error messages with retry guidance
DON'T:
- Block legitimate traffic (tune limits based on real usage)
- Use client-side rate limiting only (easily bypassed)
- Forget to handle Redis connection failures (causes complete outage)
- Implement synchronous Redis calls (adds latency to every request)
- Use rate limiting as only defense against DDoS (need multiple layers)
TIPS:
- Start conservative, increase limits based on monitoring
- Use different limits for different operations (read vs write)
- Implement per-endpoint rate limits for expensive operations
- Cache tier lookups to reduce database queries
- Log rate limit violations for security analysis
- Provide upgrade paths for users hitting limits
## Performance Considerations
**Latency Impact**
- Token bucket: 1-2ms added to request (single Redis call)
- Sliding window: 2-4ms (multiple Redis operations)
- With pipelining: <1ms for all algorithms
**Redis Memory Usage**
- Token bucket: ~100 bytes per user
- Sliding window: ~50 bytes per request in window
- Fixed window: ~50 bytes per user per window
**Throughput**
- Redis can handle 100k+ operations/second
- Use Redis Cluster for horizontal scaling
- Pipeline Redis operations when possible
- Consider local caching for extremely high throughput
## Security Considerations
1. **DDoS Protection**: Implement IP-based rate limiting as first layer
2. **Credential Stuffing**: Add stricter limits on authentication endpoints
3. **API Scraping**: Implement progressive delays for repeated violations
4. **Distributed Attacks**: Use shared Redis across all API servers
5. **Bypass Attempts**: Validate X-Forwarded-For headers, don't trust blindly
6. **State Consistency**: Use Redis transactions to prevent race conditions
## Troubleshooting
**Rate Limits Not Enforced**
```bash
# Check Redis connectivity
redis-cli -h localhost -p 6379 ping
# Verify keys are being created
redis-cli --scan --pattern 'rl:*' | head -10
# Check TTL is set correctly
redis-cli TTL rl:user:123456
```
**Too Many False Positives**
```bash
# Review blocked requests by IP
redis-cli --scan --pattern 'rl:ip:*' | xargs redis-cli MGET
# Check tier assignments
# Review application logs for tier calculation
# Analyze legitimate traffic patterns
# Adjust limits based on p95/p99 usage
```
**Redis Memory Issues**
```bash
# Check memory usage
redis-cli INFO memory
# Count rate limit keys
redis-cli --scan --pattern 'rl:*' | wc -l
# Review keys without TTL
redis-cli --scan --pattern 'rl:*' | xargs redis-cli TTL | grep -c "^-1"
```
## Related Commands
- `/create-monitoring` - Monitor rate limit metrics and violations
- `/api-authentication-builder` - Integrate with auth for user-based limits
- `/api-load-tester` - Test rate limiter under realistic load
- `/setup-logging` - Log rate limit violations for analysis
## Version History
- v1.0.0 (2024-10): Initial implementation with token bucket and sliding window
- Planned v1.1.0: Add adaptive rate limiting based on system load

93
plugin.lock.json Normal file
View File

@@ -0,0 +1,93 @@
{
"$schema": "internal://schemas/plugin.lock.v1.json",
"pluginId": "gh:jeremylongshore/claude-code-plugins-plus:plugins/api-development/api-rate-limiter",
"normalized": {
"repo": null,
"ref": "refs/tags/v20251128.0",
"commit": "9e134796cd3834b2c114ba429138d321313b9463",
"treeHash": "4681c74fc68708fab3d65366e96ee8ff7588529d1cd66814f0ee3339dba71ed4",
"generatedAt": "2025-11-28T10:18:07.744614Z",
"toolVersion": "publish_plugins.py@0.2.0"
},
"origin": {
"remote": "git@github.com:zhongweili/42plugin-data.git",
"branch": "master",
"commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390",
"repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data"
},
"manifest": {
"name": "api-rate-limiter",
"description": "Implement rate limiting with token bucket, sliding window, and Redis",
"version": "1.0.0"
},
"content": {
"files": [
{
"path": "README.md",
"sha256": "ae54372002bde47e0fd42713f359d7f7451fff30362506befbf714360119c1b5"
},
{
"path": ".claude-plugin/plugin.json",
"sha256": "b43868c03b7d1ab25aec8e57da55ddbf19cbd1dcabc512268cbffae9add186e3"
},
{
"path": "commands/add-rate-limiting.md",
"sha256": "5d5eac511d4c1c824706cf9eab197b1c2207112981dd5269dc0f01c257e3a1d3"
},
{
"path": "skills/skill-adapter/references/examples.md",
"sha256": "922bbc3c4ebf38b76f515b5c1998ebde6bf902233e00e2c5a0e9176f975a7572"
},
{
"path": "skills/skill-adapter/references/best-practices.md",
"sha256": "c8f32b3566252f50daacd346d7045a1060c718ef5cfb07c55a0f2dec5f1fb39e"
},
{
"path": "skills/skill-adapter/references/README.md",
"sha256": "4ba21318cbcdc1e9837ad8373070e2fd0ff89dc64eebe7a0768cecc3134035a0"
},
{
"path": "skills/skill-adapter/scripts/helper-template.sh",
"sha256": "0881d5660a8a7045550d09ae0acc15642c24b70de6f08808120f47f86ccdf077"
},
{
"path": "skills/skill-adapter/scripts/validation.sh",
"sha256": "92551a29a7f512d2036e4f1fb46c2a3dc6bff0f7dde4a9f699533e446db48502"
},
{
"path": "skills/skill-adapter/scripts/README.md",
"sha256": "778710b170fef62572e60d76c04c0c7f11ff40a9a4b9e405bf98caa6814d0cc1"
},
{
"path": "skills/skill-adapter/assets/test-data.json",
"sha256": "ac17dca3d6e253a5f39f2a2f1b388e5146043756b05d9ce7ac53a0042eee139d"
},
{
"path": "skills/skill-adapter/assets/README.md",
"sha256": "629f15d82e15a8d4fd1c36d891be86fec282dce472da17b02675c726ed7f379f"
},
{
"path": "skills/skill-adapter/assets/rate_limit_template.conf",
"sha256": "700c91a873be9af28b4c39c495e6343f9b6bd642aeac445b42e3e66b3784eff8"
},
{
"path": "skills/skill-adapter/assets/skill-schema.json",
"sha256": "f5639ba823a24c9ac4fb21444c0717b7aefde1a4993682897f5bf544f863c2cd"
},
{
"path": "skills/skill-adapter/assets/error_message_template.json",
"sha256": "2b9146ce11359adafbc33ecc071cfd813ec48aa7b5b8e0e4c6ab5016d1cdb3aa"
},
{
"path": "skills/skill-adapter/assets/config-template.json",
"sha256": "0c2ba33d2d3c5ccb266c0848fc43caa68a2aa6a80ff315d4b378352711f83e1c"
}
],
"dirSha256": "4681c74fc68708fab3d65366e96ee8ff7588529d1cd66814f0ee3339dba71ed4"
},
"security": {
"scannedAt": null,
"scannerVersion": null,
"flags": []
}
}

View File

@@ -0,0 +1,6 @@
# Assets
Bundled resources for api-rate-limiter skill
- [ ] rate_limit_template.conf: Template configuration file for the rate limiting implementation.
- [ ] error_message_template.json: Template for the JSON error message returned when rate limits are exceeded.

View File

@@ -0,0 +1,32 @@
{
"skill": {
"name": "skill-name",
"version": "1.0.0",
"enabled": true,
"settings": {
"verbose": false,
"autoActivate": true,
"toolRestrictions": true
}
},
"triggers": {
"keywords": [
"example-trigger-1",
"example-trigger-2"
],
"patterns": []
},
"tools": {
"allowed": [
"Read",
"Grep",
"Bash"
],
"restricted": []
},
"metadata": {
"author": "Plugin Author",
"category": "general",
"tags": []
}
}

View File

@@ -0,0 +1,33 @@
{
"_comment": "Template for error message when rate limits are exceeded",
"error": {
"code": 429,
"_comment": "HTTP status code for Too Many Requests",
"message": "Too Many Requests",
"_comment": "General error message",
"details": {
"reason": "Rate limit exceeded",
"_comment": "Specific reason for the error",
"limit": 100,
"_comment": "The allowed request limit",
"remaining": 0,
"_comment": "Requests remaining in the current window",
"reset_at": "2024-01-24T12:00:00Z",
"_comment": "ISO 8601 timestamp for when the limit resets",
"policy": "100 requests per minute",
"_comment": "Human-readable policy description",
"algorithm": "token_bucket",
"_comment": "Rate limiting algorithm used (token_bucket, sliding_window, fixed_window)",
"scope": "user",
"_comment": "Scope of the rate limit (user, ip, global)",
"key": "user_id:123",
"_comment": "Key used for rate limiting (e.g., user ID, IP address)",
"retry_after": 60,
"_comment": "Seconds to wait before retrying the request (for Retry-After header)",
"documentation_url": "https://example.com/api/rate-limiting",
"_comment": "URL to documentation about rate limiting",
"support_email": "support@example.com"
"_comment": "Support email for questions about rate limits"
}
}
}

View File

@@ -0,0 +1,97 @@
# rate_limit_template.conf
# This is a template configuration file for the API Rate Limiter plugin.
# Fill in the placeholders with your desired values.
# Save this file as rate_limit.conf (or similar) and specify its path when configuring the plugin.
# --- General Settings ---
# Enable/Disable rate limiting
enabled: true # Set to 'false' to disable rate limiting
# Rate limiting algorithm: token_bucket, sliding_window, fixed_window
algorithm: token_bucket
# Redis connection details
redis_host: localhost
redis_port: 6379
redis_password: "" # Leave blank if no password
redis_db: 0
# --- Global Rate Limits (applied to all requests if no user/IP specific limits are defined) ---
global_limit_enabled: false # Enable/disable global rate limiting
# Number of requests allowed within the specified time window
global_requests_per_window: 100
# Time window in seconds
global_window_seconds: 60
# --- Token Bucket Settings (if algorithm is set to 'token_bucket') ---
# Number of tokens in the bucket
token_bucket_capacity: 100
# Rate at which tokens are added to the bucket (tokens per second)
token_replenishment_rate: 10
# --- Sliding Window Settings (if algorithm is set to 'sliding_window') ---
# Number of requests allowed within the window
sliding_window_limit: 100
# Window size in seconds
sliding_window_seconds: 60
# --- Fixed Window Settings (if algorithm is set to 'fixed_window') ---
# Number of requests allowed within the window
fixed_window_limit: 100
# Window size in seconds
fixed_window_seconds: 60
# --- Per User/IP Rate Limits ---
# Enable per-user rate limiting (requires user identification mechanism)
user_limit_enabled: false
# Number of requests allowed per user within the specified time window
user_requests_per_window: 50
# Time window in seconds for user rate limiting
user_window_seconds: 60
# Enable per-IP rate limiting
ip_limit_enabled: true
# Number of requests allowed per IP within the specified time window
ip_requests_per_window: 20
# Time window in seconds for IP rate limiting
ip_window_seconds: 60
# --- Burst Handling ---
# Allow a burst of requests beyond the rate limit (e.g., for initial page load)
burst_limit_enabled: true
# Maximum number of requests allowed in a burst
burst_limit: 20
# --- Rate Limit Headers ---
# Enable sending rate limit headers in the response
enable_rate_limit_headers: true
# Header name for the rate limit
rate_limit_header: X-RateLimit-Limit
# Header name for the remaining requests
rate_limit_remaining_header: X-RateLimit-Remaining
# Header name for the time until the limit resets (in seconds)
rate_limit_reset_header: X-RateLimit-Reset
# --- Advanced Settings ---
# Key prefix for storing rate limit data in Redis
redis_key_prefix: rate_limit:
# Custom error message when rate limit is exceeded
rate_limit_exceeded_message: "Rate limit exceeded. Please try again later."
# HTTP status code to return when rate limit is exceeded
rate_limit_exceeded_status_code: 429 # Too Many Requests

View File

@@ -0,0 +1,28 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Claude Skill Configuration",
"type": "object",
"required": ["name", "description"],
"properties": {
"name": {
"type": "string",
"pattern": "^[a-z0-9-]+$",
"maxLength": 64,
"description": "Skill identifier (lowercase, hyphens only)"
},
"description": {
"type": "string",
"maxLength": 1024,
"description": "What the skill does and when to use it"
},
"allowed-tools": {
"type": "string",
"description": "Comma-separated list of allowed tools"
},
"version": {
"type": "string",
"pattern": "^\\d+\\.\\d+\\.\\d+$",
"description": "Semantic version (x.y.z)"
}
}
}

View File

@@ -0,0 +1,27 @@
{
"testCases": [
{
"name": "Basic activation test",
"input": "trigger phrase example",
"expected": {
"activated": true,
"toolsUsed": ["Read", "Grep"],
"success": true
}
},
{
"name": "Complex workflow test",
"input": "multi-step trigger example",
"expected": {
"activated": true,
"steps": 3,
"toolsUsed": ["Read", "Write", "Bash"],
"success": true
}
}
],
"fixtures": {
"sampleInput": "example data",
"expectedOutput": "processed result"
}
}

View File

@@ -0,0 +1,8 @@
# References
Bundled resources for api-rate-limiter skill
- [ ] rate_limiting_algorithms.md: Explains different rate limiting algorithms (token bucket, sliding window, fixed window) in detail.
- [ ] redis_setup.md: Provides instructions on setting up Redis for distributed rate limiting.
- [ ] api_gateway_integration.md: Describes how to integrate the rate limiting logic with different API gateways (e.g., Kong, Tyk, AWS API Gateway).
- [ ] example_rate_limit_config.json: Example configuration file for rate limiting.

View File

@@ -0,0 +1,69 @@
# Skill Best Practices
Guidelines for optimal skill usage and development.
## For Users
### Activation Best Practices
1. **Use Clear Trigger Phrases**
- Match phrases from skill description
- Be specific about intent
- Provide necessary context
2. **Provide Sufficient Context**
- Include relevant file paths
- Specify scope of analysis
- Mention any constraints
3. **Understand Tool Permissions**
- Check allowed-tools in frontmatter
- Know what the skill can/cannot do
- Request appropriate actions
### Workflow Optimization
- Start with simple requests
- Build up to complex workflows
- Verify each step before proceeding
- Use skill consistently for related tasks
## For Developers
### Skill Development Guidelines
1. **Clear Descriptions**
- Include explicit trigger phrases
- Document all capabilities
- Specify limitations
2. **Proper Tool Permissions**
- Use minimal necessary tools
- Document security implications
- Test with restricted tools
3. **Comprehensive Documentation**
- Provide usage examples
- Document common pitfalls
- Include troubleshooting guide
### Maintenance
- Keep version updated
- Test after tool updates
- Monitor user feedback
- Iterate on descriptions
## Performance Tips
- Scope skills to specific domains
- Avoid overlapping trigger phrases
- Keep descriptions under 1024 chars
- Test activation reliability
## Security Considerations
- Never include secrets in skill files
- Validate all inputs
- Use read-only tools when possible
- Document security requirements

View File

@@ -0,0 +1,70 @@
# Skill Usage Examples
This document provides practical examples of how to use this skill effectively.
## Basic Usage
### Example 1: Simple Activation
**User Request:**
```
[Describe trigger phrase here]
```
**Skill Response:**
1. Analyzes the request
2. Performs the required action
3. Returns results
### Example 2: Complex Workflow
**User Request:**
```
[Describe complex scenario]
```
**Workflow:**
1. Step 1: Initial analysis
2. Step 2: Data processing
3. Step 3: Result generation
4. Step 4: Validation
## Advanced Patterns
### Pattern 1: Chaining Operations
Combine this skill with other tools:
```
Step 1: Use this skill for [purpose]
Step 2: Chain with [other tool]
Step 3: Finalize with [action]
```
### Pattern 2: Error Handling
If issues occur:
- Check trigger phrase matches
- Verify context is available
- Review allowed-tools permissions
## Tips & Best Practices
- ✅ Be specific with trigger phrases
- ✅ Provide necessary context
- ✅ Check tool permissions match needs
- ❌ Avoid vague requests
- ❌ Don't mix unrelated tasks
## Common Issues
**Issue:** Skill doesn't activate
**Solution:** Use exact trigger phrases from description
**Issue:** Unexpected results
**Solution:** Check input format and context
## See Also
- Main SKILL.md for full documentation
- scripts/ for automation helpers
- assets/ for configuration examples

View File

@@ -0,0 +1,7 @@
# Scripts
Bundled resources for api-rate-limiter skill
- [ ] generate_rate_limit_config.py: Generates rate limiting configuration files based on user input.
- [ ] test_rate_limiting.sh: Tests the rate limiting implementation using curl or similar tools.
- [ ] deploy_rate_limiting.sh: Deploys the rate limiting configuration to the API gateway or server.

View File

@@ -0,0 +1,42 @@
#!/bin/bash
# Helper script template for skill automation
# Customize this for your skill's specific needs
set -e
function show_usage() {
echo "Usage: $0 [options]"
echo ""
echo "Options:"
echo " -h, --help Show this help message"
echo " -v, --verbose Enable verbose output"
echo ""
}
# Parse arguments
VERBOSE=false
while [[ $# -gt 0 ]]; do
case $1 in
-h|--help)
show_usage
exit 0
;;
-v|--verbose)
VERBOSE=true
shift
;;
*)
echo "Unknown option: $1"
show_usage
exit 1
;;
esac
done
# Your skill logic here
if [ "$VERBOSE" = true ]; then
echo "Running skill automation..."
fi
echo "✅ Complete"

View File

@@ -0,0 +1,32 @@
#!/bin/bash
# Skill validation helper
# Validates skill activation and functionality
set -e
echo "🔍 Validating skill..."
# Check if SKILL.md exists
if [ ! -f "../SKILL.md" ]; then
echo "❌ Error: SKILL.md not found"
exit 1
fi
# Validate frontmatter
if ! grep -q "^---$" "../SKILL.md"; then
echo "❌ Error: No frontmatter found"
exit 1
fi
# Check required fields
if ! grep -q "^name:" "../SKILL.md"; then
echo "❌ Error: Missing 'name' field"
exit 1
fi
if ! grep -q "^description:" "../SKILL.md"; then
echo "❌ Error: Missing 'description' field"
exit 1
fi
echo "✅ Skill validation passed"