@adcp/sdk 14.0.0 → 14.1.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 (185) hide show
  1. package/dist/lib/adapters/implicit-account-store.d.mts +12 -7
  2. package/dist/lib/adapters/implicit-account-store.d.ts +12 -7
  3. package/dist/lib/adapters/implicit-account-store.js +69 -15
  4. package/dist/lib/adapters/implicit-account-store.mjs +69 -15
  5. package/dist/lib/core/AgentClient.d.mts +1 -0
  6. package/dist/lib/core/AgentClient.d.ts +1 -0
  7. package/dist/lib/core/AgentClient.js +3 -0
  8. package/dist/lib/core/AgentClient.mjs +3 -0
  9. package/dist/lib/core/SingleAgentClient.d.mts +31 -1
  10. package/dist/lib/core/SingleAgentClient.d.ts +31 -1
  11. package/dist/lib/core/SingleAgentClient.js +355 -35
  12. package/dist/lib/core/SingleAgentClient.mjs +365 -37
  13. package/dist/lib/core/TaskExecutor.js +2 -1
  14. package/dist/lib/core/TaskExecutor.mjs +2 -1
  15. package/dist/lib/core/account-key.d.mts +3 -0
  16. package/dist/lib/core/account-key.d.ts +3 -0
  17. package/dist/lib/core/account-key.js +41 -0
  18. package/dist/lib/core/account-key.mjs +17 -0
  19. package/dist/lib/core/account-resolution.d.mts +2 -0
  20. package/dist/lib/core/account-resolution.d.ts +2 -0
  21. package/dist/lib/core/buyer-account-registry.d.mts +65 -0
  22. package/dist/lib/core/buyer-account-registry.d.ts +65 -0
  23. package/dist/lib/core/buyer-account-registry.js +518 -0
  24. package/dist/lib/core/buyer-account-registry.mjs +494 -0
  25. package/dist/lib/core/product-cache.d.mts +18 -0
  26. package/dist/lib/core/product-cache.d.ts +18 -0
  27. package/dist/lib/core/product-cache.js +137 -0
  28. package/dist/lib/core/product-cache.mjs +112 -0
  29. package/dist/lib/errors/index.d.mts +40 -1
  30. package/dist/lib/errors/index.d.ts +40 -1
  31. package/dist/lib/errors/index.js +69 -3
  32. package/dist/lib/errors/index.mjs +64 -3
  33. package/dist/lib/governance/authorization.d.mts +17 -1
  34. package/dist/lib/governance/authorization.d.ts +17 -1
  35. package/dist/lib/governance/authorization.js +55 -7
  36. package/dist/lib/governance/authorization.mjs +59 -7
  37. package/dist/lib/governance/index.d.mts +2 -2
  38. package/dist/lib/governance/index.d.ts +2 -2
  39. package/dist/lib/governance/index.js +2 -0
  40. package/dist/lib/governance/index.mjs +3 -1
  41. package/dist/lib/index.d.mts +5 -3
  42. package/dist/lib/index.d.ts +5 -3
  43. package/dist/lib/index.js +20 -0
  44. package/dist/lib/index.mjs +19 -0
  45. package/dist/lib/protocols/a2a.js +9 -1
  46. package/dist/lib/protocols/a2a.mjs +9 -1
  47. package/dist/lib/protocols/index.js +9 -2
  48. package/dist/lib/protocols/index.mjs +9 -2
  49. package/dist/lib/protocols/mcp-modern.js +2 -1
  50. package/dist/lib/protocols/mcp-modern.mjs +2 -1
  51. package/dist/lib/protocols/mcp.js +5 -2
  52. package/dist/lib/protocols/mcp.mjs +5 -2
  53. package/dist/lib/protocols/rawResponseCapture.d.mts +6 -0
  54. package/dist/lib/protocols/rawResponseCapture.d.ts +6 -0
  55. package/dist/lib/protocols/rawResponseCapture.js +41 -29
  56. package/dist/lib/protocols/rawResponseCapture.mjs +40 -29
  57. package/dist/lib/protocols/signedRequestRejection.d.mts +9 -0
  58. package/dist/lib/protocols/signedRequestRejection.d.ts +9 -0
  59. package/dist/lib/protocols/signedRequestRejection.js +209 -0
  60. package/dist/lib/protocols/signedRequestRejection.mjs +189 -0
  61. package/dist/lib/protocols/transportDiagnostics.d.mts +1 -0
  62. package/dist/lib/protocols/transportDiagnostics.d.ts +1 -0
  63. package/dist/lib/protocols/transportDiagnostics.js +2 -0
  64. package/dist/lib/protocols/transportDiagnostics.mjs +1 -0
  65. package/dist/lib/registry/types.generated.d.mts +112 -45
  66. package/dist/lib/registry/types.generated.d.ts +112 -45
  67. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  68. package/dist/lib/server/account-provisioning.d.mts +2 -0
  69. package/dist/lib/server/account-provisioning.d.ts +2 -0
  70. package/dist/lib/server/account-provisioning.js +30 -0
  71. package/dist/lib/server/account-provisioning.mjs +6 -0
  72. package/dist/lib/server/account-reference-warnings.d.mts +12 -0
  73. package/dist/lib/server/account-reference-warnings.d.ts +12 -0
  74. package/dist/lib/server/account-reference-warnings.js +48 -0
  75. package/dist/lib/server/account-reference-warnings.mjs +23 -0
  76. package/dist/lib/server/auth-signature.js +1 -0
  77. package/dist/lib/server/auth-signature.mjs +1 -0
  78. package/dist/lib/server/create-adcp-server.d.mts +34 -0
  79. package/dist/lib/server/create-adcp-server.d.ts +34 -0
  80. package/dist/lib/server/create-adcp-server.js +225 -14
  81. package/dist/lib/server/create-adcp-server.mjs +225 -14
  82. package/dist/lib/server/decisioning/account.d.mts +2 -0
  83. package/dist/lib/server/decisioning/account.d.ts +2 -0
  84. package/dist/lib/server/decisioning/runtime/from-platform.js +49 -10
  85. package/dist/lib/server/decisioning/runtime/from-platform.mjs +49 -10
  86. package/dist/lib/server/index.d.mts +2 -2
  87. package/dist/lib/server/index.d.ts +2 -2
  88. package/dist/lib/server/index.js +2 -0
  89. package/dist/lib/server/index.mjs +3 -1
  90. package/dist/lib/signing/agent-resolver/consistency.d.mts +6 -13
  91. package/dist/lib/signing/agent-resolver/consistency.d.ts +6 -13
  92. package/dist/lib/signing/agent-resolver/consistency.js +0 -1
  93. package/dist/lib/signing/agent-resolver/consistency.mjs +0 -1
  94. package/dist/lib/signing/agent-resolver/errors.d.mts +1 -1
  95. package/dist/lib/signing/agent-resolver/errors.d.ts +1 -1
  96. package/dist/lib/signing/agent-resolver/fetch-helpers.d.mts +2 -0
  97. package/dist/lib/signing/agent-resolver/fetch-helpers.d.ts +2 -0
  98. package/dist/lib/signing/agent-resolver/fetch-helpers.js +2 -1
  99. package/dist/lib/signing/agent-resolver/fetch-helpers.mjs +2 -1
  100. package/dist/lib/signing/agent-resolver/jwks-set.js +24 -3
  101. package/dist/lib/signing/agent-resolver/jwks-set.mjs +24 -3
  102. package/dist/lib/signing/agent-resolver/legacy-brand.d.mts +15 -0
  103. package/dist/lib/signing/agent-resolver/legacy-brand.d.ts +15 -0
  104. package/dist/lib/signing/agent-resolver/legacy-brand.js +60 -0
  105. package/dist/lib/signing/agent-resolver/legacy-brand.mjs +36 -0
  106. package/dist/lib/signing/agent-resolver/operator-authorization.d.mts +13 -0
  107. package/dist/lib/signing/agent-resolver/operator-authorization.d.ts +13 -0
  108. package/dist/lib/signing/agent-resolver/operator-authorization.js +108 -0
  109. package/dist/lib/signing/agent-resolver/operator-authorization.mjs +84 -0
  110. package/dist/lib/signing/agent-resolver/resolve-agent.d.mts +14 -4
  111. package/dist/lib/signing/agent-resolver/resolve-agent.d.ts +14 -4
  112. package/dist/lib/signing/agent-resolver/resolve-agent.js +101 -132
  113. package/dist/lib/signing/agent-resolver/resolve-agent.mjs +102 -133
  114. package/dist/lib/signing/agent-resolver/resolved-agent-jwks.d.mts +7 -1
  115. package/dist/lib/signing/agent-resolver/resolved-agent-jwks.d.ts +7 -1
  116. package/dist/lib/signing/agent-resolver/resolved-agent-jwks.js +50 -17
  117. package/dist/lib/signing/agent-resolver/resolved-agent-jwks.mjs +50 -17
  118. package/dist/lib/signing/agent-resolver/select-agent.d.mts +14 -15
  119. package/dist/lib/signing/agent-resolver/select-agent.d.ts +14 -15
  120. package/dist/lib/signing/agent-resolver/select-agent.js +98 -13
  121. package/dist/lib/signing/agent-resolver/select-agent.mjs +95 -13
  122. package/dist/lib/signing/brand-jwks.d.mts +27 -75
  123. package/dist/lib/signing/brand-jwks.d.ts +27 -75
  124. package/dist/lib/signing/brand-jwks.js +112 -182
  125. package/dist/lib/signing/brand-jwks.mjs +112 -182
  126. package/dist/lib/signing/errors.d.mts +3 -1
  127. package/dist/lib/signing/errors.d.ts +3 -1
  128. package/dist/lib/signing/errors.js +4 -1
  129. package/dist/lib/signing/errors.mjs +4 -1
  130. package/dist/lib/signing/jwks-https.d.mts +7 -0
  131. package/dist/lib/signing/jwks-https.d.ts +7 -0
  132. package/dist/lib/signing/jwks-https.js +31 -8
  133. package/dist/lib/signing/jwks-https.mjs +31 -8
  134. package/dist/lib/signing/jwks.d.mts +8 -0
  135. package/dist/lib/signing/jwks.d.ts +8 -0
  136. package/dist/lib/signing/middleware.js +2 -1
  137. package/dist/lib/signing/middleware.mjs +2 -1
  138. package/dist/lib/signing/publisher-pins.d.mts +11 -0
  139. package/dist/lib/signing/publisher-pins.d.ts +11 -0
  140. package/dist/lib/signing/publisher-pins.js +125 -0
  141. package/dist/lib/signing/publisher-pins.mjs +101 -0
  142. package/dist/lib/signing/server.d.mts +1 -0
  143. package/dist/lib/signing/server.d.ts +1 -0
  144. package/dist/lib/signing/types.d.mts +5 -0
  145. package/dist/lib/signing/types.d.ts +5 -0
  146. package/dist/lib/signing/verifier.js +49 -4
  147. package/dist/lib/signing/verifier.mjs +49 -4
  148. package/dist/lib/signing/webhook-verifier.d.mts +7 -2
  149. package/dist/lib/signing/webhook-verifier.d.ts +7 -2
  150. package/dist/lib/signing/webhook-verifier.js +42 -2
  151. package/dist/lib/signing/webhook-verifier.mjs +43 -3
  152. package/dist/lib/testing/storyboard/account-policy.d.mts +2 -0
  153. package/dist/lib/testing/storyboard/account-policy.d.ts +2 -0
  154. package/dist/lib/testing/storyboard/account-policy.js +35 -0
  155. package/dist/lib/testing/storyboard/account-policy.mjs +11 -0
  156. package/dist/lib/testing/storyboard/context.js +6 -0
  157. package/dist/lib/testing/storyboard/context.mjs +6 -0
  158. package/dist/lib/testing/storyboard/request-builder.js +12 -2
  159. package/dist/lib/testing/storyboard/request-builder.mjs +12 -2
  160. package/dist/lib/testing/storyboard/runner.js +3 -2
  161. package/dist/lib/testing/storyboard/runner.mjs +3 -2
  162. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  163. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  164. package/dist/lib/version.d.mts +3 -3
  165. package/dist/lib/version.d.ts +3 -3
  166. package/dist/lib/version.js +3 -3
  167. package/dist/lib/version.mjs +3 -3
  168. package/dist/lib/wholesale-feed-sync/sync.d.mts +1 -0
  169. package/dist/lib/wholesale-feed-sync/sync.d.ts +1 -0
  170. package/dist/lib/wholesale-feed-sync/sync.js +92 -21
  171. package/dist/lib/wholesale-feed-sync/sync.mjs +92 -21
  172. package/dist/lib/wholesale-feed-sync/types.d.mts +4 -3
  173. package/dist/lib/wholesale-feed-sync/types.d.ts +4 -3
  174. package/docs/TYPE-SUMMARY.md +2 -2
  175. package/docs/guides/BUILD-AN-AGENT.md +2 -2
  176. package/docs/guides/BUYER-QUICKSTART-3.2.md +2 -0
  177. package/docs/guides/FIRST-CALL-TO-A-SELLER.md +104 -0
  178. package/docs/guides/SIGNING-GUIDE.md +16 -7
  179. package/docs/guides/account-resolution.md +86 -0
  180. package/docs/llms.txt +3 -2
  181. package/docs/migration-14.x-rc-worksheet.md +4 -4
  182. package/docs/migration-4.x-to-5.x.md +1 -0
  183. package/docs/migration-agent-resolution-3.3.md +123 -0
  184. package/docs/recipes/verifying-inbound-webhooks.md +56 -15
  185. package/package.json +2 -2
