Adaptive multi-layer rate limiting & auto-remediation — in a single, cloud-portable package.
AutoBlock fills the gap between returning a 429 and pushing to your WAF + notifying your team + writing an audit trail. It works as a drop-in middleware for Express, FastAPI, Spring Boot, and Go's net/http, backed by Redis.
Current version: 0.1.0 (2026-01-15)
| Without AutoBlock | With AutoBlock |
|---|---|
| Return 429 and hope | WARN → SLOW → BLOCK → BLACKLIST (FSM) |
| Manual WAF rule updates | Auto-push to AWS WAF / Cloudflare / Nginx |
| No visibility into who is abusing your API | Audit stream + Slack/PagerDuty alerts |
| Burst and sustained abuse treated the same | Hybrid sliding-window + token-bucket algorithm |
| Per-service silos | Multi-tenant, shared Redis, one engine |
Each request is evaluated against configurable rules across multiple dimensions (IP, user ID, endpoint). A penalty score accumulates on violations and drives a state machine:
CLEAN ──→ WARN ──→ SLOW ──→ BLOCK ──→ BLACKLIST
│ │ │ │
└─────────┴────────┴────────┘ (score decays over time)
When an IP reaches BLACKLIST, the AutoBlock engine automatically:
- Pushes the IP to your WAF (AWS WAF, Cloudflare, or Nginx)
- Fires a Slack / PagerDuty alert
- Writes an audit event to a Redis Stream
| Package | Language | Install |
|---|---|---|
@autoblock/core |
TypeScript | npm install @autoblock/core |
@autoblock/express |
TypeScript | npm install @autoblock/express |
autoblock |
Python 3.11+ | pip install autoblock |
autoblock-spring-boot-starter |
Java 21 | Maven: io.autoblock |
| Go SDK | Go 1.22+ | go get github.com/autoblock/autoblock/packages/go |
import express from 'express'
import { createAutoBlockMiddleware } from '@autoblock/express'
import Redis from 'ioredis'
const app = express()
const redis = new Redis({ host: 'localhost', port: 6379 })
app.use(createAutoBlockMiddleware(redis, {
tenant: 'my-app',
rules: [
{
id: 'login-limit',
enabled: true,
dimensions: ['ip'],
endpointPattern: '^/api/auth/login$',
methods: ['POST'],
algorithm: 'hybrid',
limits: { requests: 10, windowSeconds: 60, burst: 15 },
penalties: {
warn: { scoreThreshold: 3 },
slow: { scoreThreshold: 6, delayMs: 2000 },
block: { scoreThreshold: 10, durationSeconds: 300 },
blacklist: { scoreThreshold: 15 },
},
},
],
}))
app.listen(3000)from fastapi import FastAPI
from autoblock.middleware import AutoBlockMiddleware
from autoblock.config import AutoBlockConfig, RuleConfig
app = FastAPI()
app.add_middleware(
AutoBlockMiddleware,
tenant="my-app",
fail_open=True,
rules=[
RuleConfig(
id="login-limit",
path="/api/auth/login",
methods=["POST"],
limit=10,
window_seconds=60,
algorithm="hybrid",
)
],
)# application.yml
autoblock:
tenant: my-app
fail-open: true
trust-proxy: true
rules:
- path: /api/auth/login
limit: 10
window-seconds: 60
algorithm: HYBRID
per-user: true
- path: /api/**
limit: 200
window-seconds: 60
algorithm: SLIDING_WINDOWimport "github.com/autoblock/autoblock/packages/go/autoblock"
mw := autoblock.NewMiddleware(redisClient, autoblock.Config{
Tenant: "my-app",
Rules: []autoblock.Rule{
{
ID: "login-limit",
EndpointPattern: "^/api/auth/login$",
Methods: []string{"POST"},
Algorithm: autoblock.Hybrid,
Limits: autoblock.Limits{Requests: 10, WindowSeconds: 60, Burst: 15},
},
},
})
http.Handle("/", mw(yourHandler))AutoBlock's hybrid algorithm runs both checks on every request — either violation increments the penalty score:
| Algorithm | Catches | Trade-off |
|---|---|---|
sliding_window |
Sustained rate abuse | Can allow short bursts |
token_bucket |
Burst abuse | Doesn't catch slow, sustained abuse |
hybrid |
Both | Recommended for auth/critical endpoints |
All counter operations use Lua scripts for atomicity — no WATCH/MULTI needed.
Thresholds are configurable. Defaults:
| State | Score | Effect |
|---|---|---|
CLEAN |
0–2 | Allow |
WARN |
3–5 | Allow, headers set |
SLOW |
6–9 | Allow with artificial delay (default 2 s) |
BLOCK |
10–14 | 429 Too Many Requests + Retry-After |
BLACKLIST |
≥ 15 | 403 Forbidden + WAF push + alert |
Scores decay over time via the DecayWorker (configurable half-life).
The engine runs as a sidecar or standalone service. On BLACKLIST events it:
- WAF push — AWS WAF IP Sets, Cloudflare IP Lists, or Nginx geo-module ban files
- Notifications — Slack webhook, PagerDuty incident (auto-resolves on unblock)
- Audit stream — every action appended to a Redis Stream
Configure via autoblock.yaml (see autoblock.example.yaml):
tenant: my-app
redis:
url: redis://localhost:6379
remediation:
enabled: true
decay:
enabled: true
half_life_minutes: 30
waf:
providers:
- name: aws_waf
enabled: true
type: aws_waf
config:
region: us-east-1
ip_set_id: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
scope: REGIONAL
notifications:
slack:
enabled: true
webhook_url: "" # or env AUTOBLOCK_NOTIFICATIONS_SLACK_WEBHOOK_URL
channel: "#security-alerts"The engine exposes a REST API on :8080. All endpoints require Bearer auth except /healthz.
GET /healthz
GET /api/v1/status/ip/{ip} Penalty state + score
GET /api/v1/blacklist/ip List blacklisted IPs
POST /api/v1/blacklist/ip Add IP/CIDR {"ip":"1.2.3.4","ttl":3600}
DELETE /api/v1/blacklist/ip/{ip} Remove from blacklist
GET /api/v1/whitelist/ip List whitelisted IPs
POST /api/v1/whitelist/ip Add IP/CIDR to whitelist
DELETE /api/v1/whitelist/ip/{ip} Remove from whitelist
GET /api/v1/rules List dynamic rules
POST /api/v1/rules Create rule
PUT /api/v1/rules/{id} Replace rule
DELETE /api/v1/rules/{id} Delete rule
Full spec: api/openapi.yaml
Every request receives standard headers:
| Header | Description |
|---|---|
RateLimit-Limit |
Configured limit (IETF draft) |
RateLimit-Remaining |
Remaining requests |
RateLimit-Reset |
Seconds until window resets |
X-RateLimit-* |
Legacy equivalents |
Retry-After |
Seconds to wait (on 429 responses) |
# Start full stack: Redis, engine, API, Prometheus, Grafana
docker compose -f docker/docker-compose.yml up -d
# Services:
# Redis → localhost:6379
# Management API → localhost:8080
# Prometheus → localhost:9091
# Grafana → localhost:3000 (admin / admin)TypeScript:
pnpm install
pnpm --filter @autoblock/core test
pnpm --filter @autoblock/express testPython:
cd packages/fastapi
pip install -e ".[dev]"
pytestJava:
cd packages/spring
mvn test --enable-previewGo:
go test ./... -raceautoblock/
├── packages/core/ TypeScript — algorithms, FSM, Redis client
├── packages/express/ TypeScript — Express.js middleware
├── packages/fastapi/ Python — Starlette/FastAPI middleware
├── packages/spring/ Java — Spring Boot starter
├── packages/go/ Go — net/http + chi middleware
├── engine/ Go — auto-remediation service
├── api/ OpenAPI spec + Go handlers
├── deploy/helm/autoblock/ Helm 3 chart
├── docker/ docker-compose for local dev
├── dashboards/ Grafana + Prometheus alert rules
├── tests/e2e/k6/ k6 load test scripts
└── autoblock.example.yaml Full configuration reference
Prometheus metrics exposed on :9090/metrics:
autoblock_waf_actions_total{provider,action,success}autoblock_waf_action_latency_seconds{provider,action}(p50/p95/p99)autoblock_blacklist_total{tenant}autoblock_events_processed_total{tenant,state}autoblock_api_requests_total{method,path,status}
Pre-built Grafana dashboards are in dashboards/.
OpenTelemetry tracing is supported in all SDKs (autoblock.evaluate span). It is a no-op when the OTel SDK is not configured.
See autoblock.example.yaml for the full annotated configuration file. Sensitive values can be supplied via environment variables with the AUTOBLOCK_ prefix (dots become underscores):
AUTOBLOCK_WAF_PROVIDERS_0_CONFIG_API_TOKEN=secret
AUTOBLOCK_NOTIFICATIONS_SLACK_WEBHOOK_URL=https://hooks.slack.com/...
AUTOBLOCK_API_AUTH_KEYS_0_KEY=my-api-key| Workflow | Trigger | Publishes |
|---|---|---|
ts.yml |
packages/core/**, packages/express/** |
npm |
python.yml |
packages/fastapi/** |
PyPI |
java.yml |
packages/spring/** |
Maven Central |
engine.yml |
engine/** |
ghcr.io images |
go-sdk.yml |
packages/go/** |
— |
release.yml |
Tag push (v*) |
All packages |
Apache 2.0