@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/edge-config.ts
2
23
  var DEFAULT_EDGE_CONFIG = Object.freeze({
3
24
  version: 1,
@@ -57,7 +78,13 @@ var PLATFORM_HEADER_NAMES = /* @__PURE__ */ new Set([
57
78
  "x-agent-card-url",
58
79
  "x-agent-provider"
59
80
  ]);
60
- var PLATFORM_HEADER_PREFIXES = ["x-goog-", "x-stainless-", "x-anthropic-", "x-openai-"];
81
+ var PLATFORM_HEADER_PREFIXES = [
82
+ "x-goog-",
83
+ "x-stainless-",
84
+ "x-anthropic-",
85
+ "x-openai-",
86
+ "sec-ch-"
87
+ ];
61
88
  var KEY_FORMAT_PREFIXES = [
62
89
  "sk-ant-api03",
63
90
  "sk-ant-api",
@@ -129,6 +156,7 @@ function sanitizeHeaders(raw) {
129
156
  const headers = {};
130
157
  const platformHeaders = {};
131
158
  let apiKeyFormat;
159
+ let cookieCount;
132
160
  let totalBytes = 0;
133
161
  let count = 0;
134
162
  if (!raw) return { headers };
@@ -143,6 +171,9 @@ function sanitizeHeaders(raw) {
143
171
  if (fmt && !apiKeyFormat) apiKeyFormat = fmt;
144
172
  continue;
145
173
  }
174
+ if (name === "cookie") {
175
+ cookieCount = joined.split(";").filter((s) => s.trim().length > 0).length;
176
+ }
146
177
  if (isSecretHeader(name)) continue;
147
178
  let value = joined;
148
179
  if (byteLength(value) > MAX_HEADER_VALUE_BYTES) {
@@ -158,6 +189,7 @@ function sanitizeHeaders(raw) {
158
189
  const result = { headers };
159
190
  if (apiKeyFormat) result.apiKeyFormat = apiKeyFormat;
160
191
  if (Object.keys(platformHeaders).length > 0) result.platformHeaders = platformHeaders;
192
+ if (cookieCount !== void 0) result.cookieCount = cookieCount;
161
193
  return result;
162
194
  }
163
195
 
@@ -205,12 +237,43 @@ var PLATFORM_AGENT_SIGNATURES = [
205
237
  uaPatterns: [/perplexity/i],
206
238
  agentCardPatterns: [/perplexity\.ai/i],
207
239
  apiKeyFormatPatterns: [/^pplx-/i]
240
+ },
241
+ // Agentic browsers — versioned-token patterns ONLY (a stock Chrome UA must
242
+ // never match). Distinct vendor keys (not 'chatgpt'/'perplexity'): the
243
+ // browser product is a different surface than the assistant service, and
244
+ // the DB registry mirrors one row per vendor. These sit after the primary
245
+ // vendor entries so a UA carrying an explicit vendor token keeps its
246
+ // primary attribution; a browser-shaped UA with the agentic token is
247
+ // caught here BEFORE classify's human tier, which is what makes these
248
+ // visits observable at all. Token drift is real (some builds mimic Chrome
249
+ // outright) — re-verify against captured events when a new browser ships,
250
+ // and check `sec-ch-ua` platformHeaders for the real brand.
251
+ {
252
+ vendor: "chatgpt-atlas",
253
+ displayName: "ChatGPT Atlas browser",
254
+ uaPatterns: [/\batlas\/\d/i],
255
+ headerPatterns: [/^sec-ch-ua: .*atlas/i]
256
+ },
257
+ {
258
+ vendor: "perplexity-comet",
259
+ displayName: "Perplexity Comet browser",
260
+ uaPatterns: [/\bcomet\/\d/i],
261
+ headerPatterns: [/^sec-ch-ua: .*comet/i]
262
+ },
263
+ // Our own SDK self-identifies (sdkFetch stamps this UA) — first-party
264
+ // traffic must never pollute the unknown/anonymous pool it exists to audit.
265
+ {
266
+ vendor: "astrasync-sdk",
267
+ displayName: "AstraSync SDK",
268
+ uaPatterns: [/^astrasync-sdk\//i]
208
269
  }
209
270
  ];
210
271
  function matchPlatformSignature(signatures, input) {
211
272
  const ua = input.userAgent ?? "";
212
273
  const cardUrl = input.agentCardUrl ?? "";
213
274
  const apiKeyFormat = input.apiKeyFormat ?? "";
275
+ const ja4 = input.ja4 ?? "";
276
+ const asn = input.asn ?? "";
214
277
  const headerLines = [];
215
278
  if (input.platformHeaders) {
216
279
  for (const [name, value] of Object.entries(input.platformHeaders)) {
@@ -222,7 +285,9 @@ function matchPlatformSignature(signatures, input) {
222
285
  const cardMatch = sig.agentCardPatterns?.some((p) => p.test(cardUrl));
223
286
  const keyMatch = apiKeyFormat.length > 0 && sig.apiKeyFormatPatterns?.some((p) => p.test(apiKeyFormat));
224
287
  const headerMatch = headerLines.length > 0 && sig.headerPatterns?.some((p) => headerLines.some((line) => p.test(line)));
225
- if (uaMatch || cardMatch || keyMatch || headerMatch) {
288
+ const ja4Match = ja4.length > 0 && sig.ja4Patterns?.some((p) => p.test(ja4));
289
+ const asnMatch = asn.length > 0 && sig.asnList?.includes(asn);
290
+ if (uaMatch || cardMatch || keyMatch || headerMatch || ja4Match || asnMatch) {
226
291
  const uaBase = ua.split(/[\s/]/)[0]?.toLowerCase() ?? "";
227
292
  const cardHost = (() => {
228
293
  try {
@@ -231,7 +296,7 @@ function matchPlatformSignature(signatures, input) {
231
296
  return "";
232
297
  }
233
298
  })();
234
- const keyBasis = uaBase || cardHost || apiKeyFormat || "unknown";
299
+ const keyBasis = uaBase || cardHost || apiKeyFormat || ja4 || asn && `asn${asn}` || "unknown";
235
300
  return {
236
301
  vendor: sig.vendor,
237
302
  fingerprintKey: `${sig.vendor}:${keyBasis}`,
@@ -253,9 +318,6 @@ function getTrustLevel(score) {
253
318
  return "BRONZE";
254
319
  }
255
320
 
256
- // src/version.ts
257
- var SDK_VERSION = "4.1.0";
258
-
259
321
  // src/well-known.ts
260
322
  var CACHE_TTL_MS = 60 * 60 * 1e3;
261
323
  var cache = /* @__PURE__ */ new Map();
@@ -284,7 +346,7 @@ async function performInitCheck(apiBaseUrl, debug, strictInit, apiKey) {
284
346
  initCheckPerformed = true;
285
347
  try {
286
348
  const probeUrl = `${apiBaseUrl}/agents/verify-access`;
287
- const response = await fetch(probeUrl, { method: "HEAD" });
349
+ const response = await sdkFetch(probeUrl, { method: "HEAD" });
288
350
  const contentType = response.headers.get("content-type") ?? "";
289
351
  if (contentType.startsWith("text/html")) {
290
352
  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).`;
@@ -298,7 +360,7 @@ async function performInitCheck(apiBaseUrl, debug, strictInit, apiKey) {
298
360
  );
299
361
  }
300
362
  if (apiKey) {
301
- const selfCheck = await fetch(`${apiBaseUrl}/agents/verify-access/self-check`, {
363
+ const selfCheck = await sdkFetch(`${apiBaseUrl}/agents/verify-access/self-check`, {
302
364
  headers: { Authorization: `Bearer ${apiKey}` }
303
365
  });
304
366
  if (selfCheck.status === 401) {
@@ -545,7 +607,7 @@ async function callVerifyAccessAPI(config, request) {
545
607
  headers["X-API-Key"] = config.apiKey;
546
608
  }
547
609
  try {
548
- const response = await fetch(`${config.apiBaseUrl}/agents/verify-access`, {
610
+ const response = await sdkFetch(`${config.apiBaseUrl}/agents/verify-access`, {
549
611
  method: "POST",
550
612
  headers,
551
613
  body: JSON.stringify(body),
@@ -727,12 +789,15 @@ async function verify(config, request, options) {
727
789
  const verificationContext = apiResponse.verificationContext;
728
790
  const accessLevel = apiResponse.access?.accessLevel ?? "standard";
729
791
  const result = {
730
- // Round-18 G4: backend allowed access. Identity is verified (we resolved
731
- // the caller to an agent) and policy passed all gates. Read idVerified
732
- // from verificationContext for symmetry with the deny branch; default true
733
- // on success path since `access.allowed === true` implies identity was
734
- // resolvable (anonymous-allow paths flow through createGuidanceResponse).
735
- identityVerified: apiResponse.verificationContext?.idVerified !== false,
792
+ // 4.5.0 (probe F1): read idVerified from verificationContext with the
793
+ // SAME absent⇒false default as the deny branch. The previous `!== false`
794
+ // default manufactured `identityVerified: true` for anonymous
795
+ // allow_full/allow_partial/audit responses (which allow access but omit
796
+ // verificationContext entirely) — the "anonymous caller labelled
797
+ // verified" defect. Every authenticated success response has carried
798
+ // `verificationContext.idVerified: true` since v2.2.4, so a truth claim
799
+ // this load-bearing must never be defaulted on.
800
+ identityVerified: apiResponse.verificationContext?.idVerified === true,
736
801
  policyAllowed: true,
737
802
  accessLevel,
738
803
  agent,
@@ -790,7 +855,7 @@ async function reportUnregisteredAttempt(config, data) {
790
855
  ...counterpartyUrl && { counterpartyUrl },
791
856
  ...config.counterpartyId && { counterpartyId: config.counterpartyId }
792
857
  };
793
- await fetch(`${apiBaseUrl}/verification-activity/unregistered-attempt`, {
858
+ await sdkFetch(`${apiBaseUrl}/verification-activity/unregistered-attempt`, {
794
859
  method: "POST",
795
860
  headers: { "Content-Type": "application/json" },
796
861
  body: JSON.stringify(body)
@@ -807,16 +872,39 @@ function requestUrl(req, host) {
807
872
  return `https://${host}${req.uri}${qs}`;
808
873
  }
809
874
  function buildObservedMetadata(req) {
810
- const { headers, apiKeyFormat, platformHeaders } = sanitizeHeaders(req.headers);
875
+ const { headers, apiKeyFormat, platformHeaders, cookieCount } = sanitizeHeaders(req.headers);
811
876
  const connection = req.connection ?? {};
877
+ const queryKeys = extractQueryKeys(req.querystring);
812
878
  return {
813
879
  headers,
814
880
  ...apiKeyFormat && { apiKeyFormat },
815
881
  ...platformHeaders && { platformHeaders },
816
882
  ...Object.keys(connection).length > 0 && { connection },
883
+ ...cookieCount !== void 0 && { cookieCount },
884
+ ...queryKeys && { queryKeys },
817
885
  schemaVersion: CAPTURE_SCHEMA_VERSION
818
886
  };
819
887
  }
888
+ var MAX_QUERY_KEYS = 32;
889
+ var MAX_QUERY_KEY_CHARS = 64;
890
+ function extractQueryKeys(querystring) {
891
+ if (!querystring) return void 0;
892
+ let params;
893
+ try {
894
+ params = new URLSearchParams(querystring);
895
+ } catch {
896
+ return void 0;
897
+ }
898
+ const keys = [];
899
+ for (const key of params.keys()) {
900
+ const normalized = key.toLowerCase().slice(0, MAX_QUERY_KEY_CHARS);
901
+ if (normalized && !keys.includes(normalized)) {
902
+ keys.push(normalized);
903
+ if (keys.length >= MAX_QUERY_KEYS) break;
904
+ }
905
+ }
906
+ return keys.length > 0 ? keys : void 0;
907
+ }
820
908
 
821
909
  // src/edge-core/detect.ts
822
910
  var SD_JWT_PATTERN = /^eyJ[\w-]+\.[\w-]*\.[\w-]+~/;
@@ -960,6 +1048,7 @@ function detectCommerce(req, host) {
960
1048
 
961
1049
  // src/edge-core/classify.ts
962
1050
  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;
1051
+ var SELF_UA_PATTERN = /^astrasync-sdk\//i;
963
1052
  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;
964
1053
  var BROWSER_UA_PATTERN = /mozilla\/\d/i;
965
1054
  function resolvePolicy(config, path) {
@@ -976,6 +1065,9 @@ function resolvePolicy(config, path) {
976
1065
  }
977
1066
  function classify(req, host, depth) {
978
1067
  const userAgent = headerValue(req, "user-agent") ?? "";
1068
+ if (SELF_UA_PATTERN.test(userAgent)) {
1069
+ return { tier: "self" };
1070
+ }
979
1071
  const credentials = extractCredentials(req.headers, {});
980
1072
  if (credentials.astraId || credentials.apiKey || credentials.jwt) {
981
1073
  return { tier: "verified", protocol: "x-astra" };
@@ -1006,7 +1098,7 @@ function classify(req, host, depth) {
1006
1098
  }
1007
1099
  return { tier: "human" };
1008
1100
  }
1009
- var _patterns = { STATIC_EXTENSIONS, BOT_UA_PATTERN, BROWSER_UA_PATTERN };
1101
+ var _patterns = { STATIC_EXTENSIONS, BOT_UA_PATTERN, BROWSER_UA_PATTERN, SELF_UA_PATTERN };
1010
1102
 
1011
1103
  // src/edge-core/messaging.ts
1012
1104
  var PROTOCOL_MESSAGES = {
@@ -1046,6 +1138,20 @@ function buildDenyBody(outcome, apiBaseUrl) {
1046
1138
  }
1047
1139
  };
1048
1140
  }
1141
+ function buildUnavailableBody(outcome) {
1142
+ return {
1143
+ success: false,
1144
+ error: {
1145
+ code: "VERIFICATION_UNAVAILABLE",
1146
+ message: "Agent verification is temporarily unavailable and this path enforces verification. Your identity was not evaluated and no denial was recorded \u2014 retry with exponential backoff.",
1147
+ failures: outcome.reasons,
1148
+ ...outcome.correlationId && { correlationId: outcome.correlationId },
1149
+ guidance: {
1150
+ message: "Retry the request with exponential backoff; the condition is transient."
1151
+ }
1152
+ }
1153
+ };
1154
+ }
1049
1155
  function buildManualReviewBody(outcome) {
1050
1156
  return {
1051
1157
  success: false,
@@ -1088,6 +1194,14 @@ function applyOutcome(options, outcome, policy) {
1088
1194
  }
1089
1195
  switch (outcome.decision) {
1090
1196
  case "deny":
1197
+ if (outcome.unavailable) {
1198
+ return {
1199
+ action: "respond",
1200
+ status: 503,
1201
+ headers: { "Retry-After": "30" },
1202
+ bodyJson: buildUnavailableBody(outcome)
1203
+ };
1204
+ }
1091
1205
  return {
1092
1206
  action: "respond",
1093
1207
  status: 403,
@@ -1326,6 +1440,17 @@ function outcomeFromVerifyResult(classification, result) {
1326
1440
  };
1327
1441
  return outcome;
1328
1442
  }
1443
+ function unavailableOutcome(classification, policy, reason, correlationId) {
1444
+ const outcome = baseOutcome(classification);
1445
+ outcome.unavailable = true;
1446
+ outcome.reasons = [reason];
1447
+ if (correlationId) outcome.correlationId = correlationId;
1448
+ if (policy.mode === "enforce") outcome.decision = "deny";
1449
+ return outcome;
1450
+ }
1451
+ function isApiErrorResult(result) {
1452
+ return (result.failures ?? []).some((f) => f.dimension === "verify_access.api_error") === true && !result.misconfigured;
1453
+ }
1329
1454
  async function runTier(options, req, classification, policy, apiKey, sampling) {
1330
1455
  const budgets = options.budgets ?? {};
1331
1456
  const beaconMs = budgets.beaconMs ?? 300;
@@ -1352,9 +1477,19 @@ async function runTier(options, req, classification, policy, apiKey, sampling) {
1352
1477
  verifyMs
1353
1478
  );
1354
1479
  if (!result) {
1355
- const outcome = baseOutcome(classification);
1356
- outcome.reasons = ["verify-access unreachable within budget \u2014 fail-open"];
1357
- return outcome;
1480
+ return unavailableOutcome(
1481
+ classification,
1482
+ policy,
1483
+ "verify-access unreachable within budget"
1484
+ );
1485
+ }
1486
+ if (isApiErrorResult(result)) {
1487
+ return unavailableOutcome(
1488
+ classification,
1489
+ policy,
1490
+ result.denialReasons?.[0] ?? "verify-access api error",
1491
+ result.correlationId
1492
+ );
1358
1493
  }
1359
1494
  return outcomeFromVerifyResult(classification, result);
1360
1495
  }
@@ -1365,9 +1500,19 @@ async function runTier(options, req, classification, policy, apiKey, sampling) {
1365
1500
  verifyMs
1366
1501
  );
1367
1502
  if (!result) {
1368
- const outcome2 = baseOutcome(classification);
1369
- outcome2.reasons = ["verify-access unreachable within budget \u2014 fail-open"];
1370
- return outcome2;
1503
+ return unavailableOutcome(
1504
+ classification,
1505
+ policy,
1506
+ "verify-access unreachable within budget"
1507
+ );
1508
+ }
1509
+ if (isApiErrorResult(result)) {
1510
+ return unavailableOutcome(
1511
+ classification,
1512
+ policy,
1513
+ result.denialReasons?.[0] ?? "verify-access api error",
1514
+ result.correlationId
1515
+ );
1371
1516
  }
1372
1517
  return outcomeFromVerifyResult(classification, result);
1373
1518
  }
@@ -1409,7 +1554,7 @@ async function evaluateEdgeRequest(options, req, config, apiKey) {
1409
1554
  if (policy.skip) return { policy, application: passUntouched() };
1410
1555
  const host = headerValue(req, "x-forwarded-host") ?? headerValue(req, "host") ?? new URL(options.counterpartyUrl).host;
1411
1556
  const classification = classify(req, host, policy.depth);
1412
- if (classification.tier === "human") {
1557
+ if (classification.tier === "human" || classification.tier === "self") {
1413
1558
  return { policy, classification, application: passUntouched() };
1414
1559
  }
1415
1560
  const outcome = await runTier(options, req, classification, policy, apiKey, config.sampling);