First, do no harm β to your API.
Next.js App Router Security Middleware
The first API security library that doesn't fight back.
It just nods, smiles, and feeds attackers convincing fake data.
β οΈ Traditional API security is broken. Rate limiting tells attackers to slow down. WAF blocks tell them to rotate IPs. Every403and429is feedback β making AI agents smarter.
Hippocrates flips the script. Every detected threat gets a convincing200 OKwith realistic fake data. The attacker wastes compute, burns token budgets, and their agentic pipelines fail silently β never knowing they've been caught.
π₯ The Problem β’ π Quick Start β’ βοΈ Defense Layers β’ π Comparison β’ π§ API β’ π³οΈ Anatomy
Your API has already been breached β you just served the attacker successfully.
Modern API threats aren't human. Autonomous AI agents are relentless, adaptive, and they never sleep:
| Capability | Impact | How Hippocrates Fights It |
|---|---|---|
| 10,000+ req/min from a single IP | Saturates infra, spikes cloud bills | π Silently routes to honeypot |
| Probes schema boundaries systematically | Discovers hidden endpoints | π§± Zero-Trust .strict() layers |
| Injects obfuscated payloads | Bypasses WAF keyword filters | π Recursive base64/hex scanner |
| Adapts in real-time from errors | Evolves attack strategy after every 4xx |
π«₯ No 4xx ever β no feedback loop |
| Chains API calls agentically | Correlates data across endpoints | π Breaks the chain silently |
| Approach | The Problem | Why |
|---|---|---|
Rate limiting (429) |
Feedback | Tells attacker to slow down. They do. Then resume. |
WAF blocks (403) |
Feedback | Tells attacker to rotate IP. They do. You chase. |
| API keys | Credential loss | Keys leak in env files, commits, logs. Game over. |
| Stateless validation | Amnesia | Every request is a fresh start. No memory. No pattern. |
Instead of blocking, we deceive. Instead of fighting, we fatigue. The attacker spends compute processing data that doesn't exist β and never realizes it.
Incoming Request
β
βΌ
βββββββββββββββββββ
β L-1 Allowlist?βββYESβββΊ Forward to handler (skip all checks)
ββββββββββ¬βββββββββ
β NO
βΌ
βββββββββββββββββββ ββββββββββββββββββββββββββββββββ
β L0 Pre-flight ββββββΊβ Existing score β₯ threshold? βββYESβββΊπ HONEYPOT
ββββββββββ¬βββββββββ β (remembered from Redis) β 200 OK (fake)
β NO ββββββββββββββββββββββββββββββββ
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β L1 Timing L2 Velocity L3 UA β
β L6 Headers (pre-body analyzers) β
βββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββ
β score β₯ threshold?
βββYESβββΊπ HONEYPOT
β NO
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β L4 Obfuscation L5 Schema (body analyzers) β
βββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββ
β score β₯ threshold?
βββYESβββΊπ HONEYPOT
β NO
βΌ
β
Forward to real handler
(clean, validated request)
| Feature | What It Does |
|---|---|
| π«₯ Silent Honeypot | 200 OK with fake data β zero signal to the attacker |
| π§ Stateful Threat Scoring | Redis-backed cumulative scores persist across requests |
| β‘ 6 Defense Layers | Timing, velocity, UA, obfuscation, schema, headers |
| π Edge-Ready | Works on Vercel Edge Runtime, Node.js β zero Buffer usage |
| π Zero-Trust Validation | Recursive .strict() on every nested Zod type |
| π€ AI Agent Detection | 40+ patterns: OpenAI, Anthropic, LangChain, Playwright, 2026 agents |
| πͺ No Data Leakage | Error messages intentionally vague β no schema details exposed |
| π‘οΈ IP Allowlist | Exact match + CIDR prefix for trusted IPs |
| βοΈ Config Presets | strict, moderate, relaxed β one-liner tuning |
| π§© Plugin System | Custom AnalyzerPlugin for any detection logic (v1.5) |
| π Event Hooks | Monitor violations, passes, honeypot events (v1.5) |
| π Stats Tracking | In-memory counters via StatsTracker interface (v1.6) |
| π€ ML Engine | Python sidecar for prompt injection & deep content scoring (v1.6) |
| π³ Docker Support | One-command docker compose up with Redis + ML engine |
npm install hippocrates-middleware zod @upstash/redis60 seconds to protection. Wrap any App Route handler:
// app/api/data/route.ts
import { NextRequest, NextResponse } from "next/server";
import { Redis } from "@upstash/redis";
import { withHippocrates, z } from "hippocrates-middleware";
const redis = new Redis({
url: process.env.UPSTASH_REDIS_REST_URL!,
token: process.env.UPSTASH_REDIS_REST_TOKEN!,
});
// β οΈ .strict() is MANDATORY β extra fields trigger instant honeypot
const Schema = z.object({
userId: z.string().uuid(),
action: z.enum(["read", "write"]),
}).strict();
async function handler(req: NextRequest): Promise<NextResponse> {
const body = await req.json();
return NextResponse.json({ success: true, data: body });
}
// π©Ί That's it. One wrapper. Full protection.
export const POST = withHippocrates(handler, Schema, redis);π§ With Custom Configuration β
export const POST = withHippocrates(handler, Schema, redis, {
// ββ Preset: one-liner tuning ββ
preset: "strict", // "moderate" | "relaxed" β overrides all below
// ββ Or manual overrides ββ
threatScoreThreshold: 50, // Default: 65 (lower = stricter)
velocityMaxRequests: 10, // Default: 15 req/window
velocityWindowMs: 5_000, // Default: 10s window
debugMode: process.env.NODE_ENV === "development",
decoyGenerator: (req) => ({
success: true,
data: {
id: crypto.randomUUID(),
status: "active",
timestamp: new Date().toISOString(),
},
}),
scoring: {
velocityViolation: 60, // Per-endpoint scoring override
suspiciousUserAgent: 30,
},
// ββ v1.6 features ββ
allowlist: { ips: ["10.0.0.0/8", "127.0.0.1"] },
bodyLimit: { maxBytes: 524_288, enabled: true },
});| # | Layer | Signal | Points | What Gets Flagged |
|---|---|---|---|---|
| L0 | Pre-flight Check | Existing Redis score | Instant | Repeat offenders from previous requests |
| L1 | Timing Analysis | Interval < 50ms | +25 | Machine-speed execution |
| L2 | Velocity Tracking | Burst > 15 req / 10s | +40 | Sliding window via Redis list |
| L3 | UA Fingerprinting | Suspicious / missing UA | +15 | 35+ patterns: LLM SDKs, HTTP libs |
| L4 | Obfuscation Detection | Base64/Hex/Unicode | +100 π₯ | Instant max score |
| L5 | Schema Validation | Zod .strict() violation |
+100 π₯ | Any extra field or type mismatch |
| L6 | Header Anomalies | Missing/wildcard headers | +15 | Non-browser HTTP clients |
L4 and L5 are nuclear options. Any obfuscation or schema violation immediately pushes the threat score to 100 β no incremental tolerance.
| Category | Patterns |
|---|---|
| π€ LLM SDKs | anthropic-sdk, openai-node, google-gemini, langchain, llamaindex, autogen, crewai, smolagents, cohere, mistral, together, groq, deepseek, dspy, huggingface |
| π HTTP Libs | python-requests, aiohttp, httpx, axios, node-fetch, got, curl, wget |
| π΅οΈ Browser Automation | playwright, puppeteer, selenium, cypress, headlesschrome |
| π 2026 AI Agents | claudebot, cursor, perplexitybot, githubcopilot, opencode, windsurf |
| Pattern | Example | Threshold |
|---|---|---|
| Base64 | dXNlci1pZDogMTIz... |
β₯ 24 chars |
| Hex encoding | 0x48656c6c6f576f726c64 |
β₯ 16 hex chars |
| URL encoding | %68%65%6c%6c%6f |
5+ consecutive |
| Unicode escapes | \u0068\u0065\u006c |
Any occurrence |
| HTML entities | hel |
Any occurrence |
| Feature | π©Ί Hippocrates | Rate Limiting | WAF | express-rate-limit |
|---|---|---|---|---|
| Attacker sees | 200 OK (fake) |
429 |
403 |
429 |
| Attacker knows? | Never | Yes | Yes | Yes |
| Stateful? | β Redis-backed | β Usually | β Per-request | β Per-window |
| AI agent detection | β 35+ patterns | β | β | β |
| Obfuscation scan | β Recursive | β | β | |
| Zero-Trust schema | β
Recursive .strict() |
β | β | β |
| Edge Runtime | β
No Buffer |
β | β | β |
| IP allowlist | β Exact + CIDR | β | β | β |
| Config presets | β 3 presets | β | β | β |
| Install size | ~33KB | varies | N/A | ~15KB |
| Param | Type | Required | Default | Description |
|---|---|---|---|---|
handler |
(req) => Promise<NextResponse> |
β | β | Your route handler |
schema |
ZodType<T> |
β | β | Zod schema with .strict() |
redis |
RedisClient |
β | β | Upstash / ioredis compatible client |
config |
HippocratesConfig |
β | See below | Optional overrides |
| Option | Type | Default | Description |
|---|---|---|---|
preset |
`"strict" | "moderate" | "relaxed"` |
threatScoreThreshold |
number |
65 |
Score (0β100) that triggers honeypot |
velocityWindowMs |
number |
10_000 |
Sliding window for velocity tracking |
velocityMaxRequests |
number |
15 |
Max requests per window |
threatTtlSeconds |
number |
3_600 |
Redis TTL for threat keys |
scoring |
Partial<ThreatScoringWeights> |
DEFAULT_WEIGHTS |
Per-layer weight overrides |
decoyGenerator |
(req) => object |
Built-in | Custom decoy response |
debugMode |
boolean |
false |
Verbose security logging |
plugins |
AnalyzerPlugin[] |
β | Custom detection analyzers (v1.5) |
hooks |
HippocratesHooks |
β | Event hooks for violation/pass/honeypot (v1.5) |
allowlist |
{ ips: string[] } |
β | Exact + CIDR IP bypass (v1.6) |
bodyLimit |
{ maxBytes, enabled } |
{ 1MB, enabled: true } |
Payload size enforcement (v1.6) |
methodThresholds |
Partial<Record<string, number>> |
β | Per-HTTP-method threshold overrides (v1.6) |
violationMessages |
object |
β | Custom honeypot messages per violation type (v1.6) |
statsTracker |
StatsTracker |
β | Consumer-provided stats interface for real-time metrics (v1.6) |
| Key | Purpose | TTL |
|---|---|---|
hc:s:{ip} |
Cumulative threat score (0β100) | threatTtlSeconds |
hc:t:{ip} |
Request timestamp list (velocity) | windowMs + 10s |
hc:l:{ip} |
Last-seen timestamp (timing) | 300s |
The honeypot is the heart of Hippocrates. When an attacker triggers a detection, they receive a convincing 200 OK β with deceptive data designed to waste their resources.
Key insight: The honeypot generates 4 rotating response templates with randomized fake data. Each request looks legitimate but leads nowhere. The attacker burns money processing synthetic data they can't distinguish from real API responses.
| Template | Shape | Looks Like |
|---|---|---|
| A β Generic Data | { success, requestId, data, metadata } |
Standard REST endpoint |
| B β Auth Token | { accessToken, tokenType, expiresIn, scope } |
OAuth token exchange |
| C β Paginated List | { items[], pagination } |
List API with cursors |
| D β Analytics | { dashboard, metrics[], summary } |
Metrics dashboard API |
All templates include:
- β Realistic UUIDs, timestamps, and version strings
- β Plausible pagination (hasNext: true to keep them going)
- β Randomized processing latency headers
- β No
x-powered-by, noserverheaders - β No signal the request was intercepted
Extend Hippocrates with custom detection logic using the AnalyzerPlugin interface. Plugins run in two phases:
| Phase | When It Runs | Built-in Analyzers |
|---|---|---|
| pre-body | Before request body is parsed | L1 Timing, L2 Velocity, L3 UA, L6 Headers |
| post-body | After body is parsed (has access to context.bodyRaw) |
L4 Obfuscation, L5 Schema |
import { type AnalyzerPlugin } from "hippocrates-middleware";
const myAnalyzer: AnalyzerPlugin = {
name: "custom_check",
phase: "pre-body",
priority: 50, // Lower = runs first (default: 100)
analyze(req, ctx) {
if (req.headers.get("x-custom") === "bad") {
return { score: 30, tags: ["custom:bad"] };
}
return { score: 0, tags: [] };
},
};
export const POST = withHippocrates(handler, schema, redis, {
plugins: [myAnalyzer],
});Monitor security events in real-time with onViolation, onPass, and onHoneypot callbacks:
export const POST = withHippocrates(handler, schema, redis, {
hooks: {
onViolation: (event) => {
console.log(`Violation: ${event.ip} β ${event.violations}`);
},
onPass: (event) => {
metrics.recordPass(event.ip, event.score);
},
onHoneypot: (event) => {
alertService.notify(`Honeypot served: ${event.ip}`);
},
},
});Hippocrates maintains built-in in-memory counters for all security events. Access them via ThreatScoreEngine.getStats() or provide a custom StatsTracker:
import { type StatsTracker } from "hippocrates-middleware";
const tracker: StatsTracker = {
increment(counter) {
console.log(`Event: ${counter}`);
},
getStats() {
return { totalRequests: 0, blockedByPreflight: 0, /* ... */ };
},
reset() {},
};
export const POST = withHippocrates(handler, schema, redis, {
statsTracker: tracker,
});Available counters: totalRequests, blockedByPreflight, blockedByTiming, blockedByVelocity, blockedByObfuscation, blockedBySchema, passedToHandler, honeypotServed, redisErrors.
Hippocrates ships with an optional Python sidecar for ML-based threat detection (prompt injection, advanced obfuscation, content risk scoring).
ββββββββββββββββ ββββββββββββββββ βββββββββββββ
β Hippocrates ββββββΊβ ML Engine ββββββΊβ Redis β
β (Next.js) β β (FastAPI) β β (Upstash) β
ββββββββββββββββ ββββββββββββββββ βββββββββββββ
β β
β POST /analyze β
β { body_raw, ... } β
ββββββββββββββββββββββ
# Start both Redis and ML engine
docker compose up -d
# ML engine is now available at http://localhost:8000import { mlEnginePlugin } from "hippocrates-middleware";
export const POST = withHippocrates(handler, schema, redis, {
plugins: [mlEnginePlugin({
baseUrl: "http://ml-engine:8000", // Docker service name
timeoutMs: 3000,
minScoreThreshold: 10,
})],
});- After body parsing (post-body phase), the pipeline sends the raw request body to the ML engine
- The ML engine runs prompt injection detection, obfuscation scoring, and content risk analysis
- Results contribute to the cumulative threat score
- If the ML engine is unreachable, it degrades gracefully (score = 0, no crash)
| Option | Default | Description |
|---|---|---|
baseUrl |
http://localhost:8000 |
ML engine endpoint |
timeoutMs |
3000 |
Request timeout before fallback |
minScoreThreshold |
10 |
Minimum ML score to contribute to threat score |
Run the full Hippocrates stack with a single command:
# docker-compose.yml (included in the package)
services:
hippocrates-ml-engine:
build: ./engine-python
ports:
- "8000:8000"
environment:
- REDIS_URL=redis://redis:6379
depends_on:
redis:
condition: service_healthy
redis:
image: redis:7-alpine
ports:
- "6379:6379"
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
retries: 5# Start services
docker compose up -d
# Run smoke test
powershell -File engine-python/smoke-test.ps1The ML engine runs on python:3.12-slim with FastAPI + httpx. The Docker build is verified in CI via GitHub Actions.
| Scenario | Why Hippocrates |
|---|---|
| π’ SaaS APIs | Protect B2B/B2C endpoints from LLM scraping agents |
| π€ AI Startups | Prevent competitors from extracting training data |
| π E-commerce | Block automated pricing bots and inventory scrapers |
| π₯ Social Platforms | Shadow-ban bot networks probing user data |
| π¦ Financial Services | Halt credential stuffing and enumeration attacks |
npm test # 177 tests across 10 files β all pass
npm run typecheck # tsc --noEmit β zero errors
npm run lint # ESLint flat config β zero errors
npm run build # tsup β CJS + ESM + .d.ts| Area | How to Help |
|---|---|
| π Bug reports | Open an issue with reproduction steps |
| π€ New UA patterns | Add it β especially 2026+ agents |
| π New obfuscation patterns | See SKILL.md |
| β‘ New detection layers | Architecture guidance in CLAUDE.md |
| π Documentation | Better docs = better adoption |
npm run dev # tsup --watch
npm run build # Production build
npm run test:watch # TDD mode
β Star the repo to show support and help others discover Hippocrates!
If Hippocrates protects your API, consider supporting development:
MIT Β© achmdfzn
First, do no harm β to your API.
Built for the era where your API consumers might not be human.