@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
@@ -35,6 +35,27 @@ __export(nextjs_exports, {
35
35
  });
36
36
  module.exports = __toCommonJS(nextjs_exports);
37
37
 
38
+ // src/version.ts
39
+ var SDK_VERSION = "4.4.0";
40
+
41
+ // src/http.ts
42
+ var SDK_USER_AGENT = `astrasync-sdk/${SDK_VERSION}`;
43
+ function withUserAgent(initHeaders) {
44
+ if (!initHeaders) return { "user-agent": SDK_USER_AGENT };
45
+ if (initHeaders instanceof Headers) {
46
+ const copy = new Headers(initHeaders);
47
+ if (!copy.has("user-agent")) copy.set("user-agent", SDK_USER_AGENT);
48
+ return copy;
49
+ }
50
+ if (Array.isArray(initHeaders)) {
51
+ const has2 = initHeaders.some(([name]) => name.toLowerCase() === "user-agent");
52
+ return has2 ? initHeaders : [...initHeaders, ["user-agent", SDK_USER_AGENT]];
53
+ }
54
+ const has = Object.keys(initHeaders).some((name) => name.toLowerCase() === "user-agent");
55
+ return has ? initHeaders : { "user-agent": SDK_USER_AGENT, ...initHeaders };
56
+ }
57
+ var sdkFetch = (input, init) => fetch(input, { ...init, headers: withUserAgent(init?.headers) });
58
+
38
59
  // src/adapters/stepup-poll.ts
39
60
  var DEFAULT_POLL_INTERVAL_MS = 3e3;
40
61
  var MIN_POLL_INTERVAL_MS = 250;
