@astrasyncai/verification-gateway 4.0.0 → 4.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 (134) hide show
  1. package/README.md +106 -29
  2. package/dist/adapter-interface/interface.d.mts +2 -2
  3. package/dist/adapter-interface/interface.d.ts +2 -2
  4. package/dist/adapters/express.d.mts +2 -2
  5. package/dist/adapters/express.d.ts +2 -2
  6. package/dist/adapters/express.js +398 -15
  7. package/dist/adapters/express.js.map +1 -1
  8. package/dist/adapters/express.mjs +398 -15
  9. package/dist/adapters/express.mjs.map +1 -1
  10. package/dist/adapters/http-pdlss.d.mts +3 -3
  11. package/dist/adapters/http-pdlss.d.ts +3 -3
  12. package/dist/adapters/http-pdlss.js.map +1 -1
  13. package/dist/adapters/http-pdlss.mjs.map +1 -1
  14. package/dist/adapters/mcp.d.mts +56 -36
  15. package/dist/adapters/mcp.d.ts +56 -36
  16. package/dist/adapters/mcp.js +220 -3
  17. package/dist/adapters/mcp.js.map +1 -1
  18. package/dist/adapters/mcp.mjs +220 -3
  19. package/dist/adapters/mcp.mjs.map +1 -1
  20. package/dist/adapters/nextjs.d.mts +2 -2
  21. package/dist/adapters/nextjs.d.ts +2 -2
  22. package/dist/adapters/nextjs.js +433 -18
  23. package/dist/adapters/nextjs.js.map +1 -1
  24. package/dist/adapters/nextjs.mjs +433 -18
  25. package/dist/adapters/nextjs.mjs.map +1 -1
  26. package/dist/adapters/sdk.d.mts +2 -2
  27. package/dist/adapters/sdk.d.ts +2 -2
  28. package/dist/adapters/sdk.js +2 -2
  29. package/dist/adapters/sdk.js.map +1 -1
  30. package/dist/adapters/sdk.mjs +2 -2
  31. package/dist/adapters/sdk.mjs.map +1 -1
  32. package/dist/agent/index.d.mts +2 -2
  33. package/dist/agent/index.d.ts +2 -2
  34. package/dist/agent/index.js.map +1 -1
  35. package/dist/agent/index.mjs.map +1 -1
  36. package/dist/bin/astrasync-claude-hook.js +13 -13
  37. package/dist/bin/astrasync-codex-hook.js +13 -13
  38. package/dist/bin/astrasync-guard.js +13 -13
  39. package/dist/bin/astrasync.js +80 -26
  40. package/dist/browser/background.js +13 -13
  41. package/dist/browser/background.js.map +1 -1
  42. package/dist/browser/background.mjs +13 -13
  43. package/dist/browser/background.mjs.map +1 -1
  44. package/dist/browser/browser-adapter.d.mts +2 -2
  45. package/dist/browser/browser-adapter.d.ts +2 -2
  46. package/dist/claude-code/claude-code-adapter.d.mts +2 -2
  47. package/dist/claude-code/claude-code-adapter.d.ts +2 -2
  48. package/dist/cli/index.d.mts +2 -2
  49. package/dist/cli/index.d.ts +2 -2
  50. package/dist/codex/index.d.mts +3 -3
  51. package/dist/codex/index.d.ts +3 -3
  52. package/dist/codex/index.js +13 -13
  53. package/dist/codex/index.js.map +1 -1
  54. package/dist/codex/index.mjs +13 -13
  55. package/dist/codex/index.mjs.map +1 -1
  56. package/dist/cursor/cursor-adapter.d.mts +2 -2
  57. package/dist/cursor/cursor-adapter.d.ts +2 -2
  58. package/dist/cursor/extension.d.mts +2 -2
  59. package/dist/cursor/extension.d.ts +2 -2
  60. package/dist/cursor/extension.js +13 -13
  61. package/dist/cursor/extension.js.map +1 -1
  62. package/dist/cursor/extension.mjs +13 -13
  63. package/dist/cursor/extension.mjs.map +1 -1
  64. package/dist/edge-config.d.mts +22 -7
  65. package/dist/edge-config.d.ts +22 -7
  66. package/dist/edge-config.js.map +1 -1
  67. package/dist/edge-config.mjs.map +1 -1
  68. package/dist/edge-core/index.d.mts +425 -0
  69. package/dist/edge-core/index.d.ts +425 -0
  70. package/dist/edge-core/index.js +1482 -0
  71. package/dist/edge-core/index.js.map +1 -0
  72. package/dist/edge-core/index.mjs +1437 -0
  73. package/dist/edge-core/index.mjs.map +1 -0
  74. package/dist/{express-BZajRs4i.d.mts → express-BVd1_3FE.d.ts} +9 -7
  75. package/dist/{express-CjCUB1k7.d.ts → express-D_4hTn5Z.d.mts} +9 -7
  76. package/dist/gateway/gateway.d.mts +2 -2
  77. package/dist/gateway/gateway.d.ts +2 -2
  78. package/dist/gateway/gateway.js +13 -13
  79. package/dist/gateway/gateway.js.map +1 -1
  80. package/dist/gateway/gateway.mjs +13 -13
  81. package/dist/gateway/gateway.mjs.map +1 -1
  82. package/dist/git-trigger/git-hooks.d.mts +2 -2
  83. package/dist/git-trigger/git-hooks.d.ts +2 -2
  84. package/dist/{index-adgKhujR.d.ts → index-B0YHu_SP.d.ts} +30 -27
  85. package/dist/{index-B0cyMZDL.d.mts → index-BPEBlOsE.d.mts} +30 -27
  86. package/dist/{index-BRYteOqO.d.mts → index-DQb5-_1X.d.mts} +1 -1
  87. package/dist/{index-BZQdVqNw.d.ts → index-T1aBoUcc.d.ts} +1 -1
  88. package/dist/index.d.mts +11 -11
  89. package/dist/index.d.ts +11 -11
  90. package/dist/index.js +477 -265
  91. package/dist/index.js.map +1 -1
  92. package/dist/index.mjs +474 -265
  93. package/dist/index.mjs.map +1 -1
  94. package/dist/local-evaluator/evaluator.d.mts +2 -2
  95. package/dist/local-evaluator/evaluator.d.ts +2 -2
  96. package/dist/metadata-capture.d.mts +63 -4
  97. package/dist/metadata-capture.d.ts +63 -4
  98. package/dist/metadata-capture.js +67 -0
  99. package/dist/metadata-capture.js.map +1 -1
  100. package/dist/metadata-capture.mjs +64 -0
  101. package/dist/metadata-capture.mjs.map +1 -1
  102. package/dist/{nextjs-DeUTnOBd.d.mts → nextjs-WyeVr2Kp.d.mts} +3 -3
  103. package/dist/{nextjs-D_iFf5k-.d.ts → nextjs-w7RTbP1P.d.ts} +3 -3
  104. package/dist/platform-signatures.d.mts +5 -6
  105. package/dist/platform-signatures.d.ts +5 -6
  106. package/dist/platform-signatures.js.map +1 -1
  107. package/dist/platform-signatures.mjs.map +1 -1
  108. package/dist/registration/index.d.mts +10 -11
  109. package/dist/registration/index.d.ts +10 -11
  110. package/dist/registration/index.js.map +1 -1
  111. package/dist/registration/index.mjs.map +1 -1
  112. package/dist/{sdk-BM3mwEAq.d.ts → sdk-CbTNIkAa.d.mts} +4 -4
  113. package/dist/{sdk-_il1Q41f.d.mts → sdk-QeX0Z8Ho.d.ts} +4 -4
  114. package/dist/transport/index.d.mts +2 -2
  115. package/dist/transport/index.d.ts +2 -2
  116. package/dist/transport/index.js.map +1 -1
  117. package/dist/transport/index.mjs.map +1 -1
  118. package/dist/{types-CHKCgDFC.d.ts → types-BLUx92FJ.d.ts} +1 -1
  119. package/dist/{types-DfnP7JiM.d.mts → types-DqfPU5Bl.d.mts} +1 -1
  120. package/dist/{types-BcDf9Kk0.d.mts → types-r850cOt0.d.mts} +106 -88
  121. package/dist/{types-fTQD448H.d.ts → types-sMxBT-nM.d.ts} +106 -88
  122. package/dist/ui/index.d.mts +11 -7
  123. package/dist/ui/index.d.ts +11 -7
  124. package/dist/ui/index.js +13 -9
  125. package/dist/ui/index.js.map +1 -1
  126. package/dist/ui/index.mjs +10 -8
  127. package/dist/ui/index.mjs.map +1 -1
  128. package/dist/verify.d.mts +8 -7
  129. package/dist/verify.d.ts +8 -7
  130. package/dist/verify.js +1 -1
  131. package/dist/verify.js.map +1 -1
  132. package/dist/verify.mjs +1 -1
  133. package/dist/verify.mjs.map +1 -1
  134. package/package.json +8 -2
