@kya-os/checkpoint-nextjs 1.1.4 → 1.7.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.
Files changed (43) hide show
  1. package/CHANGELOG.md +190 -0
  2. package/dist/composed-policy.d.mts +108 -0
  3. package/dist/composed-policy.d.ts +108 -0
  4. package/dist/composed-policy.js +91 -0
  5. package/dist/composed-policy.mjs +85 -0
  6. package/dist/config-_nfPN3E3.d.mts +205 -0
  7. package/dist/config-kxFihzR_.d.ts +205 -0
  8. package/dist/create-middleware.js +0 -2
  9. package/dist/create-middleware.mjs +0 -2
  10. package/dist/edge-runtime-loader.js +3 -1
  11. package/dist/edge-runtime-loader.mjs +3 -1
  12. package/dist/edge-wasm-middleware.d.mts +6 -6
  13. package/dist/edge-wasm-middleware.d.ts +6 -6
  14. package/dist/index.d.mts +6 -14
  15. package/dist/index.d.ts +6 -14
  16. package/dist/index.js +162 -9
  17. package/dist/index.mjs +163 -10
  18. package/dist/middleware-edge.d.mts +7 -3
  19. package/dist/middleware-edge.d.ts +7 -3
  20. package/dist/middleware-edge.js +159 -4
  21. package/dist/middleware-edge.mjs +156 -4
  22. package/dist/middleware-node.d.mts +39 -101
  23. package/dist/middleware-node.d.ts +39 -101
  24. package/dist/middleware-node.js +166 -4
  25. package/dist/middleware-node.mjs +163 -5
  26. package/dist/middleware.d.mts +10 -1
  27. package/dist/middleware.d.ts +10 -1
  28. package/dist/middleware.js +6 -0
  29. package/dist/middleware.mjs +6 -1
  30. package/dist/nodejs-wasm-loader.d.mts +3 -4
  31. package/dist/nodejs-wasm-loader.d.ts +3 -4
  32. package/dist/nodejs-wasm-loader.js +1 -1
  33. package/dist/nodejs-wasm-loader.mjs +1 -1
  34. package/dist/signature-verifier.js +2 -2
  35. package/dist/signature-verifier.mjs +2 -2
  36. package/dist/wasm-setup.js +1 -1
  37. package/dist/wasm-setup.mjs +1 -1
  38. package/package.json +8 -11
  39. package/dist/wasm-middleware.d.mts +0 -98
  40. package/dist/wasm-middleware.d.ts +0 -98
  41. package/dist/wasm-middleware.js +0 -125
  42. package/dist/wasm-middleware.mjs +0 -121
  43. package/templates/middleware-wasm-100.ts +0 -161
@@ -1,99 +1,12 @@
1
+ import * as _kya_os_checkpoint_wasm_runtime_engine from '@kya-os/checkpoint-wasm-runtime/engine';
1
2
  import * as _kya_os_checkpoint_wasm_runtime_adapters from '@kya-os/checkpoint-wasm-runtime/adapters';
2
- import { DidResolverAdapter, StatusListCacheAdapter, ReputationOracleAdapter, PolicyEvaluatorAdapter } from '@kya-os/checkpoint-wasm-runtime/adapters';
3
- import { NextRequest, NextResponse } from 'next/server';
4
- import { EnforcementMode, VerifyResult } from '@kya-os/checkpoint-wasm-runtime/engine';
3
+ import { NextRequest, NextFetchEvent, NextResponse } from 'next/server';
4
+ import { DetectionReporter, ReporterContext } from '@kya-os/checkpoint-wasm-runtime/reporter';
5
+ import { C as CheckpointConfig } from './config-kxFihzR_.js';
6
+ import { ComposedPolicyContext, TrustedDelegationRootsResolver } from './composed-policy.js';
7
+ import '@kya-os/checkpoint-wasm-runtime/composed-policy';
8
+ import '@kya-os/checkpoint-shared';
5
9
 
