@kya-os/mcp-i-cloudflare 1.9.6 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/dist/adapter.d.ts.map +1 -1
  2. package/dist/adapter.js +47 -16
  3. package/dist/adapter.js.map +1 -1
  4. package/dist/agent.d.ts +9 -0
  5. package/dist/agent.d.ts.map +1 -1
  6. package/dist/agent.js +203 -53
  7. package/dist/agent.js.map +1 -1
  8. package/dist/app.d.ts.map +1 -1
  9. package/dist/app.js +86 -3
  10. package/dist/app.js.map +1 -1
  11. package/dist/config.d.ts +54 -0
  12. package/dist/config.d.ts.map +1 -1
  13. package/dist/config.js +58 -0
  14. package/dist/config.js.map +1 -1
  15. package/dist/constants/storage-keys.d.ts +6 -0
  16. package/dist/constants/storage-keys.d.ts.map +1 -1
  17. package/dist/constants/storage-keys.js +6 -0
  18. package/dist/constants/storage-keys.js.map +1 -1
  19. package/dist/delegation-http/anchor-do.d.ts +84 -0
  20. package/dist/delegation-http/anchor-do.d.ts.map +1 -0
  21. package/dist/delegation-http/anchor-do.js +253 -0
  22. package/dist/delegation-http/anchor-do.js.map +1 -0
  23. package/dist/delegation-http/anchor-routing.d.ts +20 -0
  24. package/dist/delegation-http/anchor-routing.d.ts.map +1 -0
  25. package/dist/delegation-http/anchor-routing.js +27 -0
  26. package/dist/delegation-http/anchor-routing.js.map +1 -0
  27. package/dist/delegation-http/bearer-retry.d.ts +54 -0
  28. package/dist/delegation-http/bearer-retry.d.ts.map +1 -0
  29. package/dist/delegation-http/bearer-retry.js +195 -0
  30. package/dist/delegation-http/bearer-retry.js.map +1 -0
  31. package/dist/delegation-http/challenge-middleware.d.ts +28 -0
  32. package/dist/delegation-http/challenge-middleware.d.ts.map +1 -0
  33. package/dist/delegation-http/challenge-middleware.js +190 -0
  34. package/dist/delegation-http/challenge-middleware.js.map +1 -0
  35. package/dist/delegation-http/challenge.d.ts +86 -0
  36. package/dist/delegation-http/challenge.d.ts.map +1 -0
  37. package/dist/delegation-http/challenge.js +173 -0
  38. package/dist/delegation-http/challenge.js.map +1 -0
  39. package/dist/delegation-http/consent-anchor-bridge.d.ts +35 -0
  40. package/dist/delegation-http/consent-anchor-bridge.d.ts.map +1 -0
  41. package/dist/delegation-http/consent-anchor-bridge.js +355 -0
  42. package/dist/delegation-http/consent-anchor-bridge.js.map +1 -0
  43. package/dist/delegation-http/constants.d.ts +51 -0
  44. package/dist/delegation-http/constants.d.ts.map +1 -0
  45. package/dist/delegation-http/constants.js +60 -0
  46. package/dist/delegation-http/constants.js.map +1 -0
  47. package/dist/delegation-http/hash.d.ts +21 -0
  48. package/dist/delegation-http/hash.d.ts.map +1 -0
  49. package/dist/delegation-http/hash.js +92 -0
  50. package/dist/delegation-http/hash.js.map +1 -0
  51. package/dist/delegation-http/pickup-routes.d.ts +42 -0
  52. package/dist/delegation-http/pickup-routes.d.ts.map +1 -0
  53. package/dist/delegation-http/pickup-routes.js +79 -0
  54. package/dist/delegation-http/pickup-routes.js.map +1 -0
  55. package/dist/delegation-http/register.d.ts +19 -0
  56. package/dist/delegation-http/register.d.ts.map +1 -0
  57. package/dist/delegation-http/register.js +72 -0
  58. package/dist/delegation-http/register.js.map +1 -0
  59. package/dist/delegation-http/sid.d.ts +20 -0
  60. package/dist/delegation-http/sid.d.ts.map +1 -0
  61. package/dist/delegation-http/sid.js +32 -0
  62. package/dist/delegation-http/sid.js.map +1 -0
  63. package/dist/delegation-http/types.d.ts +64 -0
  64. package/dist/delegation-http/types.d.ts.map +1 -0
  65. package/dist/delegation-http/types.js +11 -0
  66. package/dist/delegation-http/types.js.map +1 -0
  67. package/dist/helpers/env-mapper.d.ts.map +1 -1
  68. package/dist/helpers/env-mapper.js +24 -0
  69. package/dist/helpers/env-mapper.js.map +1 -1
  70. package/dist/index.d.ts +5 -2
  71. package/dist/index.d.ts.map +1 -1
  72. package/dist/index.js +10 -2
  73. package/dist/index.js.map +1 -1
  74. package/dist/providers/crypto.d.ts +1 -1
  75. package/dist/providers/crypto.d.ts.map +1 -1
  76. package/dist/providers/crypto.js +1 -1
  77. package/dist/providers/crypto.js.map +1 -1
  78. package/dist/providers/kv-identity.d.ts +2 -2
  79. package/dist/providers/kv-identity.d.ts.map +1 -1
  80. package/dist/providers/kv-identity.js +1 -1
  81. package/dist/providers/kv-identity.js.map +1 -1
  82. package/dist/providers/storage.d.ts +1 -1
  83. package/dist/providers/storage.d.ts.map +1 -1
  84. package/dist/providers/storage.js +1 -1
  85. package/dist/providers/storage.js.map +1 -1
  86. package/dist/runtime/audit-logger.d.ts +1 -1
  87. package/dist/runtime/audit-logger.d.ts.map +1 -1
  88. package/dist/runtime/oauth-handler.d.ts.map +1 -1
  89. package/dist/runtime/oauth-handler.js +31 -3
  90. package/dist/runtime/oauth-handler.js.map +1 -1
  91. package/dist/runtime/oidc/authorize-url.d.ts +19 -0
  92. package/dist/runtime/oidc/authorize-url.d.ts.map +1 -0
  93. package/dist/runtime/oidc/authorize-url.js +94 -0
  94. package/dist/runtime/oidc/authorize-url.js.map +1 -0
  95. package/dist/runtime/oidc/token-exchange.d.ts +62 -0
  96. package/dist/runtime/oidc/token-exchange.d.ts.map +1 -0
  97. package/dist/runtime/oidc/token-exchange.js +75 -0
  98. package/dist/runtime/oidc/token-exchange.js.map +1 -0
  99. package/dist/runtime.d.ts +1 -1
  100. package/dist/runtime.d.ts.map +1 -1
  101. package/dist/services/consent-audit.service.d.ts +1 -1
  102. package/dist/services/consent-audit.service.d.ts.map +1 -1
  103. package/dist/services/consent-audit.service.js.map +1 -1
  104. package/dist/services/consent.service.d.ts +2 -2
  105. package/dist/services/consent.service.d.ts.map +1 -1
  106. package/dist/services/consent.service.js +48 -59
  107. package/dist/services/consent.service.js.map +1 -1
  108. package/dist/services/delegation.service.d.ts +9 -0
  109. package/dist/services/delegation.service.d.ts.map +1 -1
  110. package/dist/services/delegation.service.js +21 -7
  111. package/dist/services/delegation.service.js.map +1 -1
  112. package/dist/services/oauth-security.service.d.ts.map +1 -1
  113. package/dist/services/oauth-security.service.js +6 -10
  114. package/dist/services/oauth-security.service.js.map +1 -1
  115. package/dist/services/provider-loader.service.js +1 -1
  116. package/dist/services/provider-loader.service.js.map +1 -1
  117. package/dist/types.d.ts +5 -1
  118. package/dist/types.d.ts.map +1 -1
  119. package/dist/utils/known-clients.d.ts.map +1 -1
  120. package/dist/utils/known-clients.js +19 -10
  121. package/dist/utils/known-clients.js.map +1 -1
  122. package/package.json +3 -1