@@ -1,6 +1,14 @@
1
1
  import type { AdcpJsonWebKey } from './types.mjs';
2
2
  export interface JwksResolution {
3
3
  jwk: AdcpJsonWebKey | null;
4
+ /** Canonical agent identity established by capability-bound discovery. */
5
+ agentUrl?: string;
6
+ /** Exact capability-selected operator document for signed-buyer governance. */
7
+ operatorRecord?: {
8
+ url: string;
9
+ document: unknown;
10
+ };
11
+ legacyWebhookFallback?: boolean;
4
12
  /** Epoch seconds for a delegated-operator authorization boundary, when applicable. */
5
13
  operatorAuthorizationValidUntil?: number;
6
14
  }
@@ -1,6 +1,14 @@
1
1
  import type { AdcpJsonWebKey } from './types';
2
2
  export interface JwksResolution {
3
3
  jwk: AdcpJsonWebKey | null;
4
+ /** Canonical agent identity established by capability-bound discovery. */
5
+ agentUrl?: string;
6
+ /** Exact capability-selected operator document for signed-buyer governance. */
7
+ operatorRecord?: {
8
+ url: string;
9
+ document: unknown;
10
+ };
11
+ legacyWebhookFallback?: boolean;
4
12
  /** Epoch seconds for a delegated-operator authorization boundary, when applicable. */
5
13
  operatorAuthorizationValidUntil?: number;
6
14
  }