6
- /**
7
- * Configuration for `withCheckpoint`.
8
- *
9
- * The new minimal shape Phase D's middleware needs. Legacy
10
- * `AgentShieldMiddlewareConfig` (from `./api-middleware`) remains
11
- * exported during the deprecation window — see D.4 cutover.
12
- */
13
- interface CheckpointConfig {
14
- /**
15
- * Tenant identifier — typically the customer's dashboard hostname
16
- * (e.g. `acme.checkpoint.example`). The PolicyEvaluator uses this
17
- * to look up tenant policy from the dashboard.
18
- */
19
- tenantHost: string;
20
- /**
21
- * `'enforce'` (default) blocks; `'observe'` passes everything
22
- * through with `X-Checkpoint-Would-Have-Been` headers. Per Phase 0.2.
23
- */
24
- enforcementMode?: EnforcementMode;
25
- /**
26
- * Argus reputation oracle base URL. Omit to use the trust-by-default
27
- * baseline (reputation defaults to 1.0; orchestrator logs a one-shot
28
- * warning at first request).
29
- */
30
- argusUrl?: string;
31
- /**
32
- * Dashboard base URL for the PolicyEvaluator to fetch tenant policy
33
- * from. Omit to use the open-by-default tenant policy.
34
- */
35
- dashboardUrl?: string;
36
- /**
37
- * Returned to the PolicyEvaluator for anonymous requests (no agent
38
- * DID). Default 1.0 (trust-by-default).
39
- */
40
- reputationBaseline?: number;
41
- /**
42
- * Pre-built adapter instances. Production deployments use the
43
- * factory-built defaults from `@kya-os/checkpoint-wasm-runtime/adapters`;
44
- * tests use stubs. The factory composes any provided overrides over
45
- * defaults — partial overrides are supported.
46
- */
47
- adapters?: Partial<{
48
- didResolver: DidResolverAdapter;
49
- statusListCache: StatusListCacheAdapter;
50
- reputationOracle: ReputationOracleAdapter;
51
- policyEvaluator: PolicyEvaluatorAdapter;
52
- }>;
53
- /**
54
- * Optional callback for the post-verdict path — fires after every
55
- * verification, regardless of permit/block, with the full
56
- * `VerifyResult`. Use for logging, dashboards, telemetry. Errors
57
- * thrown here are swallowed so user code can't break the middleware
58
- * response.
59
- */
60
- onResult?: (result: VerifyResult, req: NextRequest) => void | Promise<void>;
61
- /**
62
- * Accept legacy `KYA-Delegation`-header envelope form alongside the
63
- * canonical `_meta.proof.jws` body form. Default `false`.
64
- *
65
- * **When to enable** — customers whose agents pre-date Envelope-1
66
- * (#2537) and ship MCP-I proofs as `{protected,payload,signature}`
67
- * JSON in a `KYA-Delegation` HTTP header. Post-Envelope-1 agents
68
- * ship compact JWS in the request body's `_meta.proof.jws` field;
69
- * those don't need this flag.
70
- *
71
- * Forwarded to the orchestrator's `VerifyRequestOpts.legacyEnvelopeFallback`.
72
- * Both transports (header + body) are honored when this is `true`;
73
- * the orchestrator's detection order is body first, then header
74
- * (`packages/checkpoint-wasm-runtime/src/engine/orchestrator/build-agent-request.ts`).
75
- *
76
- * SDK-Envelope-Plumbing-1 (#2594). Added in `@kya-os/checkpoint-nextjs@1.1.0`.
77
- */
78
- legacyEnvelopeFallback?: boolean;
79
- /**
80
- * Read the request body when `content-type` is `application/json` so
81
- * the orchestrator can extract an MCP-I envelope from
82
- * `_meta.proof.jws`. Default `true`.
83
- *
84
- * **When to disable** — streaming middlewares that can't tolerate
85
- * the `req.clone()` memory overhead (one full-body copy is buffered
86
- * during the read). For those, set `false` and route MCP-I
87
- * envelopes through the `KYA-Delegation` header transport instead
88
- * (requires `legacyEnvelopeFallback: true`).
89
- *
90
- * The clone preserves `req.body` for downstream handlers — disabling
91
- * is a performance optimization, not a correctness fix.
92
- *
93
- * SDK-Envelope-Plumbing-1 (#2594). Added in `@kya-os/checkpoint-nextjs@1.1.0`.
94
- */
95
- drainJsonBody?: boolean;
96
- }
97
10
  /**
98
11
  * Build the Checkpoint middleware. Returns a function `(req) => NextResponse`
99
12
  * suitable for `export default withCheckpoint({...})` in `middleware.ts`.
@@ -103,23 +16,48 @@ interface CheckpointConfig {
103
16
  * `verifyRequest`, and translates the verdict to `NextResponse`. No
104
17
  * verification logic lives in this file.
105
18
  */
106
- declare function withCheckpoint(config: CheckpointConfig): (req: NextRequest) => Promise<NextResponse>;
19
+ declare function withCheckpoint(config: CheckpointConfig): (req: NextRequest, event?: NextFetchEvent) => Promise<NextResponse>;
20
+ /**
21
+ * Installed SDK version, self-reported to the detection reporter so the
22
+ * dashboard can version-gate "composed policy enforces here". MUST equal
23
+ * `package.json` version — pinned by a unit test (the old hardcoded `0.1.0` had
24
+ * drifted). Re-exported as `VERSION` from the package index.
25
+ */
26
+ declare const VERSION = "1.7.0";
27
+ declare function buildReporter(config: CheckpointConfig, runtime?: 'node' | 'edge'): DetectionReporter | null;
28
+ /**
29
+ * Build the per-factory trusted-delegation-roots resolver (P2 / DR-1). Root
30
+ * pinning is a security concern INDEPENDENT of composed Cedar enforcement, so it
31
+ * must resolve wherever a `projectId` exists — including Edge setups that never
32
+ * wire `cedarWasmModule` (where `buildComposedContext` returns null). Prefers the
33
+ * composed context's accessor (reuses its cached fetcher / honors an injected
34
+ * enforcer); otherwise falls back to a `projectId`-only policy fetch. Returns
35
+ * `null` when no source is configured (no roots → engine open default).
36
+ */
37
+ declare function buildTrustedRootsResolver(config: CheckpointConfig, composed: ComposedPolicyContext | null): TrustedDelegationRootsResolver | null;
38
+ /**
39
+ * Pull the request context the dashboard's `/api/v1/log-detection`
40
+ * endpoint expects out of a `NextRequest`. Works under both Node and
41
+ * Edge runtimes — no runtime-specific APIs.
42
+ */
43
+ declare function extractReporterContext(req: NextRequest): ReporterContext;
107
44
  /**
108
45
  * Compose adapter defaults with caller-supplied overrides. Factored
109
46
  * out so the Edge entry (which uses the same composition) can reuse
110
47
  * the shape.
111
48
  */
