@astrasyncai/verification-gateway 4.3.0 → 4.5.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 (87) hide show
  1. package/dist/adapters/express.js +104 -28
  2. package/dist/adapters/express.js.map +1 -1
  3. package/dist/adapters/express.mjs +104 -28
  4. package/dist/adapters/express.mjs.map +1 -1
  5. package/dist/adapters/mcp.js +63 -21
  6. package/dist/adapters/mcp.js.map +1 -1
  7. package/dist/adapters/mcp.mjs +63 -21
  8. package/dist/adapters/mcp.mjs.map +1 -1
  9. package/dist/adapters/nextjs.js +103 -27
  10. package/dist/adapters/nextjs.js.map +1 -1
  11. package/dist/adapters/nextjs.mjs +103 -27
  12. package/dist/adapters/nextjs.mjs.map +1 -1
  13. package/dist/adapters/sdk.js +33 -12
  14. package/dist/adapters/sdk.js.map +1 -1
  15. package/dist/adapters/sdk.mjs +33 -12
  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 +31 -10
  22. package/dist/bin/astrasync-codex-hook.js +31 -10
  23. package/dist/bin/astrasync-guard.js +35 -14
  24. package/dist/bin/astrasync.js +75 -29
  25. package/dist/browser/background.js +31 -10
  26. package/dist/browser/background.js.map +1 -1
  27. package/dist/browser/background.mjs +31 -10
  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 +31 -10
  34. package/dist/codex/index.js.map +1 -1
  35. package/dist/codex/index.mjs +31 -10
  36. package/dist/codex/index.mjs.map +1 -1
  37. package/dist/cursor/extension.js +31 -10
  38. package/dist/cursor/extension.js.map +1 -1
  39. package/dist/cursor/extension.mjs +31 -10
  40. package/dist/cursor/extension.mjs.map +1 -1
  41. package/dist/edge-config.d.mts +11 -4
  42. package/dist/edge-config.d.ts +11 -4
  43. package/dist/edge-config.js +24 -1
  44. package/dist/edge-config.js.map +1 -1
  45. package/dist/edge-config.mjs +22 -1
  46. package/dist/edge-config.mjs.map +1 -1
  47. package/dist/edge-core/index.d.mts +21 -7
  48. package/dist/edge-core/index.d.ts +21 -7
  49. package/dist/edge-core/index.js +170 -25
  50. package/dist/edge-core/index.js.map +1 -1
  51. package/dist/edge-core/index.mjs +170 -25
  52. package/dist/edge-core/index.mjs.map +1 -1
  53. package/dist/gateway/gateway.js +31 -10
  54. package/dist/gateway/gateway.js.map +1 -1
  55. package/dist/gateway/gateway.mjs +31 -10
  56. package/dist/gateway/gateway.mjs.map +1 -1
  57. package/dist/index.d.mts +51 -2
  58. package/dist/index.d.ts +51 -2
  59. package/dist/index.js +169 -40
  60. package/dist/index.js.map +1 -1
  61. package/dist/index.mjs +165 -40
  62. package/dist/index.mjs.map +1 -1
  63. package/dist/metadata-capture.d.mts +17 -0
  64. package/dist/metadata-capture.d.ts +17 -0
  65. package/dist/metadata-capture.js +25 -4
  66. package/dist/metadata-capture.js.map +1 -1
  67. package/dist/metadata-capture.mjs +25 -4
  68. package/dist/metadata-capture.mjs.map +1 -1
  69. package/dist/platform-signatures.d.mts +18 -1
  70. package/dist/platform-signatures.d.ts +18 -1
  71. package/dist/platform-signatures.js +35 -2
  72. package/dist/platform-signatures.js.map +1 -1
  73. package/dist/platform-signatures.mjs +35 -2
  74. package/dist/platform-signatures.mjs.map +1 -1
  75. package/dist/registration/index.js +25 -4
  76. package/dist/registration/index.js.map +1 -1
  77. package/dist/registration/index.mjs +25 -4
  78. package/dist/registration/index.mjs.map +1 -1
  79. package/dist/transport/index.js +22 -1
  80. package/dist/transport/index.js.map +1 -1
  81. package/dist/transport/index.mjs +22 -1
  82. package/dist/transport/index.mjs.map +1 -1
  83. package/dist/verify.js +38 -17
  84. package/dist/verify.js.map +1 -1
  85. package/dist/verify.mjs +38 -17
  86. package/dist/verify.mjs.map +1 -1
  87. package/package.json +2 -2
