@kya-os/checkpoint-nextjs 1.2.0 → 1.7.1
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.
- package/CHANGELOG.md +159 -0
- package/EDGE_RUNTIME_WASM_SETUP.md +1 -1
- package/bin/setup-edge-wasm.js +1 -1
- package/dist/api-middleware.d.mts +9 -1
- package/dist/api-middleware.d.ts +9 -1
- package/dist/api-middleware.js +14 -4
- package/dist/api-middleware.mjs +15 -5
- package/dist/composed-policy.d.mts +115 -0
- package/dist/composed-policy.d.ts +115 -0
- package/dist/composed-policy.js +102 -0
- package/dist/composed-policy.mjs +96 -0
- package/dist/config-DAwIA4DB.d.mts +214 -0
- package/dist/config-DyU4l5er.d.ts +214 -0
- package/dist/create-middleware.js +0 -2
- package/dist/create-middleware.mjs +0 -2
- package/dist/edge-runtime-loader.js +3 -1
- package/dist/edge-runtime-loader.mjs +3 -1
- package/dist/edge-wasm-middleware.d.mts +6 -6
- package/dist/edge-wasm-middleware.d.ts +6 -6
- package/dist/index.d.mts +6 -14
- package/dist/index.d.ts +6 -14
- package/dist/index.js +191 -13
- package/dist/index.mjs +192 -14
- package/dist/middleware-edge.d.mts +7 -3
- package/dist/middleware-edge.d.ts +7 -3
- package/dist/middleware-edge.js +174 -4
- package/dist/middleware-edge.mjs +171 -4
- package/dist/middleware-node.d.mts +39 -116
- package/dist/middleware-node.d.ts +39 -116
- package/dist/middleware-node.js +181 -4
- package/dist/middleware-node.mjs +178 -5
- package/dist/middleware.d.mts +10 -1
- package/dist/middleware.d.ts +10 -1
- package/dist/middleware.js +6 -0
- package/dist/middleware.mjs +6 -1
- package/dist/nodejs-wasm-loader.d.mts +3 -4
- package/dist/nodejs-wasm-loader.d.ts +3 -4
- package/dist/nodejs-wasm-loader.js +1 -1
- package/dist/nodejs-wasm-loader.mjs +1 -1
- package/dist/wasm-setup.js +1 -1
- package/dist/wasm-setup.mjs +1 -1
- package/package.json +4 -9
- package/dist/.tsbuildinfo +0 -1
- package/dist/signature-verifier.d.mts +0 -33
- package/dist/signature-verifier.d.ts +0 -33
- package/dist/signature-verifier.js +0 -384
- package/dist/signature-verifier.mjs +0 -360
- package/dist/wasm-middleware.d.mts +0 -98
- package/dist/wasm-middleware.d.ts +0 -98
- package/dist/wasm-middleware.js +0 -125
- package/dist/wasm-middleware.mjs +0 -121
- package/templates/middleware-wasm-100.ts +0 -161
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { VERSION, withCheckpoint } from './middleware-node.js';
|
|
2
2
|
export { createAgentShieldMiddleware, createAgentShieldMiddleware as createMiddleware } from './create-middleware.js';
|
|
3
3
|
export { AgentDetectionEvent, AgentSession, AgentShieldMiddlewareConfig, CheckpointApiMiddlewareConfig, EnhancedMiddlewareConfig, StorageAdapter, StorageConfig, agentShieldMiddleware, createEnhancedAgentShieldMiddleware, withAgentShield, withCheckpointApi } from './api-middleware.js';
|
|
4
4
|
export { createAgentShieldMiddleware as createAgentShieldMiddlewareBase } from './middleware.js';
|
|
@@ -6,20 +6,12 @@ export { EdgeSessionTracker, SessionData, SessionTrackingConfig, StatelessSessio
|
|
|
6
6
|
export { AgentShieldClient, AgentShieldClientConfig, CheckpointApiClient, CheckpointApiClientConfig, EnforceInput, EnforceResponse, EnforcementDecision, LogDetectionInput, getAgentShieldClient, getCheckpointApiClient, resetAgentShieldClient, resetCheckpointApiClient } from './api-client.js';
|
|
7
7
|
export { A as AgentShieldRequest, D as DetectionContext, N as NextJSMiddlewareConfig } from './types-D9RQvPNy.js';
|
|
8
8
|
export { NextJSPolicyMiddlewareConfig, PolicyMiddlewareConfig, applyPolicy, buildBlockedResponse as buildPolicyBlockedResponse, buildRedirectResponse as buildPolicyRedirectResponse, createContextFromDetection, evaluatePolicyForDetection, getPolicy, handlePolicyDecision } from './policy.js';
|
|
9
|
+
export { C as CheckpointConfig } from './config-DyU4l5er.js';
|
|
9
10
|
export { DEFAULT_POLICY, ENFORCEMENT_ACTIONS, EnforcementAction, PolicyConfig, PolicyEvaluationContext, PolicyEvaluationResult, createEvaluationContext, evaluatePolicy } from '@kya-os/checkpoint-shared';
|
|
11
|
+
import '@kya-os/checkpoint-wasm-runtime/engine';
|
|
10
12
|
import '@kya-os/checkpoint-wasm-runtime/adapters';
|
|
11
13
|
import 'next/server';
|
|
12
|
-
import '@kya-os/checkpoint-wasm-runtime/
|
|
14
|
+
import '@kya-os/checkpoint-wasm-runtime/reporter';
|
|
15
|
+
import './composed-policy.js';
|
|
16
|
+
import '@kya-os/checkpoint-wasm-runtime/composed-policy';
|
|
13
17
|
import '@kya-os/checkpoint';
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* @fileoverview Checkpoint Next.js Integration
|
|
17
|
-
* @license MIT OR Apache-2.0
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Library version
|
|
22
|
-
*/
|
|
23
|
-
declare const VERSION = "0.1.0";
|
|
24
|
-
|
|
25
|
-
export { VERSION };
|
package/dist/index.js
CHANGED
|
@@ -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
|
|
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,96 @@ 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 {
|
|
104
|
+
decision: outcome.engineDecision,
|
|
105
|
+
acted: true,
|
|
106
|
+
// Echo the SSOT revision pin with the decision it produced (#3246
|
|
107
|
+
// PR5). Conditional spread: absent upstream stays absent here.
|
|
108
|
+
...policy.policySourceHash ? { composedBlobHash: policy.policySourceHash } : {}
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
return structured;
|
|
112
|
+
},
|
|
113
|
+
async trustedDelegationRoots() {
|
|
114
|
+
const policy = await fetcher.getPolicy(projectId);
|
|
115
|
+
return policy.trustedDelegationRoots ?? [];
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
async function resolveTrustedDelegationRootsForRequest(resolver, headers) {
|
|
120
|
+
if (!resolver) return void 0;
|
|
121
|
+
if (!checkpointShared.requestCarriesDelegationProof(headers)) return void 0;
|
|
122
|
+
const roots = await resolver();
|
|
123
|
+
return roots.length > 0 ? roots : void 0;
|
|
124
|
+
}
|
|
125
|
+
async function applyComposedPolicy(context, result, path) {
|
|
126
|
+
if (!context) return;
|
|
127
|
+
try {
|
|
128
|
+
const outcome = await context.apply(result, path);
|
|
129
|
+
if (outcome.acted) {
|
|
130
|
+
result.decision = outcome.decision;
|
|
131
|
+
if (outcome.composedBlobHash) {
|
|
132
|
+
result.composedBlobHash = outcome.composedBlobHash;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
} catch {
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
var consoleComposedPolicyLogger = {
|
|
139
|
+
shadowDivergence(info) {
|
|
140
|
+
console.warn("[checkpoint/composed-policy] shadow-divergence", info);
|
|
141
|
+
},
|
|
142
|
+
evaluationError(projectId, error) {
|
|
143
|
+
console.error(
|
|
144
|
+
`[checkpoint/composed-policy] evaluation failed for ${projectId}; using structured decision:`,
|
|
145
|
+
error
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
};
|
|
57
149
|
|
|
58
150
|
// src/translate.ts
|
|
59
151
|
async function nextRequestToHttpLike(req, opts = {}) {
|
|
@@ -98,15 +190,96 @@ function extractRemoteAddress(req) {
|
|
|
98
190
|
// src/middleware-node.ts
|
|
99
191
|
function withCheckpoint(config) {
|
|
100
192
|
const opts = buildVerifyOpts(config);
|
|
193
|
+
const reporter = buildReporter(config);
|
|
194
|
+
const composed = buildComposedContext(config);
|
|
195
|
+
const trustedRootsResolver = buildTrustedRootsResolver(config, composed);
|
|
101
196
|
const translateOpts = { drainJsonBody: config.drainJsonBody };
|
|
102
|
-
return async function checkpointMiddleware(req) {
|
|
197
|
+
return async function checkpointMiddleware(req, event) {
|
|
103
198
|
const httpLike = await nextRequestToHttpLike(req, translateOpts);
|
|
104
|
-
const
|
|
199
|
+
const trustedDelegationRoots = await resolveTrustedDelegationRootsForRequest(
|
|
200
|
+
trustedRootsResolver,
|
|
201
|
+
req.headers
|
|
202
|
+
);
|
|
203
|
+
const result = await orchestrator.verifyRequest(
|
|
204
|
+
httpLike,
|
|
205
|
+
trustedDelegationRoots ? { ...opts, trustedDelegationRoots } : opts
|
|
206
|
+
);
|
|
207
|
+
await applyComposedPolicy(composed, result, req.nextUrl.pathname);
|
|
208
|
+
if (reporter) {
|
|
209
|
+
const reportPromise = reporter(result, extractReporterContext(req));
|
|
210
|
+
if (event) {
|
|
211
|
+
event.waitUntil(reportPromise);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
105
214
|
await dispatchOnResult(config, result, req);
|
|
106
|
-
const
|
|
215
|
+
const bodyReadable200 = checkpointShared.selectBodyReadable200(
|
|
216
|
+
config.delegationChallengeMode ?? "spec-401",
|
|
217
|
+
req.headers,
|
|
218
|
+
result.detectionDetail.detectionClass
|
|
219
|
+
);
|
|
220
|
+
const rendered = orchestrator.renderDecisionAsResponse(result, { bodyReadable200 });
|
|
107
221
|
return adaptToNextResponse(rendered, req);
|
|
108
222
|
};
|
|
109
223
|
}
|
|
224
|
+
var SDK_NAME = "@kya-os/checkpoint-nextjs";
|
|
225
|
+
var VERSION = "1.7.1";
|
|
226
|
+
function buildReporter(config, runtime = "node") {
|
|
227
|
+
if (!config.apiKey) return null;
|
|
228
|
+
return reporter.makeDetectionReporter({
|
|
229
|
+
apiKey: config.apiKey,
|
|
230
|
+
baseUrl: config.baseUrl,
|
|
231
|
+
debug: config.debug,
|
|
232
|
+
// Self-identify (incl. node-vs-edge) so the dashboard can version-gate
|
|
233
|
+
// enforcement. Next.js EDGE composed enforcement is opt-in (needs
|
|
234
|
+
// `cedarWasmModule`), so the dashboard shows it as opt-in, never "Enforcing".
|
|
235
|
+
sdk: { name: SDK_NAME, version: VERSION, runtime }
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
function buildTrustedRootsResolver(config, composed) {
|
|
239
|
+
if (composed?.trustedDelegationRoots) {
|
|
240
|
+
return () => composed.trustedDelegationRoots();
|
|
241
|
+
}
|
|
242
|
+
if (!config.projectId) return null;
|
|
243
|
+
const fetcher = new checkpointShared.PolicyFetcher({
|
|
244
|
+
apiBaseUrl: config.dashboardUrl ?? config.baseUrl ?? DEFAULT_DASHBOARD_URL,
|
|
245
|
+
apiKey: config.apiKey,
|
|
246
|
+
cacheTtlSeconds: config.policyCacheTtlSeconds
|
|
247
|
+
});
|
|
248
|
+
const projectId = config.projectId;
|
|
249
|
+
return async () => (await fetcher.getPolicy(projectId)).trustedDelegationRoots ?? [];
|
|
250
|
+
}
|
|
251
|
+
function buildComposedContext(config) {
|
|
252
|
+
if (config.composedPolicyEnforcer) return config.composedPolicyEnforcer;
|
|
253
|
+
if (!config.projectId) return null;
|
|
254
|
+
return makeComposedPolicyContext({
|
|
255
|
+
projectId: config.projectId,
|
|
256
|
+
fetcher: new checkpointShared.PolicyFetcher({
|
|
257
|
+
apiBaseUrl: config.dashboardUrl ?? config.baseUrl ?? DEFAULT_DASHBOARD_URL,
|
|
258
|
+
apiKey: config.apiKey,
|
|
259
|
+
cacheTtlSeconds: config.policyCacheTtlSeconds
|
|
260
|
+
}),
|
|
261
|
+
// LAZY dynamic import — NOT a top-level `import` — so the node-only
|
|
262
|
+
// `./policy` glue (`createRequire`/`fs` at module load) is never pulled into
|
|
263
|
+
// the Edge bundle. `middleware-edge.ts` imports helpers from this file, so a
|
|
264
|
+
// top-level `./policy` import would surface as a side-effect import in the
|
|
265
|
+
// edge bundle and boot-fail on Vercel edge. The import is cached after first
|
|
266
|
+
// call; the core's single-flight cache wraps the (now async) compile.
|
|
267
|
+
compile: async (_language, source) => {
|
|
268
|
+
const { createPolicyEvaluator } = await import('@kya-os/checkpoint-wasm-runtime/policy');
|
|
269
|
+
return createPolicyEvaluator(source);
|
|
270
|
+
},
|
|
271
|
+
logger: config.debug ? consoleComposedPolicyLogger : void 0
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
function extractReporterContext(req) {
|
|
275
|
+
return {
|
|
276
|
+
userAgent: req.headers.get("user-agent") ?? void 0,
|
|
277
|
+
ipAddress: req.headers.get("x-forwarded-for")?.split(",")[0]?.trim() ?? req.headers.get("x-real-ip") ?? void 0,
|
|
278
|
+
path: req.nextUrl.pathname,
|
|
279
|
+
url: req.nextUrl.href,
|
|
280
|
+
method: req.method
|
|
281
|
+
};
|
|
282
|
+
}
|
|
110
283
|
function buildVerifyOpts(config) {
|
|
111
284
|
const overrides = config.adapters ?? {};
|
|
112
285
|
return {
|
|
@@ -130,8 +303,6 @@ async function dispatchOnResult(config, result, req) {
|
|
|
130
303
|
} catch {
|
|
131
304
|
}
|
|
132
305
|
}
|
|
133
|
-
|
|
134
|
-
// src/middleware.ts
|
|
135
306
|
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.";
|
|
136
307
|
function createAgentShieldMiddleware(_config = {}) {
|
|
137
308
|
throw new Error(MIGRATION_ERROR);
|
|
@@ -397,7 +568,7 @@ function safeHostname(url) {
|
|
|
397
568
|
// src/responses/agent-instruction.ts
|
|
398
569
|
var MCP_I_DOCS_URL = "https://docs.knowthat.ai/mcp-i/getting-started";
|
|
399
570
|
var DEFAULT_CONNECT_PATH = "/connect";
|
|
400
|
-
function buildAgentInstructionResponse(request, decision, redirectUrl) {
|
|
571
|
+
function buildAgentInstructionResponse(request, decision, redirectUrl, options) {
|
|
401
572
|
const resolved = resolveUrl(redirectUrl ?? DEFAULT_CONNECT_PATH, request.url);
|
|
402
573
|
const agentName = decision.agentName || decision.agentType || "unknown";
|
|
403
574
|
if (!resolved.searchParams.has("agent")) {
|
|
@@ -448,8 +619,11 @@ It only takes a moment and you won't need to do it again. Once you're done, ask
|
|
|
448
619
|
confidence: decision.confidence
|
|
449
620
|
}
|
|
450
621
|
};
|
|
451
|
-
const
|
|
452
|
-
response.
|
|
622
|
+
const bodyReadable200 = options?.bodyReadable200 ?? false;
|
|
623
|
+
const response = server.NextResponse.json(body, { status: bodyReadable200 ? 200 : 401 });
|
|
624
|
+
if (!bodyReadable200) {
|
|
625
|
+
response.headers.set("WWW-Authenticate", `KYA realm="api", authorization_uri="${authUrl}"`);
|
|
626
|
+
}
|
|
453
627
|
response.headers.set(
|
|
454
628
|
"Link",
|
|
455
629
|
`<${authUrl}>; rel="kya-authorize", <${MCP_I_DOCS_URL}>; rel="help"`
|
|
@@ -642,7 +816,14 @@ function withCheckpointApi(config = {}) {
|
|
|
642
816
|
return buildRedirectResponse(request, decision, config);
|
|
643
817
|
}
|
|
644
818
|
const targetUrl = config.redirectUrl || decision.redirectUrl;
|
|
645
|
-
|
|
819
|
+
const bodyReadable200 = checkpointShared.selectBodyReadable200(
|
|
820
|
+
config.delegationChallengeMode ?? "spec-401",
|
|
821
|
+
request.headers,
|
|
822
|
+
result.data.detection?.detectionClass
|
|
823
|
+
);
|
|
824
|
+
return buildAgentInstructionResponse(request, decision, targetUrl, {
|
|
825
|
+
bodyReadable200
|
|
826
|
+
});
|
|
646
827
|
}
|
|
647
828
|
case "challenge": {
|
|
648
829
|
return buildRedirectResponse(request, decision, config);
|
|
@@ -1007,9 +1188,6 @@ async function applyPolicy(request, detection, config) {
|
|
|
1007
1188
|
);
|
|
1008
1189
|
}
|
|
1009
1190
|
}
|
|
1010
|
-
|
|
1011
|
-
// src/index.ts
|
|
1012
|
-
var VERSION = "0.1.0";
|
|
1013
1191
|
/**
|
|
1014
1192
|
* @fileoverview Checkpoint Next.js Integration
|
|
1015
1193
|
* @license MIT OR Apache-2.0
|
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
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 {
|
|
4
|
-
import { isKnownAiCrawler, createEvaluationContext, evaluatePolicy, ENFORCEMENT_ACTIONS, PolicyConfigSchema, DEFAULT_POLICY, matchPath, acceptsHtml, encodeVerdictCookie, classifyResponseShape, BLOCKED_PATH, createPolicyFetcher, VERDICT_COOKIE_NAME } from '@kya-os/checkpoint-shared';
|
|
3
|
+
import { makeDetectionReporter } from '@kya-os/checkpoint-wasm-runtime/reporter';
|
|
4
|
+
import { selectBodyReadable200, PolicyFetcher, isKnownAiCrawler, createEvaluationContext, evaluatePolicy, ENFORCEMENT_ACTIONS, PolicyConfigSchema, DEFAULT_POLICY, matchPath, requestCarriesDelegationProof, acceptsHtml, encodeVerdictCookie, classifyResponseShape, BLOCKED_PATH, createPolicyFetcher, VERDICT_COOKIE_NAME } from '@kya-os/checkpoint-shared';
|
|
5
5
|
export { DEFAULT_POLICY, ENFORCEMENT_ACTIONS, createEvaluationContext, evaluatePolicy } from '@kya-os/checkpoint-shared';
|
|
6
|
+
import { NextResponse } from 'next/server';
|
|
7
|
+
import { makeComposedPolicyCache, evaluateComposedPolicy, verifyResultToAuthorizeInput } from '@kya-os/checkpoint-wasm-runtime/composed-policy';
|
|
6
8
|
|
|
7
9
|
// src/middleware-node.ts
|
|
8
10
|
function adaptToNextResponse(rendered, req) {
|
|
@@ -53,6 +55,96 @@ function applyHeaders(res, headers) {
|
|
|
53
55
|
res.headers.set(key, value);
|
|
54
56
|
}
|
|
55
57
|
}
|
|
58
|
+
var DEFAULT_DASHBOARD_URL = "https://kya.vouched.id";
|
|
59
|
+
var NOOP_LOGGER = {
|
|
60
|
+
shadowDivergence: () => {
|
|
61
|
+
},
|
|
62
|
+
evaluationError: () => {
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
function makeComposedPolicyContext(opts) {
|
|
66
|
+
const { projectId, fetcher } = opts;
|
|
67
|
+
const cache = makeComposedPolicyCache({ compile: opts.compile, cacheMax: opts.cacheMax });
|
|
68
|
+
const logger = opts.logger ?? NOOP_LOGGER;
|
|
69
|
+
return {
|
|
70
|
+
async apply(result, path) {
|
|
71
|
+
const structured = { decision: result.decision, acted: false };
|
|
72
|
+
const policy = await fetcher.getPolicy(projectId);
|
|
73
|
+
const outcome = await evaluateComposedPolicy({
|
|
74
|
+
cache,
|
|
75
|
+
projectId,
|
|
76
|
+
flags: {
|
|
77
|
+
policyLanguage: policy.policyLanguage,
|
|
78
|
+
policySourceText: policy.policySourceText,
|
|
79
|
+
engineEnforcementEnabled: policy.engineEnforcementEnabled,
|
|
80
|
+
enabled: policy.enabled
|
|
81
|
+
},
|
|
82
|
+
authorizeInput: verifyResultToAuthorizeInput(result, { tenantId: projectId, path }),
|
|
83
|
+
baselineDecisionKind: result.decision.kind
|
|
84
|
+
});
|
|
85
|
+
if ((outcome.status === "acting" || outcome.status === "shadow") && outcome.diverged) {
|
|
86
|
+
logger.shadowDivergence({
|
|
87
|
+
projectId,
|
|
88
|
+
path,
|
|
89
|
+
engineDecision: outcome.engineDecision.kind,
|
|
90
|
+
structuredDecision: result.decision.kind,
|
|
91
|
+
detectionClass: result.detectionDetail.detectionClass.type,
|
|
92
|
+
verificationMethod: result.detectionDetail.verificationMethod,
|
|
93
|
+
confidence: result.detectionDetail.confidence,
|
|
94
|
+
agentName: result.detectionDetail.detectedAgent?.name
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
if (outcome.status === "error") {
|
|
98
|
+
logger.evaluationError(projectId, outcome.error);
|
|
99
|
+
return structured;
|
|
100
|
+
}
|
|
101
|
+
if (outcome.status === "acting") {
|
|
102
|
+
return {
|
|
103
|
+
decision: outcome.engineDecision,
|
|
104
|
+
acted: true,
|
|
105
|
+
// Echo the SSOT revision pin with the decision it produced (#3246
|
|
106
|
+
// PR5). Conditional spread: absent upstream stays absent here.
|
|
107
|
+
...policy.policySourceHash ? { composedBlobHash: policy.policySourceHash } : {}
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
return structured;
|
|
111
|
+
},
|
|
112
|
+
async trustedDelegationRoots() {
|
|
113
|
+
const policy = await fetcher.getPolicy(projectId);
|
|
114
|
+
return policy.trustedDelegationRoots ?? [];
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
async function resolveTrustedDelegationRootsForRequest(resolver, headers) {
|
|
119
|
+
if (!resolver) return void 0;
|
|
120
|
+
if (!requestCarriesDelegationProof(headers)) return void 0;
|
|
121
|
+
const roots = await resolver();
|
|
122
|
+
return roots.length > 0 ? roots : void 0;
|
|
123
|
+
}
|
|
124
|
+
async function applyComposedPolicy(context, result, path) {
|
|
125
|
+
if (!context) return;
|
|
126
|
+
try {
|
|
127
|
+
const outcome = await context.apply(result, path);
|
|
128
|
+
if (outcome.acted) {
|
|
129
|
+
result.decision = outcome.decision;
|
|
130
|
+
if (outcome.composedBlobHash) {
|
|
131
|
+
result.composedBlobHash = outcome.composedBlobHash;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
} catch {
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
var consoleComposedPolicyLogger = {
|
|
138
|
+
shadowDivergence(info) {
|
|
139
|
+
console.warn("[checkpoint/composed-policy] shadow-divergence", info);
|
|
140
|
+
},
|
|
141
|
+
evaluationError(projectId, error) {
|
|
142
|
+
console.error(
|
|
143
|
+
`[checkpoint/composed-policy] evaluation failed for ${projectId}; using structured decision:`,
|
|
144
|
+
error
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
};
|
|
56
148
|
|
|
57
149
|
// src/translate.ts
|
|
58
150
|
async function nextRequestToHttpLike(req, opts = {}) {
|
|
@@ -97,15 +189,96 @@ function extractRemoteAddress(req) {
|
|
|
97
189
|
// src/middleware-node.ts
|
|
98
190
|
function withCheckpoint(config) {
|
|
99
191
|
const opts = buildVerifyOpts(config);
|
|
192
|
+
const reporter = buildReporter(config);
|
|
193
|
+
const composed = buildComposedContext(config);
|
|
194
|
+
const trustedRootsResolver = buildTrustedRootsResolver(config, composed);
|
|
100
195
|
const translateOpts = { drainJsonBody: config.drainJsonBody };
|
|
101
|
-
return async function checkpointMiddleware(req) {
|
|
196
|
+
return async function checkpointMiddleware(req, event) {
|
|
102
197
|
const httpLike = await nextRequestToHttpLike(req, translateOpts);
|
|
103
|
-
const
|
|
198
|
+
const trustedDelegationRoots = await resolveTrustedDelegationRootsForRequest(
|
|
199
|
+
trustedRootsResolver,
|
|
200
|
+
req.headers
|
|
201
|
+
);
|
|
202
|
+
const result = await verifyRequest(
|
|
203
|
+
httpLike,
|
|
204
|
+
trustedDelegationRoots ? { ...opts, trustedDelegationRoots } : opts
|
|
205
|
+
);
|
|
206
|
+
await applyComposedPolicy(composed, result, req.nextUrl.pathname);
|
|
207
|
+
if (reporter) {
|
|
208
|
+
const reportPromise = reporter(result, extractReporterContext(req));
|
|
209
|
+
if (event) {
|
|
210
|
+
event.waitUntil(reportPromise);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
104
213
|
await dispatchOnResult(config, result, req);
|
|
105
|
-
const
|
|
214
|
+
const bodyReadable200 = selectBodyReadable200(
|
|
215
|
+
config.delegationChallengeMode ?? "spec-401",
|
|
216
|
+
req.headers,
|
|
217
|
+
result.detectionDetail.detectionClass
|
|
218
|
+
);
|
|
219
|
+
const rendered = renderDecisionAsResponse(result, { bodyReadable200 });
|
|
106
220
|
return adaptToNextResponse(rendered, req);
|
|
107
221
|
};
|
|
108
222
|
}
|
|
223
|
+
var SDK_NAME = "@kya-os/checkpoint-nextjs";
|
|
224
|
+
var VERSION = "1.7.1";
|
|
225
|
+
function buildReporter(config, runtime = "node") {
|
|
226
|
+
if (!config.apiKey) return null;
|
|
227
|
+
return makeDetectionReporter({
|
|
228
|
+
apiKey: config.apiKey,
|
|
229
|
+
baseUrl: config.baseUrl,
|
|
230
|
+
debug: config.debug,
|
|
231
|
+
// Self-identify (incl. node-vs-edge) so the dashboard can version-gate
|
|
232
|
+
// enforcement. Next.js EDGE composed enforcement is opt-in (needs
|
|
233
|
+
// `cedarWasmModule`), so the dashboard shows it as opt-in, never "Enforcing".
|
|
234
|
+
sdk: { name: SDK_NAME, version: VERSION, runtime }
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
function buildTrustedRootsResolver(config, composed) {
|
|
238
|
+
if (composed?.trustedDelegationRoots) {
|
|
239
|
+
return () => composed.trustedDelegationRoots();
|
|
240
|
+
}
|
|
241
|
+
if (!config.projectId) return null;
|
|
242
|
+
const fetcher = new PolicyFetcher({
|
|
243
|
+
apiBaseUrl: config.dashboardUrl ?? config.baseUrl ?? DEFAULT_DASHBOARD_URL,
|
|
244
|
+
apiKey: config.apiKey,
|
|
245
|
+
cacheTtlSeconds: config.policyCacheTtlSeconds
|
|
246
|
+
});
|
|
247
|
+
const projectId = config.projectId;
|
|
248
|
+
return async () => (await fetcher.getPolicy(projectId)).trustedDelegationRoots ?? [];
|
|
249
|
+
}
|
|
250
|
+
function buildComposedContext(config) {
|
|
251
|
+
if (config.composedPolicyEnforcer) return config.composedPolicyEnforcer;
|
|
252
|
+
if (!config.projectId) return null;
|
|
253
|
+
return makeComposedPolicyContext({
|
|
254
|
+
projectId: config.projectId,
|
|
255
|
+
fetcher: new PolicyFetcher({
|
|
256
|
+
apiBaseUrl: config.dashboardUrl ?? config.baseUrl ?? DEFAULT_DASHBOARD_URL,
|
|
257
|
+
apiKey: config.apiKey,
|
|
258
|
+
cacheTtlSeconds: config.policyCacheTtlSeconds
|
|
259
|
+
}),
|
|
260
|
+
// LAZY dynamic import — NOT a top-level `import` — so the node-only
|
|
261
|
+
// `./policy` glue (`createRequire`/`fs` at module load) is never pulled into
|
|
262
|
+
// the Edge bundle. `middleware-edge.ts` imports helpers from this file, so a
|
|
263
|
+
// top-level `./policy` import would surface as a side-effect import in the
|
|
264
|
+
// edge bundle and boot-fail on Vercel edge. The import is cached after first
|
|
265
|
+
// call; the core's single-flight cache wraps the (now async) compile.
|
|
266
|
+
compile: async (_language, source) => {
|
|
267
|
+
const { createPolicyEvaluator } = await import('@kya-os/checkpoint-wasm-runtime/policy');
|
|
268
|
+
return createPolicyEvaluator(source);
|
|
269
|
+
},
|
|
270
|
+
logger: config.debug ? consoleComposedPolicyLogger : void 0
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
function extractReporterContext(req) {
|
|
274
|
+
return {
|
|
275
|
+
userAgent: req.headers.get("user-agent") ?? void 0,
|
|
276
|
+
ipAddress: req.headers.get("x-forwarded-for")?.split(",")[0]?.trim() ?? req.headers.get("x-real-ip") ?? void 0,
|
|
277
|
+
path: req.nextUrl.pathname,
|
|
278
|
+
url: req.nextUrl.href,
|
|
279
|
+
method: req.method
|
|
280
|
+
};
|
|
281
|
+
}
|
|
109
282
|
function buildVerifyOpts(config) {
|
|
110
283
|
const overrides = config.adapters ?? {};
|
|
111
284
|
return {
|
|
@@ -129,8 +302,6 @@ async function dispatchOnResult(config, result, req) {
|
|
|
129
302
|
} catch {
|
|
130
303
|
}
|
|
131
304
|
}
|
|
132
|
-
|
|
133
|
-
// src/middleware.ts
|
|
134
305
|
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.";
|
|
135
306
|
function createAgentShieldMiddleware(_config = {}) {
|
|
136
307
|
throw new Error(MIGRATION_ERROR);
|
|
@@ -396,7 +567,7 @@ function safeHostname(url) {
|
|
|
396
567
|
// src/responses/agent-instruction.ts
|
|
397
568
|
var MCP_I_DOCS_URL = "https://docs.knowthat.ai/mcp-i/getting-started";
|
|
398
569
|
var DEFAULT_CONNECT_PATH = "/connect";
|
|
399
|
-
function buildAgentInstructionResponse(request, decision, redirectUrl) {
|
|
570
|
+
function buildAgentInstructionResponse(request, decision, redirectUrl, options) {
|
|
400
571
|
const resolved = resolveUrl(redirectUrl ?? DEFAULT_CONNECT_PATH, request.url);
|
|
401
572
|
const agentName = decision.agentName || decision.agentType || "unknown";
|
|
402
573
|
if (!resolved.searchParams.has("agent")) {
|
|
@@ -447,8 +618,11 @@ It only takes a moment and you won't need to do it again. Once you're done, ask
|
|
|
447
618
|
confidence: decision.confidence
|
|
448
619
|
}
|
|
449
620
|
};
|
|
450
|
-
const
|
|
451
|
-
response.
|
|
621
|
+
const bodyReadable200 = options?.bodyReadable200 ?? false;
|
|
622
|
+
const response = NextResponse.json(body, { status: bodyReadable200 ? 200 : 401 });
|
|
623
|
+
if (!bodyReadable200) {
|
|
624
|
+
response.headers.set("WWW-Authenticate", `KYA realm="api", authorization_uri="${authUrl}"`);
|
|
625
|
+
}
|
|
452
626
|
response.headers.set(
|
|
453
627
|
"Link",
|
|
454
628
|
`<${authUrl}>; rel="kya-authorize", <${MCP_I_DOCS_URL}>; rel="help"`
|
|
@@ -641,7 +815,14 @@ function withCheckpointApi(config = {}) {
|
|
|
641
815
|
return buildRedirectResponse(request, decision, config);
|
|
642
816
|
}
|
|
643
817
|
const targetUrl = config.redirectUrl || decision.redirectUrl;
|
|
644
|
-
|
|
818
|
+
const bodyReadable200 = selectBodyReadable200(
|
|
819
|
+
config.delegationChallengeMode ?? "spec-401",
|
|
820
|
+
request.headers,
|
|
821
|
+
result.data.detection?.detectionClass
|
|
822
|
+
);
|
|
823
|
+
return buildAgentInstructionResponse(request, decision, targetUrl, {
|
|
824
|
+
bodyReadable200
|
|
825
|
+
});
|
|
645
826
|
}
|
|
646
827
|
case "challenge": {
|
|
647
828
|
return buildRedirectResponse(request, decision, config);
|
|
@@ -1006,9 +1187,6 @@ async function applyPolicy(request, detection, config) {
|
|
|
1006
1187
|
);
|
|
1007
1188
|
}
|
|
1008
1189
|
}
|
|
1009
|
-
|
|
1010
|
-
// src/index.ts
|
|
1011
|
-
var VERSION = "0.1.0";
|
|
1012
1190
|
/**
|
|
1013
1191
|
* @fileoverview Checkpoint Next.js Integration
|
|
1014
1192
|
* @license MIT OR Apache-2.0
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
import { NextRequest, NextResponse } from 'next/server';
|
|
1
|
+
import { NextRequest, NextFetchEvent, NextResponse } from 'next/server';
|
|
2
2
|
export { initEngineEdge } from '@kya-os/checkpoint-wasm-runtime/orchestrator/edge';
|
|
3
|
-
|
|
3
|
+
export { initPolicyEvaluatorEdge } from '@kya-os/checkpoint-wasm-runtime/policy-edge';
|
|
4
|
+
import { C as CheckpointConfig } from './config-DAwIA4DB.mjs';
|
|
4
5
|
import '@kya-os/checkpoint-wasm-runtime/adapters';
|
|
5
6
|
import '@kya-os/checkpoint-wasm-runtime/engine';
|
|
7
|
+
import '@kya-os/checkpoint-shared';
|
|
8
|
+
import './composed-policy.mjs';
|
|
9
|
+
import '@kya-os/checkpoint-wasm-runtime/composed-policy';
|
|
6
10
|
|
|
7
11
|
/**
|
|
8
12
|
* D.3 — Edge-runtime Next.js middleware entry.
|
|
@@ -41,6 +45,6 @@ import '@kya-os/checkpoint-wasm-runtime/engine';
|
|
|
41
45
|
* factory closure is being built. The first request awaits the same
|
|
42
46
|
* promise; subsequent requests resolve sync.
|
|
43
47
|
*/
|
|
44
|
-
declare function withCheckpoint(config: CheckpointConfig): (req: NextRequest) => Promise<NextResponse>;
|
|
48
|
+
declare function withCheckpoint(config: CheckpointConfig): (req: NextRequest, event?: NextFetchEvent) => Promise<NextResponse>;
|
|
45
49
|
|
|
46
50
|
export { CheckpointConfig, withCheckpoint };
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
import { NextRequest, NextResponse } from 'next/server';
|
|
1
|
+
import { NextRequest, NextFetchEvent, NextResponse } from 'next/server';
|
|
2
2
|
export { initEngineEdge } from '@kya-os/checkpoint-wasm-runtime/orchestrator/edge';
|
|
3
|
-
|
|
3
|
+
export { initPolicyEvaluatorEdge } from '@kya-os/checkpoint-wasm-runtime/policy-edge';
|
|
4
|
+
import { C as CheckpointConfig } from './config-DyU4l5er.js';
|
|
4
5
|
import '@kya-os/checkpoint-wasm-runtime/adapters';
|
|
5
6
|
import '@kya-os/checkpoint-wasm-runtime/engine';
|
|
7
|
+
import '@kya-os/checkpoint-shared';
|
|
8
|
+
import './composed-policy.js';
|
|
9
|
+
import '@kya-os/checkpoint-wasm-runtime/composed-policy';
|
|
6
10
|
|
|
7
11
|
/**
|
|
8
12
|
* D.3 — Edge-runtime Next.js middleware entry.
|
|
@@ -41,6 +45,6 @@ import '@kya-os/checkpoint-wasm-runtime/engine';
|
|
|
41
45
|
* factory closure is being built. The first request awaits the same
|
|
42
46
|
* promise; subsequent requests resolve sync.
|
|
43
47
|
*/
|
|
44
|
-
declare function withCheckpoint(config: CheckpointConfig): (req: NextRequest) => Promise<NextResponse>;
|
|
48
|
+
declare function withCheckpoint(config: CheckpointConfig): (req: NextRequest, event?: NextFetchEvent) => Promise<NextResponse>;
|
|
45
49
|
|
|
46
50
|
export { CheckpointConfig, withCheckpoint };
|