112
49
  declare function buildVerifyOpts(config: CheckpointConfig): {
113
- didResolver: DidResolverAdapter;
114
- statusListCache: StatusListCacheAdapter;
115
- reputationOracle: ReputationOracleAdapter;
116
- policyEvaluator: PolicyEvaluatorAdapter;
50
+ didResolver: _kya_os_checkpoint_wasm_runtime_adapters.DidResolverAdapter;
51
+ statusListCache: _kya_os_checkpoint_wasm_runtime_adapters.StatusListCacheAdapter;
52
+ reputationOracle: _kya_os_checkpoint_wasm_runtime_adapters.ReputationOracleAdapter;
53
+ policyEvaluator: _kya_os_checkpoint_wasm_runtime_adapters.PolicyEvaluatorAdapter;
117
54
  clock: _kya_os_checkpoint_wasm_runtime_adapters.ClockAdapter;
118
55
  tenantHost: string;
119
- enforcementMode: EnforcementMode;
56
+ enforcementMode: _kya_os_checkpoint_wasm_runtime_engine.EnforcementMode;
120
57
  reputationBaseline: number | undefined;
121
58
  argusUrl: string | undefined;
122
59
  legacyEnvelopeFallback: boolean;
60
+ engineConfig: _kya_os_checkpoint_wasm_runtime_engine.EngineConfig | undefined;
123
61
  };
124
62
 
125
- export { type CheckpointConfig, buildVerifyOpts as _buildVerifyOpts, withCheckpoint };
63
+ export { CheckpointConfig, VERSION, buildReporter as _buildReporter, buildTrustedRootsResolver as _buildTrustedRootsResolver, buildVerifyOpts as _buildVerifyOpts, extractReporterContext as _extractReporterContext, withCheckpoint };
@@ -2,8 +2,10 @@
2
2
 
3
3
  var orchestrator = require('@kya-os/checkpoint-wasm-runtime/orchestrator');
4
4
  var adapters = require('@kya-os/checkpoint-wasm-runtime/adapters');
5
- var server = require('next/server');
5
+ var reporter = require('@kya-os/checkpoint-wasm-runtime/reporter');
6
6
  var checkpointShared = require('@kya-os/checkpoint-shared');
7
+ var server = require('next/server');
8
+ var composedPolicy = require('@kya-os/checkpoint-wasm-runtime/composed-policy');
7
9
 
8
10
  // src/middleware-node.ts
9
11
  function adaptToNextResponse(rendered, req) {
@@ -54,6 +56,85 @@ function applyHeaders(res, headers) {
54
56
  res.headers.set(key, value);
55
57
  }
56
58
  }
