@kya-os/checkpoint-express 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,469 @@
1
+ import { Request, RequestHandler, Response, NextFunction } from 'express';
2
+ import { DidResolverAdapter, StatusListCacheAdapter, ReputationOracleAdapter, PolicyEvaluatorAdapter } from '@kya-os/checkpoint-wasm-runtime/adapters';
3
+ import { EnforcementMode, VerifyResult } from '@kya-os/checkpoint-wasm-runtime/engine';
4
+ import { DetectionResult, AgentShieldConfig } from '@kya-os/checkpoint-shared';
5
+ export { DEFAULT_POLICY, ENFORCEMENT_ACTIONS, PolicyConfig, PolicyEvaluationContext, PolicyEvaluationResult, createEvaluationContext, evaluatePolicy } from '@kya-os/checkpoint-shared';
6
+ export { PolicyMiddlewareConfig, applyPolicy, createContextFromDetection, evaluatePolicyForDetection, getPolicy, handlePolicyDecision, sendBlockedResponse, sendRedirectResponse } from './policy.js';
7
+
8
+ /**
9
+ * E.1 + E.3 — Express middleware entry: `withCheckpoint(config)`.
10
+ *
11
+ * The host wrapper that composes Phase B adapters + Phase C
12
+ * `verifyRequest` (sync engine) + Phase D translate/adapt into the
13
+ * `withCheckpoint(config)` factory. Mounted under Node-runtime Express
14
+ * servers via `app.use(withCheckpoint({...}))`.
15
+ *
16
+ * Phase E only ships the Node-runtime entry — Express doesn't have an
17
+ * Edge variant (Express runs on Node only). Phase D's edge orchestrator
18
+ * (`verifyRequestEdge`) exists for the Next.js + Vercel Edge use case;
19
+ * Express imports the sync `verifyRequest` directly.
20
+ *
21
+ * **Public API contract (architect § 4.1):**
22
+ *
23
+ * - `withCheckpoint(config)` — factory returning Express middleware.
24
+ * - `CheckpointConfig` — the config shape; new fields are additive.
25
+ *
26
+ * Internal implementation gutted, external contract held. The legacy
27
+ * `agentShield()` / `createAgentShieldMiddleware()` exports remain in
28
+ * the package during the deprecation window (see E.4) — but as
29
+ * throw-stubs with migration messages, not working entry points.
30
+ */
31
+
32
+ /**
33
+ * Configuration for `withCheckpoint`.
34
+ *
35
+ * Shape mirrors `checkpoint-nextjs`'s `CheckpointConfig` so customers
36
+ * running both runtimes have a single mental model.
37
+ */
38
+ interface CheckpointConfig {
39
+ /**
40
+ * Tenant identifier — typically the customer's dashboard hostname
41
+ * (e.g. `acme.checkpoint.example`). The PolicyEvaluator uses this
42
+ * to look up tenant policy from the dashboard.
43
+ */
44
+ tenantHost: string;
45
+ /**
46
+ * `'enforce'` (default) blocks; `'observe'` passes everything
47
+ * through with `X-Checkpoint-Would-Have-Been` headers. Per Phase 0.2.
48
+ */
49
+ enforcementMode?: EnforcementMode;
50
+ /**
51
+ * Argus reputation oracle base URL. Omit to use the trust-by-default
52
+ * baseline (reputation defaults to 1.0; orchestrator logs a one-shot
53
+ * warning at first request).
54
+ */
55
+ argusUrl?: string;
56
+ /**
57
+ * Dashboard base URL for the PolicyEvaluator to fetch tenant policy
58
+ * from. Omit to use the open-by-default tenant policy.
59
+ */
60
+ dashboardUrl?: string;
61
+ /**
62
+ * Returned to the PolicyEvaluator for anonymous requests (no agent
63
+ * DID). Default 1.0 (trust-by-default).
64
+ */
65
+ reputationBaseline?: number;
66
+ /**
67
+ * Pre-built adapter instances. Production deployments use the
68
+ * factory-built defaults from `@kya-os/checkpoint-wasm-runtime/
69
+ * adapters`; tests use stubs. The factory composes any provided
70
+ * overrides over defaults — partial overrides are supported.
71
+ */
72
+ adapters?: Partial<{
73
+ didResolver: DidResolverAdapter;
74
+ statusListCache: StatusListCacheAdapter;
75
+ reputationOracle: ReputationOracleAdapter;
76
+ policyEvaluator: PolicyEvaluatorAdapter;
77
+ }>;
78
+ /**
79
+ * Optional callback for the post-verdict path — fires after every
80
+ * verification, regardless of permit/block, with the full
81
+ * `VerifyResult`. Use for logging, dashboards, telemetry. Errors
82
+ * thrown here are swallowed so user code can't break the middleware
83
+ * response.
84
+ */
85
+ onResult?: (result: VerifyResult, req: Request) => void | Promise<void>;
86
+ }
87
+ /**
88
+ * Build the Checkpoint middleware. Returns an Express `RequestHandler`
89
+ * suitable for `app.use(withCheckpoint({...}))`.
90
+ *
91
+ * Every verification decision flows through the Rust `kya-os-engine`
92
+ * via WASM. The TS layer translates request shape, calls
93
+ * `verifyRequest`, and applies the verdict to `res` (or calls `next()`
94
+ * for pass-through). No verification logic lives in this file.
95
+ */
96
+ declare function withCheckpoint(config: CheckpointConfig): RequestHandler;
97
+
98
+ /**
99
+ * Express-specific type definitions
100
+ */
101
+
102
+ /**
103
+ * Express middleware configuration
104
+ */
105
+ interface ExpressMiddlewareConfig extends AgentShieldConfig {
106
+ /**
107
+ * API key for loading customer policies from AgentShield dashboard
108
+ * When provided, enables policy enforcement (deny lists, allow lists, thresholds)
109
+ */
110
+ apiKey?: string;
111
+ /**
112
+ * Custom URL for the policy API
113
+ * @default 'https://api.agentshield.io'
114
+ */
115
+ policyApiUrl?: string;
116
+ /**
117
+ * Action to take when an agent is detected
118
+ */
119
+ onAgentDetected?: 'block' | 'allow' | 'log';
120
+ /**
121
+ * Custom handler for agent detection
122
+ */
123
+ onDetection?: (req: Request, res: Response, result: DetectionResult) => void | Promise<void>;
124
+ /**
125
+ * Skip agent detection for certain routes
126
+ */
127
+ skipPaths?: string[] | RegExp[];
128
+ /**
129
+ * Custom response when blocking agents
130
+ */
131
+ blockedResponse?: {
132
+ status: number;
133
+ message: string;
134
+ headers?: Record<string, string>;
135
+ };
136
+ /**
137
+ * Enable debug logging
138
+ */
139
+ debug?: boolean;
140
+ }
141
+ /**
142
+ * Extended Express Request with AgentShield data
143
+ */
144
+ interface AgentShieldRequest extends Request {
145
+ agentShield?: {
146
+ result: DetectionResult;
147
+ skipped: boolean;
148
+ session?: {
149
+ id: string;
150
+ agent: string;
151
+ confidence: number;
152
+ detectedAt: number;
153
+ expires: number;
154
+ };
155
+ };
156
+ }
157
+ /**
158
+ * Middleware function type
159
+ */
160
+ type AgentShieldMiddleware = (req: Request, res: Response, next: NextFunction) => void | Promise<void>;
161
+
162
+ /**
163
+ * Legacy Express middleware — Phase E throw-stub.
164
+ *
165
+ * The local-detection chain (pattern matching → JS-side policy
166
+ * evaluation) was retired in Phase E. Every legacy
167
+ * call site now throws with a migration message pointing at
168
+ * `withCheckpoint()` from `@kya-os/checkpoint-express`.
169
+ *
170
+ * Per Dylan's architect call 2026-05-16: local-detection chain entry
171
+ * points become throw-stubs after the engine-backed replacement ships.
172
+ * The export names survive so customers don't `ImportError` on upgrade
173
+ * — calls throw at runtime with a clear migration path. SaaS-API
174
+ * entry points (`AgentShieldClient`, `client.enforce()`) get a
175
+ * different treatment (rename + preserve); Express had no such surface
176
+ * (audit confirmed 2026-05-16 in the Phase E kickoff brief § "addendum
177
+ * 2026-05-16").
178
+ *
179
+ * @deprecated Use `withCheckpoint` from `@kya-os/checkpoint-express`.
180
+ */
181
+
182
+ /**
183
+ * @deprecated Throws with a migration message. Use `withCheckpoint` instead.
184
+ */
185
+ declare function createAgentShieldMiddleware(_config?: Partial<ExpressMiddlewareConfig>): AgentShieldMiddleware;
186
+ /**
187
+ * @deprecated Throws with a migration message. Use `withCheckpoint` instead.
188
+ */
189
+ declare function agentShield(_config?: Partial<ExpressMiddlewareConfig>): AgentShieldMiddleware;
190
+
191
+ /**
192
+ * Session tracking helper for Express
193
+ * Provides graceful degradation of session tracking for non-Next.js environments
194
+ */
195
+
196
+ interface SessionData {
197
+ id: string;
198
+ agent: string;
199
+ confidence: number;
200
+ detectedAt: number;
201
+ expires: number;
202
+ }
203
+ /**
204
+ * Express-compatible session tracker
205
+ * Uses cookies when available, falls back to headers
206
+ */
207
+ declare class ExpressSessionTracker {
208
+ private readonly cookieName;
209
+ /**
210
+ * Check for existing session from Express request
211
+ */
212
+ check(req: Request): SessionData | null;
213
+ /**
214
+ * Track a new session in Express response
215
+ */
216
+ track(res: Response, result: DetectionResult): void;
217
+ /**
218
+ * Generate a simple ID without crypto dependency
219
+ */
220
+ private generateId;
221
+ }
222
+ /**
223
+ * Helper to add session tracking to Express middleware
224
+ */
225
+ declare function withSessionTracking(middleware: (req: Request, res: Response, next: NextFunction) => void | Promise<void>, config?: {
226
+ enabled?: boolean;
227
+ }): (req: Request, res: Response, next: NextFunction) => void | Promise<void>;
228
+
229
+ /**
230
+ * Storage adapter types for Express
231
+ * Matches the Next.js storage adapter interface for consistency
232
+ */
233
+ /**
234
+ * Agent detection event
235
+ */
236
+ interface AgentDetectionEvent {
237
+ eventId: string;
238
+ sessionId: string;
239
+ timestamp: string;
240
+ agentType: string;
241
+ agentName: string;
242
+ confidence: number;
243
+ path: string;
244
+ userAgent?: string;
245
+ ipAddress?: string;
246
+ method: string;
247
+ detectionReasons: string[];
248
+ verificationMethod: string;
249
+ detectionDetails?: {
250
+ patterns?: Record<string, unknown>;
251
+ behaviors?: Record<string, unknown>;
252
+ fingerprintMatches?: string[];
253
+ };
254
+ }
255
+ /**
256
+ * Agent session data
257
+ */
258
+ interface AgentSession {
259
+ sessionId: string;
260
+ ipAddress?: string;
261
+ userAgent?: string;
262
+ agentType: string;
263
+ agentName: string;
264
+ firstSeen: string;
265
+ lastSeen: string;
266
+ eventCount: number;
267
+ paths: string[];
268
+ averageConfidence: number;
269
+ verificationMethods: string[];
270
+ }
271
+ /**
272
+ * Storage adapter interface
273
+ */
274
+ interface StorageAdapter {
275
+ /**
276
+ * Store an agent detection event
277
+ */
278
+ storeEvent(event: AgentDetectionEvent): Promise<void>;
279
+ /**
280
+ * Store or update a session
281
+ */
282
+ storeSession(session: AgentSession): Promise<void>;
283
+ /**
284
+ * Get events for a session
285
+ */
286
+ getEvents(sessionId: string, limit?: number): Promise<AgentDetectionEvent[]>;
287
+ /**
288
+ * Get session by ID
289
+ */
290
+ getSession(sessionId: string): Promise<AgentSession | null>;
291
+ /**
292
+ * Get recent events
293
+ */
294
+ getRecentEvents(limit?: number): Promise<AgentDetectionEvent[]>;
295
+ /**
296
+ * Get active sessions
297
+ */
298
+ getActiveSessions(limit?: number): Promise<AgentSession[]>;
299
+ /**
300
+ * Clear old data
301
+ */
302
+ cleanup?(before: Date): Promise<void>;
303
+ }
304
+ /**
305
+ * Storage configuration
306
+ */
307
+ interface StorageConfig {
308
+ type: 'memory' | 'redis' | 'custom';
309
+ ttl?: number;
310
+ redis?: {
311
+ url: string;
312
+ token: string;
313
+ };
314
+ custom?: StorageAdapter;
315
+ }
316
+
317
+ /**
318
+ * Legacy enhanced middleware — Phase E throw-stub.
319
+ *
320
+ * `createEnhancedAgentShieldMiddleware` previously bundled local
321
+ * detection + session tracking + Redis/Memory event storage in one
322
+ * factory. Phase E retires the local detection per Dylan's architect
323
+ * call 2026-05-16 (local-detection chain → throw-stub) but preserves
324
+ * the session/Redis observability surface as **composable primitives**:
325
+ *
326
+ * - Storage adapters (`MemoryStorageAdapter`, `RedisStorageAdapter`,
327
+ * `createStorageAdapter`) remain exported from `./storage`.
328
+ * - Session-cookie helpers remain exported from `./session-helper`.
329
+ *
330
+ * Customers who used `createEnhancedAgentShieldMiddleware` for the
331
+ * combined behaviour migrate to `withCheckpoint` + an `onResult`
332
+ * callback that calls into the storage adapter:
333
+ *
334
+ * import { withCheckpoint } from '@kya-os/checkpoint-express';
335
+ * import { createStorageAdapter } from '@kya-os/checkpoint-express';
336
+ *
337
+ * const storage = createStorageAdapter({ type: 'redis', ... });
338
+ *
339
+ * app.use(withCheckpoint({
340
+ * tenantHost: 'your.tenant.example',
341
+ * onResult: async (result, req) => {
342
+ * await storage.recordEvent({
343
+ * verdict: result.decision.kind,
344
+ * agentDid: result.agentDid,
345
+ * ts: Date.now(),
346
+ * // ... whatever the customer's event shape is.
347
+ * });
348
+ * },
349
+ * }));
350
+ *
351
+ * This is the "session/Redis observability" bucket in Dylan's
352
+ * three-bucket decision matrix (Phase E kickoff brief § addendum 2):
353
+ * preserve, refactor to consume engine `Decision` instead of legacy
354
+ * `DetectionResult`. The refactor is customer-side — they wire the
355
+ * adapter into `onResult` themselves. The engine doesn't need to know.
356
+ *
357
+ * @deprecated Throws with a migration message. Compose `withCheckpoint`
358
+ * with the storage adapter directly (example above).
359
+ */
360
+
361
+ /**
362
+ * Enhanced middleware configuration (legacy). Kept as a type export so
363
+ * customers' config types keep type-checking through the migration
364
+ * window; the factory function below throws at runtime.
365
+ */
366
+ interface EnhancedMiddlewareConfig {
367
+ /** Storage configuration. */
368
+ storage?: StorageConfig;
369
+ /** Session tracking configuration. */
370
+ sessionTracking?: {
371
+ enabled?: boolean;
372
+ ttl?: number;
373
+ };
374
+ /** Paths to skip detection. */
375
+ skipPaths?: string[];
376
+ /** Action when agent detected. */
377
+ onAgentDetected?: 'block' | 'log' | 'allow';
378
+ /** Custom handler for agent detection. */
379
+ onDetection?: (result: DetectionResult, req: Request) => void | Promise<void>;
380
+ /** Confidence threshold. */
381
+ confidenceThreshold?: number;
382
+ /** Response when blocking. */
383
+ blockedResponse?: {
384
+ status?: number;
385
+ message?: string;
386
+ };
387
+ /** Enable WASM (if available). */
388
+ enableWasm?: boolean;
389
+ }
390
+ /**
391
+ * @deprecated Throws with a migration message. Compose `withCheckpoint`
392
+ * with `createStorageAdapter` via the `onResult` callback.
393
+ */
394
+ declare function createEnhancedAgentShieldMiddleware(_config?: EnhancedMiddlewareConfig): void;
395
+
396
+ /**
397
+ * In-memory storage adapter for Express
398
+ * Useful for development and testing
399
+ */
400
+
401
+ declare class MemoryStorageAdapter implements StorageAdapter {
402
+ private events;
403
+ private sessions;
404
+ private eventTimeline;
405
+ private maxEventsPerSession;
406
+ private maxTimelineEvents;
407
+ storeEvent(event: AgentDetectionEvent): Promise<void>;
408
+ storeSession(session: AgentSession): Promise<void>;
409
+ getEvents(sessionId: string, limit?: number): Promise<AgentDetectionEvent[]>;
410
+ getSession(sessionId: string): Promise<AgentSession | null>;
411
+ getRecentEvents(limit?: number): Promise<AgentDetectionEvent[]>;
412
+ getActiveSessions(limit?: number): Promise<AgentSession[]>;
413
+ cleanup(before: Date): Promise<void>;
414
+ }
415
+
416
+ /**
417
+ * Redis storage adapter for Express
418
+ * Provides persistent storage for production environments
419
+ */
420
+
421
+ interface RedisClient {
422
+ set(key: string, value: string, options?: {
423
+ ex?: number;
424
+ }): Promise<string>;
425
+ get(key: string): Promise<string | null>;
426
+ zadd(key: string, ...args: Array<number | string>): Promise<number>;
427
+ zrange(key: string, start: number, stop: number): Promise<string[]>;
428
+ zrevrange(key: string, start: number, stop: number): Promise<string[]>;
429
+ expire(key: string, seconds: number): Promise<number>;
430
+ del(key: string): Promise<number>;
431
+ keys(pattern: string): Promise<string[]>;
432
+ }
433
+ declare class RedisStorageAdapter implements StorageAdapter {
434
+ private redis;
435
+ private prefix;
436
+ private ttl;
437
+ constructor(redis: RedisClient, ttl?: number);
438
+ private getKey;
439
+ storeEvent(event: AgentDetectionEvent): Promise<void>;
440
+ storeSession(session: AgentSession): Promise<void>;
441
+ getEvents(sessionId: string, limit?: number): Promise<AgentDetectionEvent[]>;
442
+ getSession(sessionId: string): Promise<AgentSession | null>;
443
+ getRecentEvents(limit?: number): Promise<AgentDetectionEvent[]>;
444
+ getActiveSessions(limit?: number): Promise<AgentSession[]>;
445
+ cleanup(before: Date): Promise<void>;
446
+ }
447
+
448
+ /**
449
+ * Storage adapter exports and factory for Express
450
+ */
451
+
452
+ /**
453
+ * Create a storage adapter based on configuration
454
+ */
455
+ declare function createStorageAdapter(config?: StorageConfig): Promise<StorageAdapter>;
456
+
457
+ /**
458
+ * @fileoverview Checkpoint Express Middleware — engine-backed AI agent
459
+ * detection and MCP-I verification.
460
+ *
461
+ * @license MIT OR Apache-2.0
462
+ */
463
+
464
+ /**
465
+ * Library version
466
+ */
467
+ declare const VERSION = "1.0.0";
468
+
469
+ export { type AgentDetectionEvent, type AgentSession, type AgentShieldMiddleware, type AgentShieldRequest, type CheckpointConfig, type EnhancedMiddlewareConfig, type ExpressMiddlewareConfig, ExpressSessionTracker, MemoryStorageAdapter, RedisStorageAdapter, type SessionData, type StorageAdapter, type StorageConfig, VERSION, agentShield, createAgentShieldMiddleware, createEnhancedAgentShieldMiddleware, createStorageAdapter, withCheckpoint, withSessionTracking };