@astrasyncai/verification-gateway 4.2.1 → 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.
- package/README.md +45 -21
- package/dist/adapter-interface/interface.d.mts +2 -2
- package/dist/adapter-interface/interface.d.ts +2 -2
- package/dist/adapters/express.d.mts +2 -2
- package/dist/adapters/express.d.ts +2 -2
- package/dist/adapters/express.js +145 -21
- package/dist/adapters/express.js.map +1 -1
- package/dist/adapters/express.mjs +145 -21
- package/dist/adapters/express.mjs.map +1 -1
- package/dist/adapters/mcp.d.mts +38 -30
- package/dist/adapters/mcp.d.ts +38 -30
- package/dist/adapters/mcp.js +72 -16
- package/dist/adapters/mcp.js.map +1 -1
- package/dist/adapters/mcp.mjs +72 -16
- package/dist/adapters/mcp.mjs.map +1 -1
- package/dist/adapters/nextjs.d.mts +2 -2
- package/dist/adapters/nextjs.d.ts +2 -2
- package/dist/adapters/nextjs.js +141 -19
- package/dist/adapters/nextjs.js.map +1 -1
- package/dist/adapters/nextjs.mjs +141 -19
- package/dist/adapters/nextjs.mjs.map +1 -1
- package/dist/adapters/sdk.d.mts +2 -2
- package/dist/adapters/sdk.d.ts +2 -2
- package/dist/adapters/sdk.js +24 -6
- package/dist/adapters/sdk.js.map +1 -1
- package/dist/adapters/sdk.mjs +24 -6
- package/dist/adapters/sdk.mjs.map +1 -1
- package/dist/agent/index.d.mts +2 -2
- package/dist/agent/index.d.ts +2 -2
- package/dist/agent/index.js +24 -3
- package/dist/agent/index.js.map +1 -1
- package/dist/agent/index.mjs +24 -3
- package/dist/agent/index.mjs.map +1 -1
- package/dist/bin/astrasync-claude-hook.js +22 -4
- package/dist/bin/astrasync-codex-hook.js +22 -4
- package/dist/bin/astrasync-guard.js +26 -8
- package/dist/bin/astrasync.js +76 -23
- package/dist/browser/background.js +22 -4
- package/dist/browser/background.js.map +1 -1
- package/dist/browser/background.mjs +22 -4
- package/dist/browser/background.mjs.map +1 -1
- package/dist/browser/browser-adapter.d.mts +2 -2
- package/dist/browser/browser-adapter.d.ts +2 -2
- package/dist/claude-code/claude-code-adapter.d.mts +2 -2
- package/dist/claude-code/claude-code-adapter.d.ts +2 -2
- package/dist/cli/index.d.mts +2 -2
- package/dist/cli/index.d.ts +2 -2
- package/dist/cli/index.js +23 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +23 -2
- package/dist/cli/index.mjs.map +1 -1
- package/dist/codex/index.d.mts +2 -2
- package/dist/codex/index.d.ts +2 -2
- package/dist/codex/index.js +22 -4
- package/dist/codex/index.js.map +1 -1
- package/dist/codex/index.mjs +22 -4
- package/dist/codex/index.mjs.map +1 -1
- package/dist/cursor/cursor-adapter.d.mts +2 -2
- package/dist/cursor/cursor-adapter.d.ts +2 -2
- package/dist/cursor/extension.d.mts +2 -2
- package/dist/cursor/extension.d.ts +2 -2
- package/dist/cursor/extension.js +22 -4
- package/dist/cursor/extension.js.map +1 -1
- package/dist/cursor/extension.mjs +22 -4
- package/dist/cursor/extension.mjs.map +1 -1
- package/dist/edge-config.d.mts +1 -1
- package/dist/edge-config.d.ts +1 -1
- package/dist/edge-config.js +24 -1
- package/dist/edge-config.js.map +1 -1
- package/dist/edge-config.mjs +22 -1
- package/dist/edge-config.mjs.map +1 -1
- package/dist/edge-core/index.d.mts +1 -1
- package/dist/edge-core/index.d.ts +1 -1
- package/dist/edge-core/index.js +121 -16
- package/dist/edge-core/index.js.map +1 -1
- package/dist/edge-core/index.mjs +121 -16
- package/dist/edge-core/index.mjs.map +1 -1
- package/dist/{express-D_4hTn5Z.d.mts → express-CR6F3njD.d.mts} +1 -1
- package/dist/{express-BVd1_3FE.d.ts → express-DaqL11tx.d.ts} +1 -1
- package/dist/gateway/gateway.d.mts +2 -2
- package/dist/gateway/gateway.d.ts +2 -2
- package/dist/gateway/gateway.js +22 -4
- package/dist/gateway/gateway.js.map +1 -1
- package/dist/gateway/gateway.mjs +22 -4
- package/dist/gateway/gateway.mjs.map +1 -1
- package/dist/git-trigger/git-hooks.d.mts +2 -2
- package/dist/git-trigger/git-hooks.d.ts +2 -2
- package/dist/{index-BqHKlLOU.d.ts → index-B5Ts1WtM.d.ts} +1 -1
- package/dist/{index-T1aBoUcc.d.ts → index-BfIxCYIZ.d.ts} +1 -1
- package/dist/{index-BTeQ9fgV.d.mts → index-Cub7lK-T.d.mts} +1 -1
- package/dist/{index-DQb5-_1X.d.mts → index-CwAh7Tln.d.mts} +1 -1
- package/dist/index.d.mts +58 -9
- package/dist/index.d.ts +58 -9
- package/dist/index.js +218 -54
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +214 -54
- package/dist/index.mjs.map +1 -1
- package/dist/local-evaluator/evaluator.d.mts +2 -2
- package/dist/local-evaluator/evaluator.d.ts +2 -2
- package/dist/metadata-capture.d.mts +35 -5
- package/dist/metadata-capture.d.ts +35 -5
- package/dist/metadata-capture.js +44 -6
- package/dist/metadata-capture.js.map +1 -1
- package/dist/metadata-capture.mjs +43 -6
- package/dist/metadata-capture.mjs.map +1 -1
- package/dist/{nextjs-w7RTbP1P.d.ts → nextjs-DL6uE1ye.d.ts} +1 -1
- package/dist/{nextjs-WyeVr2Kp.d.mts → nextjs-DlX6xwbU.d.mts} +1 -1
- package/dist/platform-signatures.d.mts +18 -1
- package/dist/platform-signatures.d.ts +18 -1
- package/dist/platform-signatures.js +35 -2
- package/dist/platform-signatures.js.map +1 -1
- package/dist/platform-signatures.mjs +35 -2
- package/dist/platform-signatures.mjs.map +1 -1
- package/dist/registration/index.js +25 -4
- package/dist/registration/index.js.map +1 -1
- package/dist/registration/index.mjs +25 -4
- package/dist/registration/index.mjs.map +1 -1
- package/dist/{sdk-CbTNIkAa.d.mts → sdk-Bbh9B_dw.d.mts} +1 -1
- package/dist/{sdk-QeX0Z8Ho.d.ts → sdk-C6SASmsh.d.ts} +1 -1
- package/dist/transport/index.d.mts +2 -2
- package/dist/transport/index.d.ts +2 -2
- package/dist/transport/index.js +22 -1
- package/dist/transport/index.js.map +1 -1
- package/dist/transport/index.mjs +22 -1
- package/dist/transport/index.mjs.map +1 -1
- package/dist/{types-r850cOt0.d.mts → types-C0zfSXMB.d.mts} +22 -0
- package/dist/{types-DqfPU5Bl.d.mts → types-C7Rb-w0d.d.mts} +1 -1
- package/dist/{types-sMxBT-nM.d.ts → types-DwSfO7Sb.d.ts} +22 -0
- package/dist/{types-BLUx92FJ.d.ts → types-nIh3HtuA.d.ts} +1 -1
- package/dist/ui/index.d.mts +1 -1
- package/dist/ui/index.d.ts +1 -1
- package/dist/verify.d.mts +1 -1
- package/dist/verify.d.ts +1 -1
- package/dist/verify.js +29 -11
- package/dist/verify.js.map +1 -1
- package/dist/verify.mjs +29 -11
- package/dist/verify.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -126,7 +126,25 @@ function getCapabilities(accessLevel) {
|
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
// src/version.ts
|
|
129
|
-
var SDK_VERSION = "4.
|
|
129
|
+
var SDK_VERSION = "4.4.0";
|
|
130
|
+
|
|
131
|
+
// src/http.ts
|
|
132
|
+
var SDK_USER_AGENT = `astrasync-sdk/${SDK_VERSION}`;
|
|
133
|
+
function withUserAgent(initHeaders) {
|
|
134
|
+
if (!initHeaders) return { "user-agent": SDK_USER_AGENT };
|
|
135
|
+
if (initHeaders instanceof Headers) {
|
|
136
|
+
const copy = new Headers(initHeaders);
|
|
137
|
+
if (!copy.has("user-agent")) copy.set("user-agent", SDK_USER_AGENT);
|
|
138
|
+
return copy;
|
|
139
|
+
}
|
|
140
|
+
if (Array.isArray(initHeaders)) {
|
|
141
|
+
const has2 = initHeaders.some(([name]) => name.toLowerCase() === "user-agent");
|
|
142
|
+
return has2 ? initHeaders : [...initHeaders, ["user-agent", SDK_USER_AGENT]];
|
|
143
|
+
}
|
|
144
|
+
const has = Object.keys(initHeaders).some((name) => name.toLowerCase() === "user-agent");
|
|
145
|
+
return has ? initHeaders : { "user-agent": SDK_USER_AGENT, ...initHeaders };
|
|
146
|
+
}
|
|
147
|
+
var sdkFetch = (input, init) => fetch(input, { ...init, headers: withUserAgent(init?.headers) });
|
|
130
148
|
|
|
131
149
|
// src/well-known.ts
|
|
132
150
|
var CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
@@ -138,7 +156,7 @@ function wellKnownUrl(apiBaseUrl) {
|
|
|
138
156
|
}
|
|
139
157
|
async function fetchWellKnown(apiBaseUrl) {
|
|
140
158
|
const url = wellKnownUrl(apiBaseUrl);
|
|
141
|
-
const response = await
|
|
159
|
+
const response = await sdkFetch(url, {
|
|
142
160
|
method: "GET",
|
|
143
161
|
headers: { Accept: "application/json" },
|
|
144
162
|
signal: AbortSignal.timeout(5e3)
|
|
@@ -208,7 +226,7 @@ async function performInitCheck(apiBaseUrl, debug, strictInit, apiKey) {
|
|
|
208
226
|
initCheckPerformed = true;
|
|
209
227
|
try {
|
|
210
228
|
const probeUrl = `${apiBaseUrl}/agents/verify-access`;
|
|
211
|
-
const response = await
|
|
229
|
+
const response = await sdkFetch(probeUrl, { method: "HEAD" });
|
|
212
230
|
const contentType = response.headers.get("content-type") ?? "";
|
|
213
231
|
if (contentType.startsWith("text/html")) {
|
|
214
232
|
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).`;
|
|
@@ -222,7 +240,7 @@ async function performInitCheck(apiBaseUrl, debug, strictInit, apiKey) {
|
|
|
222
240
|
);
|
|
223
241
|
}
|
|
224
242
|
if (apiKey) {
|
|
225
|
-
const selfCheck = await
|
|
243
|
+
const selfCheck = await sdkFetch(`${apiBaseUrl}/agents/verify-access/self-check`, {
|
|
226
244
|
headers: { Authorization: `Bearer ${apiKey}` }
|
|
227
245
|
});
|
|
228
246
|
if (selfCheck.status === 401) {
|
|
@@ -475,7 +493,7 @@ async function callVerifyAccessAPI(config, request) {
|
|
|
475
493
|
headers["X-API-Key"] = config.apiKey;
|
|
476
494
|
}
|
|
477
495
|
try {
|
|
478
|
-
const response = await
|
|
496
|
+
const response = await sdkFetch(`${config.apiBaseUrl}/agents/verify-access`, {
|
|
479
497
|
method: "POST",
|
|
480
498
|
headers,
|
|
481
499
|
body: JSON.stringify(body),
|
|
@@ -718,7 +736,7 @@ async function recordDecision(config, sessionId, decision, reason) {
|
|
|
718
736
|
headers["Authorization"] = `Bearer ${config.apiKey}`;
|
|
719
737
|
headers["X-API-Key"] = config.apiKey;
|
|
720
738
|
}
|
|
721
|
-
await
|
|
739
|
+
await sdkFetch(`${config.apiBaseUrl}/agents/verify-access/${sessionId}/decision`, {
|
|
722
740
|
method: "POST",
|
|
723
741
|
headers,
|
|
724
742
|
body: JSON.stringify({ decision, reason })
|
|
@@ -736,7 +754,7 @@ async function reportAttempt(config, report) {
|
|
|
736
754
|
...report,
|
|
737
755
|
...!report.counterpartyId && config.counterpartyId && { counterpartyId: config.counterpartyId }
|
|
738
756
|
};
|
|
739
|
-
await
|
|
757
|
+
await sdkFetch(`${apiBaseUrl}/agents/verify-access/attempt-report`, {
|
|
740
758
|
method: "POST",
|
|
741
759
|
headers,
|
|
742
760
|
body: JSON.stringify(body)
|
|
@@ -751,7 +769,7 @@ async function fetchRoutes(config, counterpartyId) {
|
|
|
751
769
|
headers["X-API-Key"] = config.apiKey;
|
|
752
770
|
}
|
|
753
771
|
try {
|
|
754
|
-
const response = await
|
|
772
|
+
const response = await sdkFetch(
|
|
755
773
|
`${config.apiBaseUrl}/endpoints/${encodeURIComponent(counterpartyId)}/routes`,
|
|
756
774
|
{ method: "GET", headers }
|
|
757
775
|
);
|
|
@@ -770,7 +788,7 @@ async function reportUnregisteredAttempt(config, data) {
|
|
|
770
788
|
...counterpartyUrl && { counterpartyUrl },
|
|
771
789
|
...config.counterpartyId && { counterpartyId: config.counterpartyId }
|
|
772
790
|
};
|
|
773
|
-
await
|
|
791
|
+
await sdkFetch(`${apiBaseUrl}/verification-activity/unregistered-attempt`, {
|
|
774
792
|
method: "POST",
|
|
775
793
|
headers: { "Content-Type": "application/json" },
|
|
776
794
|
body: JSON.stringify(body)
|
|
@@ -779,7 +797,7 @@ async function reportUnregisteredAttempt(config, data) {
|
|
|
779
797
|
}
|
|
780
798
|
async function reportCounterpartyPreCheckFailure(config, data) {
|
|
781
799
|
const apiBaseUrl = config.apiBaseUrl || DEFAULT_CONFIG.apiBaseUrl;
|
|
782
|
-
await
|
|
800
|
+
await sdkFetch(`${apiBaseUrl}/verification-activity/counterparty-pre-check-failure`, {
|
|
783
801
|
method: "POST",
|
|
784
802
|
headers: { "Content-Type": "application/json" },
|
|
785
803
|
body: JSON.stringify(data)
|
|
@@ -813,7 +831,7 @@ function resolvePollUrl(config, pollUrl) {
|
|
|
813
831
|
async function pollStepUpStatus(config, approval) {
|
|
814
832
|
if (!approval.pollUrl) return null;
|
|
815
833
|
try {
|
|
816
|
-
const res = await
|
|
834
|
+
const res = await sdkFetch(resolvePollUrl(config, approval.pollUrl), {
|
|
817
835
|
method: "GET",
|
|
818
836
|
signal: AbortSignal.timeout(POLL_REQUEST_TIMEOUT_MS)
|
|
819
837
|
});
|
|
@@ -996,6 +1014,34 @@ function extractHttpCredentials(headers) {
|
|
|
996
1014
|
return credentials;
|
|
997
1015
|
}
|
|
998
1016
|
|
|
1017
|
+
// src/verified-hop.ts
|
|
1018
|
+
var MCP_VERIFIED_HOP_HEADER = "X-Astra-Verified-Hop";
|
|
1019
|
+
var MCP_VERIFIED_HOP_MAX_AGE_MS = 6e4;
|
|
1020
|
+
function serializeVerifiedHop(marker) {
|
|
1021
|
+
return `${marker.astraId};${marker.sessionId ?? ""};${marker.checkedAt}`;
|
|
1022
|
+
}
|
|
1023
|
+
function parseVerifiedHop(value) {
|
|
1024
|
+
if (!value) return null;
|
|
1025
|
+
const parts = value.split(";");
|
|
1026
|
+
if (parts.length !== 3) return null;
|
|
1027
|
+
const [astraId, sessionId, checkedAtRaw] = parts;
|
|
1028
|
+
if (!astraId) return null;
|
|
1029
|
+
const checkedAt = Number(checkedAtRaw);
|
|
1030
|
+
if (!Number.isFinite(checkedAt) || checkedAt <= 0) return null;
|
|
1031
|
+
return {
|
|
1032
|
+
astraId,
|
|
1033
|
+
...sessionId ? { sessionId } : {},
|
|
1034
|
+
checkedAt
|
|
1035
|
+
};
|
|
1036
|
+
}
|
|
1037
|
+
function isVerifiedHopValidFor(marker, expectedAstraId, opts = {}) {
|
|
1038
|
+
if (!marker) return false;
|
|
1039
|
+
if (marker.astraId !== expectedAstraId) return false;
|
|
1040
|
+
const maxAge = opts.maxAgeMs ?? MCP_VERIFIED_HOP_MAX_AGE_MS;
|
|
1041
|
+
const now = opts.now ?? Date.now();
|
|
1042
|
+
return now - marker.checkedAt <= maxAge && now >= marker.checkedAt;
|
|
1043
|
+
}
|
|
1044
|
+
|
|
999
1045
|
// src/adapters/http-pdlss.ts
|
|
1000
1046
|
var HTTP_METHOD_ACTION_TABLE = {
|
|
1001
1047
|
GET: "data.read",
|
|
@@ -1133,7 +1179,9 @@ var SECRET_HEADER_NAMES = /* @__PURE__ */ new Set([
|
|
|
1133
1179
|
"x-astrasync-signature",
|
|
1134
1180
|
"x-astrasync-secret",
|
|
1135
1181
|
"x-hub-signature",
|
|
1136
|
-
"x-hub-signature-256"
|
|
1182
|
+
"x-hub-signature-256",
|
|
1183
|
+
// Short-lived verify dedupe token — must never persist in stored metadata.
|
|
1184
|
+
"x-astra-verified-hop"
|
|
1137
1185
|
]);
|
|
1138
1186
|
var SECRET_HEADER_PREFIXES = ["x-astrasync-", "kya-", "kya_"];
|
|
1139
1187
|
var CREDENTIAL_HEADER_NAMES = /* @__PURE__ */ new Set([
|
|
@@ -1158,7 +1206,13 @@ var PLATFORM_HEADER_NAMES = /* @__PURE__ */ new Set([
|
|
|
1158
1206
|
"x-agent-card-url",
|
|
1159
1207
|
"x-agent-provider"
|
|
1160
1208
|
]);
|
|
1161
|
-
var PLATFORM_HEADER_PREFIXES = [
|
|
1209
|
+
var PLATFORM_HEADER_PREFIXES = [
|
|
1210
|
+
"x-goog-",
|
|
1211
|
+
"x-stainless-",
|
|
1212
|
+
"x-anthropic-",
|
|
1213
|
+
"x-openai-",
|
|
1214
|
+
"sec-ch-"
|
|
1215
|
+
];
|
|
1162
1216
|
var KEY_FORMAT_PREFIXES = [
|
|
1163
1217
|
"sk-ant-api03",
|
|
1164
1218
|
"sk-ant-api",
|
|
@@ -1230,6 +1284,7 @@ function sanitizeHeaders(raw) {
|
|
|
1230
1284
|
const headers = {};
|
|
1231
1285
|
const platformHeaders = {};
|
|
1232
1286
|
let apiKeyFormat;
|
|
1287
|
+
let cookieCount;
|
|
1233
1288
|
let totalBytes = 0;
|
|
1234
1289
|
let count = 0;
|
|
1235
1290
|
if (!raw) return { headers };
|
|
@@ -1244,6 +1299,9 @@ function sanitizeHeaders(raw) {
|
|
|
1244
1299
|
if (fmt && !apiKeyFormat) apiKeyFormat = fmt;
|
|
1245
1300
|
continue;
|
|
1246
1301
|
}
|
|
1302
|
+
if (name === "cookie") {
|
|
1303
|
+
cookieCount = joined.split(";").filter((s) => s.trim().length > 0).length;
|
|
1304
|
+
}
|
|
1247
1305
|
if (isSecretHeader(name)) continue;
|
|
1248
1306
|
let value = joined;
|
|
1249
1307
|
if (byteLength(value) > MAX_HEADER_VALUE_BYTES) {
|
|
@@ -1259,6 +1317,7 @@ function sanitizeHeaders(raw) {
|
|
|
1259
1317
|
const result = { headers };
|
|
1260
1318
|
if (apiKeyFormat) result.apiKeyFormat = apiKeyFormat;
|
|
1261
1319
|
if (Object.keys(platformHeaders).length > 0) result.platformHeaders = platformHeaders;
|
|
1320
|
+
if (cookieCount !== void 0) result.cookieCount = cookieCount;
|
|
1262
1321
|
return result;
|
|
1263
1322
|
}
|
|
1264
1323
|
function extractPlatformHeaders(sanitized) {
|
|
@@ -1276,16 +1335,28 @@ var CONNECTION_HEADER_SOURCES = [
|
|
|
1276
1335
|
["cf-ipcountry", "x-vercel-ip-country", "cloudfront-viewer-country", "fastly-geo-country"]
|
|
1277
1336
|
],
|
|
1278
1337
|
["region", ["x-vercel-ip-country-region", "cloudfront-viewer-country-region"]],
|
|
1338
|
+
["countryRegionName", ["cloudfront-viewer-country-region-name"]],
|
|
1279
1339
|
["city", ["x-vercel-ip-city", "cloudfront-viewer-city"]],
|
|
1340
|
+
["latitude", ["x-vercel-ip-latitude", "cloudfront-viewer-latitude"]],
|
|
1341
|
+
["longitude", ["x-vercel-ip-longitude", "cloudfront-viewer-longitude"]],
|
|
1342
|
+
["postalCode", ["x-vercel-ip-postal-code", "cloudfront-viewer-postal-code"]],
|
|
1280
1343
|
["asn", ["cloudfront-viewer-asn"]],
|
|
1281
1344
|
["tlsVersion", ["cloudfront-viewer-tls"]],
|
|
1282
1345
|
["httpVersion", ["cloudfront-viewer-http-version"]],
|
|
1346
|
+
["timeZone", ["x-vercel-ip-timezone", "cloudfront-viewer-time-zone"]],
|
|
1347
|
+
["metroCode", ["cloudfront-viewer-metro-code"]],
|
|
1283
1348
|
// TLS fingerprints can only be computed where TLS terminates. When a CDN
|
|
1284
1349
|
// terminates TLS in front of the merchant, its fingerprint headers ARE the
|
|
1285
1350
|
// connection-layer truth — normalising them here is the permanent design.
|
|
1286
|
-
|
|
1287
|
-
|
|
1351
|
+
// CloudFront delivers JA3/JA4 only via an origin request policy and only
|
|
1352
|
+
// for HTTPS viewer connections.
|
|
1353
|
+
["tlsFingerprint", ["cf-ja3-hash", "cloudfront-viewer-ja3-fingerprint"]],
|
|
1354
|
+
["ja4", ["cf-ja4", "cloudfront-viewer-ja4-fingerprint"]],
|
|
1355
|
+
// Header-structure fingerprint: browser/SDK-distinctive header ordering.
|
|
1356
|
+
["headerOrder", ["cloudfront-viewer-header-order"]],
|
|
1357
|
+
["headerCount", ["cloudfront-viewer-header-count"]]
|
|
1288
1358
|
];
|
|
1359
|
+
var MAX_CONNECTION_VALUE_CHARS = 256;
|
|
1289
1360
|
function deriveConnectionFromHeaders(headers) {
|
|
1290
1361
|
if (!headers) return void 0;
|
|
1291
1362
|
const lower = {};
|
|
@@ -1298,20 +1369,30 @@ function deriveConnectionFromHeaders(headers) {
|
|
|
1298
1369
|
for (const source of sources) {
|
|
1299
1370
|
const value = lower[source]?.trim();
|
|
1300
1371
|
if (value) {
|
|
1301
|
-
connection[key] = value;
|
|
1372
|
+
connection[key] = value.slice(0, MAX_CONNECTION_VALUE_CHARS);
|
|
1302
1373
|
break;
|
|
1303
1374
|
}
|
|
1304
1375
|
}
|
|
1305
1376
|
}
|
|
1377
|
+
{
|
|
1378
|
+
const addr = lower["cloudfront-viewer-address"]?.trim();
|
|
1379
|
+
const portMatch = addr?.match(/:(\d+)$/);
|
|
1380
|
+
if (portMatch) connection.sourcePort = portMatch[1];
|
|
1381
|
+
if (!connection.ip && addr) connection.ip = addr.replace(/:\d+$/, "");
|
|
1382
|
+
}
|
|
1306
1383
|
if (!connection.ip) {
|
|
1307
1384
|
const xff = lower["x-forwarded-for"];
|
|
1308
1385
|
const client = xff?.split(",")[0]?.trim();
|
|
1309
1386
|
if (client) connection.ip = client;
|
|
1310
1387
|
}
|
|
1388
|
+
if (!connection.tlsCipher) {
|
|
1389
|
+
const cipher = connection.tlsVersion?.split(":")[1];
|
|
1390
|
+
if (cipher) connection.tlsCipher = cipher;
|
|
1391
|
+
}
|
|
1311
1392
|
return Object.keys(connection).length > 0 ? connection : void 0;
|
|
1312
1393
|
}
|
|
1313
1394
|
function buildSdkObservedMetadata(rawHeaders) {
|
|
1314
|
-
const { headers, apiKeyFormat, platformHeaders } = sanitizeHeaders(rawHeaders);
|
|
1395
|
+
const { headers, apiKeyFormat, platformHeaders, cookieCount } = sanitizeHeaders(rawHeaders);
|
|
1315
1396
|
const flat = {};
|
|
1316
1397
|
if (rawHeaders) {
|
|
1317
1398
|
for (const name of Object.keys(rawHeaders)) {
|
|
@@ -1326,6 +1407,7 @@ function buildSdkObservedMetadata(rawHeaders) {
|
|
|
1326
1407
|
...apiKeyFormat && { apiKeyFormat },
|
|
1327
1408
|
...platformHeaders && { platformHeaders },
|
|
1328
1409
|
...connection && { connection },
|
|
1410
|
+
...cookieCount !== void 0 && { cookieCount },
|
|
1329
1411
|
schemaVersion: CAPTURE_SCHEMA_VERSION
|
|
1330
1412
|
};
|
|
1331
1413
|
}
|
|
@@ -1370,7 +1452,7 @@ async function fetchEdgeConfig(config, counterpartyId, opts) {
|
|
|
1370
1452
|
}
|
|
1371
1453
|
if (opts?.etag) headers["If-None-Match"] = opts.etag;
|
|
1372
1454
|
try {
|
|
1373
|
-
const response = await
|
|
1455
|
+
const response = await sdkFetch(
|
|
1374
1456
|
`${config.apiBaseUrl}/endpoints/${encodeURIComponent(counterpartyId)}/edge-config`,
|
|
1375
1457
|
{ method: "GET", headers, signal: AbortSignal.timeout(opts?.timeoutMs ?? 5e3) }
|
|
1376
1458
|
);
|
|
@@ -1496,12 +1578,43 @@ var PLATFORM_AGENT_SIGNATURES = [
|
|
|
1496
1578
|
uaPatterns: [/perplexity/i],
|
|
1497
1579
|
agentCardPatterns: [/perplexity\.ai/i],
|
|
1498
1580
|
apiKeyFormatPatterns: [/^pplx-/i]
|
|
1581
|
+
},
|
|
1582
|
+
// Agentic browsers — versioned-token patterns ONLY (a stock Chrome UA must
|
|
1583
|
+
// never match). Distinct vendor keys (not 'chatgpt'/'perplexity'): the
|
|
1584
|
+
// browser product is a different surface than the assistant service, and
|
|
1585
|
+
// the DB registry mirrors one row per vendor. These sit after the primary
|
|
1586
|
+
// vendor entries so a UA carrying an explicit vendor token keeps its
|
|
1587
|
+
// primary attribution; a browser-shaped UA with the agentic token is
|
|
1588
|
+
// caught here BEFORE classify's human tier, which is what makes these
|
|
1589
|
+
// visits observable at all. Token drift is real (some builds mimic Chrome
|
|
1590
|
+
// outright) — re-verify against captured events when a new browser ships,
|
|
1591
|
+
// and check `sec-ch-ua` platformHeaders for the real brand.
|
|
1592
|
+
{
|
|
1593
|
+
vendor: "chatgpt-atlas",
|
|
1594
|
+
displayName: "ChatGPT Atlas browser",
|
|
1595
|
+
uaPatterns: [/\batlas\/\d/i],
|
|
1596
|
+
headerPatterns: [/^sec-ch-ua: .*atlas/i]
|
|
1597
|
+
},
|
|
1598
|
+
{
|
|
1599
|
+
vendor: "perplexity-comet",
|
|
1600
|
+
displayName: "Perplexity Comet browser",
|
|
1601
|
+
uaPatterns: [/\bcomet\/\d/i],
|
|
1602
|
+
headerPatterns: [/^sec-ch-ua: .*comet/i]
|
|
1603
|
+
},
|
|
1604
|
+
// Our own SDK self-identifies (sdkFetch stamps this UA) — first-party
|
|
1605
|
+
// traffic must never pollute the unknown/anonymous pool it exists to audit.
|
|
1606
|
+
{
|
|
1607
|
+
vendor: "astrasync-sdk",
|
|
1608
|
+
displayName: "AstraSync SDK",
|
|
1609
|
+
uaPatterns: [/^astrasync-sdk\//i]
|
|
1499
1610
|
}
|
|
1500
1611
|
];
|
|
1501
1612
|
function matchPlatformSignature(signatures, input) {
|
|
1502
1613
|
const ua = input.userAgent ?? "";
|
|
1503
1614
|
const cardUrl = input.agentCardUrl ?? "";
|
|
1504
1615
|
const apiKeyFormat = input.apiKeyFormat ?? "";
|
|
1616
|
+
const ja4 = input.ja4 ?? "";
|
|
1617
|
+
const asn = input.asn ?? "";
|
|
1505
1618
|
const headerLines = [];
|
|
1506
1619
|
if (input.platformHeaders) {
|
|
1507
1620
|
for (const [name, value] of Object.entries(input.platformHeaders)) {
|
|
@@ -1513,7 +1626,9 @@ function matchPlatformSignature(signatures, input) {
|
|
|
1513
1626
|
const cardMatch = sig.agentCardPatterns?.some((p) => p.test(cardUrl));
|
|
1514
1627
|
const keyMatch = apiKeyFormat.length > 0 && sig.apiKeyFormatPatterns?.some((p) => p.test(apiKeyFormat));
|
|
1515
1628
|
const headerMatch = headerLines.length > 0 && sig.headerPatterns?.some((p) => headerLines.some((line) => p.test(line)));
|
|
1516
|
-
|
|
1629
|
+
const ja4Match = ja4.length > 0 && sig.ja4Patterns?.some((p) => p.test(ja4));
|
|
1630
|
+
const asnMatch = asn.length > 0 && sig.asnList?.includes(asn);
|
|
1631
|
+
if (uaMatch || cardMatch || keyMatch || headerMatch || ja4Match || asnMatch) {
|
|
1517
1632
|
const uaBase = ua.split(/[\s/]/)[0]?.toLowerCase() ?? "";
|
|
1518
1633
|
const cardHost = (() => {
|
|
1519
1634
|
try {
|
|
@@ -1522,7 +1637,7 @@ function matchPlatformSignature(signatures, input) {
|
|
|
1522
1637
|
return "";
|
|
1523
1638
|
}
|
|
1524
1639
|
})();
|
|
1525
|
-
const keyBasis = uaBase || cardHost || apiKeyFormat || "unknown";
|
|
1640
|
+
const keyBasis = uaBase || cardHost || apiKeyFormat || ja4 || asn && `asn${asn}` || "unknown";
|
|
1526
1641
|
return {
|
|
1527
1642
|
vendor: sig.vendor,
|
|
1528
1643
|
fingerprintKey: `${sig.vendor}:${keyBasis}`,
|
|
@@ -1757,6 +1872,8 @@ function createMiddleware(options) {
|
|
|
1757
1872
|
failOnError = "open",
|
|
1758
1873
|
caseInsensitiveRouteMatch = false,
|
|
1759
1874
|
anonymousBeacon = true,
|
|
1875
|
+
trustVerifiedHop = false,
|
|
1876
|
+
verifiedHopMaxAgeMs,
|
|
1760
1877
|
...config
|
|
1761
1878
|
} = options;
|
|
1762
1879
|
if (config.apiBaseUrl) {
|
|
@@ -1849,6 +1966,16 @@ function createMiddleware(options) {
|
|
|
1849
1966
|
beaconPassThrough?.(req);
|
|
1850
1967
|
return next();
|
|
1851
1968
|
}
|
|
1969
|
+
if (trustVerifiedHop && credentials.astraId) {
|
|
1970
|
+
const hopRaw = req.headers[MCP_VERIFIED_HOP_HEADER.toLowerCase()];
|
|
1971
|
+
const hopValue = typeof hopRaw === "string" ? hopRaw : Array.isArray(hopRaw) ? hopRaw[0] : void 0;
|
|
1972
|
+
const marker = parseVerifiedHop(hopValue);
|
|
1973
|
+
if (isVerifiedHopValidFor(marker, credentials.astraId, {
|
|
1974
|
+
...verifiedHopMaxAgeMs !== void 0 ? { maxAgeMs: verifiedHopMaxAgeMs } : {}
|
|
1975
|
+
})) {
|
|
1976
|
+
return next();
|
|
1977
|
+
}
|
|
1978
|
+
}
|
|
1852
1979
|
const pdlssPair = resolveRequestPdlss(
|
|
1853
1980
|
req,
|
|
1854
1981
|
routeConfig,
|
|
@@ -2304,6 +2431,8 @@ function createMiddleware2(options) {
|
|
|
2304
2431
|
enableRuntimeChallenge = true,
|
|
2305
2432
|
routesRefreshMs = DEFAULT_ROUTES_REFRESH_MS2,
|
|
2306
2433
|
anonymousBeacon = true,
|
|
2434
|
+
trustVerifiedHop = false,
|
|
2435
|
+
verifiedHopMaxAgeMs,
|
|
2307
2436
|
...config
|
|
2308
2437
|
} = options;
|
|
2309
2438
|
const interstitialEnabled = showInterstitial ?? showCommerceShield ?? true;
|
|
@@ -2366,6 +2495,14 @@ function createMiddleware2(options) {
|
|
|
2366
2495
|
return NextResponse.next();
|
|
2367
2496
|
}
|
|
2368
2497
|
const credentials = extractCredentialsFromNextRequest(request);
|
|
2498
|
+
if (trustVerifiedHop && credentials.astraId) {
|
|
2499
|
+
const marker = parseVerifiedHop(request.headers.get(MCP_VERIFIED_HOP_HEADER) ?? void 0);
|
|
2500
|
+
if (isVerifiedHopValidFor(marker, credentials.astraId, {
|
|
2501
|
+
...verifiedHopMaxAgeMs !== void 0 ? { maxAgeMs: verifiedHopMaxAgeMs } : {}
|
|
2502
|
+
})) {
|
|
2503
|
+
return NextResponse.next();
|
|
2504
|
+
}
|
|
2505
|
+
}
|
|
2369
2506
|
const counterpartyUrl = config.counterpartyUrl || request.nextUrl.origin;
|
|
2370
2507
|
const pdlssPair = resolveNextPdlss(request, routeConfig);
|
|
2371
2508
|
const purpose = pdlssPair.purpose;
|
|
@@ -3331,7 +3468,7 @@ async function fetchUCPManifest(manifestUrl, options = {}) {
|
|
|
3331
3468
|
const controller = new AbortController();
|
|
3332
3469
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
3333
3470
|
try {
|
|
3334
|
-
const res = await
|
|
3471
|
+
const res = await sdkFetch(manifestUrl, { signal: controller.signal });
|
|
3335
3472
|
if (!res.ok) return null;
|
|
3336
3473
|
return await res.json();
|
|
3337
3474
|
} catch {
|
|
@@ -5257,32 +5394,6 @@ function extractCredentialsFromProtocol(protocol, context) {
|
|
|
5257
5394
|
}
|
|
5258
5395
|
|
|
5259
5396
|
// src/transport/mcp-server.ts
|
|
5260
|
-
var MCP_VERIFIED_HOP_HEADER = "X-Astra-Verified-Hop";
|
|
5261
|
-
var MCP_VERIFIED_HOP_MAX_AGE_MS = 6e4;
|
|
5262
|
-
function serializeVerifiedHop(marker) {
|
|
5263
|
-
return `${marker.astraId};${marker.sessionId ?? ""};${marker.checkedAt}`;
|
|
5264
|
-
}
|
|
5265
|
-
function parseVerifiedHop(value) {
|
|
5266
|
-
if (!value) return null;
|
|
5267
|
-
const parts = value.split(";");
|
|
5268
|
-
if (parts.length !== 3) return null;
|
|
5269
|
-
const [astraId, sessionId, checkedAtRaw] = parts;
|
|
5270
|
-
if (!astraId) return null;
|
|
5271
|
-
const checkedAt = Number(checkedAtRaw);
|
|
5272
|
-
if (!Number.isFinite(checkedAt) || checkedAt <= 0) return null;
|
|
5273
|
-
return {
|
|
5274
|
-
astraId,
|
|
5275
|
-
...sessionId ? { sessionId } : {},
|
|
5276
|
-
checkedAt
|
|
5277
|
-
};
|
|
5278
|
-
}
|
|
5279
|
-
function isVerifiedHopValidFor(marker, expectedAstraId, opts = {}) {
|
|
5280
|
-
if (!marker) return false;
|
|
5281
|
-
if (marker.astraId !== expectedAstraId) return false;
|
|
5282
|
-
const maxAge = opts.maxAgeMs ?? MCP_VERIFIED_HOP_MAX_AGE_MS;
|
|
5283
|
-
const now = opts.now ?? Date.now();
|
|
5284
|
-
return now - marker.checkedAt <= maxAge && now >= marker.checkedAt;
|
|
5285
|
-
}
|
|
5286
5397
|
function parseMcpJsonRpc(body) {
|
|
5287
5398
|
if (!body || typeof body !== "object" || Array.isArray(body)) return null;
|
|
5288
5399
|
const obj = body;
|
|
@@ -5905,7 +6016,7 @@ var AstraSync = class {
|
|
|
5905
6016
|
*/
|
|
5906
6017
|
async pollRegistration(requestId) {
|
|
5907
6018
|
const url = `${this.baseUrl}/api/agents/request-registration/${requestId}`;
|
|
5908
|
-
const res = await
|
|
6019
|
+
const res = await sdkFetch(url, { headers: { Accept: "application/json" } });
|
|
5909
6020
|
if (!res.ok) {
|
|
5910
6021
|
const errBody = await res.json().catch(() => ({}));
|
|
5911
6022
|
throw new AstraSyncError(
|
|
@@ -5962,7 +6073,7 @@ var AstraSync = class {
|
|
|
5962
6073
|
* Check API health.
|
|
5963
6074
|
*/
|
|
5964
6075
|
async health() {
|
|
5965
|
-
const res = await
|
|
6076
|
+
const res = await sdkFetch(`${this.baseUrl}/api/health/`);
|
|
5966
6077
|
if (!res.ok) {
|
|
5967
6078
|
throw new AstraSyncError(`Health check failed: ${res.status}`, res.status);
|
|
5968
6079
|
}
|
|
@@ -5989,7 +6100,7 @@ var AstraSync = class {
|
|
|
5989
6100
|
const signature = await this.signRequest(method, endpoint, body || {});
|
|
5990
6101
|
headers["X-AstraSync-Signature"] = signature;
|
|
5991
6102
|
}
|
|
5992
|
-
const res = await
|
|
6103
|
+
const res = await sdkFetch(url, {
|
|
5993
6104
|
method,
|
|
5994
6105
|
headers,
|
|
5995
6106
|
...body ? { body: JSON.stringify(body) } : {}
|
|
@@ -6014,7 +6125,7 @@ var AstraSync = class {
|
|
|
6014
6125
|
if (this.cachedJwt && this.jwtExpiresAt && Date.now() < this.jwtExpiresAt) {
|
|
6015
6126
|
return this.cachedJwt;
|
|
6016
6127
|
}
|
|
6017
|
-
const res = await
|
|
6128
|
+
const res = await sdkFetch(`${this.baseUrl}/api/auth/login`, {
|
|
6018
6129
|
method: "POST",
|
|
6019
6130
|
headers: { "Content-Type": "application/json" },
|
|
6020
6131
|
body: JSON.stringify({ email: this.email, password: this.password })
|
|
@@ -6157,7 +6268,7 @@ var AgentClient = class _AgentClient {
|
|
|
6157
6268
|
const url = `${this.apiBaseUrl.replace(/\/+$/, "")}/api/agents/${encodeURIComponent(
|
|
6158
6269
|
this.credentials.agentId
|
|
6159
6270
|
)}/ownership`;
|
|
6160
|
-
const resp = await
|
|
6271
|
+
const resp = await sdkFetch(url, {
|
|
6161
6272
|
method: "GET",
|
|
6162
6273
|
headers: {
|
|
6163
6274
|
Authorization: `Bearer ${this.apiKey}`,
|
|
@@ -6195,7 +6306,7 @@ var AgentClient = class _AgentClient {
|
|
|
6195
6306
|
}
|
|
6196
6307
|
}
|
|
6197
6308
|
const enrichedHeaders = setHttpHeaders(existingHeaders, creds);
|
|
6198
|
-
return
|
|
6309
|
+
return sdkFetch(url, {
|
|
6199
6310
|
...fetchOptions,
|
|
6200
6311
|
headers: enrichedHeaders
|
|
6201
6312
|
});
|
|
@@ -6384,7 +6495,7 @@ async function recordDecision2(config, params) {
|
|
|
6384
6495
|
if (config.customHeaders) {
|
|
6385
6496
|
Object.assign(headers, config.customHeaders);
|
|
6386
6497
|
}
|
|
6387
|
-
const response = await
|
|
6498
|
+
const response = await sdkFetch(url, {
|
|
6388
6499
|
method: "POST",
|
|
6389
6500
|
headers,
|
|
6390
6501
|
body: JSON.stringify(body)
|
|
@@ -6401,6 +6512,51 @@ async function recordDecision2(config, params) {
|
|
|
6401
6512
|
blockchainTxHash: result.blockchainTxHash
|
|
6402
6513
|
};
|
|
6403
6514
|
}
|
|
6515
|
+
|
|
6516
|
+
// src/runtime-signatures.ts
|
|
6517
|
+
var RUNTIME_SIGNATURES = [
|
|
6518
|
+
{
|
|
6519
|
+
runtime: "browser-automation",
|
|
6520
|
+
displayName: "Browser automation",
|
|
6521
|
+
uaPatterns: [/headless/i, /phantomjs/i, /selenium/i, /playwright/i, /puppeteer/i]
|
|
6522
|
+
},
|
|
6523
|
+
{
|
|
6524
|
+
runtime: "nodejs",
|
|
6525
|
+
displayName: "Node.js runtime",
|
|
6526
|
+
// `node` exactly is the global-fetch default UA; the token forms cover
|
|
6527
|
+
// the common Node HTTP libraries.
|
|
6528
|
+
uaPatterns: [/^node$/i, /^undici/i, /node-fetch/i, /^axios/i, /^got\b/i, /needle/i]
|
|
6529
|
+
},
|
|
6530
|
+
{
|
|
6531
|
+
runtime: "python",
|
|
6532
|
+
displayName: "Python runtime",
|
|
6533
|
+
uaPatterns: [/python-requests/i, /python-urllib/i, /^python\b/i, /httpx/i, /aiohttp/i]
|
|
6534
|
+
},
|
|
6535
|
+
{ runtime: "go", displayName: "Go runtime", uaPatterns: [/go-http-client/i] },
|
|
6536
|
+
{
|
|
6537
|
+
runtime: "java",
|
|
6538
|
+
displayName: "Java runtime",
|
|
6539
|
+
uaPatterns: [/^java\//i, /okhttp/i, /apache-httpclient/i]
|
|
6540
|
+
},
|
|
6541
|
+
{ runtime: "curl", displayName: "curl", uaPatterns: [/^curl\//i] },
|
|
6542
|
+
{ runtime: "wget", displayName: "wget", uaPatterns: [/^wget\//i] },
|
|
6543
|
+
{ runtime: "ruby", displayName: "Ruby runtime", uaPatterns: [/^ruby\b/i, /faraday/i] },
|
|
6544
|
+
{
|
|
6545
|
+
runtime: "php",
|
|
6546
|
+
displayName: "PHP runtime",
|
|
6547
|
+
uaPatterns: [/^php\b/i, /guzzlehttp/i, /^symfony/i]
|
|
6548
|
+
},
|
|
6549
|
+
{ runtime: "dotnet", displayName: ".NET runtime", uaPatterns: [/^dotnet/i, /httpclient/i] },
|
|
6550
|
+
{ runtime: "dart", displayName: "Dart runtime", uaPatterns: [/^dart/i] }
|
|
6551
|
+
];
|
|
6552
|
+
function detectRuntime(userAgent) {
|
|
6553
|
+
if (!userAgent) return void 0;
|
|
6554
|
+
const ua = userAgent.trim();
|
|
6555
|
+
for (const sig of RUNTIME_SIGNATURES) {
|
|
6556
|
+
if (sig.uaPatterns.some((p) => p.test(ua))) return sig.displayName;
|
|
6557
|
+
}
|
|
6558
|
+
return void 0;
|
|
6559
|
+
}
|
|
6404
6560
|
export {
|
|
6405
6561
|
ACCESS_LEVEL_DESCRIPTIONS,
|
|
6406
6562
|
ACCESS_LEVEL_HIERARCHY,
|
|
@@ -6417,9 +6573,11 @@ export {
|
|
|
6417
6573
|
MAX_HEADER_VALUE_BYTES,
|
|
6418
6574
|
MAX_TOTAL_BYTES,
|
|
6419
6575
|
PLATFORM_AGENT_SIGNATURES,
|
|
6576
|
+
RUNTIME_SIGNATURES,
|
|
6420
6577
|
RegistrationDeniedError,
|
|
6421
6578
|
RegistrationExpiredError,
|
|
6422
6579
|
RegistrationTimeoutError,
|
|
6580
|
+
SDK_USER_AGENT,
|
|
6423
6581
|
TRUST_LEVEL_RANGES,
|
|
6424
6582
|
SDK_VERSION as VERSION,
|
|
6425
6583
|
_resetEdgeConfigCache,
|
|
@@ -6433,6 +6591,7 @@ export {
|
|
|
6433
6591
|
degradeToObserve,
|
|
6434
6592
|
deriveConnectionFromHeaders,
|
|
6435
6593
|
detectPlatformFingerprint,
|
|
6594
|
+
detectRuntime,
|
|
6436
6595
|
determineAccessLevel,
|
|
6437
6596
|
express_exports as express,
|
|
6438
6597
|
extractApiKeyFormat,
|
|
@@ -6461,6 +6620,7 @@ export {
|
|
|
6461
6620
|
reportUnregisteredAttempt,
|
|
6462
6621
|
sanitizeHeaders,
|
|
6463
6622
|
sdk_exports as sdk,
|
|
6623
|
+
sdkFetch,
|
|
6464
6624
|
setMcpMeta,
|
|
6465
6625
|
transport_exports as transport,
|
|
6466
6626
|
verify
|