@@ -49,7 +70,7 @@ function resolvePollUrl(config, pollUrl) {
49
70
  async function pollStepUpStatus(config, approval) {
50
71
  if (!approval.pollUrl) return null;
51
72
  try {
52
- const res = await fetch(resolvePollUrl(config, approval.pollUrl), {
73
+ const res = await sdkFetch(resolvePollUrl(config, approval.pollUrl), {
53
74
  method: "GET",
54
75
  signal: AbortSignal.timeout(POLL_REQUEST_TIMEOUT_MS)
55
76
  });
@@ -101,9 +122,6 @@ function getTrustLevel(score) {
101
122
  return "BRONZE";
102
123
  }
103
124
 
104
- // src/version.ts
105
- var SDK_VERSION = "4.1.0";
106
-
107
125
  // src/well-known.ts
108
126
  var CACHE_TTL_MS = 60 * 60 * 1e3;
109
127
  var cache = /* @__PURE__ */ new Map();
@@ -132,7 +150,7 @@ async function performInitCheck(apiBaseUrl, debug, strictInit, apiKey) {
132
150
  initCheckPerformed = true;
133
151
  try {
134
152
  const probeUrl = `${apiBaseUrl}/agents/verify-access`;
135
- const response = await fetch(probeUrl, { method: "HEAD" });
153
+ const response = await sdkFetch(probeUrl, { method: "HEAD" });
136
154
  const contentType = response.headers.get("content-type") ?? "";
137
155
  if (contentType.startsWith("text/html")) {
138
156
  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).`;
@@ -146,7 +164,7 @@ async function performInitCheck(apiBaseUrl, debug, strictInit, apiKey) {
146
164
  );
147
165
  }
148
166
  if (apiKey) {
149
- const selfCheck = await fetch(`${apiBaseUrl}/agents/verify-access/self-check`, {
167
+ const selfCheck = await sdkFetch(`${apiBaseUrl}/agents/verify-access/self-check`, {
150
168
  headers: { Authorization: `Bearer ${apiKey}` }
151
169
  });
152
170
  if (selfCheck.status === 401) {
@@ -393,7 +411,7 @@ async function callVerifyAccessAPI(config, request) {
393
411
  headers["X-API-Key"] = config.apiKey;
394
412
  }
395
413
  try {
396
- const response = await fetch(`${config.apiBaseUrl}/agents/verify-access`, {
414
+ const response = await sdkFetch(`${config.apiBaseUrl}/agents/verify-access`, {
397
415
  method: "POST",
398
416
  headers,
399
417
  body: JSON.stringify(body),
@@ -638,7 +656,7 @@ async function fetchRoutes(config, counterpartyId) {
638
656
  headers["X-API-Key"] = config.apiKey;
639
657
  }
640
658
  try {
641
- const response = await fetch(
659
+ const response = await sdkFetch(
642
660
  `${config.apiBaseUrl}/endpoints/${encodeURIComponent(counterpartyId)}/routes`,
643
661
  { method: "GET", headers }
644
662
  );
@@ -657,7 +675,7 @@ async function reportUnregisteredAttempt(config, data) {
657
675
  ...counterpartyUrl && { counterpartyUrl },
658
676
  ...config.counterpartyId && { counterpartyId: config.counterpartyId }
659
677
  };
660
- await fetch(`${apiBaseUrl}/verification-activity/unregistered-attempt`, {
678
+ await sdkFetch(`${apiBaseUrl}/verification-activity/unregistered-attempt`, {
661
679
  method: "POST",
662
680
  headers: { "Content-Type": "application/json" },
663
681
  body: JSON.stringify(body)
@@ -666,7 +684,7 @@ async function reportUnregisteredAttempt(config, data) {
666
684
  }
667
685
  async function reportCounterpartyPreCheckFailure(config, data) {
668
686
  const apiBaseUrl = config.apiBaseUrl || DEFAULT_CONFIG.apiBaseUrl;
669
- await fetch(`${apiBaseUrl}/verification-activity/counterparty-pre-check-failure`, {
687
+ await sdkFetch(`${apiBaseUrl}/verification-activity/counterparty-pre-check-failure`, {
670
688
  method: "POST",
671
689
  headers: { "Content-Type": "application/json" },
672
690
  body: JSON.stringify(data)
@@ -805,7 +823,13 @@ var PLATFORM_HEADER_NAMES = /* @__PURE__ */ new Set([
805
823
  "x-agent-card-url",
806
824
  "x-agent-provider"
807
825
  ]);
808
- var PLATFORM_HEADER_PREFIXES = ["x-goog-", "x-stainless-", "x-anthropic-", "x-openai-"];
826
+ var PLATFORM_HEADER_PREFIXES = [
827
+ "x-goog-",
828
+ "x-stainless-",
829
+ "x-anthropic-",
830
+ "x-openai-",
831
+ "sec-ch-"
832
+ ];
809
833
  var KEY_FORMAT_PREFIXES = [
810
834
  "sk-ant-api03",
811
835
  "sk-ant-api",
@@ -877,6 +901,7 @@ function sanitizeHeaders(raw) {
877
901
  const headers = {};
878
902
  const platformHeaders = {};
879
903
  let apiKeyFormat;
904
+ let cookieCount;
880
905
  let totalBytes = 0;
881
906
  let count = 0;
882
907
  if (!raw) return { headers };
@@ -891,6 +916,9 @@ function sanitizeHeaders(raw) {
891
916
  if (fmt && !apiKeyFormat) apiKeyFormat = fmt;
892
917
  continue;
893
918
  }
919
+ if (name === "cookie") {
920
+ cookieCount = joined.split(";").filter((s) => s.trim().length > 0).length;
921
+ }
894
922
  if (isSecretHeader(name)) continue;
895
923
  let value = joined;
896
924
  if (byteLength(value) > MAX_HEADER_VALUE_BYTES) {
@@ -906,6 +934,7 @@ function sanitizeHeaders(raw) {
906
934
  const result = { headers };
907
935
  if (apiKeyFormat) result.apiKeyFormat = apiKeyFormat;
908
936
  if (Object.keys(platformHeaders).length > 0) result.platformHeaders = platformHeaders;
937
+ if (cookieCount !== void 0) result.cookieCount = cookieCount;
909
938
  return result;
910
939
  }
911
940
  var CONNECTION_HEADER_SOURCES = [
@@ -917,6 +946,9 @@ var CONNECTION_HEADER_SOURCES = [
917
946
  ["region", ["x-vercel-ip-country-region", "cloudfront-viewer-country-region"]],
918
947
  ["countryRegionName", ["cloudfront-viewer-country-region-name"]],
919
948
  ["city", ["x-vercel-ip-city", "cloudfront-viewer-city"]],
949
+ ["latitude", ["x-vercel-ip-latitude", "cloudfront-viewer-latitude"]],
950
+ ["longitude", ["x-vercel-ip-longitude", "cloudfront-viewer-longitude"]],
951
+ ["postalCode", ["x-vercel-ip-postal-code", "cloudfront-viewer-postal-code"]],
920
952
  ["asn", ["cloudfront-viewer-asn"]],
921
953
  ["tlsVersion", ["cloudfront-viewer-tls"]],
922
954
  ["httpVersion", ["cloudfront-viewer-http-version"]],
@@ -951,19 +983,25 @@ function deriveConnectionFromHeaders(headers) {
951
983
  }
952
984
  }
953
985
  }
954
- if (!connection.ip) {
986
+ {
955
987
  const addr = lower["cloudfront-viewer-address"]?.trim();
956
- if (addr) connection.ip = addr.replace(/:\d+$/, "");
988
+ const portMatch = addr?.match(/:(\d+)$/);
989
+ if (portMatch) connection.sourcePort = portMatch[1];
990
+ if (!connection.ip && addr) connection.ip = addr.replace(/:\d+$/, "");
957
991
  }
958
992
  if (!connection.ip) {
959
993
  const xff = lower["x-forwarded-for"];
960
994
  const client = xff?.split(",")[0]?.trim();
961
995
  if (client) connection.ip = client;
962
996
  }
997
+ if (!connection.tlsCipher) {
998
+ const cipher = connection.tlsVersion?.split(":")[1];
999
+ if (cipher) connection.tlsCipher = cipher;
1000
+ }
963
1001
  return Object.keys(connection).length > 0 ? connection : void 0;
964
1002
  }
965
1003
  function buildSdkObservedMetadata(rawHeaders) {
966
- const { headers, apiKeyFormat, platformHeaders } = sanitizeHeaders(rawHeaders);
1004
+ const { headers, apiKeyFormat, platformHeaders, cookieCount } = sanitizeHeaders(rawHeaders);
967
1005
  const flat = {};
968
1006
  if (rawHeaders) {
969
1007
  for (const name of Object.keys(rawHeaders)) {
@@ -978,6 +1016,7 @@ function buildSdkObservedMetadata(rawHeaders) {
978
1016
  ...apiKeyFormat && { apiKeyFormat },
979
1017
  ...platformHeaders && { platformHeaders },
980
1018
  ...connection && { connection },
1019
+ ...cookieCount !== void 0 && { cookieCount },
981
1020
  schemaVersion: CAPTURE_SCHEMA_VERSION
982
1021
  };
983
1022
  }
@@ -1022,7 +1061,7 @@ async function fetchEdgeConfig(config, counterpartyId, opts) {
1022
1061
  }
1023
1062
  if (opts?.etag) headers["If-None-Match"] = opts.etag;
1024
1063
  try {
1025
- const response = await fetch(
1064
+ const response = await sdkFetch(
1026
1065
  `${config.apiBaseUrl}/endpoints/${encodeURIComponent(counterpartyId)}/edge-config`,
1027
1066
  { method: "GET", headers, signal: AbortSignal.timeout(opts?.timeoutMs ?? 5e3) }
1028
1067
  );
@@ -1134,12 +1173,43 @@ var PLATFORM_AGENT_SIGNATURES = [
1134
1173
  uaPatterns: [/perplexity/i],
1135
1174
  agentCardPatterns: [/perplexity\.ai/i],
1136
1175
  apiKeyFormatPatterns: [/^pplx-/i]
1176
+ },
1177
+ // Agentic browsers — versioned-token patterns ONLY (a stock Chrome UA must
1178
+ // never match). Distinct vendor keys (not 'chatgpt'/'perplexity'): the
1179
+ // browser product is a different surface than the assistant service, and
1180
+ // the DB registry mirrors one row per vendor. These sit after the primary
1181
+ // vendor entries so a UA carrying an explicit vendor token keeps its
1182
+ // primary attribution; a browser-shaped UA with the agentic token is
1183
+ // caught here BEFORE classify's human tier, which is what makes these
1184
+ // visits observable at all. Token drift is real (some builds mimic Chrome
1185
+ // outright) — re-verify against captured events when a new browser ships,
1186
+ // and check `sec-ch-ua` platformHeaders for the real brand.
1187
+ {
1188
+ vendor: "chatgpt-atlas",
1189
+ displayName: "ChatGPT Atlas browser",
1190
+ uaPatterns: [/\batlas\/\d/i],
1191
+ headerPatterns: [/^sec-ch-ua: .*atlas/i]
1192
+ },
1193
+ {
1194
+ vendor: "perplexity-comet",
1195
+ displayName: "Perplexity Comet browser",
1196
+ uaPatterns: [/\bcomet\/\d/i],
1197
+ headerPatterns: [/^sec-ch-ua: .*comet/i]
1198
+ },
1199
+ // Our own SDK self-identifies (sdkFetch stamps this UA) — first-party
1200
+ // traffic must never pollute the unknown/anonymous pool it exists to audit.
1201
+ {
1202
+ vendor: "astrasync-sdk",
1203
+ displayName: "AstraSync SDK",
1204
+ uaPatterns: [/^astrasync-sdk\//i]
1137
1205
  }
1138
1206
  ];
1139
1207
  function matchPlatformSignature(signatures, input) {
1140
1208
  const ua = input.userAgent ?? "";
1141
1209
  const cardUrl = input.agentCardUrl ?? "";
1142
1210
  const apiKeyFormat = input.apiKeyFormat ?? "";
1211
+ const ja4 = input.ja4 ?? "";
1212
+ const asn = input.asn ?? "";
1143
1213
  const headerLines = [];
1144
1214
  if (input.platformHeaders) {
1145
1215
  for (const [name, value] of Object.entries(input.platformHeaders)) {
@@ -1151,7 +1221,9 @@ function matchPlatformSignature(signatures, input) {
1151
1221
  const cardMatch = sig.agentCardPatterns?.some((p) => p.test(cardUrl));
1152
1222
  const keyMatch = apiKeyFormat.length > 0 && sig.apiKeyFormatPatterns?.some((p) => p.test(apiKeyFormat));
1153
1223
  const headerMatch = headerLines.length > 0 && sig.headerPatterns?.some((p) => headerLines.some((line) => p.test(line)));
1154
- if (uaMatch || cardMatch || keyMatch || headerMatch) {
1224
+ const ja4Match = ja4.length > 0 && sig.ja4Patterns?.some((p) => p.test(ja4));
1225
+ const asnMatch = asn.length > 0 && sig.asnList?.includes(asn);
1226
+ if (uaMatch || cardMatch || keyMatch || headerMatch || ja4Match || asnMatch) {
1155
1227
  const uaBase = ua.split(/[\s/]/)[0]?.toLowerCase() ?? "";
1156
1228
  const cardHost = (() => {
1157
1229
  try {
@@ -1160,7 +1232,7 @@ function matchPlatformSignature(signatures, input) {
1160
1232
  return "";
1161
1233
  }
1162
1234
  })();
1163
- const keyBasis = uaBase || cardHost || apiKeyFormat || "unknown";
1235
+ const keyBasis = uaBase || cardHost || apiKeyFormat || ja4 || asn && `asn${asn}` || "unknown";
1164
1236
  return {
1165
1237
  vendor: sig.vendor,
1166
1238
  fingerprintKey: `${sig.vendor}:${keyBasis}`,