59
+ var DEFAULT_DASHBOARD_URL = "https://kya.vouched.id";
60
+ var NOOP_LOGGER = {
61
+ shadowDivergence: () => {
62
+ },
63
+ evaluationError: () => {
64
+ }
65
+ };
66
+ function makeComposedPolicyContext(opts) {
67
+ const { projectId, fetcher } = opts;
68
+ const cache = composedPolicy.makeComposedPolicyCache({ compile: opts.compile, cacheMax: opts.cacheMax });
69
+ const logger = opts.logger ?? NOOP_LOGGER;
70
+ return {
71
+ async apply(result, path) {
72
+ const structured = { decision: result.decision, acted: false };
73
+ const policy = await fetcher.getPolicy(projectId);
74
+ const outcome = await composedPolicy.evaluateComposedPolicy({
75
+ cache,
76
+ projectId,
77
+ flags: {
78
+ policyLanguage: policy.policyLanguage,
79
+ policySourceText: policy.policySourceText,
80
+ engineEnforcementEnabled: policy.engineEnforcementEnabled,
81
+ enabled: policy.enabled
82
+ },
83
+ authorizeInput: composedPolicy.verifyResultToAuthorizeInput(result, { tenantId: projectId, path }),
84
+ baselineDecisionKind: result.decision.kind
85
+ });
86
+ if ((outcome.status === "acting" || outcome.status === "shadow") && outcome.diverged) {
87
+ logger.shadowDivergence({
88
+ projectId,
89
+ path,
90
+ engineDecision: outcome.engineDecision.kind,
91
+ structuredDecision: result.decision.kind,
92
+ detectionClass: result.detectionDetail.detectionClass.type,
93
+ verificationMethod: result.detectionDetail.verificationMethod,
94
+ confidence: result.detectionDetail.confidence,
95
+ agentName: result.detectionDetail.detectedAgent?.name
96
+ });
97
+ }
98
+ if (outcome.status === "error") {
99
+ logger.evaluationError(projectId, outcome.error);
100
+ return structured;
101
+ }
102
+ if (outcome.status === "acting") {
103
+ return { decision: outcome.engineDecision, acted: true };
104
+ }
105
+ return structured;
106
+ },
107
+ async trustedDelegationRoots() {
108
+ const policy = await fetcher.getPolicy(projectId);
109
+ return policy.trustedDelegationRoots ?? [];
110
+ }
111
+ };
112
+ }
113
+ async function resolveTrustedDelegationRootsForRequest(resolver, headers) {
114
+ if (!resolver) return void 0;
115
+ if (!checkpointShared.requestCarriesDelegationProof(headers)) return void 0;
116
+ const roots = await resolver();
117
+ return roots.length > 0 ? roots : void 0;
118
+ }
119
+ async function applyComposedPolicy(context, result, path) {
120
+ if (!context) return;
121
+ try {
122
+ const outcome = await context.apply(result, path);
123
+ if (outcome.acted) result.decision = outcome.decision;
124
+ } catch {
125
+ }
126
+ }
127
+ var consoleComposedPolicyLogger = {
128
+ shadowDivergence(info) {
129
+ console.warn("[checkpoint/composed-policy] shadow-divergence", info);
130
+ },
131
+ evaluationError(projectId, error) {
132
+ console.error(
133
+ `[checkpoint/composed-policy] evaluation failed for ${projectId}; using structured decision:`,
134
+ error
135
+ );
136
+ }
137
+ };
57
138
 
58
139
  // src/translate.ts
