@astrasyncai/verification-gateway 4.3.0 → 4.4.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 (83) hide show
  1. package/dist/adapters/express.js +91 -19
  2. package/dist/adapters/express.js.map +1 -1
  3. package/dist/adapters/express.mjs +91 -19
  4. package/dist/adapters/express.mjs.map +1 -1
  5. package/dist/adapters/mcp.js +52 -13
  6. package/dist/adapters/mcp.js.map +1 -1
  7. package/dist/adapters/mcp.mjs +52 -13
  8. package/dist/adapters/mcp.mjs.map +1 -1
  9. package/dist/adapters/nextjs.js +89 -17
  10. package/dist/adapters/nextjs.js.map +1 -1
  11. package/dist/adapters/nextjs.mjs +89 -17
  12. package/dist/adapters/nextjs.mjs.map +1 -1
  13. package/dist/adapters/sdk.js +24 -6
  14. package/dist/adapters/sdk.js.map +1 -1
  15. package/dist/adapters/sdk.mjs +24 -6
  16. package/dist/adapters/sdk.mjs.map +1 -1
  17. package/dist/agent/index.js +24 -3
  18. package/dist/agent/index.js.map +1 -1
  19. package/dist/agent/index.mjs +24 -3
  20. package/dist/agent/index.mjs.map +1 -1
  21. package/dist/bin/astrasync-claude-hook.js +22 -4
  22. package/dist/bin/astrasync-codex-hook.js +22 -4
  23. package/dist/bin/astrasync-guard.js +26 -8
  24. package/dist/bin/astrasync.js +66 -23
  25. package/dist/browser/background.js +22 -4
  26. package/dist/browser/background.js.map +1 -1
  27. package/dist/browser/background.mjs +22 -4
  28. package/dist/browser/background.mjs.map +1 -1
  29. package/dist/cli/index.js +23 -2
  30. package/dist/cli/index.js.map +1 -1
  31. package/dist/cli/index.mjs +23 -2
  32. package/dist/cli/index.mjs.map +1 -1
  33. package/dist/codex/index.js +22 -4
  34. package/dist/codex/index.js.map +1 -1
  35. package/dist/codex/index.mjs +22 -4
  36. package/dist/codex/index.mjs.map +1 -1
  37. package/dist/cursor/extension.js +22 -4
  38. package/dist/cursor/extension.js.map +1 -1
  39. package/dist/cursor/extension.mjs +22 -4
  40. package/dist/cursor/extension.mjs.map +1 -1
  41. package/dist/edge-config.js +24 -1
  42. package/dist/edge-config.js.map +1 -1
  43. package/dist/edge-config.mjs +22 -1
  44. package/dist/edge-config.mjs.map +1 -1
  45. package/dist/edge-core/index.js +96 -11
  46. package/dist/edge-core/index.js.map +1 -1
  47. package/dist/edge-core/index.mjs +96 -11
  48. package/dist/edge-core/index.mjs.map +1 -1
  49. package/dist/gateway/gateway.js +22 -4
  50. package/dist/gateway/gateway.js.map +1 -1
  51. package/dist/gateway/gateway.mjs +22 -4
  52. package/dist/gateway/gateway.mjs.map +1 -1
  53. package/dist/index.d.mts +51 -2
  54. package/dist/index.d.ts +51 -2
  55. package/dist/index.js +151 -26
  56. package/dist/index.js.map +1 -1
  57. package/dist/index.mjs +147 -26
  58. package/dist/index.mjs.map +1 -1
  59. package/dist/metadata-capture.d.mts +17 -0
  60. package/dist/metadata-capture.d.ts +17 -0
  61. package/dist/metadata-capture.js +25 -4
  62. package/dist/metadata-capture.js.map +1 -1
  63. package/dist/metadata-capture.mjs +25 -4
  64. package/dist/metadata-capture.mjs.map +1 -1
  65. package/dist/platform-signatures.d.mts +18 -1
  66. package/dist/platform-signatures.d.ts +18 -1
  67. package/dist/platform-signatures.js +35 -2
  68. package/dist/platform-signatures.js.map +1 -1
  69. package/dist/platform-signatures.mjs +35 -2
  70. package/dist/platform-signatures.mjs.map +1 -1
  71. package/dist/registration/index.js +25 -4
  72. package/dist/registration/index.js.map +1 -1
  73. package/dist/registration/index.mjs +25 -4
  74. package/dist/registration/index.mjs.map +1 -1
  75. package/dist/transport/index.js +22 -1
  76. package/dist/transport/index.js.map +1 -1
  77. package/dist/transport/index.mjs +22 -1
  78. package/dist/transport/index.mjs.map +1 -1
  79. package/dist/verify.js +29 -11
  80. package/dist/verify.js.map +1 -1
  81. package/dist/verify.mjs +29 -11
  82. package/dist/verify.mjs.map +1 -1
  83. package/package.json +1 -1
