@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
package/dist/index.js CHANGED
@@ -36,6 +36,7 @@ __export(src_exports, {
36
36
  AstraSync: () => AstraSync,
37
37
  AstraSyncError: () => AstraSyncError,
38
38
  AuthenticationError: () => AuthenticationError,
39
+ CAPTURE_SCHEMA_VERSION: () => CAPTURE_SCHEMA_VERSION,
39
40
  ChallengeHandler: () => ChallengeHandler,
40
41
  DEFAULT_EDGE_CONFIG: () => DEFAULT_EDGE_CONFIG,
41
42
  DEFAULT_TRUST_THRESHOLDS: () => DEFAULT_TRUST_THRESHOLDS,
@@ -54,9 +55,11 @@ __export(src_exports, {
54
55
  authorizeSettlement: () => authorizeSettlement,
55
56
  awaitStepUpApproval: () => awaitStepUpApproval,
56
57
  buildGuidance: () => buildGuidance,
58
+ buildSdkObservedMetadata: () => buildSdkObservedMetadata,
57
59
  clearCache: () => clearCache,
58
60
  createMcpMiddleware: () => createMcpMiddleware,
59
61
  degradeToObserve: () => degradeToObserve,
62
+ deriveConnectionFromHeaders: () => deriveConnectionFromHeaders,
60
63
  detectPlatformFingerprint: () => detectPlatformFingerprint,
61
64
  determineAccessLevel: () => determineAccessLevel,
62
65
  express: () => express_exports,
@@ -214,7 +217,7 @@ function getCapabilities(accessLevel) {
214
217
  }
215
218
 
216
219
  // src/version.ts
217
- var SDK_VERSION = "4.0.0";
220
+ var SDK_VERSION = "4.1.0";
218
221
 
219
222
  // src/well-known.ts
220
223
  var CACHE_TTL_MS = 60 * 60 * 1e3;
@@ -1212,6 +1215,7 @@ function performCounterpartyPreCheck(routeConfig, astraCreds, purpose) {
1212
1215
  }
1213
1216
 
1214
1217
  // src/metadata-capture.ts
1218
+ var CAPTURE_SCHEMA_VERSION = 1;
1215
1219
  var SECRET_HEADER_NAMES = /* @__PURE__ */ new Set([
1216
1220
  "cookie",
1217
1221
  "set-cookie",
@@ -1356,6 +1360,373 @@ function extractPlatformHeaders(sanitized) {
1356
1360
  }
1357
1361
  return out;
1358
1362
  }
1363
+ var CONNECTION_HEADER_SOURCES = [
1364
+ ["ip", ["cf-connecting-ip", "fastly-client-ip", "true-client-ip", "x-real-ip"]],
1365
+ [
1366
+ "country",
1367
+ ["cf-ipcountry", "x-vercel-ip-country", "cloudfront-viewer-country", "fastly-geo-country"]
1368
+ ],
1369
+ ["region", ["x-vercel-ip-country-region", "cloudfront-viewer-country-region"]],
1370
+ ["city", ["x-vercel-ip-city", "cloudfront-viewer-city"]],
1371
+ ["asn", ["cloudfront-viewer-asn"]],
1372
+ ["tlsVersion", ["cloudfront-viewer-tls"]],
1373
+ ["httpVersion", ["cloudfront-viewer-http-version"]],
1374
+ // TLS fingerprints can only be computed where TLS terminates. When a CDN
1375
+ // terminates TLS in front of the merchant, its fingerprint headers ARE the
1376
+ // connection-layer truth — normalising them here is the permanent design.
1377
+ ["tlsFingerprint", ["cf-ja3-hash"]],
1378
+ ["ja4", ["cf-ja4"]]
1379
+ ];
1380
+ function deriveConnectionFromHeaders(headers) {
1381
+ if (!headers) return void 0;
1382
+ const lower = {};
1383
+ for (const name of Object.keys(headers)) {
1384
+ const value = headers[name];
1385
+ if (typeof value === "string" && value.length > 0) lower[name.toLowerCase()] = value;
1386
+ }
1387
+ const connection = {};
1388
+ for (const [key, sources] of CONNECTION_HEADER_SOURCES) {
1389
+ for (const source of sources) {
1390
+ const value = lower[source]?.trim();
1391
+ if (value) {
1392
+ connection[key] = value;
1393
+ break;
1394
+ }
1395
+ }
1396
+ }
1397
+ if (!connection.ip) {
1398
+ const xff = lower["x-forwarded-for"];
1399
+ const client = xff?.split(",")[0]?.trim();
1400
+ if (client) connection.ip = client;
1401
+ }
1402
+ return Object.keys(connection).length > 0 ? connection : void 0;
1403
+ }
1404
+ function buildSdkObservedMetadata(rawHeaders) {
1405
+ const { headers, apiKeyFormat, platformHeaders } = sanitizeHeaders(rawHeaders);
1406
+ const flat = {};
1407
+ if (rawHeaders) {
1408
+ for (const name of Object.keys(rawHeaders)) {
1409
+ const value = rawHeaders[name];
1410
+ if (value === void 0) continue;
1411
+ flat[name.toLowerCase()] = Array.isArray(value) ? value.join(", ") : String(value);
1412
+ }
1413
+ }
1414
+ const connection = deriveConnectionFromHeaders(flat);
1415
+ return {
1416
+ headers,
1417
+ ...apiKeyFormat && { apiKeyFormat },
1418
+ ...platformHeaders && { platformHeaders },
1419
+ ...connection && { connection },
1420
+ schemaVersion: CAPTURE_SCHEMA_VERSION
1421
+ };
1422
+ }
1423
+
1424
+ // src/edge-config.ts
1425
+ var DEFAULT_EDGE_CONFIG = Object.freeze({
1426
+ version: 1,
1427
+ mode: "observe",
1428
+ depth: "classify",
1429
+ pathRules: [],
1430
+ sampling: Object.freeze({ anonymousBeaconRate: 0.1 }),
1431
+ failurePosture: "open"
1432
+ });
1433
+ var EDGE_MODES = ["observe", "enforce"];
1434
+ var EDGE_DEPTHS = ["classify", "authenticate", "authorize"];
1435
+ function isEdgeConfig(value) {
1436
+ if (typeof value !== "object" || value === null) return false;
1437
+ const v = value;
1438
+ if (v.version !== 1) return false;
1439
+ if (!EDGE_MODES.includes(v.mode)) return false;
1440
+ if (!EDGE_DEPTHS.includes(v.depth)) return false;
1441
+ if (!Array.isArray(v.pathRules)) return false;
1442
+ for (const rule of v.pathRules) {
1443
+ if (typeof rule !== "object" || rule === null) return false;
1444
+ const r = rule;
1445
+ if (typeof r.pattern !== "string" || r.pattern.length === 0) return false;
1446
+ if (r.mode !== void 0 && !EDGE_MODES.includes(r.mode)) return false;
1447
+ if (r.depth !== void 0 && !EDGE_DEPTHS.includes(r.depth)) return false;
1448
+ }
1449
+ const sampling = v.sampling;
1450
+ if (typeof sampling !== "object" || sampling === null || typeof sampling.anonymousBeaconRate !== "number" || sampling.anonymousBeaconRate < 0 || sampling.anonymousBeaconRate > 1) {
1451
+ return false;
1452
+ }
1453
+ return v.failurePosture === "open";
1454
+ }
1455
+ async function fetchEdgeConfig(config, counterpartyId, opts) {
1456
+ if (!counterpartyId) return null;
1457
+ const headers = { Accept: "application/json" };
1458
+ if (config.apiKey) {
1459
+ headers["Authorization"] = `Bearer ${config.apiKey}`;
1460
+ headers["X-API-Key"] = config.apiKey;
1461
+ }
1462
+ if (opts?.etag) headers["If-None-Match"] = opts.etag;
1463
+ try {
1464
+ const response = await fetch(
1465
+ `${config.apiBaseUrl}/endpoints/${encodeURIComponent(counterpartyId)}/edge-config`,
1466
+ { method: "GET", headers, signal: AbortSignal.timeout(opts?.timeoutMs ?? 5e3) }
1467
+ );
1468
+ if (response.status === 304) return { notModified: true };
1469
+ if (!response.ok) return null;
1470
+ const body = await response.json();
1471
+ const edgeConfig = body.data?.edgeConfig;
1472
+ if (!isEdgeConfig(edgeConfig)) return null;
1473
+ return { edgeConfig, etag: response.headers.get("etag") ?? void 0 };
1474
+ } catch {
1475
+ return null;
1476
+ }
1477
+ }
1478
+ var DEFAULT_TTL_MS = 60 * 1e3;
1479
+ var DEFAULT_MAX_STALE_MS = 24 * 60 * 60 * 1e3;
1480
+ var cache2 = /* @__PURE__ */ new Map();
1481
+ var inflight2 = /* @__PURE__ */ new Map();
1482
+ function cacheKey(apiBaseUrl, counterpartyId) {
1483
+ return `${apiBaseUrl}|${counterpartyId}`;
1484
+ }
1485
+ function degradeToObserve(config) {
1486
+ if (config.mode !== "enforce" && !config.pathRules.some((r) => r.mode === "enforce")) {
1487
+ return config;
1488
+ }
1489
+ return {
1490
+ ...config,
1491
+ mode: "observe",
1492
+ pathRules: config.pathRules.map((r) => r.mode === "enforce" ? { ...r, mode: "observe" } : r)
1493
+ };
1494
+ }
1495
+ async function revalidate(config, counterpartyId) {
1496
+ const key = cacheKey(config.apiBaseUrl, counterpartyId);
1497
+ const existing = inflight2.get(key);
1498
+ if (existing) return existing;
1499
+ const promise = fetchEdgeConfig(config, counterpartyId, { etag: cache2.get(key)?.etag }).then((result) => {
1500
+ if (result === null) return;
1501
+ const now = Date.now();
1502
+ if ("notModified" in result) {
1503
+ const entry = cache2.get(key);
1504
+ if (entry) entry.lastSuccessAt = now;
1505
+ return;
1506
+ }
1507
+ cache2.set(key, { config: result.edgeConfig, etag: result.etag, lastSuccessAt: now });
1508
+ }).finally(() => {
1509
+ inflight2.delete(key);
1510
+ });
1511
+ inflight2.set(key, promise);
1512
+ return promise;
1513
+ }
1514
+ async function getEdgeConfig(config, counterpartyId, opts) {
1515
+ const ttlMs = opts?.ttlMs ?? DEFAULT_TTL_MS;
1516
+ const maxStaleMs = opts?.maxStaleMs ?? DEFAULT_MAX_STALE_MS;
1517
+ const key = cacheKey(config.apiBaseUrl, counterpartyId);
1518
+ const entry = cache2.get(key);
1519
+ if (entry) {
1520
+ const age = Date.now() - entry.lastSuccessAt;
1521
+ if (age > ttlMs) {
1522
+ revalidate(config, counterpartyId).catch(() => {
1523
+ });
1524
+ }
1525
+ return age > maxStaleMs ? degradeToObserve(entry.config) : entry.config;
1526
+ }
1527
+ await revalidate(config, counterpartyId).catch(() => {
1528
+ });
1529
+ return cache2.get(key)?.config ?? DEFAULT_EDGE_CONFIG;
1530
+ }
1531
+ function matchEdgePathRule(rules, path) {
1532
+ for (const rule of rules) {
1533
+ if (globToRegExp(rule.pattern).test(path)) return rule;
1534
+ }
1535
+ return void 0;
1536
+ }
1537
+ function globToRegExp(pattern) {
1538
+ const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
1539
+ return new RegExp(`^${escaped}$`, "i");
1540
+ }
1541
+ function _resetEdgeConfigCache() {
1542
+ cache2.clear();
1543
+ inflight2.clear();
1544
+ }
1545
+
1546
+ // src/platform-signatures.ts
1547
+ var PLATFORM_AGENT_SIGNATURES = [
1548
+ {
1549
+ vendor: "claude",
1550
+ displayName: "Anthropic Claude",
1551
+ uaPatterns: [/claude/i, /anthropic/i],
1552
+ agentCardPatterns: [/anthropic\.com/i, /claude\.ai/i],
1553
+ apiKeyFormatPatterns: [/^sk-ant/i],
1554
+ headerPatterns: [/^anthropic-version/i, /^anthropic-beta/i, /^x-anthropic-/i]
1555
+ },
1556
+ {
1557
+ vendor: "chatgpt",
1558
+ displayName: "OpenAI ChatGPT",
1559
+ uaPatterns: [/chatgpt/i, /openai/i, /gpt-?\d/i],
1560
+ agentCardPatterns: [/openai\.com/i, /chatgpt\.com/i],
1561
+ apiKeyFormatPatterns: [/^sk-proj/i, /^sk-svcacct/i],
1562
+ headerPatterns: [/^openai-organization/i, /^openai-project/i, /^openai-version/i]
1563
+ },
1564
+ {
1565
+ vendor: "gemini",
1566
+ displayName: "Google Gemini",
1567
+ uaPatterns: [/gemini/i, /google-gemini/i, /bard/i],
1568
+ agentCardPatterns: [/gemini\.google\.com/i, /ai\.google\.dev/i],
1569
+ apiKeyFormatPatterns: [/^aiza/i],
1570
+ headerPatterns: [/^x-goog-user-project/i, /^x-goog-api-client/i]
1571
+ },
1572
+ {
1573
+ vendor: "cursor",
1574
+ displayName: "Cursor",
1575
+ uaPatterns: [/cursor/i],
1576
+ agentCardPatterns: [/cursor\.sh/i, /cursor\.com/i]
1577
+ },
1578
+ {
1579
+ vendor: "goose",
1580
+ displayName: "Block Goose",
1581
+ uaPatterns: [/goose/i, /block-goose/i],
1582
+ agentCardPatterns: [/block\.xyz\/goose/i, /goose\.tools/i]
1583
+ },
1584
+ {
1585
+ vendor: "perplexity",
1586
+ displayName: "Perplexity",
1587
+ uaPatterns: [/perplexity/i],
1588
+ agentCardPatterns: [/perplexity\.ai/i],
1589
+ apiKeyFormatPatterns: [/^pplx-/i]
1590
+ }
1591
+ ];
1592
+ function matchPlatformSignature(signatures, input) {
1593
+ const ua = input.userAgent ?? "";
1594
+ const cardUrl = input.agentCardUrl ?? "";
1595
+ const apiKeyFormat = input.apiKeyFormat ?? "";
1596
+ const headerLines = [];
1597
+ if (input.platformHeaders) {
1598
+ for (const [name, value] of Object.entries(input.platformHeaders)) {
1599
+ headerLines.push(name, `${name}: ${value}`);
1600
+ }
1601
+ }
1602
+ for (const sig of signatures) {
1603
+ const uaMatch = sig.uaPatterns?.some((p) => p.test(ua));
1604
+ const cardMatch = sig.agentCardPatterns?.some((p) => p.test(cardUrl));
1605
+ const keyMatch = apiKeyFormat.length > 0 && sig.apiKeyFormatPatterns?.some((p) => p.test(apiKeyFormat));
1606
+ const headerMatch = headerLines.length > 0 && sig.headerPatterns?.some((p) => headerLines.some((line) => p.test(line)));
1607
+ if (uaMatch || cardMatch || keyMatch || headerMatch) {
1608
+ const uaBase = ua.split(/[\s/]/)[0]?.toLowerCase() ?? "";
1609
+ const cardHost = (() => {
1610
+ try {
1611
+ return cardUrl ? new URL(cardUrl).host : "";
1612
+ } catch {
1613
+ return "";
1614
+ }
1615
+ })();
1616
+ const keyBasis = uaBase || cardHost || apiKeyFormat || "unknown";
1617
+ return {
1618
+ vendor: sig.vendor,
1619
+ fingerprintKey: `${sig.vendor}:${keyBasis}`,
1620
+ displayName: sig.displayName
1621
+ };
1622
+ }
1623
+ }
1624
+ return void 0;
1625
+ }
1626
+ function detectPlatformFingerprint(input) {
1627
+ return matchPlatformSignature(PLATFORM_AGENT_SIGNATURES, input);
1628
+ }
1629
+
1630
+ // src/edge-core/classify.ts
1631
+ 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;
1632
+ 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;
1633
+ var BROWSER_UA_PATTERN = /mozilla\/\d/i;
1634
+ var _patterns = { STATIC_EXTENSIONS, BOT_UA_PATTERN, BROWSER_UA_PATTERN };
1635
+
1636
+ // src/edge-core/events.ts
1637
+ function sampled(rate, random = Math.random) {
1638
+ if (rate <= 0) return false;
1639
+ if (rate >= 1) return true;
1640
+ return random() < rate;
1641
+ }
1642
+
1643
+ // src/adapters/anonymous-beacon.ts
1644
+ function readHeader(raw, name) {
1645
+ const value = raw[name];
1646
+ if (typeof value === "string") return value;
1647
+ if (Array.isArray(value)) return value[0];
1648
+ return void 0;
1649
+ }
1650
+ function classifyPassThroughTraffic(path, rawHeaders) {
1651
+ if (_patterns.STATIC_EXTENSIONS.test(path)) return { kind: "never" };
1652
+ const credentials = extractCredentials(rawHeaders, {});
1653
+ if (credentials.astraId || credentials.apiKey || credentials.jwt) {
1654
+ return { kind: "never" };
1655
+ }
1656
+ const userAgent = readHeader(rawHeaders, "user-agent") ?? "";
1657
+ const { apiKeyFormat, platformHeaders } = sanitizeHeaders(rawHeaders);
1658
+ const fingerprint = detectPlatformFingerprint({ userAgent, apiKeyFormat, platformHeaders });
1659
+ if (fingerprint) {
1660
+ return {
1661
+ kind: "platform",
1662
+ protocol: "platform-ua",
1663
+ evidence: { vendor: fingerprint.vendor, userAgent: userAgent || "(none)" }
1664
+ };
1665
+ }
1666
+ if (!userAgent || _patterns.BOT_UA_PATTERN.test(userAgent) || !_patterns.BROWSER_UA_PATTERN.test(userAgent)) {
1667
+ return {
1668
+ kind: "anonymous-bot",
1669
+ protocol: "ua-heuristic",
1670
+ evidence: { userAgent: userAgent || "(none)" }
1671
+ };
1672
+ }
1673
+ if (!readHeader(rawHeaders, "sec-fetch-mode") && !readHeader(rawHeaders, "accept-language")) {
1674
+ return {
1675
+ kind: "anonymous-bot",
1676
+ protocol: "ua-heuristic",
1677
+ evidence: { userAgent, spoofedBrowser: true }
1678
+ };
1679
+ }
1680
+ return { kind: "never" };
1681
+ }
1682
+ function createAnonymousBeacon(config, options) {
1683
+ const counterpartyId = config.counterpartyId;
1684
+ if (!counterpartyId) return () => {
1685
+ };
1686
+ const random = options?.random ?? Math.random;
1687
+ let edgeConfig;
1688
+ let refreshing = false;
1689
+ const refreshConfig = () => {
1690
+ if (refreshing) return;
1691
+ refreshing = true;
1692
+ getEdgeConfig(config, counterpartyId).then((cfg) => {
1693
+ edgeConfig = cfg;
1694
+ }).catch(() => {
1695
+ }).finally(() => {
1696
+ refreshing = false;
1697
+ });
1698
+ };
1699
+ refreshConfig();
1700
+ return (input) => {
1701
+ try {
1702
+ refreshConfig();
1703
+ const cfg = edgeConfig;
1704
+ if (!cfg) return;
1705
+ const traffic = classifyPassThroughTraffic(input.requestPath, input.rawHeaders);
1706
+ if (traffic.kind === "never") return;
1707
+ if (traffic.kind === "anonymous-bot" && !sampled(cfg.sampling.anonymousBeaconRate, random)) {
1708
+ return;
1709
+ }
1710
+ const userAgent = readHeader(input.rawHeaders, "user-agent");
1711
+ const xff = input.rawHeaders["x-forwarded-for"];
1712
+ const xffStr = Array.isArray(xff) ? xff.join(", ") : xff;
1713
+ const sourceIp = input.sourceIp ?? xffStr?.split(",")[0]?.trim();
1714
+ reportUnregisteredAttempt(config, {
1715
+ ...input.counterpartyUrl && { counterpartyUrl: input.counterpartyUrl },
1716
+ counterpartyType: options?.counterpartyType ?? config.counterpartyType ?? "api",
1717
+ ...sourceIp && { sourceIp },
1718
+ ...userAgent && { userAgent },
1719
+ requestPath: input.requestPath,
1720
+ requestMethod: input.requestMethod,
1721
+ ...traffic.protocol && { protocol: traffic.protocol },
1722
+ ...traffic.evidence && { evidence: traffic.evidence },
1723
+ observedMetadata: buildSdkObservedMetadata(input.rawHeaders)
1724
+ }).catch(() => {
1725
+ });
1726
+ } catch {
1727
+ }
1728
+ };
1729
+ }
1359
1730
 
1360
1731
  // src/adapters/express.ts
1361
1732
  function defaultExtractCredentials(req) {
@@ -1367,7 +1738,7 @@ function defaultExtractCredentials(req) {
1367
1738
  function extractAstraSyncCredentials(req) {
1368
1739
  return extractHttpCredentials(req.headers);
1369
1740
  }
1370
- function headerValue(value) {
1741
+ function headerValue2(value) {
1371
1742
  if (typeof value === "string") return value;
1372
1743
  if (Array.isArray(value)) return value[0];
1373
1744
  return void 0;
@@ -1375,9 +1746,9 @@ function headerValue(value) {
1375
1746
  function resolveRequestPdlss(req, routeConfig, customExtractPurpose, customExtractAction) {
1376
1747
  return resolveHttpPdlss({
1377
1748
  method: req.method,
1378
- astraPurpose: headerValue(req.headers["x-astra-purpose"]),
1379
- astraAction: headerValue(req.headers["x-astra-action"]),
1380
- legacyPurpose: headerValue(req.headers["x-purpose"] ?? req.headers["X-Purpose"]),
1749
+ astraPurpose: headerValue2(req.headers["x-astra-purpose"]),
1750
+ astraAction: headerValue2(req.headers["x-astra-action"]),
1751
+ legacyPurpose: headerValue2(req.headers["x-purpose"] ?? req.headers["X-Purpose"]),
1381
1752
  queryPurpose: typeof req.query.purpose === "string" ? req.query.purpose : void 0,
1382
1753
  routePurpose: routeConfig?.purpose,
1383
1754
  routeAction: routeConfig?.action,
@@ -1460,6 +1831,11 @@ function defaultOnDenied(result, _req, res) {
1460
1831
  }
1461
1832
  var DEFAULT_ROUTES_REFRESH_MS = 5 * 60 * 1e3;
1462
1833
  function createMiddleware(options) {
1834
+ if ("routes" in options) {
1835
+ console.warn(
1836
+ "[VerificationGateway] `routes` is dashboard-managed since v2.9.7; this option is ignored \u2014 configure route policy in your AstraSync dashboard."
1837
+ );
1838
+ }
1463
1839
  const {
1464
1840
  extractCredentials: customExtractCredentials,
1465
1841
  extractPurpose: customExtractPurpose,
@@ -1471,12 +1847,25 @@ function createMiddleware(options) {
1471
1847
  routesRefreshMs = DEFAULT_ROUTES_REFRESH_MS,
1472
1848
  failOnError = "open",
1473
1849
  caseInsensitiveRouteMatch = false,
1850
+ anonymousBeacon = true,
1474
1851
  ...config
1475
1852
  } = options;
1476
1853
  if (config.apiBaseUrl) {
1477
1854
  prefetchWellKnown(config.apiBaseUrl).catch(() => {
1478
1855
  });
1479
1856
  }
1857
+ const emitAnonymousBeacon = anonymousBeacon ? createAnonymousBeacon(config, { counterpartyType: config.counterpartyType ?? "api" }) : void 0;
1858
+ const beaconPassThrough = emitAnonymousBeacon ? (req) => {
1859
+ const xff = req.headers["x-forwarded-for"];
1860
+ const xffStr = Array.isArray(xff) ? xff.join(", ") : xff;
1861
+ emitAnonymousBeacon({
1862
+ requestPath: req.path,
1863
+ requestMethod: req.method,
1864
+ counterpartyUrl: config.counterpartyUrl || `${req.protocol}://${req.get("host")}`,
1865
+ sourceIp: req.ip ?? (xffStr ? xffStr.split(",")[0].trim() : void 0),
1866
+ rawHeaders: req.headers
1867
+ });
1868
+ } : void 0;
1480
1869
  let cachedRoutes = [];
1481
1870
  let lastFetchAt = 0;
1482
1871
  let refreshing = null;
@@ -1537,6 +1926,7 @@ function createMiddleware(options) {
1537
1926
  cachedRoutes.length === 0 ? "no-policy" : "no-match"
1538
1927
  );
1539
1928
  }
1929
+ beaconPassThrough?.(req);
1540
1930
  return next();
1541
1931
  }
1542
1932
  const wellKnownUrls = config.apiBaseUrl ? await getWellKnownUrls(config.apiBaseUrl).catch(() => void 0) : void 0;
@@ -1547,6 +1937,7 @@ function createMiddleware(options) {
1547
1937
  res.setHeader("X-Astra-Gateway-Mode", "unenforced");
1548
1938
  res.setHeader("X-Astra-Gateway-Reason", "route-none");
1549
1939
  }
1940
+ beaconPassThrough?.(req);
1550
1941
  return next();
1551
1942
  }
1552
1943
  const pdlssPair = resolveRequestPdlss(
@@ -1639,16 +2030,10 @@ function createMiddleware(options) {
1639
2030
  forwardedFor: forwardedForStr,
1640
2031
  agentCardUrl,
1641
2032
  // Workstream D — capture EVERY inbound header (SDK-only merchants
1642
- // have no edge to do it upstream). Local string work, secrets
1643
- // stripped; out of the verify cache key so it doesn't defeat caching.
1644
- observedMetadata: (() => {
1645
- const { headers, apiKeyFormat, platformHeaders } = sanitizeHeaders(req.headers);
1646
- return {
1647
- headers,
1648
- ...apiKeyFormat && { apiKeyFormat },
1649
- ...platformHeaders && { platformHeaders }
1650
- };
1651
- })()
2033
+ // have no edge to do it upstream) plus the CDN-derived connection
2034
+ // block. Local string work, secrets stripped; out of the verify
2035
+ // cache key so it doesn't defeat caching.
2036
+ observedMetadata: buildSdkObservedMetadata(req.headers)
1652
2037
  }
1653
2038
  };
1654
2039
  let result = await verify(config, verificationRequest);
@@ -1842,7 +2227,7 @@ function resolveNextPdlss(request, routeConfig) {
1842
2227
  routeAction: routeConfig?.action
1843
2228
  });
1844
2229
  }
1845
- function generateCommerceShieldHtml(result, options) {
2230
+ function generateInterstitialHtml(result, options) {
1846
2231
  const title = escapeHtml(options.commerceShield?.title || "AstraSync Agent Verification");
1847
2232
  const message = escapeHtml(
1848
2233
  options.commerceShield?.message || result.guidance?.message || "This site verifies AI agents before granting access. We noticed you're visiting without AstraSync credentials."
@@ -1998,13 +2383,31 @@ function generateCommerceShieldHtml(result, options) {
1998
2383
  }
1999
2384
  var DEFAULT_ROUTES_REFRESH_MS2 = 5 * 60 * 1e3;
2000
2385
  function createMiddleware2(options) {
2386
+ if ("routes" in options) {
2387
+ console.warn(
2388
+ "[VerificationGateway/Next.js] `routes` is dashboard-managed since v2.9.7; this option is ignored \u2014 configure route policy in your AstraSync dashboard."
2389
+ );
2390
+ }
2001
2391
  const {
2002
2392
  skipPaths = [],
2003
- showCommerceShield = true,
2393
+ showInterstitial,
2394
+ showCommerceShield,
2004
2395
  enableRuntimeChallenge = true,
2005
2396
  routesRefreshMs = DEFAULT_ROUTES_REFRESH_MS2,
2397
+ anonymousBeacon = true,
2006
2398
  ...config
2007
2399
  } = options;
2400
+ const interstitialEnabled = showInterstitial ?? showCommerceShield ?? true;
2401
+ const emitAnonymousBeacon = anonymousBeacon ? createAnonymousBeacon(config, { counterpartyType: config.counterpartyType ?? "website" }) : void 0;
2402
+ const beaconPassThrough = emitAnonymousBeacon ? (request, pathname) => {
2403
+ emitAnonymousBeacon({
2404
+ requestPath: pathname,
2405
+ requestMethod: request.method,
2406
+ counterpartyUrl: config.counterpartyUrl || request.nextUrl.origin,
2407
+ sourceIp: request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() || void 0,
2408
+ rawHeaders: Object.fromEntries(request.headers.entries())
2409
+ });
2410
+ } : void 0;
2008
2411
  let cachedRoutes = [];
2009
2412
  let lastFetchAt = 0;
2010
2413
  let refreshing = null;
@@ -2046,9 +2449,11 @@ function createMiddleware2(options) {
2046
2449
  }
2047
2450
  const routeConfig = findRouteConfig2(cachedRoutes, pathname, request.method);
2048
2451
  if (!routeConfig) {
2452
+ beaconPassThrough?.(request, pathname);
2049
2453
  return NextResponse.next();
2050
2454
  }
2051
2455
  if (routeConfig.minAccessLevel === "none") {
2456
+ beaconPassThrough?.(request, pathname);
2052
2457
  return NextResponse.next();
2053
2458
  }
2054
2459
  const credentials = extractCredentialsFromNextRequest(request);
@@ -2100,8 +2505,8 @@ function createMiddleware2(options) {
2100
2505
  { status: 403 }
2101
2506
  );
2102
2507
  }
2103
- if (showCommerceShield) {
2104
- return new NextResponse(generateCommerceShieldHtml(preCheckResult, options), {
2508
+ if (interstitialEnabled) {
2509
+ return new NextResponse(generateInterstitialHtml(preCheckResult, options), {
2105
2510
  status: 200,
2106
2511
  headers: {
2107
2512
  "Content-Type": "text/html",
@@ -2129,17 +2534,9 @@ function createMiddleware2(options) {
2129
2534
  host: request.headers.get("host") || void 0,
2130
2535
  forwardedFor,
2131
2536
  agentCardUrl: request.headers.get("x-astrasync-agent-card") || void 0,
2132
- // Workstream D — full sanitised header capture (Web Headers → plain map).
2133
- observedMetadata: (() => {
2134
- const { headers, apiKeyFormat, platformHeaders } = sanitizeHeaders(
2135
- Object.fromEntries(request.headers.entries())
2136
- );
2137
- return {
2138
- headers,
2139
- ...apiKeyFormat && { apiKeyFormat },
2140
- ...platformHeaders && { platformHeaders }
2141
- };
2142
- })()
2537
+ // Workstream D — full sanitised header capture + CDN-derived
2538
+ // connection block (Web Headers → plain map).
2539
+ observedMetadata: buildSdkObservedMetadata(Object.fromEntries(request.headers.entries()))
2143
2540
  }
2144
2541
  };
2145
2542
  let result = await verify(config, verificationRequest);
@@ -2194,8 +2591,8 @@ function createMiddleware2(options) {
2194
2591
  }
2195
2592
  );
2196
2593
  }
2197
- if (showCommerceShield) {
2198
- return new NextResponse(generateCommerceShieldHtml(result, options), {
2594
+ if (interstitialEnabled) {
2595
+ return new NextResponse(generateInterstitialHtml(result, options), {
2199
2596
  status: 200,
2200
2597
  headers: {
2201
2598
  "Content-Type": "text/html",
@@ -2283,7 +2680,7 @@ var VerificationGatewayClient = class {
2283
2680
  /**
2284
2681
  * Quick verification — checks credentials and policy in one call.
2285
2682
  *
2286
- * Round-18 G4: return shape mirrors `VerificationResult`'s identity/policy
2683
+ * The return shape mirrors `VerificationResult`'s identity/policy
2287
2684
  * split. Map to HTTP status the same way: `!identityVerified` → 401,
2288
2685
  * `identityVerified && !policyAllowed` → 403.
2289
2686
  */
@@ -2713,8 +3110,8 @@ function extractX402TransactionValue(req) {
2713
3110
  // src/transport/rfc9421.ts
2714
3111
  var import_structured_headers = require("structured-headers");
2715
3112
  function parseRFC9421(headers) {
2716
- const sigInput = readHeader(headers, "signature-input");
2717
- const sig = readHeader(headers, "signature");
3113
+ const sigInput = readHeader2(headers, "signature-input");
3114
+ const sig = readHeader2(headers, "signature");
2718
3115
  if (!sigInput || !sig) return null;
2719
3116
  let inputDict;
2720
3117
  let sigDict;
@@ -2758,7 +3155,7 @@ function parseRFC9421(headers) {
2758
3155
  if (signatures.length === 0) return null;
2759
3156
  return { signatures };
2760
3157
  }
2761
- function readHeader(headers, name) {
3158
+ function readHeader2(headers, name) {
2762
3159
  for (const key of Object.keys(headers)) {
2763
3160
  if (key.toLowerCase() === name) {
2764
3161
  const raw = headers[key];
@@ -3076,11 +3473,11 @@ function extractACPContext(request) {
3076
3473
  const purpose = mapACPRequestToPurpose(method, path);
3077
3474
  const sessionId = extractSessionId2(path);
3078
3475
  const headers = request.headers ?? {};
3079
- const signatureHeader = readHeader2(headers, "signature");
3080
- const timestampHeader = readHeader2(headers, "timestamp");
3081
- const idempotencyKey = readHeader2(headers, "idempotency-key");
3082
- const apiVersion = readHeader2(headers, "api-version");
3083
- const bearer = extractBearer(readHeader2(headers, "authorization"));
3476
+ const signatureHeader = readHeader3(headers, "signature");
3477
+ const timestampHeader = readHeader3(headers, "timestamp");
3478
+ const idempotencyKey = readHeader3(headers, "idempotency-key");
3479
+ const apiVersion = readHeader3(headers, "api-version");
3480
+ const bearer = extractBearer(readHeader3(headers, "authorization"));
3084
3481
  const body = request.body ?? {};
3085
3482
  const merchantId = coerceString3(body.merchant_id ?? body.merchantId);
3086
3483
  const totals = Array.isArray(body.totals) ? body.totals : void 0;
@@ -3148,7 +3545,7 @@ function extractFulfillmentOption(body) {
3148
3545
  }
3149
3546
  return void 0;
3150
3547
  }
3151
- function readHeader2(headers, name) {
3548
+ function readHeader3(headers, name) {
3152
3549
  for (const key of Object.keys(headers)) {
3153
3550
  if (key.toLowerCase() === name) {
3154
3551
  const raw = headers[key];
@@ -3942,9 +4339,9 @@ function toBuf(bytes) {
3942
4339
  new Uint8Array(out).set(bytes);
3943
4340
  return out;
3944
4341
  }
3945
- function checkTimestamp(headerValue2, toleranceSec, nowFn) {
3946
- if (!headerValue2) return { ok: false, error: "missing Timestamp header" };
3947
- const ts = parseTimestamp(headerValue2);
4342
+ function checkTimestamp(headerValue3, toleranceSec, nowFn) {
4343
+ if (!headerValue3) return { ok: false, error: "missing Timestamp header" };
4344
+ const ts = parseTimestamp(headerValue3);
3948
4345
  if (ts === null) return { ok: false, error: "unparseable Timestamp header" };
3949
4346
  const now = nowFn ? nowFn() : Math.floor(Date.now() / 1e3);
3950
4347
  if (Math.abs(now - ts) > toleranceSec) {
@@ -3988,7 +4385,7 @@ async function getSubtle() {
3988
4385
  // src/transport/mpp.ts
3989
4386
  var import_mppx = require("mppx");
3990
4387
  function extractMPPFromRequest(request) {
3991
- const auth = readHeader3(request.headers, "authorization");
4388
+ const auth = readHeader4(request.headers, "authorization");
3992
4389
  if (!auth || !/^\s*Payment\s+/i.test(auth)) return null;
3993
4390
  try {
3994
4391
  const credential = import_mppx.Credential.deserialize(auth);
@@ -4016,7 +4413,7 @@ function extractMPPFromResponse(response) {
4016
4413
  offeredMethods: methods
4017
4414
  };
4018
4415
  }
4019
- const receiptHeader = readHeader3(response.headers, "payment-receipt");
4416
+ const receiptHeader = readHeader4(response.headers, "payment-receipt");
4020
4417
  if (receiptHeader) {
4021
4418
  try {
4022
4419
  const parsed = import_mppx.Receipt.deserialize(receiptHeader);
@@ -4036,7 +4433,7 @@ function extractMPPFromResponse(response) {
4036
4433
  return { kind: "error", error: { type: "invalid-receipt-encoding" } };
4037
4434
  }
4038
4435
  }
4039
- const contentType = readHeader3(response.headers, "content-type");
4436
+ const contentType = readHeader4(response.headers, "content-type");
4040
4437
  if (contentType && /application\/problem\+json/i.test(contentType)) {
4041
4438
  const body = typeof response.body === "object" && response.body !== null ? response.body : {};
4042
4439
  return {
@@ -4059,7 +4456,7 @@ function extractMPPContext(message) {
4059
4456
  return extractMPPFromRequest(message);
4060
4457
  }
4061
4458
  function collectChallenges(response) {
4062
- const wwwAuth = readHeader3(response.headers, "www-authenticate");
4459
+ const wwwAuth = readHeader4(response.headers, "www-authenticate");
4063
4460
  if (!wwwAuth) return [];
4064
4461
  const headers = new Headers();
4065
4462
  headers.set("www-authenticate", wwwAuth);
@@ -4087,7 +4484,7 @@ function summarizeChallenge(ch) {
4087
4484
  opaque: raw.opaque
4088
4485
  };
4089
4486
  }
4090
- function readHeader3(headers, name) {
4487
+ function readHeader4(headers, name) {
4091
4488
  for (const key of Object.keys(headers)) {
4092
4489
  if (key.toLowerCase() === name) {
4093
4490
  const raw = headers[key];
@@ -4176,14 +4573,14 @@ function verifyMPP(input) {
4176
4573
  var import_schemas = require("@x402/core/schemas");
4177
4574
  var import_utils = require("@x402/core/utils");
4178
4575
  function extractX402FromRequest(request) {
4179
- const headerValue2 = readHeader4(request.headers, "x-payment");
4576
+ const headerValue3 = readHeader5(request.headers, "x-payment");
4180
4577
  if (request.body && typeof request.body === "object") {
4181
4578
  const parsed = tryParsePayload(request.body);
4182
4579
  if (parsed) return buildPayloadContext(parsed, "body");
4183
4580
  }
4184
- if (headerValue2) {
4581
+ if (headerValue3) {
4185
4582
  try {
4186
- const decoded = (0, import_utils.safeBase64Decode)(headerValue2);
4583
+ const decoded = (0, import_utils.safeBase64Decode)(headerValue3);
4187
4584
  if (decoded) {
4188
4585
  const json = JSON.parse(decoded);
4189
4586
  const parsed = tryParsePayload(json);
@@ -4206,10 +4603,10 @@ function extractX402FromResponse(response) {
4206
4603
  const parsed = tryParseRequired(response.body);
4207
4604
  if (parsed) return buildRequiredContext(parsed, "body");
4208
4605
  }
4209
- const headerValue2 = readHeader4(response.headers, "x-payment-required");
4210
- if (headerValue2) {
4606
+ const headerValue3 = readHeader5(response.headers, "x-payment-required");
4607
+ if (headerValue3) {
4211
4608
  try {
4212
- const decoded = (0, import_utils.safeBase64Decode)(headerValue2);
4609
+ const decoded = (0, import_utils.safeBase64Decode)(headerValue3);
4213
4610
  if (decoded) {
4214
4611
  const json = JSON.parse(decoded);
4215
4612
  const parsed = tryParseRequired(json);
@@ -4306,7 +4703,7 @@ function coerceVersion(v) {
4306
4703
  if (v === 1 || v === 2) return v;
4307
4704
  return null;
4308
4705
  }
4309
- function readHeader4(headers, name) {
4706
+ function readHeader5(headers, name) {
4310
4707
  if (!headers) return void 0;
4311
4708
  for (const key of Object.keys(headers)) {
4312
4709
  if (key.toLowerCase() === name) {
@@ -5054,8 +5451,11 @@ function mcpToPdlss(parsed, requestPath, headerPurpose, headerAction, toolGate)
5054
5451
  } else if (parsed.actionFromBody && parsed.actionSourceFromBody) {
5055
5452
  action = parsed.actionFromBody;
5056
5453
  actionSource = parsed.actionSourceFromBody;
5454
+ } else if (parsed.method === "tools/call") {
5455
+ action = void 0;
5456
+ actionSource = void 0;
5057
5457
  } else {
5058
- action = parsed.toolName ? parsed.method === "tools/call" ? parsed.toolName : `${parsed.method}:${parsed.toolName}` : parsed.method;
5458
+ action = parsed.toolName ? `${parsed.method}:${parsed.toolName}` : parsed.method;
5059
5459
  actionSource = "transport_layer";
5060
5460
  }
5061
5461
  return { purpose, action, resource, purposeSource, actionSource };
@@ -5170,6 +5570,15 @@ function createMcpMiddleware(options) {
5170
5570
  prefetchWellKnown(config.apiBaseUrl).catch(() => {
5171
5571
  });
5172
5572
  }
5573
+ if (toolGates) {
5574
+ for (const [toolName, gate] of Object.entries(toolGates)) {
5575
+ if (typeof gate === "string") {
5576
+ console.warn(
5577
+ `[VerificationGateway/MCP] toolGates['${toolName}'] uses the bare-string shorthand ('${gate}'). Gated tools need a PDLSS purpose (else PDLSS_PURPOSE_REQUIRED 400) and evaluate purpose-only without a declared action. Use the object form instead: { minAccessLevel: '${gate}', purpose: '...', action: '...' }.`
5578
+ );
5579
+ }
5580
+ }
5581
+ }
5173
5582
  return async (req, res, next) => {
5174
5583
  try {
5175
5584
  if (skip) return next();
@@ -5289,7 +5698,13 @@ function createMcpMiddleware(options) {
5289
5698
  callerMetadata: {
5290
5699
  sourceIp: req.ip,
5291
5700
  userAgent: req.headers["user-agent"],
5292
- host: req.headers.host
5701
+ host: req.headers.host,
5702
+ // Capture EVERY inbound header (parity with the express adapter —
5703
+ // MCP-only merchants have no edge to do it upstream) plus the
5704
+ // CDN-derived connection block. Local string work, secrets
5705
+ // stripped; out of the verify cache key so it doesn't defeat
5706
+ // caching.
5707
+ observedMetadata: buildSdkObservedMetadata(req.headers)
5293
5708
  }
5294
5709
  };
5295
5710
  let result = await verify(config, verificationRequest);
@@ -6074,212 +6489,6 @@ async function recordDecision2(config, params) {
6074
6489
  blockchainTxHash: result.blockchainTxHash
6075
6490
  };
6076
6491
  }
6077
-
6078
- // src/edge-config.ts
6079
- var DEFAULT_EDGE_CONFIG = Object.freeze({
6080
- version: 1,
6081
- mode: "observe",
6082
- depth: "classify",
6083
- pathRules: [],
6084
- sampling: Object.freeze({ anonymousBeaconRate: 0.1 }),
6085
- failurePosture: "open"
6086
- });
6087
- var EDGE_MODES = ["observe", "enforce"];
6088
- var EDGE_DEPTHS = ["classify", "authenticate", "authorize"];
6089
- function isEdgeConfig(value) {
6090
- if (typeof value !== "object" || value === null) return false;
6091
- const v = value;
6092
- if (v.version !== 1) return false;
6093
- if (!EDGE_MODES.includes(v.mode)) return false;
6094
- if (!EDGE_DEPTHS.includes(v.depth)) return false;
6095
- if (!Array.isArray(v.pathRules)) return false;
6096
- for (const rule of v.pathRules) {
6097
- if (typeof rule !== "object" || rule === null) return false;
6098
- const r = rule;
6099
- if (typeof r.pattern !== "string" || r.pattern.length === 0) return false;
6100
- if (r.mode !== void 0 && !EDGE_MODES.includes(r.mode)) return false;
6101
- if (r.depth !== void 0 && !EDGE_DEPTHS.includes(r.depth)) return false;
6102
- }
6103
- const sampling = v.sampling;
6104
- if (typeof sampling !== "object" || sampling === null || typeof sampling.anonymousBeaconRate !== "number" || sampling.anonymousBeaconRate < 0 || sampling.anonymousBeaconRate > 1) {
6105
- return false;
6106
- }
6107
- return v.failurePosture === "open";
6108
- }
6109
- async function fetchEdgeConfig(config, counterpartyId, opts) {
6110
- if (!counterpartyId) return null;
6111
- const headers = { Accept: "application/json" };
6112
- if (config.apiKey) {
6113
- headers["Authorization"] = `Bearer ${config.apiKey}`;
6114
- headers["X-API-Key"] = config.apiKey;
6115
- }
6116
- if (opts?.etag) headers["If-None-Match"] = opts.etag;
6117
- try {
6118
- const response = await fetch(
6119
- `${config.apiBaseUrl}/endpoints/${encodeURIComponent(counterpartyId)}/edge-config`,
6120
- { method: "GET", headers, signal: AbortSignal.timeout(opts?.timeoutMs ?? 5e3) }
6121
- );
6122
- if (response.status === 304) return { notModified: true };
6123
- if (!response.ok) return null;
6124
- const body = await response.json();
6125
- const edgeConfig = body.data?.edgeConfig;
6126
- if (!isEdgeConfig(edgeConfig)) return null;
6127
- return { edgeConfig, etag: response.headers.get("etag") ?? void 0 };
6128
- } catch {
6129
- return null;
6130
- }
6131
- }
6132
- var DEFAULT_TTL_MS = 60 * 1e3;
6133
- var DEFAULT_MAX_STALE_MS = 24 * 60 * 60 * 1e3;
6134
- var cache2 = /* @__PURE__ */ new Map();
6135
- var inflight2 = /* @__PURE__ */ new Map();
6136
- function cacheKey(apiBaseUrl, counterpartyId) {
6137
- return `${apiBaseUrl}|${counterpartyId}`;
6138
- }
6139
- function degradeToObserve(config) {
6140
- if (config.mode !== "enforce" && !config.pathRules.some((r) => r.mode === "enforce")) {
6141
- return config;
6142
- }
6143
- return {
6144
- ...config,
6145
- mode: "observe",
6146
- pathRules: config.pathRules.map((r) => r.mode === "enforce" ? { ...r, mode: "observe" } : r)
6147
- };
6148
- }
6149
- async function revalidate(config, counterpartyId) {
6150
- const key = cacheKey(config.apiBaseUrl, counterpartyId);
6151
- const existing = inflight2.get(key);
6152
- if (existing) return existing;
6153
- const promise = fetchEdgeConfig(config, counterpartyId, { etag: cache2.get(key)?.etag }).then((result) => {
6154
- if (result === null) return;
6155
- const now = Date.now();
6156
- if ("notModified" in result) {
6157
- const entry = cache2.get(key);
6158
- if (entry) entry.lastSuccessAt = now;
6159
- return;
6160
- }
6161
- cache2.set(key, { config: result.edgeConfig, etag: result.etag, lastSuccessAt: now });
6162
- }).finally(() => {
6163
- inflight2.delete(key);
6164
- });
6165
- inflight2.set(key, promise);
6166
- return promise;
6167
- }
6168
- async function getEdgeConfig(config, counterpartyId, opts) {
6169
- const ttlMs = opts?.ttlMs ?? DEFAULT_TTL_MS;
6170
- const maxStaleMs = opts?.maxStaleMs ?? DEFAULT_MAX_STALE_MS;
6171
- const key = cacheKey(config.apiBaseUrl, counterpartyId);
6172
- const entry = cache2.get(key);
6173
- if (entry) {
6174
- const age = Date.now() - entry.lastSuccessAt;
6175
- if (age > ttlMs) {
6176
- revalidate(config, counterpartyId).catch(() => {
6177
- });
6178
- }
6179
- return age > maxStaleMs ? degradeToObserve(entry.config) : entry.config;
6180
- }
6181
- await revalidate(config, counterpartyId).catch(() => {
6182
- });
6183
- return cache2.get(key)?.config ?? DEFAULT_EDGE_CONFIG;
6184
- }
6185
- function matchEdgePathRule(rules, path) {
6186
- for (const rule of rules) {
6187
- if (globToRegExp(rule.pattern).test(path)) return rule;
6188
- }
6189
- return void 0;
6190
- }
6191
- function globToRegExp(pattern) {
6192
- const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
6193
- return new RegExp(`^${escaped}$`, "i");
6194
- }
6195
- function _resetEdgeConfigCache() {
6196
- cache2.clear();
6197
- inflight2.clear();
6198
- }
6199
-
6200
- // src/platform-signatures.ts
6201
- var PLATFORM_AGENT_SIGNATURES = [
6202
- {
6203
- vendor: "claude",
6204
- displayName: "Anthropic Claude",
6205
- uaPatterns: [/claude/i, /anthropic/i],
6206
- agentCardPatterns: [/anthropic\.com/i, /claude\.ai/i],
6207
- apiKeyFormatPatterns: [/^sk-ant/i],
6208
- headerPatterns: [/^anthropic-version/i, /^anthropic-beta/i, /^x-anthropic-/i]
6209
- },
6210
- {
6211
- vendor: "chatgpt",
6212
- displayName: "OpenAI ChatGPT",
6213
- uaPatterns: [/chatgpt/i, /openai/i, /gpt-?\d/i],
6214
- agentCardPatterns: [/openai\.com/i, /chatgpt\.com/i],
6215
- apiKeyFormatPatterns: [/^sk-proj/i, /^sk-svcacct/i],
6216
- headerPatterns: [/^openai-organization/i, /^openai-project/i, /^openai-version/i]
6217
- },
6218
- {
6219
- vendor: "gemini",
6220
- displayName: "Google Gemini",
6221
- uaPatterns: [/gemini/i, /google-gemini/i, /bard/i],
6222
- agentCardPatterns: [/gemini\.google\.com/i, /ai\.google\.dev/i],
6223
- apiKeyFormatPatterns: [/^aiza/i],
6224
- headerPatterns: [/^x-goog-user-project/i, /^x-goog-api-client/i]
6225
- },
6226
- {
6227
- vendor: "cursor",
6228
- displayName: "Cursor",
6229
- uaPatterns: [/cursor/i],
6230
- agentCardPatterns: [/cursor\.sh/i, /cursor\.com/i]
6231
- },
6232
- {
6233
- vendor: "goose",
6234
- displayName: "Block Goose",
6235
- uaPatterns: [/goose/i, /block-goose/i],
6236
- agentCardPatterns: [/block\.xyz\/goose/i, /goose\.tools/i]
6237
- },
6238
- {
6239
- vendor: "perplexity",
6240
- displayName: "Perplexity",
6241
- uaPatterns: [/perplexity/i],
6242
- agentCardPatterns: [/perplexity\.ai/i],
6243
- apiKeyFormatPatterns: [/^pplx-/i]
6244
- }
6245
- ];
6246
- function matchPlatformSignature(signatures, input) {
6247
- const ua = input.userAgent ?? "";
6248
- const cardUrl = input.agentCardUrl ?? "";
6249
- const apiKeyFormat = input.apiKeyFormat ?? "";
6250
- const headerLines = [];
6251
- if (input.platformHeaders) {
6252
- for (const [name, value] of Object.entries(input.platformHeaders)) {
6253
- headerLines.push(name, `${name}: ${value}`);
6254
- }
6255
- }
6256
- for (const sig of signatures) {
6257
- const uaMatch = sig.uaPatterns?.some((p) => p.test(ua));
6258
- const cardMatch = sig.agentCardPatterns?.some((p) => p.test(cardUrl));
6259
- const keyMatch = apiKeyFormat.length > 0 && sig.apiKeyFormatPatterns?.some((p) => p.test(apiKeyFormat));
6260
- const headerMatch = headerLines.length > 0 && sig.headerPatterns?.some((p) => headerLines.some((line) => p.test(line)));
6261
- if (uaMatch || cardMatch || keyMatch || headerMatch) {
6262
- const uaBase = ua.split(/[\s/]/)[0]?.toLowerCase() ?? "";
6263
- const cardHost = (() => {
6264
- try {
6265
- return cardUrl ? new URL(cardUrl).host : "";
6266
- } catch {
6267
- return "";
6268
- }
6269
- })();
6270
- const keyBasis = uaBase || cardHost || apiKeyFormat || "unknown";
6271
- return {
6272
- vendor: sig.vendor,
6273
- fingerprintKey: `${sig.vendor}:${keyBasis}`,
6274
- displayName: sig.displayName
6275
- };
6276
- }
6277
- }
6278
- return void 0;
6279
- }
6280
- function detectPlatformFingerprint(input) {
6281
- return matchPlatformSignature(PLATFORM_AGENT_SIGNATURES, input);
6282
- }
6283
6492
  // Annotate the CommonJS export names for ESM import in node:
6284
6493
  0 && (module.exports = {
6285
6494
  ACCESS_LEVEL_DESCRIPTIONS,
@@ -6288,6 +6497,7 @@ function detectPlatformFingerprint(input) {
6288
6497
  AstraSync,
6289
6498
  AstraSyncError,
6290
6499
  AuthenticationError,
6500
+ CAPTURE_SCHEMA_VERSION,
6291
6501
  ChallengeHandler,
6292
6502
  DEFAULT_EDGE_CONFIG,
6293
6503
  DEFAULT_TRUST_THRESHOLDS,
@@ -6306,9 +6516,11 @@ function detectPlatformFingerprint(input) {
6306
6516
  authorizeSettlement,
6307
6517
  awaitStepUpApproval,
6308
6518
  buildGuidance,
6519
+ buildSdkObservedMetadata,
6309
6520
  clearCache,
6310
6521
  createMcpMiddleware,
6311
6522
  degradeToObserve,
6523
+ deriveConnectionFromHeaders,
6312
6524
  detectPlatformFingerprint,
6313
6525
  determineAccessLevel,
6314
6526
  express,