59
140
  async function nextRequestToHttpLike(req, opts = {}) {
@@ -98,15 +179,91 @@ function extractRemoteAddress(req) {
98
179
  // src/middleware-node.ts
99
180
  function withCheckpoint(config) {
100
181
  const opts = buildVerifyOpts(config);
182
+ const reporter = buildReporter(config);
183
+ const composed = buildComposedContext(config);
184
+ const trustedRootsResolver = buildTrustedRootsResolver(config, composed);
101
185
  const translateOpts = { drainJsonBody: config.drainJsonBody };
102
- return async function checkpointMiddleware(req) {
186
+ return async function checkpointMiddleware(req, event) {
103
187
  const httpLike = await nextRequestToHttpLike(req, translateOpts);
104
- const result = await orchestrator.verifyRequest(httpLike, opts);
188
+ const trustedDelegationRoots = await resolveTrustedDelegationRootsForRequest(
189
+ trustedRootsResolver,
190
+ req.headers
191
+ );
192
+ const result = await orchestrator.verifyRequest(
193
+ httpLike,
194
+ trustedDelegationRoots ? { ...opts, trustedDelegationRoots } : opts
195
+ );
196
+ await applyComposedPolicy(composed, result, req.nextUrl.pathname);
197
+ if (reporter) {
198
+ const reportPromise = reporter(result, extractReporterContext(req));
199
+ if (event) {
200
+ event.waitUntil(reportPromise);
201
+ }
202
+ }
105
203
  await dispatchOnResult(config, result, req);
106
204
  const rendered = orchestrator.renderDecisionAsResponse(result);
107
205
  return adaptToNextResponse(rendered, req);
108
206
  };
109
207
  }
208
+ var SDK_NAME = "@kya-os/checkpoint-nextjs";
209
+ var VERSION = "1.7.0";
210
+ function buildReporter(config, runtime = "node") {
211
+ if (!config.apiKey) return null;
212
+ return reporter.makeDetectionReporter({
213
+ apiKey: config.apiKey,
214
+ baseUrl: config.baseUrl,
215
+ debug: config.debug,
216
+ // Self-identify (incl. node-vs-edge) so the dashboard can version-gate
217
+ // enforcement. Next.js EDGE composed enforcement is opt-in (needs
218
+ // `cedarWasmModule`), so the dashboard shows it as opt-in, never "Enforcing".
219
+ sdk: { name: SDK_NAME, version: VERSION, runtime }
220
+ });
221
+ }
222
+ function buildTrustedRootsResolver(config, composed) {
223
+ if (composed?.trustedDelegationRoots) {
224
+ return () => composed.trustedDelegationRoots();
225
+ }
226
+ if (!config.projectId) return null;
227
+ const fetcher = new checkpointShared.PolicyFetcher({
228
+ apiBaseUrl: config.dashboardUrl ?? config.baseUrl ?? DEFAULT_DASHBOARD_URL,
229
+ apiKey: config.apiKey,
230
+ cacheTtlSeconds: config.policyCacheTtlSeconds
231
+ });
232
+ const projectId = config.projectId;
233
+ return async () => (await fetcher.getPolicy(projectId)).trustedDelegationRoots ?? [];
234
+ }
235
+ function buildComposedContext(config) {
236
+ if (config.composedPolicyEnforcer) return config.composedPolicyEnforcer;
237
+ if (!config.projectId) return null;
238
+ return makeComposedPolicyContext({
239
+ projectId: config.projectId,
240
+ fetcher: new checkpointShared.PolicyFetcher({
241
+ apiBaseUrl: config.dashboardUrl ?? config.baseUrl ?? DEFAULT_DASHBOARD_URL,
242
+ apiKey: config.apiKey,
243
+ cacheTtlSeconds: config.policyCacheTtlSeconds
244
+ }),
245
+ // LAZY dynamic import — NOT a top-level `import` — so the node-only
246
+ // `./policy` glue (`createRequire`/`fs` at module load) is never pulled into
247
+ // the Edge bundle. `middleware-edge.ts` imports helpers from this file, so a
248
+ // top-level `./policy` import would surface as a side-effect import in the
249
+ // edge bundle and boot-fail on Vercel edge. The import is cached after first
250
+ // call; the core's single-flight cache wraps the (now async) compile.
251
+ compile: async (_language, source) => {
252
+ const { createPolicyEvaluator } = await import('@kya-os/checkpoint-wasm-runtime/policy');
253
+ return createPolicyEvaluator(source);
254
+ },
255
+ logger: config.debug ? consoleComposedPolicyLogger : void 0
256
+ });
257
+ }
258
+ function extractReporterContext(req) {
259
+ return {
260
+ userAgent: req.headers.get("user-agent") ?? void 0,
261
+ ipAddress: req.headers.get("x-forwarded-for")?.split(",")[0]?.trim() ?? req.headers.get("x-real-ip") ?? void 0,
262
+ path: req.nextUrl.pathname,
263
+ url: req.nextUrl.href,
264
+ method: req.method
265
+ };
266
+ }
110
267
  function buildVerifyOpts(config) {
111
268
  const overrides = config.adapters ?? {};
112
269
  return {
@@ -119,7 +276,8 @@ function buildVerifyOpts(config) {
119
276
  enforcementMode: config.enforcementMode ?? "enforce",
120
277
  reputationBaseline: config.reputationBaseline,
121
278
  argusUrl: config.argusUrl,
122
- legacyEnvelopeFallback: config.legacyEnvelopeFallback ?? false
279
+ legacyEnvelopeFallback: config.legacyEnvelopeFallback ?? false,
280
+ engineConfig: config.engineConfig
123
281
  };
124
282
  }
125
283
  async function dispatchOnResult(config, result, req) {
@@ -130,5 +288,9 @@ async function dispatchOnResult(config, result, req) {
130
288
  }
131
289
  }
132
290
 
291
+ exports.VERSION = VERSION;
292
+ exports._buildReporter = buildReporter;
293
+ exports._buildTrustedRootsResolver = buildTrustedRootsResolver;
133
294
  exports._buildVerifyOpts = buildVerifyOpts;
295
+ exports._extractReporterContext = extractReporterContext;
134
296
  exports.withCheckpoint = withCheckpoint;
@@ -1,7 +1,9 @@
1
1
  import { verifyRequest, renderDecisionAsResponse } from '@kya-os/checkpoint-wasm-runtime/orchestrator';
2
2
  import { makeSystemClock, makePolicyEvaluator, makeReputationOracle, makeStatusListCache, makeDidResolver } from '@kya-os/checkpoint-wasm-runtime/adapters';
3
+ import { makeDetectionReporter } from '@kya-os/checkpoint-wasm-runtime/reporter';
4
+ import { PolicyFetcher, requestCarriesDelegationProof, acceptsHtml, encodeVerdictCookie, classifyResponseShape, BLOCKED_PATH, VERDICT_COOKIE_NAME } from '@kya-os/checkpoint-shared';
3
5
  import { NextResponse } from 'next/server';
4
- import { acceptsHtml, encodeVerdictCookie, classifyResponseShape, BLOCKED_PATH, VERDICT_COOKIE_NAME } from '@kya-os/checkpoint-shared';
6
+ import { makeComposedPolicyCache, evaluateComposedPolicy, verifyResultToAuthorizeInput } from '@kya-os/checkpoint-wasm-runtime/composed-policy';
5
7
 
6
8
  // src/middleware-node.ts
7
9
  function adaptToNextResponse(rendered, req) {
@@ -52,6 +54,85 @@ function applyHeaders(res, headers) {
52
54
  res.headers.set(key, value);
53
55
  }
54
56
  }
57
+ var DEFAULT_DASHBOARD_URL = "https://kya.vouched.id";
58
+ var NOOP_LOGGER = {
59
+ shadowDivergence: () => {
60
+ },
61
+ evaluationError: () => {
62
+ }
63
+ };
64
+ function makeComposedPolicyContext(opts) {
65
+ const { projectId, fetcher } = opts;
66
+ const cache = makeComposedPolicyCache({ compile: opts.compile, cacheMax: opts.cacheMax });
67
+ const logger = opts.logger ?? NOOP_LOGGER;
68
+ return {
69
+ async apply(result, path) {
70
+ const structured = { decision: result.decision, acted: false };
71
+ const policy = await fetcher.getPolicy(projectId);
72
+ const outcome = await evaluateComposedPolicy({
73
+ cache,
74
+ projectId,
75
+ flags: {
76
+ policyLanguage: policy.policyLanguage,
77
+ policySourceText: policy.policySourceText,
78
+ engineEnforcementEnabled: policy.engineEnforcementEnabled,
79
+ enabled: policy.enabled
80
+ },
81
+ authorizeInput: verifyResultToAuthorizeInput(result, { tenantId: projectId, path }),
82
+ baselineDecisionKind: result.decision.kind
83
+ });
84
+ if ((outcome.status === "acting" || outcome.status === "shadow") && outcome.diverged) {
85
+ logger.shadowDivergence({
86
+ projectId,
87
+ path,
88
+ engineDecision: outcome.engineDecision.kind,
89
+ structuredDecision: result.decision.kind,
90
+ detectionClass: result.detectionDetail.detectionClass.type,
91
+ verificationMethod: result.detectionDetail.verificationMethod,
92
+ confidence: result.detectionDetail.confidence,
93
+ agentName: result.detectionDetail.detectedAgent?.name
94
+ });
95
+ }
96
+ if (outcome.status === "error") {
97
+ logger.evaluationError(projectId, outcome.error);
98
+ return structured;
99
+ }
100
+ if (outcome.status === "acting") {
101
+ return { decision: outcome.engineDecision, acted: true };
102
+ }
103
+ return structured;
104
+ },
105
+ async trustedDelegationRoots() {
106
+ const policy = await fetcher.getPolicy(projectId);
107
+ return policy.trustedDelegationRoots ?? [];
108
+ }
109
+ };
110
+ }
111
+ async function resolveTrustedDelegationRootsForRequest(resolver, headers) {
112
+ if (!resolver) return void 0;
113
+ if (!requestCarriesDelegationProof(headers)) return void 0;
114
+ const roots = await resolver();
115
+ return roots.length > 0 ? roots : void 0;
116
+ }
117
+ async function applyComposedPolicy(context, result, path) {
118
+ if (!context) return;
119
+ try {
120
+ const outcome = await context.apply(result, path);
121
+ if (outcome.acted) result.decision = outcome.decision;
122
+ } catch {
123
+ }
124
+ }
125
+ var consoleComposedPolicyLogger = {
126
+ shadowDivergence(info) {
127
+ console.warn("[checkpoint/composed-policy] shadow-divergence", info);
128
+ },
129
+ evaluationError(projectId, error) {
130
+ console.error(
131
+ `[checkpoint/composed-policy] evaluation failed for ${projectId}; using structured decision:`,
132
+ error
133
+ );
134
+ }
135
+ };
55
136
 
56
137
  // src/translate.ts
57
138
  async function nextRequestToHttpLike(req, opts = {}) {
@@ -96,15 +177,91 @@ function extractRemoteAddress(req) {
96
177
  // src/middleware-node.ts
97
178
  function withCheckpoint(config) {
98
179
  const opts = buildVerifyOpts(config);
180
+ const reporter = buildReporter(config);
181
+ const composed = buildComposedContext(config);
182
+ const trustedRootsResolver = buildTrustedRootsResolver(config, composed);
99
183
  const translateOpts = { drainJsonBody: config.drainJsonBody };
100
- return async function checkpointMiddleware(req) {
184
+ return async function checkpointMiddleware(req, event) {
101
185
  const httpLike = await nextRequestToHttpLike(req, translateOpts);
102
- const result = await verifyRequest(httpLike, opts);
186
+ const trustedDelegationRoots = await resolveTrustedDelegationRootsForRequest(
187
+ trustedRootsResolver,
188
+ req.headers
189
+ );
190
+ const result = await verifyRequest(
191
+ httpLike,
192
+ trustedDelegationRoots ? { ...opts, trustedDelegationRoots } : opts
193
+ );
194
+ await applyComposedPolicy(composed, result, req.nextUrl.pathname);
195
+ if (reporter) {
196
+ const reportPromise = reporter(result, extractReporterContext(req));
197
+ if (event) {
198
+ event.waitUntil(reportPromise);
199
+ }
200
+ }
103
201
  await dispatchOnResult(config, result, req);
104
202
  const rendered = renderDecisionAsResponse(result);
105
203
  return adaptToNextResponse(rendered, req);
106
204
  };
107
205
  }
206
+ var SDK_NAME = "@kya-os/checkpoint-nextjs";
207
+ var VERSION = "1.7.0";
208
+ function buildReporter(config, runtime = "node") {
209
+ if (!config.apiKey) return null;
210
+ return makeDetectionReporter({
211
+ apiKey: config.apiKey,
212
+ baseUrl: config.baseUrl,
213
+ debug: config.debug,
214
+ // Self-identify (incl. node-vs-edge) so the dashboard can version-gate
215
+ // enforcement. Next.js EDGE composed enforcement is opt-in (needs
216
+ // `cedarWasmModule`), so the dashboard shows it as opt-in, never "Enforcing".
217
+ sdk: { name: SDK_NAME, version: VERSION, runtime }
218
+ });
219
+ }
220
+ function buildTrustedRootsResolver(config, composed) {
221
+ if (composed?.trustedDelegationRoots) {
222
+ return () => composed.trustedDelegationRoots();
223
+ }
224
+ if (!config.projectId) return null;
225
+ const fetcher = new PolicyFetcher({
226
+ apiBaseUrl: config.dashboardUrl ?? config.baseUrl ?? DEFAULT_DASHBOARD_URL,
227
+ apiKey: config.apiKey,
228
+ cacheTtlSeconds: config.policyCacheTtlSeconds
229
+ });
230
+ const projectId = config.projectId;
231
+ return async () => (await fetcher.getPolicy(projectId)).trustedDelegationRoots ?? [];
232
+ }
233
+ function buildComposedContext(config) {
234
+ if (config.composedPolicyEnforcer) return config.composedPolicyEnforcer;
235
+ if (!config.projectId) return null;
236
+ return makeComposedPolicyContext({
237
+ projectId: config.projectId,
238
+ fetcher: new PolicyFetcher({
239
+ apiBaseUrl: config.dashboardUrl ?? config.baseUrl ?? DEFAULT_DASHBOARD_URL,
240
+ apiKey: config.apiKey,
241
+ cacheTtlSeconds: config.policyCacheTtlSeconds
242
+ }),
243
+ // LAZY dynamic import — NOT a top-level `import` — so the node-only
244
+ // `./policy` glue (`createRequire`/`fs` at module load) is never pulled into
245
+ // the Edge bundle. `middleware-edge.ts` imports helpers from this file, so a
246
+ // top-level `./policy` import would surface as a side-effect import in the
247
+ // edge bundle and boot-fail on Vercel edge. The import is cached after first
248
+ // call; the core's single-flight cache wraps the (now async) compile.
249
+ compile: async (_language, source) => {
250
+ const { createPolicyEvaluator } = await import('@kya-os/checkpoint-wasm-runtime/policy');
251
+ return createPolicyEvaluator(source);
252
+ },
253
+ logger: config.debug ? consoleComposedPolicyLogger : void 0
254
+ });
255
+ }
256
+ function extractReporterContext(req) {
257
+ return {
258
+ userAgent: req.headers.get("user-agent") ?? void 0,
259
+ ipAddress: req.headers.get("x-forwarded-for")?.split(",")[0]?.trim() ?? req.headers.get("x-real-ip") ?? void 0,
260
+ path: req.nextUrl.pathname,
261
+ url: req.nextUrl.href,
262
+ method: req.method
263
+ };
264
+ }
108
265
  function buildVerifyOpts(config) {
109
266
  const overrides = config.adapters ?? {};
110
267
  return {
@@ -117,7 +274,8 @@ function buildVerifyOpts(config) {
117
274
  enforcementMode: config.enforcementMode ?? "enforce",
118
275
  reputationBaseline: config.reputationBaseline,
119
276
  argusUrl: config.argusUrl,
120
- legacyEnvelopeFallback: config.legacyEnvelopeFallback ?? false
277
+ legacyEnvelopeFallback: config.legacyEnvelopeFallback ?? false,
278
+ engineConfig: config.engineConfig
121
279
  };
122
280
  }
123
281
  async function dispatchOnResult(config, result, req) {
@@ -128,4 +286,4 @@ async function dispatchOnResult(config, result, req) {
128
286
  }
129
287
  }
130
288
 
131
- export { buildVerifyOpts as _buildVerifyOpts, withCheckpoint };
289
+ export { VERSION, buildReporter as _buildReporter, buildTrustedRootsResolver as _buildTrustedRootsResolver, buildVerifyOpts as _buildVerifyOpts, extractReporterContext as _extractReporterContext, withCheckpoint };
@@ -32,5 +32,14 @@ declare function createAgentShieldMiddleware(_config?: Partial<NextJSMiddlewareC
32
32
  * Migrate to `withCheckpoint`.
33
33
  */
34
34
  declare function agentShield(config?: Partial<NextJSMiddlewareConfig>): (request: NextRequest) => Promise<NextResponse>;
35
+ /**
36
+ * Pass-through export required by Next.js 16+ middleware file validation.
37
+ * Next.js requires any file named `middleware.ts` to export a function named
38
+ * `middleware` or a default function. This stub satisfies that constraint so
39
+ * consumers that still reference the `./middleware` subpath can build.
40
+ *
41
+ * @deprecated Migrate to `withCheckpoint` from `@kya-os/checkpoint-nextjs`.
42
+ */
43
+ declare function middleware(_request: NextRequest): NextResponse<unknown>;
35
44
 
36
- export { agentShield, createAgentShieldMiddleware };
45
+ export { agentShield, createAgentShieldMiddleware, middleware };
@@ -32,5 +32,14 @@ declare function createAgentShieldMiddleware(_config?: Partial<NextJSMiddlewareC
32
32
  * Migrate to `withCheckpoint`.
33
33
  */
34
34
  declare function agentShield(config?: Partial<NextJSMiddlewareConfig>): (request: NextRequest) => Promise<NextResponse>;
35
+ /**
36
+ * Pass-through export required by Next.js 16+ middleware file validation.
37
+ * Next.js requires any file named `middleware.ts` to export a function named
38
+ * `middleware` or a default function. This stub satisfies that constraint so
39
+ * consumers that still reference the `./middleware` subpath can build.
40
+ *
41
+ * @deprecated Migrate to `withCheckpoint` from `@kya-os/checkpoint-nextjs`.
42
+ */
43
+ declare function middleware(_request: NextRequest): NextResponse<unknown>;
35
44
 
36
- export { agentShield, createAgentShieldMiddleware };
45
+ export { agentShield, createAgentShieldMiddleware, middleware };
@@ -1,5 +1,7 @@
1
1
  'use strict';
2
2
 
3
+ var server = require('next/server');
4
+
3
5
  // src/middleware.ts
4
6
  var MIGRATION_ERROR = "@kya-os/checkpoint-nextjs's `createAgentShieldMiddleware` / `agentShield` were deleted in Phase D (engine consolidation). The 600-line TS pattern matcher that backed them is gone. Migrate to `withCheckpoint` from `@kya-os/checkpoint-nextjs` (Node runtime) or `@kya-os/checkpoint-nextjs/edge` (Edge runtime). See packages/checkpoint-nextjs/CHANGELOG.md (1.0.0) for the recipe.";
5
7
  function createAgentShieldMiddleware(_config = {}) {
@@ -8,6 +10,10 @@ function createAgentShieldMiddleware(_config = {}) {
8
10
  function agentShield(config = {}) {
9
11
  return createAgentShieldMiddleware(config);
10
12
  }
13
+ function middleware(_request) {
14
+ return server.NextResponse.next();
15
+ }
11
16
 
12
17
  exports.agentShield = agentShield;
13
18
  exports.createAgentShieldMiddleware = createAgentShieldMiddleware;
19
+ exports.middleware = middleware;
@@ -1,3 +1,5 @@
1
+ import { NextResponse } from 'next/server';
2
+
1
3
  // src/middleware.ts
2
4
  var MIGRATION_ERROR = "@kya-os/checkpoint-nextjs's `createAgentShieldMiddleware` / `agentShield` were deleted in Phase D (engine consolidation). The 600-line TS pattern matcher that backed them is gone. Migrate to `withCheckpoint` from `@kya-os/checkpoint-nextjs` (Node runtime) or `@kya-os/checkpoint-nextjs/edge` (Edge runtime). See packages/checkpoint-nextjs/CHANGELOG.md (1.0.0) for the recipe.";
3
5
  function createAgentShieldMiddleware(_config = {}) {
@@ -6,5 +8,8 @@ function createAgentShieldMiddleware(_config = {}) {
6
8
  function agentShield(config = {}) {
7
9
  return createAgentShieldMiddleware(config);
8
10
  }
11
+ function middleware(_request) {
12
+ return NextResponse.next();
13
+ }
9
14
 
10
- export { agentShield, createAgentShieldMiddleware };
15
+ export { agentShield, createAgentShieldMiddleware, middleware };
@@ -1,10 +1,9 @@
1
1
  /**
2
2
  * @deprecated Phase-D.9a — legacy Node.js WASM loader for the retired
3
3
  * `agentshield-wasm` Rust crate. This file used `fs.readFileSync` to
4
- * locate + load the legacy detector's WASM binary into the
5
- * `@kya-os/checkpoint` `AgentDetector` class via `setWasmModule`. Both
6
- * the WASM crate (Phase-D.9a/D.9b) and the AgentDetector class
7
- * (AgentDetector-Deletion-2, next minor) are slated for deletion.
4
+ * locate + load the legacy detector's WASM binary into the legacy
5
+ * detection class. Both the WASM crate (Phase-D.9a/D.9b) and the
6
+ * detection class (removed in AgentDetector-Deletion-2) are retired.
8
7
  *
9
8
  * Migrate to `withCheckpoint` from `@kya-os/checkpoint-nextjs` — it
10
9
  * loads the canonical `kya-os-engine` WASM automatically via