@@ -1,3 +1,24 @@
1
+ // src/version.ts
2
+ var SDK_VERSION = "4.4.0";
3
+
4
+ // src/http.ts
5
+ var SDK_USER_AGENT = `astrasync-sdk/${SDK_VERSION}`;
6
+ function withUserAgent(initHeaders) {
7
+ if (!initHeaders) return { "user-agent": SDK_USER_AGENT };
8
+ if (initHeaders instanceof Headers) {
9
+ const copy = new Headers(initHeaders);
10
+ if (!copy.has("user-agent")) copy.set("user-agent", SDK_USER_AGENT);
11
+ return copy;
12
+ }
13
+ if (Array.isArray(initHeaders)) {
14
+ const has2 = initHeaders.some(([name]) => name.toLowerCase() === "user-agent");
15
+ return has2 ? initHeaders : [...initHeaders, ["user-agent", SDK_USER_AGENT]];
16
+ }
17
+ const has = Object.keys(initHeaders).some((name) => name.toLowerCase() === "user-agent");
18
+ return has ? initHeaders : { "user-agent": SDK_USER_AGENT, ...initHeaders };
19
+ }
20
+ var sdkFetch = (input, init) => fetch(input, { ...init, headers: withUserAgent(init?.headers) });
21
+
1
22
  // src/adapters/stepup-poll.ts
2
23
  var DEFAULT_POLL_INTERVAL_MS = 3e3;
3
24
  var MIN_POLL_INTERVAL_MS = 250;