@@ -65,7 +65,7 @@ function getTrustLevel(score) {
65
65
  }
66
66
 
67
67
  // src/version.ts
68
- var SDK_VERSION = "4.0.0";
68
+ var SDK_VERSION = "4.1.0";
69
69
 
70
70
  // src/well-known.ts
71
71
  var CACHE_TTL_MS = 60 * 60 * 1e3;
@@ -204,6 +204,40 @@ function cacheResult(request, result, configuredTtl, counterpartyId) {
204
204
  expiresAt: Date.now() + ttlSeconds * 1e3
205
205
  });
206
206
  }
207
+ function extractCredentials(headers, query) {
208
+ const credentials = {};
209
+ const ASTRA_ID_PATTERN = /^ASTRAE?-[A-Za-z0-9_-]{1,64}$/;
210
+ const astraIdHeader = headers["x-astra-id"] || headers["X-Astra-Id"] || headers["X-ASTRA-ID"] || headers["x-astra-agentid"] || headers["X-Astra-AgentId"] || headers["x-astra-agent-id"] || headers["X-Astra-Agent-Id"] || headers["X-ASTRA-AGENT-ID"];
211
+ if (astraIdHeader) {
212
+ const raw = Array.isArray(astraIdHeader) ? astraIdHeader[0] : typeof astraIdHeader === "string" ? astraIdHeader : void 0;
213
+ if (typeof raw === "string" && ASTRA_ID_PATTERN.test(raw)) {
214
+ credentials.astraId = raw;
215
+ }
216
+ }
217
+ const apiKeyHeader = headers["x-api-key"] || headers["X-Api-Key"] || headers["X-API-KEY"];
218
+ if (apiKeyHeader) {
219
+ credentials.apiKey = Array.isArray(apiKeyHeader) ? apiKeyHeader[0] : apiKeyHeader;
220
+ }
221
+ const authHeader = headers["authorization"] || headers["Authorization"];
222
+ if (authHeader) {
223
+ const authValue = Array.isArray(authHeader) ? authHeader[0] : authHeader;
224
+ if (typeof authValue === "string") {
225
+ credentials.authorizationHeader = authValue;
226
+ if (authValue.startsWith("Bearer ")) {
227
+ credentials.jwt = authValue.slice(7);
228
+ }
229
+ }
230
+ }
231
+ if (query) {
232
+ if (query.astraId && !credentials.astraId) {
233
+ credentials.astraId = query.astraId;
234
+ }
235
+ if (query.apiKey && !credentials.apiKey) {
236
+ credentials.apiKey = query.apiKey;
237
+ }
238
+ }
239
+ return credentials;
240
+ }
207
241
  function createGuidanceResponse(_config, reason, options = {}) {
208
242
  const source = options.source ?? "no_credentials";
209
243
  const isApiError = source === "api_error";
@@ -578,6 +612,21 @@ async function fetchRoutes(config, counterpartyId) {
578
612
  return null;
579
613
  }
580
614
  }
