@kya-os/checkpoint-wasm-runtime 1.1.1 → 1.1.2

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.
@@ -1,8 +1,203 @@
1
- import { V as VerifyResult } from './types-D0j85fF0.mjs';
2
- import { V as VerifyRequestOpts, I as IncomingHttpLike } from './render-decision-C1a-iuiW.mjs';
3
- export { B as BuildAgentRequestOpts, R as RenderedResponse, b as buildAgentRequest, e as extractAgentDid, a as extractCredentialStatusUrl, c as extractIssuer, h as hasMalformedJwsBody, r as renderDecisionAsResponse } from './render-decision-C1a-iuiW.mjs';
1
+ import { d as DidDocument, D as Decision, E as EnforcementMode, V as VerifyResult, A as AgentRequest } from './types-D0j85fF0.mjs';
4
2
  import '@kya-os/checkpoint-shared';
5
- import './adapters.mjs';
3
+
4
+ /**
5
+ * DidResolver adapter — sub-phase B.1.
6
+ *
7
+ * Resolves `did:key:*` (in-memory multibase decode) and `did:web:*`
8
+ * (HTTPS fetch of `.well-known/did.json`) into the engine's
9
+ * `DidDocument` shape. The async half does any required I/O; the
10
+ * adapter's output is plain data that the host wrapper bundles into
11
+ * `ContextSpec.didDocs` before calling `engineVerify`.
12
+ *
13
+ * Phase 1 supports only Ed25519 verification methods (the engine's
14
+ * `KeyType` is `#[non_exhaustive]` with `Ed25519` as its single
15
+ * variant). Non-Ed25519 methods on a resolved doc are silently
16
+ * filtered out — the engine wouldn't accept them as a valid Stage 2
17
+ * key match anyway.
18
+ *
19
+ * **`kid` for `did:key`**: the verification-method id is
20
+ * `<did>#<multibase>` per mcp-i-core PR #16. **NOT** `<did>#keys-1`.
21
+ * H-1's `stage2_did_key_fragment_resolution` test pins this.
22
+ */
23
+
24
+ interface DidResolverAdapter {
25
+ resolve(did: string): Promise<DidDocument>;
26
+ }
27
+
28
+ interface StatusListCacheAdapter {
29
+ /**
30
+ * Fetch the status list at `url`, decode it, and return the sorted
31
+ * set of revoked credential indices.
32
+ *
33
+ * @throws {StatusListUnavailable} on transport / non-2xx response.
34
+ * @throws {StatusListTimeout} when the fetch budget is exceeded.
35
+ * @throws {MalformedStatusList} when the VC is unparseable.
36
+ */
37
+ fetch(url: string): Promise<number[]>;
38
+ }
39
+
40
+ /**
41
+ * ReputationOracle adapter — sub-phase B.3.
42
+ *
43
+ * Resolves a per-DID reputation score in `[0.0, 1.0]`. Phase 1 wires
44
+ * an optional HTTP endpoint (Argus). Unavailable / misconfigured /
45
+ * out-of-range responses degrade to a baseline score so infrastructure
46
+ * blips can't silently DOS Adobe-class traffic. The engine's Stage 6
47
+ * compares the returned score against the tenant-configured threshold
48
+ * (Phase B.4 builds the threshold via `PolicyEvaluator`).
49
+ *
50
+ * **Degrade-to-trust** (Phase B § 4.5). Reputation is best-effort;
51
+ * Argus outages do not block traffic. The adapter returns the
52
+ * configured baseline (default 1.0 = "no signal, treat as trusted")
53
+ * and logs loudly. The engine's `LowReputation` block fires only
54
+ * when score < threshold from tenant policy; baseline-1.0 ensures
55
+ * no traffic is silently rejected just because Argus went down.
56
+ */
57
+ interface ReputationOracleAdapter {
58
+ /**
59
+ * Return the agent's reputation in `[0.0, 1.0]`. Higher is better.
60
+ *
61
+ * **Does not throw.** Network / parse / range failures degrade to
62
+ * the baseline + log; the engine should never see a thrown error
63
+ * from this adapter.
64
+ */
65
+ score(agentDid: string): Promise<number>;
66
+ }
67
+
68
+ /**
69
+ * PolicyEvaluator adapter — sub-phase B.4.
70
+ *
71
+ * Computes the **tenant verdict** on the JS side and hands the engine
72
+ * a constant `Decision` for the WASM `WasmConstantPolicy` to echo as
73
+ * Stage 7's contribution. The engine's cross-stage priority order
74
+ * (locked in D-design § 6 row 4) handles how Stage 7 interacts with
75
+ * Stages 2 / 3 / 4 / 5.
76
+ *
77
+ * **Sync-engine / async-host invariant.** The JS adapter is async (it
78
+ * fetches tenant policy from the Checkpoint dashboard); the engine
79
+ * sees only the resolved `Decision`. Cedar-1 will replace this
80
+ * adapter's implementation without touching the surrounding orchestrator.
81
+ *
82
+ * **Cedar-1 forward-compat.** [`PolicyEvaluatorAdapter`] is the
83
+ * Cedar-swappable interface: `(input) → Decision`. The Phase 1 stub
84
+ * is a reputation-threshold check; Cedar-1 will replace the
85
+ * implementation, not the surface. Don't bake Cedar internals into
86
+ * this seam.
87
+ *
88
+ * **Degrade-to-permit.** Per Phase B § 4.5: when the dashboard policy
89
+ * endpoint is unreachable, fall back to `defaultPolicy` (caller-supplied)
90
+ * or `permit-by-default`. Loud log; no silent block.
91
+ */
92
+
93
+ /**
94
+ * Pre-fetched inputs the JS host knows before calling the engine.
95
+ * Phase 1's evaluator only consumes `reputation` + `tenantHost`; later
96
+ * implementations (Cedar-1) may extend.
97
+ */
98
+ interface PolicyEvalInput {
99
+ tenantHost: string;
100
+ reputation: number;
101
+ }
102
+ interface PolicyEvaluatorAdapter {
103
+ evaluate(input: PolicyEvalInput): Promise<Decision>;
104
+ }
105
+
106
+ /**
107
+ * Clock adapter — sub-phase B.5.
108
+ *
109
+ * Provides the Unix-seconds timestamp the engine's `Clock` trait needs
110
+ * for Stage 4 expiration checks. Trivial; the trait shape exists so
111
+ * tests can inject a frozen clock (and so the engine's sync-trait
112
+ * surface is satisfied without a JS callback crossing the WASM
113
+ * boundary — `ContextSpec.nowUnix` carries the value).
114
+ */
115
+ interface ClockAdapter {
116
+ nowUnix(): number;
117
+ }
118
+
119
+ /**
120
+ * Orchestrator-layer types — Phase C, host-side only.
121
+ *
122
+ * Nothing here crosses the WASM boundary. The engine ABI types live
123
+ * in `../types.ts`; the adapter interfaces live in
124
+ * `../adapters/index.ts`. This file is the host-wrapper-facing
125
+ * surface — what Phase D (Next.js) and Phase E (Express) import.
126
+ */
127
+
128
+ /**
129
+ * Framework-agnostic HTTP request shape.
130
+ *
131
+ * Next.js / Express / Cloudflare Workers / Hono adapters marshal
132
+ * their native request type into this shape before calling
133
+ * `verifyRequest`. The shape is intentionally minimal — only what
134
+ * the engine needs to make a verdict.
135
+ */
136
+ interface IncomingHttpLike {
137
+ method: string;
138
+ /** Path + query string (no scheme + host). */
139
+ url: string;
140
+ headers: Record<string, string | string[] | undefined>;
141
+ /**
142
+ * Parsed body if the framework has already parsed it (Next.js
143
+ * with `await req.json()`, Express with `body-parser`). Falsy if
144
+ * the caller hasn't materialised the body — the orchestrator
145
+ * treats that as "no MCP-I envelope present" and routes to
146
+ * PlainHttp.
147
+ */
148
+ body?: Buffer | string | object | null;
149
+ /** Client IP if the framework surfaces one (Express `req.ip`). */
150
+ remoteAddress?: string;
151
+ }
152
+ /**
153
+ * Options the host wrapper passes per-`verifyRequest`-construction.
154
+ * The five adapters + clock + tenant identifier + enforcement mode.
155
+ */
156
+ interface VerifyRequestOpts {
157
+ didResolver: DidResolverAdapter;
158
+ statusListCache: StatusListCacheAdapter;
159
+ reputationOracle: ReputationOracleAdapter;
160
+ policyEvaluator: PolicyEvaluatorAdapter;
161
+ clock: ClockAdapter;
162
+ /** Tenant identifier — the host customer this request targets. */
163
+ tenantHost: string;
164
+ enforcementMode: EnforcementMode;
165
+ /** Returned to the PolicyEvaluator when the request has no agent DID. Default 1.0. */
166
+ reputationBaseline?: number;
167
+ /**
168
+ * **Envelope-1 (#2537) coordination flag.** Pre-Envelope-1 the TS
169
+ * bouncer ships MCP-I proofs as `{protected,payload,signature}` JSON
170
+ * in a `KYA-Delegation` header. Post-Envelope-1 they ship compact
171
+ * JWS in `_meta.proof.jws` of the body. When this flag is true the
172
+ * orchestrator also accepts the legacy header form. **Default off.**
173
+ * Delete this flag once Envelope-1 ships end-to-end.
174
+ */
175
+ legacyEnvelopeFallback?: boolean;
176
+ /**
177
+ * Argus URL — passed only so the orchestrator can detect "Argus
178
+ * not configured" at construction time and log the one-shot
179
+ * warning. The actual reputation fetch goes through `reputationOracle`.
180
+ */
181
+ argusUrl?: string;
182
+ /** Injectable for the once-only Argus configuration warning. */
183
+ logger?: (msg: string) => void;
184
+ }
185
+ /**
186
+ * Transport-agnostic response shape `renderDecisionAsResponse`
187
+ * produces. Host wrappers adapt this to their framework's response
188
+ * type (NextResponse / Express `res` / Cloudflare Response).
189
+ *
190
+ * `status === null` means "pass through" — the request continues to
191
+ * the next handler. Happens in two cases:
192
+ * 1. `Decision::Permit` (no block in Enforce mode).
193
+ * 2. **Any verdict in Observe mode** — Observe never blocks, but
194
+ * the response headers still carry the would-have-been verdict.
195
+ */
196
+ interface RenderedResponse {
197
+ status: number | null;
198
+ headers: Record<string, string>;
199
+ body?: string | object;
200
+ }
6
201
 