@@ -0,0 +1,253 @@
1
+ /**
2
+ * draft-kya-http-02 delegation layer — pickup-anchor durable object.
3
+ *
4
+ * Implements the §13 anchor state machine: create → PENDING (or PINNED for a
5
+ * challenge-time signed caller) → first poll pins the holder → human approves
6
+ * (PINNED→APPROVED, credential parked) → the pinned holder polls 200-ready,
7
+ * repeatably, until the anchor TTL elapses.
8
+ *
9
+ * Design:
10
+ * - ALL transition logic lives in the pure, synchronous reducers
11
+ * `reduceCreate` / `reducePoll` / `reduceApprove` (injectable `now`). They never
12
+ * touch storage and never await — which is what lets the Cloudflare DO input gate
13
+ * serialize the first-poll race (the dispatcher does `get → reduce → put` with no
14
+ * intervening non-storage await, so the read-modify-write is atomic per the gate).
15
+ * - `handlePickupInternal` is the only async surface: it reads the record from
16
+ * `ctx.storage`, runs the reducer, then persists or deletes.
17
+ *
18
+ * Non-negotiables:
19
+ * - never store/log a raw token — only its `sha256Hex` digest (see `./hash`);
20
+ * - no `alarm()` override: the `agents` SDK base class assigns `this.alarm` as an
21
+ * own-property arrow (its SQL scheduler), which shadows any subclass method and
22
+ * exposes no `super.alarm` to delegate to — so anchor lifetime is instead bounded
23
+ * by idempotent creation (one anchor per deterministic sid) plus the ≤600s TTL;
24
+ * - lazy expiry with delete-on-expired-read (a 410 outcome deletes the key);
25
+ * - every response carries `Cache-Control: no-store`;
26
+ * - 429 is status + `Retry-After` only — never a body.
27
+ *
28
+ * @module @kya-os/mcp-i-cloudflare/delegation-http/anchor-do
29
+ */
30
+ import { sha256Hex } from "./hash";
31
+ import { KYAOS_ERRORS, INTERNAL_PICKUP_CREATE, INTERNAL_PICKUP_POLL, INTERNAL_PICKUP_APPROVE, INTERNAL_PICKUP_INFO, } from "./constants";
32
+ import { STORAGE_KEYS } from "../constants/storage-keys";
33
+ const pendingBody = () => ({ status: "pending" });
34
+ const pinnedErr = () => ({ error: KYAOS_ERRORS.pickupPinned });
35
+ const invalidSessionErr = () => ({ error: KYAOS_ERRORS.invalidSession });
36
+ const pickupExpiredErr = () => ({ error: KYAOS_ERRORS.pickupExpired });
37
+ /**
38
+ * Pure reducer for `create`. Writes a fresh anchor record (PENDING, or PINNED when a
39
+ * challenge-time keyid pin is supplied). Idempotent by construction: a `pickup:<sid>`
40
+ * instance is created once, by the challenge emitter, before the 401 leaves.
41
+ */
42
+ export function reduceCreate(rec, input, now) {
43
+ const i = input;
44
+ // Creation-bounding (W1): create is idempotent. A still-live anchor for this sid
45
+ // is reused as-is, so repeated challenges for the same (session, scopes) — which
46
+ // derive the same deterministic sid (see challenge-middleware.ts) — never orphan a
47
+ // fresh durable object or reset an in-flight pin/approval.
48
+ if (rec && now <= rec.expiresAt) {
49
+ return { response: { status: 201, body: { success: true } } };
50
+ }
51
+ const record = {
52
+ sid: i.sid,
53
+ status: i.pin ? "pinned" : "pending",
54
+ requiredScopes: i.requiredScopes,
55
+ minAssurance: i.minAssurance,
56
+ pollIntervalS: i.pollIntervalS,
57
+ createdAt: now,
58
+ expiresAt: now + i.anchorTtlS * 1000,
59
+ pin: i.pin
60
+ ? {
61
+ type: i.pin.type,
62
+ valueSha256: i.pin.valueSha256,
63
+ pinnedAt: now,
64
+ source: "challenge",
65
+ }
66
+ : undefined,
67
+ };
68
+ return { record, response: { status: 201, body: { success: true } } };
69
+ }
70
+ /**
71
+ * Pure reducer for `poll`. The §13 wire state machine:
72
+ * - no record → 404 invalid-session;
73
+ * - `now > expiresAt` → 410 pickup-expired (the dispatcher deletes the key);
74
+ * - no pin yet → this poll pins `sha256Hex(holder.value)` (PENDING→PINNED);
75
+ * - pin mismatch (any state, incl. after approval) → 403 pickup-pinned;
76
+ * - pinned holder, awaiting consent, polling faster than `pollIntervalS` → 429;
77
+ * - pinned holder, credential parked → 200 ready (repeatable, no consume-on-read);
78
+ * - otherwise → 202 pending.
79
+ */
80
+ export function reducePoll(rec, holder, now) {
81
+ if (!rec) {
82
+ return { response: { status: 404, body: invalidSessionErr() } };
83
+ }
84
+ if (now > rec.expiresAt) {
85
+ // delete-on-expired-read: a 410 outcome tells the dispatcher to drop the key.
86
+ return { response: { status: 410, body: pickupExpiredErr() } };
87
+ }
88
+ const holderSha = sha256Hex(holder.value);
89
+ let working = rec;
90
+ let pinnedJustNow = false;
91
+ if (rec.pin) {
92
+ // W2: require a matching holder TYPE as well as digest, so a token whose value
93
+ // equals a pinned keyid string (or vice-versa) can never satisfy the pin.
94
+ if (rec.pin.type !== holder.type || rec.pin.valueSha256 !== holderSha) {
95
+ return { response: { status: 403, body: pinnedErr() } };
96
+ }
97
+ }
98
+ else {
99
+ // First poll on an unpinned anchor pins the holder. The DO input gate makes this
100
+ // read-modify-write the compare-and-set: a racing second holder sees this pin.
101
+ working = {
102
+ ...rec,
103
+ status: rec.status === "pending" ? "pinned" : rec.status,
104
+ pin: {
105
+ type: holder.type,
106
+ valueSha256: holderSha,
107
+ pinnedAt: now,
108
+ source: "first-poll",
109
+ },
110
+ };
111
+ pinnedJustNow = true;
112
+ }
113
+ // The holder is now the pinned holder. Deliver the parked credential if approved.
114
+ if (working.status === "approved" && working.delegation) {
115
+ const body = {
116
+ status: "ready",
117
+ token_type: "Bearer",
118
+ delegation: working.delegation.vcJwt,
119
+ expires_in: working.delegation.expiresInS,
120
+ };
121
+ return pinnedJustNow
122
+ ? { record: working, response: { status: 200, body } }
123
+ : { response: { status: 200, body } };
124
+ }
125
+ // Awaiting consent. Throttle repeat polls by the pinned holder (never the pinning
126
+ // poll itself, and never the repeatable ready path above).
127
+ if (!pinnedJustNow &&
128
+ working.lastPollAt !== undefined &&
129
+ now - working.lastPollAt < working.pollIntervalS * 1000) {
130
+ return { response: { status: 429, retryAfterS: working.pollIntervalS } };
131
+ }
132
+ return {
133
+ record: { ...working, lastPollAt: now },
134
+ response: { status: 202, body: pendingBody() },
135
+ };
136
+ }
137
+ /**
138
+ * Pure reducer for `approve`. Parks the minted credential on a live anchor
139
+ * (PENDING or PINNED → APPROVED). Consent on an EXPIRED anchor MUST fail (410); an
140
+ * unknown anchor is 404 invalid-session.
141
+ */
142
+ export function reduceApprove(rec, input, now) {
143
+ const i = input;
144
+ if (!rec) {
145
+ return { response: { status: 404, body: invalidSessionErr() } };
146
+ }
147
+ if (now > rec.expiresAt) {
148
+ return { response: { status: 410, body: pickupExpiredErr() } };
149
+ }
150
+ // Idempotent approve: once a credential is parked, a duplicate approval (a
151
+ // double-submit, two consent tabs) must NOT replace it. Each approve mints a fresh
152
+ // VC-JWT, so a blind upsert would swap a live delegation the pinned holder may
153
+ // already be picking up. Return the existing success without re-parking.
154
+ if (rec.status === "approved" && rec.delegation) {
155
+ return { response: { status: 200, body: { success: true } } };
156
+ }
157
+ const record = {
158
+ ...rec,
159
+ status: "approved",
160
+ delegation: { vcJwt: i.vcJwt, expiresInS: i.expiresInS, approvedAt: now },
161
+ };
162
+ return { record, response: { status: 200, body: { success: true } } };
163
+ }
164
+ /** Reducer outcome → wire `Response`, applying the frozen header rules. */
165
+ function wireResponse(r) {
166
+ const headers = { "Cache-Control": "no-store" };
167
+ if (r.status === 429) {
168
+ // 429 is status + Retry-After only — never a body (the §9 registry has no
169
+ // over-rate string to put there).
170
+ if (r.retryAfterS !== undefined)
171
+ headers["Retry-After"] = String(r.retryAfterS);
172
+ return new Response(null, { status: 429, headers });
173
+ }
174
+ headers["Content-Type"] = "application/json";
175
+ return new Response(r.body === undefined ? "" : JSON.stringify(r.body), {
176
+ status: r.status,
177
+ headers,
178
+ });
179
+ }
180
+ /**
181
+ * Dispatch `/_internal/pickup/*` requests against the anchor's durable-object
182
+ * storage. One `ctx.storage.get` up front, the matching pure reducer, then a single
183
+ * `put` (or `delete` on an expired 410 read) — no non-storage await between read and
184
+ * write, so the DO input gate makes the first-poll pin a compare-and-set.
185
+ *
186
+ * `now` is injectable for tests; production callers omit it and it resolves to the
187
+ * wall clock.
188
+ */
189
+ export async function handlePickupInternal(ctx, request, now) {
190
+ const t = now ?? Date.now();
191
+ const path = new URL(request.url).pathname;
192
+ let body;
193
+ try {
194
+ body = (await request.json());
195
+ }
196
+ catch {
197
+ body = {};
198
+ }
199
+ const key = STORAGE_KEYS.pickupAnchor(String(body.sid));
200
+ const rec = (await ctx.storage.get(key));
201
+ if (path === INTERNAL_PICKUP_CREATE) {
202
+ const { record, response } = reduceCreate(rec, body, t);
203
+ if (record)
204
+ await ctx.storage.put(key, record);
205
+ return wireResponse(response);
206
+ }
207
+ if (path === INTERNAL_PICKUP_POLL) {
208
+ const h = body.holder;
209
+ if (!h ||
210
+ typeof h.value !== "string" ||
211
+ (h.type !== "token" && h.type !== "keyid")) {
212
+ return wireResponse({
213
+ status: 400,
214
+ body: { error: "kyaos/invalid-request", message: "missing or malformed holder" },
215
+ });
216
+ }
217
+ const { record, response } = reducePoll(rec, h, t);
218
+ if (response.status === 410)
219
+ await ctx.storage.delete(key);
220
+ else if (record)
221
+ await ctx.storage.put(key, record);
222
+ return wireResponse(response);
223
+ }
224
+ if (path === INTERNAL_PICKUP_APPROVE) {
225
+ const { record, response } = reduceApprove(rec, body, t);
226
+ if (response.status === 410)
227
+ await ctx.storage.delete(key);
228
+ else if (record)
229
+ await ctx.storage.put(key, record);
230
+ return wireResponse(response);
231
+ }
232
+ if (path === INTERNAL_PICKUP_INFO) {
233
+ if (!rec) {
234
+ return wireResponse({ status: 404, body: invalidSessionErr() });
235
+ }
236
+ if (t > rec.expiresAt) {
237
+ await ctx.storage.delete(key);
238
+ return wireResponse({ status: 410, body: pickupExpiredErr() });
239
+ }
240
+ // NEVER expose the pin or the parked delegation here.
241
+ return wireResponse({
242
+ status: 200,
243
+ body: {
244
+ status: rec.status,
245
+ requiredScopes: rec.requiredScopes,
246
+ minAssurance: rec.minAssurance,
247
+ expiresAt: rec.expiresAt,
248
+ },
249
+ });
250
+ }
251
+ return wireResponse({ status: 404, body: invalidSessionErr() });
252
+ }
253
+ //# sourceMappingURL=anchor-do.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anchor-do.js","sourceRoot":"","sources":["../../src/delegation-http/anchor-do.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * draft-kya-http-02 delegation layer — pickup-anchor durable-object routing.
3
+ *
4
+ * Anchor state lives in the existing `MCP_OBJECT` durable object (no new DO class,
5
+ * no wrangler migration). Each anchor gets its OWN instance, named `pickup:<sid>`,
6
+ * so the §13 first-poll race is serialized by the DO input gate and never collides
7
+ * with the MCP session namespace (which uses `DO_ROUTING_STRATEGY`).
8
+ *
9
+ * @module @kya-os/mcp-i-cloudflare/delegation-http/anchor-routing
10
+ */
11
+ import type { DurableObjectStub } from "@cloudflare/workers-types";
12
+ import type { CloudflareEnv } from "../types";
13
+ /** Durable-object instance name for an anchor: one instance per sid. */
14
+ export declare function anchorInstanceId(sid: string): string;
15
+ /**
16
+ * Resolve the durable-object stub for an anchor's dedicated instance. Throws when
17
+ * `MCP_OBJECT` is not bound (the anchor layer cannot function without it).
18
+ */
19
+ export declare function getAnchorStub(env: CloudflareEnv, sid: string): DurableObjectStub;
20
+ //# sourceMappingURL=anchor-routing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anchor-routing.d.ts","sourceRoot":"","sources":["../../src/delegation-http/anchor-routing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE9C,wEAAwE;AACxE,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,aAAa,EAClB,GAAG,EAAE,MAAM,GACV,iBAAiB,CASnB"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * draft-kya-http-02 delegation layer — pickup-anchor durable-object routing.
3
+ *
4
+ * Anchor state lives in the existing `MCP_OBJECT` durable object (no new DO class,
5
+ * no wrangler migration). Each anchor gets its OWN instance, named `pickup:<sid>`,
6
+ * so the §13 first-poll race is serialized by the DO input gate and never collides
7
+ * with the MCP session namespace (which uses `DO_ROUTING_STRATEGY`).
8
+ *
9
+ * @module @kya-os/mcp-i-cloudflare/delegation-http/anchor-routing
10
+ */
11
+ /** Durable-object instance name for an anchor: one instance per sid. */
12
+ export function anchorInstanceId(sid) {
13
+ return `pickup:${sid}`;
14
+ }
15
+ /**
16
+ * Resolve the durable-object stub for an anchor's dedicated instance. Throws when
17
+ * `MCP_OBJECT` is not bound (the anchor layer cannot function without it).
18
+ */
19
+ export function getAnchorStub(env, sid) {
20
+ const namespace = env.MCP_OBJECT;
21
+ if (!namespace) {
22
+ throw new Error("[kya-http] MCP_OBJECT durable object namespace is not bound; cannot route pickup anchor");
23
+ }
24
+ const id = namespace.idFromName(anchorInstanceId(sid));
25
+ return namespace.get(id);
26
+ }
27
+ //# sourceMappingURL=anchor-routing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anchor-routing.js","sourceRoot":"","sources":["../../src/delegation-http/anchor-routing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,wEAAwE;AACxE,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,OAAO,UAAU,GAAG,EAAE,CAAC;AACzB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAC3B,GAAkB,EAClB,GAAW;IAEX,MAAM,SAAS,GAAG,GAAG,CAAC,UAAU,CAAC;IACjC,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CACb,yFAAyF,CAC1F,CAAC;IACJ,CAAC;IACD,MAAM,EAAE,GAAG,SAAS,CAAC,UAAU,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC;IACvD,OAAO,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAC3B,CAAC"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * draft-kya-http-02 delegation layer — Bearer retry verification.
3
+ *
4
+ * After a §8.1 challenge, a kya-capable caller retries the MCP request carrying the
5
+ * minted delegation as a Bearer credential. This module turns that retry into the
6
+ * adapter's existing `params.delegationToken` priority seam (adapter.ts:~458 — tested
7
+ * by `adapter-delegation-priority.test.ts`), WITHOUT editing the adapter:
8
+ *
9
+ * - `extractDelegationVcJwt` accepts `Authorization: Bearer <jwt>` ONLY when the
10
+ * decoded JWT payload carries a `vc` claim. An opaque OAuth bearer (no `vc`) is
11
+ * left untouched (returns null) so existing OAuth clients pass through. When
12
+ * `Authorization` is occupied by such a non-VC bearer, the `KYA-Delegation` header
13
+ * is used as the fallback carrier.
14
+ * - `applyBearerDelegation` verifies the VC-JWT fail-fast via `DelegationService`
15
+ * and, on success, clones the request with `params.delegationToken = vcJwt`. An
16
+ * invalid chain → 403 `kyaos/chain-broken`; an expired credential → 401
17
+ * `kyaos/expired`. Both error responses carry `Cache-Control: no-store`.
18
+ *
19
+ * @module @kya-os/mcp-i-cloudflare/delegation-http/bearer-retry
20
+ */
21
+ import type { CloudflareEnv } from "../types";
22
+ import type { HttpChallengeConfig } from "../config";
23
+ /**
24
+ * Extract a delegation VC-JWT from the request, or `null` when there is none to act
25
+ * on. Resolution order, mirroring the wire contract:
26
+ *
27
+ * 1. `Authorization: Bearer <jwt>` whose payload carries a `vc` claim.
28
+ * 2. Otherwise (no Authorization, OR an opaque OAuth bearer without `vc`), the
29
+ * `KYA-Delegation` fallback header — used ONLY when it too is a `vc`-bearing JWT.
30
+ *
31
+ * An opaque OAuth bearer with no `vc` and no fallback header yields `null`, so the
32
+ * request passes through to the adapter untouched.
33
+ */
34
+ export declare function extractDelegationVcJwt(request: Request): string | null;
35
+ /**
36
+ * Apply a presented delegation to the outgoing MCP request before it reaches the
37
+ * adapter.
38
+ *
39
+ * - No delegation VC-JWT present → pass the request through unchanged.
40
+ * - Expired VC-JWT → `{ errorResponse }` 401 `kyaos/expired` (`no-store`).
41
+ * - Invalid / unverifiable VC-JWT → `{ errorResponse }` 403 `kyaos/chain-broken`
42
+ * (`no-store`).
43
+ * - Valid VC-JWT → `{ request }` whose JSON-RPC body now carries
44
+ * `params.delegationToken = vcJwt` (the adapter's existing priority seam).
45
+ * - Valid VC-JWT but a non-JSON-RPC body the token cannot be injected into →
46
+ * `{ errorResponse }` 400 `invalid_request` (`no-store`); we never silently drop
47
+ * an accepted credential.
48
+ */
49
+ export declare function applyBearerDelegation(request: Request, env: CloudflareEnv, _cfg: Required<HttpChallengeConfig>): Promise<{
50
+ request: Request;
51
+ } | {
52
+ errorResponse: Response;
53
+ }>;
54
+ //# sourceMappingURL=bearer-retry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bearer-retry.d.ts","sourceRoot":"","sources":["../../src/delegation-http/bearer-retry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AA4DrD;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAgBtE;AA+CD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,aAAa,EAClB,IAAI,EAAE,QAAQ,CAAC,mBAAmB,CAAC,GAClC,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,aAAa,EAAE,QAAQ,CAAA;CAAE,CAAC,CAqC7D"}
@@ -0,0 +1,195 @@
1
+ /**
2
+ * draft-kya-http-02 delegation layer — Bearer retry verification.
3
+ *
4
+ * After a §8.1 challenge, a kya-capable caller retries the MCP request carrying the
5
+ * minted delegation as a Bearer credential. This module turns that retry into the
6
+ * adapter's existing `params.delegationToken` priority seam (adapter.ts:~458 — tested
7
+ * by `adapter-delegation-priority.test.ts`), WITHOUT editing the adapter:
8
+ *
9
+ * - `extractDelegationVcJwt` accepts `Authorization: Bearer <jwt>` ONLY when the
10
+ * decoded JWT payload carries a `vc` claim. An opaque OAuth bearer (no `vc`) is
11
+ * left untouched (returns null) so existing OAuth clients pass through. When
12
+ * `Authorization` is occupied by such a non-VC bearer, the `KYA-Delegation` header
13
+ * is used as the fallback carrier.
14
+ * - `applyBearerDelegation` verifies the VC-JWT fail-fast via `DelegationService`
15
+ * and, on success, clones the request with `params.delegationToken = vcJwt`. An
16
+ * invalid chain → 403 `kyaos/chain-broken`; an expired credential → 401
17
+ * `kyaos/expired`. Both error responses carry `Cache-Control: no-store`.
18
+ *
19
+ * @module @kya-os/mcp-i-cloudflare/delegation-http/bearer-retry
20
+ */
21
+ import { DelegationService } from "../services/delegation.service";
22
+ import { FALLBACK_DELEGATION_HEADER, KYAOS_ERRORS } from "./constants";
23
+ /**
24
+ * Decode a JWT payload (the second `.`-delimited segment) as JSON, or `null` when the
25
+ * value is not a well-formed three-segment JWT with a base64url-decodable JSON body.
26
+ * Never throws.
27
+ */
28
+ function decodeJwtPayload(jwt) {
29
+ if (!jwt || typeof jwt !== "string")
30
+ return null;
31
+ const parts = jwt.split(".");
32
+ if (parts.length !== 3)
33
+ return null;
34
+ try {
35
+ // Re-add the base64url padding `atob` needs (the `=` is stripped on the wire);
36
+ // a strict atob rejects an unpadded segment whose length isn't a multiple of 4.
37
+ const b64 = parts[1].replace(/-/g, "+").replace(/_/g, "/");
38
+ const json = atob(b64 + "=".repeat((4 - (b64.length % 4)) % 4));
39
+ const payload = JSON.parse(json);
40
+ return payload && typeof payload === "object"
41
+ ? payload
42
+ : null;
43
+ }
44
+ catch {
45
+ return null;
46
+ }
47
+ }
48
+ /**
49
+ * Whether the JWT payload carries a `vc` claim — the marker that distinguishes a
50
+ * delegation VC-JWT from an opaque OAuth bearer.
51
+ */
52
+ function hasVcClaim(jwt) {
53
+ const payload = decodeJwtPayload(jwt);
54
+ return payload !== null && "vc" in payload;
55
+ }
56
+ /**
57
+ * Whether the JWT is expired by its `exp` claim (seconds since epoch). A token with
58
+ * no numeric `exp`, or one that cannot be decoded, is treated as NOT expired here —
59
+ * the cryptographic verification path owns the invalid-vs-expired distinction for
60
+ * those cases (and reports them as 403 chain-broken).
61
+ */
62
+ function isVcJwtExpired(jwt) {
63
+ const payload = decodeJwtPayload(jwt);
64
+ if (!payload)
65
+ return false;
66
+ const exp = payload.exp;
67
+ if (typeof exp !== "number" || !Number.isFinite(exp))
68
+ return false;
69
+ return exp < Math.floor(Date.now() / 1000);
70
+ }
71
+ /**
72
+ * Read the bearer token from an `Authorization: Bearer <token>` header, or `null`.
73
+ */
74
+ function readBearerToken(request) {
75
+ const auth = request.headers.get("Authorization");
76
+ if (!auth)
77
+ return null;
78
+ const match = /^Bearer\s+(.+)$/i.exec(auth.trim());
79
+ return match ? match[1].trim() : null;
80
+ }
81
+ /**
82
+ * Extract a delegation VC-JWT from the request, or `null` when there is none to act
83
+ * on. Resolution order, mirroring the wire contract:
84
+ *
85
+ * 1. `Authorization: Bearer <jwt>` whose payload carries a `vc` claim.
86
+ * 2. Otherwise (no Authorization, OR an opaque OAuth bearer without `vc`), the
87
+ * `KYA-Delegation` fallback header — used ONLY when it too is a `vc`-bearing JWT.
88
+ *
89
+ * An opaque OAuth bearer with no `vc` and no fallback header yields `null`, so the
90
+ * request passes through to the adapter untouched.
91
+ */
92
+ export function extractDelegationVcJwt(request) {
93
+ const bearer = readBearerToken(request);
94
+ if (bearer && hasVcClaim(bearer)) {
95
+ return bearer;
96
+ }
97
+ // Authorization is absent or occupied by a non-VC bearer — try the fallback header.
98
+ const fallback = request.headers.get(FALLBACK_DELEGATION_HEADER);
99
+ if (fallback) {
100
+ const token = fallback.trim();
101
+ if (token && hasVcClaim(token)) {
102
+ return token;
103
+ }
104
+ }
105
+ return null;
106
+ }
107
+ /**
108
+ * Build a `kyaos/*` JSON error response with the mandated `Cache-Control: no-store`.
109
+ */
110
+ function kyaosError(error, status) {
111
+ return new Response(JSON.stringify({ error }), {
112
+ status,
113
+ headers: {
114
+ "Content-Type": "application/json",
115
+ "Cache-Control": "no-store",
116
+ },
117
+ });
118
+ }
119
+ /**
120
+ * Clone `request` rewriting the JSON-RPC body so `params.delegationToken = vcJwt`.
121
+ * Returns `null` when the body is not JSON-RPC-shaped JSON (so the caller can pass the
122
+ * request through untouched rather than corrupt a non-JSON-RPC request).
123
+ */
124
+ async function rewriteDelegationToken(request, vcJwt) {
125
+ let body;
126
+ try {
127
+ body = await request.clone().json();
128
+ }
129
+ catch {
130
+ return null;
131
+ }
132
+ if (!body || typeof body !== "object")
133
+ return null;
134
+ const rpc = body;
135
+ const params = rpc.params && typeof rpc.params === "object"
136
+ ? { ...rpc.params }
137
+ : {};
138
+ params.delegationToken = vcJwt;
139
+ const nextBody = { ...rpc, params };
140
+ return new Request(request.url, {
141
+ method: request.method,
142
+ headers: request.headers,
143
+ body: JSON.stringify(nextBody),
144
+ });
145
+ }
146
+ /**
147
+ * Apply a presented delegation to the outgoing MCP request before it reaches the
148
+ * adapter.
149
+ *
150
+ * - No delegation VC-JWT present → pass the request through unchanged.
151
+ * - Expired VC-JWT → `{ errorResponse }` 401 `kyaos/expired` (`no-store`).
152
+ * - Invalid / unverifiable VC-JWT → `{ errorResponse }` 403 `kyaos/chain-broken`
153
+ * (`no-store`).
154
+ * - Valid VC-JWT → `{ request }` whose JSON-RPC body now carries
155
+ * `params.delegationToken = vcJwt` (the adapter's existing priority seam).
156
+ * - Valid VC-JWT but a non-JSON-RPC body the token cannot be injected into →
157
+ * `{ errorResponse }` 400 `invalid_request` (`no-store`); we never silently drop
158
+ * an accepted credential.
159
+ */
160
+ export async function applyBearerDelegation(request, env, _cfg) {
161
+ const vcJwt = extractDelegationVcJwt(request);
162
+ if (!vcJwt) {
163
+ return { request };
164
+ }
165
+ // Fail-fast expiry check before verification so we can emit the precise 401.
166
+ if (isVcJwtExpired(vcJwt)) {
167
+ return { errorResponse: kyaosError(KYAOS_ERRORS.expired, 401) };
168
+ }
169
+ const isValid = await new DelegationService(env).verifyDelegation(vcJwt);
170
+ if (!isValid) {
171
+ // verifyDelegation also rejects expired tokens; re-check so an expiry that the
172
+ // pre-check missed (e.g. exp encoded unusually) still maps to 401 not 403.
173
+ if (isVcJwtExpired(vcJwt)) {
174
+ return { errorResponse: kyaosError(KYAOS_ERRORS.expired, 401) };
175
+ }
176
+ return { errorResponse: kyaosError(KYAOS_ERRORS.chainBroken, 403) };
177
+ }
178
+ const rewritten = await rewriteDelegationToken(request, vcJwt);
179
+ if (!rewritten) {
180
+ // The delegation verified, but the request body is not a JSON-RPC object we can
181
+ // augment with params.delegationToken. Fail explicitly with 400: silently
182
+ // forwarding the original request would drop the ACCEPTED credential, and the
183
+ // adapter would then treat the call as unauthorized — masking the acceptance.
184
+ // (A real /mcp POST always carries a JSON-RPC object, so this is a
185
+ // malformed-request guard, not a normal path.)
186
+ //
187
+ // `invalid_request`/400 is deliberately OUT-OF-BAND from the §9 `kyaos/*` registry:
188
+ // this is a transport-level malformed-body error, not a delegation-chain verdict.
189
+ // Reusing a `kyaos/*` 401/403 here would mislead the caller into a retry/challenge
190
+ // loop, and the registry (vendored error-codes.json) has no malformed-body code.
191
+ return { errorResponse: kyaosError("invalid_request", 400) };
192
+ }
193
+ return { request: rewritten };
194
+ }
195
+ //# sourceMappingURL=bearer-retry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bearer-retry.js","sourceRoot":"","sources":["../../src/delegation-http/bearer-retry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * draft-kya-http-02 delegation layer — §8.1 challenge emitter middleware.
3
+ *
4
+ * Triggers on JSON-RPC responses whose `result._meta.errorType ===
5
+ * "authorization_required"` (the existing `DelegationRequiredError` → `result._meta`
6
+ * mapping in `adapter.ts`), mints a sid, creates the pickup anchor (which MUST exist
7
+ * before the 401 leaves), signs the `challenge_proof`, and emits the §8.1 401 in
8
+ * `negotiated`/`always` mode. Returns `null` to pass the original response through.
9
+ *
10
+ * @module @kya-os/mcp-i-cloudflare/delegation-http/challenge-middleware
11
+ */
12
+ import type { CloudflareEnv } from "../types";
13
+ import type { HttpChallengeConfig } from "../config";
14
+ import type { CloudflareProofGenerator } from "../proof-generator";
15
+ import type { getAnchorStub } from "./anchor-routing";
16
+ /**
17
+ * Emit a §8.1 delegation challenge in place of an `authorization_required` MCP
18
+ * response, or return `null` to pass the original response through untouched.
19
+ *
20
+ * `negotiated` (default): when the caller is NOT kya-capable, the ORIGINAL MCP error
21
+ * response is returned (so existing MCP/OAuth clients still see the unmodified error),
22
+ * never a 401. `always`: every `authorization_required` becomes a 401.
23
+ */
24
+ export declare function maybeEmitDelegationChallenge(request: Request, response: Response, env: CloudflareEnv, cfg: Required<HttpChallengeConfig>, deps: {
25
+ getAnchorStub: typeof getAnchorStub;
26
+ proofGenerator: () => Promise<CloudflareProofGenerator>;
27
+ }): Promise<Response | null>;
28
+ //# sourceMappingURL=challenge-middleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"challenge-middleware.d.ts","sourceRoot":"","sources":["../../src/delegation-http/challenge-middleware.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AACrD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AA+GtD;;;;;;;GAOG;AACH,wBAAsB,4BAA4B,CAChD,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,QAAQ,EAClB,GAAG,EAAE,aAAa,EAClB,GAAG,EAAE,QAAQ,CAAC,mBAAmB,CAAC,EAClC,IAAI,EAAE;IACJ,aAAa,EAAE,OAAO,aAAa,CAAC;IACpC,cAAc,EAAE,MAAM,OAAO,CAAC,wBAAwB,CAAC,CAAC;CACzD,GACA,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAwG1B"}