615
+ async function reportUnregisteredAttempt(config, data) {
616
+ const apiBaseUrl = config.apiBaseUrl || DEFAULT_CONFIG.apiBaseUrl;
617
+ const { counterpartyUrl, ...rest } = data;
618
+ const body = {
619
+ ...rest,
620
+ ...counterpartyUrl && { counterpartyUrl },
621
+ ...config.counterpartyId && { counterpartyId: config.counterpartyId }
622
+ };
623
+ await fetch(`${apiBaseUrl}/verification-activity/unregistered-attempt`, {
624
+ method: "POST",
625
+ headers: { "Content-Type": "application/json" },
626
+ body: JSON.stringify(body)
627
+ }).catch(() => {
628
+ });
629
+ }
581
630
  async function reportCounterpartyPreCheckFailure(config, data) {
582
631
  const apiBaseUrl = config.apiBaseUrl || DEFAULT_CONFIG.apiBaseUrl;
583
632
  await fetch(`${apiBaseUrl}/verification-activity/counterparty-pre-check-failure`, {
@@ -683,6 +732,7 @@ function performCounterpartyPreCheck(routeConfig, astraCreds, purpose) {
683
732
  }
684
733
 
685
734
  // src/metadata-capture.ts
735
+ var CAPTURE_SCHEMA_VERSION = 1;
686
736
  var SECRET_HEADER_NAMES = /* @__PURE__ */ new Set([
687
737
  "cookie",
688
738
  "set-cookie",
@@ -819,6 +869,359 @@ function sanitizeHeaders(raw) {
819
869
  if (Object.keys(platformHeaders).length > 0) result.platformHeaders = platformHeaders;
820
870
  return result;
821
871
  }
872
+ var CONNECTION_HEADER_SOURCES = [
873
+ ["ip", ["cf-connecting-ip", "fastly-client-ip", "true-client-ip", "x-real-ip"]],
874
+ [
875
+ "country",
876
+ ["cf-ipcountry", "x-vercel-ip-country", "cloudfront-viewer-country", "fastly-geo-country"]
877
+ ],
878
+ ["region", ["x-vercel-ip-country-region", "cloudfront-viewer-country-region"]],
879
+ ["city", ["x-vercel-ip-city", "cloudfront-viewer-city"]],
880
+ ["asn", ["cloudfront-viewer-asn"]],
881
+ ["tlsVersion", ["cloudfront-viewer-tls"]],
882
+ ["httpVersion", ["cloudfront-viewer-http-version"]],
883
+ // TLS fingerprints can only be computed where TLS terminates. When a CDN
884
+ // terminates TLS in front of the merchant, its fingerprint headers ARE the
885
+ // connection-layer truth — normalising them here is the permanent design.
886
+ ["tlsFingerprint", ["cf-ja3-hash"]],
887
+ ["ja4", ["cf-ja4"]]
888
+ ];
889
+ function deriveConnectionFromHeaders(headers) {
890
+ if (!headers) return void 0;
891
+ const lower = {};
892
+ for (const name of Object.keys(headers)) {
893
+ const value = headers[name];
894
+ if (typeof value === "string" && value.length > 0) lower[name.toLowerCase()] = value;
895
+ }
896
+ const connection = {};
897
+ for (const [key, sources] of CONNECTION_HEADER_SOURCES) {
898
+ for (const source of sources) {
899
+ const value = lower[source]?.trim();
900
+ if (value) {
901
+ connection[key] = value;
902
+ break;
903
+ }
904
+ }
905
+ }
906
+ if (!connection.ip) {
907
+ const xff = lower["x-forwarded-for"];
908
+ const client = xff?.split(",")[0]?.trim();
909
+ if (client) connection.ip = client;
910
+ }
911
+ return Object.keys(connection).length > 0 ? connection : void 0;
912
+ }
913
+ function buildSdkObservedMetadata(rawHeaders) {
914
+ const { headers, apiKeyFormat, platformHeaders } = sanitizeHeaders(rawHeaders);
915
+ const flat = {};
916
+ if (rawHeaders) {
917
+ for (const name of Object.keys(rawHeaders)) {
918
+ const value = rawHeaders[name];
919
+ if (value === void 0) continue;
920
+ flat[name.toLowerCase()] = Array.isArray(value) ? value.join(", ") : String(value);
921
+ }
922
+ }
923
+ const connection = deriveConnectionFromHeaders(flat);
924
+ return {
925
+ headers,
926
+ ...apiKeyFormat && { apiKeyFormat },
927
+ ...platformHeaders && { platformHeaders },
928
+ ...connection && { connection },
929
+ schemaVersion: CAPTURE_SCHEMA_VERSION
930
+ };
931
+ }
932
+
933
+ // src/edge-config.ts
934
+ var DEFAULT_EDGE_CONFIG = Object.freeze({
935
+ version: 1,
936
+ mode: "observe",
937
+ depth: "classify",
938
+ pathRules: [],
939
+ sampling: Object.freeze({ anonymousBeaconRate: 0.1 }),
940
+ failurePosture: "open"
941
+ });
942
+ var EDGE_MODES = ["observe", "enforce"];
943
+ var EDGE_DEPTHS = ["classify", "authenticate", "authorize"];
944
+ function isEdgeConfig(value) {
945
+ if (typeof value !== "object" || value === null) return false;
946
+ const v = value;
947
+ if (v.version !== 1) return false;
948
+ if (!EDGE_MODES.includes(v.mode)) return false;
949
+ if (!EDGE_DEPTHS.includes(v.depth)) return false;
950
+ if (!Array.isArray(v.pathRules)) return false;
951
+ for (const rule of v.pathRules) {
952
+ if (typeof rule !== "object" || rule === null) return false;
953
+ const r = rule;
954
+ if (typeof r.pattern !== "string" || r.pattern.length === 0) return false;
955
+ if (r.mode !== void 0 && !EDGE_MODES.includes(r.mode)) return false;
956
+ if (r.depth !== void 0 && !EDGE_DEPTHS.includes(r.depth)) return false;
957
+ }
958
+ const sampling = v.sampling;
959
+ if (typeof sampling !== "object" || sampling === null || typeof sampling.anonymousBeaconRate !== "number" || sampling.anonymousBeaconRate < 0 || sampling.anonymousBeaconRate > 1) {
960
+ return false;
961
+ }
962
+ return v.failurePosture === "open";
963
+ }
964
+ async function fetchEdgeConfig(config, counterpartyId, opts) {
965
+ if (!counterpartyId) return null;
966
+ const headers = { Accept: "application/json" };
967
+ if (config.apiKey) {
968
+ headers["Authorization"] = `Bearer ${config.apiKey}`;
969
+ headers["X-API-Key"] = config.apiKey;
970
+ }
971
+ if (opts?.etag) headers["If-None-Match"] = opts.etag;
972
+ try {
973
+ const response = await fetch(
974
+ `${config.apiBaseUrl}/endpoints/${encodeURIComponent(counterpartyId)}/edge-config`,
975
+ { method: "GET", headers, signal: AbortSignal.timeout(opts?.timeoutMs ?? 5e3) }
976
+ );
977
+ if (response.status === 304) return { notModified: true };
978
+ if (!response.ok) return null;
979
+ const body = await response.json();
980
+ const edgeConfig = body.data?.edgeConfig;
981
+ if (!isEdgeConfig(edgeConfig)) return null;
982
+ return { edgeConfig, etag: response.headers.get("etag") ?? void 0 };
983
+ } catch {
984
+ return null;
985
+ }
986
+ }
987
+ var DEFAULT_TTL_MS = 60 * 1e3;
988
+ var DEFAULT_MAX_STALE_MS = 24 * 60 * 60 * 1e3;
989
+ var cache2 = /* @__PURE__ */ new Map();
990
+ var inflight = /* @__PURE__ */ new Map();
991
+ function cacheKey(apiBaseUrl, counterpartyId) {
992
+ return `${apiBaseUrl}|${counterpartyId}`;
993
+ }
994
+ function degradeToObserve(config) {
995
+ if (config.mode !== "enforce" && !config.pathRules.some((r) => r.mode === "enforce")) {
996
+ return config;
997
+ }
998
+ return {
999
+ ...config,
1000
+ mode: "observe",
1001
+ pathRules: config.pathRules.map((r) => r.mode === "enforce" ? { ...r, mode: "observe" } : r)
1002
+ };
1003
+ }
1004
+ async function revalidate(config, counterpartyId) {
1005
+ const key = cacheKey(config.apiBaseUrl, counterpartyId);
1006
+ const existing = inflight.get(key);
1007
+ if (existing) return existing;
1008
+ const promise = fetchEdgeConfig(config, counterpartyId, { etag: cache2.get(key)?.etag }).then((result) => {
1009
+ if (result === null) return;
1010
+ const now = Date.now();
1011
+ if ("notModified" in result) {
1012
+ const entry = cache2.get(key);
1013
+ if (entry) entry.lastSuccessAt = now;
1014
+ return;
1015
+ }
1016
+ cache2.set(key, { config: result.edgeConfig, etag: result.etag, lastSuccessAt: now });
1017
+ }).finally(() => {
1018
+ inflight.delete(key);
1019
+ });
1020
+ inflight.set(key, promise);
1021
+ return promise;
1022
+ }
1023
+ async function getEdgeConfig(config, counterpartyId, opts) {
1024
+ const ttlMs = opts?.ttlMs ?? DEFAULT_TTL_MS;
1025
+ const maxStaleMs = opts?.maxStaleMs ?? DEFAULT_MAX_STALE_MS;
1026
+ const key = cacheKey(config.apiBaseUrl, counterpartyId);
1027
+ const entry = cache2.get(key);
1028
+ if (entry) {
1029
+ const age = Date.now() - entry.lastSuccessAt;
1030
+ if (age > ttlMs) {
1031
+ revalidate(config, counterpartyId).catch(() => {
1032
+ });
1033
+ }
1034
+ return age > maxStaleMs ? degradeToObserve(entry.config) : entry.config;
1035
+ }
1036
+ await revalidate(config, counterpartyId).catch(() => {
1037
+ });
1038
+ return cache2.get(key)?.config ?? DEFAULT_EDGE_CONFIG;
1039
+ }
1040
+
1041
+ // src/platform-signatures.ts
1042
+ var PLATFORM_AGENT_SIGNATURES = [
1043
+ {
1044
+ vendor: "claude",
1045
+ displayName: "Anthropic Claude",
1046
+ uaPatterns: [/claude/i, /anthropic/i],
1047
+ agentCardPatterns: [/anthropic\.com/i, /claude\.ai/i],
1048
+ apiKeyFormatPatterns: [/^sk-ant/i],
1049
+ headerPatterns: [/^anthropic-version/i, /^anthropic-beta/i, /^x-anthropic-/i]
1050
+ },
1051
+ {
1052
+ vendor: "chatgpt",
1053
+ displayName: "OpenAI ChatGPT",
1054
+ uaPatterns: [/chatgpt/i, /openai/i, /gpt-?\d/i],
1055
+ agentCardPatterns: [/openai\.com/i, /chatgpt\.com/i],
1056
+ apiKeyFormatPatterns: [/^sk-proj/i, /^sk-svcacct/i],
1057
+ headerPatterns: [/^openai-organization/i, /^openai-project/i, /^openai-version/i]
1058
+ },
1059
+ {
1060
+ vendor: "gemini",
1061
+ displayName: "Google Gemini",
1062
+ uaPatterns: [/gemini/i, /google-gemini/i, /bard/i],
1063
+ agentCardPatterns: [/gemini\.google\.com/i, /ai\.google\.dev/i],
1064
+ apiKeyFormatPatterns: [/^aiza/i],
1065
+ headerPatterns: [/^x-goog-user-project/i, /^x-goog-api-client/i]
1066
+ },
1067
+ {
1068
+ vendor: "cursor",
1069
+ displayName: "Cursor",
1070
+ uaPatterns: [/cursor/i],
1071
+ agentCardPatterns: [/cursor\.sh/i, /cursor\.com/i]
1072
+ },
1073
+ {
1074
+ vendor: "goose",
1075
+ displayName: "Block Goose",
1076
+ uaPatterns: [/goose/i, /block-goose/i],
1077
+ agentCardPatterns: [/block\.xyz\/goose/i, /goose\.tools/i]
1078
+ },
1079
+ {
1080
+ vendor: "perplexity",
1081
+ displayName: "Perplexity",
1082
+ uaPatterns: [/perplexity/i],
1083
+ agentCardPatterns: [/perplexity\.ai/i],
1084
+ apiKeyFormatPatterns: [/^pplx-/i]
1085
+ }
1086
+ ];
1087
+ function matchPlatformSignature(signatures, input) {
1088
+ const ua = input.userAgent ?? "";
1089
+ const cardUrl = input.agentCardUrl ?? "";
1090
+ const apiKeyFormat = input.apiKeyFormat ?? "";
1091
+ const headerLines = [];
1092
+ if (input.platformHeaders) {
1093
+ for (const [name, value] of Object.entries(input.platformHeaders)) {
1094
+ headerLines.push(name, `${name}: ${value}`);
1095
+ }
1096
+ }
1097
+ for (const sig of signatures) {
1098
+ const uaMatch = sig.uaPatterns?.some((p) => p.test(ua));
1099
+ const cardMatch = sig.agentCardPatterns?.some((p) => p.test(cardUrl));
1100
+ const keyMatch = apiKeyFormat.length > 0 && sig.apiKeyFormatPatterns?.some((p) => p.test(apiKeyFormat));
1101
+ const headerMatch = headerLines.length > 0 && sig.headerPatterns?.some((p) => headerLines.some((line) => p.test(line)));
1102
+ if (uaMatch || cardMatch || keyMatch || headerMatch) {
1103
+ const uaBase = ua.split(/[\s/]/)[0]?.toLowerCase() ?? "";
1104
+ const cardHost = (() => {
1105
+ try {
1106
+ return cardUrl ? new URL(cardUrl).host : "";
1107
+ } catch {
1108
+ return "";
1109
+ }
1110
+ })();
1111
+ const keyBasis = uaBase || cardHost || apiKeyFormat || "unknown";
1112
+ return {
1113
+ vendor: sig.vendor,
1114
+ fingerprintKey: `${sig.vendor}:${keyBasis}`,
1115
+ displayName: sig.displayName
1116
+ };
1117
+ }
1118
+ }
1119
+ return void 0;
1120
+ }
1121
+ function detectPlatformFingerprint(input) {
1122
+ return matchPlatformSignature(PLATFORM_AGENT_SIGNATURES, input);
1123
+ }
1124
+
1125
+ // src/edge-core/classify.ts
1126
+ var STATIC_EXTENSIONS = /\.(css|js|mjs|map|png|jpe?g|gif|webp|avif|svg|ico|woff2?|ttf|otf|eot|mp4|webm|pdf|txt|xml)$/i;
1127
+ var BOT_UA_PATTERN = /curl|wget|python-requests|python-urllib|node-fetch|axios|got|okhttp|go-http-client|libwww|httpie|java\/|ruby|php|scrapy|headless|phantomjs|selenium|playwright|puppeteer|bot|crawler|spider|slurp/i;
1128
+ var BROWSER_UA_PATTERN = /mozilla\/\d/i;
1129
+ var _patterns = { STATIC_EXTENSIONS, BOT_UA_PATTERN, BROWSER_UA_PATTERN };
1130
+
1131
+ // src/edge-core/events.ts
1132
+ function sampled(rate, random = Math.random) {
1133
+ if (rate <= 0) return false;
1134
+ if (rate >= 1) return true;
1135
+ return random() < rate;
1136
+ }
1137
+
1138
+ // src/adapters/anonymous-beacon.ts
1139
+ function readHeader(raw, name) {
1140
+ const value = raw[name];
1141
+ if (typeof value === "string") return value;
1142
+ if (Array.isArray(value)) return value[0];
1143
+ return void 0;
1144
+ }
1145
+ function classifyPassThroughTraffic(path, rawHeaders) {
1146
+ if (_patterns.STATIC_EXTENSIONS.test(path)) return { kind: "never" };
1147
+ const credentials = extractCredentials(rawHeaders, {});
1148
+ if (credentials.astraId || credentials.apiKey || credentials.jwt) {
1149
+ return { kind: "never" };
1150
+ }
1151
+ const userAgent = readHeader(rawHeaders, "user-agent") ?? "";
1152
+ const { apiKeyFormat, platformHeaders } = sanitizeHeaders(rawHeaders);
1153
+ const fingerprint = detectPlatformFingerprint({ userAgent, apiKeyFormat, platformHeaders });
1154
+ if (fingerprint) {
1155
+ return {
1156
+ kind: "platform",
1157
+ protocol: "platform-ua",
1158
+ evidence: { vendor: fingerprint.vendor, userAgent: userAgent || "(none)" }
1159
+ };
1160
+ }
1161
+ if (!userAgent || _patterns.BOT_UA_PATTERN.test(userAgent) || !_patterns.BROWSER_UA_PATTERN.test(userAgent)) {
1162
+ return {
1163
+ kind: "anonymous-bot",
1164
+ protocol: "ua-heuristic",
1165
+ evidence: { userAgent: userAgent || "(none)" }
1166
+ };
1167
+ }
1168
+ if (!readHeader(rawHeaders, "sec-fetch-mode") && !readHeader(rawHeaders, "accept-language")) {
1169
+ return {
1170
+ kind: "anonymous-bot",
1171
+ protocol: "ua-heuristic",
1172
+ evidence: { userAgent, spoofedBrowser: true }
1173
+ };
1174
+ }
1175
+ return { kind: "never" };
1176
+ }
1177
+ function createAnonymousBeacon(config, options) {
1178
+ const counterpartyId = config.counterpartyId;
1179
+ if (!counterpartyId) return () => {
1180
+ };
1181
+ const random = options?.random ?? Math.random;
1182
+ let edgeConfig;
1183
+ let refreshing = false;
1184
+ const refreshConfig = () => {
1185
+ if (refreshing) return;
1186
+ refreshing = true;
1187
+ getEdgeConfig(config, counterpartyId).then((cfg) => {
1188
+ edgeConfig = cfg;
1189
+ }).catch(() => {
1190
+ }).finally(() => {
1191
+ refreshing = false;
1192
+ });
1193
+ };
1194
+ refreshConfig();
1195
+ return (input) => {
1196
+ try {
1197
+ refreshConfig();
1198
+ const cfg = edgeConfig;
1199
+ if (!cfg) return;
1200
+ const traffic = classifyPassThroughTraffic(input.requestPath, input.rawHeaders);
1201
+ if (traffic.kind === "never") return;
1202
+ if (traffic.kind === "anonymous-bot" && !sampled(cfg.sampling.anonymousBeaconRate, random)) {
1203
+ return;
1204
+ }
1205
+ const userAgent = readHeader(input.rawHeaders, "user-agent");
1206
+ const xff = input.rawHeaders["x-forwarded-for"];
1207
+ const xffStr = Array.isArray(xff) ? xff.join(", ") : xff;
1208
+ const sourceIp = input.sourceIp ?? xffStr?.split(",")[0]?.trim();
1209
+ reportUnregisteredAttempt(config, {
1210
+ ...input.counterpartyUrl && { counterpartyUrl: input.counterpartyUrl },
1211
+ counterpartyType: options?.counterpartyType ?? config.counterpartyType ?? "api",
1212
+ ...sourceIp && { sourceIp },
1213
+ ...userAgent && { userAgent },
1214
+ requestPath: input.requestPath,
1215
+ requestMethod: input.requestMethod,
1216
+ ...traffic.protocol && { protocol: traffic.protocol },
1217
+ ...traffic.evidence && { evidence: traffic.evidence },
1218
+ observedMetadata: buildSdkObservedMetadata(input.rawHeaders)
1219
+ }).catch(() => {
1220
+ });
1221
+ } catch {
1222
+ }
1223
+ };
1224
+ }
822
1225
 
823
1226
  // src/adapters/http-pdlss.ts
824
1227
  var HTTP_METHOD_ACTION_TABLE = {
@@ -982,7 +1385,7 @@ function resolveNextPdlss(request, routeConfig) {
982
1385
  routeAction: routeConfig?.action
983
1386
  });
984
1387
  }
985
- function generateCommerceShieldHtml(result, options) {
1388
+ function generateInterstitialHtml(result, options) {
986
1389
  const title = escapeHtml(options.commerceShield?.title || "AstraSync Agent Verification");
987
1390
  const message = escapeHtml(
988
1391
  options.commerceShield?.message || result.guidance?.message || "This site verifies AI agents before granting access. We noticed you're visiting without AstraSync credentials."
@@ -1138,13 +1541,31 @@ function generateCommerceShieldHtml(result, options) {
1138
1541
  }
1139
1542
  var DEFAULT_ROUTES_REFRESH_MS = 5 * 60 * 1e3;
1140
1543
  function createMiddleware(options) {
1544
+ if ("routes" in options) {
1545
+ console.warn(
1546
+ "[VerificationGateway/Next.js] `routes` is dashboard-managed since v2.9.7; this option is ignored \u2014 configure route policy in your AstraSync dashboard."
1547
+ );
1548
+ }
1141
1549
  const {
1142
1550
  skipPaths = [],
1143
- showCommerceShield = true,
1551
+ showInterstitial,
1552
+ showCommerceShield,
1144
1553
  enableRuntimeChallenge = true,
1145
1554
  routesRefreshMs = DEFAULT_ROUTES_REFRESH_MS,
1555
+ anonymousBeacon = true,
1146
1556
  ...config
1147
1557
  } = options;
1558
+ const interstitialEnabled = showInterstitial ?? showCommerceShield ?? true;
1559
+ const emitAnonymousBeacon = anonymousBeacon ? createAnonymousBeacon(config, { counterpartyType: config.counterpartyType ?? "website" }) : void 0;
1560
+ const beaconPassThrough = emitAnonymousBeacon ? (request, pathname) => {
1561
+ emitAnonymousBeacon({
1562
+ requestPath: pathname,
1563
+ requestMethod: request.method,
1564
+ counterpartyUrl: config.counterpartyUrl || request.nextUrl.origin,
1565
+ sourceIp: request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() || void 0,
1566
+ rawHeaders: Object.fromEntries(request.headers.entries())
1567
+ });
1568
+ } : void 0;
1148
1569
  let cachedRoutes = [];
1149
1570
  let lastFetchAt = 0;
1150
1571
  let refreshing = null;
@@ -1186,9 +1607,11 @@ function createMiddleware(options) {
1186
1607
  }
1187
1608
  const routeConfig = findRouteConfig(cachedRoutes, pathname, request.method);
1188
1609
  if (!routeConfig) {
1610
+ beaconPassThrough?.(request, pathname);
1189
1611
  return NextResponse.next();
1190
1612
  }
1191
1613
  if (routeConfig.minAccessLevel === "none") {
1614
+ beaconPassThrough?.(request, pathname);
1192
1615
  return NextResponse.next();
1193
1616
  }
1194
1617
  const credentials = extractCredentialsFromNextRequest(request);
@@ -1240,8 +1663,8 @@ function createMiddleware(options) {
1240
1663
  { status: 403 }
1241
1664
  );
1242
1665
  }
1243
- if (showCommerceShield) {
1244
- return new NextResponse(generateCommerceShieldHtml(preCheckResult, options), {
1666
+ if (interstitialEnabled) {
1667
+ return new NextResponse(generateInterstitialHtml(preCheckResult, options), {
1245
1668
  status: 200,
1246
1669
  headers: {
1247
1670
  "Content-Type": "text/html",
@@ -1269,17 +1692,9 @@ function createMiddleware(options) {
1269
1692
  host: request.headers.get("host") || void 0,
1270
1693
  forwardedFor,
1271
1694
  agentCardUrl: request.headers.get("x-astrasync-agent-card") || void 0,
1272
- // Workstream D — full sanitised header capture (Web Headers → plain map).
1273
- observedMetadata: (() => {
1274
- const { headers, apiKeyFormat, platformHeaders } = sanitizeHeaders(
1275
- Object.fromEntries(request.headers.entries())
1276
- );
1277
- return {
1278
- headers,
1279
- ...apiKeyFormat && { apiKeyFormat },
1280
- ...platformHeaders && { platformHeaders }
1281
- };
1282
- })()
1695
+ // Workstream D — full sanitised header capture + CDN-derived
1696
+ // connection block (Web Headers → plain map).
1697
+ observedMetadata: buildSdkObservedMetadata(Object.fromEntries(request.headers.entries()))
1283
1698
  }
1284
1699
  };
1285
1700
  let result = await verify(config, verificationRequest);
@@ -1334,8 +1749,8 @@ function createMiddleware(options) {
1334
1749
  }
1335
1750
  );
1336
1751
  }
1337
- if (showCommerceShield) {
1338
- return new NextResponse(generateCommerceShieldHtml(result, options), {
1752
+ if (interstitialEnabled) {
1753
+ return new NextResponse(generateInterstitialHtml(result, options), {
1339
1754
  status: 200,
1340
1755
  headers: {
1341
1756
  "Content-Type": "text/html",