Express Integration
Server-side AI agent detection for Express and Node.js applications
Overview
The Checkpoint Express integration provides server-side AI agent detection and enforcement for Node.js applications. Mount one middleware factory — withCheckpoint — and every request flows through the in-process WASM engine: UA pattern detection, KYA-OS signature verification, scope evaluation, delegation-chain verification, and policy evaluation.
- < 2ms latency — In-process WASM engine, no per-request network hop
- Enforce or Observe modes — Active enforcement or log-only dry runs
- Cedar policy enforcement — Enforces your deployed dashboard policy in-process, byte-for-byte the same as the DNS Gateway
- Route-level control — Protect specific endpoints
- Composable observability — Session tracking and event storage via
onResult - TypeScript — Full type definitions included
Prerequisites
- A Checkpoint project — get your Project ID and API key from Installations in the dashboard.
- An existing Express app.
withCheckpointneedsexpress.json()mounted ahead of it (see Quick Setup below) so it can parse the KYA-OS envelope.
Installation
npm install @kya-os/checkpoint-expressQuick Setup
const express = require('express');
const { withCheckpoint } = require('@kya-os/checkpoint-express');
const app = express();
app.use(express.json()); // body-parser — required for KYA-OS envelope parsing
app.use(
withCheckpoint({
tenantHost: 'your.tenant.example',
apiKey: process.env.CHECKPOINT_API_KEY, // enables dashboard reporting
})
);
app.get('/', (req, res) => {
res.send('Protected by Checkpoint');
});
app.listen(3000);import express from 'express';
import { withCheckpoint, type CheckpointConfig } from '@kya-os/checkpoint-express';
const app = express();
app.use(express.json()); // body-parser — required for KYA-OS envelope parsing
const config: CheckpointConfig = {
tenantHost: 'your.tenant.example',
apiKey: process.env.CHECKPOINT_API_KEY, // enables dashboard reporting
projectId: process.env.CHECKPOINT_PROJECT_ID, // enforces your deployed Cedar policy
};
app.use(withCheckpoint(config));
app.listen(3000);The legacy createEnhancedAgentShieldMiddleware was removed in
@kya-os/checkpoint-express@1.3.0. Session tracking and event storage are now composable
primitives wired through withCheckpoint — see Express Observability &
Storage.
Configuration
The most common withCheckpoint options:
withCheckpoint({
// Required
tenantHost: string, // Tenant identifier (your dashboard hostname)
// Dashboard integration
apiKey?: string, // Project API key — required for dashboard reporting
projectId?: string, // Enforce this project's deployed Cedar policy in-process
baseUrl?: string, // Dashboard base URL (default: https://kya.vouched.id)
// Behavior
enforcementMode?: 'enforce' | 'observe', // Default: 'enforce'
policyCacheTtlSeconds?: number, // Policy-fetch cache TTL (default: 300)
// Observability
onResult?: (result, req) => void, // Fires after every verification with the full VerifyResult
debug?: boolean, // Surface reporter + policy telemetry via console.warn
});See Middleware Enforcement for the complete option table, response headers, and response shapes shared with the Next.js and .NET middleware.
Environment Variables
# .env
CHECKPOINT_API_KEY=your_api_key_here
CHECKPOINT_PROJECT_ID=your_project_id_hereapiKey is required for detections to land in the dashboard — without it the verdict path works
locally, but the onboarding Verify connection check never passes because no detection rows are
written. Get both values from Installations in the dashboard.
Enforce vs Observe Mode
| Mode | Behavior |
|---|---|
enforce | Applies the policy verdict — block, redirect, or challenge for consent/identity. Default. |
observe | Classifies every request and stamps X-Checkpoint-Would-Have-Been headers. All traffic passes through. |
// Observe mode — log everything, block nothing
app.use(
withCheckpoint({
tenantHost: 'your.tenant.example',
apiKey: process.env.CHECKPOINT_API_KEY,
enforcementMode: 'observe',
})
);Start in observe, review the would-have-been decisions in the dashboard, then remove the override (or set 'enforce') when the verdicts look right.
Response Headers
The engine sets X-Checkpoint-* response headers and a __checkpoint_verdict cookie — byte-identical to the Next.js sibling package. In observe mode, X-Checkpoint-Would-Have-Been is added instead of enforcing the verdict.
When session tracking is wired via withSessionTracking, ExpressSessionTracker additionally emits kya-session, kya-session-agent, and kya-session-id. See Express Observability & Storage for session tracking setup, and Middleware Enforcement for the complete header reference shared with Next.js and .NET.
Observability & Storage
Plain withCheckpoint attaches nothing to req — read verdicts from the onResult callback, which fires after every verification (errors thrown inside it are swallowed so observability can never break the verdict path):
app.use(
withCheckpoint({
tenantHost: 'your.tenant.example',
apiKey: process.env.CHECKPOINT_API_KEY,
onResult: (result, req) => {
console.log(req.path, result.detectionDetail.detectionClass.type);
},
})
);For session continuity (withSessionTracking, req.checkpoint) and persisting detection events to Memory/Redis/custom stores (createStorageAdapter), see Express Observability & Storage.
Usage Patterns
Global Protection
// Protect all routes
app.use(
withCheckpoint({
tenantHost: 'your.tenant.example',
apiKey: process.env.CHECKPOINT_API_KEY,
})
);
app.get('/api/data', (req, res) => {
res.json({ protected: true });
});Route-Specific Protection
withCheckpoint returns standard Express middleware, so you can mount it per-route. Construct it once and reuse it:
const protect = withCheckpoint({
tenantHost: 'your.tenant.example',
apiKey: process.env.CHECKPOINT_API_KEY,
});
// Only protect specific routes
app.get('/api/sensitive', protect, (req, res) => {
res.json({ sensitive: 'data' });
});
// Unprotected route
app.get('/api/public', (req, res) => {
res.json({ public: 'data' });
});Testing
const request = require('supertest');
const app = require('./app');
describe('Checkpoint Protection', () => {
it('should allow normal browsers', async () => {
const response = await request(app)
.get('/api/protected')
.set('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)...');
expect(response.status).toBe(200);
});
it('should pass agents through in observe mode with a would-have-been header', async () => {
const response = await request(app).get('/api/protected').set('User-Agent', 'GPTBot/1.0');
// observe mode: request passes through, verdict is stamped on the response
expect(response.status).toBe(200);
expect(response.headers['x-checkpoint-would-have-been']).toBeDefined();
});
});Troubleshooting
Middleware not working
- Verify
tenantHostis set — it is the one required option - Ensure
app.use(express.json())runs beforewithCheckpoint(required for KYA-OS envelope parsing) - Ensure the middleware is mounted before your routes (
app.usebeforeapp.get) - Enable
debug: trueto surface reporter and policy telemetry
Detections not showing in the dashboard
- Set
apiKey— without it, verdicts are computed locally but never reported - Enable
debug: trueand watch for reporter warnings to confirmapiKey/baseUrlrouting
Policy changes not taking effect
- Fetched policies are cached for
policyCacheTtlSeconds(default 300s) — wait out the TTL or lower it - Confirm the policy is deployed (not a draft) and engine enforcement is enabled for the project
Next Steps
- Middleware Enforcement — Complete middleware documentation with all features
- Express Observability & Storage — Session tracking and event storage
- Policies — Configure enforcement rules
- Next.js Integration — Edge-optimized middleware for Next.js
- Choose Your Integration — Compare all integration options