@@ -12,7 +33,7 @@ function resolvePollUrl(config, pollUrl) {
12
33
  async function pollStepUpStatus(config, approval) {
13
34
  if (!approval.pollUrl) return null;
14
35
  try {
15
- const res = await fetch(resolvePollUrl(config, approval.pollUrl), {
36
+ const res = await sdkFetch(resolvePollUrl(config, approval.pollUrl), {
16
37
  method: "GET",
17
38
  signal: AbortSignal.timeout(POLL_REQUEST_TIMEOUT_MS)
18
39
  });
@@ -64,9 +85,6 @@ function getTrustLevel(score) {
64
85
  return "BRONZE";
65
86
  }
66
87
 
67
- // src/version.ts
68
- var SDK_VERSION = "4.1.0";
69
-
70
88
  // src/well-known.ts
71
89
  var CACHE_TTL_MS = 60 * 60 * 1e3;
72
90
  var cache = /* @__PURE__ */ new Map();
@@ -77,7 +95,7 @@ function wellKnownUrl(apiBaseUrl) {
77
95
  }
78
96
  async function fetchWellKnown(apiBaseUrl) {
79
97
  const url = wellKnownUrl(apiBaseUrl);
80
- const response = await fetch(url, {
98
+ const response = await sdkFetch(url, {
81
99
  method: "GET",
82
100
  headers: { Accept: "application/json" },
83
101
  signal: AbortSignal.timeout(5e3)
@@ -147,7 +165,7 @@ async function performInitCheck(apiBaseUrl, debug, strictInit, apiKey) {
147
165
  initCheckPerformed = true;
148
166
  try {
149
167
  const probeUrl = `${apiBaseUrl}/agents/verify-access`;
150
- const response = await fetch(probeUrl, { method: "HEAD" });
168
+ const response = await sdkFetch(probeUrl, { method: "HEAD" });
151
169
  const contentType = response.headers.get("content-type") ?? "";
152
170
  if (contentType.startsWith("text/html")) {
153
171
  const message = `[VerificationGateway] apiBaseUrl '${apiBaseUrl}' returned HTML (content-type: ${contentType}). This usually means apiBaseUrl is pointing at a marketing site instead of the API. Expected: 'https://astrasync.ai/api' (prod) or 'https://staging.astrasync.ai/api' (staging).`;
@@ -161,7 +179,7 @@ async function performInitCheck(apiBaseUrl, debug, strictInit, apiKey) {
161
179
  );
162
180
  }
163
181
  if (apiKey) {
164
- const selfCheck = await fetch(`${apiBaseUrl}/agents/verify-access/self-check`, {
182
+ const selfCheck = await sdkFetch(`${apiBaseUrl}/agents/verify-access/self-check`, {
165
183
  headers: { Authorization: `Bearer ${apiKey}` }
166
184
  });
167
185
  if (selfCheck.status === 401) {
@@ -408,7 +426,7 @@ async function callVerifyAccessAPI(config, request) {
408
426
  headers["X-API-Key"] = config.apiKey;
409
427
  }
410
428
  try {
411
- const response = await fetch(`${config.apiBaseUrl}/agents/verify-access`, {
429
+ const response = await sdkFetch(`${config.apiBaseUrl}/agents/verify-access`, {
412
430
  method: "POST",
413
431
  headers,
414
432
  body: JSON.stringify(body),
@@ -651,7 +669,7 @@ async function recordDecision(config, sessionId, decision, reason) {
651
669
  headers["Authorization"] = `Bearer ${config.apiKey}`;
652
670
  headers["X-API-Key"] = config.apiKey;
653
671
  }
654
- await fetch(`${config.apiBaseUrl}/agents/verify-access/${sessionId}/decision`, {
672
+ await sdkFetch(`${config.apiBaseUrl}/agents/verify-access/${sessionId}/decision`, {
655
673
  method: "POST",
656
674
  headers,
657
675
  body: JSON.stringify({ decision, reason })
@@ -666,7 +684,7 @@ async function fetchRoutes(config, counterpartyId) {
666
684
  headers["X-API-Key"] = config.apiKey;
667
685
  }
668
686
  try {
669
- const response = await fetch(
687
+ const response = await sdkFetch(
670
688
  `${config.apiBaseUrl}/endpoints/${encodeURIComponent(counterpartyId)}/routes`,
671
689
  { method: "GET", headers }
672
690
  );
@@ -685,7 +703,7 @@ async function reportUnregisteredAttempt(config, data) {
685
703
  ...counterpartyUrl && { counterpartyUrl },
686
704
  ...config.counterpartyId && { counterpartyId: config.counterpartyId }
687
705
  };
688
- await fetch(`${apiBaseUrl}/verification-activity/unregistered-attempt`, {
706
+ await sdkFetch(`${apiBaseUrl}/verification-activity/unregistered-attempt`, {
689
707
  method: "POST",
690
708
  headers: { "Content-Type": "application/json" },
691
709
  body: JSON.stringify(body)
@@ -694,7 +712,7 @@ async function reportUnregisteredAttempt(config, data) {
694
712
  }
695
713
  async function reportCounterpartyPreCheckFailure(config, data) {
696
714
  const apiBaseUrl = config.apiBaseUrl || DEFAULT_CONFIG.apiBaseUrl;
697
- await fetch(`${apiBaseUrl}/verification-activity/counterparty-pre-check-failure`, {
715
+ await sdkFetch(`${apiBaseUrl}/verification-activity/counterparty-pre-check-failure`, {
698
716
  method: "POST",
699
717
  headers: { "Content-Type": "application/json" },
700
718
  body: JSON.stringify(data)
@@ -950,7 +968,13 @@ var PLATFORM_HEADER_NAMES = /* @__PURE__ */ new Set([
950
968
  "x-agent-card-url",
951
969
  "x-agent-provider"
952
970
  ]);
953
- var PLATFORM_HEADER_PREFIXES = ["x-goog-", "x-stainless-", "x-anthropic-", "x-openai-"];
971
+ var PLATFORM_HEADER_PREFIXES = [
972
+ "x-goog-",
973
+ "x-stainless-",
974
+ "x-anthropic-",
975
+ "x-openai-",
976
+ "sec-ch-"
977
+ ];
954
978
  var KEY_FORMAT_PREFIXES = [
955
979
  "sk-ant-api03",
956
980
  "sk-ant-api",
@@ -1022,6 +1046,7 @@ function sanitizeHeaders(raw) {
1022
1046
  const headers = {};
1023
1047
  const platformHeaders = {};
1024
1048
  let apiKeyFormat;
1049
+ let cookieCount;
1025
1050
  let totalBytes = 0;
1026
1051
  let count = 0;
1027
1052
  if (!raw) return { headers };
@@ -1036,6 +1061,9 @@ function sanitizeHeaders(raw) {
1036
1061
  if (fmt && !apiKeyFormat) apiKeyFormat = fmt;
1037
1062
  continue;
1038
1063
  }
1064
+ if (name === "cookie") {
1065
+ cookieCount = joined.split(";").filter((s) => s.trim().length > 0).length;
1066
+ }
1039
1067
  if (isSecretHeader(name)) continue;
1040
1068
  let value = joined;
1041
1069
  if (byteLength(value) > MAX_HEADER_VALUE_BYTES) {
@@ -1051,6 +1079,7 @@ function sanitizeHeaders(raw) {
1051
1079
  const result = { headers };
1052
1080
  if (apiKeyFormat) result.apiKeyFormat = apiKeyFormat;
1053
1081
  if (Object.keys(platformHeaders).length > 0) result.platformHeaders = platformHeaders;
1082
+ if (cookieCount !== void 0) result.cookieCount = cookieCount;
1054
1083
  return result;
1055
1084
  }
1056
1085
  var CONNECTION_HEADER_SOURCES = [
@@ -1062,6 +1091,9 @@ var CONNECTION_HEADER_SOURCES = [
1062
1091
  ["region", ["x-vercel-ip-country-region", "cloudfront-viewer-country-region"]],
1063
1092
  ["countryRegionName", ["cloudfront-viewer-country-region-name"]],
1064
1093
  ["city", ["x-vercel-ip-city", "cloudfront-viewer-city"]],
1094
+ ["latitude", ["x-vercel-ip-latitude", "cloudfront-viewer-latitude"]],
1095
+ ["longitude", ["x-vercel-ip-longitude", "cloudfront-viewer-longitude"]],
1096
+ ["postalCode", ["x-vercel-ip-postal-code", "cloudfront-viewer-postal-code"]],
1065
1097
  ["asn", ["cloudfront-viewer-asn"]],
1066
1098
  ["tlsVersion", ["cloudfront-viewer-tls"]],
1067
1099
  ["httpVersion", ["cloudfront-viewer-http-version"]],
@@ -1096,19 +1128,25 @@ function deriveConnectionFromHeaders(headers) {
1096
1128
  }
1097
1129
  }
1098
1130
  }
1099
- if (!connection.ip) {
1131
+ {
1100
1132
  const addr = lower["cloudfront-viewer-address"]?.trim();
1101
- if (addr) connection.ip = addr.replace(/:\d+$/, "");
1133
+ const portMatch = addr?.match(/:(\d+)$/);
1134
+ if (portMatch) connection.sourcePort = portMatch[1];
1135
+ if (!connection.ip && addr) connection.ip = addr.replace(/:\d+$/, "");
1102
1136
  }
1103
1137
  if (!connection.ip) {
1104
1138
  const xff = lower["x-forwarded-for"];
1105
1139
  const client = xff?.split(",")[0]?.trim();
1106
1140
  if (client) connection.ip = client;
1107
1141
  }
1142
+ if (!connection.tlsCipher) {
1143
+ const cipher = connection.tlsVersion?.split(":")[1];
1144
+ if (cipher) connection.tlsCipher = cipher;
1145
+ }
1108
1146
  return Object.keys(connection).length > 0 ? connection : void 0;
1109
1147
  }
1110
1148
  function buildSdkObservedMetadata(rawHeaders) {
1111
- const { headers, apiKeyFormat, platformHeaders } = sanitizeHeaders(rawHeaders);
1149
+ const { headers, apiKeyFormat, platformHeaders, cookieCount } = sanitizeHeaders(rawHeaders);
1112
1150
  const flat = {};
1113
1151
  if (rawHeaders) {
1114
1152
  for (const name of Object.keys(rawHeaders)) {
@@ -1123,6 +1161,7 @@ function buildSdkObservedMetadata(rawHeaders) {
1123
1161
  ...apiKeyFormat && { apiKeyFormat },
1124
1162
  ...platformHeaders && { platformHeaders },
1125
1163
  ...connection && { connection },
1164
+ ...cookieCount !== void 0 && { cookieCount },
1126
1165
  schemaVersion: CAPTURE_SCHEMA_VERSION
1127
1166
  };
1128
1167
  }
@@ -1167,7 +1206,7 @@ async function fetchEdgeConfig(config, counterpartyId, opts) {
1167
1206
  }
1168
1207
  if (opts?.etag) headers["If-None-Match"] = opts.etag;
1169
1208
  try {
1170
- const response = await fetch(
1209
+ const response = await sdkFetch(
1171
1210
  `${config.apiBaseUrl}/endpoints/${encodeURIComponent(counterpartyId)}/edge-config`,
1172
1211
  { method: "GET", headers, signal: AbortSignal.timeout(opts?.timeoutMs ?? 5e3) }
1173
1212
  );
@@ -1279,12 +1318,43 @@ var PLATFORM_AGENT_SIGNATURES = [
1279
1318
  uaPatterns: [/perplexity/i],
1280
1319
  agentCardPatterns: [/perplexity\.ai/i],
1281
1320
  apiKeyFormatPatterns: [/^pplx-/i]
1321
+ },
1322
+ // Agentic browsers — versioned-token patterns ONLY (a stock Chrome UA must
1323
+ // never match). Distinct vendor keys (not 'chatgpt'/'perplexity'): the
1324
+ // browser product is a different surface than the assistant service, and
1325
+ // the DB registry mirrors one row per vendor. These sit after the primary
1326
+ // vendor entries so a UA carrying an explicit vendor token keeps its
1327
+ // primary attribution; a browser-shaped UA with the agentic token is
1328
+ // caught here BEFORE classify's human tier, which is what makes these
1329
+ // visits observable at all. Token drift is real (some builds mimic Chrome
1330
+ // outright) — re-verify against captured events when a new browser ships,
1331
+ // and check `sec-ch-ua` platformHeaders for the real brand.
1332
+ {
1333
+ vendor: "chatgpt-atlas",
1334
+ displayName: "ChatGPT Atlas browser",
1335
+ uaPatterns: [/\batlas\/\d/i],
1336
+ headerPatterns: [/^sec-ch-ua: .*atlas/i]
1337
+ },
1338
+ {
1339
+ vendor: "perplexity-comet",
1340
+ displayName: "Perplexity Comet browser",
1341
+ uaPatterns: [/\bcomet\/\d/i],
1342
+ headerPatterns: [/^sec-ch-ua: .*comet/i]
1343
+ },
1344
+ // Our own SDK self-identifies (sdkFetch stamps this UA) — first-party
1345
+ // traffic must never pollute the unknown/anonymous pool it exists to audit.
1346
+ {
1347
+ vendor: "astrasync-sdk",
1348
+ displayName: "AstraSync SDK",
1349
+ uaPatterns: [/^astrasync-sdk\//i]
1282
1350
  }
1283
1351
  ];
1284
1352
  function matchPlatformSignature(signatures, input) {
1285
1353
  const ua = input.userAgent ?? "";
1286
1354
  const cardUrl = input.agentCardUrl ?? "";
1287
1355
  const apiKeyFormat = input.apiKeyFormat ?? "";
1356
+ const ja4 = input.ja4 ?? "";
1357
+ const asn = input.asn ?? "";
1288
1358
  const headerLines = [];
1289
1359
  if (input.platformHeaders) {
1290
1360
  for (const [name, value] of Object.entries(input.platformHeaders)) {
@@ -1296,7 +1366,9 @@ function matchPlatformSignature(signatures, input) {
1296
1366
  const cardMatch = sig.agentCardPatterns?.some((p) => p.test(cardUrl));
1297
1367
  const keyMatch = apiKeyFormat.length > 0 && sig.apiKeyFormatPatterns?.some((p) => p.test(apiKeyFormat));
1298
1368
  const headerMatch = headerLines.length > 0 && sig.headerPatterns?.some((p) => headerLines.some((line) => p.test(line)));
1299
- if (uaMatch || cardMatch || keyMatch || headerMatch) {
1369
+ const ja4Match = ja4.length > 0 && sig.ja4Patterns?.some((p) => p.test(ja4));
1370
+ const asnMatch = asn.length > 0 && sig.asnList?.includes(asn);
1371
+ if (uaMatch || cardMatch || keyMatch || headerMatch || ja4Match || asnMatch) {
1300
1372
  const uaBase = ua.split(/[\s/]/)[0]?.toLowerCase() ?? "";
1301
1373
  const cardHost = (() => {
1302
1374
  try {
@@ -1305,7 +1377,7 @@ function matchPlatformSignature(signatures, input) {
1305
1377
  return "";
1306
1378
  }
1307
1379
  })();
1308
- const keyBasis = uaBase || cardHost || apiKeyFormat || "unknown";
1380
+ const keyBasis = uaBase || cardHost || apiKeyFormat || ja4 || asn && `asn${asn}` || "unknown";
1309
1381
  return {
1310
1382
  vendor: sig.vendor,
1311
1383
  fingerprintKey: `${sig.vendor}:${keyBasis}`,