7
202
  /**
8
203
  * `verifyRequest` async orchestrator — Phase C.2.
@@ -46,4 +241,117 @@ declare function makeVerifyRequest(opts: VerifyRequestOpts): (req: IncomingHttpL
46
241
  */
47
242
  declare function verifyRequest(req: IncomingHttpLike, opts: VerifyRequestOpts): Promise<VerifyResult>;
48
243
 
49
- export { IncomingHttpLike, VerifyRequestOpts, makeVerifyRequest, verifyRequest };
244
+ /**
245
+ * HTTP-to-`AgentRequest` translator — Phase C.1.
246
+ *
247
+ * Detects which engine protocol the request belongs to and builds
248
+ * the typed `AgentRequest` the WASM consumes. Conservative
249
+ * detection: never escalates an ambiguous request into a higher
250
+ * verification tier. Anonymous PlainHttp is the default.
251
+ *
252
+ * **What this layer parses and what it doesn't.** It parses *only
253
+ * what's needed to drive conditional pre-fetch* — header presence,
254
+ * the MCP-I envelope's payload segment (to extract `iss` + `sub` +
255
+ * optional credentialStatus URL). It does **not** verify
256
+ * signatures, decode VC chains for revocation bits, or evaluate
257
+ * scope. Those live in the engine (H-1's parser + Stages 2-5).
258
+ *
259
+ * **Buffer-portability note.** This module uses `Buffer.from(...)` and
260
+ * `Buffer.isBuffer(...)` at multiple call sites (the JWS preflight
261
+ * `hasMalformedJwsBody`, both `tryBuildMcpIFromBody` variants, the
262
+ * legacy-header reconstitution path, and `bodyAsBytes`). All of these
263
+ * assume the Node `Buffer` global is available — provided natively by
264
+ * the Node runtime, polyfilled on Vercel Edge, and gated behind
265
+ * `nodejs_compat` on Cloudflare Workers. Bare-Edge and pure-browser
266
+ * embedders would need a `Buffer` polyfill or a refactor to
267
+ * `TextEncoder` / `Uint8Array.from`. Tracked as a follow-up since
268
+ * Phase D's Vercel Node + Vercel Edge targets are both covered today.
269
+ */
270
+
271
+ interface BuildAgentRequestOpts {
272
+ /** See `VerifyRequestOpts.legacyEnvelopeFallback`. */
273
+ legacyEnvelopeFallback?: boolean;
274
+ }
275
+ /**
276
+ * Translate an HTTP-like request into the engine's `AgentRequest`.
277
+ *
278
+ * Detection order (conservative — never escalate ambiguous input):
279
+ * 1. MCP-I L2 detached proof in `_meta.proof.jws` (spec form).
280
+ * 2. (Legacy, opt-in) MCP-I in `KYA-Delegation` header
281
+ * (Envelope-1 #2537 transition window only).
282
+ * 3. RFC 9421 HTTP Message Signatures (`Signature-Input` header).
283
+ * 4. PlainHttp (default — anonymous traffic).
284
+ */
285
+ declare function buildAgentRequest(req: IncomingHttpLike, opts?: BuildAgentRequestOpts): AgentRequest;
286
+ /**
287
+ * Preflight check — does the request body carry a `_meta.proof.jws`
288
+ * string that `parseJwsPayloadStruct` cannot project into a typed
289
+ * `McpIPayload`?
290
+ *
291
+ * The caller declared intent (an MCP-I envelope) by including the
292
+ * JWS field; structural failure to extract the payload means the
293
+ * envelope is malformed, not absent. Without this preflight, the
294
+ * orchestrator would silently fall through to PlainHttp — pre-#2560
295
+ * that happened to also Block (engine returned `Block(ParseError)`
296
+ * for every PlainHttp), so the regression was invisible; post-#2560
297
+ * the engine's Stage 1 + stub policy returns `Permit` for anonymous
298
+ * PlainHttp and tampered envelopes would be silently accepted.
299
+ *
300
+ * Returns `true` when the orchestrator should synthesize
301
+ * `Block(ParseError)` BEFORE calling `buildAgentRequest`.
302
+ */
303
+ declare function hasMalformedJwsBody(req: IncomingHttpLike): boolean;
304
+ /**
305
+ * Issuer DID — Stage 1 (identity resolution) targets this. `null`
306
+ * for PlainHttp (anonymous → no DID to resolve).
307
+ */
308
+ declare function extractIssuer(request: AgentRequest): string | null;
309
+ /**
310
+ * Agent DID — used by ReputationOracle. Defaults to `payload.sub`
311
+ * for MCP-I (subject = the agent the proof is *about*).
312
+ */
313
+ declare function extractAgentDid(request: AgentRequest): string | null;
314
+ /**
315
+ * Status-list URL for revocation pre-fetch. Pulled from the JWS
316
+ * payload's `vc.credentialStatus.id` (W3C VC Data Model 1.1).
317
+ * `null` when the envelope is L1 (no VC chain) — Stage 3 will skip.
318
+ */
319
+ declare function extractCredentialStatusUrl(request: AgentRequest): string | null;
320
+
321
+ /**
322
+ * Transport-agnostic `Decision` → HTTP renderer — Phase C.3.
323
+ *
324
+ * Translates a `VerifyResult` into a framework-neutral
325
+ * `{ status, headers, body }` shape. Phase D (Next.js) adapts this
326
+ * to `NextResponse`; Phase E (Express) adapts it to `res.status().set().send()`.
327
+ * One source of truth for the verdict→HTTP mapping.
328
+ *
329
+ * Mapping table (§ 4.5 of Phase C kickoff):
330
+ *
331
+ * | Decision | HTTP | Notes |
332
+ * |-----------------------------------|------|-----------------------------------------|
333
+ * | Permit | null | Pass through to next handler |
334
+ * | Block(Unauthenticated) | 401 | WWW-Authenticate header |
335
+ * | Block(InvalidSignature) | 403 | |
336
+ * | Block(Revoked) | 403 | |
337
+ * | Block(Expired) | 401 | Refresh-the-credential semantics |
338
+ * | Block(OutOfScope) | 403 | Body carries requested + granted |
339
+ * | Block(LowReputation) | 403 | Body carries score + threshold |
340
+ * | Block(PolicyDenied) | 403 | Body carries detail |
341
+ * | Block(ParseError) | 400 | Body carries detail |
342
+ * | Challenge | 401 | Body carries ChallengeParams |
343
+ * | Redirect | 302 | Location header |
344
+ * | Instruct | 422 | application/problem+json body |
345
+ *
346
+ * Observe mode overrides: every verdict renders as `status: null`
347
+ * (pass through) with an `X-Checkpoint-Would-Have-Been` header
348
+ * carrying the verdict kind, plus the standard attribution headers.
349
+ *
350
+ * Every response carries the Phase 0.1 attribution headers:
351
+ * `X-Checkpoint-Engine`, `X-Checkpoint-Engine-Version`, and
352
+ * (when present) `X-Checkpoint-Ruleset-Hash`.
353
+ */
354
+
355
+ declare function renderDecisionAsResponse(result: VerifyResult): RenderedResponse;
356
+
357
+ export { type BuildAgentRequestOpts, type IncomingHttpLike, type RenderedResponse, type VerifyRequestOpts, buildAgentRequest, extractAgentDid, extractCredentialStatusUrl, extractIssuer, hasMalformedJwsBody, makeVerifyRequest, renderDecisionAsResponse, verifyRequest };
@@ -1,8 +1,203 @@
1
- import { V as VerifyResult } from './types-D0j85fF0.js';
2
- import { V as VerifyRequestOpts, I as IncomingHttpLike } from './render-decision-Dsjwt96g.js';
3
- export { B as BuildAgentRequestOpts, R as RenderedResponse, b as buildAgentRequest, e as extractAgentDid, a as extractCredentialStatusUrl, c as extractIssuer, h as hasMalformedJwsBody, r as renderDecisionAsResponse } from './render-decision-Dsjwt96g.js';
1
+ import { d as DidDocument, D as Decision, E as EnforcementMode, V as VerifyResult, A as AgentRequest } from './types-D0j85fF0.js';
4
2
  import '@kya-os/checkpoint-shared';
