@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.
- package/CHANGELOG.md +190 -0
- package/dist/composed-policy.d.mts +108 -0
- package/dist/composed-policy.d.ts +108 -0
- package/dist/composed-policy.js +91 -0
- package/dist/composed-policy.mjs +85 -0
- package/dist/config-_nfPN3E3.d.mts +205 -0
- package/dist/config-kxFihzR_.d.ts +205 -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 +162 -9
- package/dist/index.mjs +163 -10
- package/dist/middleware-edge.d.mts +7 -3
- package/dist/middleware-edge.d.ts +7 -3
- package/dist/middleware-edge.js +159 -4
- package/dist/middleware-edge.mjs +156 -4
- package/dist/middleware-node.d.mts +39 -101
- package/dist/middleware-node.d.ts +39 -101
- package/dist/middleware-node.js +166 -4
- package/dist/middleware-node.mjs +163 -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/signature-verifier.js +2 -2
- package/dist/signature-verifier.mjs +2 -2
- package/dist/wasm-setup.js +1 -1
- package/dist/wasm-setup.mjs +1 -1
- package/package.json +8 -11
- 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
|
@@ -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 {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
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 {
|
|
63
|
+
export { CheckpointConfig, VERSION, buildReporter as _buildReporter, buildTrustedRootsResolver as _buildTrustedRootsResolver, buildVerifyOpts as _buildVerifyOpts, extractReporterContext as _extractReporterContext, withCheckpoint };
|
package/dist/middleware-node.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,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
|
|
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;
|
package/dist/middleware-node.mjs
CHANGED
|
@@ -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 {
|
|
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
|
|
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 };
|
package/dist/middleware.d.mts
CHANGED
|
@@ -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 };
|
package/dist/middleware.d.ts
CHANGED
|
@@ -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 };
|
package/dist/middleware.js
CHANGED
|
@@ -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;
|
package/dist/middleware.mjs
CHANGED
|
@@ -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
|
-
*
|
|
6
|
-
*
|
|
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
|