@@ -49,7 +49,8 @@ function createExpressVerifier(options) {
49
49
  req.verifiedSigner = {
50
50
  keyid: result.keyid,
51
51
  agent_url: result.agent_url,
52
- verified_at: result.verified_at
52
+ verified_at: result.verified_at,
53
+ ...result.operatorRecord !== void 0 && { operatorRecord: result.operatorRecord }
53
54
  };
54
55
  }
55
56
  next();
@@ -26,7 +26,8 @@ function createExpressVerifier(options) {
26
26
  req.verifiedSigner = {
27
27
  keyid: result.keyid,
28
28
  agent_url: result.agent_url,
29
- verified_at: result.verified_at
29
+ verified_at: result.verified_at,
30
+ ...result.operatorRecord !== void 0 && { operatorRecord: result.operatorRecord }
30
31
  };
31
32
  }
32
33
  next();
@@ -0,0 +1,11 @@
1
+ import type { AdcpJsonWebKey } from './types.mjs';
2
+ export interface PublisherSigningKeyPin {
3
+ /** Publisher identity from the buyer's own record, for local diagnostics. */
4
+ publisher: string;
5
+ /** undefined means no pin; an empty pin accepts no keys. */
6
+ signingKeys?: readonly Record<string, unknown>[];
7
+ /** Fetch adagents.json without its usual cache. Reuse a callback bound to this agent/publisher/tenant across deliveries; refreshes share a 30-second cooldown. Return null only for confirmed pin removal; throw on failure. */
8
+ refresh: () => Promise<readonly Record<string, unknown>[] | null>;
9
+ }
10
+ /** Intersect an already discovered JWKS key with every applicable publisher pin. */
11
+ export declare function assertPublisherPins(jwk: AdcpJsonWebKey, publishers: readonly PublisherSigningKeyPin[], now: () => number): Promise<void>;
@@ -0,0 +1,11 @@
1
+ import type { AdcpJsonWebKey } from './types';
2
+ export interface PublisherSigningKeyPin {
3
+ /** Publisher identity from the buyer's own record, for local diagnostics. */
4
+ publisher: string;
5
+ /** undefined means no pin; an empty pin accepts no keys. */
6
+ signingKeys?: readonly Record<string, unknown>[];
7
+ /** Fetch adagents.json without its usual cache. Reuse a callback bound to this agent/publisher/tenant across deliveries; refreshes share a 30-second cooldown. Return null only for confirmed pin removal; throw on failure. */
8
+ refresh: () => Promise<readonly Record<string, unknown>[] | null>;
9
+ }
10
+ /** Intersect an already discovered JWKS key with every applicable publisher pin. */
11
+ export declare function assertPublisherPins(jwk: AdcpJsonWebKey, publishers: readonly PublisherSigningKeyPin[], now: () => number): Promise<void>;
@@ -0,0 +1,125 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var publisher_pins_exports = {};
20
+ __export(publisher_pins_exports, {
21
+ assertPublisherPins: () => assertPublisherPins
22
+ });
23
+ module.exports = __toCommonJS(publisher_pins_exports);
24
+ var import_jose = require("jose");
25
+ var import_errors = require('./agent-resolver/errors.js');
26
+ const refreshes = /* @__PURE__ */ new WeakMap();
27
+ async function refreshPin(publisher, now) {
28
+ let states = refreshes.get(publisher.refresh);
29
+ if (!states) refreshes.set(publisher.refresh, states = /* @__PURE__ */ new Map());
30
+ const checkedAt = now();
31
+ const existing = states.get(publisher.publisher);
32
+ if (existing && (existing.inFlight || checkedAt - existing.attemptedAt < 30) && existing.inputPins !== JSON.stringify(publisher.signingKeys)) {
33
+ throw new Error("Publisher pin changed during the refresh cooldown");
34
+ }
35
+ if (existing?.inFlight) return existing.inFlight;
36
+ if (existing && checkedAt - existing.attemptedAt < 30) {
37
+ if (existing.error !== void 0) throw existing.error;
38
+ return existing.snapshot;
39
+ }
40
+ const state = { attemptedAt: checkedAt, inputPins: JSON.stringify(publisher.signingKeys) };
41
+ if (states.size >= 512) {
42
+ const evictable = [...states].find(([, candidate]) => !candidate.inFlight);
43
+ if (!evictable) throw new Error("Publisher refresh capacity exceeded");
44
+ states.delete(evictable[0]);
45
+ }
46
+ states.set(publisher.publisher, state);
47
+ state.inFlight = Promise.resolve().then(async () => {
48
+ try {
49
+ const snapshot = await publisher.refresh();
50
+ if (snapshot === void 0) throw new TypeError("Publisher refresh must return a confirmed pin or null");
51
+ state.snapshot = snapshot === null ? null : Object.freeze(snapshot.map((key) => Object.freeze({ ...key })));
52
+ return state.snapshot;
53
+ } catch (error) {
54
+ state.error = error ?? new Error("Publisher refresh failed");
55
+ throw state.error;
56
+ } finally {
57
+ state.inFlight = void 0;
58
+ }
59
+ });
60
+ return state.inFlight;
61
+ }
62
+ async function thumbprint(key) {
63
+ const members = key.kty === "OKP" ? ["crv", "x"] : key.kty === "EC" ? ["crv", "x", "y"] : key.kty === "RSA" ? ["n", "e"] : void 0;
64
+ if (!members || members.some((name) => typeof key[name] !== "string" || key[name] === "")) return void 0;
65
+ try {
66
+ return await (0, import_jose.calculateJwkThumbprint)(key, "sha256");
67
+ } catch {
68
+ return void 0;
69
+ }
70
+ }
71
+ async function matchingValidity(keyThumbprint, pins) {
72
+ if (pins === void 0) return [Infinity];
73
+ const validity = [];
74
+ for (const pin of pins) {
75
+ if (!pin || typeof pin !== "object" || await thumbprint(pin) !== keyThumbprint) continue;
76
+ if (pin.revoked_at === void 0) validity.push(Infinity);
77
+ else if (typeof pin.revoked_at === "string") {
78
+ const revoked = Date.parse(pin.revoked_at) / 1e3;
79
+ validity.push(Number.isFinite(revoked) ? revoked : -Infinity);
80
+ } else validity.push(-Infinity);
81
+ }
82
+ return validity.length ? [Math.min(...validity)] : [];
83
+ }
84
+ async function matches(keyThumbprint, pins, now) {
85
+ const validity = await matchingValidity(keyThumbprint, pins);
86
+ return validity.some((until) => now() < until);
87
+ }
88
+ async function assertPublisherPins(jwk, publishers, now) {
89
+ const fingerprint = await thumbprint(jwk);
90
+ if (!fingerprint) throw new import_errors.AgentResolverError("request_signature_key_unknown", "Invalid public key", {});
91
+ const applicable = [];
92
+ const supplied = [];
93
+ for (const publisher of publishers) {
94
+ const inputPins = JSON.stringify(publisher.signingKeys);
95
+ supplied.push(inputPins);
96
+ const recent = refreshes.get(publisher.refresh)?.get(publisher.publisher);
97
+ let selected = recent?.snapshot !== void 0 && recent.inputPins === inputPins && now() - recent.attemptedAt < 30 ? recent.snapshot ?? void 0 : publisher.signingKeys;
98
+ if (!await matches(fingerprint, selected, now)) {
99
+ const refreshed = await refreshPin(publisher, now);
100
+ selected = refreshed ?? void 0;
101
+ if (!await matches(fingerprint, selected, now))
102
+ throw new import_errors.AgentResolverError(
103
+ "request_signature_key_unknown",
104
+ "Key is outside the applicable publisher pin",
105
+ {}
106
+ );
107
+ }
108
+ applicable.push(selected);
109
+ }
110
+ const validity = await Promise.all(applicable.map((selected) => matchingValidity(fingerprint, selected)));
111
+ const checkedInputs = publishers.map((publisher) => JSON.stringify(publisher.signingKeys));
112
+ for (let i = 0; i < publishers.length; i++) {
113
+ if (supplied[i] !== checkedInputs[i]) {
114
+ validity.push(await matchingValidity(fingerprint, publishers[i].signingKeys));
115
+ }
116
+ }
117
+ const checkedAt = now();
118
+ if (publishers.some((publisher, i) => JSON.stringify(publisher.signingKeys) !== checkedInputs[i]) || validity.some((bounds) => !bounds.some((until) => checkedAt < until))) {
119
+ throw new import_errors.AgentResolverError("request_signature_key_unknown", "Key is outside the applicable publisher pin", {});
120
+ }
121
+ }
122
+ // Annotate the CommonJS export names for ESM import in node:
123
+ 0 && (module.exports = {
124
+ assertPublisherPins
125
+ });
@@ -0,0 +1,101 @@
1
+ import { calculateJwkThumbprint } from "jose";
2
+ import { AgentResolverError } from "./agent-resolver/errors.mjs";
3
+ const refreshes = /* @__PURE__ */ new WeakMap();
4
+ async function refreshPin(publisher, now) {
5
+ let states = refreshes.get(publisher.refresh);
6
+ if (!states) refreshes.set(publisher.refresh, states = /* @__PURE__ */ new Map());
7
+ const checkedAt = now();
8
+ const existing = states.get(publisher.publisher);
9
+ if (existing && (existing.inFlight || checkedAt - existing.attemptedAt < 30) && existing.inputPins !== JSON.stringify(publisher.signingKeys)) {
10
+ throw new Error("Publisher pin changed during the refresh cooldown");
11
+ }
12
+ if (existing?.inFlight) return existing.inFlight;
13
+ if (existing && checkedAt - existing.attemptedAt < 30) {
14
+ if (existing.error !== void 0) throw existing.error;
15
+ return existing.snapshot;
16
+ }
17
+ const state = { attemptedAt: checkedAt, inputPins: JSON.stringify(publisher.signingKeys) };
18
+ if (states.size >= 512) {
19
+ const evictable = [...states].find(([, candidate]) => !candidate.inFlight);
20
+ if (!evictable) throw new Error("Publisher refresh capacity exceeded");
21
+ states.delete(evictable[0]);
22
+ }
23
+ states.set(publisher.publisher, state);
24
+ state.inFlight = Promise.resolve().then(async () => {
25
+ try {
26
+ const snapshot = await publisher.refresh();
27
+ if (snapshot === void 0) throw new TypeError("Publisher refresh must return a confirmed pin or null");
28
+ state.snapshot = snapshot === null ? null : Object.freeze(snapshot.map((key) => Object.freeze({ ...key })));
29
+ return state.snapshot;
30
+ } catch (error) {
31
+ state.error = error ?? new Error("Publisher refresh failed");
32
+ throw state.error;
33
+ } finally {
34
+ state.inFlight = void 0;
35
+ }
36
+ });
37
+ return state.inFlight;
38
+ }
39
+ async function thumbprint(key) {
40
+ const members = key.kty === "OKP" ? ["crv", "x"] : key.kty === "EC" ? ["crv", "x", "y"] : key.kty === "RSA" ? ["n", "e"] : void 0;
41
+ if (!members || members.some((name) => typeof key[name] !== "string" || key[name] === "")) return void 0;
42
+ try {
43
+ return await calculateJwkThumbprint(key, "sha256");
44
+ } catch {
45
+ return void 0;
46
+ }
47
+ }
48
+ async function matchingValidity(keyThumbprint, pins) {
49
+ if (pins === void 0) return [Infinity];
50
+ const validity = [];
51
+ for (const pin of pins) {
52
+ if (!pin || typeof pin !== "object" || await thumbprint(pin) !== keyThumbprint) continue;
53
+ if (pin.revoked_at === void 0) validity.push(Infinity);
54
+ else if (typeof pin.revoked_at === "string") {
55
+ const revoked = Date.parse(pin.revoked_at) / 1e3;
56
+ validity.push(Number.isFinite(revoked) ? revoked : -Infinity);
57
+ } else validity.push(-Infinity);
58
+ }
59
+ return validity.length ? [Math.min(...validity)] : [];
60
+ }
61
+ async function matches(keyThumbprint, pins, now) {
62
+ const validity = await matchingValidity(keyThumbprint, pins);
63
+ return validity.some((until) => now() < until);
64
+ }
65
+ async function assertPublisherPins(jwk, publishers, now) {
66
+ const fingerprint = await thumbprint(jwk);
67
+ if (!fingerprint) throw new AgentResolverError("request_signature_key_unknown", "Invalid public key", {});
68
+ const applicable = [];
69
+ const supplied = [];
70
+ for (const publisher of publishers) {
71
+ const inputPins = JSON.stringify(publisher.signingKeys);
72
+ supplied.push(inputPins);
73
+ const recent = refreshes.get(publisher.refresh)?.get(publisher.publisher);
74
+ let selected = recent?.snapshot !== void 0 && recent.inputPins === inputPins && now() - recent.attemptedAt < 30 ? recent.snapshot ?? void 0 : publisher.signingKeys;
75
+ if (!await matches(fingerprint, selected, now)) {
76
+ const refreshed = await refreshPin(publisher, now);
77
+ selected = refreshed ?? void 0;
78
+ if (!await matches(fingerprint, selected, now))
79
+ throw new AgentResolverError(
80
+ "request_signature_key_unknown",
81
+ "Key is outside the applicable publisher pin",
82
+ {}
83
+ );
84
+ }
85
+ applicable.push(selected);
86
+ }
87
+ const validity = await Promise.all(applicable.map((selected) => matchingValidity(fingerprint, selected)));
88
+ const checkedInputs = publishers.map((publisher) => JSON.stringify(publisher.signingKeys));
89
+ for (let i = 0; i < publishers.length; i++) {
90
+ if (supplied[i] !== checkedInputs[i]) {
91
+ validity.push(await matchingValidity(fingerprint, publishers[i].signingKeys));
92
+ }
93
+ }
94
+ const checkedAt = now();
95
+ if (publishers.some((publisher, i) => JSON.stringify(publisher.signingKeys) !== checkedInputs[i]) || validity.some((bounds) => !bounds.some((until) => checkedAt < until))) {
96
+ throw new AgentResolverError("request_signature_key_unknown", "Key is outside the applicable publisher pin", {});
97
+ }
98
+ }
99
+ export {
100
+ assertPublisherPins
101
+ };
@@ -30,3 +30,4 @@ export { createExpressVerifier, type ExpressLike, type ExpressMiddlewareOptions
30
30
  export { finalizeResponseSignature, prepareResponseSignature, signResponse, type PreparedResponseSignature, type SignedResponse, type SignResponseOptions, } from './signer.mjs';
31
31
  export { signResponseAsync } from './signer-async.mjs';
32
32
  export { resolveAgent, ResolvedAgentJwksResolver, getAgentJwks, createAgentJwksSet, AgentResolverError, attackerInfluencedFields, ATTACKER_INFLUENCED, readBrandJsonUrl, readIdentityPosture, type AgentResolution, type AgentProtocol, type AuthorizedOperatorScope, type DelegatedOperatorAuthorizationContext, type AgentResolverErrorCode, type AgentResolverErrorDetail, type AgentEntry, type AgentJwksResult, type CapabilitiesWithBrandJsonUrl, type CreateAgentJwksSetOptions, type FetchCapabilitiesFn, type GetAgentJwksOptions, type IdentityKeyOriginPurpose, type IdentityKeyOrigins, type IdentityPosture, type ResolveAgentOptions, type ResolvedAgentJwksResolverOptions, type TraceStep, } from './agent-resolver/index.mjs';
33
+ export type { PublisherSigningKeyPin } from './publisher-pins.mjs';
@@ -30,3 +30,4 @@ export { createExpressVerifier, type ExpressLike, type ExpressMiddlewareOptions
30
30
  export { finalizeResponseSignature, prepareResponseSignature, signResponse, type PreparedResponseSignature, type SignedResponse, type SignResponseOptions, } from './signer';
31
31
  export { signResponseAsync } from './signer-async';
32
32
  export { resolveAgent, ResolvedAgentJwksResolver, getAgentJwks, createAgentJwksSet, AgentResolverError, attackerInfluencedFields, ATTACKER_INFLUENCED, readBrandJsonUrl, readIdentityPosture, type AgentResolution, type AgentProtocol, type AuthorizedOperatorScope, type DelegatedOperatorAuthorizationContext, type AgentResolverErrorCode, type AgentResolverErrorDetail, type AgentEntry, type AgentJwksResult, type CapabilitiesWithBrandJsonUrl, type CreateAgentJwksSetOptions, type FetchCapabilitiesFn, type GetAgentJwksOptions, type IdentityKeyOriginPurpose, type IdentityKeyOrigins, type IdentityPosture, type ResolveAgentOptions, type ResolvedAgentJwksResolverOptions, type TraceStep, } from './agent-resolver';
33
+ export type { PublisherSigningKeyPin } from './publisher-pins';
@@ -60,6 +60,11 @@ export interface RevocationSnapshot {
60
60
  export interface VerifiedSigner {
61
61
  keyid: string;
62
62
  agent_url?: string;
63
+ /** Exact operator record selected during verified agent discovery. */
64
+ operatorRecord?: {
65
+ url: string;
66
+ document: unknown;
67
+ };
63
68
  verified_at: number;
64
69
  }
65
70
  /**
@@ -60,6 +60,11 @@ export interface RevocationSnapshot {
60
60
  export interface VerifiedSigner {
61
61
  keyid: string;
62
62
  agent_url?: string;
63
+ /** Exact operator record selected during verified agent discovery. */
64
+ operatorRecord?: {
65
+ url: string;
66
+ document: unknown;
67
+ };
63
68
  verified_at: number;
64
69
  }
65
70
  /**
@@ -32,7 +32,8 @@ var import_crypto = require('./crypto.js');
32
32
  var import_webhook_auth_detection = require('./webhook-auth-detection.js');
33
33
  var import_types = require('./types.js');
34
34
  async function verifyRequestSignature(request, options) {
35
- const now = options.now ? options.now() : Math.floor(Date.now() / 1e3);
35
+ const currentTime = options.now ?? (() => Math.floor(Date.now() / 1e3));
36
+ const now = currentTime();
36
37
  const pinnedBinaryEncoding = options.adcpVersion !== void 0 ? (0, import_content_digest.requestSigningEncodingForVersion)(options.adcpVersion) : void 0;
37
38
  const sigInputHeader = (0, import_canonicalize.getHeaderValue)(request.headers, "Signature-Input");
38
39
  const sigHeader = (0, import_canonicalize.getHeaderValue)(request.headers, "Signature");
@@ -119,14 +120,38 @@ async function verifyRequestSignature(request, options) {
119
120
  const effectiveCapability = pinnedBinaryEncoding === "rfc8941-base64" ? { ...options.capability, covers_content_digest: "required" } : options.capability;
120
121
  validateCoveredComponents(parsedInput.components, effectiveCapability, request);
121
122
  let jwk;
123
+ let resolvedAgentUrl;
124
+ let authorizationValidUntil;
125
+ let operatorRecord;
122
126
  try {
123
- jwk = await options.jwks.resolve(parsedInput.params.keyid);
127
+ if (options.jwks.resolveWithMetadata) {
128
+ const resolution = await options.jwks.resolveWithMetadata(parsedInput.params.keyid);
129
+ if (resolution.legacyWebhookFallback)
130
+ throw new import_errors2.AgentResolverError(
131
+ "request_signature_brand_json_url_missing",
132
+ "Legacy discovery is limited to webhook verification",
133
+ {}
134
+ );
135
+ jwk = resolution.jwk;
136
+ resolvedAgentUrl = resolution.agentUrl;
137
+ operatorRecord = resolution.operatorRecord;
138
+ authorizationValidUntil = resolution.operatorAuthorizationValidUntil;
139
+ } else {
140
+ jwk = await options.jwks.resolve(parsedInput.params.keyid);
141
+ }
124
142
  } catch (err) {
125
143
  if (err instanceof import_errors2.AgentResolverError) {
126
144
  throw new import_errors.RequestSignatureError(err.code, 7, err.message, err.detail);
127
145
  }
128
146
  throw err;
129
147
  }
148
+ if (authorizationValidUntil !== void 0 && currentTime() >= authorizationValidUntil) {
149
+ throw new import_errors.RequestSignatureError(
150
+ "request_signature_brand_origin_mismatch",
151
+ 7,
152
+ "Delegated operator authorization expired before verification"
153
+ );
154
+ }
130
155
  if (!jwk) {
131
156
  throw new import_errors.RequestSignatureError(
132
157
  "request_signature_key_unknown",
@@ -222,6 +247,13 @@ async function verifyRequestSignature(request, options) {
222
247
  throw err;
223
248
  }
224
249
  }
250
+ if (authorizationValidUntil !== void 0 && currentTime() >= authorizationValidUntil) {
251
+ throw new import_errors.RequestSignatureError(
252
+ "request_signature_brand_origin_mismatch",
253
+ 7,
254
+ "Delegated operator authorization expired during verification"
255
+ );
256
+ }
225
257
  const remaining = parsedInput.params.expires - now + import_types.CLOCK_SKEW_TOLERANCE_SECONDS;
226
258
  const ttl = Math.max(remaining, import_types.MAX_SIGNATURE_WINDOW_SECONDS + import_types.CLOCK_SKEW_TOLERANCE_SECONDS);
227
259
  const insertResult = await options.replayStore.insert(jwk.kid, replayScope, parsedInput.params.nonce, ttl, now);
@@ -239,8 +271,21 @@ async function verifyRequestSignature(request, options) {
239
271
  `Per-keyid replay cache cap exceeded for keyid=${jwk.kid}`
240
272
  );
241
273
  }
242
- const agent_url = options.agentUrlForKeyid?.(jwk.kid);
243
- return { status: "verified", keyid: jwk.kid, agent_url, verified_at: now };
274
+ if (authorizationValidUntil !== void 0 && currentTime() >= authorizationValidUntil) {
275
+ throw new import_errors.RequestSignatureError(
276
+ "request_signature_brand_origin_mismatch",
277
+ 7,
278
+ "Delegated operator authorization expired before acceptance"
279
+ );
280
+ }
281
+ const agent_url = resolvedAgentUrl ?? options.agentUrlForKeyid?.(jwk.kid);
282
+ return {
283
+ status: "verified",
284
+ keyid: jwk.kid,
285
+ agent_url,
286
+ verified_at: now,
287
+ ...operatorRecord !== void 0 && { operatorRecord }
288
+ };
244
289
  }
245
290
  function signatureEncodingCandidates(pinned, digestPolicy) {
246
291
  if (!pinned) return ["legacy-base64url", "rfc8941-base64"];
@@ -25,7 +25,8 @@ import {
25
25
  REQUEST_SIGNING_TAG
26
26
  } from "./types.mjs";
27
27
  async function verifyRequestSignature(request, options) {
28
- const now = options.now ? options.now() : Math.floor(Date.now() / 1e3);
28
+ const currentTime = options.now ?? (() => Math.floor(Date.now() / 1e3));
29
+ const now = currentTime();
29
30
  const pinnedBinaryEncoding = options.adcpVersion !== void 0 ? requestSigningEncodingForVersion(options.adcpVersion) : void 0;
30
31
  const sigInputHeader = getHeaderValue(request.headers, "Signature-Input");
31
32
  const sigHeader = getHeaderValue(request.headers, "Signature");
@@ -112,14 +113,38 @@ async function verifyRequestSignature(request, options) {
112
113
  const effectiveCapability = pinnedBinaryEncoding === "rfc8941-base64" ? { ...options.capability, covers_content_digest: "required" } : options.capability;
113
114
  validateCoveredComponents(parsedInput.components, effectiveCapability, request);
114
115
  let jwk;
116
+ let resolvedAgentUrl;
117
+ let authorizationValidUntil;
118
+ let operatorRecord;
115
119
  try {
116
- jwk = await options.jwks.resolve(parsedInput.params.keyid);
120
+ if (options.jwks.resolveWithMetadata) {
121
+ const resolution = await options.jwks.resolveWithMetadata(parsedInput.params.keyid);
122
+ if (resolution.legacyWebhookFallback)
123
+ throw new AgentResolverError(
124
+ "request_signature_brand_json_url_missing",
125
+ "Legacy discovery is limited to webhook verification",
126
+ {}
127
+ );
128
+ jwk = resolution.jwk;
129
+ resolvedAgentUrl = resolution.agentUrl;
130
+ operatorRecord = resolution.operatorRecord;
131
+ authorizationValidUntil = resolution.operatorAuthorizationValidUntil;
132
+ } else {
133
+ jwk = await options.jwks.resolve(parsedInput.params.keyid);
134
+ }
117
135
  } catch (err) {
118
136
  if (err instanceof AgentResolverError) {
119
137
  throw new RequestSignatureError(err.code, 7, err.message, err.detail);
120
138
  }
121
139
  throw err;
122
140
  }
141
+ if (authorizationValidUntil !== void 0 && currentTime() >= authorizationValidUntil) {
142
+ throw new RequestSignatureError(
143
+ "request_signature_brand_origin_mismatch",
144
+ 7,
145
+ "Delegated operator authorization expired before verification"
146
+ );
147
+ }
123
148
  if (!jwk) {
124
149
  throw new RequestSignatureError(
125
150
  "request_signature_key_unknown",
@@ -215,6 +240,13 @@ async function verifyRequestSignature(request, options) {
215
240
  throw err;
216
241
  }
217
242
  }
243
+ if (authorizationValidUntil !== void 0 && currentTime() >= authorizationValidUntil) {
244
+ throw new RequestSignatureError(
245
+ "request_signature_brand_origin_mismatch",
246
+ 7,
247
+ "Delegated operator authorization expired during verification"
248
+ );
249
+ }
218
250
  const remaining = parsedInput.params.expires - now + CLOCK_SKEW_TOLERANCE_SECONDS;
219
251
  const ttl = Math.max(remaining, MAX_SIGNATURE_WINDOW_SECONDS + CLOCK_SKEW_TOLERANCE_SECONDS);
220
252
  const insertResult = await options.replayStore.insert(jwk.kid, replayScope, parsedInput.params.nonce, ttl, now);
@@ -232,8 +264,21 @@ async function verifyRequestSignature(request, options) {
232
264
  `Per-keyid replay cache cap exceeded for keyid=${jwk.kid}`
233
265
  );
234
266
  }
235
- const agent_url = options.agentUrlForKeyid?.(jwk.kid);
236
- return { status: "verified", keyid: jwk.kid, agent_url, verified_at: now };
267
+ if (authorizationValidUntil !== void 0 && currentTime() >= authorizationValidUntil) {
268
+ throw new RequestSignatureError(
269
+ "request_signature_brand_origin_mismatch",
270
+ 7,
271
+ "Delegated operator authorization expired before acceptance"
272
+ );
273
+ }
274
+ const agent_url = resolvedAgentUrl ?? options.agentUrlForKeyid?.(jwk.kid);
275
+ return {
276
+ status: "verified",
277
+ keyid: jwk.kid,
278
+ agent_url,
279
+ verified_at: now,
280
+ ...operatorRecord !== void 0 && { operatorRecord }
281
+ };
237
282
  }
238
283
  function signatureEncodingCandidates(pinned, digestPolicy) {
239
284
  if (!pinned) return ["legacy-base64url", "rfc8941-base64"];
@@ -14,8 +14,8 @@
14
14
  * `content-type`, and `content-digest` — `content-digest` is unconditional
15
15
  * for webhooks (vs policy-driven on requests) because every webhook
16
16
  * carries a JSON body.
17
- * - JWKS resolved via the publisher's `brand.json` `agents[]` `jwks_uri`,
18
- * not the request-path capability document.
17
+ * - JWKS resolved via the capabilities-selected operator brand.json;
18
+ * publisher pins narrow its keys for applicable inventory.
19
19
  *
20
20
  * Checklist steps below mirror the 14-step shape in
21
21
  * `docs/building/implementation/security.mdx#verifier-checklist-for-webhooks`
@@ -23,6 +23,7 @@
23
23
  * match the security doc.
24
24
  */
25
25
  import type { JwksResolver } from './jwks.mjs';
26
+ import { type PublisherSigningKeyPin } from './publisher-pins.mjs';
26
27
  import { type ReplayStore } from './replay.mjs';
27
28
  import { type RevocationStore } from './revocation.mjs';
28
29
  export declare const WEBHOOK_SIGNING_TAG = "adcp/webhook-signing/v1";
@@ -42,6 +43,10 @@ export interface WebhookRequestLike {
42
43
  export declare const WEBHOOK_MANDATORY_COMPONENTS: ReadonlyArray<string>;
43
44
  export interface VerifyWebhookOptions {
44
45
  jwks: JwksResolver;
46
+ /** Applicable publishers from the verifier's own media-buy record. */
47
+ publisherPins?: readonly PublisherSigningKeyPin[];
48
+ /** Log the underlying request_signature_* discovery cause locally. */
49
+ onKeyResolutionError?: (error: unknown) => void;
45
50
  replayStore: ReplayStore;
46
51
  revocationStore: RevocationStore;
47
52
  /** Now in seconds since epoch. Defaults to `Date.now() / 1000`. */
@@ -14,8 +14,8 @@
14
14
  * `content-type`, and `content-digest` — `content-digest` is unconditional
15
15
  * for webhooks (vs policy-driven on requests) because every webhook
16
16
  * carries a JSON body.
17
- * - JWKS resolved via the publisher's `brand.json` `agents[]` `jwks_uri`,
18
- * not the request-path capability document.
17
+ * - JWKS resolved via the capabilities-selected operator brand.json;
18
+ * publisher pins narrow its keys for applicable inventory.
19
19
  *
20
20
  * Checklist steps below mirror the 14-step shape in
21
21
  * `docs/building/implementation/security.mdx#verifier-checklist-for-webhooks`
@@ -23,6 +23,7 @@
23
23
  * match the security doc.
24
24
  */
25
25
  import type { JwksResolver } from './jwks';
26
+ import { type PublisherSigningKeyPin } from './publisher-pins';
26
27
  import { type ReplayStore } from './replay';
27
28
  import { type RevocationStore } from './revocation';
28
29
  export declare const WEBHOOK_SIGNING_TAG = "adcp/webhook-signing/v1";
@@ -42,6 +43,10 @@ export interface WebhookRequestLike {
42
43
  export declare const WEBHOOK_MANDATORY_COMPONENTS: ReadonlyArray<string>;
43
44
  export interface VerifyWebhookOptions {
44
45
  jwks: JwksResolver;
46
+ /** Applicable publishers from the verifier's own media-buy record. */
47
+ publisherPins?: readonly PublisherSigningKeyPin[];
48
+ /** Log the underlying request_signature_* discovery cause locally. */
49
+ onKeyResolutionError?: (error: unknown) => void;
45
50
  replayStore: ReplayStore;
46
51
  revocationStore: RevocationStore;
47
52
  /** Now in seconds since epoch. Defaults to `Date.now() / 1000`. */
@@ -28,11 +28,19 @@ module.exports = __toCommonJS(webhook_verifier_exports);
28
28
  var import_canonicalize = require('./canonicalize.js');
29
29
  var import_content_digest = require('./content-digest.js');
30
30
  var import_errors = require('./errors.js');
31
+ var import_errors2 = require('./agent-resolver/errors.js');
32
+ var import_brand_jwks = require('./brand-jwks.js');
31
33
  var import_parser = require('./parser.js');
32
34
  var import_crypto = require('./crypto.js');
35
+ var import_publisher_pins = require('./publisher-pins.js');
33
36
  var import_replay = require('./replay.js');
34
37
  var import_revocation = require('./revocation.js');
35
38
  var import_types = require('./types.js');
39
+ function keyDiscoveryRetryable(error) {
40
+ if (error instanceof import_errors2.AgentResolverError) return import_errors.RequestSigningErrorCodeMetadata[error.code].recovery === "transient";
41
+ if (error instanceof import_brand_jwks.BrandJsonResolverError) return error.code === "fetch_failed";
42
+ return true;
43
+ }
36
44
  const WEBHOOK_SIGNING_TAG = "adcp/webhook-signing/v1";
37
45
  const WEBHOOK_MANDATORY_COMPONENTS = [
38
46
  "@method",
@@ -84,7 +92,22 @@ async function verifyWebhookSignature(request, options) {
84
92
  validateWindow(parsedInput.params.created, parsedInput.params.expires, now);
85
93
  validateCoveredComponents(parsedInput.components);
86
94
  const canonicalWebhookTarget = validateTargetUri(request.url);
87
- const keyResolution = options.jwks.resolveWithMetadata ? await options.jwks.resolveWithMetadata(parsedInput.params.keyid) : { jwk: await options.jwks.resolve(parsedInput.params.keyid) };
95
+ let keyResolution;
96
+ try {
97
+ keyResolution = options.jwks.resolveWithMetadata ? await options.jwks.resolveWithMetadata(parsedInput.params.keyid) : { jwk: await options.jwks.resolve(parsedInput.params.keyid) };
98
+ } catch (error) {
99
+ try {
100
+ options.onKeyResolutionError?.(error);
101
+ } catch {
102
+ }
103
+ throw new import_errors.WebhookSignatureError(
104
+ "webhook_signature_key_unknown",
105
+ 7,
106
+ "Signing key discovery or publisher pin check failed.",
107
+ void 0,
108
+ keyDiscoveryRetryable(error)
109
+ );
110
+ }
88
111
  const jwk = keyResolution.jwk;
89
112
  if (!jwk) {
90
113
  throw new import_errors.WebhookSignatureError(
@@ -166,6 +189,23 @@ async function verifyWebhookSignature(request, options) {
166
189
  "Content-Digest header does not match recomputed body hash."
167
190
  );
168
191
  }
192
+ if (options.publisherPins) {
193
+ try {
194
+ await (0, import_publisher_pins.assertPublisherPins)(jwk, options.publisherPins, currentTime);
195
+ } catch (error) {
196
+ try {
197
+ options.onKeyResolutionError?.(error);
198
+ } catch {
199
+ }
200
+ throw new import_errors.WebhookSignatureError(
201
+ "webhook_signature_key_unknown",
202
+ 7,
203
+ "Signing key is outside the applicable publisher pins.",
204
+ void 0,
205
+ keyDiscoveryRetryable(error)
206
+ );
207
+ }
208
+ }
169
209
  if (keyResolution.operatorAuthorizationValidUntil !== void 0) {
170
210
  assertDelegatedOperatorAuthorizationActive(keyResolution.operatorAuthorizationValidUntil, currentTime());
171
211
  }
@@ -189,7 +229,7 @@ async function verifyWebhookSignature(request, options) {
189
229
  if (keyResolution.operatorAuthorizationValidUntil !== void 0) {
190
230
  assertDelegatedOperatorAuthorizationActive(keyResolution.operatorAuthorizationValidUntil, currentTime());
191
231
  }
192
- const agent_url = options.agentUrlForKeyid?.(jwk.kid);
232
+ const agent_url = keyResolution.agentUrl ?? options.agentUrlForKeyid?.(jwk.kid);
193
233
  return {
194
234
  status: "verified",
195
235
  keyid: jwk.kid,