Enforce: Middleware Enforcement
Block AI agents in your application code with Next.js or Express middleware
Goal
Add code-level AI agent enforcement to your Next.js or Express application. By the end of this cookbook, you'll have:
- Server-side detection on every request
- Configurable blocking, redirecting, or challenging of detected agents
- Session tracking with Redis (production-ready)
- Custom response handling for blocked requests
Best for: Applications where you need fine-grained control over enforcement behavior, custom responses, or integration with existing authentication.
Prerequisites
- A Checkpoint account with a project created
- A Next.js 13+ or Express 4+ application
- Node.js 16+
- (Optional) Redis for production session storage
Time Estimate
20-25 minutes
Steps
Install the Package
npm install @kya-os/agentshield-nextjsnpm install @kya-os/agentshield-expressConfigure Environment Variables
- Go to your Checkpoint dashboard
- Select your project
- Go to Settings → API Keys
- Create or copy your API key
# .env.local
AGENTSHIELD_API_KEY=as_live_xxxxxxxxxxxx
CHECKPOINT_PROJECT_ID=proj_abc123def456
# For production session storage
REDIS_URL=redis://localhost:6379Set Up Basic Enforcement
Create middleware.ts in your project root:
// middleware.ts
import { withAgentShield } from '@kya-os/agentshield-nextjs/api-middleware';
export default withAgentShield({
apiKey: process.env.AGENTSHIELD_API_KEY!,
onBlock: 'block', // Enable enforcement
});
export const config = {
matcher: [
// Match all paths except static assets
'/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)',
],
};This blocks all detected AI agents with a default 403 response.
// app.ts
import express from 'express';
import { createEnhancedAgentShieldMiddleware } from '@kya-os/agentshield-express';
const app = express();
app.use(
createEnhancedAgentShieldMiddleware({
projectId: process.env.CHECKPOINT_PROJECT_ID!,
mode: 'enforce', // Enable enforcement (not just 'detect')
})
);
app.get('/', (req, res) => {
res.json({ message: 'Only humans allowed!' });
});
app.listen(3000);Customize Enforcement Behavior
// middleware.ts
import { withAgentShield } from '@kya-os/agentshield-nextjs/api-middleware';
import { NextResponse } from 'next/server';
export default withAgentShield({
apiKey: process.env.AGENTSHIELD_API_KEY!,
// Custom blocking behavior
onBlock: (detection, request) => {
// Option 1: Block with custom response
if (detection.detectionClass === 'ai_agent') {
return new NextResponse(
JSON.stringify({
error: 'AI agent access is not permitted',
agent: detection.agentName,
}),
{
status: 403,
headers: { 'Content-Type': 'application/json' },
}
);
}
// Option 2: Redirect bots to a notice page
if (detection.detectionClass === 'bot') {
return NextResponse.redirect(new URL('/bot-notice', request.url));
}
// Option 3: Allow with warning header
const response = NextResponse.next();
response.headers.set('X-Agent-Warning', 'Detected as automated traffic');
return response;
},
// Only block high-confidence detections
confidenceThreshold: 70,
// Allow specific agents
allowList: ['Googlebot', 'Bingbot'],
});
export const config = {
matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
};// app.ts
import express from 'express';
import { createEnhancedAgentShieldMiddleware } from '@kya-os/agentshield-express';
const app = express();
app.use(
createEnhancedAgentShieldMiddleware({
projectId: process.env.CHECKPOINT_PROJECT_ID!,
mode: 'enforce',
// Custom enforcement logic
onBlock: (detection, req, res) => {
// Block AI agents
if (detection.detectionClass === 'ai_agent') {
return res.status(403).json({
error: 'AI agent access denied',
agent: detection.agentName,
confidence: detection.confidence,
});
}
// Redirect unknown bots
if (
detection.detectionClass === 'bot' &&
!['Googlebot', 'Bingbot'].includes(detection.agentName || '')
) {
return res.redirect(302, '/bot-notice');
}
// Allow everything else
return null; // Continue to next middleware
},
// Only enforce above threshold
confidenceThreshold: 70,
})
);
app.get('/bot-notice', (req, res) => {
res.send('Bot traffic detected. Please contact support if you believe this is an error.');
});
app.get('/', (req, res) => {
res.json({ message: 'Welcome, human!' });
});
app.listen(3000);Add Production Session Storage
For production, use Redis to persist detection sessions:
// middleware.ts
import { createEnhancedAgentShieldMiddleware } from '@kya-os/agentshield-nextjs';
export default createEnhancedAgentShieldMiddleware({
projectId: process.env.CHECKPOINT_PROJECT_ID!,
// Redis storage for production
storage: {
type: 'redis',
url: process.env.REDIS_URL,
keyPrefix: 'agentshield:', // Optional: namespace keys
},
onDetection: (event) => {
console.log(`Detection: ${event.detectionClass} (${event.confidence}%)`);
},
});// app.ts
import express from 'express';
import { createEnhancedAgentShieldMiddleware } from '@kya-os/agentshield-express';
const app = express();
app.use(
createEnhancedAgentShieldMiddleware({
projectId: process.env.CHECKPOINT_PROJECT_ID!,
mode: 'enforce',
// Redis storage for production
storage: {
type: 'redis',
url: process.env.REDIS_URL,
keyPrefix: 'agentshield:',
},
onDetection: (event) => {
console.log(`Detection: ${event.detectionClass} (${event.confidence}%)`);
},
})
);
app.listen(3000);Memory storage (default) is fine for development but resets on restart and doesn't share across instances. Use Redis for production.
Protect Specific Routes
Instead of global middleware, protect only specific routes:
Middleware approach — match specific paths:
// middleware.ts
export const config = {
matcher: [
'/api/:path*', // All API routes
'/dashboard/:path*', // Dashboard routes
'/checkout/:path*', // Checkout flow
],
};Route handler approach — per-endpoint protection:
// app/api/sensitive/route.ts
import { withAgentShield } from '@kya-os/agentshield-nextjs';
export const POST = withAgentShield(
async (req) => {
// Only executes if not blocked
return Response.json({ secret: 'data' });
},
{
apiKey: process.env.AGENTSHIELD_API_KEY!,
onBlock: 'block',
}
);// app.ts
import express from 'express';
import { createEnhancedAgentShieldMiddleware } from '@kya-os/agentshield-express';
const app = express();
// Create reusable middleware instances
const detectOnly = createEnhancedAgentShieldMiddleware({
projectId: process.env.CHECKPOINT_PROJECT_ID!,
mode: 'detect',
});
const enforceStrict = createEnhancedAgentShieldMiddleware({
projectId: process.env.CHECKPOINT_PROJECT_ID!,
mode: 'enforce',
confidenceThreshold: 60,
});
// Public routes — detect only
app.get('/', detectOnly, (req, res) => {
res.json({ message: 'Welcome!' });
});
// Sensitive routes — enforce
app.use('/api', enforceStrict);
app.get('/api/data', (req, res) => {
res.json({ sensitive: 'data' });
});
// Payment routes — strict enforcement
app.use('/checkout', enforceStrict);
app.listen(3000);Test Your Enforcement
Test as a human:
curl http://localhost:3000/api/data
# Should return 200 OK with dataTest as ChatGPT (should be blocked):
curl -H "User-Agent: Mozilla/5.0 (compatible; GPTBot/1.0; +https://openai.com/gptbot)" \
http://localhost:3000/api/data
# Should return 403 ForbiddenTest as Googlebot (if allowed):
curl -H "User-Agent: Googlebot/2.1 (+http://www.google.com/bot.html)" \
http://localhost:3000/api/data
# Should return 200 OK (if Googlebot is in allowList)Check your dashboard → Analytics to see the blocked requests.
Advanced Patterns
Allow Verified AI Agents
Block unverified agents but allow those with valid cryptographic signatures:
// middleware.ts
export default withAgentShield({
apiKey: process.env.AGENTSHIELD_API_KEY!,
onBlock: (detection, request) => {
// Allow agents with verified signatures
if (detection.signatureVerified) {
return null; // Allow through
}
// Block unverified AI agents
if (detection.detectionClass === 'ai_agent') {
return new NextResponse('Unverified agent denied', { status: 403 });
}
return null;
},
});Rate Limiting by Agent Type
// Express example with rate limiting
import rateLimit from 'express-rate-limit';
const humanLimiter = rateLimit({ windowMs: 60000, max: 100 });
const botLimiter = rateLimit({ windowMs: 60000, max: 10 });
app.use((req, res, next) => {
const detection = req.agentShield;
if (detection?.detectionClass === 'bot') {
return botLimiter(req, res, next);
}
return humanLimiter(req, res, next);
});Logging Blocked Requests
onBlock: (detection, request) => {
// Log to your monitoring system
console.log(
JSON.stringify({
event: 'agent_blocked',
class: detection.detectionClass,
agent: detection.agentName,
confidence: detection.confidence,
path: request.nextUrl?.pathname,
timestamp: new Date().toISOString(),
})
);
return new NextResponse('Access denied', { status: 403 });
};Troubleshooting
All Requests Being Blocked
| Symptom | Cause | Fix |
|---|---|---|
| Humans blocked | Confidence too low | Increase confidenceThreshold to 70+ |
| API calls blocked | Missing allowList | Add legitimate clients to allowList |
| Internal traffic blocked | Internal services flagged | Exclude internal routes from matcher |
Blocking Not Working
- Check mode — Must be
'enforce', not'detect' - Check onBlock — Must return a Response, not
null - Check matcher — Route must be included in matcher
Performance Issues
- Use Redis — Memory storage doesn't scale
- Cache detection results — Reduce API calls
- Narrow the matcher — Only protect necessary routes
What You Learned
- How to set up enforcement middleware in Next.js and Express
- How to customize blocking behavior (block, redirect, challenge)
- How to configure production-ready session storage
- How to protect specific routes instead of global enforcement
- Advanced patterns for verified agents and rate limiting
Next Steps
| Goal | Next Cookbook |
|---|---|
| DNS-level enforcement | Gateway Setup |
| Configure policies | Policy Configuration |
| Authorize agents | Govern Overview |