@@ -1,3 +1,24 @@
1
+ // src/version.ts
2
+ var SDK_VERSION = "4.5.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();
@@ -95,7 +113,7 @@ async function performInitCheck(apiBaseUrl, debug, strictInit, apiKey) {
95
113
  initCheckPerformed = true;
96
114
  try {
97
115
  const probeUrl = `${apiBaseUrl}/agents/verify-access`;
98
- const response = await fetch(probeUrl, { method: "HEAD" });
116
+ const response = await sdkFetch(probeUrl, { method: "HEAD" });
99
117
  const contentType = response.headers.get("content-type") ?? "";
100
118
  if (contentType.startsWith("text/html")) {
101
119
  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).`;
@@ -109,7 +127,7 @@ async function performInitCheck(apiBaseUrl, debug, strictInit, apiKey) {
109
127
  );
110
128
  }
111
129
  if (apiKey) {
112
- const selfCheck = await fetch(`${apiBaseUrl}/agents/verify-access/self-check`, {
130
+ const selfCheck = await sdkFetch(`${apiBaseUrl}/agents/verify-access/self-check`, {
113
131
  headers: { Authorization: `Bearer ${apiKey}` }
114
132
  });
115
133
  if (selfCheck.status === 401) {
@@ -356,7 +374,7 @@ async function callVerifyAccessAPI(config, request) {
356
374
  headers["X-API-Key"] = config.apiKey;
357
375
  }
358
376
  try {
359
- const response = await fetch(`${config.apiBaseUrl}/agents/verify-access`, {
377
+ const response = await sdkFetch(`${config.apiBaseUrl}/agents/verify-access`, {
360
378
  method: "POST",
361
379
  headers,
362
380
  body: JSON.stringify(body),
@@ -538,12 +556,15 @@ async function verify(config, request, options) {
538
556
  const verificationContext = apiResponse.verificationContext;
539
557
  const accessLevel = apiResponse.access?.accessLevel ?? "standard";
540
558
  const result = {
541
- // Round-18 G4: backend allowed access. Identity is verified (we resolved
542
- // the caller to an agent) and policy passed all gates. Read idVerified
543
- // from verificationContext for symmetry with the deny branch; default true
544
- // on success path since `access.allowed === true` implies identity was
545
- // resolvable (anonymous-allow paths flow through createGuidanceResponse).
546
- identityVerified: apiResponse.verificationContext?.idVerified !== false,
559
+ // 4.5.0 (probe F1): read idVerified from verificationContext with the
560
+ // SAME absent⇒false default as the deny branch. The previous `!== false`
561
+ // default manufactured `identityVerified: true` for anonymous
562
+ // allow_full/allow_partial/audit responses (which allow access but omit
563
+ // verificationContext entirely) — the "anonymous caller labelled
564
+ // verified" defect. Every authenticated success response has carried
565
+ // `verificationContext.idVerified: true` since v2.2.4, so a truth claim
566
+ // this load-bearing must never be defaulted on.
567
+ identityVerified: apiResponse.verificationContext?.idVerified === true,
547
568
  policyAllowed: true,
548
569
  accessLevel,
549
570
  agent,
@@ -601,7 +622,7 @@ async function fetchRoutes(config, counterpartyId) {
601
622
  headers["X-API-Key"] = config.apiKey;
602
623
  }
603
624
  try {
604
- const response = await fetch(
625
+ const response = await sdkFetch(
605
626
  `${config.apiBaseUrl}/endpoints/${encodeURIComponent(counterpartyId)}/routes`,
606
627
  { method: "GET", headers }
607
628
  );
@@ -620,7 +641,7 @@ async function reportUnregisteredAttempt(config, data) {
620
641
  ...counterpartyUrl && { counterpartyUrl },
621
642
  ...config.counterpartyId && { counterpartyId: config.counterpartyId }
622
643
  };
623
- await fetch(`${apiBaseUrl}/verification-activity/unregistered-attempt`, {
644
+ await sdkFetch(`${apiBaseUrl}/verification-activity/unregistered-attempt`, {
624
645
  method: "POST",
625
646
  headers: { "Content-Type": "application/json" },
626
647
  body: JSON.stringify(body)
@@ -629,7 +650,7 @@ async function reportUnregisteredAttempt(config, data) {
629
650
  }
630
651
  async function reportCounterpartyPreCheckFailure(config, data) {
631
652
  const apiBaseUrl = config.apiBaseUrl || DEFAULT_CONFIG.apiBaseUrl;
632
- await fetch(`${apiBaseUrl}/verification-activity/counterparty-pre-check-failure`, {
653
+ await sdkFetch(`${apiBaseUrl}/verification-activity/counterparty-pre-check-failure`, {
633
654
  method: "POST",
634
655
  headers: { "Content-Type": "application/json" },
635
656
  body: JSON.stringify(data)
@@ -768,7 +789,13 @@ var PLATFORM_HEADER_NAMES = /* @__PURE__ */ new Set([
768
789
  "x-agent-card-url",
769
790
  "x-agent-provider"
770
791
  ]);
771
- var PLATFORM_HEADER_PREFIXES = ["x-goog-", "x-stainless-", "x-anthropic-", "x-openai-"];
792
+ var PLATFORM_HEADER_PREFIXES = [
793
+ "x-goog-",
794
+ "x-stainless-",
795
+ "x-anthropic-",
796
+ "x-openai-",
797
+ "sec-ch-"
798
+ ];
772
799
  var KEY_FORMAT_PREFIXES = [
773
800
  "sk-ant-api03",
774
801
  "sk-ant-api",
@@ -840,6 +867,7 @@ function sanitizeHeaders(raw) {
840
867
  const headers = {};
841
868
  const platformHeaders = {};
842
869
  let apiKeyFormat;
870
+ let cookieCount;
843
871
  let totalBytes = 0;
844
872
  let count = 0;
845
873
  if (!raw) return { headers };
@@ -854,6 +882,9 @@ function sanitizeHeaders(raw) {
854
882
  if (fmt && !apiKeyFormat) apiKeyFormat = fmt;
855
883
  continue;
856
884
  }
885
+ if (name === "cookie") {
886
+ cookieCount = joined.split(";").filter((s) => s.trim().length > 0).length;
887
+ }
857
888
  if (isSecretHeader(name)) continue;
858
889
  let value = joined;
859
890
  if (byteLength(value) > MAX_HEADER_VALUE_BYTES) {
@@ -869,6 +900,7 @@ function sanitizeHeaders(raw) {
869
900
  const result = { headers };
870
901
  if (apiKeyFormat) result.apiKeyFormat = apiKeyFormat;
871
902
  if (Object.keys(platformHeaders).length > 0) result.platformHeaders = platformHeaders;
903
+ if (cookieCount !== void 0) result.cookieCount = cookieCount;
872
904
  return result;
873
905
  }
874
906
  var CONNECTION_HEADER_SOURCES = [
@@ -880,6 +912,9 @@ var CONNECTION_HEADER_SOURCES = [
880
912
  ["region", ["x-vercel-ip-country-region", "cloudfront-viewer-country-region"]],
881
913
  ["countryRegionName", ["cloudfront-viewer-country-region-name"]],
882
914
  ["city", ["x-vercel-ip-city", "cloudfront-viewer-city"]],
915
+ ["latitude", ["x-vercel-ip-latitude", "cloudfront-viewer-latitude"]],
916
+ ["longitude", ["x-vercel-ip-longitude", "cloudfront-viewer-longitude"]],
917
+ ["postalCode", ["x-vercel-ip-postal-code", "cloudfront-viewer-postal-code"]],
883
918
  ["asn", ["cloudfront-viewer-asn"]],
884
919
  ["tlsVersion", ["cloudfront-viewer-tls"]],
885
920
  ["httpVersion", ["cloudfront-viewer-http-version"]],
@@ -914,19 +949,25 @@ function deriveConnectionFromHeaders(headers) {
914
949
  }
915
950
  }
916
951
  }
917
- if (!connection.ip) {
952
+ {
918
953
  const addr = lower["cloudfront-viewer-address"]?.trim();
919
- if (addr) connection.ip = addr.replace(/:\d+$/, "");
954
+ const portMatch = addr?.match(/:(\d+)$/);
955
+ if (portMatch) connection.sourcePort = portMatch[1];
956
+ if (!connection.ip && addr) connection.ip = addr.replace(/:\d+$/, "");
920
957
  }
921
958
  if (!connection.ip) {
922
959
  const xff = lower["x-forwarded-for"];
923
960
  const client = xff?.split(",")[0]?.trim();
924
961
  if (client) connection.ip = client;
925
962
  }
963
+ if (!connection.tlsCipher) {
964
+ const cipher = connection.tlsVersion?.split(":")[1];
965
+ if (cipher) connection.tlsCipher = cipher;
966
+ }
926
967
  return Object.keys(connection).length > 0 ? connection : void 0;
927
968
  }
928
969
  function buildSdkObservedMetadata(rawHeaders) {
929
- const { headers, apiKeyFormat, platformHeaders } = sanitizeHeaders(rawHeaders);
970
+ const { headers, apiKeyFormat, platformHeaders, cookieCount } = sanitizeHeaders(rawHeaders);
930
971
  const flat = {};
931
972
  if (rawHeaders) {
932
973
  for (const name of Object.keys(rawHeaders)) {
@@ -941,6 +982,7 @@ function buildSdkObservedMetadata(rawHeaders) {
941
982
  ...apiKeyFormat && { apiKeyFormat },
942
983
  ...platformHeaders && { platformHeaders },
943
984
  ...connection && { connection },
985
+ ...cookieCount !== void 0 && { cookieCount },
944
986
  schemaVersion: CAPTURE_SCHEMA_VERSION
945
987
  };
946
988
  }
@@ -985,7 +1027,7 @@ async function fetchEdgeConfig(config, counterpartyId, opts) {
985
1027
  }
986
1028
  if (opts?.etag) headers["If-None-Match"] = opts.etag;
987
1029
  try {
988
- const response = await fetch(
1030
+ const response = await sdkFetch(
989
1031
  `${config.apiBaseUrl}/endpoints/${encodeURIComponent(counterpartyId)}/edge-config`,
990
1032
  { method: "GET", headers, signal: AbortSignal.timeout(opts?.timeoutMs ?? 5e3) }
991
1033
  );
@@ -1097,12 +1139,43 @@ var PLATFORM_AGENT_SIGNATURES = [
1097
1139
  uaPatterns: [/perplexity/i],
1098
1140
  agentCardPatterns: [/perplexity\.ai/i],
1099
1141
  apiKeyFormatPatterns: [/^pplx-/i]
1142
+ },
1143
+ // Agentic browsers — versioned-token patterns ONLY (a stock Chrome UA must
1144
+ // never match). Distinct vendor keys (not 'chatgpt'/'perplexity'): the
1145
+ // browser product is a different surface than the assistant service, and
1146
+ // the DB registry mirrors one row per vendor. These sit after the primary
1147
+ // vendor entries so a UA carrying an explicit vendor token keeps its
1148
+ // primary attribution; a browser-shaped UA with the agentic token is
1149
+ // caught here BEFORE classify's human tier, which is what makes these
1150
+ // visits observable at all. Token drift is real (some builds mimic Chrome
1151
+ // outright) — re-verify against captured events when a new browser ships,
1152
+ // and check `sec-ch-ua` platformHeaders for the real brand.
1153
+ {
1154
+ vendor: "chatgpt-atlas",
1155
+ displayName: "ChatGPT Atlas browser",
1156
+ uaPatterns: [/\batlas\/\d/i],
1157
+ headerPatterns: [/^sec-ch-ua: .*atlas/i]
1158
+ },
1159
+ {
1160
+ vendor: "perplexity-comet",
1161
+ displayName: "Perplexity Comet browser",
1162
+ uaPatterns: [/\bcomet\/\d/i],
1163
+ headerPatterns: [/^sec-ch-ua: .*comet/i]
1164
+ },
1165
+ // Our own SDK self-identifies (sdkFetch stamps this UA) — first-party
1166
+ // traffic must never pollute the unknown/anonymous pool it exists to audit.
1167
+ {
1168
+ vendor: "astrasync-sdk",
1169
+ displayName: "AstraSync SDK",
1170
+ uaPatterns: [/^astrasync-sdk\//i]
1100
1171
  }
1101
1172
  ];
1102
1173
  function matchPlatformSignature(signatures, input) {
1103
1174
  const ua = input.userAgent ?? "";
1104
1175
  const cardUrl = input.agentCardUrl ?? "";
1105
1176
  const apiKeyFormat = input.apiKeyFormat ?? "";
1177
+ const ja4 = input.ja4 ?? "";
1178
+ const asn = input.asn ?? "";
1106
1179
  const headerLines = [];
1107
1180
  if (input.platformHeaders) {
1108
1181
  for (const [name, value] of Object.entries(input.platformHeaders)) {
@@ -1114,7 +1187,9 @@ function matchPlatformSignature(signatures, input) {
1114
1187
  const cardMatch = sig.agentCardPatterns?.some((p) => p.test(cardUrl));
1115
1188
  const keyMatch = apiKeyFormat.length > 0 && sig.apiKeyFormatPatterns?.some((p) => p.test(apiKeyFormat));
1116
1189
  const headerMatch = headerLines.length > 0 && sig.headerPatterns?.some((p) => headerLines.some((line) => p.test(line)));
1117
- if (uaMatch || cardMatch || keyMatch || headerMatch) {
1190
+ const ja4Match = ja4.length > 0 && sig.ja4Patterns?.some((p) => p.test(ja4));
1191
+ const asnMatch = asn.length > 0 && sig.asnList?.includes(asn);
1192
+ if (uaMatch || cardMatch || keyMatch || headerMatch || ja4Match || asnMatch) {
1118
1193
  const uaBase = ua.split(/[\s/]/)[0]?.toLowerCase() ?? "";
1119
1194
  const cardHost = (() => {
1120
1195
  try {
@@ -1123,7 +1198,7 @@ function matchPlatformSignature(signatures, input) {
1123
1198
  return "";
1124
1199
  }
1125
1200
  })();
1126
- const keyBasis = uaBase || cardHost || apiKeyFormat || "unknown";
1201
+ const keyBasis = uaBase || cardHost || apiKeyFormat || ja4 || asn && `asn${asn}` || "unknown";
1127
1202
  return {
1128
1203
  vendor: sig.vendor,
1129
1204
  fingerprintKey: `${sig.vendor}:${keyBasis}`,
@@ -1139,9 +1214,10 @@ function detectPlatformFingerprint(input) {
1139
1214
 
1140
1215
  // src/edge-core/classify.ts
1141
1216
  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;
1217
+ var SELF_UA_PATTERN = /^astrasync-sdk\//i;
1142
1218
  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;
1143
1219
  var BROWSER_UA_PATTERN = /mozilla\/\d/i;
1144
- var _patterns = { STATIC_EXTENSIONS, BOT_UA_PATTERN, BROWSER_UA_PATTERN };
1220
+ var _patterns = { STATIC_EXTENSIONS, BOT_UA_PATTERN, BROWSER_UA_PATTERN, SELF_UA_PATTERN };
1145
1221
 
1146
1222
  // src/edge-core/events.ts
1147
1223
  function sampled(rate, random = Math.random) {
@@ -1749,7 +1825,7 @@ function createMiddleware(options) {
1749
1825
  };
1750
1826
  let result = await verify(config, verificationRequest);
1751
1827
  let stepUpHoldOutcome;
1752
- if (config.stepUpMaxWaitMs !== void 0 && result.identityVerified && requiresHumanApproval(result) && result.stepUpApproval?.pollUrl) {
1828
+ if (config.stepUpMaxWaitMs !== void 0 && result.policyAllowed && requiresHumanApproval(result) && result.stepUpApproval?.pollUrl) {
1753
1829
  stepUpHoldOutcome = await awaitStepUpApproval(config, result.stepUpApproval);
1754
1830
  if (stepUpHoldOutcome === "approved") {
1755
1831
  result = await verify(config, verificationRequest, { bypassCache: true });
@@ -1757,9 +1833,9 @@ function createMiddleware(options) {
1757
1833
  result.stepUpOutcome = stepUpHoldOutcome;
1758
1834
  }
1759
1835
  }
1760
- const approvalRequired = result.identityVerified && result.policyAllowed && requiresHumanApproval(result);
1836
+ const approvalRequired = result.policyAllowed && requiresHumanApproval(result);
1761
1837
  if (approvalRequired) annotateApprovalRequired(result);
1762
- if (!result.identityVerified || !result.policyAllowed || approvalRequired) {
1838
+ if (!result.policyAllowed || approvalRequired) {
1763
1839
  if (result.misconfigured && pathname.startsWith("/api/")) {
1764
1840
  return NextResponse.json(
1765
1841
  {