5
- import './adapters.js';
3
+
4
+ /**
5
+ * DidResolver adapter — sub-phase B.1.
6
+ *
7
+ * Resolves `did:key:*` (in-memory multibase decode) and `did:web:*`
8
+ * (HTTPS fetch of `.well-known/did.json`) into the engine's
9
+ * `DidDocument` shape. The async half does any required I/O; the
10
+ * adapter's output is plain data that the host wrapper bundles into
11
+ * `ContextSpec.didDocs` before calling `engineVerify`.
12
+ *
13
+ * Phase 1 supports only Ed25519 verification methods (the engine's
14
+ * `KeyType` is `#[non_exhaustive]` with `Ed25519` as its single
15
+ * variant). Non-Ed25519 methods on a resolved doc are silently
16
+ * filtered out — the engine wouldn't accept them as a valid Stage 2
17
+ * key match anyway.
18
+ *
19
+ * **`kid` for `did:key`**: the verification-method id is
20
+ * `<did>#<multibase>` per mcp-i-core PR #16. **NOT** `<did>#keys-1`.
21
+ * H-1's `stage2_did_key_fragment_resolution` test pins this.
22
+ */
23
+
24
+ interface DidResolverAdapter {
25
+ resolve(did: string): Promise<DidDocument>;
26
+ }
27
+
28
+ interface StatusListCacheAdapter {
29
+ /**
30
+ * Fetch the status list at `url`, decode it, and return the sorted
31
+ * set of revoked credential indices.
32
+ *
33
+ * @throws {StatusListUnavailable} on transport / non-2xx response.
34
+ * @throws {StatusListTimeout} when the fetch budget is exceeded.
35
+ * @throws {MalformedStatusList} when the VC is unparseable.
36
+ */
37
+ fetch(url: string): Promise<number[]>;
38
+ }
39
+
40
+ /**
41
+ * ReputationOracle adapter — sub-phase B.3.
42
+ *
43
+ * Resolves a per-DID reputation score in `[0.0, 1.0]`. Phase 1 wires
44
+ * an optional HTTP endpoint (Argus). Unavailable / misconfigured /
45
+ * out-of-range responses degrade to a baseline score so infrastructure
46
+ * blips can't silently DOS Adobe-class traffic. The engine's Stage 6
47
+ * compares the returned score against the tenant-configured threshold
48
+ * (Phase B.4 builds the threshold via `PolicyEvaluator`).
49
+ *
50
+ * **Degrade-to-trust** (Phase B § 4.5). Reputation is best-effort;
51
+ * Argus outages do not block traffic. The adapter returns the
52
+ * configured baseline (default 1.0 = "no signal, treat as trusted")
53
+ * and logs loudly. The engine's `LowReputation` block fires only
54
+ * when score < threshold from tenant policy; baseline-1.0 ensures
55
+ * no traffic is silently rejected just because Argus went down.
56
+ */
57
+ interface ReputationOracleAdapter {
58
+ /**
59
+ * Return the agent's reputation in `[0.0, 1.0]`. Higher is better.
60
+ *
61
+ * **Does not throw.** Network / parse / range failures degrade to
62
+ * the baseline + log; the engine should never see a thrown error
63
+ * from this adapter.
64
+ */
65
+ score(agentDid: string): Promise<number>;
66
+ }
67
+
68
+ /**
69
+ * PolicyEvaluator adapter — sub-phase B.4.
70
+ *
71
+ * Computes the **tenant verdict** on the JS side and hands the engine
72
+ * a constant `Decision` for the WASM `WasmConstantPolicy` to echo as
73
+ * Stage 7's contribution. The engine's cross-stage priority order
74
+ * (locked in D-design § 6 row 4) handles how Stage 7 interacts with
75
+ * Stages 2 / 3 / 4 / 5.
76
+ *
77
+ * **Sync-engine / async-host invariant.** The JS adapter is async (it
78
+ * fetches tenant policy from the Checkpoint dashboard); the engine
79
+ * sees only the resolved `Decision`. Cedar-1 will replace this
80
+ * adapter's implementation without touching the surrounding orchestrator.
81
+ *
82
+ * **Cedar-1 forward-compat.** [`PolicyEvaluatorAdapter`] is the
83
+ * Cedar-swappable interface: `(input) → Decision`. The Phase 1 stub
84
+ * is a reputation-threshold check; Cedar-1 will replace the
85
+ * implementation, not the surface. Don't bake Cedar internals into
86
+ * this seam.
87
+ *
88
+ * **Degrade-to-permit.** Per Phase B § 4.5: when the dashboard policy
89
+ * endpoint is unreachable, fall back to `defaultPolicy` (caller-supplied)
90
+ * or `permit-by-default`. Loud log; no silent block.
91
+ */
92
+
93
+ /**
94
+ * Pre-fetched inputs the JS host knows before calling the engine.
95
+ * Phase 1's evaluator only consumes `reputation` + `tenantHost`; later
96
+ * implementations (Cedar-1) may extend.
97
+ */
98
+ interface PolicyEvalInput {
99
+ tenantHost: string;
100
+ reputation: number;
101
+ }
102
+ interface PolicyEvaluatorAdapter {
103
+ evaluate(input: PolicyEvalInput): Promise<Decision>;
104
+ }
105
+
106
+ /**
107
+ * Clock adapter — sub-phase B.5.
108
+ *
109
+ * Provides the Unix-seconds timestamp the engine's `Clock` trait needs
110
+ * for Stage 4 expiration checks. Trivial; the trait shape exists so
111
+ * tests can inject a frozen clock (and so the engine's sync-trait
112
+ * surface is satisfied without a JS callback crossing the WASM
113
+ * boundary — `ContextSpec.nowUnix` carries the value).
114
+ */
115
+ interface ClockAdapter {
116
+ nowUnix(): number;
117
+ }
118
+
119
+ /**
120
+ * Orchestrator-layer types — Phase C, host-side only.
121
+ *
122
+ * Nothing here crosses the WASM boundary. The engine ABI types live
123
+ * in `../types.ts`; the adapter interfaces live in
124
+ * `../adapters/index.ts`. This file is the host-wrapper-facing
125
+ * surface — what Phase D (Next.js) and Phase E (Express) import.
126
+ */
127
+
128
+ /**
129
+ * Framework-agnostic HTTP request shape.
130
+ *
131
+ * Next.js / Express / Cloudflare Workers / Hono adapters marshal
132
+ * their native request type into this shape before calling
133
+ * `verifyRequest`. The shape is intentionally minimal — only what
134
+ * the engine needs to make a verdict.
135
+ */
136
+ interface IncomingHttpLike {
137
+ method: string;
138
+ /** Path + query string (no scheme + host). */
139
+ url: string;
140
+ headers: Record<string, string | string[] | undefined>;
141
+ /**
142
+ * Parsed body if the framework has already parsed it (Next.js
143
+ * with `await req.json()`, Express with `body-parser`). Falsy if
144
+ * the caller hasn't materialised the body — the orchestrator
145
+ * treats that as "no MCP-I envelope present" and routes to
146
+ * PlainHttp.
147
+ */
148
+ body?: Buffer | string | object | null;
149
+ /** Client IP if the framework surfaces one (Express `req.ip`). */
150
+ remoteAddress?: string;
151
+ }
152
+ /**
153
+ * Options the host wrapper passes per-`verifyRequest`-construction.
154
+ * The five adapters + clock + tenant identifier + enforcement mode.
155
+ */
156
+ interface VerifyRequestOpts {
157
+ didResolver: DidResolverAdapter;
158
+ statusListCache: StatusListCacheAdapter;
159
+ reputationOracle: ReputationOracleAdapter;
160
+ policyEvaluator: PolicyEvaluatorAdapter;
161
+ clock: ClockAdapter;
162
+ /** Tenant identifier — the host customer this request targets. */
163
+ tenantHost: string;
164
+ enforcementMode: EnforcementMode;
165
+ /** Returned to the PolicyEvaluator when the request has no agent DID. Default 1.0. */
166
+ reputationBaseline?: number;
167
+ /**
168
+ * **Envelope-1 (#2537) coordination flag.** Pre-Envelope-1 the TS
169
+ * bouncer ships MCP-I proofs as `{protected,payload,signature}` JSON
170
+ * in a `KYA-Delegation` header. Post-Envelope-1 they ship compact
171
+ * JWS in `_meta.proof.jws` of the body. When this flag is true the
172
+ * orchestrator also accepts the legacy header form. **Default off.**
173
+ * Delete this flag once Envelope-1 ships end-to-end.
174
+ */
175
+ legacyEnvelopeFallback?: boolean;
176
+ /**
177
+ * Argus URL — passed only so the orchestrator can detect "Argus
178
+ * not configured" at construction time and log the one-shot
179
+ * warning. The actual reputation fetch goes through `reputationOracle`.
180
+ */
181
+ argusUrl?: string;
182
+ /** Injectable for the once-only Argus configuration warning. */
183
+ logger?: (msg: string) => void;
184
+ }
185
+ /**
186
+ * Transport-agnostic response shape `renderDecisionAsResponse`
187
+ * produces. Host wrappers adapt this to their framework's response
188
+ * type (NextResponse / Express `res` / Cloudflare Response).
189
+ *
190
+ * `status === null` means "pass through" — the request continues to
191
+ * the next handler. Happens in two cases:
192
+ * 1. `Decision::Permit` (no block in Enforce mode).
193
+ * 2. **Any verdict in Observe mode** — Observe never blocks, but
194
+ * the response headers still carry the would-have-been verdict.
195
+ */
196
+ interface RenderedResponse {
197
+ status: number | null;
198
+ headers: Record<string, string>;
199
+ body?: string | object;
200
+ }
6
201
 
