@astrasyncai/adapter-lambda 1.0.0 → 1.1.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/dist/index.js CHANGED
@@ -57,6 +57,7 @@ __export(src_exports, {
57
57
  module.exports = __toCommonJS(src_exports);
58
58
 
59
59
  // src/cf.ts
60
+ var import_metadata_capture = require("@astrasyncai/verification-gateway/metadata-capture");
60
61
  function getRequest(event) {
61
62
  return event.Records[0].cf.request;
62
63
  }
@@ -77,6 +78,55 @@ function decodeBody(request) {
77
78
  const text = body.encoding === "base64" ? Buffer.from(body.data, "base64").toString("utf8") : body.data;
78
79
  return { text, truncated: body.inputTruncated === true };
79
80
  }
81
+ function collectConnectionMetadata(request) {
82
+ const out = {};
83
+ const put = (key, headerName, transform) => {
84
+ const v = getHeader(request, headerName);
85
+ if (v) out[key] = transform ? transform(v) : v;
86
+ };
87
+ const addr = getHeader(request, "cloudfront-viewer-address");
88
+ if (addr) out.ip = addr.replace(/:\d+$/, "");
89
+ put("country", "cloudfront-viewer-country");
90
+ put("countryName", "cloudfront-viewer-country-name");
91
+ put("region", "cloudfront-viewer-country-region");
92
+ put("city", "cloudfront-viewer-city");
93
+ put("asn", "cloudfront-viewer-asn");
94
+ put("tlsVersion", "cloudfront-viewer-tls");
95
+ put("httpVersion", "cloudfront-viewer-http-version");
96
+ const devices = [];
97
+ for (const [label, header] of [
98
+ ["desktop", "cloudfront-is-desktop-viewer"],
99
+ ["mobile", "cloudfront-is-mobile-viewer"],
100
+ ["tablet", "cloudfront-is-tablet-viewer"],
101
+ ["smarttv", "cloudfront-is-smarttv-viewer"]
102
+ ]) {
103
+ if (getHeader(request, header) === "true") devices.push(label);
104
+ }
105
+ if (devices.length) out.device = devices.join(",");
106
+ return out;
107
+ }
108
+ function buildObservedMetadata(request) {
109
+ const { headers, apiKeyFormat, platformHeaders } = (0, import_metadata_capture.sanitizeHeaders)(
110
+ flattenHeaders(request.headers)
111
+ );
112
+ const connection = collectConnectionMetadata(request);
113
+ return {
114
+ headers,
115
+ ...apiKeyFormat && { apiKeyFormat },
116
+ ...platformHeaders && { platformHeaders },
117
+ ...Object.keys(connection).length > 0 && { connection }
118
+ };
119
+ }
120
+ function normalizeOriginHost(request) {
121
+ const originDomain = request.origin?.custom?.domainName ?? request.origin?.s3?.domainName ?? void 0;
122
+ if (!originDomain) return;
123
+ const currentHost = getHeader(request, "host");
124
+ if (!currentHost || currentHost === originDomain) return;
125
+ if (!getHeader(request, "x-forwarded-host")) {
126
+ setHeader(request, "X-Forwarded-Host", currentHost);
127
+ }
128
+ setHeader(request, "Host", originDomain);
129
+ }
80
130
  function requestUrl(request, host) {
81
131
  const qs = request.querystring ? `?${request.querystring}` : "";
82
132
  return `https://${host}${request.uri}${qs}`;
@@ -151,6 +201,7 @@ function _resetConfigCaches() {
151
201
  var import_verify = require("@astrasyncai/verification-gateway/verify");
152
202
  var import_platform_signatures = require("@astrasyncai/verification-gateway/platform-signatures");
153
203
  var import_edge_config2 = require("@astrasyncai/verification-gateway/edge-config");
204
+ var import_metadata_capture2 = require("@astrasyncai/verification-gateway/metadata-capture");
154
205
 
155
206
  // src/detect.ts
156
207
  var SD_JWT_PATTERN = /^eyJ[\w-]+\.[\w-]*\.[\w-]+~/;
@@ -302,10 +353,13 @@ function resolvePolicy(config, path) {
302
353
  }
303
354
  function classify(request, host, depth) {
304
355
  const userAgent = getHeader(request, "user-agent") ?? "";
305
- const credentials = (0, import_verify.extractCredentials)(flattenHeaders(request.headers), {});
356
+ const flat = flattenHeaders(request.headers);
357
+ const credentials = (0, import_verify.extractCredentials)(flat, {});
306
358
  if (credentials.astraId || credentials.apiKey || credentials.jwt) {
307
359
  return { tier: "verified", protocol: "x-astra" };
308
360
  }
361
+ const { apiKeyFormat, platformHeaders } = (0, import_metadata_capture2.sanitizeHeaders)(flat);
362
+ const detectionInput = { userAgent, apiKeyFormat, platformHeaders };
309
363
  if (depth !== "classify") {
310
364
  const commerce = detectCommerce(request, host, decodeBody(request));
311
365
  if (commerce) {
@@ -314,11 +368,11 @@ function classify(request, host, depth) {
314
368
  protocol: commerce.protocol,
315
369
  artifacts: commerce.artifacts,
316
370
  evidence: commerce.evidence,
317
- fingerprint: (0, import_platform_signatures.detectPlatformFingerprint)({ userAgent })
371
+ fingerprint: (0, import_platform_signatures.detectPlatformFingerprint)(detectionInput)
318
372
  };
319
373
  }
320
374
  }
321
- const fingerprint = (0, import_platform_signatures.detectPlatformFingerprint)({ userAgent });
375
+ const fingerprint = (0, import_platform_signatures.detectPlatformFingerprint)(detectionInput);
322
376
  if (fingerprint) {
323
377
  return { tier: "identified", protocol: "platform-ua", fingerprint };
324
378
  }
@@ -366,7 +420,10 @@ async function emitBeacon(options, request, classification, budgetMs) {
366
420
  requestMethod: request.method,
367
421
  // 3.9.0 evidence fields (backend persists them into eventData).
368
422
  protocol: classification.protocol,
369
- evidence: classification.evidence
423
+ evidence: classification.evidence,
424
+ // Workstream D — full sanitised header + connection capture for the
425
+ // unregistered caller (fire-and-forget, so free).
426
+ observedMetadata: buildObservedMetadata(request)
370
427
  }
371
428
  ),
372
429
  budgetMs
@@ -407,7 +464,11 @@ function buildVerificationRequest(options, request, classification, includeArtif
407
464
  sourceIp: clientIp,
408
465
  userAgent: getHeader(request, "user-agent"),
409
466
  referer: getHeader(request, "referer"),
410
- host: getHeader(request, "host")
467
+ host: getHeader(request, "host"),
468
+ // Workstream D — the full sanitised header + connection capture. The
469
+ // backend stores it fire-and-forget (zero decision latency) and uses the
470
+ // key-format / platform headers for attribution.
471
+ observedMetadata: buildObservedMetadata(request)
411
472
  },
412
473
  ...includeArtifacts && classification.artifacts && { commerceArtifacts: classification.artifacts }
413
474
  };
@@ -611,6 +672,7 @@ function createEdgeHandler(options) {
611
672
  return async function handler(event) {
612
673
  const request = getRequest(event);
613
674
  try {
675
+ normalizeOriginHost(request);
614
676
  const apiKey = await resolveApiKey({ ...options, log });
615
677
  const config = await loadEdgeConfig(options, apiKey);
616
678
  const policy = resolvePolicy(config, request.uri);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/cf.ts","../src/config.ts","../src/classify.ts","../src/detect.ts","../src/pipeline.ts","../src/events.ts","../src/messaging.ts","../src/enforce.ts","../src/handler.ts"],"sourcesContent":["/**\n * @astrasyncai/adapter-lambda — Commerce Shield edge adapter (spec §7.4,\n * backlog §33.5 #1).\n *\n * Library surface: `createEdgeHandler(options)` builds a Lambda@Edge\n * origin-request handler. The deployable single-file bundle lives at\n * `dist/lambda/index.js` (see `src/lambda-entry.ts` + `infra/`).\n */\n\nexport { createEdgeHandler, type EdgeHandler } from './handler';\nexport { classify, resolvePolicy } from './classify';\nexport { detectCommerce, type DetectedCommerce } from './detect';\nexport { runTier } from './pipeline';\nexport { applyOutcome, ASAT_HEADERS } from './enforce';\nexport { buildDenyBody, buildManualReviewBody } from './messaging';\nexport { emitBeacon, withBudget, sampled } from './events';\nexport { resolveApiKey, loadEdgeConfig, _resetConfigCaches } from './config';\nexport {\n getRequest,\n getHeader,\n flattenHeaders,\n decodeBody,\n requestUrl,\n setHeader,\n stripHeadersByPrefix,\n jsonResponse,\n} from './cf';\nexport type {\n EdgeAdapterOptions,\n Classification,\n TrafficTier,\n TierOutcome,\n EdgeDecision,\n EffectivePolicy,\n} from './types';\n","/**\n * CloudFront origin-request event helpers. The Lambda@Edge event shape puts\n * headers in `{ key, value }[]` arrays keyed by LOWERCASED header name, and\n * (with IncludeBody) the body arrives base64-encoded with an\n * `inputTruncated` flag when it exceeded CloudFront's ~1MB exposure cap.\n */\n\nimport type {\n CloudFrontRequest,\n CloudFrontRequestEvent,\n CloudFrontHeaders,\n CloudFrontResultResponse,\n} from 'aws-lambda';\n\nexport function getRequest(event: CloudFrontRequestEvent): CloudFrontRequest {\n return event.Records[0].cf.request;\n}\n\n/** First value of a header (lowercase name), or undefined. */\nexport function getHeader(request: CloudFrontRequest, name: string): string | undefined {\n return request.headers[name.toLowerCase()]?.[0]?.value;\n}\n\n/**\n * Flatten CloudFront headers into the `Record<string, string | string[]>`\n * shape the SDK extractors and verify-access artifacts expect.\n */\nexport function flattenHeaders(headers: CloudFrontHeaders): Record<string, string | string[]> {\n const out: Record<string, string | string[]> = {};\n for (const [name, entries] of Object.entries(headers)) {\n if (!entries?.length) continue;\n out[name] = entries.length === 1 ? entries[0].value : entries.map((e) => e.value);\n }\n return out;\n}\n\nexport interface DecodedBody {\n /** UTF-8 body text, undefined when there is no body. */\n text?: string;\n /** True when CloudFront truncated the body before handing it to the trigger. */\n truncated: boolean;\n}\n\nexport function decodeBody(request: CloudFrontRequest): DecodedBody {\n const body = request.body;\n if (!body?.data) return { truncated: false };\n const text =\n body.encoding === 'base64' ? Buffer.from(body.data, 'base64').toString('utf8') : body.data;\n return { text, truncated: body.inputTruncated === true };\n}\n\n/** Full request URL as the origin will see it (scheme fixed to https). */\nexport function requestUrl(request: CloudFrontRequest, host: string): string {\n const qs = request.querystring ? `?${request.querystring}` : '';\n return `https://${host}${request.uri}${qs}`;\n}\n\n/**\n * Set a header on the origin-bound request (overwrites all prior values).\n */\nexport function setHeader(request: CloudFrontRequest, name: string, value: string): void {\n request.headers[name.toLowerCase()] = [{ key: name, value }];\n}\n\n/**\n * Strip every inbound header matching a prefix (lowercase compare). Used to\n * drop spoofed `x-astrasync-*` trust headers before the shell injects its\n * own — in BOTH modes, so a caller can never smuggle trust past the origin.\n */\nexport function stripHeadersByPrefix(request: CloudFrontRequest, prefix: string): void {\n const p = prefix.toLowerCase();\n for (const name of Object.keys(request.headers)) {\n if (name.startsWith(p)) delete request.headers[name];\n }\n}\n\nexport function jsonResponse(\n status: number,\n body: unknown,\n extraHeaders?: Record<string, string>\n): CloudFrontResultResponse {\n const headers: CloudFrontResultResponse['headers'] = {\n 'content-type': [{ key: 'Content-Type', value: 'application/json' }],\n };\n for (const [name, value] of Object.entries(extraHeaders ?? {})) {\n headers[name.toLowerCase()] = [{ key: name, value }];\n }\n return {\n status: String(status),\n statusDescription: status === 403 ? 'Forbidden' : status === 202 ? 'Accepted' : 'OK',\n headers,\n body: JSON.stringify(body),\n };\n}\n","/**\n * Runtime configuration for the edge shell.\n *\n * Lambda@Edge forbids environment variables, so configuration is layered:\n * 1. Build-time constants baked into the bundle (apiBaseUrl,\n * counterpartyId, counterpartyUrl, apiKeySecretName) — see\n * `tsup.config.ts` define block and `infra/deploy.sh`.\n * 2. The merchant API key from AWS Secrets Manager (us-east-1), fetched\n * lazily once per container and cached with a 15-minute TTL so\n * rotation needs no redeploy. Observe mode works fully without it —\n * the beacon endpoint is unauthenticated and verify() degrades\n * keyless — so a Secrets Manager outage can never take the shell down.\n * 3. The dashboard EdgeConfig via the SDK's getEdgeConfig (60s TTL,\n * stale-while-revalidate, enforce→observe degradation after 24h\n * staleness, DEFAULT_EDGE_CONFIG fallback — all in the SDK).\n */\n\nimport {\n getEdgeConfig,\n DEFAULT_EDGE_CONFIG,\n type EdgeConfig,\n} from '@astrasyncai/verification-gateway/edge-config';\nimport type { EdgeAdapterOptions } from './types';\n\nconst SECRET_TTL_MS = 15 * 60 * 1000;\n\ninterface SecretCacheEntry {\n value: string | undefined;\n fetchedAt: number;\n}\n\nconst secretCache = new Map<string, SecretCacheEntry>();\n\nasync function defaultFetchSecret(secretName: string): Promise<string | undefined> {\n // Lazy import so tests (and the library build) never touch the AWS SDK\n // unless a secret name is actually configured.\n const { SecretsManagerClient, GetSecretValueCommand } =\n await import('@aws-sdk/client-secrets-manager');\n // Lambda@Edge replicates globally but secrets live in us-east-1 (the\n // Lambda@Edge home region).\n const client = new SecretsManagerClient({ region: 'us-east-1' });\n const result = await client.send(new GetSecretValueCommand({ SecretId: secretName }));\n return result.SecretString ?? undefined;\n}\n\n/**\n * Resolve the merchant API key. Never throws — a missing/unreachable secret\n * degrades to keyless operation (observe mode is fully functional without\n * it; enforce mode without a key cannot fetch a config that enforces, so it\n * degrades to observe by construction).\n */\nexport async function resolveApiKey(options: EdgeAdapterOptions): Promise<string | undefined> {\n if (options.apiKey) return options.apiKey;\n const secretName = options.apiKeySecretName;\n if (!secretName) return undefined;\n\n const cached = secretCache.get(secretName);\n if (cached && Date.now() - cached.fetchedAt < SECRET_TTL_MS) return cached.value;\n\n try {\n const fetcher = options.fetchSecret ?? defaultFetchSecret;\n const value = await fetcher(secretName);\n secretCache.set(secretName, { value, fetchedAt: Date.now() });\n return value;\n } catch (err) {\n options.log?.('warn', 'Secrets Manager fetch failed — continuing keyless', {\n secretName,\n error: err instanceof Error ? err.message : String(err),\n });\n // Serve a stale value if we ever had one; otherwise keyless.\n return cached?.value;\n }\n}\n\n/**\n * Load the effective EdgeConfig. Delegates caching/fallback/degradation to\n * the SDK; adds the compile-time fallback override and never throws.\n */\nexport async function loadEdgeConfig(\n options: EdgeAdapterOptions,\n apiKey: string | undefined\n): Promise<EdgeConfig> {\n try {\n return await getEdgeConfig({ apiBaseUrl: options.apiBaseUrl, apiKey }, options.counterpartyId, {\n ttlMs: options.configTtlMs,\n });\n } catch {\n return options.fallbackConfig ?? DEFAULT_EDGE_CONFIG;\n }\n}\n\n/** Reset module caches — tests only. */\nexport function _resetConfigCaches(): void {\n secretCache.clear();\n}\n","/**\n * Three-tier traffic classification (§33.5 #1: verified /\n * identified-but-unregistered / anonymous) plus the two non-tiers the shell\n * touches as little as possible: `human` (zero network calls, zero logging)\n * and `skipped` (static assets / excluded paths).\n *\n * Cheapest checks first — the human path must add microseconds, not\n * milliseconds. Platform signatures come from the SDK so the edge and the\n * backend's verify-access anonymous handler classify identically.\n */\n\nimport type { CloudFrontRequest } from 'aws-lambda';\nimport { extractCredentials } from '@astrasyncai/verification-gateway/verify';\nimport { detectPlatformFingerprint } from '@astrasyncai/verification-gateway/platform-signatures';\nimport { matchEdgePathRule, type EdgeConfig } from '@astrasyncai/verification-gateway/edge-config';\nimport { getHeader, decodeBody, flattenHeaders } from './cf';\nimport { detectCommerce } from './detect';\nimport type { Classification, EffectivePolicy } from './types';\n\n/** Extensions the shell never classifies — static assets. */\nconst STATIC_EXTENSIONS =\n /\\.(css|js|mjs|map|png|jpe?g|gif|webp|avif|svg|ico|woff2?|ttf|otf|eot|mp4|webm|pdf|txt|xml)$/i;\n\n/** Non-browser, non-platform CLI/library UA tokens → anonymous bot. */\nconst BOT_UA_PATTERN =\n /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;\n\n/** Browser-shaped UA. */\nconst BROWSER_UA_PATTERN = /mozilla\\/\\d/i;\n\n/**\n * Resolve the effective mode/depth for a path from the EdgeConfig — ordered\n * first-match-wins rules with case-insensitive globs; no match falls through\n * to the config's top-level defaults. Static extensions are always skipped\n * regardless of rules (a rule can only skip MORE, never un-skip an asset).\n */\nexport function resolvePolicy(config: EdgeConfig, path: string): EffectivePolicy {\n if (STATIC_EXTENSIONS.test(path)) {\n return { mode: config.mode, depth: config.depth, skip: true };\n }\n const rule = matchEdgePathRule(config.pathRules, path);\n if (!rule) return { mode: config.mode, depth: config.depth, skip: false };\n return {\n mode: rule.mode ?? config.mode,\n depth: rule.depth ?? config.depth,\n skip: rule.skip === true,\n };\n}\n\n/**\n * Classify one request. Order (cheapest sufficient evidence wins):\n * 1. X-Astra-* credentials → `verified` candidate\n * 2. commerce-protocol credentials on the wire → `identified`\n * 3. platform UA fingerprint (Claude/ChatGPT/Gemini/…) → `identified`\n * 4. bot-shaped UA / headerless non-browser → `anonymous-bot`\n * 5. everything browser-shaped → `human`\n *\n * `depth: 'classify'` skips step 2 (no body decode, no header sniffing\n * beyond UA) — that is the zero-friction setting.\n */\nexport function classify(\n request: CloudFrontRequest,\n host: string,\n depth: 'classify' | 'authenticate' | 'authorize'\n): Classification {\n const userAgent = getHeader(request, 'user-agent') ?? '';\n\n // 1. Native AstraSync credentials.\n const credentials = extractCredentials(flattenHeaders(request.headers), {});\n if (credentials.astraId || credentials.apiKey || credentials.jwt) {\n return { tier: 'verified', protocol: 'x-astra' };\n }\n\n // 2. Commerce-protocol credentials (skipped entirely at classify depth).\n if (depth !== 'classify') {\n const commerce = detectCommerce(request, host, decodeBody(request));\n if (commerce) {\n return {\n tier: 'identified',\n protocol: commerce.protocol,\n artifacts: commerce.artifacts,\n evidence: commerce.evidence,\n fingerprint: detectPlatformFingerprint({ userAgent }),\n };\n }\n }\n\n // 3. Platform-agent UA fingerprint.\n const fingerprint = detectPlatformFingerprint({ userAgent });\n if (fingerprint) {\n return { tier: 'identified', protocol: 'platform-ua', fingerprint };\n }\n\n // 4. Bot-shaped: CLI/library UA, no UA at all, or any non-browser UA —\n // humans arrive with Mozilla-family UAs; everything else is automation.\n if (!userAgent || BOT_UA_PATTERN.test(userAgent) || !BROWSER_UA_PATTERN.test(userAgent)) {\n return { tier: 'anonymous-bot', evidence: { userAgent: userAgent || '(none)' } };\n }\n\n // 4b. Claims to be a browser but is missing every browser fetch-metadata\n // header — headless/spoofed. `sec-fetch-mode` is sent by all evergreen\n // browsers; its absence on a Mozilla UA is a strong bot signal.\n if (!getHeader(request, 'sec-fetch-mode') && !getHeader(request, 'accept-language')) {\n return { tier: 'anonymous-bot', evidence: { userAgent, spoofedBrowser: true } };\n }\n\n // 5. Human traffic — the shell gets out of the way.\n return { tier: 'human' };\n}\n\n/** Exported for tests. */\nexport const _patterns = { STATIC_EXTENSIONS, BOT_UA_PATTERN, BROWSER_UA_PATTERN };\n","/**\n * Crypto-free commerce-protocol detection (§7.4.2 detection order).\n *\n * The edge only SNIFFS — header shapes, compact-JWT patterns, body field\n * names — and packages what it saw as raw `CommerceArtifactsPayload` for\n * verify-access, whose Step 0.5 pipeline does the authoritative parsing and\n * cryptographic verification server-side. Nothing here imports jose/sd-jwt/\n * signature libraries: detection must stay cheap enough to run on every\n * non-human request, and verification must happen exactly once, in the\n * canonical sink, where it is recorded.\n */\n\nimport type { CloudFrontRequest } from 'aws-lambda';\nimport type { CommerceArtifactsPayload } from '@astrasyncai/verification-gateway';\nimport { flattenHeaders, getHeader, requestUrl, type DecodedBody } from './cf';\n\nexport interface DetectedCommerce {\n /** Detection-order label: rfc9421-agent-pay | rfc9421-tap | rfc9421-web-bot-auth | vi-sd-jwt | ap2 | acp | ucp | mpp | x402 */\n protocol: string;\n artifacts: CommerceArtifactsPayload;\n /** Light detection detail for beacons/audit — never secrets or full credentials. */\n evidence: Record<string, unknown>;\n}\n\n/** Compact SD-JWT: JWT + `~`-separated disclosures. */\nconst SD_JWT_PATTERN = /^eyJ[\\w-]+\\.[\\w-]*\\.[\\w-]+~/;\n\n/** Decode a JWT header segment without verifying anything. */\nfunction sniffJwtHeader(compact: string): Record<string, unknown> | undefined {\n try {\n const seg = compact.split('.')[0];\n return JSON.parse(Buffer.from(seg, 'base64url').toString('utf8')) as Record<string, unknown>;\n } catch {\n return undefined;\n }\n}\n\nfunction parseJsonBody(body: DecodedBody): Record<string, unknown> | undefined {\n if (!body.text) return undefined;\n try {\n const parsed: unknown = JSON.parse(body.text);\n return typeof parsed === 'object' && parsed !== null\n ? (parsed as Record<string, unknown>)\n : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * §7.4.2 order (after the X-Astra native check, which classify.ts owns):\n * SD-JWT/VI → RFC 9421 (Agent Pay / TAP / Web Bot Auth by kid namespace or\n * Signature-Agent) → ACP HMAC → UCP session → AP2 body triple → MPP → x402.\n * Returns undefined when nothing commerce-shaped is on the wire.\n *\n * When the body was truncated by CloudFront (`inputTruncated`), body-bound\n * artifacts still forward what arrived but carry a `bodyTruncated` evidence\n * flag — the backend downgrades those signatures rather than hard-failing.\n */\nexport function detectCommerce(\n request: CloudFrontRequest,\n host: string,\n body: DecodedBody\n): DetectedCommerce | undefined {\n const url = requestUrl(request, host);\n const method = request.method;\n const headers = flattenHeaders(request.headers);\n const jsonBody = parseJsonBody(body);\n const truncated = body.truncated ? { bodyTruncated: true } : {};\n\n // --- VI / SD-JWT credential in a header ------------------------------\n const authHeader = getHeader(request, 'authorization');\n const bearer = authHeader?.replace(/^Bearer\\s+/i, '');\n const mandateHeader =\n getHeader(request, 'x-payment-mandate') ?? getHeader(request, 'payment-mandate');\n const sdJwtCandidate = [mandateHeader, bearer].find((v) => v && SD_JWT_PATTERN.test(v));\n if (sdJwtCandidate) {\n const typ = sniffJwtHeader(sdJwtCandidate)?.typ;\n return {\n protocol: 'vi-sd-jwt',\n artifacts: { viSdJwt: sdJwtCandidate },\n evidence: { typ, ...truncated },\n };\n }\n\n // --- RFC 9421 (Agent Pay / TAP / Web Bot Auth) ------------------------\n const signature = getHeader(request, 'signature');\n const signatureInput = getHeader(request, 'signature-input');\n if (signature && signatureInput) {\n const signatureAgent = getHeader(request, 'signature-agent');\n const registryHint = getHeader(request, 'x-agent-registry');\n const lowerInput = signatureInput.toLowerCase();\n const protocol = signatureAgent\n ? 'rfc9421-web-bot-auth'\n : registryHint?.toLowerCase().includes('visa') || lowerInput.includes('visa')\n ? 'rfc9421-tap'\n : registryHint?.toLowerCase().includes('mastercard') || lowerInput.includes('mastercard')\n ? 'rfc9421-agent-pay'\n : 'rfc9421';\n return {\n protocol,\n artifacts: {\n rfc9421: { headers, method, url, ...(body.text && { body: body.text }) },\n },\n evidence: {\n ...(signatureAgent && { signatureAgent }),\n ...(registryHint && { registryHint }),\n keyid: signatureInput.match(/keyid=\"([^\"]+)\"/)?.[1],\n ...truncated,\n },\n };\n }\n\n // --- ACP (HMAC-signed webhook/checkout: signature WITHOUT signature-input)\n const acpTimestamp = getHeader(request, 'timestamp');\n if (signature && !signatureInput && (acpTimestamp || getHeader(request, 'idempotency-key'))) {\n return {\n protocol: 'acp',\n artifacts: {\n acpRequest: {\n method,\n url,\n headers,\n ...(body.text && { rawBody: body.text }),\n ...(jsonBody && { body: jsonBody }),\n },\n },\n evidence: { hasTimestamp: Boolean(acpTimestamp), ...truncated },\n };\n }\n\n // --- UCP checkout session ---------------------------------------------\n if (/\\/checkout[-_]sessions(\\/|$)/.test(request.uri)) {\n return {\n protocol: 'ucp',\n artifacts: { ucpRequest: { method, url, ...(jsonBody && { body: jsonBody }) } },\n evidence: { path: request.uri, ...truncated },\n };\n }\n\n // --- AP2 mandate triple in the body -----------------------------------\n if (jsonBody) {\n const intent = jsonBody.intent_mandate ?? jsonBody.intentMandate;\n const cart = jsonBody.cart_mandate ?? jsonBody.cartMandate;\n const payment = jsonBody.payment_mandate ?? jsonBody.paymentMandate;\n if (typeof intent === 'string' || typeof cart === 'string' || typeof payment === 'string') {\n return {\n protocol: 'ap2',\n artifacts: {\n ap2Mandates: {\n ...(typeof intent === 'string' && { intent }),\n ...(typeof cart === 'string' && { cart }),\n ...(typeof payment === 'string' && { payment }),\n },\n },\n evidence: {\n mandatesPresent: [\n typeof intent === 'string' && 'intent',\n typeof cart === 'string' && 'cart',\n typeof payment === 'string' && 'payment',\n ].filter(Boolean),\n ...truncated,\n },\n };\n }\n }\n\n // --- MPP (`Authorization: Payment <credential>`) -----------------------\n if (authHeader && /^Payment\\s+/i.test(authHeader)) {\n return {\n protocol: 'mpp',\n artifacts: {\n mppRequest: {\n method,\n url,\n headers,\n ...(body.text && { rawBody: body.text }),\n ...(jsonBody && { body: jsonBody }),\n },\n },\n evidence: truncated,\n };\n }\n\n // --- x402 (X-PAYMENT header) ------------------------------------------\n if (getHeader(request, 'x-payment')) {\n return {\n protocol: 'x402',\n artifacts: {\n x402Request: { method, url, headers, ...(jsonBody && { body: jsonBody }) },\n },\n evidence: truncated,\n };\n }\n\n return undefined;\n}\n","/**\n * Depth-controlled verification per tier. The edge never verifies\n * credentials itself — it forwards what it detected to verify-access, the\n * canonical sole verification sink (backend Step 0.5 runs the commerce\n * pipeline, records events, provisions orphans, persists sessions). What\n * the depth setting controls is how much the edge LOOKS and how much it\n * FORWARDS:\n *\n * classify — what is it? UA tiers only. Zero verify-access calls;\n * beacons only.\n * authenticate — who is it? + protocol detection. X-Astra agents get a\n * verify-access identity check; commerce/platform traffic\n * is beaconed with protocol + evidence (no signature\n * verification anywhere).\n * authorize — are they allowed? + artifact forwarding. X-Astra verify\n * calls carry commerceArtifacts; identified traffic gets an\n * anonymous verify-access call with artifacts +\n * callerMetadata so the backend cryptographically proves\n * and policy-evaluates once.\n */\n\nimport type { CloudFrontRequest } from 'aws-lambda';\nimport { verify, extractCredentials } from '@astrasyncai/verification-gateway/verify';\nimport { resolveHttpPdlss } from '@astrasyncai/verification-gateway/http-pdlss';\nimport type { GatewayConfig, VerificationRequest } from '@astrasyncai/verification-gateway';\nimport { flattenHeaders, getHeader } from './cf';\nimport { withBudget, emitBeacon, sampled } from './events';\nimport type { Classification, EdgeAdapterOptions, EffectivePolicy, TierOutcome } from './types';\n\nfunction baseOutcome(classification: Classification): TierOutcome {\n return {\n tier: classification.tier,\n decision: 'allow',\n reasons: [],\n protocol: classification.protocol,\n };\n}\n\nfunction buildVerificationRequest(\n options: EdgeAdapterOptions,\n request: CloudFrontRequest,\n classification: Classification,\n includeArtifacts: boolean\n): VerificationRequest {\n const headers = flattenHeaders(request.headers);\n const clientIp = getHeader(request, 'cloudfront-viewer-address')?.split(':')[0];\n // verify-access REQUIRES a purpose. Resolve via the shared §4.6 chain:\n // the agent's X-Astra-Purpose/-Action declarations, else the pinned\n // method table (GET → data/data.read etc.) — a transport verb never\n // travels as a PDLSS action.\n const pdlss = resolveHttpPdlss({\n method: request.method,\n astraPurpose: getHeader(request, 'x-astra-purpose'),\n astraAction: getHeader(request, 'x-astra-action'),\n });\n return {\n credentials: extractCredentials(headers, {}),\n purpose: pdlss.purpose,\n action: pdlss.action,\n resource: request.uri,\n createSession: true,\n counterpartyType: 'website',\n counterpartyUrl: options.counterpartyUrl,\n callerMetadata: {\n sourceIp: clientIp,\n userAgent: getHeader(request, 'user-agent'),\n referer: getHeader(request, 'referer'),\n host: getHeader(request, 'host'),\n },\n ...(includeArtifacts &&\n classification.artifacts && { commerceArtifacts: classification.artifacts }),\n };\n}\n\nfunction outcomeFromVerifyResult(\n classification: Classification,\n result: Awaited<ReturnType<typeof verify>>\n): TierOutcome {\n const outcome = baseOutcome(classification);\n outcome.sessionId = (result as { sessionId?: string }).sessionId;\n outcome.correlationId = result.correlationId;\n outcome.guidance = result.guidance;\n\n if (result.requiresStepUp || result.stepUpApproval) {\n outcome.decision = 'manual_review';\n outcome.reasons = ['step-up approval required'];\n outcome.stepUp = {\n pollUrl: (result.stepUpApproval as { pollUrl?: string } | undefined)?.pollUrl,\n expiresAt: (result.stepUpApproval as { expiresAt?: string } | undefined)?.expiresAt,\n };\n return outcome;\n }\n\n if (!result.identityVerified || !result.policyAllowed) {\n outcome.decision = 'deny';\n outcome.reasons = result.denialReasons?.length\n ? result.denialReasons\n : [!result.identityVerified ? 'identity not verified' : 'denied by access policy'];\n return outcome;\n }\n\n outcome.asat = {\n trustScore: result.agent?.trustScore,\n agentId: result.agent?.astraId,\n developerId: result.developer?.astradId,\n accessLevel: result.accessLevel,\n };\n return outcome;\n}\n\n/**\n * Run the tier-appropriate verification/telemetry for one classified\n * request. NEVER throws; on any failure returns an `allow` outcome with the\n * failure recorded in `reasons` (infra failures fail open — a presented\n * credential the BACKEND rejected is a deny, but an unreachable backend is\n * not a rejection).\n */\nexport async function runTier(\n options: EdgeAdapterOptions,\n request: CloudFrontRequest,\n classification: Classification,\n policy: EffectivePolicy,\n apiKey: string | undefined,\n sampling: { anonymousBeaconRate: number }\n): Promise<TierOutcome> {\n const budgets = options.budgets ?? {};\n const beaconMs = budgets.beaconMs ?? 300;\n const verifyMs =\n policy.mode === 'enforce'\n ? (budgets.verifyEnforceMs ?? 3000)\n : (budgets.verifyObserveMs ?? 1500);\n\n const gatewayConfig: GatewayConfig = {\n apiBaseUrl: options.apiBaseUrl,\n ...(apiKey && { apiKey }),\n counterpartyId: options.counterpartyId,\n // The shell handles its own error semantics; init self-probes don't\n // belong in a per-request edge function.\n disableInitChecks: true,\n } as GatewayConfig;\n\n try {\n switch (classification.tier) {\n case 'verified': {\n // classify depth = zero API calls, even for credentialed agents.\n if (policy.depth === 'classify') {\n return baseOutcome(classification);\n }\n const result = await withBudget(\n verify(\n gatewayConfig,\n buildVerificationRequest(options, request, classification, policy.depth === 'authorize')\n ),\n verifyMs\n );\n if (!result) {\n const outcome = baseOutcome(classification);\n outcome.reasons = ['verify-access unreachable within budget — fail-open'];\n return outcome;\n }\n return outcomeFromVerifyResult(classification, result);\n }\n\n case 'identified': {\n if (policy.depth === 'authorize' && classification.artifacts) {\n // Anonymous verify-access call WITH artifacts: the backend runs\n // the commerce pipeline, records unverified events, and\n // provisions orphan platform agents — one sink, recorded once.\n const result = await withBudget(\n verify(gatewayConfig, buildVerificationRequest(options, request, classification, true)),\n verifyMs\n );\n if (!result) {\n const outcome = baseOutcome(classification);\n outcome.reasons = ['verify-access unreachable within budget — fail-open'];\n return outcome;\n }\n return outcomeFromVerifyResult(classification, result);\n }\n // classify/credentials depth (or no artifacts): lightweight beacon.\n await emitBeacon(options, request, classification, beaconMs);\n const outcome = baseOutcome(classification);\n if (policy.mode === 'enforce' && policy.depth !== 'classify') {\n // Unregistered agent on an enforced path: deny with guidance.\n outcome.decision = 'deny';\n outcome.reasons = ['agent is not registered with AstraSync'];\n }\n return outcome;\n }\n\n case 'anonymous-bot': {\n if (sampled(sampling.anonymousBeaconRate, options.random)) {\n await emitBeacon(\n options,\n request,\n { ...classification, protocol: classification.protocol ?? 'ua-heuristic' },\n beaconMs\n );\n }\n // Anonymous bots pass through even in enforce mode — enforce gates\n // AGENT traffic; generic bot management stays the CDN/WAF's job\n // (mixed-traffic classification §5.1.7 is the enforce-mode\n // prerequisite tracked as §33.5 #9).\n return baseOutcome(classification);\n }\n\n default:\n return baseOutcome(classification);\n }\n } catch (err) {\n const outcome = baseOutcome(classification);\n outcome.reasons = [\n `edge pipeline error — fail-open: ${err instanceof Error ? err.message : String(err)}`,\n ];\n return outcome;\n }\n}\n","/**\n * Time-budgeted event emission.\n *\n * Lambda@Edge has no `waitUntil` — anything not awaited before the handler\n * returns may never run (the container freezes). So every network call is\n * awaited under an explicit budget and DROPPED on timeout: latency to the\n * customer's traffic is bounded, and a slow AstraSync API costs telemetry,\n * never page loads.\n */\n\nimport { reportUnregisteredAttempt } from '@astrasyncai/verification-gateway/verify';\nimport type { CloudFrontRequest } from 'aws-lambda';\nimport { getHeader } from './cf';\nimport type { Classification, EdgeAdapterOptions } from './types';\n\n/** Await `promise` for at most `ms`; resolve `undefined` on timeout/error. */\nexport async function withBudget<T>(promise: Promise<T>, ms: number): Promise<T | undefined> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n promise,\n new Promise<undefined>((resolve) => {\n timer = setTimeout(() => resolve(undefined), ms);\n }),\n ]);\n } catch {\n return undefined;\n } finally {\n if (timer) clearTimeout(timer);\n }\n}\n\n/**\n * Emit an unregistered-attempt beacon for `identified` (non-crypto depths)\n * and sampled `anonymous-bot` traffic. Unauthenticated by design\n * (CSRF-exempt on the backend); the backend fingerprints the UA and\n * find-or-provisions orphan platform agents server-side.\n */\nexport async function emitBeacon(\n options: EdgeAdapterOptions,\n request: CloudFrontRequest,\n classification: Classification,\n budgetMs: number\n): Promise<void> {\n const clientIp = getHeader(request, 'cloudfront-viewer-address')?.split(':')[0];\n await withBudget(\n reportUnregisteredAttempt(\n { apiBaseUrl: options.apiBaseUrl, counterpartyId: options.counterpartyId },\n {\n counterpartyUrl: options.counterpartyUrl,\n counterpartyType: 'website',\n sourceIp: clientIp,\n userAgent: getHeader(request, 'user-agent'),\n requestPath: request.uri,\n requestMethod: request.method,\n // 3.9.0 evidence fields (backend persists them into eventData).\n protocol: classification.protocol,\n evidence: classification.evidence,\n }\n ),\n budgetMs\n );\n}\n\n/** Deterministic-in-tests sampling gate. */\nexport function sampled(rate: number, random: () => number = Math.random): boolean {\n if (rate <= 0) return false;\n if (rate >= 1) return true;\n return random() < rate;\n}\n","/**\n * §7.4.6 — protocol-specific machine-readable messaging for enforce-mode\n * responses. Agents (not humans) consume these bodies: stable error codes,\n * the failure list, and registration guidance with real URLs.\n */\n\nimport type { TierOutcome } from './types';\n\nconst PROTOCOL_MESSAGES: Record<string, string> = {\n 'vi-sd-jwt':\n 'This request presented Visa Intelligent Commerce credentials, but the agent is not verified with AstraSync. Register the agent to pair your VI credentials with a verified identity and mandate.',\n 'rfc9421-agent-pay':\n 'This request carried a Mastercard Agent Pay signature, but the agent is not verified with AstraSync. Register the agent to pair Agent Pay credentials with a verified identity and mandate.',\n 'rfc9421-tap':\n 'This request carried a Visa TAP signature, but the agent is not verified with AstraSync. Register the agent to pair TAP credentials with a verified identity and mandate.',\n 'rfc9421-web-bot-auth':\n 'This request carried a Web Bot Auth signature, but the agent is not verified with AstraSync. Register the agent to link your signed-agent identity to a verified principal.',\n rfc9421:\n 'This request carried an RFC 9421 HTTP message signature, but the agent is not verified with AstraSync. Register the agent to link the signing identity to a verified principal.',\n acp: 'This request used the Agentic Commerce Protocol, but the agent is not verified with AstraSync. Register the agent to transact on this property.',\n ucp: 'This request used a UCP checkout session, but the agent is not verified with AstraSync. Register the agent to transact on this property.',\n ap2: 'This request presented AP2 mandates, but the agent is not verified with AstraSync. Register the agent to pair your mandates with a verified identity.',\n mpp: 'This request presented MPP payment credentials, but the agent is not verified with AstraSync. Register the agent to transact on this property.',\n x402: 'This request used x402 payment headers, but the agent is not verified with AstraSync. Register the agent to transact on this property.',\n 'platform-ua':\n 'This traffic was identified as a platform AI agent without AstraSync credentials. Register the agent to access this property with a verified identity.',\n 'x-astra':\n 'The presented AstraSync credentials failed verification or were denied by this property’s access policy.',\n};\n\nexport interface DenyBody {\n success: false;\n error: {\n code: 'UNAUTHORIZED' | 'INSUFFICIENT_ACCESS';\n message: string;\n protocol?: string;\n failures: string[];\n correlationId?: string;\n guidance: {\n message?: string;\n registrationUrl?: string;\n documentationUrl?: string;\n };\n };\n}\n\nexport function buildDenyBody(outcome: TierOutcome, apiBaseUrl: string): DenyBody {\n const origin = apiBaseUrl.replace(/\\/api\\/?$/, '');\n const protocolMessage =\n (outcome.protocol && PROTOCOL_MESSAGES[outcome.protocol]) ??\n 'This request could not be verified by AstraSync.';\n return {\n success: false,\n error: {\n // UNAUTHORIZED = identity failed (register/re-authenticate helps);\n // INSUFFICIENT_ACCESS = identity fine, policy denied (fix the mandate).\n code: outcome.tier === 'verified' ? 'INSUFFICIENT_ACCESS' : 'UNAUTHORIZED',\n message: protocolMessage,\n ...(outcome.protocol && { protocol: outcome.protocol }),\n failures: outcome.reasons,\n ...(outcome.correlationId && { correlationId: outcome.correlationId }),\n guidance: {\n // `||` not `??`: verify() fills these with EMPTY STRINGS when the\n // well-known cache is cold — empty means \"use the fallback\".\n message:\n outcome.guidance?.message ||\n 'Register this agent with AstraSync to receive verifiable credentials.',\n registrationUrl: outcome.guidance?.registrationUrl || `${origin}/register`,\n documentationUrl: outcome.guidance?.documentationUrl || `${origin}/docs/edge-adapter`,\n },\n },\n };\n}\n\nexport interface ManualReviewBody {\n success: false;\n status: 'manual_review';\n message: string;\n stepUpApproval?: { pollUrl?: string; expiresAt?: string };\n correlationId?: string;\n}\n\nexport function buildManualReviewBody(outcome: TierOutcome): ManualReviewBody {\n return {\n success: false,\n status: 'manual_review',\n message:\n 'This action requires human approval. Poll the step-up approval endpoint and retry once approved.',\n ...(outcome.stepUp && { stepUpApproval: outcome.stepUp }),\n ...(outcome.correlationId && { correlationId: outcome.correlationId }),\n };\n}\n","/**\n * Decision → CloudFront result wiring (§7.4 enforcement, §7.4.7 ASAT\n * headers). Executed in BOTH modes so observe mode records exactly what\n * enforce mode would have done — but only enforce mode is allowed to return\n * anything other than the origin-bound request.\n */\n\nimport type { CloudFrontRequest, CloudFrontRequestResult } from 'aws-lambda';\nimport { jsonResponse, setHeader, stripHeadersByPrefix } from './cf';\nimport { buildDenyBody, buildManualReviewBody } from './messaging';\nimport type { EdgeAdapterOptions, EffectivePolicy, TierOutcome } from './types';\n\n/** §7.4.7 ASAT trust-attestation headers injected on allowed requests. */\nexport const ASAT_HEADERS = {\n trustScore: 'X-AstraSync-Trust-Score',\n agentId: 'X-AstraSync-Agent-Id',\n developerId: 'X-AstraSync-Developer-Id',\n permissions: 'X-AstraSync-Permissions',\n observedDecision: 'X-AstraSync-Observed-Decision',\n} as const;\n\n/**\n * Apply the outcome. Always strips inbound spoofed `x-astrasync-*` headers\n * first (both modes — trust headers must only ever originate here).\n *\n * Observe mode: returns the request untouched apart from header hygiene and\n * an `X-AstraSync-Observed-Decision` stamp for origin-side log correlation.\n *\n * Enforce mode: ALLOW → request + ASAT headers; DENY → 403 machine-readable\n * body; MANUAL_REVIEW → 202 with step-up poll info.\n */\nexport function applyOutcome(\n options: EdgeAdapterOptions,\n request: CloudFrontRequest,\n outcome: TierOutcome,\n policy: EffectivePolicy\n): CloudFrontRequestResult {\n stripHeadersByPrefix(request, 'x-astrasync-');\n\n if (policy.mode !== 'enforce') {\n // Shadow stamp: what enforce WOULD have done, visible in origin logs.\n if (outcome.decision !== 'allow') {\n setHeader(request, ASAT_HEADERS.observedDecision, outcome.decision);\n }\n injectAsat(request, outcome);\n return request;\n }\n\n switch (outcome.decision) {\n case 'deny':\n return jsonResponse(403, buildDenyBody(outcome, options.apiBaseUrl));\n case 'manual_review':\n return jsonResponse(202, buildManualReviewBody(outcome), {\n 'Retry-After': '30',\n });\n default:\n injectAsat(request, outcome);\n return request;\n }\n}\n\nfunction injectAsat(request: CloudFrontRequest, outcome: TierOutcome): void {\n if (!outcome.asat) return;\n const { trustScore, agentId, developerId, accessLevel } = outcome.asat;\n if (trustScore !== undefined) {\n setHeader(request, ASAT_HEADERS.trustScore, String(trustScore));\n }\n if (agentId) setHeader(request, ASAT_HEADERS.agentId, agentId);\n if (developerId) setHeader(request, ASAT_HEADERS.developerId, developerId);\n if (accessLevel) setHeader(request, ASAT_HEADERS.permissions, accessLevel);\n}\n","/**\n * Commerce Shield edge shell — Lambda@Edge origin-request handler factory\n * (§7.4 / §33.5 #1).\n *\n * Composition per request:\n * config (cached) → path policy → classify → tier pipeline → apply outcome\n *\n * THE invariant (tested): outside enforce mode the handler ALWAYS returns\n * the origin-bound request — any error, timeout, or misconfiguration\n * results in pass-through. A top-level catch guarantees it even against\n * bugs in the shell itself.\n */\n\nimport type { CloudFrontRequestEvent, CloudFrontRequestResult } from 'aws-lambda';\nimport { getRequest, getHeader } from './cf';\nimport { resolveApiKey, loadEdgeConfig } from './config';\nimport { classify, resolvePolicy } from './classify';\nimport { runTier } from './pipeline';\nimport { applyOutcome } from './enforce';\nimport type { EdgeAdapterOptions } from './types';\n\nexport type EdgeHandler = (event: CloudFrontRequestEvent) => Promise<CloudFrontRequestResult>;\n\nexport function createEdgeHandler(options: EdgeAdapterOptions): EdgeHandler {\n const log =\n options.log ??\n ((level, message, data) => {\n // Lambda@Edge writes console output to the regional CloudWatch log\n // group of whichever edge location served the request.\n console[level === 'info' ? 'log' : level](`[astrasync-edge] ${message}`, data ?? '');\n });\n\n return async function handler(event) {\n const request = getRequest(event);\n try {\n const apiKey = await resolveApiKey({ ...options, log });\n const config = await loadEdgeConfig(options, apiKey);\n const policy = resolvePolicy(config, request.uri);\n\n // Skipped paths (static assets / skip rules): untouched, zero work.\n if (policy.skip) return request;\n\n const host =\n getHeader(request, 'x-forwarded-host') ??\n getHeader(request, 'host') ??\n new URL(options.counterpartyUrl).host;\n\n const classification = classify(request, host, policy.depth);\n\n // Humans: get out of the way. No calls, no logs, no header changes\n // beyond nothing at all.\n if (classification.tier === 'human') return request;\n\n const outcome = await runTier(\n { ...options, log },\n request,\n classification,\n policy,\n apiKey,\n config.sampling\n );\n\n if (outcome.decision !== 'allow') {\n log(\n 'info',\n policy.mode === 'enforce'\n ? `${outcome.decision.toUpperCase()}: ${request.method} ${request.uri}`\n : `[OBSERVE] would have been ${outcome.decision.toUpperCase()}: ${request.method} ${request.uri}`,\n { tier: outcome.tier, protocol: outcome.protocol, reasons: outcome.reasons }\n );\n }\n\n return applyOutcome(options, request, outcome, policy);\n } catch (err) {\n // Fail-open, unconditionally: the shell must never take the site down.\n log('error', 'edge handler error — passing request through', {\n error: err instanceof Error ? err.message : String(err),\n });\n return request;\n }\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACcO,SAAS,WAAW,OAAkD;AAC3E,SAAO,MAAM,QAAQ,CAAC,EAAE,GAAG;AAC7B;AAGO,SAAS,UAAU,SAA4B,MAAkC;AACtF,SAAO,QAAQ,QAAQ,KAAK,YAAY,CAAC,IAAI,CAAC,GAAG;AACnD;AAMO,SAAS,eAAe,SAA+D;AAC5F,QAAM,MAAyC,CAAC;AAChD,aAAW,CAAC,MAAM,OAAO,KAAK,OAAO,QAAQ,OAAO,GAAG;AACrD,QAAI,CAAC,SAAS,OAAQ;AACtB,QAAI,IAAI,IAAI,QAAQ,WAAW,IAAI,QAAQ,CAAC,EAAE,QAAQ,QAAQ,IAAI,CAAC,MAAM,EAAE,KAAK;AAAA,EAClF;AACA,SAAO;AACT;AASO,SAAS,WAAW,SAAyC;AAClE,QAAM,OAAO,QAAQ;AACrB,MAAI,CAAC,MAAM,KAAM,QAAO,EAAE,WAAW,MAAM;AAC3C,QAAM,OACJ,KAAK,aAAa,WAAW,OAAO,KAAK,KAAK,MAAM,QAAQ,EAAE,SAAS,MAAM,IAAI,KAAK;AACxF,SAAO,EAAE,MAAM,WAAW,KAAK,mBAAmB,KAAK;AACzD;AAGO,SAAS,WAAW,SAA4B,MAAsB;AAC3E,QAAM,KAAK,QAAQ,cAAc,IAAI,QAAQ,WAAW,KAAK;AAC7D,SAAO,WAAW,IAAI,GAAG,QAAQ,GAAG,GAAG,EAAE;AAC3C;AAKO,SAAS,UAAU,SAA4B,MAAc,OAAqB;AACvF,UAAQ,QAAQ,KAAK,YAAY,CAAC,IAAI,CAAC,EAAE,KAAK,MAAM,MAAM,CAAC;AAC7D;AAOO,SAAS,qBAAqB,SAA4B,QAAsB;AACrF,QAAM,IAAI,OAAO,YAAY;AAC7B,aAAW,QAAQ,OAAO,KAAK,QAAQ,OAAO,GAAG;AAC/C,QAAI,KAAK,WAAW,CAAC,EAAG,QAAO,QAAQ,QAAQ,IAAI;AAAA,EACrD;AACF;AAEO,SAAS,aACd,QACA,MACA,cAC0B;AAC1B,QAAM,UAA+C;AAAA,IACnD,gBAAgB,CAAC,EAAE,KAAK,gBAAgB,OAAO,mBAAmB,CAAC;AAAA,EACrE;AACA,aAAW,CAAC,MAAM,KAAK,KAAK,OAAO,QAAQ,gBAAgB,CAAC,CAAC,GAAG;AAC9D,YAAQ,KAAK,YAAY,CAAC,IAAI,CAAC,EAAE,KAAK,MAAM,MAAM,CAAC;AAAA,EACrD;AACA,SAAO;AAAA,IACL,QAAQ,OAAO,MAAM;AAAA,IACrB,mBAAmB,WAAW,MAAM,cAAc,WAAW,MAAM,aAAa;AAAA,IAChF;AAAA,IACA,MAAM,KAAK,UAAU,IAAI;AAAA,EAC3B;AACF;;;AC5EA,yBAIO;AAGP,IAAM,gBAAgB,KAAK,KAAK;AAOhC,IAAM,cAAc,oBAAI,IAA8B;AAEtD,eAAe,mBAAmB,YAAiD;AAGjF,QAAM,EAAE,sBAAsB,sBAAsB,IAClD,MAAM,OAAO,iCAAiC;AAGhD,QAAM,SAAS,IAAI,qBAAqB,EAAE,QAAQ,YAAY,CAAC;AAC/D,QAAM,SAAS,MAAM,OAAO,KAAK,IAAI,sBAAsB,EAAE,UAAU,WAAW,CAAC,CAAC;AACpF,SAAO,OAAO,gBAAgB;AAChC;AAQA,eAAsB,cAAc,SAA0D;AAC5F,MAAI,QAAQ,OAAQ,QAAO,QAAQ;AACnC,QAAM,aAAa,QAAQ;AAC3B,MAAI,CAAC,WAAY,QAAO;AAExB,QAAM,SAAS,YAAY,IAAI,UAAU;AACzC,MAAI,UAAU,KAAK,IAAI,IAAI,OAAO,YAAY,cAAe,QAAO,OAAO;AAE3E,MAAI;AACF,UAAM,UAAU,QAAQ,eAAe;AACvC,UAAM,QAAQ,MAAM,QAAQ,UAAU;AACtC,gBAAY,IAAI,YAAY,EAAE,OAAO,WAAW,KAAK,IAAI,EAAE,CAAC;AAC5D,WAAO;AAAA,EACT,SAAS,KAAK;AACZ,YAAQ,MAAM,QAAQ,0DAAqD;AAAA,MACzE;AAAA,MACA,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,IACxD,CAAC;AAED,WAAO,QAAQ;AAAA,EACjB;AACF;AAMA,eAAsB,eACpB,SACA,QACqB;AACrB,MAAI;AACF,WAAO,UAAM,kCAAc,EAAE,YAAY,QAAQ,YAAY,OAAO,GAAG,QAAQ,gBAAgB;AAAA,MAC7F,OAAO,QAAQ;AAAA,IACjB,CAAC;AAAA,EACH,QAAQ;AACN,WAAO,QAAQ,kBAAkB;AAAA,EACnC;AACF;AAGO,SAAS,qBAA2B;AACzC,cAAY,MAAM;AACpB;;;AClFA,oBAAmC;AACnC,iCAA0C;AAC1C,IAAAA,sBAAmD;;;ACWnD,IAAM,iBAAiB;AAGvB,SAAS,eAAe,SAAsD;AAC5E,MAAI;AACF,UAAM,MAAM,QAAQ,MAAM,GAAG,EAAE,CAAC;AAChC,WAAO,KAAK,MAAM,OAAO,KAAK,KAAK,WAAW,EAAE,SAAS,MAAM,CAAC;AAAA,EAClE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,cAAc,MAAwD;AAC7E,MAAI,CAAC,KAAK,KAAM,QAAO;AACvB,MAAI;AACF,UAAM,SAAkB,KAAK,MAAM,KAAK,IAAI;AAC5C,WAAO,OAAO,WAAW,YAAY,WAAW,OAC3C,SACD;AAAA,EACN,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAYO,SAAS,eACd,SACA,MACA,MAC8B;AAC9B,QAAM,MAAM,WAAW,SAAS,IAAI;AACpC,QAAM,SAAS,QAAQ;AACvB,QAAM,UAAU,eAAe,QAAQ,OAAO;AAC9C,QAAM,WAAW,cAAc,IAAI;AACnC,QAAM,YAAY,KAAK,YAAY,EAAE,eAAe,KAAK,IAAI,CAAC;AAG9D,QAAM,aAAa,UAAU,SAAS,eAAe;AACrD,QAAM,SAAS,YAAY,QAAQ,eAAe,EAAE;AACpD,QAAM,gBACJ,UAAU,SAAS,mBAAmB,KAAK,UAAU,SAAS,iBAAiB;AACjF,QAAM,iBAAiB,CAAC,eAAe,MAAM,EAAE,KAAK,CAAC,MAAM,KAAK,eAAe,KAAK,CAAC,CAAC;AACtF,MAAI,gBAAgB;AAClB,UAAM,MAAM,eAAe,cAAc,GAAG;AAC5C,WAAO;AAAA,MACL,UAAU;AAAA,MACV,WAAW,EAAE,SAAS,eAAe;AAAA,MACrC,UAAU,EAAE,KAAK,GAAG,UAAU;AAAA,IAChC;AAAA,EACF;AAGA,QAAM,YAAY,UAAU,SAAS,WAAW;AAChD,QAAM,iBAAiB,UAAU,SAAS,iBAAiB;AAC3D,MAAI,aAAa,gBAAgB;AAC/B,UAAM,iBAAiB,UAAU,SAAS,iBAAiB;AAC3D,UAAM,eAAe,UAAU,SAAS,kBAAkB;AAC1D,UAAM,aAAa,eAAe,YAAY;AAC9C,UAAM,WAAW,iBACb,yBACA,cAAc,YAAY,EAAE,SAAS,MAAM,KAAK,WAAW,SAAS,MAAM,IACxE,gBACA,cAAc,YAAY,EAAE,SAAS,YAAY,KAAK,WAAW,SAAS,YAAY,IACpF,sBACA;AACR,WAAO;AAAA,MACL;AAAA,MACA,WAAW;AAAA,QACT,SAAS,EAAE,SAAS,QAAQ,KAAK,GAAI,KAAK,QAAQ,EAAE,MAAM,KAAK,KAAK,EAAG;AAAA,MACzE;AAAA,MACA,UAAU;AAAA,QACR,GAAI,kBAAkB,EAAE,eAAe;AAAA,QACvC,GAAI,gBAAgB,EAAE,aAAa;AAAA,QACnC,OAAO,eAAe,MAAM,iBAAiB,IAAI,CAAC;AAAA,QAClD,GAAG;AAAA,MACL;AAAA,IACF;AAAA,EACF;AAGA,QAAM,eAAe,UAAU,SAAS,WAAW;AACnD,MAAI,aAAa,CAAC,mBAAmB,gBAAgB,UAAU,SAAS,iBAAiB,IAAI;AAC3F,WAAO;AAAA,MACL,UAAU;AAAA,MACV,WAAW;AAAA,QACT,YAAY;AAAA,UACV;AAAA,UACA;AAAA,UACA;AAAA,UACA,GAAI,KAAK,QAAQ,EAAE,SAAS,KAAK,KAAK;AAAA,UACtC,GAAI,YAAY,EAAE,MAAM,SAAS;AAAA,QACnC;AAAA,MACF;AAAA,MACA,UAAU,EAAE,cAAc,QAAQ,YAAY,GAAG,GAAG,UAAU;AAAA,IAChE;AAAA,EACF;AAGA,MAAI,+BAA+B,KAAK,QAAQ,GAAG,GAAG;AACpD,WAAO;AAAA,MACL,UAAU;AAAA,MACV,WAAW,EAAE,YAAY,EAAE,QAAQ,KAAK,GAAI,YAAY,EAAE,MAAM,SAAS,EAAG,EAAE;AAAA,MAC9E,UAAU,EAAE,MAAM,QAAQ,KAAK,GAAG,UAAU;AAAA,IAC9C;AAAA,EACF;AAGA,MAAI,UAAU;AACZ,UAAM,SAAS,SAAS,kBAAkB,SAAS;AACnD,UAAM,OAAO,SAAS,gBAAgB,SAAS;AAC/C,UAAM,UAAU,SAAS,mBAAmB,SAAS;AACrD,QAAI,OAAO,WAAW,YAAY,OAAO,SAAS,YAAY,OAAO,YAAY,UAAU;AACzF,aAAO;AAAA,QACL,UAAU;AAAA,QACV,WAAW;AAAA,UACT,aAAa;AAAA,YACX,GAAI,OAAO,WAAW,YAAY,EAAE,OAAO;AAAA,YAC3C,GAAI,OAAO,SAAS,YAAY,EAAE,KAAK;AAAA,YACvC,GAAI,OAAO,YAAY,YAAY,EAAE,QAAQ;AAAA,UAC/C;AAAA,QACF;AAAA,QACA,UAAU;AAAA,UACR,iBAAiB;AAAA,YACf,OAAO,WAAW,YAAY;AAAA,YAC9B,OAAO,SAAS,YAAY;AAAA,YAC5B,OAAO,YAAY,YAAY;AAAA,UACjC,EAAE,OAAO,OAAO;AAAA,UAChB,GAAG;AAAA,QACL;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,MAAI,cAAc,eAAe,KAAK,UAAU,GAAG;AACjD,WAAO;AAAA,MACL,UAAU;AAAA,MACV,WAAW;AAAA,QACT,YAAY;AAAA,UACV;AAAA,UACA;AAAA,UACA;AAAA,UACA,GAAI,KAAK,QAAQ,EAAE,SAAS,KAAK,KAAK;AAAA,UACtC,GAAI,YAAY,EAAE,MAAM,SAAS;AAAA,QACnC;AAAA,MACF;AAAA,MACA,UAAU;AAAA,IACZ;AAAA,EACF;AAGA,MAAI,UAAU,SAAS,WAAW,GAAG;AACnC,WAAO;AAAA,MACL,UAAU;AAAA,MACV,WAAW;AAAA,QACT,aAAa,EAAE,QAAQ,KAAK,SAAS,GAAI,YAAY,EAAE,MAAM,SAAS,EAAG;AAAA,MAC3E;AAAA,MACA,UAAU;AAAA,IACZ;AAAA,EACF;AAEA,SAAO;AACT;;;ADhLA,IAAM,oBACJ;AAGF,IAAM,iBACJ;AAGF,IAAM,qBAAqB;AAQpB,SAAS,cAAc,QAAoB,MAA+B;AAC/E,MAAI,kBAAkB,KAAK,IAAI,GAAG;AAChC,WAAO,EAAE,MAAM,OAAO,MAAM,OAAO,OAAO,OAAO,MAAM,KAAK;AAAA,EAC9D;AACA,QAAM,WAAO,uCAAkB,OAAO,WAAW,IAAI;AACrD,MAAI,CAAC,KAAM,QAAO,EAAE,MAAM,OAAO,MAAM,OAAO,OAAO,OAAO,MAAM,MAAM;AACxE,SAAO;AAAA,IACL,MAAM,KAAK,QAAQ,OAAO;AAAA,IAC1B,OAAO,KAAK,SAAS,OAAO;AAAA,IAC5B,MAAM,KAAK,SAAS;AAAA,EACtB;AACF;AAaO,SAAS,SACd,SACA,MACA,OACgB;AAChB,QAAM,YAAY,UAAU,SAAS,YAAY,KAAK;AAGtD,QAAM,kBAAc,kCAAmB,eAAe,QAAQ,OAAO,GAAG,CAAC,CAAC;AAC1E,MAAI,YAAY,WAAW,YAAY,UAAU,YAAY,KAAK;AAChE,WAAO,EAAE,MAAM,YAAY,UAAU,UAAU;AAAA,EACjD;AAGA,MAAI,UAAU,YAAY;AACxB,UAAM,WAAW,eAAe,SAAS,MAAM,WAAW,OAAO,CAAC;AAClE,QAAI,UAAU;AACZ,aAAO;AAAA,QACL,MAAM;AAAA,QACN,UAAU,SAAS;AAAA,QACnB,WAAW,SAAS;AAAA,QACpB,UAAU,SAAS;AAAA,QACnB,iBAAa,sDAA0B,EAAE,UAAU,CAAC;AAAA,MACtD;AAAA,IACF;AAAA,EACF;AAGA,QAAM,kBAAc,sDAA0B,EAAE,UAAU,CAAC;AAC3D,MAAI,aAAa;AACf,WAAO,EAAE,MAAM,cAAc,UAAU,eAAe,YAAY;AAAA,EACpE;AAIA,MAAI,CAAC,aAAa,eAAe,KAAK,SAAS,KAAK,CAAC,mBAAmB,KAAK,SAAS,GAAG;AACvF,WAAO,EAAE,MAAM,iBAAiB,UAAU,EAAE,WAAW,aAAa,SAAS,EAAE;AAAA,EACjF;AAKA,MAAI,CAAC,UAAU,SAAS,gBAAgB,KAAK,CAAC,UAAU,SAAS,iBAAiB,GAAG;AACnF,WAAO,EAAE,MAAM,iBAAiB,UAAU,EAAE,WAAW,gBAAgB,KAAK,EAAE;AAAA,EAChF;AAGA,SAAO,EAAE,MAAM,QAAQ;AACzB;;;AEtFA,IAAAC,iBAA2C;AAC3C,wBAAiC;;;ACbjC,IAAAC,iBAA0C;AAM1C,eAAsB,WAAc,SAAqB,IAAoC;AAC3F,MAAI;AACJ,MAAI;AACF,WAAO,MAAM,QAAQ,KAAK;AAAA,MACxB;AAAA,MACA,IAAI,QAAmB,CAAC,YAAY;AAClC,gBAAQ,WAAW,MAAM,QAAQ,MAAS,GAAG,EAAE;AAAA,MACjD,CAAC;AAAA,IACH,CAAC;AAAA,EACH,QAAQ;AACN,WAAO;AAAA,EACT,UAAE;AACA,QAAI,MAAO,cAAa,KAAK;AAAA,EAC/B;AACF;AAQA,eAAsB,WACpB,SACA,SACA,gBACA,UACe;AACf,QAAM,WAAW,UAAU,SAAS,2BAA2B,GAAG,MAAM,GAAG,EAAE,CAAC;AAC9E,QAAM;AAAA,QACJ;AAAA,MACE,EAAE,YAAY,QAAQ,YAAY,gBAAgB,QAAQ,eAAe;AAAA,MACzE;AAAA,QACE,iBAAiB,QAAQ;AAAA,QACzB,kBAAkB;AAAA,QAClB,UAAU;AAAA,QACV,WAAW,UAAU,SAAS,YAAY;AAAA,QAC1C,aAAa,QAAQ;AAAA,QACrB,eAAe,QAAQ;AAAA;AAAA,QAEvB,UAAU,eAAe;AAAA,QACzB,UAAU,eAAe;AAAA,MAC3B;AAAA,IACF;AAAA,IACA;AAAA,EACF;AACF;AAGO,SAAS,QAAQ,MAAc,SAAuB,KAAK,QAAiB;AACjF,MAAI,QAAQ,EAAG,QAAO;AACtB,MAAI,QAAQ,EAAG,QAAO;AACtB,SAAO,OAAO,IAAI;AACpB;;;ADxCA,SAAS,YAAY,gBAA6C;AAChE,SAAO;AAAA,IACL,MAAM,eAAe;AAAA,IACrB,UAAU;AAAA,IACV,SAAS,CAAC;AAAA,IACV,UAAU,eAAe;AAAA,EAC3B;AACF;AAEA,SAAS,yBACP,SACA,SACA,gBACA,kBACqB;AACrB,QAAM,UAAU,eAAe,QAAQ,OAAO;AAC9C,QAAM,WAAW,UAAU,SAAS,2BAA2B,GAAG,MAAM,GAAG,EAAE,CAAC;AAK9E,QAAM,YAAQ,oCAAiB;AAAA,IAC7B,QAAQ,QAAQ;AAAA,IAChB,cAAc,UAAU,SAAS,iBAAiB;AAAA,IAClD,aAAa,UAAU,SAAS,gBAAgB;AAAA,EAClD,CAAC;AACD,SAAO;AAAA,IACL,iBAAa,mCAAmB,SAAS,CAAC,CAAC;AAAA,IAC3C,SAAS,MAAM;AAAA,IACf,QAAQ,MAAM;AAAA,IACd,UAAU,QAAQ;AAAA,IAClB,eAAe;AAAA,IACf,kBAAkB;AAAA,IAClB,iBAAiB,QAAQ;AAAA,IACzB,gBAAgB;AAAA,MACd,UAAU;AAAA,MACV,WAAW,UAAU,SAAS,YAAY;AAAA,MAC1C,SAAS,UAAU,SAAS,SAAS;AAAA,MACrC,MAAM,UAAU,SAAS,MAAM;AAAA,IACjC;AAAA,IACA,GAAI,oBACF,eAAe,aAAa,EAAE,mBAAmB,eAAe,UAAU;AAAA,EAC9E;AACF;AAEA,SAAS,wBACP,gBACA,QACa;AACb,QAAM,UAAU,YAAY,cAAc;AAC1C,UAAQ,YAAa,OAAkC;AACvD,UAAQ,gBAAgB,OAAO;AAC/B,UAAQ,WAAW,OAAO;AAE1B,MAAI,OAAO,kBAAkB,OAAO,gBAAgB;AAClD,YAAQ,WAAW;AACnB,YAAQ,UAAU,CAAC,2BAA2B;AAC9C,YAAQ,SAAS;AAAA,MACf,SAAU,OAAO,gBAAqD;AAAA,MACtE,WAAY,OAAO,gBAAuD;AAAA,IAC5E;AACA,WAAO;AAAA,EACT;AAEA,MAAI,CAAC,OAAO,oBAAoB,CAAC,OAAO,eAAe;AACrD,YAAQ,WAAW;AACnB,YAAQ,UAAU,OAAO,eAAe,SACpC,OAAO,gBACP,CAAC,CAAC,OAAO,mBAAmB,0BAA0B,yBAAyB;AACnF,WAAO;AAAA,EACT;AAEA,UAAQ,OAAO;AAAA,IACb,YAAY,OAAO,OAAO;AAAA,IAC1B,SAAS,OAAO,OAAO;AAAA,IACvB,aAAa,OAAO,WAAW;AAAA,IAC/B,aAAa,OAAO;AAAA,EACtB;AACA,SAAO;AACT;AASA,eAAsB,QACpB,SACA,SACA,gBACA,QACA,QACA,UACsB;AACtB,QAAM,UAAU,QAAQ,WAAW,CAAC;AACpC,QAAM,WAAW,QAAQ,YAAY;AACrC,QAAM,WACJ,OAAO,SAAS,YACX,QAAQ,mBAAmB,MAC3B,QAAQ,mBAAmB;AAElC,QAAM,gBAA+B;AAAA,IACnC,YAAY,QAAQ;AAAA,IACpB,GAAI,UAAU,EAAE,OAAO;AAAA,IACvB,gBAAgB,QAAQ;AAAA;AAAA;AAAA,IAGxB,mBAAmB;AAAA,EACrB;AAEA,MAAI;AACF,YAAQ,eAAe,MAAM;AAAA,MAC3B,KAAK,YAAY;AAEf,YAAI,OAAO,UAAU,YAAY;AAC/B,iBAAO,YAAY,cAAc;AAAA,QACnC;AACA,cAAM,SAAS,MAAM;AAAA,cACnB;AAAA,YACE;AAAA,YACA,yBAAyB,SAAS,SAAS,gBAAgB,OAAO,UAAU,WAAW;AAAA,UACzF;AAAA,UACA;AAAA,QACF;AACA,YAAI,CAAC,QAAQ;AACX,gBAAM,UAAU,YAAY,cAAc;AAC1C,kBAAQ,UAAU,CAAC,0DAAqD;AACxE,iBAAO;AAAA,QACT;AACA,eAAO,wBAAwB,gBAAgB,MAAM;AAAA,MACvD;AAAA,MAEA,KAAK,cAAc;AACjB,YAAI,OAAO,UAAU,eAAe,eAAe,WAAW;AAI5D,gBAAM,SAAS,MAAM;AAAA,gBACnB,uBAAO,eAAe,yBAAyB,SAAS,SAAS,gBAAgB,IAAI,CAAC;AAAA,YACtF;AAAA,UACF;AACA,cAAI,CAAC,QAAQ;AACX,kBAAMC,WAAU,YAAY,cAAc;AAC1C,YAAAA,SAAQ,UAAU,CAAC,0DAAqD;AACxE,mBAAOA;AAAA,UACT;AACA,iBAAO,wBAAwB,gBAAgB,MAAM;AAAA,QACvD;AAEA,cAAM,WAAW,SAAS,SAAS,gBAAgB,QAAQ;AAC3D,cAAM,UAAU,YAAY,cAAc;AAC1C,YAAI,OAAO,SAAS,aAAa,OAAO,UAAU,YAAY;AAE5D,kBAAQ,WAAW;AACnB,kBAAQ,UAAU,CAAC,wCAAwC;AAAA,QAC7D;AACA,eAAO;AAAA,MACT;AAAA,MAEA,KAAK,iBAAiB;AACpB,YAAI,QAAQ,SAAS,qBAAqB,QAAQ,MAAM,GAAG;AACzD,gBAAM;AAAA,YACJ;AAAA,YACA;AAAA,YACA,EAAE,GAAG,gBAAgB,UAAU,eAAe,YAAY,eAAe;AAAA,YACzE;AAAA,UACF;AAAA,QACF;AAKA,eAAO,YAAY,cAAc;AAAA,MACnC;AAAA,MAEA;AACE,eAAO,YAAY,cAAc;AAAA,IACrC;AAAA,EACF,SAAS,KAAK;AACZ,UAAM,UAAU,YAAY,cAAc;AAC1C,YAAQ,UAAU;AAAA,MAChB,yCAAoC,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,IACtF;AACA,WAAO;AAAA,EACT;AACF;;;AEhNA,IAAM,oBAA4C;AAAA,EAChD,aACE;AAAA,EACF,qBACE;AAAA,EACF,eACE;AAAA,EACF,wBACE;AAAA,EACF,SACE;AAAA,EACF,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,MAAM;AAAA,EACN,eACE;AAAA,EACF,WACE;AACJ;AAkBO,SAAS,cAAc,SAAsB,YAA8B;AAChF,QAAM,SAAS,WAAW,QAAQ,aAAa,EAAE;AACjD,QAAM,mBACH,QAAQ,YAAY,kBAAkB,QAAQ,QAAQ,MACvD;AACF,SAAO;AAAA,IACL,SAAS;AAAA,IACT,OAAO;AAAA;AAAA;AAAA,MAGL,MAAM,QAAQ,SAAS,aAAa,wBAAwB;AAAA,MAC5D,SAAS;AAAA,MACT,GAAI,QAAQ,YAAY,EAAE,UAAU,QAAQ,SAAS;AAAA,MACrD,UAAU,QAAQ;AAAA,MAClB,GAAI,QAAQ,iBAAiB,EAAE,eAAe,QAAQ,cAAc;AAAA,MACpE,UAAU;AAAA;AAAA;AAAA,QAGR,SACE,QAAQ,UAAU,WAClB;AAAA,QACF,iBAAiB,QAAQ,UAAU,mBAAmB,GAAG,MAAM;AAAA,QAC/D,kBAAkB,QAAQ,UAAU,oBAAoB,GAAG,MAAM;AAAA,MACnE;AAAA,IACF;AAAA,EACF;AACF;AAUO,SAAS,sBAAsB,SAAwC;AAC5E,SAAO;AAAA,IACL,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,SACE;AAAA,IACF,GAAI,QAAQ,UAAU,EAAE,gBAAgB,QAAQ,OAAO;AAAA,IACvD,GAAI,QAAQ,iBAAiB,EAAE,eAAe,QAAQ,cAAc;AAAA,EACtE;AACF;;;AC9EO,IAAM,eAAe;AAAA,EAC1B,YAAY;AAAA,EACZ,SAAS;AAAA,EACT,aAAa;AAAA,EACb,aAAa;AAAA,EACb,kBAAkB;AACpB;AAYO,SAAS,aACd,SACA,SACA,SACA,QACyB;AACzB,uBAAqB,SAAS,cAAc;AAE5C,MAAI,OAAO,SAAS,WAAW;AAE7B,QAAI,QAAQ,aAAa,SAAS;AAChC,gBAAU,SAAS,aAAa,kBAAkB,QAAQ,QAAQ;AAAA,IACpE;AACA,eAAW,SAAS,OAAO;AAC3B,WAAO;AAAA,EACT;AAEA,UAAQ,QAAQ,UAAU;AAAA,IACxB,KAAK;AACH,aAAO,aAAa,KAAK,cAAc,SAAS,QAAQ,UAAU,CAAC;AAAA,IACrE,KAAK;AACH,aAAO,aAAa,KAAK,sBAAsB,OAAO,GAAG;AAAA,QACvD,eAAe;AAAA,MACjB,CAAC;AAAA,IACH;AACE,iBAAW,SAAS,OAAO;AAC3B,aAAO;AAAA,EACX;AACF;AAEA,SAAS,WAAW,SAA4B,SAA4B;AAC1E,MAAI,CAAC,QAAQ,KAAM;AACnB,QAAM,EAAE,YAAY,SAAS,aAAa,YAAY,IAAI,QAAQ;AAClE,MAAI,eAAe,QAAW;AAC5B,cAAU,SAAS,aAAa,YAAY,OAAO,UAAU,CAAC;AAAA,EAChE;AACA,MAAI,QAAS,WAAU,SAAS,aAAa,SAAS,OAAO;AAC7D,MAAI,YAAa,WAAU,SAAS,aAAa,aAAa,WAAW;AACzE,MAAI,YAAa,WAAU,SAAS,aAAa,aAAa,WAAW;AAC3E;;;AC/CO,SAAS,kBAAkB,SAA0C;AAC1E,QAAM,MACJ,QAAQ,QACP,CAAC,OAAO,SAAS,SAAS;AAGzB,YAAQ,UAAU,SAAS,QAAQ,KAAK,EAAE,oBAAoB,OAAO,IAAI,QAAQ,EAAE;AAAA,EACrF;AAEF,SAAO,eAAe,QAAQ,OAAO;AACnC,UAAM,UAAU,WAAW,KAAK;AAChC,QAAI;AACF,YAAM,SAAS,MAAM,cAAc,EAAE,GAAG,SAAS,IAAI,CAAC;AACtD,YAAM,SAAS,MAAM,eAAe,SAAS,MAAM;AACnD,YAAM,SAAS,cAAc,QAAQ,QAAQ,GAAG;AAGhD,UAAI,OAAO,KAAM,QAAO;AAExB,YAAM,OACJ,UAAU,SAAS,kBAAkB,KACrC,UAAU,SAAS,MAAM,KACzB,IAAI,IAAI,QAAQ,eAAe,EAAE;AAEnC,YAAM,iBAAiB,SAAS,SAAS,MAAM,OAAO,KAAK;AAI3D,UAAI,eAAe,SAAS,QAAS,QAAO;AAE5C,YAAM,UAAU,MAAM;AAAA,QACpB,EAAE,GAAG,SAAS,IAAI;AAAA,QAClB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,OAAO;AAAA,MACT;AAEA,UAAI,QAAQ,aAAa,SAAS;AAChC;AAAA,UACE;AAAA,UACA,OAAO,SAAS,YACZ,GAAG,QAAQ,SAAS,YAAY,CAAC,KAAK,QAAQ,MAAM,IAAI,QAAQ,GAAG,KACnE,6BAA6B,QAAQ,SAAS,YAAY,CAAC,KAAK,QAAQ,MAAM,IAAI,QAAQ,GAAG;AAAA,UACjG,EAAE,MAAM,QAAQ,MAAM,UAAU,QAAQ,UAAU,SAAS,QAAQ,QAAQ;AAAA,QAC7E;AAAA,MACF;AAEA,aAAO,aAAa,SAAS,SAAS,SAAS,MAAM;AAAA,IACvD,SAAS,KAAK;AAEZ,UAAI,SAAS,qDAAgD;AAAA,QAC3D,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,MACxD,CAAC;AACD,aAAO;AAAA,IACT;AAAA,EACF;AACF;","names":["import_edge_config","import_verify","import_verify","outcome"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/cf.ts","../src/config.ts","../src/classify.ts","../src/detect.ts","../src/pipeline.ts","../src/events.ts","../src/messaging.ts","../src/enforce.ts","../src/handler.ts"],"sourcesContent":["/**\n * @astrasyncai/adapter-lambda — Commerce Shield edge adapter (spec §7.4,\n * backlog §33.5 #1).\n *\n * Library surface: `createEdgeHandler(options)` builds a Lambda@Edge\n * origin-request handler. The deployable single-file bundle lives at\n * `dist/lambda/index.js` (see `src/lambda-entry.ts` + `infra/`).\n */\n\nexport { createEdgeHandler, type EdgeHandler } from './handler';\nexport { classify, resolvePolicy } from './classify';\nexport { detectCommerce, type DetectedCommerce } from './detect';\nexport { runTier } from './pipeline';\nexport { applyOutcome, ASAT_HEADERS } from './enforce';\nexport { buildDenyBody, buildManualReviewBody } from './messaging';\nexport { emitBeacon, withBudget, sampled } from './events';\nexport { resolveApiKey, loadEdgeConfig, _resetConfigCaches } from './config';\nexport {\n getRequest,\n getHeader,\n flattenHeaders,\n decodeBody,\n requestUrl,\n setHeader,\n stripHeadersByPrefix,\n jsonResponse,\n} from './cf';\nexport type {\n EdgeAdapterOptions,\n Classification,\n TrafficTier,\n TierOutcome,\n EdgeDecision,\n EffectivePolicy,\n} from './types';\n","/**\n * CloudFront origin-request event helpers. The Lambda@Edge event shape puts\n * headers in `{ key, value }[]` arrays keyed by LOWERCASED header name, and\n * (with IncludeBody) the body arrives base64-encoded with an\n * `inputTruncated` flag when it exceeded CloudFront's ~1MB exposure cap.\n */\n\nimport type {\n CloudFrontRequest,\n CloudFrontRequestEvent,\n CloudFrontHeaders,\n CloudFrontResultResponse,\n} from 'aws-lambda';\nimport {\n sanitizeHeaders,\n type ObservedMetadata,\n} from '@astrasyncai/verification-gateway/metadata-capture';\n\nexport function getRequest(event: CloudFrontRequestEvent): CloudFrontRequest {\n return event.Records[0].cf.request;\n}\n\n/** First value of a header (lowercase name), or undefined. */\nexport function getHeader(request: CloudFrontRequest, name: string): string | undefined {\n return request.headers[name.toLowerCase()]?.[0]?.value;\n}\n\n/**\n * Flatten CloudFront headers into the `Record<string, string | string[]>`\n * shape the SDK extractors and verify-access artifacts expect.\n */\nexport function flattenHeaders(headers: CloudFrontHeaders): Record<string, string | string[]> {\n const out: Record<string, string | string[]> = {};\n for (const [name, entries] of Object.entries(headers)) {\n if (!entries?.length) continue;\n out[name] = entries.length === 1 ? entries[0].value : entries.map((e) => e.value);\n }\n return out;\n}\n\nexport interface DecodedBody {\n /** UTF-8 body text, undefined when there is no body. */\n text?: string;\n /** True when CloudFront truncated the body before handing it to the trigger. */\n truncated: boolean;\n}\n\nexport function decodeBody(request: CloudFrontRequest): DecodedBody {\n const body = request.body;\n if (!body?.data) return { truncated: false };\n const text =\n body.encoding === 'base64' ? Buffer.from(body.data, 'base64').toString('utf8') : body.data;\n return { text, truncated: body.inputTruncated === true };\n}\n\n/**\n * Workstream D — connection-layer signals the edge can see but the SDK cannot.\n * CloudFront injects the `CloudFront-Viewer-*` header family (IP, ASN, country,\n * TLS version/cipher, HTTP version, device class) when the OriginRequestPolicy\n * whitelists them (see infra/template.yaml). Absent values are simply omitted —\n * a partial connection block is fine.\n */\nexport function collectConnectionMetadata(request: CloudFrontRequest): Record<string, string> {\n const out: Record<string, string> = {};\n const put = (key: string, headerName: string, transform?: (v: string) => string) => {\n const v = getHeader(request, headerName);\n if (v) out[key] = transform ? transform(v) : v;\n };\n // `cloudfront-viewer-address` is `ip:port` (IPv6 keeps its brackets); take the\n // address portion the same way the beacon/pipeline already do.\n const addr = getHeader(request, 'cloudfront-viewer-address');\n if (addr) out.ip = addr.replace(/:\\d+$/, '');\n put('country', 'cloudfront-viewer-country');\n put('countryName', 'cloudfront-viewer-country-name');\n put('region', 'cloudfront-viewer-country-region');\n put('city', 'cloudfront-viewer-city');\n put('asn', 'cloudfront-viewer-asn');\n put('tlsVersion', 'cloudfront-viewer-tls');\n put('httpVersion', 'cloudfront-viewer-http-version');\n // Device-class booleans → a single compact device string.\n const devices: string[] = [];\n for (const [label, header] of [\n ['desktop', 'cloudfront-is-desktop-viewer'],\n ['mobile', 'cloudfront-is-mobile-viewer'],\n ['tablet', 'cloudfront-is-tablet-viewer'],\n ['smarttv', 'cloudfront-is-smarttv-viewer'],\n ] as const) {\n if (getHeader(request, header) === 'true') devices.push(label);\n }\n if (devices.length) out.device = devices.join(',');\n return out;\n}\n\n/**\n * Workstream D — the FULL sanitised metadata capture for one request: every\n * inbound header (secrets stripped, credential headers reduced to a safe format\n * prefix) plus the connection-layer block. The edge is the richest capture\n * point — the header arrays are already in memory — and this is pure string\n * work, so it adds no page latency. Forwarded on both the verify-access call\n * and the beacon.\n */\nexport function buildObservedMetadata(request: CloudFrontRequest): ObservedMetadata {\n const { headers, apiKeyFormat, platformHeaders } = sanitizeHeaders(\n flattenHeaders(request.headers)\n );\n const connection = collectConnectionMetadata(request);\n return {\n headers,\n ...(apiKeyFormat && { apiKeyFormat }),\n ...(platformHeaders && { platformHeaders }),\n ...(Object.keys(connection).length > 0 && { connection }),\n };\n}\n\n/**\n * Workstream D — normalize the origin-bound `Host` header.\n *\n * To receive the `CloudFront-Viewer-*` connection headers (IP/ASN/TLS/country)\n * we must move off the managed `AllViewerExceptHostHeader` origin-request policy\n * onto one that whitelists those headers — but every such policy ALSO forwards\n * the viewer `Host`, which would break vhost / PaaS origin routing that expects\n * the origin's own hostname. So the Lambda takes ownership of the Host rewrite:\n * stash the public (viewer) host into `X-Forwarded-Host` for downstream use and\n * detection, then set `Host` to the origin's domain (available on the event as\n * `request.origin.custom.domainName`). No-ops when the origin domain is absent\n * (synthetic test events) or already matches — so it is safe on every path,\n * including static-asset pass-through.\n */\nexport function normalizeOriginHost(request: CloudFrontRequest): void {\n const originDomain =\n request.origin?.custom?.domainName ?? request.origin?.s3?.domainName ?? undefined;\n if (!originDomain) return;\n const currentHost = getHeader(request, 'host');\n if (!currentHost || currentHost === originDomain) return;\n if (!getHeader(request, 'x-forwarded-host')) {\n setHeader(request, 'X-Forwarded-Host', currentHost);\n }\n setHeader(request, 'Host', originDomain);\n}\n\n/** Full request URL as the origin will see it (scheme fixed to https). */\nexport function requestUrl(request: CloudFrontRequest, host: string): string {\n const qs = request.querystring ? `?${request.querystring}` : '';\n return `https://${host}${request.uri}${qs}`;\n}\n\n/**\n * Set a header on the origin-bound request (overwrites all prior values).\n */\nexport function setHeader(request: CloudFrontRequest, name: string, value: string): void {\n request.headers[name.toLowerCase()] = [{ key: name, value }];\n}\n\n/**\n * Strip every inbound header matching a prefix (lowercase compare). Used to\n * drop spoofed `x-astrasync-*` trust headers before the shell injects its\n * own — in BOTH modes, so a caller can never smuggle trust past the origin.\n */\nexport function stripHeadersByPrefix(request: CloudFrontRequest, prefix: string): void {\n const p = prefix.toLowerCase();\n for (const name of Object.keys(request.headers)) {\n if (name.startsWith(p)) delete request.headers[name];\n }\n}\n\nexport function jsonResponse(\n status: number,\n body: unknown,\n extraHeaders?: Record<string, string>\n): CloudFrontResultResponse {\n const headers: CloudFrontResultResponse['headers'] = {\n 'content-type': [{ key: 'Content-Type', value: 'application/json' }],\n };\n for (const [name, value] of Object.entries(extraHeaders ?? {})) {\n headers[name.toLowerCase()] = [{ key: name, value }];\n }\n return {\n status: String(status),\n statusDescription: status === 403 ? 'Forbidden' : status === 202 ? 'Accepted' : 'OK',\n headers,\n body: JSON.stringify(body),\n };\n}\n","/**\n * Runtime configuration for the edge shell.\n *\n * Lambda@Edge forbids environment variables, so configuration is layered:\n * 1. Build-time constants baked into the bundle (apiBaseUrl,\n * counterpartyId, counterpartyUrl, apiKeySecretName) — see\n * `tsup.config.ts` define block and `infra/deploy.sh`.\n * 2. The merchant API key from AWS Secrets Manager (us-east-1), fetched\n * lazily once per container and cached with a 15-minute TTL so\n * rotation needs no redeploy. Observe mode works fully without it —\n * the beacon endpoint is unauthenticated and verify() degrades\n * keyless — so a Secrets Manager outage can never take the shell down.\n * 3. The dashboard EdgeConfig via the SDK's getEdgeConfig (60s TTL,\n * stale-while-revalidate, enforce→observe degradation after 24h\n * staleness, DEFAULT_EDGE_CONFIG fallback — all in the SDK).\n */\n\nimport {\n getEdgeConfig,\n DEFAULT_EDGE_CONFIG,\n type EdgeConfig,\n} from '@astrasyncai/verification-gateway/edge-config';\nimport type { EdgeAdapterOptions } from './types';\n\nconst SECRET_TTL_MS = 15 * 60 * 1000;\n\ninterface SecretCacheEntry {\n value: string | undefined;\n fetchedAt: number;\n}\n\nconst secretCache = new Map<string, SecretCacheEntry>();\n\nasync function defaultFetchSecret(secretName: string): Promise<string | undefined> {\n // Lazy import so tests (and the library build) never touch the AWS SDK\n // unless a secret name is actually configured.\n const { SecretsManagerClient, GetSecretValueCommand } =\n await import('@aws-sdk/client-secrets-manager');\n // Lambda@Edge replicates globally but secrets live in us-east-1 (the\n // Lambda@Edge home region).\n const client = new SecretsManagerClient({ region: 'us-east-1' });\n const result = await client.send(new GetSecretValueCommand({ SecretId: secretName }));\n return result.SecretString ?? undefined;\n}\n\n/**\n * Resolve the merchant API key. Never throws — a missing/unreachable secret\n * degrades to keyless operation (observe mode is fully functional without\n * it; enforce mode without a key cannot fetch a config that enforces, so it\n * degrades to observe by construction).\n */\nexport async function resolveApiKey(options: EdgeAdapterOptions): Promise<string | undefined> {\n if (options.apiKey) return options.apiKey;\n const secretName = options.apiKeySecretName;\n if (!secretName) return undefined;\n\n const cached = secretCache.get(secretName);\n if (cached && Date.now() - cached.fetchedAt < SECRET_TTL_MS) return cached.value;\n\n try {\n const fetcher = options.fetchSecret ?? defaultFetchSecret;\n const value = await fetcher(secretName);\n secretCache.set(secretName, { value, fetchedAt: Date.now() });\n return value;\n } catch (err) {\n options.log?.('warn', 'Secrets Manager fetch failed — continuing keyless', {\n secretName,\n error: err instanceof Error ? err.message : String(err),\n });\n // Serve a stale value if we ever had one; otherwise keyless.\n return cached?.value;\n }\n}\n\n/**\n * Load the effective EdgeConfig. Delegates caching/fallback/degradation to\n * the SDK; adds the compile-time fallback override and never throws.\n */\nexport async function loadEdgeConfig(\n options: EdgeAdapterOptions,\n apiKey: string | undefined\n): Promise<EdgeConfig> {\n try {\n return await getEdgeConfig({ apiBaseUrl: options.apiBaseUrl, apiKey }, options.counterpartyId, {\n ttlMs: options.configTtlMs,\n });\n } catch {\n return options.fallbackConfig ?? DEFAULT_EDGE_CONFIG;\n }\n}\n\n/** Reset module caches — tests only. */\nexport function _resetConfigCaches(): void {\n secretCache.clear();\n}\n","/**\n * Three-tier traffic classification (§33.5 #1: verified /\n * identified-but-unregistered / anonymous) plus the two non-tiers the shell\n * touches as little as possible: `human` (zero network calls, zero logging)\n * and `skipped` (static assets / excluded paths).\n *\n * Cheapest checks first — the human path must add microseconds, not\n * milliseconds. Platform signatures come from the SDK so the edge and the\n * backend's verify-access anonymous handler classify identically.\n */\n\nimport type { CloudFrontRequest } from 'aws-lambda';\nimport { extractCredentials } from '@astrasyncai/verification-gateway/verify';\nimport { detectPlatformFingerprint } from '@astrasyncai/verification-gateway/platform-signatures';\nimport { matchEdgePathRule, type EdgeConfig } from '@astrasyncai/verification-gateway/edge-config';\nimport { sanitizeHeaders } from '@astrasyncai/verification-gateway/metadata-capture';\nimport { getHeader, decodeBody, flattenHeaders } from './cf';\nimport { detectCommerce } from './detect';\nimport type { Classification, EffectivePolicy } from './types';\n\n/** Extensions the shell never classifies — static assets. */\nconst STATIC_EXTENSIONS =\n /\\.(css|js|mjs|map|png|jpe?g|gif|webp|avif|svg|ico|woff2?|ttf|otf|eot|mp4|webm|pdf|txt|xml)$/i;\n\n/** Non-browser, non-platform CLI/library UA tokens → anonymous bot. */\nconst BOT_UA_PATTERN =\n /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;\n\n/** Browser-shaped UA. */\nconst BROWSER_UA_PATTERN = /mozilla\\/\\d/i;\n\n/**\n * Resolve the effective mode/depth for a path from the EdgeConfig — ordered\n * first-match-wins rules with case-insensitive globs; no match falls through\n * to the config's top-level defaults. Static extensions are always skipped\n * regardless of rules (a rule can only skip MORE, never un-skip an asset).\n */\nexport function resolvePolicy(config: EdgeConfig, path: string): EffectivePolicy {\n if (STATIC_EXTENSIONS.test(path)) {\n return { mode: config.mode, depth: config.depth, skip: true };\n }\n const rule = matchEdgePathRule(config.pathRules, path);\n if (!rule) return { mode: config.mode, depth: config.depth, skip: false };\n return {\n mode: rule.mode ?? config.mode,\n depth: rule.depth ?? config.depth,\n skip: rule.skip === true,\n };\n}\n\n/**\n * Classify one request. Order (cheapest sufficient evidence wins):\n * 1. X-Astra-* credentials → `verified` candidate\n * 2. commerce-protocol credentials on the wire → `identified`\n * 3. platform UA fingerprint (Claude/ChatGPT/Gemini/…) → `identified`\n * 4. bot-shaped UA / headerless non-browser → `anonymous-bot`\n * 5. everything browser-shaped → `human`\n *\n * `depth: 'classify'` skips step 2 (no body decode, no header sniffing\n * beyond UA) — that is the zero-friction setting.\n */\nexport function classify(\n request: CloudFrontRequest,\n host: string,\n depth: 'classify' | 'authenticate' | 'authorize'\n): Classification {\n const userAgent = getHeader(request, 'user-agent') ?? '';\n const flat = flattenHeaders(request.headers);\n\n // 1. Native AstraSync credentials.\n const credentials = extractCredentials(flat, {});\n if (credentials.astraId || credentials.apiKey || credentials.jwt) {\n return { tier: 'verified', protocol: 'x-astra' };\n }\n\n // Workstream D — the key-format prefix + platform-signal headers are stronger,\n // less-spoofable platform signals than the UA. Compute once (cheap string\n // work) and feed every fingerprint call below.\n const { apiKeyFormat, platformHeaders } = sanitizeHeaders(flat);\n const detectionInput = { userAgent, apiKeyFormat, platformHeaders };\n\n // 2. Commerce-protocol credentials (skipped entirely at classify depth).\n if (depth !== 'classify') {\n const commerce = detectCommerce(request, host, decodeBody(request));\n if (commerce) {\n return {\n tier: 'identified',\n protocol: commerce.protocol,\n artifacts: commerce.artifacts,\n evidence: commerce.evidence,\n fingerprint: detectPlatformFingerprint(detectionInput),\n };\n }\n }\n\n // 3. Platform-agent fingerprint (UA, key-format, or platform header).\n const fingerprint = detectPlatformFingerprint(detectionInput);\n if (fingerprint) {\n return { tier: 'identified', protocol: 'platform-ua', fingerprint };\n }\n\n // 4. Bot-shaped: CLI/library UA, no UA at all, or any non-browser UA —\n // humans arrive with Mozilla-family UAs; everything else is automation.\n if (!userAgent || BOT_UA_PATTERN.test(userAgent) || !BROWSER_UA_PATTERN.test(userAgent)) {\n return { tier: 'anonymous-bot', evidence: { userAgent: userAgent || '(none)' } };\n }\n\n // 4b. Claims to be a browser but is missing every browser fetch-metadata\n // header — headless/spoofed. `sec-fetch-mode` is sent by all evergreen\n // browsers; its absence on a Mozilla UA is a strong bot signal.\n if (!getHeader(request, 'sec-fetch-mode') && !getHeader(request, 'accept-language')) {\n return { tier: 'anonymous-bot', evidence: { userAgent, spoofedBrowser: true } };\n }\n\n // 5. Human traffic — the shell gets out of the way.\n return { tier: 'human' };\n}\n\n/** Exported for tests. */\nexport const _patterns = { STATIC_EXTENSIONS, BOT_UA_PATTERN, BROWSER_UA_PATTERN };\n","/**\n * Crypto-free commerce-protocol detection (§7.4.2 detection order).\n *\n * The edge only SNIFFS — header shapes, compact-JWT patterns, body field\n * names — and packages what it saw as raw `CommerceArtifactsPayload` for\n * verify-access, whose Step 0.5 pipeline does the authoritative parsing and\n * cryptographic verification server-side. Nothing here imports jose/sd-jwt/\n * signature libraries: detection must stay cheap enough to run on every\n * non-human request, and verification must happen exactly once, in the\n * canonical sink, where it is recorded.\n */\n\nimport type { CloudFrontRequest } from 'aws-lambda';\nimport type { CommerceArtifactsPayload } from '@astrasyncai/verification-gateway';\nimport { flattenHeaders, getHeader, requestUrl, type DecodedBody } from './cf';\n\nexport interface DetectedCommerce {\n /** Detection-order label: rfc9421-agent-pay | rfc9421-tap | rfc9421-web-bot-auth | vi-sd-jwt | ap2 | acp | ucp | mpp | x402 */\n protocol: string;\n artifacts: CommerceArtifactsPayload;\n /** Light detection detail for beacons/audit — never secrets or full credentials. */\n evidence: Record<string, unknown>;\n}\n\n/** Compact SD-JWT: JWT + `~`-separated disclosures. */\nconst SD_JWT_PATTERN = /^eyJ[\\w-]+\\.[\\w-]*\\.[\\w-]+~/;\n\n/** Decode a JWT header segment without verifying anything. */\nfunction sniffJwtHeader(compact: string): Record<string, unknown> | undefined {\n try {\n const seg = compact.split('.')[0];\n return JSON.parse(Buffer.from(seg, 'base64url').toString('utf8')) as Record<string, unknown>;\n } catch {\n return undefined;\n }\n}\n\nfunction parseJsonBody(body: DecodedBody): Record<string, unknown> | undefined {\n if (!body.text) return undefined;\n try {\n const parsed: unknown = JSON.parse(body.text);\n return typeof parsed === 'object' && parsed !== null\n ? (parsed as Record<string, unknown>)\n : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * §7.4.2 order (after the X-Astra native check, which classify.ts owns):\n * SD-JWT/VI → RFC 9421 (Agent Pay / TAP / Web Bot Auth by kid namespace or\n * Signature-Agent) → ACP HMAC → UCP session → AP2 body triple → MPP → x402.\n * Returns undefined when nothing commerce-shaped is on the wire.\n *\n * When the body was truncated by CloudFront (`inputTruncated`), body-bound\n * artifacts still forward what arrived but carry a `bodyTruncated` evidence\n * flag — the backend downgrades those signatures rather than hard-failing.\n */\nexport function detectCommerce(\n request: CloudFrontRequest,\n host: string,\n body: DecodedBody\n): DetectedCommerce | undefined {\n const url = requestUrl(request, host);\n const method = request.method;\n const headers = flattenHeaders(request.headers);\n const jsonBody = parseJsonBody(body);\n const truncated = body.truncated ? { bodyTruncated: true } : {};\n\n // --- VI / SD-JWT credential in a header ------------------------------\n const authHeader = getHeader(request, 'authorization');\n const bearer = authHeader?.replace(/^Bearer\\s+/i, '');\n const mandateHeader =\n getHeader(request, 'x-payment-mandate') ?? getHeader(request, 'payment-mandate');\n const sdJwtCandidate = [mandateHeader, bearer].find((v) => v && SD_JWT_PATTERN.test(v));\n if (sdJwtCandidate) {\n const typ = sniffJwtHeader(sdJwtCandidate)?.typ;\n return {\n protocol: 'vi-sd-jwt',\n artifacts: { viSdJwt: sdJwtCandidate },\n evidence: { typ, ...truncated },\n };\n }\n\n // --- RFC 9421 (Agent Pay / TAP / Web Bot Auth) ------------------------\n const signature = getHeader(request, 'signature');\n const signatureInput = getHeader(request, 'signature-input');\n if (signature && signatureInput) {\n const signatureAgent = getHeader(request, 'signature-agent');\n const registryHint = getHeader(request, 'x-agent-registry');\n const lowerInput = signatureInput.toLowerCase();\n const protocol = signatureAgent\n ? 'rfc9421-web-bot-auth'\n : registryHint?.toLowerCase().includes('visa') || lowerInput.includes('visa')\n ? 'rfc9421-tap'\n : registryHint?.toLowerCase().includes('mastercard') || lowerInput.includes('mastercard')\n ? 'rfc9421-agent-pay'\n : 'rfc9421';\n return {\n protocol,\n artifacts: {\n rfc9421: { headers, method, url, ...(body.text && { body: body.text }) },\n },\n evidence: {\n ...(signatureAgent && { signatureAgent }),\n ...(registryHint && { registryHint }),\n keyid: signatureInput.match(/keyid=\"([^\"]+)\"/)?.[1],\n ...truncated,\n },\n };\n }\n\n // --- ACP (HMAC-signed webhook/checkout: signature WITHOUT signature-input)\n const acpTimestamp = getHeader(request, 'timestamp');\n if (signature && !signatureInput && (acpTimestamp || getHeader(request, 'idempotency-key'))) {\n return {\n protocol: 'acp',\n artifacts: {\n acpRequest: {\n method,\n url,\n headers,\n ...(body.text && { rawBody: body.text }),\n ...(jsonBody && { body: jsonBody }),\n },\n },\n evidence: { hasTimestamp: Boolean(acpTimestamp), ...truncated },\n };\n }\n\n // --- UCP checkout session ---------------------------------------------\n if (/\\/checkout[-_]sessions(\\/|$)/.test(request.uri)) {\n return {\n protocol: 'ucp',\n artifacts: { ucpRequest: { method, url, ...(jsonBody && { body: jsonBody }) } },\n evidence: { path: request.uri, ...truncated },\n };\n }\n\n // --- AP2 mandate triple in the body -----------------------------------\n if (jsonBody) {\n const intent = jsonBody.intent_mandate ?? jsonBody.intentMandate;\n const cart = jsonBody.cart_mandate ?? jsonBody.cartMandate;\n const payment = jsonBody.payment_mandate ?? jsonBody.paymentMandate;\n if (typeof intent === 'string' || typeof cart === 'string' || typeof payment === 'string') {\n return {\n protocol: 'ap2',\n artifacts: {\n ap2Mandates: {\n ...(typeof intent === 'string' && { intent }),\n ...(typeof cart === 'string' && { cart }),\n ...(typeof payment === 'string' && { payment }),\n },\n },\n evidence: {\n mandatesPresent: [\n typeof intent === 'string' && 'intent',\n typeof cart === 'string' && 'cart',\n typeof payment === 'string' && 'payment',\n ].filter(Boolean),\n ...truncated,\n },\n };\n }\n }\n\n // --- MPP (`Authorization: Payment <credential>`) -----------------------\n if (authHeader && /^Payment\\s+/i.test(authHeader)) {\n return {\n protocol: 'mpp',\n artifacts: {\n mppRequest: {\n method,\n url,\n headers,\n ...(body.text && { rawBody: body.text }),\n ...(jsonBody && { body: jsonBody }),\n },\n },\n evidence: truncated,\n };\n }\n\n // --- x402 (X-PAYMENT header) ------------------------------------------\n if (getHeader(request, 'x-payment')) {\n return {\n protocol: 'x402',\n artifacts: {\n x402Request: { method, url, headers, ...(jsonBody && { body: jsonBody }) },\n },\n evidence: truncated,\n };\n }\n\n return undefined;\n}\n","/**\n * Depth-controlled verification per tier. The edge never verifies\n * credentials itself — it forwards what it detected to verify-access, the\n * canonical sole verification sink (backend Step 0.5 runs the commerce\n * pipeline, records events, provisions orphans, persists sessions). What\n * the depth setting controls is how much the edge LOOKS and how much it\n * FORWARDS:\n *\n * classify — what is it? UA tiers only. Zero verify-access calls;\n * beacons only.\n * authenticate — who is it? + protocol detection. X-Astra agents get a\n * verify-access identity check; commerce/platform traffic\n * is beaconed with protocol + evidence (no signature\n * verification anywhere).\n * authorize — are they allowed? + artifact forwarding. X-Astra verify\n * calls carry commerceArtifacts; identified traffic gets an\n * anonymous verify-access call with artifacts +\n * callerMetadata so the backend cryptographically proves\n * and policy-evaluates once.\n */\n\nimport type { CloudFrontRequest } from 'aws-lambda';\nimport { verify, extractCredentials } from '@astrasyncai/verification-gateway/verify';\nimport { resolveHttpPdlss } from '@astrasyncai/verification-gateway/http-pdlss';\nimport type { GatewayConfig, VerificationRequest } from '@astrasyncai/verification-gateway';\nimport { flattenHeaders, getHeader, buildObservedMetadata } from './cf';\nimport { withBudget, emitBeacon, sampled } from './events';\nimport type { Classification, EdgeAdapterOptions, EffectivePolicy, TierOutcome } from './types';\n\nfunction baseOutcome(classification: Classification): TierOutcome {\n return {\n tier: classification.tier,\n decision: 'allow',\n reasons: [],\n protocol: classification.protocol,\n };\n}\n\nfunction buildVerificationRequest(\n options: EdgeAdapterOptions,\n request: CloudFrontRequest,\n classification: Classification,\n includeArtifacts: boolean\n): VerificationRequest {\n const headers = flattenHeaders(request.headers);\n const clientIp = getHeader(request, 'cloudfront-viewer-address')?.split(':')[0];\n // verify-access REQUIRES a purpose. Resolve via the shared §4.6 chain:\n // the agent's X-Astra-Purpose/-Action declarations, else the pinned\n // method table (GET → data/data.read etc.) — a transport verb never\n // travels as a PDLSS action.\n const pdlss = resolveHttpPdlss({\n method: request.method,\n astraPurpose: getHeader(request, 'x-astra-purpose'),\n astraAction: getHeader(request, 'x-astra-action'),\n });\n return {\n credentials: extractCredentials(headers, {}),\n purpose: pdlss.purpose,\n action: pdlss.action,\n resource: request.uri,\n createSession: true,\n counterpartyType: 'website',\n counterpartyUrl: options.counterpartyUrl,\n callerMetadata: {\n sourceIp: clientIp,\n userAgent: getHeader(request, 'user-agent'),\n referer: getHeader(request, 'referer'),\n host: getHeader(request, 'host'),\n // Workstream D — the full sanitised header + connection capture. The\n // backend stores it fire-and-forget (zero decision latency) and uses the\n // key-format / platform headers for attribution.\n observedMetadata: buildObservedMetadata(request),\n },\n ...(includeArtifacts &&\n classification.artifacts && { commerceArtifacts: classification.artifacts }),\n };\n}\n\nfunction outcomeFromVerifyResult(\n classification: Classification,\n result: Awaited<ReturnType<typeof verify>>\n): TierOutcome {\n const outcome = baseOutcome(classification);\n outcome.sessionId = (result as { sessionId?: string }).sessionId;\n outcome.correlationId = result.correlationId;\n outcome.guidance = result.guidance;\n\n if (result.requiresStepUp || result.stepUpApproval) {\n outcome.decision = 'manual_review';\n outcome.reasons = ['step-up approval required'];\n outcome.stepUp = {\n pollUrl: (result.stepUpApproval as { pollUrl?: string } | undefined)?.pollUrl,\n expiresAt: (result.stepUpApproval as { expiresAt?: string } | undefined)?.expiresAt,\n };\n return outcome;\n }\n\n if (!result.identityVerified || !result.policyAllowed) {\n outcome.decision = 'deny';\n outcome.reasons = result.denialReasons?.length\n ? result.denialReasons\n : [!result.identityVerified ? 'identity not verified' : 'denied by access policy'];\n return outcome;\n }\n\n outcome.asat = {\n trustScore: result.agent?.trustScore,\n agentId: result.agent?.astraId,\n developerId: result.developer?.astradId,\n accessLevel: result.accessLevel,\n };\n return outcome;\n}\n\n/**\n * Run the tier-appropriate verification/telemetry for one classified\n * request. NEVER throws; on any failure returns an `allow` outcome with the\n * failure recorded in `reasons` (infra failures fail open — a presented\n * credential the BACKEND rejected is a deny, but an unreachable backend is\n * not a rejection).\n */\nexport async function runTier(\n options: EdgeAdapterOptions,\n request: CloudFrontRequest,\n classification: Classification,\n policy: EffectivePolicy,\n apiKey: string | undefined,\n sampling: { anonymousBeaconRate: number }\n): Promise<TierOutcome> {\n const budgets = options.budgets ?? {};\n const beaconMs = budgets.beaconMs ?? 300;\n const verifyMs =\n policy.mode === 'enforce'\n ? (budgets.verifyEnforceMs ?? 3000)\n : (budgets.verifyObserveMs ?? 1500);\n\n const gatewayConfig: GatewayConfig = {\n apiBaseUrl: options.apiBaseUrl,\n ...(apiKey && { apiKey }),\n counterpartyId: options.counterpartyId,\n // The shell handles its own error semantics; init self-probes don't\n // belong in a per-request edge function.\n disableInitChecks: true,\n } as GatewayConfig;\n\n try {\n switch (classification.tier) {\n case 'verified': {\n // classify depth = zero API calls, even for credentialed agents.\n if (policy.depth === 'classify') {\n return baseOutcome(classification);\n }\n const result = await withBudget(\n verify(\n gatewayConfig,\n buildVerificationRequest(options, request, classification, policy.depth === 'authorize')\n ),\n verifyMs\n );\n if (!result) {\n const outcome = baseOutcome(classification);\n outcome.reasons = ['verify-access unreachable within budget — fail-open'];\n return outcome;\n }\n return outcomeFromVerifyResult(classification, result);\n }\n\n case 'identified': {\n if (policy.depth === 'authorize' && classification.artifacts) {\n // Anonymous verify-access call WITH artifacts: the backend runs\n // the commerce pipeline, records unverified events, and\n // provisions orphan platform agents — one sink, recorded once.\n const result = await withBudget(\n verify(gatewayConfig, buildVerificationRequest(options, request, classification, true)),\n verifyMs\n );\n if (!result) {\n const outcome = baseOutcome(classification);\n outcome.reasons = ['verify-access unreachable within budget — fail-open'];\n return outcome;\n }\n return outcomeFromVerifyResult(classification, result);\n }\n // classify/credentials depth (or no artifacts): lightweight beacon.\n await emitBeacon(options, request, classification, beaconMs);\n const outcome = baseOutcome(classification);\n if (policy.mode === 'enforce' && policy.depth !== 'classify') {\n // Unregistered agent on an enforced path: deny with guidance.\n outcome.decision = 'deny';\n outcome.reasons = ['agent is not registered with AstraSync'];\n }\n return outcome;\n }\n\n case 'anonymous-bot': {\n if (sampled(sampling.anonymousBeaconRate, options.random)) {\n await emitBeacon(\n options,\n request,\n { ...classification, protocol: classification.protocol ?? 'ua-heuristic' },\n beaconMs\n );\n }\n // Anonymous bots pass through even in enforce mode — enforce gates\n // AGENT traffic; generic bot management stays the CDN/WAF's job\n // (mixed-traffic classification §5.1.7 is the enforce-mode\n // prerequisite tracked as §33.5 #9).\n return baseOutcome(classification);\n }\n\n default:\n return baseOutcome(classification);\n }\n } catch (err) {\n const outcome = baseOutcome(classification);\n outcome.reasons = [\n `edge pipeline error — fail-open: ${err instanceof Error ? err.message : String(err)}`,\n ];\n return outcome;\n }\n}\n","/**\n * Time-budgeted event emission.\n *\n * Lambda@Edge has no `waitUntil` — anything not awaited before the handler\n * returns may never run (the container freezes). So every network call is\n * awaited under an explicit budget and DROPPED on timeout: latency to the\n * customer's traffic is bounded, and a slow AstraSync API costs telemetry,\n * never page loads.\n */\n\nimport { reportUnregisteredAttempt } from '@astrasyncai/verification-gateway/verify';\nimport type { CloudFrontRequest } from 'aws-lambda';\nimport { getHeader, buildObservedMetadata } from './cf';\nimport type { Classification, EdgeAdapterOptions } from './types';\n\n/** Await `promise` for at most `ms`; resolve `undefined` on timeout/error. */\nexport async function withBudget<T>(promise: Promise<T>, ms: number): Promise<T | undefined> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n promise,\n new Promise<undefined>((resolve) => {\n timer = setTimeout(() => resolve(undefined), ms);\n }),\n ]);\n } catch {\n return undefined;\n } finally {\n if (timer) clearTimeout(timer);\n }\n}\n\n/**\n * Emit an unregistered-attempt beacon for `identified` (non-crypto depths)\n * and sampled `anonymous-bot` traffic. Unauthenticated by design\n * (CSRF-exempt on the backend); the backend fingerprints the UA and\n * find-or-provisions orphan platform agents server-side.\n */\nexport async function emitBeacon(\n options: EdgeAdapterOptions,\n request: CloudFrontRequest,\n classification: Classification,\n budgetMs: number\n): Promise<void> {\n const clientIp = getHeader(request, 'cloudfront-viewer-address')?.split(':')[0];\n await withBudget(\n reportUnregisteredAttempt(\n { apiBaseUrl: options.apiBaseUrl, counterpartyId: options.counterpartyId },\n {\n counterpartyUrl: options.counterpartyUrl,\n counterpartyType: 'website',\n sourceIp: clientIp,\n userAgent: getHeader(request, 'user-agent'),\n requestPath: request.uri,\n requestMethod: request.method,\n // 3.9.0 evidence fields (backend persists them into eventData).\n protocol: classification.protocol,\n evidence: classification.evidence,\n // Workstream D — full sanitised header + connection capture for the\n // unregistered caller (fire-and-forget, so free).\n observedMetadata: buildObservedMetadata(request),\n }\n ),\n budgetMs\n );\n}\n\n/** Deterministic-in-tests sampling gate. */\nexport function sampled(rate: number, random: () => number = Math.random): boolean {\n if (rate <= 0) return false;\n if (rate >= 1) return true;\n return random() < rate;\n}\n","/**\n * §7.4.6 — protocol-specific machine-readable messaging for enforce-mode\n * responses. Agents (not humans) consume these bodies: stable error codes,\n * the failure list, and registration guidance with real URLs.\n */\n\nimport type { TierOutcome } from './types';\n\nconst PROTOCOL_MESSAGES: Record<string, string> = {\n 'vi-sd-jwt':\n 'This request presented Visa Intelligent Commerce credentials, but the agent is not verified with AstraSync. Register the agent to pair your VI credentials with a verified identity and mandate.',\n 'rfc9421-agent-pay':\n 'This request carried a Mastercard Agent Pay signature, but the agent is not verified with AstraSync. Register the agent to pair Agent Pay credentials with a verified identity and mandate.',\n 'rfc9421-tap':\n 'This request carried a Visa TAP signature, but the agent is not verified with AstraSync. Register the agent to pair TAP credentials with a verified identity and mandate.',\n 'rfc9421-web-bot-auth':\n 'This request carried a Web Bot Auth signature, but the agent is not verified with AstraSync. Register the agent to link your signed-agent identity to a verified principal.',\n rfc9421:\n 'This request carried an RFC 9421 HTTP message signature, but the agent is not verified with AstraSync. Register the agent to link the signing identity to a verified principal.',\n acp: 'This request used the Agentic Commerce Protocol, but the agent is not verified with AstraSync. Register the agent to transact on this property.',\n ucp: 'This request used a UCP checkout session, but the agent is not verified with AstraSync. Register the agent to transact on this property.',\n ap2: 'This request presented AP2 mandates, but the agent is not verified with AstraSync. Register the agent to pair your mandates with a verified identity.',\n mpp: 'This request presented MPP payment credentials, but the agent is not verified with AstraSync. Register the agent to transact on this property.',\n x402: 'This request used x402 payment headers, but the agent is not verified with AstraSync. Register the agent to transact on this property.',\n 'platform-ua':\n 'This traffic was identified as a platform AI agent without AstraSync credentials. Register the agent to access this property with a verified identity.',\n 'x-astra':\n 'The presented AstraSync credentials failed verification or were denied by this property’s access policy.',\n};\n\nexport interface DenyBody {\n success: false;\n error: {\n code: 'UNAUTHORIZED' | 'INSUFFICIENT_ACCESS';\n message: string;\n protocol?: string;\n failures: string[];\n correlationId?: string;\n guidance: {\n message?: string;\n registrationUrl?: string;\n documentationUrl?: string;\n };\n };\n}\n\nexport function buildDenyBody(outcome: TierOutcome, apiBaseUrl: string): DenyBody {\n const origin = apiBaseUrl.replace(/\\/api\\/?$/, '');\n const protocolMessage =\n (outcome.protocol && PROTOCOL_MESSAGES[outcome.protocol]) ??\n 'This request could not be verified by AstraSync.';\n return {\n success: false,\n error: {\n // UNAUTHORIZED = identity failed (register/re-authenticate helps);\n // INSUFFICIENT_ACCESS = identity fine, policy denied (fix the mandate).\n code: outcome.tier === 'verified' ? 'INSUFFICIENT_ACCESS' : 'UNAUTHORIZED',\n message: protocolMessage,\n ...(outcome.protocol && { protocol: outcome.protocol }),\n failures: outcome.reasons,\n ...(outcome.correlationId && { correlationId: outcome.correlationId }),\n guidance: {\n // `||` not `??`: verify() fills these with EMPTY STRINGS when the\n // well-known cache is cold — empty means \"use the fallback\".\n message:\n outcome.guidance?.message ||\n 'Register this agent with AstraSync to receive verifiable credentials.',\n registrationUrl: outcome.guidance?.registrationUrl || `${origin}/register`,\n documentationUrl: outcome.guidance?.documentationUrl || `${origin}/docs/edge-adapter`,\n },\n },\n };\n}\n\nexport interface ManualReviewBody {\n success: false;\n status: 'manual_review';\n message: string;\n stepUpApproval?: { pollUrl?: string; expiresAt?: string };\n correlationId?: string;\n}\n\nexport function buildManualReviewBody(outcome: TierOutcome): ManualReviewBody {\n return {\n success: false,\n status: 'manual_review',\n message:\n 'This action requires human approval. Poll the step-up approval endpoint and retry once approved.',\n ...(outcome.stepUp && { stepUpApproval: outcome.stepUp }),\n ...(outcome.correlationId && { correlationId: outcome.correlationId }),\n };\n}\n","/**\n * Decision → CloudFront result wiring (§7.4 enforcement, §7.4.7 ASAT\n * headers). Executed in BOTH modes so observe mode records exactly what\n * enforce mode would have done — but only enforce mode is allowed to return\n * anything other than the origin-bound request.\n */\n\nimport type { CloudFrontRequest, CloudFrontRequestResult } from 'aws-lambda';\nimport { jsonResponse, setHeader, stripHeadersByPrefix } from './cf';\nimport { buildDenyBody, buildManualReviewBody } from './messaging';\nimport type { EdgeAdapterOptions, EffectivePolicy, TierOutcome } from './types';\n\n/** §7.4.7 ASAT trust-attestation headers injected on allowed requests. */\nexport const ASAT_HEADERS = {\n trustScore: 'X-AstraSync-Trust-Score',\n agentId: 'X-AstraSync-Agent-Id',\n developerId: 'X-AstraSync-Developer-Id',\n permissions: 'X-AstraSync-Permissions',\n observedDecision: 'X-AstraSync-Observed-Decision',\n} as const;\n\n/**\n * Apply the outcome. Always strips inbound spoofed `x-astrasync-*` headers\n * first (both modes — trust headers must only ever originate here).\n *\n * Observe mode: returns the request untouched apart from header hygiene and\n * an `X-AstraSync-Observed-Decision` stamp for origin-side log correlation.\n *\n * Enforce mode: ALLOW → request + ASAT headers; DENY → 403 machine-readable\n * body; MANUAL_REVIEW → 202 with step-up poll info.\n */\nexport function applyOutcome(\n options: EdgeAdapterOptions,\n request: CloudFrontRequest,\n outcome: TierOutcome,\n policy: EffectivePolicy\n): CloudFrontRequestResult {\n stripHeadersByPrefix(request, 'x-astrasync-');\n\n if (policy.mode !== 'enforce') {\n // Shadow stamp: what enforce WOULD have done, visible in origin logs.\n if (outcome.decision !== 'allow') {\n setHeader(request, ASAT_HEADERS.observedDecision, outcome.decision);\n }\n injectAsat(request, outcome);\n return request;\n }\n\n switch (outcome.decision) {\n case 'deny':\n return jsonResponse(403, buildDenyBody(outcome, options.apiBaseUrl));\n case 'manual_review':\n return jsonResponse(202, buildManualReviewBody(outcome), {\n 'Retry-After': '30',\n });\n default:\n injectAsat(request, outcome);\n return request;\n }\n}\n\nfunction injectAsat(request: CloudFrontRequest, outcome: TierOutcome): void {\n if (!outcome.asat) return;\n const { trustScore, agentId, developerId, accessLevel } = outcome.asat;\n if (trustScore !== undefined) {\n setHeader(request, ASAT_HEADERS.trustScore, String(trustScore));\n }\n if (agentId) setHeader(request, ASAT_HEADERS.agentId, agentId);\n if (developerId) setHeader(request, ASAT_HEADERS.developerId, developerId);\n if (accessLevel) setHeader(request, ASAT_HEADERS.permissions, accessLevel);\n}\n","/**\n * Commerce Shield edge shell — Lambda@Edge origin-request handler factory\n * (§7.4 / §33.5 #1).\n *\n * Composition per request:\n * config (cached) → path policy → classify → tier pipeline → apply outcome\n *\n * THE invariant (tested): outside enforce mode the handler ALWAYS returns\n * the origin-bound request — any error, timeout, or misconfiguration\n * results in pass-through. A top-level catch guarantees it even against\n * bugs in the shell itself.\n */\n\nimport type { CloudFrontRequestEvent, CloudFrontRequestResult } from 'aws-lambda';\nimport { getRequest, getHeader, normalizeOriginHost } from './cf';\nimport { resolveApiKey, loadEdgeConfig } from './config';\nimport { classify, resolvePolicy } from './classify';\nimport { runTier } from './pipeline';\nimport { applyOutcome } from './enforce';\nimport type { EdgeAdapterOptions } from './types';\n\nexport type EdgeHandler = (event: CloudFrontRequestEvent) => Promise<CloudFrontRequestResult>;\n\nexport function createEdgeHandler(options: EdgeAdapterOptions): EdgeHandler {\n const log =\n options.log ??\n ((level, message, data) => {\n // Lambda@Edge writes console output to the regional CloudWatch log\n // group of whichever edge location served the request.\n console[level === 'info' ? 'log' : level](`[astrasync-edge] ${message}`, data ?? '');\n });\n\n return async function handler(event) {\n const request = getRequest(event);\n try {\n // Own the Host rewrite (Workstream D): the CloudFront-Viewer-* capture\n // requires an origin-request policy that also forwards the viewer Host,\n // so the shell restores origin-domain Host + X-Forwarded-Host itself.\n // Runs first so EVERY origin-bound return (skip / human / allow) is\n // correctly routed.\n normalizeOriginHost(request);\n\n const apiKey = await resolveApiKey({ ...options, log });\n const config = await loadEdgeConfig(options, apiKey);\n const policy = resolvePolicy(config, request.uri);\n\n // Skipped paths (static assets / skip rules): untouched, zero work.\n if (policy.skip) return request;\n\n const host =\n getHeader(request, 'x-forwarded-host') ??\n getHeader(request, 'host') ??\n new URL(options.counterpartyUrl).host;\n\n const classification = classify(request, host, policy.depth);\n\n // Humans: get out of the way. No calls, no logs, no header changes\n // beyond nothing at all.\n if (classification.tier === 'human') return request;\n\n const outcome = await runTier(\n { ...options, log },\n request,\n classification,\n policy,\n apiKey,\n config.sampling\n );\n\n if (outcome.decision !== 'allow') {\n log(\n 'info',\n policy.mode === 'enforce'\n ? `${outcome.decision.toUpperCase()}: ${request.method} ${request.uri}`\n : `[OBSERVE] would have been ${outcome.decision.toUpperCase()}: ${request.method} ${request.uri}`,\n { tier: outcome.tier, protocol: outcome.protocol, reasons: outcome.reasons }\n );\n }\n\n return applyOutcome(options, request, outcome, policy);\n } catch (err) {\n // Fail-open, unconditionally: the shell must never take the site down.\n log('error', 'edge handler error — passing request through', {\n error: err instanceof Error ? err.message : String(err),\n });\n return request;\n }\n };\n}\n"],"mappings":"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","names":["import_edge_config","import_metadata_capture","import_verify","import_verify","outcome"]}
package/dist/index.mjs CHANGED
@@ -1,4 +1,7 @@
1
1
  // src/cf.ts
2
+ import {
3
+ sanitizeHeaders
4
+ } from "@astrasyncai/verification-gateway/metadata-capture";
2
5
  function getRequest(event) {
3
6
  return event.Records[0].cf.request;
4
7
  }
@@ -19,6 +22,55 @@ function decodeBody(request) {
19
22
  const text = body.encoding === "base64" ? Buffer.from(body.data, "base64").toString("utf8") : body.data;
20
23
  return { text, truncated: body.inputTruncated === true };
21
24
  }
25
+ function collectConnectionMetadata(request) {
26
+ const out = {};
27
+ const put = (key, headerName, transform) => {
28
+ const v = getHeader(request, headerName);
29
+ if (v) out[key] = transform ? transform(v) : v;
30
+ };
31
+ const addr = getHeader(request, "cloudfront-viewer-address");
32
+ if (addr) out.ip = addr.replace(/:\d+$/, "");
33
+ put("country", "cloudfront-viewer-country");
34
+ put("countryName", "cloudfront-viewer-country-name");
35
+ put("region", "cloudfront-viewer-country-region");
36
+ put("city", "cloudfront-viewer-city");
37
+ put("asn", "cloudfront-viewer-asn");
38
+ put("tlsVersion", "cloudfront-viewer-tls");
39
+ put("httpVersion", "cloudfront-viewer-http-version");
40
+ const devices = [];
41
+ for (const [label, header] of [
42
+ ["desktop", "cloudfront-is-desktop-viewer"],
43
+ ["mobile", "cloudfront-is-mobile-viewer"],
44
+ ["tablet", "cloudfront-is-tablet-viewer"],
45
+ ["smarttv", "cloudfront-is-smarttv-viewer"]
46
+ ]) {
47
+ if (getHeader(request, header) === "true") devices.push(label);
48
+ }
49
+ if (devices.length) out.device = devices.join(",");
50
+ return out;
51
+ }
52
+ function buildObservedMetadata(request) {
53
+ const { headers, apiKeyFormat, platformHeaders } = sanitizeHeaders(
54
+ flattenHeaders(request.headers)
55
+ );
56
+ const connection = collectConnectionMetadata(request);
57
+ return {
58
+ headers,
59
+ ...apiKeyFormat && { apiKeyFormat },
60
+ ...platformHeaders && { platformHeaders },
61
+ ...Object.keys(connection).length > 0 && { connection }
62
+ };
63
+ }
64
+ function normalizeOriginHost(request) {
65
+ const originDomain = request.origin?.custom?.domainName ?? request.origin?.s3?.domainName ?? void 0;
66
+ if (!originDomain) return;
67
+ const currentHost = getHeader(request, "host");
68
+ if (!currentHost || currentHost === originDomain) return;
69
+ if (!getHeader(request, "x-forwarded-host")) {
70
+ setHeader(request, "X-Forwarded-Host", currentHost);
71
+ }
72
+ setHeader(request, "Host", originDomain);
73
+ }
22
74
  function requestUrl(request, host) {
23
75
  const qs = request.querystring ? `?${request.querystring}` : "";
24
76
  return `https://${host}${request.uri}${qs}`;
@@ -96,6 +148,7 @@ function _resetConfigCaches() {
96
148
  import { extractCredentials } from "@astrasyncai/verification-gateway/verify";
97
149
  import { detectPlatformFingerprint } from "@astrasyncai/verification-gateway/platform-signatures";
98
150
  import { matchEdgePathRule } from "@astrasyncai/verification-gateway/edge-config";
151
+ import { sanitizeHeaders as sanitizeHeaders2 } from "@astrasyncai/verification-gateway/metadata-capture";
99
152
 
100
153
  // src/detect.ts
101
154
  var SD_JWT_PATTERN = /^eyJ[\w-]+\.[\w-]*\.[\w-]+~/;
@@ -247,10 +300,13 @@ function resolvePolicy(config, path) {
247
300
  }
248
301
  function classify(request, host, depth) {
249
302
  const userAgent = getHeader(request, "user-agent") ?? "";
250
- const credentials = extractCredentials(flattenHeaders(request.headers), {});
303
+ const flat = flattenHeaders(request.headers);
304
+ const credentials = extractCredentials(flat, {});
251
305
  if (credentials.astraId || credentials.apiKey || credentials.jwt) {
252
306
  return { tier: "verified", protocol: "x-astra" };
253
307
  }
308
+ const { apiKeyFormat, platformHeaders } = sanitizeHeaders2(flat);
309
+ const detectionInput = { userAgent, apiKeyFormat, platformHeaders };
254
310
  if (depth !== "classify") {
255
311
  const commerce = detectCommerce(request, host, decodeBody(request));
256
312
  if (commerce) {
@@ -259,11 +315,11 @@ function classify(request, host, depth) {
259
315
  protocol: commerce.protocol,
260
316
  artifacts: commerce.artifacts,
261
317
  evidence: commerce.evidence,
262
- fingerprint: detectPlatformFingerprint({ userAgent })
318
+ fingerprint: detectPlatformFingerprint(detectionInput)
263
319
  };
264
320
  }
265
321
  }
266
- const fingerprint = detectPlatformFingerprint({ userAgent });
322
+ const fingerprint = detectPlatformFingerprint(detectionInput);
267
323
  if (fingerprint) {
268
324
  return { tier: "identified", protocol: "platform-ua", fingerprint };
269
325
  }
@@ -311,7 +367,10 @@ async function emitBeacon(options, request, classification, budgetMs) {
311
367
  requestMethod: request.method,
312
368
  // 3.9.0 evidence fields (backend persists them into eventData).
313
369
  protocol: classification.protocol,
314
- evidence: classification.evidence
370
+ evidence: classification.evidence,
371
+ // Workstream D — full sanitised header + connection capture for the
372
+ // unregistered caller (fire-and-forget, so free).
373
+ observedMetadata: buildObservedMetadata(request)
315
374
  }
316
375
  ),
317
376
  budgetMs
@@ -352,7 +411,11 @@ function buildVerificationRequest(options, request, classification, includeArtif
352
411
  sourceIp: clientIp,
353
412
  userAgent: getHeader(request, "user-agent"),
354
413
  referer: getHeader(request, "referer"),
355
- host: getHeader(request, "host")
414
+ host: getHeader(request, "host"),
415
+ // Workstream D — the full sanitised header + connection capture. The
416
+ // backend stores it fire-and-forget (zero decision latency) and uses the
417
+ // key-format / platform headers for attribution.
418
+ observedMetadata: buildObservedMetadata(request)
356
419
  },
357
420
  ...includeArtifacts && classification.artifacts && { commerceArtifacts: classification.artifacts }
358
421
  };
@@ -556,6 +619,7 @@ function createEdgeHandler(options) {
556
619
  return async function handler(event) {
557
620
  const request = getRequest(event);
558
621
  try {
622
+ normalizeOriginHost(request);
559
623
  const apiKey = await resolveApiKey({ ...options, log });
560
624
  const config = await loadEdgeConfig(options, apiKey);
561
625
  const policy = resolvePolicy(config, request.uri);
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cf.ts","../src/config.ts","../src/classify.ts","../src/detect.ts","../src/pipeline.ts","../src/events.ts","../src/messaging.ts","../src/enforce.ts","../src/handler.ts"],"sourcesContent":["/**\n * CloudFront origin-request event helpers. The Lambda@Edge event shape puts\n * headers in `{ key, value }[]` arrays keyed by LOWERCASED header name, and\n * (with IncludeBody) the body arrives base64-encoded with an\n * `inputTruncated` flag when it exceeded CloudFront's ~1MB exposure cap.\n */\n\nimport type {\n CloudFrontRequest,\n CloudFrontRequestEvent,\n CloudFrontHeaders,\n CloudFrontResultResponse,\n} from 'aws-lambda';\n\nexport function getRequest(event: CloudFrontRequestEvent): CloudFrontRequest {\n return event.Records[0].cf.request;\n}\n\n/** First value of a header (lowercase name), or undefined. */\nexport function getHeader(request: CloudFrontRequest, name: string): string | undefined {\n return request.headers[name.toLowerCase()]?.[0]?.value;\n}\n\n/**\n * Flatten CloudFront headers into the `Record<string, string | string[]>`\n * shape the SDK extractors and verify-access artifacts expect.\n */\nexport function flattenHeaders(headers: CloudFrontHeaders): Record<string, string | string[]> {\n const out: Record<string, string | string[]> = {};\n for (const [name, entries] of Object.entries(headers)) {\n if (!entries?.length) continue;\n out[name] = entries.length === 1 ? entries[0].value : entries.map((e) => e.value);\n }\n return out;\n}\n\nexport interface DecodedBody {\n /** UTF-8 body text, undefined when there is no body. */\n text?: string;\n /** True when CloudFront truncated the body before handing it to the trigger. */\n truncated: boolean;\n}\n\nexport function decodeBody(request: CloudFrontRequest): DecodedBody {\n const body = request.body;\n if (!body?.data) return { truncated: false };\n const text =\n body.encoding === 'base64' ? Buffer.from(body.data, 'base64').toString('utf8') : body.data;\n return { text, truncated: body.inputTruncated === true };\n}\n\n/** Full request URL as the origin will see it (scheme fixed to https). */\nexport function requestUrl(request: CloudFrontRequest, host: string): string {\n const qs = request.querystring ? `?${request.querystring}` : '';\n return `https://${host}${request.uri}${qs}`;\n}\n\n/**\n * Set a header on the origin-bound request (overwrites all prior values).\n */\nexport function setHeader(request: CloudFrontRequest, name: string, value: string): void {\n request.headers[name.toLowerCase()] = [{ key: name, value }];\n}\n\n/**\n * Strip every inbound header matching a prefix (lowercase compare). Used to\n * drop spoofed `x-astrasync-*` trust headers before the shell injects its\n * own — in BOTH modes, so a caller can never smuggle trust past the origin.\n */\nexport function stripHeadersByPrefix(request: CloudFrontRequest, prefix: string): void {\n const p = prefix.toLowerCase();\n for (const name of Object.keys(request.headers)) {\n if (name.startsWith(p)) delete request.headers[name];\n }\n}\n\nexport function jsonResponse(\n status: number,\n body: unknown,\n extraHeaders?: Record<string, string>\n): CloudFrontResultResponse {\n const headers: CloudFrontResultResponse['headers'] = {\n 'content-type': [{ key: 'Content-Type', value: 'application/json' }],\n };\n for (const [name, value] of Object.entries(extraHeaders ?? {})) {\n headers[name.toLowerCase()] = [{ key: name, value }];\n }\n return {\n status: String(status),\n statusDescription: status === 403 ? 'Forbidden' : status === 202 ? 'Accepted' : 'OK',\n headers,\n body: JSON.stringify(body),\n };\n}\n","/**\n * Runtime configuration for the edge shell.\n *\n * Lambda@Edge forbids environment variables, so configuration is layered:\n * 1. Build-time constants baked into the bundle (apiBaseUrl,\n * counterpartyId, counterpartyUrl, apiKeySecretName) — see\n * `tsup.config.ts` define block and `infra/deploy.sh`.\n * 2. The merchant API key from AWS Secrets Manager (us-east-1), fetched\n * lazily once per container and cached with a 15-minute TTL so\n * rotation needs no redeploy. Observe mode works fully without it —\n * the beacon endpoint is unauthenticated and verify() degrades\n * keyless — so a Secrets Manager outage can never take the shell down.\n * 3. The dashboard EdgeConfig via the SDK's getEdgeConfig (60s TTL,\n * stale-while-revalidate, enforce→observe degradation after 24h\n * staleness, DEFAULT_EDGE_CONFIG fallback — all in the SDK).\n */\n\nimport {\n getEdgeConfig,\n DEFAULT_EDGE_CONFIG,\n type EdgeConfig,\n} from '@astrasyncai/verification-gateway/edge-config';\nimport type { EdgeAdapterOptions } from './types';\n\nconst SECRET_TTL_MS = 15 * 60 * 1000;\n\ninterface SecretCacheEntry {\n value: string | undefined;\n fetchedAt: number;\n}\n\nconst secretCache = new Map<string, SecretCacheEntry>();\n\nasync function defaultFetchSecret(secretName: string): Promise<string | undefined> {\n // Lazy import so tests (and the library build) never touch the AWS SDK\n // unless a secret name is actually configured.\n const { SecretsManagerClient, GetSecretValueCommand } =\n await import('@aws-sdk/client-secrets-manager');\n // Lambda@Edge replicates globally but secrets live in us-east-1 (the\n // Lambda@Edge home region).\n const client = new SecretsManagerClient({ region: 'us-east-1' });\n const result = await client.send(new GetSecretValueCommand({ SecretId: secretName }));\n return result.SecretString ?? undefined;\n}\n\n/**\n * Resolve the merchant API key. Never throws — a missing/unreachable secret\n * degrades to keyless operation (observe mode is fully functional without\n * it; enforce mode without a key cannot fetch a config that enforces, so it\n * degrades to observe by construction).\n */\nexport async function resolveApiKey(options: EdgeAdapterOptions): Promise<string | undefined> {\n if (options.apiKey) return options.apiKey;\n const secretName = options.apiKeySecretName;\n if (!secretName) return undefined;\n\n const cached = secretCache.get(secretName);\n if (cached && Date.now() - cached.fetchedAt < SECRET_TTL_MS) return cached.value;\n\n try {\n const fetcher = options.fetchSecret ?? defaultFetchSecret;\n const value = await fetcher(secretName);\n secretCache.set(secretName, { value, fetchedAt: Date.now() });\n return value;\n } catch (err) {\n options.log?.('warn', 'Secrets Manager fetch failed — continuing keyless', {\n secretName,\n error: err instanceof Error ? err.message : String(err),\n });\n // Serve a stale value if we ever had one; otherwise keyless.\n return cached?.value;\n }\n}\n\n/**\n * Load the effective EdgeConfig. Delegates caching/fallback/degradation to\n * the SDK; adds the compile-time fallback override and never throws.\n */\nexport async function loadEdgeConfig(\n options: EdgeAdapterOptions,\n apiKey: string | undefined\n): Promise<EdgeConfig> {\n try {\n return await getEdgeConfig({ apiBaseUrl: options.apiBaseUrl, apiKey }, options.counterpartyId, {\n ttlMs: options.configTtlMs,\n });\n } catch {\n return options.fallbackConfig ?? DEFAULT_EDGE_CONFIG;\n }\n}\n\n/** Reset module caches — tests only. */\nexport function _resetConfigCaches(): void {\n secretCache.clear();\n}\n","/**\n * Three-tier traffic classification (§33.5 #1: verified /\n * identified-but-unregistered / anonymous) plus the two non-tiers the shell\n * touches as little as possible: `human` (zero network calls, zero logging)\n * and `skipped` (static assets / excluded paths).\n *\n * Cheapest checks first — the human path must add microseconds, not\n * milliseconds. Platform signatures come from the SDK so the edge and the\n * backend's verify-access anonymous handler classify identically.\n */\n\nimport type { CloudFrontRequest } from 'aws-lambda';\nimport { extractCredentials } from '@astrasyncai/verification-gateway/verify';\nimport { detectPlatformFingerprint } from '@astrasyncai/verification-gateway/platform-signatures';\nimport { matchEdgePathRule, type EdgeConfig } from '@astrasyncai/verification-gateway/edge-config';\nimport { getHeader, decodeBody, flattenHeaders } from './cf';\nimport { detectCommerce } from './detect';\nimport type { Classification, EffectivePolicy } from './types';\n\n/** Extensions the shell never classifies — static assets. */\nconst STATIC_EXTENSIONS =\n /\\.(css|js|mjs|map|png|jpe?g|gif|webp|avif|svg|ico|woff2?|ttf|otf|eot|mp4|webm|pdf|txt|xml)$/i;\n\n/** Non-browser, non-platform CLI/library UA tokens → anonymous bot. */\nconst BOT_UA_PATTERN =\n /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;\n\n/** Browser-shaped UA. */\nconst BROWSER_UA_PATTERN = /mozilla\\/\\d/i;\n\n/**\n * Resolve the effective mode/depth for a path from the EdgeConfig — ordered\n * first-match-wins rules with case-insensitive globs; no match falls through\n * to the config's top-level defaults. Static extensions are always skipped\n * regardless of rules (a rule can only skip MORE, never un-skip an asset).\n */\nexport function resolvePolicy(config: EdgeConfig, path: string): EffectivePolicy {\n if (STATIC_EXTENSIONS.test(path)) {\n return { mode: config.mode, depth: config.depth, skip: true };\n }\n const rule = matchEdgePathRule(config.pathRules, path);\n if (!rule) return { mode: config.mode, depth: config.depth, skip: false };\n return {\n mode: rule.mode ?? config.mode,\n depth: rule.depth ?? config.depth,\n skip: rule.skip === true,\n };\n}\n\n/**\n * Classify one request. Order (cheapest sufficient evidence wins):\n * 1. X-Astra-* credentials → `verified` candidate\n * 2. commerce-protocol credentials on the wire → `identified`\n * 3. platform UA fingerprint (Claude/ChatGPT/Gemini/…) → `identified`\n * 4. bot-shaped UA / headerless non-browser → `anonymous-bot`\n * 5. everything browser-shaped → `human`\n *\n * `depth: 'classify'` skips step 2 (no body decode, no header sniffing\n * beyond UA) — that is the zero-friction setting.\n */\nexport function classify(\n request: CloudFrontRequest,\n host: string,\n depth: 'classify' | 'authenticate' | 'authorize'\n): Classification {\n const userAgent = getHeader(request, 'user-agent') ?? '';\n\n // 1. Native AstraSync credentials.\n const credentials = extractCredentials(flattenHeaders(request.headers), {});\n if (credentials.astraId || credentials.apiKey || credentials.jwt) {\n return { tier: 'verified', protocol: 'x-astra' };\n }\n\n // 2. Commerce-protocol credentials (skipped entirely at classify depth).\n if (depth !== 'classify') {\n const commerce = detectCommerce(request, host, decodeBody(request));\n if (commerce) {\n return {\n tier: 'identified',\n protocol: commerce.protocol,\n artifacts: commerce.artifacts,\n evidence: commerce.evidence,\n fingerprint: detectPlatformFingerprint({ userAgent }),\n };\n }\n }\n\n // 3. Platform-agent UA fingerprint.\n const fingerprint = detectPlatformFingerprint({ userAgent });\n if (fingerprint) {\n return { tier: 'identified', protocol: 'platform-ua', fingerprint };\n }\n\n // 4. Bot-shaped: CLI/library UA, no UA at all, or any non-browser UA —\n // humans arrive with Mozilla-family UAs; everything else is automation.\n if (!userAgent || BOT_UA_PATTERN.test(userAgent) || !BROWSER_UA_PATTERN.test(userAgent)) {\n return { tier: 'anonymous-bot', evidence: { userAgent: userAgent || '(none)' } };\n }\n\n // 4b. Claims to be a browser but is missing every browser fetch-metadata\n // header — headless/spoofed. `sec-fetch-mode` is sent by all evergreen\n // browsers; its absence on a Mozilla UA is a strong bot signal.\n if (!getHeader(request, 'sec-fetch-mode') && !getHeader(request, 'accept-language')) {\n return { tier: 'anonymous-bot', evidence: { userAgent, spoofedBrowser: true } };\n }\n\n // 5. Human traffic — the shell gets out of the way.\n return { tier: 'human' };\n}\n\n/** Exported for tests. */\nexport const _patterns = { STATIC_EXTENSIONS, BOT_UA_PATTERN, BROWSER_UA_PATTERN };\n","/**\n * Crypto-free commerce-protocol detection (§7.4.2 detection order).\n *\n * The edge only SNIFFS — header shapes, compact-JWT patterns, body field\n * names — and packages what it saw as raw `CommerceArtifactsPayload` for\n * verify-access, whose Step 0.5 pipeline does the authoritative parsing and\n * cryptographic verification server-side. Nothing here imports jose/sd-jwt/\n * signature libraries: detection must stay cheap enough to run on every\n * non-human request, and verification must happen exactly once, in the\n * canonical sink, where it is recorded.\n */\n\nimport type { CloudFrontRequest } from 'aws-lambda';\nimport type { CommerceArtifactsPayload } from '@astrasyncai/verification-gateway';\nimport { flattenHeaders, getHeader, requestUrl, type DecodedBody } from './cf';\n\nexport interface DetectedCommerce {\n /** Detection-order label: rfc9421-agent-pay | rfc9421-tap | rfc9421-web-bot-auth | vi-sd-jwt | ap2 | acp | ucp | mpp | x402 */\n protocol: string;\n artifacts: CommerceArtifactsPayload;\n /** Light detection detail for beacons/audit — never secrets or full credentials. */\n evidence: Record<string, unknown>;\n}\n\n/** Compact SD-JWT: JWT + `~`-separated disclosures. */\nconst SD_JWT_PATTERN = /^eyJ[\\w-]+\\.[\\w-]*\\.[\\w-]+~/;\n\n/** Decode a JWT header segment without verifying anything. */\nfunction sniffJwtHeader(compact: string): Record<string, unknown> | undefined {\n try {\n const seg = compact.split('.')[0];\n return JSON.parse(Buffer.from(seg, 'base64url').toString('utf8')) as Record<string, unknown>;\n } catch {\n return undefined;\n }\n}\n\nfunction parseJsonBody(body: DecodedBody): Record<string, unknown> | undefined {\n if (!body.text) return undefined;\n try {\n const parsed: unknown = JSON.parse(body.text);\n return typeof parsed === 'object' && parsed !== null\n ? (parsed as Record<string, unknown>)\n : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * §7.4.2 order (after the X-Astra native check, which classify.ts owns):\n * SD-JWT/VI → RFC 9421 (Agent Pay / TAP / Web Bot Auth by kid namespace or\n * Signature-Agent) → ACP HMAC → UCP session → AP2 body triple → MPP → x402.\n * Returns undefined when nothing commerce-shaped is on the wire.\n *\n * When the body was truncated by CloudFront (`inputTruncated`), body-bound\n * artifacts still forward what arrived but carry a `bodyTruncated` evidence\n * flag — the backend downgrades those signatures rather than hard-failing.\n */\nexport function detectCommerce(\n request: CloudFrontRequest,\n host: string,\n body: DecodedBody\n): DetectedCommerce | undefined {\n const url = requestUrl(request, host);\n const method = request.method;\n const headers = flattenHeaders(request.headers);\n const jsonBody = parseJsonBody(body);\n const truncated = body.truncated ? { bodyTruncated: true } : {};\n\n // --- VI / SD-JWT credential in a header ------------------------------\n const authHeader = getHeader(request, 'authorization');\n const bearer = authHeader?.replace(/^Bearer\\s+/i, '');\n const mandateHeader =\n getHeader(request, 'x-payment-mandate') ?? getHeader(request, 'payment-mandate');\n const sdJwtCandidate = [mandateHeader, bearer].find((v) => v && SD_JWT_PATTERN.test(v));\n if (sdJwtCandidate) {\n const typ = sniffJwtHeader(sdJwtCandidate)?.typ;\n return {\n protocol: 'vi-sd-jwt',\n artifacts: { viSdJwt: sdJwtCandidate },\n evidence: { typ, ...truncated },\n };\n }\n\n // --- RFC 9421 (Agent Pay / TAP / Web Bot Auth) ------------------------\n const signature = getHeader(request, 'signature');\n const signatureInput = getHeader(request, 'signature-input');\n if (signature && signatureInput) {\n const signatureAgent = getHeader(request, 'signature-agent');\n const registryHint = getHeader(request, 'x-agent-registry');\n const lowerInput = signatureInput.toLowerCase();\n const protocol = signatureAgent\n ? 'rfc9421-web-bot-auth'\n : registryHint?.toLowerCase().includes('visa') || lowerInput.includes('visa')\n ? 'rfc9421-tap'\n : registryHint?.toLowerCase().includes('mastercard') || lowerInput.includes('mastercard')\n ? 'rfc9421-agent-pay'\n : 'rfc9421';\n return {\n protocol,\n artifacts: {\n rfc9421: { headers, method, url, ...(body.text && { body: body.text }) },\n },\n evidence: {\n ...(signatureAgent && { signatureAgent }),\n ...(registryHint && { registryHint }),\n keyid: signatureInput.match(/keyid=\"([^\"]+)\"/)?.[1],\n ...truncated,\n },\n };\n }\n\n // --- ACP (HMAC-signed webhook/checkout: signature WITHOUT signature-input)\n const acpTimestamp = getHeader(request, 'timestamp');\n if (signature && !signatureInput && (acpTimestamp || getHeader(request, 'idempotency-key'))) {\n return {\n protocol: 'acp',\n artifacts: {\n acpRequest: {\n method,\n url,\n headers,\n ...(body.text && { rawBody: body.text }),\n ...(jsonBody && { body: jsonBody }),\n },\n },\n evidence: { hasTimestamp: Boolean(acpTimestamp), ...truncated },\n };\n }\n\n // --- UCP checkout session ---------------------------------------------\n if (/\\/checkout[-_]sessions(\\/|$)/.test(request.uri)) {\n return {\n protocol: 'ucp',\n artifacts: { ucpRequest: { method, url, ...(jsonBody && { body: jsonBody }) } },\n evidence: { path: request.uri, ...truncated },\n };\n }\n\n // --- AP2 mandate triple in the body -----------------------------------\n if (jsonBody) {\n const intent = jsonBody.intent_mandate ?? jsonBody.intentMandate;\n const cart = jsonBody.cart_mandate ?? jsonBody.cartMandate;\n const payment = jsonBody.payment_mandate ?? jsonBody.paymentMandate;\n if (typeof intent === 'string' || typeof cart === 'string' || typeof payment === 'string') {\n return {\n protocol: 'ap2',\n artifacts: {\n ap2Mandates: {\n ...(typeof intent === 'string' && { intent }),\n ...(typeof cart === 'string' && { cart }),\n ...(typeof payment === 'string' && { payment }),\n },\n },\n evidence: {\n mandatesPresent: [\n typeof intent === 'string' && 'intent',\n typeof cart === 'string' && 'cart',\n typeof payment === 'string' && 'payment',\n ].filter(Boolean),\n ...truncated,\n },\n };\n }\n }\n\n // --- MPP (`Authorization: Payment <credential>`) -----------------------\n if (authHeader && /^Payment\\s+/i.test(authHeader)) {\n return {\n protocol: 'mpp',\n artifacts: {\n mppRequest: {\n method,\n url,\n headers,\n ...(body.text && { rawBody: body.text }),\n ...(jsonBody && { body: jsonBody }),\n },\n },\n evidence: truncated,\n };\n }\n\n // --- x402 (X-PAYMENT header) ------------------------------------------\n if (getHeader(request, 'x-payment')) {\n return {\n protocol: 'x402',\n artifacts: {\n x402Request: { method, url, headers, ...(jsonBody && { body: jsonBody }) },\n },\n evidence: truncated,\n };\n }\n\n return undefined;\n}\n","/**\n * Depth-controlled verification per tier. The edge never verifies\n * credentials itself — it forwards what it detected to verify-access, the\n * canonical sole verification sink (backend Step 0.5 runs the commerce\n * pipeline, records events, provisions orphans, persists sessions). What\n * the depth setting controls is how much the edge LOOKS and how much it\n * FORWARDS:\n *\n * classify — what is it? UA tiers only. Zero verify-access calls;\n * beacons only.\n * authenticate — who is it? + protocol detection. X-Astra agents get a\n * verify-access identity check; commerce/platform traffic\n * is beaconed with protocol + evidence (no signature\n * verification anywhere).\n * authorize — are they allowed? + artifact forwarding. X-Astra verify\n * calls carry commerceArtifacts; identified traffic gets an\n * anonymous verify-access call with artifacts +\n * callerMetadata so the backend cryptographically proves\n * and policy-evaluates once.\n */\n\nimport type { CloudFrontRequest } from 'aws-lambda';\nimport { verify, extractCredentials } from '@astrasyncai/verification-gateway/verify';\nimport { resolveHttpPdlss } from '@astrasyncai/verification-gateway/http-pdlss';\nimport type { GatewayConfig, VerificationRequest } from '@astrasyncai/verification-gateway';\nimport { flattenHeaders, getHeader } from './cf';\nimport { withBudget, emitBeacon, sampled } from './events';\nimport type { Classification, EdgeAdapterOptions, EffectivePolicy, TierOutcome } from './types';\n\nfunction baseOutcome(classification: Classification): TierOutcome {\n return {\n tier: classification.tier,\n decision: 'allow',\n reasons: [],\n protocol: classification.protocol,\n };\n}\n\nfunction buildVerificationRequest(\n options: EdgeAdapterOptions,\n request: CloudFrontRequest,\n classification: Classification,\n includeArtifacts: boolean\n): VerificationRequest {\n const headers = flattenHeaders(request.headers);\n const clientIp = getHeader(request, 'cloudfront-viewer-address')?.split(':')[0];\n // verify-access REQUIRES a purpose. Resolve via the shared §4.6 chain:\n // the agent's X-Astra-Purpose/-Action declarations, else the pinned\n // method table (GET → data/data.read etc.) — a transport verb never\n // travels as a PDLSS action.\n const pdlss = resolveHttpPdlss({\n method: request.method,\n astraPurpose: getHeader(request, 'x-astra-purpose'),\n astraAction: getHeader(request, 'x-astra-action'),\n });\n return {\n credentials: extractCredentials(headers, {}),\n purpose: pdlss.purpose,\n action: pdlss.action,\n resource: request.uri,\n createSession: true,\n counterpartyType: 'website',\n counterpartyUrl: options.counterpartyUrl,\n callerMetadata: {\n sourceIp: clientIp,\n userAgent: getHeader(request, 'user-agent'),\n referer: getHeader(request, 'referer'),\n host: getHeader(request, 'host'),\n },\n ...(includeArtifacts &&\n classification.artifacts && { commerceArtifacts: classification.artifacts }),\n };\n}\n\nfunction outcomeFromVerifyResult(\n classification: Classification,\n result: Awaited<ReturnType<typeof verify>>\n): TierOutcome {\n const outcome = baseOutcome(classification);\n outcome.sessionId = (result as { sessionId?: string }).sessionId;\n outcome.correlationId = result.correlationId;\n outcome.guidance = result.guidance;\n\n if (result.requiresStepUp || result.stepUpApproval) {\n outcome.decision = 'manual_review';\n outcome.reasons = ['step-up approval required'];\n outcome.stepUp = {\n pollUrl: (result.stepUpApproval as { pollUrl?: string } | undefined)?.pollUrl,\n expiresAt: (result.stepUpApproval as { expiresAt?: string } | undefined)?.expiresAt,\n };\n return outcome;\n }\n\n if (!result.identityVerified || !result.policyAllowed) {\n outcome.decision = 'deny';\n outcome.reasons = result.denialReasons?.length\n ? result.denialReasons\n : [!result.identityVerified ? 'identity not verified' : 'denied by access policy'];\n return outcome;\n }\n\n outcome.asat = {\n trustScore: result.agent?.trustScore,\n agentId: result.agent?.astraId,\n developerId: result.developer?.astradId,\n accessLevel: result.accessLevel,\n };\n return outcome;\n}\n\n/**\n * Run the tier-appropriate verification/telemetry for one classified\n * request. NEVER throws; on any failure returns an `allow` outcome with the\n * failure recorded in `reasons` (infra failures fail open — a presented\n * credential the BACKEND rejected is a deny, but an unreachable backend is\n * not a rejection).\n */\nexport async function runTier(\n options: EdgeAdapterOptions,\n request: CloudFrontRequest,\n classification: Classification,\n policy: EffectivePolicy,\n apiKey: string | undefined,\n sampling: { anonymousBeaconRate: number }\n): Promise<TierOutcome> {\n const budgets = options.budgets ?? {};\n const beaconMs = budgets.beaconMs ?? 300;\n const verifyMs =\n policy.mode === 'enforce'\n ? (budgets.verifyEnforceMs ?? 3000)\n : (budgets.verifyObserveMs ?? 1500);\n\n const gatewayConfig: GatewayConfig = {\n apiBaseUrl: options.apiBaseUrl,\n ...(apiKey && { apiKey }),\n counterpartyId: options.counterpartyId,\n // The shell handles its own error semantics; init self-probes don't\n // belong in a per-request edge function.\n disableInitChecks: true,\n } as GatewayConfig;\n\n try {\n switch (classification.tier) {\n case 'verified': {\n // classify depth = zero API calls, even for credentialed agents.\n if (policy.depth === 'classify') {\n return baseOutcome(classification);\n }\n const result = await withBudget(\n verify(\n gatewayConfig,\n buildVerificationRequest(options, request, classification, policy.depth === 'authorize')\n ),\n verifyMs\n );\n if (!result) {\n const outcome = baseOutcome(classification);\n outcome.reasons = ['verify-access unreachable within budget — fail-open'];\n return outcome;\n }\n return outcomeFromVerifyResult(classification, result);\n }\n\n case 'identified': {\n if (policy.depth === 'authorize' && classification.artifacts) {\n // Anonymous verify-access call WITH artifacts: the backend runs\n // the commerce pipeline, records unverified events, and\n // provisions orphan platform agents — one sink, recorded once.\n const result = await withBudget(\n verify(gatewayConfig, buildVerificationRequest(options, request, classification, true)),\n verifyMs\n );\n if (!result) {\n const outcome = baseOutcome(classification);\n outcome.reasons = ['verify-access unreachable within budget — fail-open'];\n return outcome;\n }\n return outcomeFromVerifyResult(classification, result);\n }\n // classify/credentials depth (or no artifacts): lightweight beacon.\n await emitBeacon(options, request, classification, beaconMs);\n const outcome = baseOutcome(classification);\n if (policy.mode === 'enforce' && policy.depth !== 'classify') {\n // Unregistered agent on an enforced path: deny with guidance.\n outcome.decision = 'deny';\n outcome.reasons = ['agent is not registered with AstraSync'];\n }\n return outcome;\n }\n\n case 'anonymous-bot': {\n if (sampled(sampling.anonymousBeaconRate, options.random)) {\n await emitBeacon(\n options,\n request,\n { ...classification, protocol: classification.protocol ?? 'ua-heuristic' },\n beaconMs\n );\n }\n // Anonymous bots pass through even in enforce mode — enforce gates\n // AGENT traffic; generic bot management stays the CDN/WAF's job\n // (mixed-traffic classification §5.1.7 is the enforce-mode\n // prerequisite tracked as §33.5 #9).\n return baseOutcome(classification);\n }\n\n default:\n return baseOutcome(classification);\n }\n } catch (err) {\n const outcome = baseOutcome(classification);\n outcome.reasons = [\n `edge pipeline error — fail-open: ${err instanceof Error ? err.message : String(err)}`,\n ];\n return outcome;\n }\n}\n","/**\n * Time-budgeted event emission.\n *\n * Lambda@Edge has no `waitUntil` — anything not awaited before the handler\n * returns may never run (the container freezes). So every network call is\n * awaited under an explicit budget and DROPPED on timeout: latency to the\n * customer's traffic is bounded, and a slow AstraSync API costs telemetry,\n * never page loads.\n */\n\nimport { reportUnregisteredAttempt } from '@astrasyncai/verification-gateway/verify';\nimport type { CloudFrontRequest } from 'aws-lambda';\nimport { getHeader } from './cf';\nimport type { Classification, EdgeAdapterOptions } from './types';\n\n/** Await `promise` for at most `ms`; resolve `undefined` on timeout/error. */\nexport async function withBudget<T>(promise: Promise<T>, ms: number): Promise<T | undefined> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n promise,\n new Promise<undefined>((resolve) => {\n timer = setTimeout(() => resolve(undefined), ms);\n }),\n ]);\n } catch {\n return undefined;\n } finally {\n if (timer) clearTimeout(timer);\n }\n}\n\n/**\n * Emit an unregistered-attempt beacon for `identified` (non-crypto depths)\n * and sampled `anonymous-bot` traffic. Unauthenticated by design\n * (CSRF-exempt on the backend); the backend fingerprints the UA and\n * find-or-provisions orphan platform agents server-side.\n */\nexport async function emitBeacon(\n options: EdgeAdapterOptions,\n request: CloudFrontRequest,\n classification: Classification,\n budgetMs: number\n): Promise<void> {\n const clientIp = getHeader(request, 'cloudfront-viewer-address')?.split(':')[0];\n await withBudget(\n reportUnregisteredAttempt(\n { apiBaseUrl: options.apiBaseUrl, counterpartyId: options.counterpartyId },\n {\n counterpartyUrl: options.counterpartyUrl,\n counterpartyType: 'website',\n sourceIp: clientIp,\n userAgent: getHeader(request, 'user-agent'),\n requestPath: request.uri,\n requestMethod: request.method,\n // 3.9.0 evidence fields (backend persists them into eventData).\n protocol: classification.protocol,\n evidence: classification.evidence,\n }\n ),\n budgetMs\n );\n}\n\n/** Deterministic-in-tests sampling gate. */\nexport function sampled(rate: number, random: () => number = Math.random): boolean {\n if (rate <= 0) return false;\n if (rate >= 1) return true;\n return random() < rate;\n}\n","/**\n * §7.4.6 — protocol-specific machine-readable messaging for enforce-mode\n * responses. Agents (not humans) consume these bodies: stable error codes,\n * the failure list, and registration guidance with real URLs.\n */\n\nimport type { TierOutcome } from './types';\n\nconst PROTOCOL_MESSAGES: Record<string, string> = {\n 'vi-sd-jwt':\n 'This request presented Visa Intelligent Commerce credentials, but the agent is not verified with AstraSync. Register the agent to pair your VI credentials with a verified identity and mandate.',\n 'rfc9421-agent-pay':\n 'This request carried a Mastercard Agent Pay signature, but the agent is not verified with AstraSync. Register the agent to pair Agent Pay credentials with a verified identity and mandate.',\n 'rfc9421-tap':\n 'This request carried a Visa TAP signature, but the agent is not verified with AstraSync. Register the agent to pair TAP credentials with a verified identity and mandate.',\n 'rfc9421-web-bot-auth':\n 'This request carried a Web Bot Auth signature, but the agent is not verified with AstraSync. Register the agent to link your signed-agent identity to a verified principal.',\n rfc9421:\n 'This request carried an RFC 9421 HTTP message signature, but the agent is not verified with AstraSync. Register the agent to link the signing identity to a verified principal.',\n acp: 'This request used the Agentic Commerce Protocol, but the agent is not verified with AstraSync. Register the agent to transact on this property.',\n ucp: 'This request used a UCP checkout session, but the agent is not verified with AstraSync. Register the agent to transact on this property.',\n ap2: 'This request presented AP2 mandates, but the agent is not verified with AstraSync. Register the agent to pair your mandates with a verified identity.',\n mpp: 'This request presented MPP payment credentials, but the agent is not verified with AstraSync. Register the agent to transact on this property.',\n x402: 'This request used x402 payment headers, but the agent is not verified with AstraSync. Register the agent to transact on this property.',\n 'platform-ua':\n 'This traffic was identified as a platform AI agent without AstraSync credentials. Register the agent to access this property with a verified identity.',\n 'x-astra':\n 'The presented AstraSync credentials failed verification or were denied by this property’s access policy.',\n};\n\nexport interface DenyBody {\n success: false;\n error: {\n code: 'UNAUTHORIZED' | 'INSUFFICIENT_ACCESS';\n message: string;\n protocol?: string;\n failures: string[];\n correlationId?: string;\n guidance: {\n message?: string;\n registrationUrl?: string;\n documentationUrl?: string;\n };\n };\n}\n\nexport function buildDenyBody(outcome: TierOutcome, apiBaseUrl: string): DenyBody {\n const origin = apiBaseUrl.replace(/\\/api\\/?$/, '');\n const protocolMessage =\n (outcome.protocol && PROTOCOL_MESSAGES[outcome.protocol]) ??\n 'This request could not be verified by AstraSync.';\n return {\n success: false,\n error: {\n // UNAUTHORIZED = identity failed (register/re-authenticate helps);\n // INSUFFICIENT_ACCESS = identity fine, policy denied (fix the mandate).\n code: outcome.tier === 'verified' ? 'INSUFFICIENT_ACCESS' : 'UNAUTHORIZED',\n message: protocolMessage,\n ...(outcome.protocol && { protocol: outcome.protocol }),\n failures: outcome.reasons,\n ...(outcome.correlationId && { correlationId: outcome.correlationId }),\n guidance: {\n // `||` not `??`: verify() fills these with EMPTY STRINGS when the\n // well-known cache is cold — empty means \"use the fallback\".\n message:\n outcome.guidance?.message ||\n 'Register this agent with AstraSync to receive verifiable credentials.',\n registrationUrl: outcome.guidance?.registrationUrl || `${origin}/register`,\n documentationUrl: outcome.guidance?.documentationUrl || `${origin}/docs/edge-adapter`,\n },\n },\n };\n}\n\nexport interface ManualReviewBody {\n success: false;\n status: 'manual_review';\n message: string;\n stepUpApproval?: { pollUrl?: string; expiresAt?: string };\n correlationId?: string;\n}\n\nexport function buildManualReviewBody(outcome: TierOutcome): ManualReviewBody {\n return {\n success: false,\n status: 'manual_review',\n message:\n 'This action requires human approval. Poll the step-up approval endpoint and retry once approved.',\n ...(outcome.stepUp && { stepUpApproval: outcome.stepUp }),\n ...(outcome.correlationId && { correlationId: outcome.correlationId }),\n };\n}\n","/**\n * Decision → CloudFront result wiring (§7.4 enforcement, §7.4.7 ASAT\n * headers). Executed in BOTH modes so observe mode records exactly what\n * enforce mode would have done — but only enforce mode is allowed to return\n * anything other than the origin-bound request.\n */\n\nimport type { CloudFrontRequest, CloudFrontRequestResult } from 'aws-lambda';\nimport { jsonResponse, setHeader, stripHeadersByPrefix } from './cf';\nimport { buildDenyBody, buildManualReviewBody } from './messaging';\nimport type { EdgeAdapterOptions, EffectivePolicy, TierOutcome } from './types';\n\n/** §7.4.7 ASAT trust-attestation headers injected on allowed requests. */\nexport const ASAT_HEADERS = {\n trustScore: 'X-AstraSync-Trust-Score',\n agentId: 'X-AstraSync-Agent-Id',\n developerId: 'X-AstraSync-Developer-Id',\n permissions: 'X-AstraSync-Permissions',\n observedDecision: 'X-AstraSync-Observed-Decision',\n} as const;\n\n/**\n * Apply the outcome. Always strips inbound spoofed `x-astrasync-*` headers\n * first (both modes — trust headers must only ever originate here).\n *\n * Observe mode: returns the request untouched apart from header hygiene and\n * an `X-AstraSync-Observed-Decision` stamp for origin-side log correlation.\n *\n * Enforce mode: ALLOW → request + ASAT headers; DENY → 403 machine-readable\n * body; MANUAL_REVIEW → 202 with step-up poll info.\n */\nexport function applyOutcome(\n options: EdgeAdapterOptions,\n request: CloudFrontRequest,\n outcome: TierOutcome,\n policy: EffectivePolicy\n): CloudFrontRequestResult {\n stripHeadersByPrefix(request, 'x-astrasync-');\n\n if (policy.mode !== 'enforce') {\n // Shadow stamp: what enforce WOULD have done, visible in origin logs.\n if (outcome.decision !== 'allow') {\n setHeader(request, ASAT_HEADERS.observedDecision, outcome.decision);\n }\n injectAsat(request, outcome);\n return request;\n }\n\n switch (outcome.decision) {\n case 'deny':\n return jsonResponse(403, buildDenyBody(outcome, options.apiBaseUrl));\n case 'manual_review':\n return jsonResponse(202, buildManualReviewBody(outcome), {\n 'Retry-After': '30',\n });\n default:\n injectAsat(request, outcome);\n return request;\n }\n}\n\nfunction injectAsat(request: CloudFrontRequest, outcome: TierOutcome): void {\n if (!outcome.asat) return;\n const { trustScore, agentId, developerId, accessLevel } = outcome.asat;\n if (trustScore !== undefined) {\n setHeader(request, ASAT_HEADERS.trustScore, String(trustScore));\n }\n if (agentId) setHeader(request, ASAT_HEADERS.agentId, agentId);\n if (developerId) setHeader(request, ASAT_HEADERS.developerId, developerId);\n if (accessLevel) setHeader(request, ASAT_HEADERS.permissions, accessLevel);\n}\n","/**\n * Commerce Shield edge shell — Lambda@Edge origin-request handler factory\n * (§7.4 / §33.5 #1).\n *\n * Composition per request:\n * config (cached) → path policy → classify → tier pipeline → apply outcome\n *\n * THE invariant (tested): outside enforce mode the handler ALWAYS returns\n * the origin-bound request — any error, timeout, or misconfiguration\n * results in pass-through. A top-level catch guarantees it even against\n * bugs in the shell itself.\n */\n\nimport type { CloudFrontRequestEvent, CloudFrontRequestResult } from 'aws-lambda';\nimport { getRequest, getHeader } from './cf';\nimport { resolveApiKey, loadEdgeConfig } from './config';\nimport { classify, resolvePolicy } from './classify';\nimport { runTier } from './pipeline';\nimport { applyOutcome } from './enforce';\nimport type { EdgeAdapterOptions } from './types';\n\nexport type EdgeHandler = (event: CloudFrontRequestEvent) => Promise<CloudFrontRequestResult>;\n\nexport function createEdgeHandler(options: EdgeAdapterOptions): EdgeHandler {\n const log =\n options.log ??\n ((level, message, data) => {\n // Lambda@Edge writes console output to the regional CloudWatch log\n // group of whichever edge location served the request.\n console[level === 'info' ? 'log' : level](`[astrasync-edge] ${message}`, data ?? '');\n });\n\n return async function handler(event) {\n const request = getRequest(event);\n try {\n const apiKey = await resolveApiKey({ ...options, log });\n const config = await loadEdgeConfig(options, apiKey);\n const policy = resolvePolicy(config, request.uri);\n\n // Skipped paths (static assets / skip rules): untouched, zero work.\n if (policy.skip) return request;\n\n const host =\n getHeader(request, 'x-forwarded-host') ??\n getHeader(request, 'host') ??\n new URL(options.counterpartyUrl).host;\n\n const classification = classify(request, host, policy.depth);\n\n // Humans: get out of the way. No calls, no logs, no header changes\n // beyond nothing at all.\n if (classification.tier === 'human') return request;\n\n const outcome = await runTier(\n { ...options, log },\n request,\n classification,\n policy,\n apiKey,\n config.sampling\n );\n\n if (outcome.decision !== 'allow') {\n log(\n 'info',\n policy.mode === 'enforce'\n ? `${outcome.decision.toUpperCase()}: ${request.method} ${request.uri}`\n : `[OBSERVE] would have been ${outcome.decision.toUpperCase()}: ${request.method} ${request.uri}`,\n { tier: outcome.tier, protocol: outcome.protocol, reasons: outcome.reasons }\n );\n }\n\n return applyOutcome(options, request, outcome, policy);\n } catch (err) {\n // Fail-open, unconditionally: the shell must never take the site down.\n log('error', 'edge handler error — passing request through', {\n error: err instanceof Error ? err.message : String(err),\n });\n return request;\n }\n };\n}\n"],"mappings":"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","names":["extractCredentials","extractCredentials","outcome"]}
1
+ {"version":3,"sources":["../src/cf.ts","../src/config.ts","../src/classify.ts","../src/detect.ts","../src/pipeline.ts","../src/events.ts","../src/messaging.ts","../src/enforce.ts","../src/handler.ts"],"sourcesContent":["/**\n * CloudFront origin-request event helpers. The Lambda@Edge event shape puts\n * headers in `{ key, value }[]` arrays keyed by LOWERCASED header name, and\n * (with IncludeBody) the body arrives base64-encoded with an\n * `inputTruncated` flag when it exceeded CloudFront's ~1MB exposure cap.\n */\n\nimport type {\n CloudFrontRequest,\n CloudFrontRequestEvent,\n CloudFrontHeaders,\n CloudFrontResultResponse,\n} from 'aws-lambda';\nimport {\n sanitizeHeaders,\n type ObservedMetadata,\n} from '@astrasyncai/verification-gateway/metadata-capture';\n\nexport function getRequest(event: CloudFrontRequestEvent): CloudFrontRequest {\n return event.Records[0].cf.request;\n}\n\n/** First value of a header (lowercase name), or undefined. */\nexport function getHeader(request: CloudFrontRequest, name: string): string | undefined {\n return request.headers[name.toLowerCase()]?.[0]?.value;\n}\n\n/**\n * Flatten CloudFront headers into the `Record<string, string | string[]>`\n * shape the SDK extractors and verify-access artifacts expect.\n */\nexport function flattenHeaders(headers: CloudFrontHeaders): Record<string, string | string[]> {\n const out: Record<string, string | string[]> = {};\n for (const [name, entries] of Object.entries(headers)) {\n if (!entries?.length) continue;\n out[name] = entries.length === 1 ? entries[0].value : entries.map((e) => e.value);\n }\n return out;\n}\n\nexport interface DecodedBody {\n /** UTF-8 body text, undefined when there is no body. */\n text?: string;\n /** True when CloudFront truncated the body before handing it to the trigger. */\n truncated: boolean;\n}\n\nexport function decodeBody(request: CloudFrontRequest): DecodedBody {\n const body = request.body;\n if (!body?.data) return { truncated: false };\n const text =\n body.encoding === 'base64' ? Buffer.from(body.data, 'base64').toString('utf8') : body.data;\n return { text, truncated: body.inputTruncated === true };\n}\n\n/**\n * Workstream D — connection-layer signals the edge can see but the SDK cannot.\n * CloudFront injects the `CloudFront-Viewer-*` header family (IP, ASN, country,\n * TLS version/cipher, HTTP version, device class) when the OriginRequestPolicy\n * whitelists them (see infra/template.yaml). Absent values are simply omitted —\n * a partial connection block is fine.\n */\nexport function collectConnectionMetadata(request: CloudFrontRequest): Record<string, string> {\n const out: Record<string, string> = {};\n const put = (key: string, headerName: string, transform?: (v: string) => string) => {\n const v = getHeader(request, headerName);\n if (v) out[key] = transform ? transform(v) : v;\n };\n // `cloudfront-viewer-address` is `ip:port` (IPv6 keeps its brackets); take the\n // address portion the same way the beacon/pipeline already do.\n const addr = getHeader(request, 'cloudfront-viewer-address');\n if (addr) out.ip = addr.replace(/:\\d+$/, '');\n put('country', 'cloudfront-viewer-country');\n put('countryName', 'cloudfront-viewer-country-name');\n put('region', 'cloudfront-viewer-country-region');\n put('city', 'cloudfront-viewer-city');\n put('asn', 'cloudfront-viewer-asn');\n put('tlsVersion', 'cloudfront-viewer-tls');\n put('httpVersion', 'cloudfront-viewer-http-version');\n // Device-class booleans → a single compact device string.\n const devices: string[] = [];\n for (const [label, header] of [\n ['desktop', 'cloudfront-is-desktop-viewer'],\n ['mobile', 'cloudfront-is-mobile-viewer'],\n ['tablet', 'cloudfront-is-tablet-viewer'],\n ['smarttv', 'cloudfront-is-smarttv-viewer'],\n ] as const) {\n if (getHeader(request, header) === 'true') devices.push(label);\n }\n if (devices.length) out.device = devices.join(',');\n return out;\n}\n\n/**\n * Workstream D — the FULL sanitised metadata capture for one request: every\n * inbound header (secrets stripped, credential headers reduced to a safe format\n * prefix) plus the connection-layer block. The edge is the richest capture\n * point — the header arrays are already in memory — and this is pure string\n * work, so it adds no page latency. Forwarded on both the verify-access call\n * and the beacon.\n */\nexport function buildObservedMetadata(request: CloudFrontRequest): ObservedMetadata {\n const { headers, apiKeyFormat, platformHeaders } = sanitizeHeaders(\n flattenHeaders(request.headers)\n );\n const connection = collectConnectionMetadata(request);\n return {\n headers,\n ...(apiKeyFormat && { apiKeyFormat }),\n ...(platformHeaders && { platformHeaders }),\n ...(Object.keys(connection).length > 0 && { connection }),\n };\n}\n\n/**\n * Workstream D — normalize the origin-bound `Host` header.\n *\n * To receive the `CloudFront-Viewer-*` connection headers (IP/ASN/TLS/country)\n * we must move off the managed `AllViewerExceptHostHeader` origin-request policy\n * onto one that whitelists those headers — but every such policy ALSO forwards\n * the viewer `Host`, which would break vhost / PaaS origin routing that expects\n * the origin's own hostname. So the Lambda takes ownership of the Host rewrite:\n * stash the public (viewer) host into `X-Forwarded-Host` for downstream use and\n * detection, then set `Host` to the origin's domain (available on the event as\n * `request.origin.custom.domainName`). No-ops when the origin domain is absent\n * (synthetic test events) or already matches — so it is safe on every path,\n * including static-asset pass-through.\n */\nexport function normalizeOriginHost(request: CloudFrontRequest): void {\n const originDomain =\n request.origin?.custom?.domainName ?? request.origin?.s3?.domainName ?? undefined;\n if (!originDomain) return;\n const currentHost = getHeader(request, 'host');\n if (!currentHost || currentHost === originDomain) return;\n if (!getHeader(request, 'x-forwarded-host')) {\n setHeader(request, 'X-Forwarded-Host', currentHost);\n }\n setHeader(request, 'Host', originDomain);\n}\n\n/** Full request URL as the origin will see it (scheme fixed to https). */\nexport function requestUrl(request: CloudFrontRequest, host: string): string {\n const qs = request.querystring ? `?${request.querystring}` : '';\n return `https://${host}${request.uri}${qs}`;\n}\n\n/**\n * Set a header on the origin-bound request (overwrites all prior values).\n */\nexport function setHeader(request: CloudFrontRequest, name: string, value: string): void {\n request.headers[name.toLowerCase()] = [{ key: name, value }];\n}\n\n/**\n * Strip every inbound header matching a prefix (lowercase compare). Used to\n * drop spoofed `x-astrasync-*` trust headers before the shell injects its\n * own — in BOTH modes, so a caller can never smuggle trust past the origin.\n */\nexport function stripHeadersByPrefix(request: CloudFrontRequest, prefix: string): void {\n const p = prefix.toLowerCase();\n for (const name of Object.keys(request.headers)) {\n if (name.startsWith(p)) delete request.headers[name];\n }\n}\n\nexport function jsonResponse(\n status: number,\n body: unknown,\n extraHeaders?: Record<string, string>\n): CloudFrontResultResponse {\n const headers: CloudFrontResultResponse['headers'] = {\n 'content-type': [{ key: 'Content-Type', value: 'application/json' }],\n };\n for (const [name, value] of Object.entries(extraHeaders ?? {})) {\n headers[name.toLowerCase()] = [{ key: name, value }];\n }\n return {\n status: String(status),\n statusDescription: status === 403 ? 'Forbidden' : status === 202 ? 'Accepted' : 'OK',\n headers,\n body: JSON.stringify(body),\n };\n}\n","/**\n * Runtime configuration for the edge shell.\n *\n * Lambda@Edge forbids environment variables, so configuration is layered:\n * 1. Build-time constants baked into the bundle (apiBaseUrl,\n * counterpartyId, counterpartyUrl, apiKeySecretName) — see\n * `tsup.config.ts` define block and `infra/deploy.sh`.\n * 2. The merchant API key from AWS Secrets Manager (us-east-1), fetched\n * lazily once per container and cached with a 15-minute TTL so\n * rotation needs no redeploy. Observe mode works fully without it —\n * the beacon endpoint is unauthenticated and verify() degrades\n * keyless — so a Secrets Manager outage can never take the shell down.\n * 3. The dashboard EdgeConfig via the SDK's getEdgeConfig (60s TTL,\n * stale-while-revalidate, enforce→observe degradation after 24h\n * staleness, DEFAULT_EDGE_CONFIG fallback — all in the SDK).\n */\n\nimport {\n getEdgeConfig,\n DEFAULT_EDGE_CONFIG,\n type EdgeConfig,\n} from '@astrasyncai/verification-gateway/edge-config';\nimport type { EdgeAdapterOptions } from './types';\n\nconst SECRET_TTL_MS = 15 * 60 * 1000;\n\ninterface SecretCacheEntry {\n value: string | undefined;\n fetchedAt: number;\n}\n\nconst secretCache = new Map<string, SecretCacheEntry>();\n\nasync function defaultFetchSecret(secretName: string): Promise<string | undefined> {\n // Lazy import so tests (and the library build) never touch the AWS SDK\n // unless a secret name is actually configured.\n const { SecretsManagerClient, GetSecretValueCommand } =\n await import('@aws-sdk/client-secrets-manager');\n // Lambda@Edge replicates globally but secrets live in us-east-1 (the\n // Lambda@Edge home region).\n const client = new SecretsManagerClient({ region: 'us-east-1' });\n const result = await client.send(new GetSecretValueCommand({ SecretId: secretName }));\n return result.SecretString ?? undefined;\n}\n\n/**\n * Resolve the merchant API key. Never throws — a missing/unreachable secret\n * degrades to keyless operation (observe mode is fully functional without\n * it; enforce mode without a key cannot fetch a config that enforces, so it\n * degrades to observe by construction).\n */\nexport async function resolveApiKey(options: EdgeAdapterOptions): Promise<string | undefined> {\n if (options.apiKey) return options.apiKey;\n const secretName = options.apiKeySecretName;\n if (!secretName) return undefined;\n\n const cached = secretCache.get(secretName);\n if (cached && Date.now() - cached.fetchedAt < SECRET_TTL_MS) return cached.value;\n\n try {\n const fetcher = options.fetchSecret ?? defaultFetchSecret;\n const value = await fetcher(secretName);\n secretCache.set(secretName, { value, fetchedAt: Date.now() });\n return value;\n } catch (err) {\n options.log?.('warn', 'Secrets Manager fetch failed — continuing keyless', {\n secretName,\n error: err instanceof Error ? err.message : String(err),\n });\n // Serve a stale value if we ever had one; otherwise keyless.\n return cached?.value;\n }\n}\n\n/**\n * Load the effective EdgeConfig. Delegates caching/fallback/degradation to\n * the SDK; adds the compile-time fallback override and never throws.\n */\nexport async function loadEdgeConfig(\n options: EdgeAdapterOptions,\n apiKey: string | undefined\n): Promise<EdgeConfig> {\n try {\n return await getEdgeConfig({ apiBaseUrl: options.apiBaseUrl, apiKey }, options.counterpartyId, {\n ttlMs: options.configTtlMs,\n });\n } catch {\n return options.fallbackConfig ?? DEFAULT_EDGE_CONFIG;\n }\n}\n\n/** Reset module caches — tests only. */\nexport function _resetConfigCaches(): void {\n secretCache.clear();\n}\n","/**\n * Three-tier traffic classification (§33.5 #1: verified /\n * identified-but-unregistered / anonymous) plus the two non-tiers the shell\n * touches as little as possible: `human` (zero network calls, zero logging)\n * and `skipped` (static assets / excluded paths).\n *\n * Cheapest checks first — the human path must add microseconds, not\n * milliseconds. Platform signatures come from the SDK so the edge and the\n * backend's verify-access anonymous handler classify identically.\n */\n\nimport type { CloudFrontRequest } from 'aws-lambda';\nimport { extractCredentials } from '@astrasyncai/verification-gateway/verify';\nimport { detectPlatformFingerprint } from '@astrasyncai/verification-gateway/platform-signatures';\nimport { matchEdgePathRule, type EdgeConfig } from '@astrasyncai/verification-gateway/edge-config';\nimport { sanitizeHeaders } from '@astrasyncai/verification-gateway/metadata-capture';\nimport { getHeader, decodeBody, flattenHeaders } from './cf';\nimport { detectCommerce } from './detect';\nimport type { Classification, EffectivePolicy } from './types';\n\n/** Extensions the shell never classifies — static assets. */\nconst STATIC_EXTENSIONS =\n /\\.(css|js|mjs|map|png|jpe?g|gif|webp|avif|svg|ico|woff2?|ttf|otf|eot|mp4|webm|pdf|txt|xml)$/i;\n\n/** Non-browser, non-platform CLI/library UA tokens → anonymous bot. */\nconst BOT_UA_PATTERN =\n /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;\n\n/** Browser-shaped UA. */\nconst BROWSER_UA_PATTERN = /mozilla\\/\\d/i;\n\n/**\n * Resolve the effective mode/depth for a path from the EdgeConfig — ordered\n * first-match-wins rules with case-insensitive globs; no match falls through\n * to the config's top-level defaults. Static extensions are always skipped\n * regardless of rules (a rule can only skip MORE, never un-skip an asset).\n */\nexport function resolvePolicy(config: EdgeConfig, path: string): EffectivePolicy {\n if (STATIC_EXTENSIONS.test(path)) {\n return { mode: config.mode, depth: config.depth, skip: true };\n }\n const rule = matchEdgePathRule(config.pathRules, path);\n if (!rule) return { mode: config.mode, depth: config.depth, skip: false };\n return {\n mode: rule.mode ?? config.mode,\n depth: rule.depth ?? config.depth,\n skip: rule.skip === true,\n };\n}\n\n/**\n * Classify one request. Order (cheapest sufficient evidence wins):\n * 1. X-Astra-* credentials → `verified` candidate\n * 2. commerce-protocol credentials on the wire → `identified`\n * 3. platform UA fingerprint (Claude/ChatGPT/Gemini/…) → `identified`\n * 4. bot-shaped UA / headerless non-browser → `anonymous-bot`\n * 5. everything browser-shaped → `human`\n *\n * `depth: 'classify'` skips step 2 (no body decode, no header sniffing\n * beyond UA) — that is the zero-friction setting.\n */\nexport function classify(\n request: CloudFrontRequest,\n host: string,\n depth: 'classify' | 'authenticate' | 'authorize'\n): Classification {\n const userAgent = getHeader(request, 'user-agent') ?? '';\n const flat = flattenHeaders(request.headers);\n\n // 1. Native AstraSync credentials.\n const credentials = extractCredentials(flat, {});\n if (credentials.astraId || credentials.apiKey || credentials.jwt) {\n return { tier: 'verified', protocol: 'x-astra' };\n }\n\n // Workstream D — the key-format prefix + platform-signal headers are stronger,\n // less-spoofable platform signals than the UA. Compute once (cheap string\n // work) and feed every fingerprint call below.\n const { apiKeyFormat, platformHeaders } = sanitizeHeaders(flat);\n const detectionInput = { userAgent, apiKeyFormat, platformHeaders };\n\n // 2. Commerce-protocol credentials (skipped entirely at classify depth).\n if (depth !== 'classify') {\n const commerce = detectCommerce(request, host, decodeBody(request));\n if (commerce) {\n return {\n tier: 'identified',\n protocol: commerce.protocol,\n artifacts: commerce.artifacts,\n evidence: commerce.evidence,\n fingerprint: detectPlatformFingerprint(detectionInput),\n };\n }\n }\n\n // 3. Platform-agent fingerprint (UA, key-format, or platform header).\n const fingerprint = detectPlatformFingerprint(detectionInput);\n if (fingerprint) {\n return { tier: 'identified', protocol: 'platform-ua', fingerprint };\n }\n\n // 4. Bot-shaped: CLI/library UA, no UA at all, or any non-browser UA —\n // humans arrive with Mozilla-family UAs; everything else is automation.\n if (!userAgent || BOT_UA_PATTERN.test(userAgent) || !BROWSER_UA_PATTERN.test(userAgent)) {\n return { tier: 'anonymous-bot', evidence: { userAgent: userAgent || '(none)' } };\n }\n\n // 4b. Claims to be a browser but is missing every browser fetch-metadata\n // header — headless/spoofed. `sec-fetch-mode` is sent by all evergreen\n // browsers; its absence on a Mozilla UA is a strong bot signal.\n if (!getHeader(request, 'sec-fetch-mode') && !getHeader(request, 'accept-language')) {\n return { tier: 'anonymous-bot', evidence: { userAgent, spoofedBrowser: true } };\n }\n\n // 5. Human traffic — the shell gets out of the way.\n return { tier: 'human' };\n}\n\n/** Exported for tests. */\nexport const _patterns = { STATIC_EXTENSIONS, BOT_UA_PATTERN, BROWSER_UA_PATTERN };\n","/**\n * Crypto-free commerce-protocol detection (§7.4.2 detection order).\n *\n * The edge only SNIFFS — header shapes, compact-JWT patterns, body field\n * names — and packages what it saw as raw `CommerceArtifactsPayload` for\n * verify-access, whose Step 0.5 pipeline does the authoritative parsing and\n * cryptographic verification server-side. Nothing here imports jose/sd-jwt/\n * signature libraries: detection must stay cheap enough to run on every\n * non-human request, and verification must happen exactly once, in the\n * canonical sink, where it is recorded.\n */\n\nimport type { CloudFrontRequest } from 'aws-lambda';\nimport type { CommerceArtifactsPayload } from '@astrasyncai/verification-gateway';\nimport { flattenHeaders, getHeader, requestUrl, type DecodedBody } from './cf';\n\nexport interface DetectedCommerce {\n /** Detection-order label: rfc9421-agent-pay | rfc9421-tap | rfc9421-web-bot-auth | vi-sd-jwt | ap2 | acp | ucp | mpp | x402 */\n protocol: string;\n artifacts: CommerceArtifactsPayload;\n /** Light detection detail for beacons/audit — never secrets or full credentials. */\n evidence: Record<string, unknown>;\n}\n\n/** Compact SD-JWT: JWT + `~`-separated disclosures. */\nconst SD_JWT_PATTERN = /^eyJ[\\w-]+\\.[\\w-]*\\.[\\w-]+~/;\n\n/** Decode a JWT header segment without verifying anything. */\nfunction sniffJwtHeader(compact: string): Record<string, unknown> | undefined {\n try {\n const seg = compact.split('.')[0];\n return JSON.parse(Buffer.from(seg, 'base64url').toString('utf8')) as Record<string, unknown>;\n } catch {\n return undefined;\n }\n}\n\nfunction parseJsonBody(body: DecodedBody): Record<string, unknown> | undefined {\n if (!body.text) return undefined;\n try {\n const parsed: unknown = JSON.parse(body.text);\n return typeof parsed === 'object' && parsed !== null\n ? (parsed as Record<string, unknown>)\n : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * §7.4.2 order (after the X-Astra native check, which classify.ts owns):\n * SD-JWT/VI → RFC 9421 (Agent Pay / TAP / Web Bot Auth by kid namespace or\n * Signature-Agent) → ACP HMAC → UCP session → AP2 body triple → MPP → x402.\n * Returns undefined when nothing commerce-shaped is on the wire.\n *\n * When the body was truncated by CloudFront (`inputTruncated`), body-bound\n * artifacts still forward what arrived but carry a `bodyTruncated` evidence\n * flag — the backend downgrades those signatures rather than hard-failing.\n */\nexport function detectCommerce(\n request: CloudFrontRequest,\n host: string,\n body: DecodedBody\n): DetectedCommerce | undefined {\n const url = requestUrl(request, host);\n const method = request.method;\n const headers = flattenHeaders(request.headers);\n const jsonBody = parseJsonBody(body);\n const truncated = body.truncated ? { bodyTruncated: true } : {};\n\n // --- VI / SD-JWT credential in a header ------------------------------\n const authHeader = getHeader(request, 'authorization');\n const bearer = authHeader?.replace(/^Bearer\\s+/i, '');\n const mandateHeader =\n getHeader(request, 'x-payment-mandate') ?? getHeader(request, 'payment-mandate');\n const sdJwtCandidate = [mandateHeader, bearer].find((v) => v && SD_JWT_PATTERN.test(v));\n if (sdJwtCandidate) {\n const typ = sniffJwtHeader(sdJwtCandidate)?.typ;\n return {\n protocol: 'vi-sd-jwt',\n artifacts: { viSdJwt: sdJwtCandidate },\n evidence: { typ, ...truncated },\n };\n }\n\n // --- RFC 9421 (Agent Pay / TAP / Web Bot Auth) ------------------------\n const signature = getHeader(request, 'signature');\n const signatureInput = getHeader(request, 'signature-input');\n if (signature && signatureInput) {\n const signatureAgent = getHeader(request, 'signature-agent');\n const registryHint = getHeader(request, 'x-agent-registry');\n const lowerInput = signatureInput.toLowerCase();\n const protocol = signatureAgent\n ? 'rfc9421-web-bot-auth'\n : registryHint?.toLowerCase().includes('visa') || lowerInput.includes('visa')\n ? 'rfc9421-tap'\n : registryHint?.toLowerCase().includes('mastercard') || lowerInput.includes('mastercard')\n ? 'rfc9421-agent-pay'\n : 'rfc9421';\n return {\n protocol,\n artifacts: {\n rfc9421: { headers, method, url, ...(body.text && { body: body.text }) },\n },\n evidence: {\n ...(signatureAgent && { signatureAgent }),\n ...(registryHint && { registryHint }),\n keyid: signatureInput.match(/keyid=\"([^\"]+)\"/)?.[1],\n ...truncated,\n },\n };\n }\n\n // --- ACP (HMAC-signed webhook/checkout: signature WITHOUT signature-input)\n const acpTimestamp = getHeader(request, 'timestamp');\n if (signature && !signatureInput && (acpTimestamp || getHeader(request, 'idempotency-key'))) {\n return {\n protocol: 'acp',\n artifacts: {\n acpRequest: {\n method,\n url,\n headers,\n ...(body.text && { rawBody: body.text }),\n ...(jsonBody && { body: jsonBody }),\n },\n },\n evidence: { hasTimestamp: Boolean(acpTimestamp), ...truncated },\n };\n }\n\n // --- UCP checkout session ---------------------------------------------\n if (/\\/checkout[-_]sessions(\\/|$)/.test(request.uri)) {\n return {\n protocol: 'ucp',\n artifacts: { ucpRequest: { method, url, ...(jsonBody && { body: jsonBody }) } },\n evidence: { path: request.uri, ...truncated },\n };\n }\n\n // --- AP2 mandate triple in the body -----------------------------------\n if (jsonBody) {\n const intent = jsonBody.intent_mandate ?? jsonBody.intentMandate;\n const cart = jsonBody.cart_mandate ?? jsonBody.cartMandate;\n const payment = jsonBody.payment_mandate ?? jsonBody.paymentMandate;\n if (typeof intent === 'string' || typeof cart === 'string' || typeof payment === 'string') {\n return {\n protocol: 'ap2',\n artifacts: {\n ap2Mandates: {\n ...(typeof intent === 'string' && { intent }),\n ...(typeof cart === 'string' && { cart }),\n ...(typeof payment === 'string' && { payment }),\n },\n },\n evidence: {\n mandatesPresent: [\n typeof intent === 'string' && 'intent',\n typeof cart === 'string' && 'cart',\n typeof payment === 'string' && 'payment',\n ].filter(Boolean),\n ...truncated,\n },\n };\n }\n }\n\n // --- MPP (`Authorization: Payment <credential>`) -----------------------\n if (authHeader && /^Payment\\s+/i.test(authHeader)) {\n return {\n protocol: 'mpp',\n artifacts: {\n mppRequest: {\n method,\n url,\n headers,\n ...(body.text && { rawBody: body.text }),\n ...(jsonBody && { body: jsonBody }),\n },\n },\n evidence: truncated,\n };\n }\n\n // --- x402 (X-PAYMENT header) ------------------------------------------\n if (getHeader(request, 'x-payment')) {\n return {\n protocol: 'x402',\n artifacts: {\n x402Request: { method, url, headers, ...(jsonBody && { body: jsonBody }) },\n },\n evidence: truncated,\n };\n }\n\n return undefined;\n}\n","/**\n * Depth-controlled verification per tier. The edge never verifies\n * credentials itself — it forwards what it detected to verify-access, the\n * canonical sole verification sink (backend Step 0.5 runs the commerce\n * pipeline, records events, provisions orphans, persists sessions). What\n * the depth setting controls is how much the edge LOOKS and how much it\n * FORWARDS:\n *\n * classify — what is it? UA tiers only. Zero verify-access calls;\n * beacons only.\n * authenticate — who is it? + protocol detection. X-Astra agents get a\n * verify-access identity check; commerce/platform traffic\n * is beaconed with protocol + evidence (no signature\n * verification anywhere).\n * authorize — are they allowed? + artifact forwarding. X-Astra verify\n * calls carry commerceArtifacts; identified traffic gets an\n * anonymous verify-access call with artifacts +\n * callerMetadata so the backend cryptographically proves\n * and policy-evaluates once.\n */\n\nimport type { CloudFrontRequest } from 'aws-lambda';\nimport { verify, extractCredentials } from '@astrasyncai/verification-gateway/verify';\nimport { resolveHttpPdlss } from '@astrasyncai/verification-gateway/http-pdlss';\nimport type { GatewayConfig, VerificationRequest } from '@astrasyncai/verification-gateway';\nimport { flattenHeaders, getHeader, buildObservedMetadata } from './cf';\nimport { withBudget, emitBeacon, sampled } from './events';\nimport type { Classification, EdgeAdapterOptions, EffectivePolicy, TierOutcome } from './types';\n\nfunction baseOutcome(classification: Classification): TierOutcome {\n return {\n tier: classification.tier,\n decision: 'allow',\n reasons: [],\n protocol: classification.protocol,\n };\n}\n\nfunction buildVerificationRequest(\n options: EdgeAdapterOptions,\n request: CloudFrontRequest,\n classification: Classification,\n includeArtifacts: boolean\n): VerificationRequest {\n const headers = flattenHeaders(request.headers);\n const clientIp = getHeader(request, 'cloudfront-viewer-address')?.split(':')[0];\n // verify-access REQUIRES a purpose. Resolve via the shared §4.6 chain:\n // the agent's X-Astra-Purpose/-Action declarations, else the pinned\n // method table (GET → data/data.read etc.) — a transport verb never\n // travels as a PDLSS action.\n const pdlss = resolveHttpPdlss({\n method: request.method,\n astraPurpose: getHeader(request, 'x-astra-purpose'),\n astraAction: getHeader(request, 'x-astra-action'),\n });\n return {\n credentials: extractCredentials(headers, {}),\n purpose: pdlss.purpose,\n action: pdlss.action,\n resource: request.uri,\n createSession: true,\n counterpartyType: 'website',\n counterpartyUrl: options.counterpartyUrl,\n callerMetadata: {\n sourceIp: clientIp,\n userAgent: getHeader(request, 'user-agent'),\n referer: getHeader(request, 'referer'),\n host: getHeader(request, 'host'),\n // Workstream D — the full sanitised header + connection capture. The\n // backend stores it fire-and-forget (zero decision latency) and uses the\n // key-format / platform headers for attribution.\n observedMetadata: buildObservedMetadata(request),\n },\n ...(includeArtifacts &&\n classification.artifacts && { commerceArtifacts: classification.artifacts }),\n };\n}\n\nfunction outcomeFromVerifyResult(\n classification: Classification,\n result: Awaited<ReturnType<typeof verify>>\n): TierOutcome {\n const outcome = baseOutcome(classification);\n outcome.sessionId = (result as { sessionId?: string }).sessionId;\n outcome.correlationId = result.correlationId;\n outcome.guidance = result.guidance;\n\n if (result.requiresStepUp || result.stepUpApproval) {\n outcome.decision = 'manual_review';\n outcome.reasons = ['step-up approval required'];\n outcome.stepUp = {\n pollUrl: (result.stepUpApproval as { pollUrl?: string } | undefined)?.pollUrl,\n expiresAt: (result.stepUpApproval as { expiresAt?: string } | undefined)?.expiresAt,\n };\n return outcome;\n }\n\n if (!result.identityVerified || !result.policyAllowed) {\n outcome.decision = 'deny';\n outcome.reasons = result.denialReasons?.length\n ? result.denialReasons\n : [!result.identityVerified ? 'identity not verified' : 'denied by access policy'];\n return outcome;\n }\n\n outcome.asat = {\n trustScore: result.agent?.trustScore,\n agentId: result.agent?.astraId,\n developerId: result.developer?.astradId,\n accessLevel: result.accessLevel,\n };\n return outcome;\n}\n\n/**\n * Run the tier-appropriate verification/telemetry for one classified\n * request. NEVER throws; on any failure returns an `allow` outcome with the\n * failure recorded in `reasons` (infra failures fail open — a presented\n * credential the BACKEND rejected is a deny, but an unreachable backend is\n * not a rejection).\n */\nexport async function runTier(\n options: EdgeAdapterOptions,\n request: CloudFrontRequest,\n classification: Classification,\n policy: EffectivePolicy,\n apiKey: string | undefined,\n sampling: { anonymousBeaconRate: number }\n): Promise<TierOutcome> {\n const budgets = options.budgets ?? {};\n const beaconMs = budgets.beaconMs ?? 300;\n const verifyMs =\n policy.mode === 'enforce'\n ? (budgets.verifyEnforceMs ?? 3000)\n : (budgets.verifyObserveMs ?? 1500);\n\n const gatewayConfig: GatewayConfig = {\n apiBaseUrl: options.apiBaseUrl,\n ...(apiKey && { apiKey }),\n counterpartyId: options.counterpartyId,\n // The shell handles its own error semantics; init self-probes don't\n // belong in a per-request edge function.\n disableInitChecks: true,\n } as GatewayConfig;\n\n try {\n switch (classification.tier) {\n case 'verified': {\n // classify depth = zero API calls, even for credentialed agents.\n if (policy.depth === 'classify') {\n return baseOutcome(classification);\n }\n const result = await withBudget(\n verify(\n gatewayConfig,\n buildVerificationRequest(options, request, classification, policy.depth === 'authorize')\n ),\n verifyMs\n );\n if (!result) {\n const outcome = baseOutcome(classification);\n outcome.reasons = ['verify-access unreachable within budget — fail-open'];\n return outcome;\n }\n return outcomeFromVerifyResult(classification, result);\n }\n\n case 'identified': {\n if (policy.depth === 'authorize' && classification.artifacts) {\n // Anonymous verify-access call WITH artifacts: the backend runs\n // the commerce pipeline, records unverified events, and\n // provisions orphan platform agents — one sink, recorded once.\n const result = await withBudget(\n verify(gatewayConfig, buildVerificationRequest(options, request, classification, true)),\n verifyMs\n );\n if (!result) {\n const outcome = baseOutcome(classification);\n outcome.reasons = ['verify-access unreachable within budget — fail-open'];\n return outcome;\n }\n return outcomeFromVerifyResult(classification, result);\n }\n // classify/credentials depth (or no artifacts): lightweight beacon.\n await emitBeacon(options, request, classification, beaconMs);\n const outcome = baseOutcome(classification);\n if (policy.mode === 'enforce' && policy.depth !== 'classify') {\n // Unregistered agent on an enforced path: deny with guidance.\n outcome.decision = 'deny';\n outcome.reasons = ['agent is not registered with AstraSync'];\n }\n return outcome;\n }\n\n case 'anonymous-bot': {\n if (sampled(sampling.anonymousBeaconRate, options.random)) {\n await emitBeacon(\n options,\n request,\n { ...classification, protocol: classification.protocol ?? 'ua-heuristic' },\n beaconMs\n );\n }\n // Anonymous bots pass through even in enforce mode — enforce gates\n // AGENT traffic; generic bot management stays the CDN/WAF's job\n // (mixed-traffic classification §5.1.7 is the enforce-mode\n // prerequisite tracked as §33.5 #9).\n return baseOutcome(classification);\n }\n\n default:\n return baseOutcome(classification);\n }\n } catch (err) {\n const outcome = baseOutcome(classification);\n outcome.reasons = [\n `edge pipeline error — fail-open: ${err instanceof Error ? err.message : String(err)}`,\n ];\n return outcome;\n }\n}\n","/**\n * Time-budgeted event emission.\n *\n * Lambda@Edge has no `waitUntil` — anything not awaited before the handler\n * returns may never run (the container freezes). So every network call is\n * awaited under an explicit budget and DROPPED on timeout: latency to the\n * customer's traffic is bounded, and a slow AstraSync API costs telemetry,\n * never page loads.\n */\n\nimport { reportUnregisteredAttempt } from '@astrasyncai/verification-gateway/verify';\nimport type { CloudFrontRequest } from 'aws-lambda';\nimport { getHeader, buildObservedMetadata } from './cf';\nimport type { Classification, EdgeAdapterOptions } from './types';\n\n/** Await `promise` for at most `ms`; resolve `undefined` on timeout/error. */\nexport async function withBudget<T>(promise: Promise<T>, ms: number): Promise<T | undefined> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n promise,\n new Promise<undefined>((resolve) => {\n timer = setTimeout(() => resolve(undefined), ms);\n }),\n ]);\n } catch {\n return undefined;\n } finally {\n if (timer) clearTimeout(timer);\n }\n}\n\n/**\n * Emit an unregistered-attempt beacon for `identified` (non-crypto depths)\n * and sampled `anonymous-bot` traffic. Unauthenticated by design\n * (CSRF-exempt on the backend); the backend fingerprints the UA and\n * find-or-provisions orphan platform agents server-side.\n */\nexport async function emitBeacon(\n options: EdgeAdapterOptions,\n request: CloudFrontRequest,\n classification: Classification,\n budgetMs: number\n): Promise<void> {\n const clientIp = getHeader(request, 'cloudfront-viewer-address')?.split(':')[0];\n await withBudget(\n reportUnregisteredAttempt(\n { apiBaseUrl: options.apiBaseUrl, counterpartyId: options.counterpartyId },\n {\n counterpartyUrl: options.counterpartyUrl,\n counterpartyType: 'website',\n sourceIp: clientIp,\n userAgent: getHeader(request, 'user-agent'),\n requestPath: request.uri,\n requestMethod: request.method,\n // 3.9.0 evidence fields (backend persists them into eventData).\n protocol: classification.protocol,\n evidence: classification.evidence,\n // Workstream D — full sanitised header + connection capture for the\n // unregistered caller (fire-and-forget, so free).\n observedMetadata: buildObservedMetadata(request),\n }\n ),\n budgetMs\n );\n}\n\n/** Deterministic-in-tests sampling gate. */\nexport function sampled(rate: number, random: () => number = Math.random): boolean {\n if (rate <= 0) return false;\n if (rate >= 1) return true;\n return random() < rate;\n}\n","/**\n * §7.4.6 — protocol-specific machine-readable messaging for enforce-mode\n * responses. Agents (not humans) consume these bodies: stable error codes,\n * the failure list, and registration guidance with real URLs.\n */\n\nimport type { TierOutcome } from './types';\n\nconst PROTOCOL_MESSAGES: Record<string, string> = {\n 'vi-sd-jwt':\n 'This request presented Visa Intelligent Commerce credentials, but the agent is not verified with AstraSync. Register the agent to pair your VI credentials with a verified identity and mandate.',\n 'rfc9421-agent-pay':\n 'This request carried a Mastercard Agent Pay signature, but the agent is not verified with AstraSync. Register the agent to pair Agent Pay credentials with a verified identity and mandate.',\n 'rfc9421-tap':\n 'This request carried a Visa TAP signature, but the agent is not verified with AstraSync. Register the agent to pair TAP credentials with a verified identity and mandate.',\n 'rfc9421-web-bot-auth':\n 'This request carried a Web Bot Auth signature, but the agent is not verified with AstraSync. Register the agent to link your signed-agent identity to a verified principal.',\n rfc9421:\n 'This request carried an RFC 9421 HTTP message signature, but the agent is not verified with AstraSync. Register the agent to link the signing identity to a verified principal.',\n acp: 'This request used the Agentic Commerce Protocol, but the agent is not verified with AstraSync. Register the agent to transact on this property.',\n ucp: 'This request used a UCP checkout session, but the agent is not verified with AstraSync. Register the agent to transact on this property.',\n ap2: 'This request presented AP2 mandates, but the agent is not verified with AstraSync. Register the agent to pair your mandates with a verified identity.',\n mpp: 'This request presented MPP payment credentials, but the agent is not verified with AstraSync. Register the agent to transact on this property.',\n x402: 'This request used x402 payment headers, but the agent is not verified with AstraSync. Register the agent to transact on this property.',\n 'platform-ua':\n 'This traffic was identified as a platform AI agent without AstraSync credentials. Register the agent to access this property with a verified identity.',\n 'x-astra':\n 'The presented AstraSync credentials failed verification or were denied by this property’s access policy.',\n};\n\nexport interface DenyBody {\n success: false;\n error: {\n code: 'UNAUTHORIZED' | 'INSUFFICIENT_ACCESS';\n message: string;\n protocol?: string;\n failures: string[];\n correlationId?: string;\n guidance: {\n message?: string;\n registrationUrl?: string;\n documentationUrl?: string;\n };\n };\n}\n\nexport function buildDenyBody(outcome: TierOutcome, apiBaseUrl: string): DenyBody {\n const origin = apiBaseUrl.replace(/\\/api\\/?$/, '');\n const protocolMessage =\n (outcome.protocol && PROTOCOL_MESSAGES[outcome.protocol]) ??\n 'This request could not be verified by AstraSync.';\n return {\n success: false,\n error: {\n // UNAUTHORIZED = identity failed (register/re-authenticate helps);\n // INSUFFICIENT_ACCESS = identity fine, policy denied (fix the mandate).\n code: outcome.tier === 'verified' ? 'INSUFFICIENT_ACCESS' : 'UNAUTHORIZED',\n message: protocolMessage,\n ...(outcome.protocol && { protocol: outcome.protocol }),\n failures: outcome.reasons,\n ...(outcome.correlationId && { correlationId: outcome.correlationId }),\n guidance: {\n // `||` not `??`: verify() fills these with EMPTY STRINGS when the\n // well-known cache is cold — empty means \"use the fallback\".\n message:\n outcome.guidance?.message ||\n 'Register this agent with AstraSync to receive verifiable credentials.',\n registrationUrl: outcome.guidance?.registrationUrl || `${origin}/register`,\n documentationUrl: outcome.guidance?.documentationUrl || `${origin}/docs/edge-adapter`,\n },\n },\n };\n}\n\nexport interface ManualReviewBody {\n success: false;\n status: 'manual_review';\n message: string;\n stepUpApproval?: { pollUrl?: string; expiresAt?: string };\n correlationId?: string;\n}\n\nexport function buildManualReviewBody(outcome: TierOutcome): ManualReviewBody {\n return {\n success: false,\n status: 'manual_review',\n message:\n 'This action requires human approval. Poll the step-up approval endpoint and retry once approved.',\n ...(outcome.stepUp && { stepUpApproval: outcome.stepUp }),\n ...(outcome.correlationId && { correlationId: outcome.correlationId }),\n };\n}\n","/**\n * Decision → CloudFront result wiring (§7.4 enforcement, §7.4.7 ASAT\n * headers). Executed in BOTH modes so observe mode records exactly what\n * enforce mode would have done — but only enforce mode is allowed to return\n * anything other than the origin-bound request.\n */\n\nimport type { CloudFrontRequest, CloudFrontRequestResult } from 'aws-lambda';\nimport { jsonResponse, setHeader, stripHeadersByPrefix } from './cf';\nimport { buildDenyBody, buildManualReviewBody } from './messaging';\nimport type { EdgeAdapterOptions, EffectivePolicy, TierOutcome } from './types';\n\n/** §7.4.7 ASAT trust-attestation headers injected on allowed requests. */\nexport const ASAT_HEADERS = {\n trustScore: 'X-AstraSync-Trust-Score',\n agentId: 'X-AstraSync-Agent-Id',\n developerId: 'X-AstraSync-Developer-Id',\n permissions: 'X-AstraSync-Permissions',\n observedDecision: 'X-AstraSync-Observed-Decision',\n} as const;\n\n/**\n * Apply the outcome. Always strips inbound spoofed `x-astrasync-*` headers\n * first (both modes — trust headers must only ever originate here).\n *\n * Observe mode: returns the request untouched apart from header hygiene and\n * an `X-AstraSync-Observed-Decision` stamp for origin-side log correlation.\n *\n * Enforce mode: ALLOW → request + ASAT headers; DENY → 403 machine-readable\n * body; MANUAL_REVIEW → 202 with step-up poll info.\n */\nexport function applyOutcome(\n options: EdgeAdapterOptions,\n request: CloudFrontRequest,\n outcome: TierOutcome,\n policy: EffectivePolicy\n): CloudFrontRequestResult {\n stripHeadersByPrefix(request, 'x-astrasync-');\n\n if (policy.mode !== 'enforce') {\n // Shadow stamp: what enforce WOULD have done, visible in origin logs.\n if (outcome.decision !== 'allow') {\n setHeader(request, ASAT_HEADERS.observedDecision, outcome.decision);\n }\n injectAsat(request, outcome);\n return request;\n }\n\n switch (outcome.decision) {\n case 'deny':\n return jsonResponse(403, buildDenyBody(outcome, options.apiBaseUrl));\n case 'manual_review':\n return jsonResponse(202, buildManualReviewBody(outcome), {\n 'Retry-After': '30',\n });\n default:\n injectAsat(request, outcome);\n return request;\n }\n}\n\nfunction injectAsat(request: CloudFrontRequest, outcome: TierOutcome): void {\n if (!outcome.asat) return;\n const { trustScore, agentId, developerId, accessLevel } = outcome.asat;\n if (trustScore !== undefined) {\n setHeader(request, ASAT_HEADERS.trustScore, String(trustScore));\n }\n if (agentId) setHeader(request, ASAT_HEADERS.agentId, agentId);\n if (developerId) setHeader(request, ASAT_HEADERS.developerId, developerId);\n if (accessLevel) setHeader(request, ASAT_HEADERS.permissions, accessLevel);\n}\n","/**\n * Commerce Shield edge shell — Lambda@Edge origin-request handler factory\n * (§7.4 / §33.5 #1).\n *\n * Composition per request:\n * config (cached) → path policy → classify → tier pipeline → apply outcome\n *\n * THE invariant (tested): outside enforce mode the handler ALWAYS returns\n * the origin-bound request — any error, timeout, or misconfiguration\n * results in pass-through. A top-level catch guarantees it even against\n * bugs in the shell itself.\n */\n\nimport type { CloudFrontRequestEvent, CloudFrontRequestResult } from 'aws-lambda';\nimport { getRequest, getHeader, normalizeOriginHost } from './cf';\nimport { resolveApiKey, loadEdgeConfig } from './config';\nimport { classify, resolvePolicy } from './classify';\nimport { runTier } from './pipeline';\nimport { applyOutcome } from './enforce';\nimport type { EdgeAdapterOptions } from './types';\n\nexport type EdgeHandler = (event: CloudFrontRequestEvent) => Promise<CloudFrontRequestResult>;\n\nexport function createEdgeHandler(options: EdgeAdapterOptions): EdgeHandler {\n const log =\n options.log ??\n ((level, message, data) => {\n // Lambda@Edge writes console output to the regional CloudWatch log\n // group of whichever edge location served the request.\n console[level === 'info' ? 'log' : level](`[astrasync-edge] ${message}`, data ?? '');\n });\n\n return async function handler(event) {\n const request = getRequest(event);\n try {\n // Own the Host rewrite (Workstream D): the CloudFront-Viewer-* capture\n // requires an origin-request policy that also forwards the viewer Host,\n // so the shell restores origin-domain Host + X-Forwarded-Host itself.\n // Runs first so EVERY origin-bound return (skip / human / allow) is\n // correctly routed.\n normalizeOriginHost(request);\n\n const apiKey = await resolveApiKey({ ...options, log });\n const config = await loadEdgeConfig(options, apiKey);\n const policy = resolvePolicy(config, request.uri);\n\n // Skipped paths (static assets / skip rules): untouched, zero work.\n if (policy.skip) return request;\n\n const host =\n getHeader(request, 'x-forwarded-host') ??\n getHeader(request, 'host') ??\n new URL(options.counterpartyUrl).host;\n\n const classification = classify(request, host, policy.depth);\n\n // Humans: get out of the way. No calls, no logs, no header changes\n // beyond nothing at all.\n if (classification.tier === 'human') return request;\n\n const outcome = await runTier(\n { ...options, log },\n request,\n classification,\n policy,\n apiKey,\n config.sampling\n );\n\n if (outcome.decision !== 'allow') {\n log(\n 'info',\n policy.mode === 'enforce'\n ? `${outcome.decision.toUpperCase()}: ${request.method} ${request.uri}`\n : `[OBSERVE] would have been ${outcome.decision.toUpperCase()}: ${request.method} ${request.uri}`,\n { tier: outcome.tier, protocol: outcome.protocol, reasons: outcome.reasons }\n );\n }\n\n return applyOutcome(options, request, outcome, policy);\n } catch (err) {\n // Fail-open, unconditionally: the shell must never take the site down.\n log('error', 'edge handler error — passing request through', {\n error: err instanceof Error ? err.message : String(err),\n });\n return request;\n }\n };\n}\n"],"mappings":"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","names":["sanitizeHeaders","sanitizeHeaders","extractCredentials","extractCredentials","outcome"]}
@@ -1 +1 @@
1
- "use strict";var he=Object.create;var S=Object.defineProperty;var ve=Object.getOwnPropertyDescriptor;var Ae=Object.getOwnPropertyNames;var we=Object.getPrototypeOf,Re=Object.prototype.hasOwnProperty;var Ce=(e,t)=>{for(var r in t)S(e,r,{get:t[r],enumerable:!0})},D=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of Ae(t))!Re.call(e,a)&&a!==r&&S(e,a,{get:()=>t[a],enumerable:!(n=ve(t,a))||n.enumerable});return e};var Te=(e,t,r)=>(r=e!=null?he(we(e)):{},D(t||!e||!e.__esModule?S(r,"default",{value:e,enumerable:!0}):r,e)),Se=e=>D(S({},"__esModule",{value:!0}),e);var at={};Ce(at,{handler:()=>nt});module.exports=Se(at);function F(e){return e.Records[0].cf.request}function c(e,t){return e.headers[t.toLowerCase()]?.[0]?.value}function w(e){let t={};for(let[r,n]of Object.entries(e))n?.length&&(t[r]=n.length===1?n[0].value:n.map(a=>a.value));return t}function k(e){let t=e.body;return t?.data?{text:t.encoding==="base64"?Buffer.from(t.data,"base64").toString("utf8"):t.data,truncated:t.inputTruncated===!0}:{truncated:!1}}function M(e,t){let r=e.querystring?`?${e.querystring}`:"";return`https://${t}${e.uri}${r}`}function R(e,t,r){e.headers[t.toLowerCase()]=[{key:t,value:r}]}function N(e,t){let r=t.toLowerCase();for(let n of Object.keys(e.headers))n.startsWith(r)&&delete e.headers[n]}function U(e,t,r){let n={"content-type":[{key:"Content-Type",value:"application/json"}]};for(let[a,i]of Object.entries(r??{}))n[a.toLowerCase()]=[{key:a,value:i}];return{status:String(e),statusDescription:e===403?"Forbidden":e===202?"Accepted":"OK",headers:n,body:JSON.stringify(t)}}var P=Object.freeze({version:1,mode:"observe",depth:"classify",pathRules:[],sampling:Object.freeze({anonymousBeaconRate:.1}),failurePosture:"open"}),H=["observe","enforce"],L=["classify","authenticate","authorize"];function be(e){if(typeof e!="object"||e===null)return!1;let t=e;if(t.version!==1||!H.includes(t.mode)||!L.includes(t.depth)||!Array.isArray(t.pathRules))return!1;for(let n of t.pathRules){if(typeof n!="object"||n===null)return!1;let a=n;if(typeof a.pattern!="string"||a.pattern.length===0||a.mode!==void 0&&!H.includes(a.mode)||a.depth!==void 0&&!L.includes(a.depth))return!1}let r=t.sampling;return typeof r!="object"||r===null||typeof r.anonymousBeaconRate!="number"||r.anonymousBeaconRate<0||r.anonymousBeaconRate>1?!1:t.failurePosture==="open"}async function Ie(e,t,r){if(!t)return null;let n={Accept:"application/json"};e.apiKey&&(n.Authorization=`Bearer ${e.apiKey}`,n["X-API-Key"]=e.apiKey),r?.etag&&(n["If-None-Match"]=r.etag);try{let a=await fetch(`${e.apiBaseUrl}/endpoints/${encodeURIComponent(t)}/edge-config`,{method:"GET",headers:n,signal:AbortSignal.timeout(r?.timeoutMs??5e3)});if(a.status===304)return{notModified:!0};if(!a.ok)return null;let o=(await a.json()).data?.edgeConfig;return be(o)?{edgeConfig:o,etag:a.headers.get("etag")??void 0}:null}catch{return null}}var Ue=60*1e3,Ee=1440*60*1e3,C=new Map,E=new Map;function V(e,t){return`${e}|${t}`}function Pe(e){return e.mode!=="enforce"&&!e.pathRules.some(t=>t.mode==="enforce")?e:{...e,mode:"observe",pathRules:e.pathRules.map(t=>t.mode==="enforce"?{...t,mode:"observe"}:t)}}async function $(e,t){let r=V(e.apiBaseUrl,t),n=E.get(r);if(n)return n;let a=Ie(e,t,{etag:C.get(r)?.etag}).then(i=>{if(i===null)return;let o=Date.now();if("notModified"in i){let s=C.get(r);s&&(s.lastSuccessAt=o);return}C.set(r,{config:i.edgeConfig,etag:i.etag,lastSuccessAt:o})}).finally(()=>{E.delete(r)});return E.set(r,a),a}async function K(e,t,r){let n=r?.ttlMs??Ue,a=r?.maxStaleMs??Ee,i=V(e.apiBaseUrl,t),o=C.get(i);if(o){let s=Date.now()-o.lastSuccessAt;return s>n&&$(e,t).catch(()=>{}),s>a?Pe(o.config):o.config}return await $(e,t).catch(()=>{}),C.get(i)?.config??P}function j(e,t){for(let r of e)if(xe(r.pattern).test(t))return r}function xe(e){let t=e.replace(/[.+?^${}()|[\]\\]/g,"\\$&").replace(/\*/g,".*");return new RegExp(`^${t}$`,"i")}var _e=900*1e3,G=new Map;async function Oe(e){let{SecretsManagerClient:t,GetSecretValueCommand:r}=await import("@aws-sdk/client-secrets-manager");return(await new t({region:"us-east-1"}).send(new r({SecretId:e}))).SecretString??void 0}async function q(e){if(e.apiKey)return e.apiKey;let t=e.apiKeySecretName;if(!t)return;let r=G.get(t);if(r&&Date.now()-r.fetchedAt<_e)return r.value;try{let a=await(e.fetchSecret??Oe)(t);return G.set(t,{value:a,fetchedAt:Date.now()}),a}catch(n){return e.log?.("warn","Secrets Manager fetch failed \u2014 continuing keyless",{secretName:t,error:n instanceof Error?n.message:String(n)}),r?.value}}async function z(e,t){try{return await K({apiBaseUrl:e.apiBaseUrl,apiKey:t},e.counterpartyId,{ttlMs:e.configTtlMs})}catch{return e.fallbackConfig??P}}function Be(e){return e>=80?"PLATINUM":e>=60?"GOLD":e>=40?"SILVER":"BRONZE"}var De="3.9.0",ut=3600*1e3,Fe=new Map;function ke(e){return Fe.get(e)?.data}var Y={apiBaseUrl:"https://astrasync.ai/api",defaultAccessLevel:"none",debug:!1},W=!1,X=!1;async function J(e,t,r){W=!0;try{let n=`${e}/agents/verify-access`,i=(await fetch(n,{method:"HEAD"})).headers.get("content-type")??"";if(i.startsWith("text/html")){let o=`[VerificationGateway] apiBaseUrl '${e}' returned HTML (content-type: ${i}). 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).`;if(r)throw new Error(`${o} (strictInit=true)`);console.warn(`${o} Set disableInitChecks: true on GatewayConfig to silence.`)}else t&&console.log(`[VerificationGateway] init check passed for ${e} (content-type: ${i})`)}catch(n){if(r)throw n;t&&console.log(`[VerificationGateway] init check failed (non-blocking): ${String(n)}`)}}var x=new Map;function Z(e,t){let r=e.credentials;return[r.astraId||"",r.apiKey||"",r.jwt||"",e.purpose||"",e.action||"",e.resourceType||"",e.resource||"",e.jurisdiction||"",e.transactionValue??"",e.currency||"",t||"",e.counterpartyUrl||"",e.counterpartyType||"",e.isSubAgentRequest?"1":"0",e.parentAgentId||"",e.subAgentDepth??"",e.durationRequired??"",e.invocationProtocol||"",e.enableRuntimeChallenge?"1":"0",e.callerMetadata?.sourceIp||"",e.callerMetadata?.userAgent||"",e.callerMetadata?.forwardedFor||"",e.callerMetadata?.agentCardUrl||""].join("|")}function Me(e,t){let r=Z(e,t),n=x.get(r);return n&&n.expiresAt>Date.now()?n.result:(n&&x.delete(r),null)}var Ne=60;function He(e,t,r,n){let a=r&&r>0?r:Ne,i=Z(e,n);x.set(i,{result:t,expiresAt:Date.now()+a*1e3})}function b(e,t){let r={},n=/^ASTRAE?-[A-Za-z0-9_-]{1,64}$/,a=e["x-astra-id"]||e["X-Astra-Id"]||e["X-ASTRA-ID"]||e["x-astra-agentid"]||e["X-Astra-AgentId"]||e["x-astra-agent-id"]||e["X-Astra-Agent-Id"]||e["X-ASTRA-AGENT-ID"];if(a){let s=Array.isArray(a)?a[0]:typeof a=="string"?a:void 0;typeof s=="string"&&n.test(s)&&(r.astraId=s)}let i=e["x-api-key"]||e["X-Api-Key"]||e["X-API-KEY"];i&&(r.apiKey=Array.isArray(i)?i[0]:i);let o=e.authorization||e.Authorization;if(o){let s=Array.isArray(o)?o[0]:o;typeof s=="string"&&(r.authorizationHeader=s,s.startsWith("Bearer ")&&(r.jwt=s.slice(7)))}return t&&(t.astraId&&!r.astraId&&(r.astraId=t.astraId),t.apiKey&&!r.apiKey&&(r.apiKey=t.apiKey)),r}function Le(e,t,r={}){let a=(r.source??"no_credentials")==="api_error",i=r.urls,o=a?{message:"Verification is temporarily unavailable. Retry with exponential backoff; if the issue persists, contact support with the correlationId.",registrationUrl:i?.registrationUrl??"",documentationUrl:i?.documentationUrl??"",steps:["Retry the request with exponential backoff","If failures persist, share the correlationId with support"]}:{message:"This service verifies AI agents before granting access. Please register your agent with AstraSync.",registrationUrl:i?.registrationUrl??"",documentationUrl:i?.documentationUrl??"",steps:["Register for an AstraSync account","Create and register your agent","Add your ASTRA-ID to request headers","Retry your request"]};return{identityVerified:!1,policyAllowed:!1,accessLevel:"none",guidance:o,denialReasons:t?[t]:["No valid agent credentials provided"],failures:a?[{dimension:"verify_access.api_error",message:t??"Verification temporarily unavailable",guidance:o.message}]:void 0,correlationId:r.correlationId,verifiedAt:new Date}}async function $e(e,t){let{credentials:r,...n}=t,a={...r.astraId&&{agentId:r.astraId},...n.purpose&&{purpose:n.purpose}};if(n.action&&(a.action=n.action),n.resourceType&&(a.resourceType=n.resourceType),n.resource&&(a.resource=n.resource),n.jurisdiction&&(a.jurisdiction=n.jurisdiction),n.transactionValue&&(a.transactionValue=n.transactionValue),n.currency&&(a.currency=n.currency),n.isSubAgentRequest&&(a.isSubAgentRequest=n.isSubAgentRequest),n.parentAgentId&&(a.parentAgentId=n.parentAgentId),n.subAgentDepth!==void 0&&(a.subAgentDepth=n.subAgentDepth),n.enableRuntimeChallenge&&(a.enableRuntimeChallenge=n.enableRuntimeChallenge),n.createSession&&(a.createSession=n.createSession),n.durationRequired&&(a.durationRequired=n.durationRequired),n.counterpartyType&&(a.counterpartyType=n.counterpartyType),n.counterpartyUrl&&(a.counterpartyUrl=n.counterpartyUrl),e.counterpartyId&&(a.counterpartyId=e.counterpartyId),n.runtimeChallengeOptions&&(a.runtimeChallengeOptions=n.runtimeChallengeOptions),n.invocationProtocol&&(a.invocationProtocol=n.invocationProtocol),n.commerceArtifacts&&(a.commerceArtifacts=n.commerceArtifacts),a.sdkVersion=De,n.callerMetadata||n.clientIp||n.userAgent){let o={...n.clientIp&&{sourceIp:n.clientIp},...n.userAgent&&{userAgent:n.userAgent},...n.callerMetadata};Object.keys(o).length>0&&(a.callerMetadata=o)}let i={"Content-Type":"application/json",...e.customHeaders};e.apiKey&&(i.Authorization=`Bearer ${e.apiKey}`,i["X-API-Key"]=e.apiKey);try{let o=await fetch(`${e.apiBaseUrl}/agents/verify-access`,{method:"POST",headers:i,body:JSON.stringify(a)}),s=await o.json();return o.status===410?{success:!0,access:{allowed:!1,accessLevel:"none",reason:"endpoint_deactivated",failures:[{dimension:"endpoint.deactivated",message:typeof s?.message=="string"?s.message:"Endpoint has been deactivated",guidance:typeof s?.guidance=="string"?s.guidance:"Reactivate via POST /api/endpoints/{id}/reactivate, or update the URL on the calling agent."}]}}:o.ok?s:{success:!1,error:s.message||s.error||`API returned ${o.status}`,correlationId:typeof s?.correlationId=="string"?s.correlationId:void 0}}catch(o){return{success:!1,error:`Failed to call verify-access API: ${o instanceof Error?o.message:"Unknown error"}`}}}async function _(e,t,r){let n={...Y,...e},a=n.apiBaseUrl?ke(n.apiBaseUrl):void 0;if(!W&&!n.disableInitChecks&&n.apiBaseUrl&&(n.strictInit?await J(n.apiBaseUrl,n.debug,!0):J(n.apiBaseUrl,n.debug,!1)),!X&&(e.minTrustScore!==void 0||e.minTrustScoreForFull!==void 0)&&(X=!0,console.warn("[VerificationGateway] minTrustScore / minTrustScoreForFull are deprecated in v2.3.0 and have no effect. Server is now the single source of truth for access-level decisions (the SDK reads access.accessLevel from the verify-access response). To gate access to an endpoint, configure the endpoint's trust_score_requirement server-side.")),n.cacheTtl!==0&&!r?.bypassCache){let l=Me(t,n.counterpartyId);if(l)return n.debug&&console.log("[VerificationGateway] Returning cached result"),l}let i={...t};!i.counterpartyUrl&&n.counterpartyUrl&&(i.counterpartyUrl=n.counterpartyUrl),!i.counterpartyType&&n.counterpartyType&&(i.counterpartyType=n.counterpartyType),n.debug&&console.log("[VerificationGateway] Calling verify-access API");let o=await $e(n,i),s=o.denialReasons,f=Array.isArray(s)&&s.length>0?s:void 0;if(!o.success)return Le(n,o.error,{source:"api_error",correlationId:o.correlationId,urls:a});if(!o.access?.allowed){let l=o.access?.failures;return{identityVerified:o.verificationContext?.idVerified===!0,policyAllowed:!1,accessLevel:"none",denialReasons:f??(l&&l.length>0?l.map(me=>me.message):o.access?.reason?[o.access.reason]:["Access denied"]),failures:l,requiresStepUp:o.access?.requiresStepUp,requiresApproval:o.access?.requiresApproval,guidance:{message:o.access?.reason||"Access denied by PDLSS policy",registrationUrl:a?.registrationUrl??"",documentationUrl:a?.documentationUrl??""},verifiedAt:new Date,sessionId:o.sessionId,correlationId:o.correlationId,recommendation:o.recommendation,recommendationReasons:o.recommendationReasons,stepUpApproval:o.stepUpApproval,settlement:o.settlement}}let y=o.agent?{astraId:o.agent.astraId,name:o.agent.name,trustScore:o.agent.trustScore,trustLevel:Be(o.agent.trustScore),blockchainVerified:o.agent.blockchainStatus==="verified",status:o.agent.agentStatus}:void 0,u=o.developer?{astradId:o.developer.kyaOwnerId,name:o.developer.fullName,trustScore:o.developer.trustScore||0,verified:o.developer.identityVerified}:void 0,g=o.organization?{name:o.organization.name,verified:o.organization.verified,trustScore:o.organization.trustScore}:void 0,v=o.verificationContext,A=o.access?.accessLevel??"standard",d={identityVerified:o.verificationContext?.idVerified!==!1,policyAllowed:!0,accessLevel:A,agent:y,developer:u,organization:g,appliedPolicy:o.access?.appliedPolicy,verificationContext:v,requiresStepUp:o.access?.requiresStepUp,requiresApproval:o.access?.requiresApproval,verifiedAt:new Date,cacheTtl:n.cacheTtl,sessionId:o.sessionId,correlationId:o.correlationId,runtimeChallenge:o.runtimeChallenge,tokenGuidance:o.tokenGuidance,recommendation:o.recommendation,recommendationReasons:o.recommendationReasons,warningHeader:o.warningHeader,stepUpApproval:o.stepUpApproval,settlement:o.settlement};d.recommendation==="deny"?(d.policyAllowed=!1,d.accessLevel="none",d.denialReasons=f??d.recommendationReasons??["Access denied by AstraSync recommendation"],d.guidance=d.runtimeChallenge?{message:`Verification failed: ${d.runtimeChallenge.reason||"runtime challenge failed"}`,registrationUrl:a?.registrationUrl??"",documentationUrl:a?.documentationUrl??""}:{message:d.recommendationReasons?.[0]||"Access denied by AstraSync recommendation",registrationUrl:a?.registrationUrl??"",documentationUrl:a?.documentationUrl??""}):d.recommendation==="step_up_required"&&(d.requiresStepUp=!0,d.denialReasons=f??d.recommendationReasons??["Step-up verification required"]);let p=d.requiresStepUp===!0||d.requiresApproval===!0||d.recommendation==="step_up_required";return n.cacheTtl!==0&&d.recommendation!=="deny"&&!p&&He(t,d,n.cacheTtl,n.counterpartyId),d}async function Q(e,t){let r=e.apiBaseUrl||Y.apiBaseUrl,{counterpartyUrl:n,...a}=t,i={...a,...n&&{counterpartyUrl:n},...e.counterpartyId&&{counterpartyId:e.counterpartyId}};await fetch(`${r}/verification-activity/unregistered-attempt`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)}).catch(()=>{})}var Ve=[{vendor:"claude",displayName:"Anthropic Claude",uaPatterns:[/claude/i,/anthropic/i],agentCardPatterns:[/anthropic\.com/i,/claude\.ai/i]},{vendor:"chatgpt",displayName:"OpenAI ChatGPT",uaPatterns:[/chatgpt/i,/openai/i,/gpt-?\d/i],agentCardPatterns:[/openai\.com/i,/chatgpt\.com/i]},{vendor:"gemini",displayName:"Google Gemini",uaPatterns:[/gemini/i,/google-gemini/i,/bard/i],agentCardPatterns:[/gemini\.google\.com/i,/ai\.google\.dev/i]},{vendor:"cursor",displayName:"Cursor",uaPatterns:[/cursor/i],agentCardPatterns:[/cursor\.sh/i,/cursor\.com/i]},{vendor:"goose",displayName:"Block Goose",uaPatterns:[/goose/i,/block-goose/i],agentCardPatterns:[/block\.xyz\/goose/i,/goose\.tools/i]}];function O(e){let t=e.userAgent??"",r=e.agentCardUrl??"";for(let n of Ve){let a=n.uaPatterns?.some(o=>o.test(t)),i=n.agentCardPatterns?.some(o=>o.test(r));if(a||i){let o=t.split(/[\s/]/)[0]?.toLowerCase()??"",s=(()=>{try{return r?new URL(r).host:""}catch{return""}})(),f=`${n.vendor}:${o||s||"unknown"}`;return{vendor:n.vendor,fingerprintKey:f,displayName:n.displayName}}}}var Ke=/^eyJ[\w-]+\.[\w-]*\.[\w-]+~/;function je(e){try{let t=e.split(".")[0];return JSON.parse(Buffer.from(t,"base64url").toString("utf8"))}catch{return}}function Ge(e){if(e.text)try{let t=JSON.parse(e.text);return typeof t=="object"&&t!==null?t:void 0}catch{return}}function ee(e,t,r){let n=M(e,t),a=e.method,i=w(e.headers),o=Ge(r),s=r.truncated?{bodyTruncated:!0}:{},f=c(e,"authorization"),y=f?.replace(/^Bearer\s+/i,""),g=[c(e,"x-payment-mandate")??c(e,"payment-mandate"),y].find(p=>p&&Ke.test(p));if(g){let p=je(g)?.typ;return{protocol:"vi-sd-jwt",artifacts:{viSdJwt:g},evidence:{typ:p,...s}}}let v=c(e,"signature"),A=c(e,"signature-input");if(v&&A){let p=c(e,"signature-agent"),l=c(e,"x-agent-registry"),m=A.toLowerCase();return{protocol:p?"rfc9421-web-bot-auth":l?.toLowerCase().includes("visa")||m.includes("visa")?"rfc9421-tap":l?.toLowerCase().includes("mastercard")||m.includes("mastercard")?"rfc9421-agent-pay":"rfc9421",artifacts:{rfc9421:{headers:i,method:a,url:n,...r.text&&{body:r.text}}},evidence:{...p&&{signatureAgent:p},...l&&{registryHint:l},keyid:A.match(/keyid="([^"]+)"/)?.[1],...s}}}let d=c(e,"timestamp");if(v&&!A&&(d||c(e,"idempotency-key")))return{protocol:"acp",artifacts:{acpRequest:{method:a,url:n,headers:i,...r.text&&{rawBody:r.text},...o&&{body:o}}},evidence:{hasTimestamp:!!d,...s}};if(/\/checkout[-_]sessions(\/|$)/.test(e.uri))return{protocol:"ucp",artifacts:{ucpRequest:{method:a,url:n,...o&&{body:o}}},evidence:{path:e.uri,...s}};if(o){let p=o.intent_mandate??o.intentMandate,l=o.cart_mandate??o.cartMandate,m=o.payment_mandate??o.paymentMandate;if(typeof p=="string"||typeof l=="string"||typeof m=="string")return{protocol:"ap2",artifacts:{ap2Mandates:{...typeof p=="string"&&{intent:p},...typeof l=="string"&&{cart:l},...typeof m=="string"&&{payment:m}}},evidence:{mandatesPresent:[typeof p=="string"&&"intent",typeof l=="string"&&"cart",typeof m=="string"&&"payment"].filter(Boolean),...s}}}if(f&&/^Payment\s+/i.test(f))return{protocol:"mpp",artifacts:{mppRequest:{method:a,url:n,headers:i,...r.text&&{rawBody:r.text},...o&&{body:o}}},evidence:s};if(c(e,"x-payment"))return{protocol:"x402",artifacts:{x402Request:{method:a,url:n,headers:i,...o&&{body:o}}},evidence:s}}var qe=/\.(css|js|mjs|map|png|jpe?g|gif|webp|avif|svg|ico|woff2?|ttf|otf|eot|mp4|webm|pdf|txt|xml)$/i,ze=/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,Xe=/mozilla\/\d/i;function te(e,t){if(qe.test(t))return{mode:e.mode,depth:e.depth,skip:!0};let r=j(e.pathRules,t);return r?{mode:r.mode??e.mode,depth:r.depth??e.depth,skip:r.skip===!0}:{mode:e.mode,depth:e.depth,skip:!1}}function re(e,t,r){let n=c(e,"user-agent")??"",a=b(w(e.headers),{});if(a.astraId||a.apiKey||a.jwt)return{tier:"verified",protocol:"x-astra"};if(r!=="classify"){let o=ee(e,t,k(e));if(o)return{tier:"identified",protocol:o.protocol,artifacts:o.artifacts,evidence:o.evidence,fingerprint:O({userAgent:n})}}let i=O({userAgent:n});return i?{tier:"identified",protocol:"platform-ua",fingerprint:i}:!n||ze.test(n)||!Xe.test(n)?{tier:"anonymous-bot",evidence:{userAgent:n||"(none)"}}:!c(e,"sec-fetch-mode")&&!c(e,"accept-language")?{tier:"anonymous-bot",evidence:{userAgent:n,spoofedBrowser:!0}}:{tier:"human"}}var Je={GET:"data.read",HEAD:"data.read",OPTIONS:"data.read",POST:"data.write",PUT:"data.write",PATCH:"data.write",DELETE:"data.delete"},Ye="data.write",We="data";function Ze(e){return Je[e.toUpperCase()]??Ye}function Qe(e){let t=e.indexOf(":");if(t>=0)return{purpose:e.slice(0,t)};let r=e.indexOf(".");return r>0&&r<e.length-1?{purpose:e.slice(0,r),actionCandidate:e}:{purpose:e}}function ne(e){let t=e.astraPurpose?Qe(e.astraPurpose):void 0,r,n;e.routeAction?(r=e.routeAction,n="route_config"):e.hasCustomActionExtractor&&e.customAction?(r=e.customAction,n="custom_extractor"):!e.hasCustomActionExtractor&&e.astraAction?(r=e.astraAction,n="header"):!e.hasCustomActionExtractor&&t?.actionCandidate?(r=t.actionCandidate,n="purpose_header_derived"):(r=Ze(e.method),n="method_table");let a,i;if(e.routePurpose?(a=e.routePurpose,i="route_config"):e.hasCustomPurposeExtractor?e.customPurpose&&(a=e.customPurpose,i="custom_extractor"):t?(a=t.purpose,i="header"):e.legacyPurpose?(a=e.legacyPurpose,i="legacy_header"):e.queryPurpose&&(a=e.queryPurpose,i="query"),!a){let o=r.indexOf(".");o>0?(a=r.slice(0,o),i="action_derived"):(a=We,i="transport_default")}return{purpose:a,action:r,purposeSource:i,actionSource:n}}async function I(e,t){let r;try{return await Promise.race([e,new Promise(n=>{r=setTimeout(()=>n(void 0),t)})])}catch{return}finally{r&&clearTimeout(r)}}async function B(e,t,r,n){let a=c(t,"cloudfront-viewer-address")?.split(":")[0];await I(Q({apiBaseUrl:e.apiBaseUrl,counterpartyId:e.counterpartyId},{counterpartyUrl:e.counterpartyUrl,counterpartyType:"website",sourceIp:a,userAgent:c(t,"user-agent"),requestPath:t.uri,requestMethod:t.method,protocol:r.protocol,evidence:r.evidence}),n)}function ae(e,t=Math.random){return e<=0?!1:e>=1?!0:t()<e}function h(e){return{tier:e.tier,decision:"allow",reasons:[],protocol:e.protocol}}function oe(e,t,r,n){let a=w(t.headers),i=c(t,"cloudfront-viewer-address")?.split(":")[0],o=ne({method:t.method,astraPurpose:c(t,"x-astra-purpose"),astraAction:c(t,"x-astra-action")});return{credentials:b(a,{}),purpose:o.purpose,action:o.action,resource:t.uri,createSession:!0,counterpartyType:"website",counterpartyUrl:e.counterpartyUrl,callerMetadata:{sourceIp:i,userAgent:c(t,"user-agent"),referer:c(t,"referer"),host:c(t,"host")},...n&&r.artifacts&&{commerceArtifacts:r.artifacts}}}function ie(e,t){let r=h(e);return r.sessionId=t.sessionId,r.correlationId=t.correlationId,r.guidance=t.guidance,t.requiresStepUp||t.stepUpApproval?(r.decision="manual_review",r.reasons=["step-up approval required"],r.stepUp={pollUrl:t.stepUpApproval?.pollUrl,expiresAt:t.stepUpApproval?.expiresAt},r):!t.identityVerified||!t.policyAllowed?(r.decision="deny",r.reasons=t.denialReasons?.length?t.denialReasons:[t.identityVerified?"denied by access policy":"identity not verified"],r):(r.asat={trustScore:t.agent?.trustScore,agentId:t.agent?.astraId,developerId:t.developer?.astradId,accessLevel:t.accessLevel},r)}async function se(e,t,r,n,a,i){let o=e.budgets??{},s=o.beaconMs??300,f=n.mode==="enforce"?o.verifyEnforceMs??3e3:o.verifyObserveMs??1500,y={apiBaseUrl:e.apiBaseUrl,...a&&{apiKey:a},counterpartyId:e.counterpartyId,disableInitChecks:!0};try{switch(r.tier){case"verified":{if(n.depth==="classify")return h(r);let u=await I(_(y,oe(e,t,r,n.depth==="authorize")),f);if(!u){let g=h(r);return g.reasons=["verify-access unreachable within budget \u2014 fail-open"],g}return ie(r,u)}case"identified":{if(n.depth==="authorize"&&r.artifacts){let g=await I(_(y,oe(e,t,r,!0)),f);if(!g){let v=h(r);return v.reasons=["verify-access unreachable within budget \u2014 fail-open"],v}return ie(r,g)}await B(e,t,r,s);let u=h(r);return n.mode==="enforce"&&n.depth!=="classify"&&(u.decision="deny",u.reasons=["agent is not registered with AstraSync"]),u}case"anonymous-bot":return ae(i.anonymousBeaconRate,e.random)&&await B(e,t,{...r,protocol:r.protocol??"ua-heuristic"},s),h(r);default:return h(r)}}catch(u){let g=h(r);return g.reasons=[`edge pipeline error \u2014 fail-open: ${u instanceof Error?u.message:String(u)}`],g}}var et={"vi-sd-jwt":"This request presented Visa Intelligent Commerce credentials, but the agent is not verified with AstraSync. Register the agent to pair your VI credentials with a verified identity and mandate.","rfc9421-agent-pay":"This request carried a Mastercard Agent Pay signature, but the agent is not verified with AstraSync. Register the agent to pair Agent Pay credentials with a verified identity and mandate.","rfc9421-tap":"This request carried a Visa TAP signature, but the agent is not verified with AstraSync. Register the agent to pair TAP credentials with a verified identity and mandate.","rfc9421-web-bot-auth":"This request carried a Web Bot Auth signature, but the agent is not verified with AstraSync. Register the agent to link your signed-agent identity to a verified principal.",rfc9421:"This request carried an RFC 9421 HTTP message signature, but the agent is not verified with AstraSync. Register the agent to link the signing identity to a verified principal.",acp:"This request used the Agentic Commerce Protocol, but the agent is not verified with AstraSync. Register the agent to transact on this property.",ucp:"This request used a UCP checkout session, but the agent is not verified with AstraSync. Register the agent to transact on this property.",ap2:"This request presented AP2 mandates, but the agent is not verified with AstraSync. Register the agent to pair your mandates with a verified identity.",mpp:"This request presented MPP payment credentials, but the agent is not verified with AstraSync. Register the agent to transact on this property.",x402:"This request used x402 payment headers, but the agent is not verified with AstraSync. Register the agent to transact on this property.","platform-ua":"This traffic was identified as a platform AI agent without AstraSync credentials. Register the agent to access this property with a verified identity.","x-astra":"The presented AstraSync credentials failed verification or were denied by this property\u2019s access policy."};function ce(e,t){let r=t.replace(/\/api\/?$/,""),n=(e.protocol&&et[e.protocol])??"This request could not be verified by AstraSync.";return{success:!1,error:{code:e.tier==="verified"?"INSUFFICIENT_ACCESS":"UNAUTHORIZED",message:n,...e.protocol&&{protocol:e.protocol},failures:e.reasons,...e.correlationId&&{correlationId:e.correlationId},guidance:{message:e.guidance?.message||"Register this agent with AstraSync to receive verifiable credentials.",registrationUrl:e.guidance?.registrationUrl||`${r}/register`,documentationUrl:e.guidance?.documentationUrl||`${r}/docs/edge-adapter`}}}}function de(e){return{success:!1,status:"manual_review",message:"This action requires human approval. Poll the step-up approval endpoint and retry once approved.",...e.stepUp&&{stepUpApproval:e.stepUp},...e.correlationId&&{correlationId:e.correlationId}}}var T={trustScore:"X-AstraSync-Trust-Score",agentId:"X-AstraSync-Agent-Id",developerId:"X-AstraSync-Developer-Id",permissions:"X-AstraSync-Permissions",observedDecision:"X-AstraSync-Observed-Decision"};function le(e,t,r,n){if(N(t,"x-astrasync-"),n.mode!=="enforce")return r.decision!=="allow"&&R(t,T.observedDecision,r.decision),ue(t,r),t;switch(r.decision){case"deny":return U(403,ce(r,e.apiBaseUrl));case"manual_review":return U(202,de(r),{"Retry-After":"30"});default:return ue(t,r),t}}function ue(e,t){if(!t.asat)return;let{trustScore:r,agentId:n,developerId:a,accessLevel:i}=t.asat;r!==void 0&&R(e,T.trustScore,String(r)),n&&R(e,T.agentId,n),a&&R(e,T.developerId,a),i&&R(e,T.permissions,i)}function pe(e){let t=e.log??((r,n,a)=>{console[r==="info"?"log":r](`[astrasync-edge] ${n}`,a??"")});return async function(n){let a=F(n);try{let i=await q({...e,log:t}),o=await z(e,i),s=te(o,a.uri);if(s.skip)return a;let f=c(a,"x-forwarded-host")??c(a,"host")??new URL(e.counterpartyUrl).host,y=re(a,f,s.depth);if(y.tier==="human")return a;let u=await se({...e,log:t},a,y,s,i,o.sampling);return u.decision!=="allow"&&t("info",s.mode==="enforce"?`${u.decision.toUpperCase()}: ${a.method} ${a.uri}`:`[OBSERVE] would have been ${u.decision.toUpperCase()}: ${a.method} ${a.uri}`,{tier:u.tier,protocol:u.protocol,reasons:u.reasons}),le(e,a,u,s)}catch(i){return t("error","edge handler error \u2014 passing request through",{error:i instanceof Error?i.message:String(i)}),a}}}var tt="https://astrasync.ai/api",fe="",ge="",rt=void 0;(!fe||!ge)&&console.error("[astrasync-edge] ASTRASYNC_COUNTERPARTY_ID / ASTRASYNC_COUNTERPARTY_URL were not baked into the bundle \u2014 rebuild via infra/deploy.sh with the ASTRASYNC_* env vars set.");var nt=pe({apiBaseUrl:tt,counterpartyId:fe,counterpartyUrl:ge,apiKeySecretName:rt});0&&(module.exports={handler});
1
+ "use strict";var Ce=Object.create;var T=Object.defineProperty;var Se=Object.getOwnPropertyDescriptor;var Te=Object.getOwnPropertyNames;var be=Object.getPrototypeOf,Ee=Object.prototype.hasOwnProperty;var Ie=(e,t)=>{for(var r in t)T(e,r,{get:t[r],enumerable:!0})},M=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of Te(t))!Ee.call(e,a)&&a!==r&&T(e,a,{get:()=>t[a],enumerable:!(n=Se(t,a))||n.enumerable});return e};var Pe=(e,t,r)=>(r=e!=null?Ce(be(e)):{},M(t||!e||!e.__esModule?T(r,"default",{value:e,enumerable:!0}):r,e)),Ue=e=>M(T({},"__esModule",{value:!0}),e);var St={};Ie(St,{handler:()=>Ct});module.exports=Ue(St);var xe=new Set(["cookie","set-cookie","x-csrf-token","proxy-authorization","x-astrasync-signature","x-astrasync-secret","x-hub-signature","x-hub-signature-256"]),_e=["x-astrasync-","kya-","kya_"],Oe=new Set(["authorization","x-api-key","x-goog-api-key","api-key"]);function ke(e){return e==="signature"||e.endsWith("-signature")||e.endsWith("-signature-256")}var Fe=new Set(["openai-organization","openai-project","openai-version","openai-processing-ms","anthropic-version","anthropic-beta","x-goog-user-project","x-goog-api-client","user-agent","x-agent-card-url","x-agent-provider"]),Be=["x-goog-","x-stainless-","x-anthropic-","x-openai-"],He=["sk-ant-api03","sk-ant-api","sk-ant","sk-proj","sk-svcacct","sk-or-v1","sk-or","sk-lf","sk-","gsk_","aiza","ya29","ghp_","gho_","github_pat_","xai-","pplx-","r8_","hf_","astra-","astrae-"],Me=64,D=1024,De=16*1024;function U(e){let t=0;for(let r=0;r<e.length;r++){let n=e.charCodeAt(r);n<128?t+=1:n<2048?t+=2:n>=55296&&n<=56319?(t+=4,r++):t+=3}return t}function Ne(e){if(!e)return;let t=e.trim(),r=t.indexOf(" ");if(r>0){let a=t.slice(0,r).toLowerCase();(a==="bearer"||a==="basic"||a==="token")&&(t=t.slice(r+1).trim())}let n=t.toLowerCase();for(let a of He)if(n.startsWith(a))return a}function Le(e){return!!(xe.has(e)||_e.some(t=>e.startsWith(t))||ke(e))}function je(e){return Fe.has(e)?!0:Be.some(t=>e.startsWith(t))}function b(e){let t={},r={},n,a=0,i=0;if(!e)return{headers:t};for(let s of Object.keys(e)){if(i>=Me)break;let l=s.toLowerCase(),f=e[s];if(f===void 0)continue;let d=Array.isArray(f)?f.join(", "):String(f);if(Oe.has(l)){let h=Ne(d);h&&!n&&(n=h);continue}if(Le(l))continue;let u=d;U(u)>D&&(u=u.slice(0,D));let m=U(l)+U(u);a+m>De||(a+=m,i++,t[l]=u,je(l)&&(r[l]=u))}let o={headers:t};return n&&(o.apiKeyFormat=n),Object.keys(r).length>0&&(o.platformHeaders=r),o}function N(e){return e.Records[0].cf.request}function c(e,t){return e.headers[t.toLowerCase()]?.[0]?.value}function R(e){let t={};for(let[r,n]of Object.entries(e))n?.length&&(t[r]=n.length===1?n[0].value:n.map(a=>a.value));return t}function L(e){let t=e.body;return t?.data?{text:t.encoding==="base64"?Buffer.from(t.data,"base64").toString("utf8"):t.data,truncated:t.inputTruncated===!0}:{truncated:!1}}function Ke(e){let t={},r=(i,o,s)=>{let l=c(e,o);l&&(t[i]=s?s(l):l)},n=c(e,"cloudfront-viewer-address");n&&(t.ip=n.replace(/:\d+$/,"")),r("country","cloudfront-viewer-country"),r("countryName","cloudfront-viewer-country-name"),r("region","cloudfront-viewer-country-region"),r("city","cloudfront-viewer-city"),r("asn","cloudfront-viewer-asn"),r("tlsVersion","cloudfront-viewer-tls"),r("httpVersion","cloudfront-viewer-http-version");let a=[];for(let[i,o]of[["desktop","cloudfront-is-desktop-viewer"],["mobile","cloudfront-is-mobile-viewer"],["tablet","cloudfront-is-tablet-viewer"],["smarttv","cloudfront-is-smarttv-viewer"]])c(e,o)==="true"&&a.push(i);return a.length&&(t.device=a.join(",")),t}function E(e){let{headers:t,apiKeyFormat:r,platformHeaders:n}=b(R(e.headers)),a=Ke(e);return{headers:t,...r&&{apiKeyFormat:r},...n&&{platformHeaders:n},...Object.keys(a).length>0&&{connection:a}}}function j(e){let t=e.origin?.custom?.domainName??e.origin?.s3?.domainName??void 0;if(!t)return;let r=c(e,"host");!r||r===t||(c(e,"x-forwarded-host")||A(e,"X-Forwarded-Host",r),A(e,"Host",t))}function K(e,t){let r=e.querystring?`?${e.querystring}`:"";return`https://${t}${e.uri}${r}`}function A(e,t,r){e.headers[t.toLowerCase()]=[{key:t,value:r}]}function $(e,t){let r=t.toLowerCase();for(let n of Object.keys(e.headers))n.startsWith(r)&&delete e.headers[n]}function x(e,t,r){let n={"content-type":[{key:"Content-Type",value:"application/json"}]};for(let[a,i]of Object.entries(r??{}))n[a.toLowerCase()]=[{key:a,value:i}];return{status:String(e),statusDescription:e===403?"Forbidden":e===202?"Accepted":"OK",headers:n,body:JSON.stringify(t)}}var O=Object.freeze({version:1,mode:"observe",depth:"classify",pathRules:[],sampling:Object.freeze({anonymousBeaconRate:.1}),failurePosture:"open"}),V=["observe","enforce"],G=["classify","authenticate","authorize"];function $e(e){if(typeof e!="object"||e===null)return!1;let t=e;if(t.version!==1||!V.includes(t.mode)||!G.includes(t.depth)||!Array.isArray(t.pathRules))return!1;for(let n of t.pathRules){if(typeof n!="object"||n===null)return!1;let a=n;if(typeof a.pattern!="string"||a.pattern.length===0||a.mode!==void 0&&!V.includes(a.mode)||a.depth!==void 0&&!G.includes(a.depth))return!1}let r=t.sampling;return typeof r!="object"||r===null||typeof r.anonymousBeaconRate!="number"||r.anonymousBeaconRate<0||r.anonymousBeaconRate>1?!1:t.failurePosture==="open"}async function Ve(e,t,r){if(!t)return null;let n={Accept:"application/json"};e.apiKey&&(n.Authorization=`Bearer ${e.apiKey}`,n["X-API-Key"]=e.apiKey),r?.etag&&(n["If-None-Match"]=r.etag);try{let a=await fetch(`${e.apiBaseUrl}/endpoints/${encodeURIComponent(t)}/edge-config`,{method:"GET",headers:n,signal:AbortSignal.timeout(r?.timeoutMs??5e3)});if(a.status===304)return{notModified:!0};if(!a.ok)return null;let o=(await a.json()).data?.edgeConfig;return $e(o)?{edgeConfig:o,etag:a.headers.get("etag")??void 0}:null}catch{return null}}var Ge=60*1e3,ze=1440*60*1e3,C=new Map,_=new Map;function X(e,t){return`${e}|${t}`}function Xe(e){return e.mode!=="enforce"&&!e.pathRules.some(t=>t.mode==="enforce")?e:{...e,mode:"observe",pathRules:e.pathRules.map(t=>t.mode==="enforce"?{...t,mode:"observe"}:t)}}async function z(e,t){let r=X(e.apiBaseUrl,t),n=_.get(r);if(n)return n;let a=Ve(e,t,{etag:C.get(r)?.etag}).then(i=>{if(i===null)return;let o=Date.now();if("notModified"in i){let s=C.get(r);s&&(s.lastSuccessAt=o);return}C.set(r,{config:i.edgeConfig,etag:i.etag,lastSuccessAt:o})}).finally(()=>{_.delete(r)});return _.set(r,a),a}async function q(e,t,r){let n=r?.ttlMs??Ge,a=r?.maxStaleMs??ze,i=X(e.apiBaseUrl,t),o=C.get(i);if(o){let s=Date.now()-o.lastSuccessAt;return s>n&&z(e,t).catch(()=>{}),s>a?Xe(o.config):o.config}return await z(e,t).catch(()=>{}),C.get(i)?.config??O}function Y(e,t){for(let r of e)if(qe(r.pattern).test(t))return r}function qe(e){let t=e.replace(/[.+?^${}()|[\]\\]/g,"\\$&").replace(/\*/g,".*");return new RegExp(`^${t}$`,"i")}var Ye=900*1e3,J=new Map;async function Je(e){let{SecretsManagerClient:t,GetSecretValueCommand:r}=await import("@aws-sdk/client-secrets-manager");return(await new t({region:"us-east-1"}).send(new r({SecretId:e}))).SecretString??void 0}async function W(e){if(e.apiKey)return e.apiKey;let t=e.apiKeySecretName;if(!t)return;let r=J.get(t);if(r&&Date.now()-r.fetchedAt<Ye)return r.value;try{let a=await(e.fetchSecret??Je)(t);return J.set(t,{value:a,fetchedAt:Date.now()}),a}catch(n){return e.log?.("warn","Secrets Manager fetch failed \u2014 continuing keyless",{secretName:t,error:n instanceof Error?n.message:String(n)}),r?.value}}async function Z(e,t){try{return await q({apiBaseUrl:e.apiBaseUrl,apiKey:t},e.counterpartyId,{ttlMs:e.configTtlMs})}catch{return e.fallbackConfig??O}}function We(e){return e>=80?"PLATINUM":e>=60?"GOLD":e>=40?"SILVER":"BRONZE"}var Ze="3.10.0",_t=3600*1e3,Qe=new Map;function et(e){return Qe.get(e)?.data}var te={apiBaseUrl:"https://astrasync.ai/api",defaultAccessLevel:"none",debug:!1},re=!1,Q=!1;async function ee(e,t,r){re=!0;try{let n=`${e}/agents/verify-access`,i=(await fetch(n,{method:"HEAD"})).headers.get("content-type")??"";if(i.startsWith("text/html")){let o=`[VerificationGateway] apiBaseUrl '${e}' returned HTML (content-type: ${i}). 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).`;if(r)throw new Error(`${o} (strictInit=true)`);console.warn(`${o} Set disableInitChecks: true on GatewayConfig to silence.`)}else t&&console.log(`[VerificationGateway] init check passed for ${e} (content-type: ${i})`)}catch(n){if(r)throw n;t&&console.log(`[VerificationGateway] init check failed (non-blocking): ${String(n)}`)}}var k=new Map;function ne(e,t){let r=e.credentials;return[r.astraId||"",r.apiKey||"",r.jwt||"",e.purpose||"",e.action||"",e.resourceType||"",e.resource||"",e.jurisdiction||"",e.transactionValue??"",e.currency||"",t||"",e.counterpartyUrl||"",e.counterpartyType||"",e.isSubAgentRequest?"1":"0",e.parentAgentId||"",e.subAgentDepth??"",e.durationRequired??"",e.invocationProtocol||"",e.enableRuntimeChallenge?"1":"0",e.callerMetadata?.sourceIp||"",e.callerMetadata?.userAgent||"",e.callerMetadata?.forwardedFor||"",e.callerMetadata?.agentCardUrl||""].join("|")}function tt(e,t){let r=ne(e,t),n=k.get(r);return n&&n.expiresAt>Date.now()?n.result:(n&&k.delete(r),null)}var rt=60;function nt(e,t,r,n){let a=r&&r>0?r:rt,i=ne(e,n);k.set(i,{result:t,expiresAt:Date.now()+a*1e3})}function I(e,t){let r={},n=/^ASTRAE?-[A-Za-z0-9_-]{1,64}$/,a=e["x-astra-id"]||e["X-Astra-Id"]||e["X-ASTRA-ID"]||e["x-astra-agentid"]||e["X-Astra-AgentId"]||e["x-astra-agent-id"]||e["X-Astra-Agent-Id"]||e["X-ASTRA-AGENT-ID"];if(a){let s=Array.isArray(a)?a[0]:typeof a=="string"?a:void 0;typeof s=="string"&&n.test(s)&&(r.astraId=s)}let i=e["x-api-key"]||e["X-Api-Key"]||e["X-API-KEY"];i&&(r.apiKey=Array.isArray(i)?i[0]:i);let o=e.authorization||e.Authorization;if(o){let s=Array.isArray(o)?o[0]:o;typeof s=="string"&&(r.authorizationHeader=s,s.startsWith("Bearer ")&&(r.jwt=s.slice(7)))}return t&&(t.astraId&&!r.astraId&&(r.astraId=t.astraId),t.apiKey&&!r.apiKey&&(r.apiKey=t.apiKey)),r}function at(e,t,r={}){let a=(r.source??"no_credentials")==="api_error",i=r.urls,o=a?{message:"Verification is temporarily unavailable. Retry with exponential backoff; if the issue persists, contact support with the correlationId.",registrationUrl:i?.registrationUrl??"",documentationUrl:i?.documentationUrl??"",steps:["Retry the request with exponential backoff","If failures persist, share the correlationId with support"]}:{message:"This service verifies AI agents before granting access. Please register your agent with AstraSync.",registrationUrl:i?.registrationUrl??"",documentationUrl:i?.documentationUrl??"",steps:["Register for an AstraSync account","Create and register your agent","Add your ASTRA-ID to request headers","Retry your request"]};return{identityVerified:!1,policyAllowed:!1,accessLevel:"none",guidance:o,denialReasons:t?[t]:["No valid agent credentials provided"],failures:a?[{dimension:"verify_access.api_error",message:t??"Verification temporarily unavailable",guidance:o.message}]:void 0,correlationId:r.correlationId,verifiedAt:new Date}}async function ot(e,t){let{credentials:r,...n}=t,a={...r.astraId&&{agentId:r.astraId},...n.purpose&&{purpose:n.purpose}};if(n.action&&(a.action=n.action),n.resourceType&&(a.resourceType=n.resourceType),n.resource&&(a.resource=n.resource),n.jurisdiction&&(a.jurisdiction=n.jurisdiction),n.transactionValue&&(a.transactionValue=n.transactionValue),n.currency&&(a.currency=n.currency),n.isSubAgentRequest&&(a.isSubAgentRequest=n.isSubAgentRequest),n.parentAgentId&&(a.parentAgentId=n.parentAgentId),n.subAgentDepth!==void 0&&(a.subAgentDepth=n.subAgentDepth),n.enableRuntimeChallenge&&(a.enableRuntimeChallenge=n.enableRuntimeChallenge),n.createSession&&(a.createSession=n.createSession),n.durationRequired&&(a.durationRequired=n.durationRequired),n.counterpartyType&&(a.counterpartyType=n.counterpartyType),n.counterpartyUrl&&(a.counterpartyUrl=n.counterpartyUrl),e.counterpartyId&&(a.counterpartyId=e.counterpartyId),n.runtimeChallengeOptions&&(a.runtimeChallengeOptions=n.runtimeChallengeOptions),n.invocationProtocol&&(a.invocationProtocol=n.invocationProtocol),n.commerceArtifacts&&(a.commerceArtifacts=n.commerceArtifacts),a.sdkVersion=Ze,n.callerMetadata||n.clientIp||n.userAgent){let o={...n.clientIp&&{sourceIp:n.clientIp},...n.userAgent&&{userAgent:n.userAgent},...n.callerMetadata};Object.keys(o).length>0&&(a.callerMetadata=o)}let i={"Content-Type":"application/json",...e.customHeaders};e.apiKey&&(i.Authorization=`Bearer ${e.apiKey}`,i["X-API-Key"]=e.apiKey);try{let o=await fetch(`${e.apiBaseUrl}/agents/verify-access`,{method:"POST",headers:i,body:JSON.stringify(a)}),s=await o.json();return o.status===410?{success:!0,access:{allowed:!1,accessLevel:"none",reason:"endpoint_deactivated",failures:[{dimension:"endpoint.deactivated",message:typeof s?.message=="string"?s.message:"Endpoint has been deactivated",guidance:typeof s?.guidance=="string"?s.guidance:"Reactivate via POST /api/endpoints/{id}/reactivate, or update the URL on the calling agent."}]}}:o.ok?s:{success:!1,error:s.message||s.error||`API returned ${o.status}`,correlationId:typeof s?.correlationId=="string"?s.correlationId:void 0}}catch(o){return{success:!1,error:`Failed to call verify-access API: ${o instanceof Error?o.message:"Unknown error"}`}}}async function F(e,t,r){let n={...te,...e},a=n.apiBaseUrl?et(n.apiBaseUrl):void 0;if(!re&&!n.disableInitChecks&&n.apiBaseUrl&&(n.strictInit?await ee(n.apiBaseUrl,n.debug,!0):ee(n.apiBaseUrl,n.debug,!1)),!Q&&(e.minTrustScore!==void 0||e.minTrustScoreForFull!==void 0)&&(Q=!0,console.warn("[VerificationGateway] minTrustScore / minTrustScoreForFull are deprecated in v2.3.0 and have no effect. Server is now the single source of truth for access-level decisions (the SDK reads access.accessLevel from the verify-access response). To gate access to an endpoint, configure the endpoint's trust_score_requirement server-side.")),n.cacheTtl!==0&&!r?.bypassCache){let g=tt(t,n.counterpartyId);if(g)return n.debug&&console.log("[VerificationGateway] Returning cached result"),g}let i={...t};!i.counterpartyUrl&&n.counterpartyUrl&&(i.counterpartyUrl=n.counterpartyUrl),!i.counterpartyType&&n.counterpartyType&&(i.counterpartyType=n.counterpartyType),n.debug&&console.log("[VerificationGateway] Calling verify-access API");let o=await ot(n,i),s=o.denialReasons,l=Array.isArray(s)&&s.length>0?s:void 0;if(!o.success)return at(n,o.error,{source:"api_error",correlationId:o.correlationId,urls:a});if(!o.access?.allowed){let g=o.access?.failures;return{identityVerified:o.verificationContext?.idVerified===!0,policyAllowed:!1,accessLevel:"none",denialReasons:l??(g&&g.length>0?g.map(Re=>Re.message):o.access?.reason?[o.access.reason]:["Access denied"]),failures:g,requiresStepUp:o.access?.requiresStepUp,requiresApproval:o.access?.requiresApproval,guidance:{message:o.access?.reason||"Access denied by PDLSS policy",registrationUrl:a?.registrationUrl??"",documentationUrl:a?.documentationUrl??""},verifiedAt:new Date,sessionId:o.sessionId,correlationId:o.correlationId,recommendation:o.recommendation,recommendationReasons:o.recommendationReasons,stepUpApproval:o.stepUpApproval,settlement:o.settlement}}let f=o.agent?{astraId:o.agent.astraId,name:o.agent.name,trustScore:o.agent.trustScore,trustLevel:We(o.agent.trustScore),blockchainVerified:o.agent.blockchainStatus==="verified",status:o.agent.agentStatus}:void 0,d=o.developer?{astradId:o.developer.kyaOwnerId,name:o.developer.fullName,trustScore:o.developer.trustScore||0,verified:o.developer.identityVerified}:void 0,u=o.organization?{name:o.organization.name,verified:o.organization.verified,trustScore:o.organization.trustScore}:void 0,m=o.verificationContext,h=o.access?.accessLevel??"standard",p={identityVerified:o.verificationContext?.idVerified!==!1,policyAllowed:!0,accessLevel:h,agent:f,developer:d,organization:u,appliedPolicy:o.access?.appliedPolicy,verificationContext:m,requiresStepUp:o.access?.requiresStepUp,requiresApproval:o.access?.requiresApproval,verifiedAt:new Date,cacheTtl:n.cacheTtl,sessionId:o.sessionId,correlationId:o.correlationId,runtimeChallenge:o.runtimeChallenge,tokenGuidance:o.tokenGuidance,recommendation:o.recommendation,recommendationReasons:o.recommendationReasons,warningHeader:o.warningHeader,stepUpApproval:o.stepUpApproval,settlement:o.settlement};p.recommendation==="deny"?(p.policyAllowed=!1,p.accessLevel="none",p.denialReasons=l??p.recommendationReasons??["Access denied by AstraSync recommendation"],p.guidance=p.runtimeChallenge?{message:`Verification failed: ${p.runtimeChallenge.reason||"runtime challenge failed"}`,registrationUrl:a?.registrationUrl??"",documentationUrl:a?.documentationUrl??""}:{message:p.recommendationReasons?.[0]||"Access denied by AstraSync recommendation",registrationUrl:a?.registrationUrl??"",documentationUrl:a?.documentationUrl??""}):p.recommendation==="step_up_required"&&(p.requiresStepUp=!0,p.denialReasons=l??p.recommendationReasons??["Step-up verification required"]);let y=p.requiresStepUp===!0||p.requiresApproval===!0||p.recommendation==="step_up_required";return n.cacheTtl!==0&&p.recommendation!=="deny"&&!y&&nt(t,p,n.cacheTtl,n.counterpartyId),p}async function ae(e,t){let r=e.apiBaseUrl||te.apiBaseUrl,{counterpartyUrl:n,...a}=t,i={...a,...n&&{counterpartyUrl:n},...e.counterpartyId&&{counterpartyId:e.counterpartyId}};await fetch(`${r}/verification-activity/unregistered-attempt`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)}).catch(()=>{})}var it=[{vendor:"claude",displayName:"Anthropic Claude",uaPatterns:[/claude/i,/anthropic/i],agentCardPatterns:[/anthropic\.com/i,/claude\.ai/i],apiKeyFormatPatterns:[/^sk-ant/i],headerPatterns:[/^anthropic-version/i,/^anthropic-beta/i,/^x-anthropic-/i]},{vendor:"chatgpt",displayName:"OpenAI ChatGPT",uaPatterns:[/chatgpt/i,/openai/i,/gpt-?\d/i],agentCardPatterns:[/openai\.com/i,/chatgpt\.com/i],apiKeyFormatPatterns:[/^sk-proj/i,/^sk-svcacct/i],headerPatterns:[/^openai-organization/i,/^openai-project/i,/^openai-version/i]},{vendor:"gemini",displayName:"Google Gemini",uaPatterns:[/gemini/i,/google-gemini/i,/bard/i],agentCardPatterns:[/gemini\.google\.com/i,/ai\.google\.dev/i],apiKeyFormatPatterns:[/^aiza/i],headerPatterns:[/^x-goog-user-project/i,/^x-goog-api-client/i]},{vendor:"cursor",displayName:"Cursor",uaPatterns:[/cursor/i],agentCardPatterns:[/cursor\.sh/i,/cursor\.com/i]},{vendor:"goose",displayName:"Block Goose",uaPatterns:[/goose/i,/block-goose/i],agentCardPatterns:[/block\.xyz\/goose/i,/goose\.tools/i]},{vendor:"perplexity",displayName:"Perplexity",uaPatterns:[/perplexity/i],agentCardPatterns:[/perplexity\.ai/i],apiKeyFormatPatterns:[/^pplx-/i]}];function st(e,t){let r=t.userAgent??"",n=t.agentCardUrl??"",a=t.apiKeyFormat??"",i=[];if(t.platformHeaders)for(let[o,s]of Object.entries(t.platformHeaders))i.push(o,`${o}: ${s}`);for(let o of e){let s=o.uaPatterns?.some(u=>u.test(r)),l=o.agentCardPatterns?.some(u=>u.test(n)),f=a.length>0&&o.apiKeyFormatPatterns?.some(u=>u.test(a)),d=i.length>0&&o.headerPatterns?.some(u=>i.some(m=>u.test(m)));if(s||l||f||d){let u=r.split(/[\s/]/)[0]?.toLowerCase()??"",m=(()=>{try{return n?new URL(n).host:""}catch{return""}})(),h=u||m||a||"unknown";return{vendor:o.vendor,fingerprintKey:`${o.vendor}:${h}`,displayName:o.displayName}}}}function B(e){return st(it,e)}var ct=/^eyJ[\w-]+\.[\w-]*\.[\w-]+~/;function dt(e){try{let t=e.split(".")[0];return JSON.parse(Buffer.from(t,"base64url").toString("utf8"))}catch{return}}function ut(e){if(e.text)try{let t=JSON.parse(e.text);return typeof t=="object"&&t!==null?t:void 0}catch{return}}function oe(e,t,r){let n=K(e,t),a=e.method,i=R(e.headers),o=ut(r),s=r.truncated?{bodyTruncated:!0}:{},l=c(e,"authorization"),f=l?.replace(/^Bearer\s+/i,""),u=[c(e,"x-payment-mandate")??c(e,"payment-mandate"),f].find(y=>y&&ct.test(y));if(u){let y=dt(u)?.typ;return{protocol:"vi-sd-jwt",artifacts:{viSdJwt:u},evidence:{typ:y,...s}}}let m=c(e,"signature"),h=c(e,"signature-input");if(m&&h){let y=c(e,"signature-agent"),g=c(e,"x-agent-registry"),v=h.toLowerCase();return{protocol:y?"rfc9421-web-bot-auth":g?.toLowerCase().includes("visa")||v.includes("visa")?"rfc9421-tap":g?.toLowerCase().includes("mastercard")||v.includes("mastercard")?"rfc9421-agent-pay":"rfc9421",artifacts:{rfc9421:{headers:i,method:a,url:n,...r.text&&{body:r.text}}},evidence:{...y&&{signatureAgent:y},...g&&{registryHint:g},keyid:h.match(/keyid="([^"]+)"/)?.[1],...s}}}let p=c(e,"timestamp");if(m&&!h&&(p||c(e,"idempotency-key")))return{protocol:"acp",artifacts:{acpRequest:{method:a,url:n,headers:i,...r.text&&{rawBody:r.text},...o&&{body:o}}},evidence:{hasTimestamp:!!p,...s}};if(/\/checkout[-_]sessions(\/|$)/.test(e.uri))return{protocol:"ucp",artifacts:{ucpRequest:{method:a,url:n,...o&&{body:o}}},evidence:{path:e.uri,...s}};if(o){let y=o.intent_mandate??o.intentMandate,g=o.cart_mandate??o.cartMandate,v=o.payment_mandate??o.paymentMandate;if(typeof y=="string"||typeof g=="string"||typeof v=="string")return{protocol:"ap2",artifacts:{ap2Mandates:{...typeof y=="string"&&{intent:y},...typeof g=="string"&&{cart:g},...typeof v=="string"&&{payment:v}}},evidence:{mandatesPresent:[typeof y=="string"&&"intent",typeof g=="string"&&"cart",typeof v=="string"&&"payment"].filter(Boolean),...s}}}if(l&&/^Payment\s+/i.test(l))return{protocol:"mpp",artifacts:{mppRequest:{method:a,url:n,headers:i,...r.text&&{rawBody:r.text},...o&&{body:o}}},evidence:s};if(c(e,"x-payment"))return{protocol:"x402",artifacts:{x402Request:{method:a,url:n,headers:i,...o&&{body:o}}},evidence:s}}var lt=/\.(css|js|mjs|map|png|jpe?g|gif|webp|avif|svg|ico|woff2?|ttf|otf|eot|mp4|webm|pdf|txt|xml)$/i,pt=/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,ft=/mozilla\/\d/i;function ie(e,t){if(lt.test(t))return{mode:e.mode,depth:e.depth,skip:!0};let r=Y(e.pathRules,t);return r?{mode:r.mode??e.mode,depth:r.depth??e.depth,skip:r.skip===!0}:{mode:e.mode,depth:e.depth,skip:!1}}function se(e,t,r){let n=c(e,"user-agent")??"",a=R(e.headers),i=I(a,{});if(i.astraId||i.apiKey||i.jwt)return{tier:"verified",protocol:"x-astra"};let{apiKeyFormat:o,platformHeaders:s}=b(a),l={userAgent:n,apiKeyFormat:o,platformHeaders:s};if(r!=="classify"){let d=oe(e,t,L(e));if(d)return{tier:"identified",protocol:d.protocol,artifacts:d.artifacts,evidence:d.evidence,fingerprint:B(l)}}let f=B(l);return f?{tier:"identified",protocol:"platform-ua",fingerprint:f}:!n||pt.test(n)||!ft.test(n)?{tier:"anonymous-bot",evidence:{userAgent:n||"(none)"}}:!c(e,"sec-fetch-mode")&&!c(e,"accept-language")?{tier:"anonymous-bot",evidence:{userAgent:n,spoofedBrowser:!0}}:{tier:"human"}}var gt={GET:"data.read",HEAD:"data.read",OPTIONS:"data.read",POST:"data.write",PUT:"data.write",PATCH:"data.write",DELETE:"data.delete"},yt="data.write",mt="data";function ht(e){return gt[e.toUpperCase()]??yt}function vt(e){let t=e.indexOf(":");if(t>=0)return{purpose:e.slice(0,t)};let r=e.indexOf(".");return r>0&&r<e.length-1?{purpose:e.slice(0,r),actionCandidate:e}:{purpose:e}}function ce(e){let t=e.astraPurpose?vt(e.astraPurpose):void 0,r,n;e.routeAction?(r=e.routeAction,n="route_config"):e.hasCustomActionExtractor&&e.customAction?(r=e.customAction,n="custom_extractor"):!e.hasCustomActionExtractor&&e.astraAction?(r=e.astraAction,n="header"):!e.hasCustomActionExtractor&&t?.actionCandidate?(r=t.actionCandidate,n="purpose_header_derived"):(r=ht(e.method),n="method_table");let a,i;if(e.routePurpose?(a=e.routePurpose,i="route_config"):e.hasCustomPurposeExtractor?e.customPurpose&&(a=e.customPurpose,i="custom_extractor"):t?(a=t.purpose,i="header"):e.legacyPurpose?(a=e.legacyPurpose,i="legacy_header"):e.queryPurpose&&(a=e.queryPurpose,i="query"),!a){let o=r.indexOf(".");o>0?(a=r.slice(0,o),i="action_derived"):(a=mt,i="transport_default")}return{purpose:a,action:r,purposeSource:i,actionSource:n}}async function P(e,t){let r;try{return await Promise.race([e,new Promise(n=>{r=setTimeout(()=>n(void 0),t)})])}catch{return}finally{r&&clearTimeout(r)}}async function H(e,t,r,n){let a=c(t,"cloudfront-viewer-address")?.split(":")[0];await P(ae({apiBaseUrl:e.apiBaseUrl,counterpartyId:e.counterpartyId},{counterpartyUrl:e.counterpartyUrl,counterpartyType:"website",sourceIp:a,userAgent:c(t,"user-agent"),requestPath:t.uri,requestMethod:t.method,protocol:r.protocol,evidence:r.evidence,observedMetadata:E(t)}),n)}function de(e,t=Math.random){return e<=0?!1:e>=1?!0:t()<e}function w(e){return{tier:e.tier,decision:"allow",reasons:[],protocol:e.protocol}}function ue(e,t,r,n){let a=R(t.headers),i=c(t,"cloudfront-viewer-address")?.split(":")[0],o=ce({method:t.method,astraPurpose:c(t,"x-astra-purpose"),astraAction:c(t,"x-astra-action")});return{credentials:I(a,{}),purpose:o.purpose,action:o.action,resource:t.uri,createSession:!0,counterpartyType:"website",counterpartyUrl:e.counterpartyUrl,callerMetadata:{sourceIp:i,userAgent:c(t,"user-agent"),referer:c(t,"referer"),host:c(t,"host"),observedMetadata:E(t)},...n&&r.artifacts&&{commerceArtifacts:r.artifacts}}}function le(e,t){let r=w(e);return r.sessionId=t.sessionId,r.correlationId=t.correlationId,r.guidance=t.guidance,t.requiresStepUp||t.stepUpApproval?(r.decision="manual_review",r.reasons=["step-up approval required"],r.stepUp={pollUrl:t.stepUpApproval?.pollUrl,expiresAt:t.stepUpApproval?.expiresAt},r):!t.identityVerified||!t.policyAllowed?(r.decision="deny",r.reasons=t.denialReasons?.length?t.denialReasons:[t.identityVerified?"denied by access policy":"identity not verified"],r):(r.asat={trustScore:t.agent?.trustScore,agentId:t.agent?.astraId,developerId:t.developer?.astradId,accessLevel:t.accessLevel},r)}async function pe(e,t,r,n,a,i){let o=e.budgets??{},s=o.beaconMs??300,l=n.mode==="enforce"?o.verifyEnforceMs??3e3:o.verifyObserveMs??1500,f={apiBaseUrl:e.apiBaseUrl,...a&&{apiKey:a},counterpartyId:e.counterpartyId,disableInitChecks:!0};try{switch(r.tier){case"verified":{if(n.depth==="classify")return w(r);let d=await P(F(f,ue(e,t,r,n.depth==="authorize")),l);if(!d){let u=w(r);return u.reasons=["verify-access unreachable within budget \u2014 fail-open"],u}return le(r,d)}case"identified":{if(n.depth==="authorize"&&r.artifacts){let u=await P(F(f,ue(e,t,r,!0)),l);if(!u){let m=w(r);return m.reasons=["verify-access unreachable within budget \u2014 fail-open"],m}return le(r,u)}await H(e,t,r,s);let d=w(r);return n.mode==="enforce"&&n.depth!=="classify"&&(d.decision="deny",d.reasons=["agent is not registered with AstraSync"]),d}case"anonymous-bot":return de(i.anonymousBeaconRate,e.random)&&await H(e,t,{...r,protocol:r.protocol??"ua-heuristic"},s),w(r);default:return w(r)}}catch(d){let u=w(r);return u.reasons=[`edge pipeline error \u2014 fail-open: ${d instanceof Error?d.message:String(d)}`],u}}var At={"vi-sd-jwt":"This request presented Visa Intelligent Commerce credentials, but the agent is not verified with AstraSync. Register the agent to pair your VI credentials with a verified identity and mandate.","rfc9421-agent-pay":"This request carried a Mastercard Agent Pay signature, but the agent is not verified with AstraSync. Register the agent to pair Agent Pay credentials with a verified identity and mandate.","rfc9421-tap":"This request carried a Visa TAP signature, but the agent is not verified with AstraSync. Register the agent to pair TAP credentials with a verified identity and mandate.","rfc9421-web-bot-auth":"This request carried a Web Bot Auth signature, but the agent is not verified with AstraSync. Register the agent to link your signed-agent identity to a verified principal.",rfc9421:"This request carried an RFC 9421 HTTP message signature, but the agent is not verified with AstraSync. Register the agent to link the signing identity to a verified principal.",acp:"This request used the Agentic Commerce Protocol, but the agent is not verified with AstraSync. Register the agent to transact on this property.",ucp:"This request used a UCP checkout session, but the agent is not verified with AstraSync. Register the agent to transact on this property.",ap2:"This request presented AP2 mandates, but the agent is not verified with AstraSync. Register the agent to pair your mandates with a verified identity.",mpp:"This request presented MPP payment credentials, but the agent is not verified with AstraSync. Register the agent to transact on this property.",x402:"This request used x402 payment headers, but the agent is not verified with AstraSync. Register the agent to transact on this property.","platform-ua":"This traffic was identified as a platform AI agent without AstraSync credentials. Register the agent to access this property with a verified identity.","x-astra":"The presented AstraSync credentials failed verification or were denied by this property\u2019s access policy."};function fe(e,t){let r=t.replace(/\/api\/?$/,""),n=(e.protocol&&At[e.protocol])??"This request could not be verified by AstraSync.";return{success:!1,error:{code:e.tier==="verified"?"INSUFFICIENT_ACCESS":"UNAUTHORIZED",message:n,...e.protocol&&{protocol:e.protocol},failures:e.reasons,...e.correlationId&&{correlationId:e.correlationId},guidance:{message:e.guidance?.message||"Register this agent with AstraSync to receive verifiable credentials.",registrationUrl:e.guidance?.registrationUrl||`${r}/register`,documentationUrl:e.guidance?.documentationUrl||`${r}/docs/edge-adapter`}}}}function ge(e){return{success:!1,status:"manual_review",message:"This action requires human approval. Poll the step-up approval endpoint and retry once approved.",...e.stepUp&&{stepUpApproval:e.stepUp},...e.correlationId&&{correlationId:e.correlationId}}}var S={trustScore:"X-AstraSync-Trust-Score",agentId:"X-AstraSync-Agent-Id",developerId:"X-AstraSync-Developer-Id",permissions:"X-AstraSync-Permissions",observedDecision:"X-AstraSync-Observed-Decision"};function me(e,t,r,n){if($(t,"x-astrasync-"),n.mode!=="enforce")return r.decision!=="allow"&&A(t,S.observedDecision,r.decision),ye(t,r),t;switch(r.decision){case"deny":return x(403,fe(r,e.apiBaseUrl));case"manual_review":return x(202,ge(r),{"Retry-After":"30"});default:return ye(t,r),t}}function ye(e,t){if(!t.asat)return;let{trustScore:r,agentId:n,developerId:a,accessLevel:i}=t.asat;r!==void 0&&A(e,S.trustScore,String(r)),n&&A(e,S.agentId,n),a&&A(e,S.developerId,a),i&&A(e,S.permissions,i)}function he(e){let t=e.log??((r,n,a)=>{console[r==="info"?"log":r](`[astrasync-edge] ${n}`,a??"")});return async function(n){let a=N(n);try{j(a);let i=await W({...e,log:t}),o=await Z(e,i),s=ie(o,a.uri);if(s.skip)return a;let l=c(a,"x-forwarded-host")??c(a,"host")??new URL(e.counterpartyUrl).host,f=se(a,l,s.depth);if(f.tier==="human")return a;let d=await pe({...e,log:t},a,f,s,i,o.sampling);return d.decision!=="allow"&&t("info",s.mode==="enforce"?`${d.decision.toUpperCase()}: ${a.method} ${a.uri}`:`[OBSERVE] would have been ${d.decision.toUpperCase()}: ${a.method} ${a.uri}`,{tier:d.tier,protocol:d.protocol,reasons:d.reasons}),me(e,a,d,s)}catch(i){return t("error","edge handler error \u2014 passing request through",{error:i instanceof Error?i.message:String(i)}),a}}}var wt="https://astrasync.ai/api",ve="",Ae="",Rt=void 0;(!ve||!Ae)&&console.error("[astrasync-edge] ASTRASYNC_COUNTERPARTY_ID / ASTRASYNC_COUNTERPARTY_URL were not baked into the bundle \u2014 rebuild via infra/deploy.sh with the ASTRASYNC_* env vars set.");var Ct=he({apiBaseUrl:wt,counterpartyId:ve,counterpartyUrl:Ae,apiKeySecretName:Rt});0&&(module.exports={handler});
package/infra/RUNBOOK.md CHANGED
@@ -49,10 +49,20 @@ default — the deploy changes no site behaviour; it adds visibility.
49
49
  create a DNS record that reaches the box directly (e.g.
50
50
  `staging-origin.astrasync.ai` → EC2), and make sure nginx serves it
51
51
  over HTTPS (add to the vhost/cert or serve as default vhost). The
52
- original host travels to the origin as `X-Forwarded-Host`
53
- (Managed-AllViewerExceptHostHeader). For partner installs on PaaS
54
- hosting (e.g. Railway), the platform-issued domain
55
- (`<service>.up.railway.app`) is the origin.
52
+ original host travels to the origin as `X-Forwarded-Host` and the
53
+ `Host` header is rewritten to the origin domain by the edge Lambda
54
+ (`normalizeOriginHost`). For partner installs on PaaS hosting (e.g.
55
+ Railway), the platform-issued domain (`<service>.up.railway.app`) is
56
+ the origin.
57
+
58
+ > **Metadata capture (Workstream D / SDK 3.10.0):** the template now
59
+ > provisions a **custom `OriginRequestPolicy`** (`allViewerAndWhitelistCloudFront`)
60
+ > so CloudFront forwards the `CloudFront-Viewer-*` connection signals
61
+ > (IP / ASN / country / TLS version / HTTP version / device). Unlike the
62
+ > previous Managed-AllViewerExceptHostHeader policy, this also forwards the
63
+ > viewer `Host` — which is why the Lambda now owns the Host rewrite (above).
64
+ > No manual action: the policy is created by the stack. If you pin a
65
+ > pre-existing policy, ensure it whitelists those Viewer-\* headers.
56
66
 
57
67
  ## Deploy (staging example)
58
68
 
@@ -71,6 +71,39 @@ Resources:
71
71
  AutoPublishAlias: live
72
72
  Role: !GetAtt EdgeFunctionRole.Arn
73
73
 
74
+ # Workstream D — capture the CloudFront-Viewer-* connection signals
75
+ # (IP / ASN / country / TLS version / HTTP version / device class). The
76
+ # managed AllViewerExceptHostHeader policy does NOT add these; only a policy
77
+ # with `allViewerAndWhitelistCloudFront` does — and that also forwards the
78
+ # viewer Host. The edge Lambda restores origin-domain Host + X-Forwarded-Host
79
+ # itself (see cf.ts:normalizeOriginHost), so vhost / PaaS origin routing is
80
+ # unaffected.
81
+ EdgeOriginRequestPolicy:
82
+ Type: AWS::CloudFront::OriginRequestPolicy
83
+ Properties:
84
+ OriginRequestPolicyConfig:
85
+ Name: !Sub '${AWS::StackName}-all-viewer-and-cf-signals'
86
+ Comment: All viewer headers/cookies/query + CloudFront-Viewer connection signals.
87
+ HeadersConfig:
88
+ HeaderBehavior: allViewerAndWhitelistCloudFront
89
+ Headers:
90
+ - CloudFront-Viewer-Address
91
+ - CloudFront-Viewer-ASN
92
+ - CloudFront-Viewer-Country
93
+ - CloudFront-Viewer-Country-Name
94
+ - CloudFront-Viewer-Country-Region
95
+ - CloudFront-Viewer-City
96
+ - CloudFront-Viewer-TLS
97
+ - CloudFront-Viewer-HTTP-Version
98
+ - CloudFront-Is-Desktop-Viewer
99
+ - CloudFront-Is-Mobile-Viewer
100
+ - CloudFront-Is-Tablet-Viewer
101
+ - CloudFront-Is-SmartTV-Viewer
102
+ CookiesConfig:
103
+ CookieBehavior: all
104
+ QueryStringsConfig:
105
+ QueryStringBehavior: all
106
+
74
107
  Distribution:
75
108
  Type: AWS::CloudFront::Distribution
76
109
  Properties:
@@ -98,11 +131,12 @@ Resources:
98
131
  # observe-mode fidelity beats cache hit-rate while the Agent
99
132
  # Traffic Audit is the product. Tighten per-behavior later.
100
133
  CachePolicyId: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad # Managed-CachingDisabled
101
- # Forward all viewer headers/cookies/query EXCEPT Host — the
102
- # origin must receive the hostname it actually serves (nginx
103
- # vhost / PaaS routing); the original host travels as
104
- # X-Forwarded-Host.
105
- OriginRequestPolicyId: b689b0a8-53d0-40ab-baf2-68738e2966ac # Managed-AllViewerExceptHostHeader
134
+ # Forward all viewer headers/cookies/query PLUS the CloudFront-Viewer
135
+ # connection signals (Workstream D). This policy forwards the viewer
136
+ # Host too; the edge Lambda rewrites Host to the origin domain and
137
+ # preserves the public host in X-Forwarded-Host, so vhost / PaaS
138
+ # routing still receives the hostname it serves.
139
+ OriginRequestPolicyId: !Ref EdgeOriginRequestPolicy
106
140
  LambdaFunctionAssociations:
107
141
  - EventType: origin-request
108
142
  IncludeBody: true
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrasyncai/adapter-lambda",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "AstraSync Commerce Shield edge adapter — Lambda@Edge/CloudFront interception shell that classifies and verifies inbound agent traffic against the AstraSync platform. Observe-only by default.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -44,7 +44,7 @@
44
44
  "directory": "packages/adapter-lambda"
45
45
  },
46
46
  "dependencies": {
47
- "@astrasyncai/verification-gateway": "^3.9.0",
47
+ "@astrasyncai/verification-gateway": "^3.10.0",
48
48
  "@aws-sdk/client-secrets-manager": "^3.632.0"
49
49
  },
50
50
  "devDependencies": {