7
202
  /**
8
203
  * `verifyRequest` async orchestrator — Phase C.2.
@@ -46,4 +241,117 @@ declare function makeVerifyRequest(opts: VerifyRequestOpts): (req: IncomingHttpL
46
241
  */
47
242
  declare function verifyRequest(req: IncomingHttpLike, opts: VerifyRequestOpts): Promise<VerifyResult>;
48
243
 
49
- export { IncomingHttpLike, VerifyRequestOpts, makeVerifyRequest, verifyRequest };
244
+ /**
245
+ * HTTP-to-`AgentRequest` translator — Phase C.1.
246
+ *
247
+ * Detects which engine protocol the request belongs to and builds
248
+ * the typed `AgentRequest` the WASM consumes. Conservative
249
+ * detection: never escalates an ambiguous request into a higher
250
+ * verification tier. Anonymous PlainHttp is the default.
251
+ *
252
+ * **What this layer parses and what it doesn't.** It parses *only
253
+ * what's needed to drive conditional pre-fetch* — header presence,
254
+ * the MCP-I envelope's payload segment (to extract `iss` + `sub` +
255
+ * optional credentialStatus URL). It does **not** verify
256
+ * signatures, decode VC chains for revocation bits, or evaluate
257
+ * scope. Those live in the engine (H-1's parser + Stages 2-5).
258
+ *
259
+ * **Buffer-portability note.** This module uses `Buffer.from(...)` and
260
+ * `Buffer.isBuffer(...)` at multiple call sites (the JWS preflight
261
+ * `hasMalformedJwsBody`, both `tryBuildMcpIFromBody` variants, the
262
+ * legacy-header reconstitution path, and `bodyAsBytes`). All of these
263
+ * assume the Node `Buffer` global is available — provided natively by
264
+ * the Node runtime, polyfilled on Vercel Edge, and gated behind
265
+ * `nodejs_compat` on Cloudflare Workers. Bare-Edge and pure-browser
266
+ * embedders would need a `Buffer` polyfill or a refactor to
267
+ * `TextEncoder` / `Uint8Array.from`. Tracked as a follow-up since
268
+ * Phase D's Vercel Node + Vercel Edge targets are both covered today.
269
+ */
270
+
271
+ interface BuildAgentRequestOpts {
272
+ /** See `VerifyRequestOpts.legacyEnvelopeFallback`. */
273
+ legacyEnvelopeFallback?: boolean;
274
+ }
275
+ /**
276
+ * Translate an HTTP-like request into the engine's `AgentRequest`.
277
+ *
278
+ * Detection order (conservative — never escalate ambiguous input):
279
+ * 1. MCP-I L2 detached proof in `_meta.proof.jws` (spec form).
280
+ * 2. (Legacy, opt-in) MCP-I in `KYA-Delegation` header
281
+ * (Envelope-1 #2537 transition window only).
282
+ * 3. RFC 9421 HTTP Message Signatures (`Signature-Input` header).
283
+ * 4. PlainHttp (default — anonymous traffic).
284
+ */
285
+ declare function buildAgentRequest(req: IncomingHttpLike, opts?: BuildAgentRequestOpts): AgentRequest;
286
+ /**
287
+ * Preflight check — does the request body carry a `_meta.proof.jws`
288
+ * string that `parseJwsPayloadStruct` cannot project into a typed
289
+ * `McpIPayload`?
290
+ *
291
+ * The caller declared intent (an MCP-I envelope) by including the
292
+ * JWS field; structural failure to extract the payload means the
293
+ * envelope is malformed, not absent. Without this preflight, the
294
+ * orchestrator would silently fall through to PlainHttp — pre-#2560
295
+ * that happened to also Block (engine returned `Block(ParseError)`
296
+ * for every PlainHttp), so the regression was invisible; post-#2560
297
+ * the engine's Stage 1 + stub policy returns `Permit` for anonymous
298
+ * PlainHttp and tampered envelopes would be silently accepted.
299
+ *
300
+ * Returns `true` when the orchestrator should synthesize
301
+ * `Block(ParseError)` BEFORE calling `buildAgentRequest`.
302
+ */
303
+ declare function hasMalformedJwsBody(req: IncomingHttpLike): boolean;
304
+ /**
305
+ * Issuer DID — Stage 1 (identity resolution) targets this. `null`
306
+ * for PlainHttp (anonymous → no DID to resolve).
307
+ */
308
+ declare function extractIssuer(request: AgentRequest): string | null;
309
+ /**
310
+ * Agent DID — used by ReputationOracle. Defaults to `payload.sub`
311
+ * for MCP-I (subject = the agent the proof is *about*).
312
+ */
313
+ declare function extractAgentDid(request: AgentRequest): string | null;
314
+ /**
315
+ * Status-list URL for revocation pre-fetch. Pulled from the JWS
316
+ * payload's `vc.credentialStatus.id` (W3C VC Data Model 1.1).
317
+ * `null` when the envelope is L1 (no VC chain) — Stage 3 will skip.
318
+ */
319
+ declare function extractCredentialStatusUrl(request: AgentRequest): string | null;
320
+
321
+ /**
322
+ * Transport-agnostic `Decision` → HTTP renderer — Phase C.3.
323
+ *
324
+ * Translates a `VerifyResult` into a framework-neutral
325
+ * `{ status, headers, body }` shape. Phase D (Next.js) adapts this
326
+ * to `NextResponse`; Phase E (Express) adapts it to `res.status().set().send()`.
327
+ * One source of truth for the verdict→HTTP mapping.
328
+ *
329
+ * Mapping table (§ 4.5 of Phase C kickoff):
330
+ *
331
+ * | Decision | HTTP | Notes |
332
+ * |-----------------------------------|------|-----------------------------------------|
333
+ * | Permit | null | Pass through to next handler |
334
+ * | Block(Unauthenticated) | 401 | WWW-Authenticate header |
335
+ * | Block(InvalidSignature) | 403 | |
336
+ * | Block(Revoked) | 403 | |
337
+ * | Block(Expired) | 401 | Refresh-the-credential semantics |
338
+ * | Block(OutOfScope) | 403 | Body carries requested + granted |
339
+ * | Block(LowReputation) | 403 | Body carries score + threshold |
340
+ * | Block(PolicyDenied) | 403 | Body carries detail |
341
+ * | Block(ParseError) | 400 | Body carries detail |
342
+ * | Challenge | 401 | Body carries ChallengeParams |
343
+ * | Redirect | 302 | Location header |
344
+ * | Instruct | 422 | application/problem+json body |
345
+ *
346
+ * Observe mode overrides: every verdict renders as `status: null`
347
+ * (pass through) with an `X-Checkpoint-Would-Have-Been` header
348
+ * carrying the verdict kind, plus the standard attribution headers.
349
+ *
350
+ * Every response carries the Phase 0.1 attribution headers:
351
+ * `X-Checkpoint-Engine`, `X-Checkpoint-Engine-Version`, and
352
+ * (when present) `X-Checkpoint-Ruleset-Hash`.
353
+ */
354
+
355
+ declare function renderDecisionAsResponse(result: VerifyResult): RenderedResponse;
356
+
357
+ export { type BuildAgentRequestOpts, type IncomingHttpLike, type RenderedResponse, type VerifyRequestOpts, buildAgentRequest, extractAgentDid, extractCredentialStatusUrl, extractIssuer, hasMalformedJwsBody, makeVerifyRequest, renderDecisionAsResponse, verifyRequest };