@astrasyncai/adapter-lambda 2.0.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -9
- package/dist/index.d.mts +56 -220
- package/dist/index.d.ts +56 -220
- package/dist/index.js +100 -510
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +110 -506
- package/dist/index.mjs.map +1 -1
- package/dist/lambda/index.js +1 -1
- package/infra/RUNBOOK.md +20 -5
- package/infra/deploy.sh +1 -1
- package/infra/template.yaml +18 -8
- package/package.json +7 -5
package/dist/index.mjs.map
CHANGED
|
@@ -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';\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 // Trust Record (§33.5 #3): forward the caller's attempt-chain handle when\n // well-formed (agent-declared, like x-astra-purpose — not a trust header,\n // so no spoof-strip needed). The edge synthesises NO consideration data —\n // it can't see offer semantics; pure pass-through.\n const attemptId = getHeader(request, 'x-astra-attempt-id');\n return {\n ...(attemptId && /^att_[0-9a-f]{32}$/.test(attemptId) && { attemptId }),\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 // 2.0.0 BREAKING (enforce-mode semantic): the PROPERTY'S own API key was\n // rejected by the backend — neither an agent deny nor an infra failure.\n // Fail OPEN in both modes: agents must not be blocked by the property's\n // dead key. The misconfig flag stamps X-AstraSync-Observed-Decision so\n // origin logs surface it; the owner is alerted server-side\n // (api_key.dead_key_in_use) on every rejected call.\n if (result.misconfigured) {\n outcome.reasons = ['verification key misconfigured — fail-open (merchant_misconfig)'];\n outcome.misconfig = true;\n return outcome;\n }\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 // 2.0.0: surface a rejected property key in origin logs in BOTH modes —\n // the outcome is allow (fail-open) so the deny stamp below never fires.\n if (outcome.misconfig) {\n setHeader(request, ASAT_HEADERS.observedDecision, 'misconfig');\n }\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
|
+
{"version":3,"sources":["../src/handler.ts","../src/cf.ts","../src/config.ts","../src/classify.ts","../src/detect.ts","../src/pipeline.ts","../src/enforce.ts","../src/messaging.ts","../src/events.ts"],"sourcesContent":["/**\n * Trusted Agent Gateway edge shell — Lambda@Edge origin-request handler\n * factory.\n *\n * Composition per request:\n * config (cached) → edge core evaluation (path policy → classify → tier\n * pipeline) → apply outcome onto the CloudFront request\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 { evaluateEdgeRequest } from '@astrasyncai/verification-gateway/edge-core';\nimport { getRequest, normalizeOriginHost, toNormalizedRequest, applyCoreOutcome } from './cf';\nimport { resolveApiKey, loadEdgeConfig } from './config';\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 (metadata capture): 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 — and so the normalized request the core sees\n // already carries the viewer host in X-Forwarded-Host.\n normalizeOriginHost(request);\n\n const apiKey = await resolveApiKey({ ...options, log });\n const config = await loadEdgeConfig(options, apiKey);\n\n const { application } = await evaluateEdgeRequest(\n { ...options, log },\n toNormalizedRequest(request),\n config,\n apiKey\n );\n\n return applyCoreOutcome(request, application);\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","/**\n * CloudFront platform I/O — the Lambda@Edge side of the edge core's\n * PlatformIo contract, plus origin-request event helpers.\n *\n * The Lambda@Edge event shape puts headers in `{ key, value }[]` arrays\n * keyed by LOWERCASED header name, and (with IncludeBody) the body arrives\n * base64-encoded with an `inputTruncated` flag when it exceeded\n * CloudFront's ~1MB exposure cap. `toNormalizedRequest` converts that into\n * the core's NormalizedRequest; `applyCoreOutcome` executes the core's\n * abstract outcome application against the CloudFront request/response\n * primitives.\n */\n\nimport type {\n CloudFrontRequest,\n CloudFrontRequestEvent,\n CloudFrontRequestResult,\n CloudFrontHeaders,\n CloudFrontResultResponse,\n} from 'aws-lambda';\nimport type {\n NormalizedRequest,\n OutcomeApplication,\n} from '@astrasyncai/verification-gateway/edge-core';\nimport { buildObservedMetadata as buildObservedMetadataCore } from '@astrasyncai/verification-gateway/edge-core';\nimport type { ObservedMetadata } 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\n/**\n * Flatten CloudFront headers to single string values (multi-valued headers\n * joined with `\", \"`, standard HTTP field-value concatenation) — the\n * NormalizedRequest header shape.\n */\nfunction flattenHeadersSingle(headers: CloudFrontHeaders): Record<string, string> {\n const out: Record<string, string> = {};\n for (const [name, entries] of Object.entries(headers)) {\n if (!entries?.length) continue;\n out[name] = entries.map((e) => e.value).join(', ');\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 * Connection-layer signals the edge can see but the SDK cannot. CloudFront\n * injects the `CloudFront-Viewer-*` header family (IP, ASN, country, TLS\n * version/cipher, HTTP version, device class) when the OriginRequestPolicy\n * whitelists them (see infra/template.yaml). Absent values are simply\n * omitted — 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('countryRegionName', 'cloudfront-viewer-country-region-name');\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 put('timeZone', 'cloudfront-viewer-time-zone');\n put('metroCode', 'cloudfront-viewer-metro-code');\n // JA3/JA4 client fingerprints (HTTPS viewers only; delivered solely via the\n // OriginRequestPolicy — see infra/template.yaml, kept in lockstep by test).\n put('tlsFingerprint', 'cloudfront-viewer-ja3-fingerprint');\n put('ja4', 'cloudfront-viewer-ja4-fingerprint');\n // Header-structure fingerprint. headerOrder can exceed the platform's\n // 256-char connection-value bound — truncate at the emitter.\n put('headerOrder', 'cloudfront-viewer-header-order', (v) => v.slice(0, 256));\n put('headerCount', 'cloudfront-viewer-header-count');\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 * CloudFront PlatformIo normalization: origin-request event → the edge\n * core's NormalizedRequest. `clientIp` keeps the pipeline's historical\n * viewer-address derivation (address portion before the first `:`) so\n * verify-access/beacon telemetry is byte-identical to earlier releases.\n */\nexport function toNormalizedRequest(request: CloudFrontRequest): NormalizedRequest {\n const body = decodeBody(request);\n const connection = collectConnectionMetadata(request);\n const clientIp = getHeader(request, 'cloudfront-viewer-address')?.split(':')[0];\n return {\n method: request.method,\n uri: request.uri,\n querystring: request.querystring ?? '',\n headers: flattenHeadersSingle(request.headers),\n ...(body.text !== undefined && { bodyText: body.text }),\n ...(body.truncated && { bodyTruncated: true }),\n ...(clientIp && { clientIp }),\n ...(Object.keys(connection).length > 0 && { connection }),\n };\n}\n\n/**\n * Execute the core's abstract outcome application against the CloudFront\n * request: `pass` mutates the origin-bound request (strip spoofable\n * prefixes, inject trust headers) and returns it; `respond` short-circuits\n * with a CloudFront JSON response.\n */\nexport function applyCoreOutcome(\n request: CloudFrontRequest,\n application: OutcomeApplication\n): CloudFrontRequestResult {\n if (application.action === 'respond') {\n return jsonResponse(application.status, application.bodyJson, application.headers);\n }\n for (const prefix of application.stripHeaderPrefixes) {\n stripHeadersByPrefix(request, prefix);\n }\n for (const [name, value] of Object.entries(application.injectHeaders)) {\n setHeader(request, name, value);\n }\n return request;\n}\n\n/**\n * The FULL sanitised metadata capture for one request: every inbound header\n * (secrets stripped, credential headers reduced to a safe format prefix)\n * plus the connection-layer block. Forwarded on both the verify-access call\n * and the beacon.\n */\nexport function buildObservedMetadata(request: CloudFrontRequest): ObservedMetadata {\n return buildObservedMetadataCore(toNormalizedRequest(request));\n}\n\n/**\n * 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 — CloudFront wrapper over\n * the edge core's classifier. The classification logic itself (tier order,\n * UA patterns, path-rule resolution) lives in\n * `@astrasyncai/verification-gateway/edge-core` and is shared with every\n * edge platform adapter.\n */\n\nimport type { CloudFrontRequest } from 'aws-lambda';\nimport {\n classify as classifyCore,\n resolvePolicy,\n _patterns,\n} from '@astrasyncai/verification-gateway/edge-core';\nimport { toNormalizedRequest } from './cf';\nimport type { Classification } from './types';\n\nexport { resolvePolicy, _patterns };\n\n/** Classify one CloudFront origin-request. See the edge core for the tier order. */\nexport function classify(\n request: CloudFrontRequest,\n host: string,\n depth: 'classify' | 'authenticate' | 'authorize'\n): Classification {\n return classifyCore(toNormalizedRequest(request), host, depth);\n}\n","/**\n * Crypto-free commerce-protocol detection (fixed detection order) —\n * CloudFront wrapper over the edge core's detector. The sniffing logic\n * (header shapes, compact-JWT patterns, body field names) lives in\n * `@astrasyncai/verification-gateway/edge-core`; this module adapts the\n * CloudFront event shape (separately-decoded body) onto it.\n */\n\nimport type { CloudFrontRequest } from 'aws-lambda';\nimport {\n detectCommerce as detectCommerceCore,\n type DetectedCommerce,\n} from '@astrasyncai/verification-gateway/edge-core';\nimport { toNormalizedRequest, type DecodedBody } from './cf';\n\nexport type { DetectedCommerce };\n\n/**\n * Detect commerce-protocol credentials on a CloudFront origin-request.\n * `body` is the caller-decoded request body (see `decodeBody`) — kept as an\n * explicit parameter so callers that already decoded it don't pay twice.\n */\nexport function detectCommerce(\n request: CloudFrontRequest,\n host: string,\n body: DecodedBody\n): DetectedCommerce | undefined {\n const normalized = toNormalizedRequest(request);\n return detectCommerceCore(\n {\n ...normalized,\n bodyText: body.text,\n bodyTruncated: body.truncated,\n },\n host\n );\n}\n","/**\n * Depth-controlled verification per tier — CloudFront wrapper over the edge\n * core's tier pipeline. The verification semantics (what each depth looks\n * at and forwards, the fail-open rules, the verify-access request shape)\n * live in `@astrasyncai/verification-gateway/edge-core`; the edge never\n * verifies credentials itself — verify-access is the canonical sole\n * verification sink.\n */\n\nimport type { CloudFrontRequest } from 'aws-lambda';\nimport { runTier as runTierCore } from '@astrasyncai/verification-gateway/edge-core';\nimport { toNormalizedRequest } from './cf';\nimport type { Classification, EdgeAdapterOptions, EffectivePolicy, TierOutcome } from './types';\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 return runTierCore(\n options,\n toNormalizedRequest(request),\n classification,\n policy,\n apiKey,\n sampling\n );\n}\n","/**\n * Decision → CloudFront result wiring (enforcement + ASAT trust-attestation\n * headers). The decision logic (observe shadow stamps, misconfig fail-open,\n * enforce-mode response bodies) lives in the edge core; this module\n * executes the core's abstract outcome application against CloudFront\n * request/response primitives.\n */\n\nimport type { CloudFrontRequest, CloudFrontRequestResult } from 'aws-lambda';\nimport {\n applyOutcome as resolveOutcomeApplication,\n ASAT_HEADERS,\n} from '@astrasyncai/verification-gateway/edge-core';\nimport { applyCoreOutcome } from './cf';\nimport type { EdgeAdapterOptions, EffectivePolicy, TierOutcome } from './types';\n\nexport { ASAT_HEADERS };\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 return applyCoreOutcome(request, resolveOutcomeApplication(options, outcome, policy));\n}\n","/**\n * Protocol-specific machine-readable messaging for enforce-mode\n * responses. The message catalogue and body builders live in the edge core\n * (shared across every edge platform adapter); re-exported here so the\n * adapter's public API is unchanged.\n */\n\nexport {\n buildDenyBody,\n buildManualReviewBody,\n type DenyBody,\n type ManualReviewBody,\n} from '@astrasyncai/verification-gateway/edge-core';\n","/**\n * Time-budgeted event emission — CloudFront wrapper over the edge core's\n * telemetry. Lambda@Edge has no `waitUntil`, so every network call is\n * awaited under an explicit budget and DROPPED on timeout (the core's\n * default posture): latency to the customer's traffic is bounded, and a\n * slow AstraSync API costs telemetry, never page loads.\n */\n\nimport type { CloudFrontRequest } from 'aws-lambda';\nimport {\n emitBeacon as emitBeaconCore,\n withBudget,\n sampled,\n} from '@astrasyncai/verification-gateway/edge-core';\nimport { toNormalizedRequest } from './cf';\nimport type { Classification, EdgeAdapterOptions } from './types';\n\nexport { withBudget, sampled };\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 await emitBeaconCore(options, toNormalizedRequest(request), classification, budgetMs);\n}\n"],"mappings":"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","names":[]}
|
package/dist/lambda/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var Ce=Object.create;var I=Object.defineProperty;var Se=Object.getOwnPropertyDescriptor;var Ie=Object.getOwnPropertyNames;var Te=Object.getPrototypeOf,be=Object.prototype.hasOwnProperty;var Ee=(e,t)=>{for(var r in t)I(e,r,{get:t[r],enumerable:!0})},M=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of Ie(t))!be.call(e,a)&&a!==r&&I(e,a,{get:()=>t[a],enumerable:!(n=Se(t,a))||n.enumerable});return e};var Pe=(e,t,r)=>(r=e!=null?Ce(Te(e)):{},M(t||!e||!e.__esModule?I(r,"default",{value:e,enumerable:!0}):r,e)),Ue=e=>M(I({},"__esModule",{value:!0}),e);var St={};Ee(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_"],ke=new Set(["authorization","x-api-key","x-goog-api-key","api-key"]);function Oe(e){return e==="signature"||e.endsWith("-signature")||e.endsWith("-signature-256")}var Be=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"]),Fe=["x-goog-","x-stainless-","x-anthropic-","x-openai-"],De=["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,H=1024,He=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 De)if(n.startsWith(a))return a}function Le(e){return!!(xe.has(e)||_e.some(t=>e.startsWith(t))||Oe(e))}function $e(e){return Be.has(e)?!0:Fe.some(t=>e.startsWith(t))}function T(e){let t={},r={},n,a=0,s=0;if(!e)return{headers:t};for(let i of Object.keys(e)){if(s>=Me)break;let c=i.toLowerCase(),l=e[i];if(l===void 0)continue;let u=Array.isArray(l)?l.join(", "):String(l);if(ke.has(c)){let v=Ne(u);v&&!n&&(n=v);continue}if(Le(c))continue;let p=u;U(p)>H&&(p=p.slice(0,H));let m=U(c)+U(p);a+m>He||(a+=m,s++,t[c]=p,$e(c)&&(r[c]=p))}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 d(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 je(e){let t={},r=(s,o,i)=>{let c=d(e,o);c&&(t[s]=i?i(c):c)},n=d(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[s,o]of[["desktop","cloudfront-is-desktop-viewer"],["mobile","cloudfront-is-mobile-viewer"],["tablet","cloudfront-is-tablet-viewer"],["smarttv","cloudfront-is-smarttv-viewer"]])d(e,o)==="true"&&a.push(s);return a.length&&(t.device=a.join(",")),t}function b(e){let{headers:t,apiKeyFormat:r,platformHeaders:n}=T(R(e.headers)),a=je(e);return{headers:t,...r&&{apiKeyFormat:r},...n&&{platformHeaders:n},...Object.keys(a).length>0&&{connection:a}}}function $(e){let t=e.origin?.custom?.domainName??e.origin?.s3?.domainName??void 0;if(!t)return;let r=d(e,"host");!r||r===t||(d(e,"x-forwarded-host")||A(e,"X-Forwarded-Host",r),A(e,"Host",t))}function j(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 K(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,s]of Object.entries(r??{}))n[a.toLowerCase()]=[{key:a,value:s}];return{status:String(e),statusDescription:e===403?"Forbidden":e===202?"Accepted":"OK",headers:n,body:JSON.stringify(t)}}var k=Object.freeze({version:1,mode:"observe",depth:"classify",pathRules:[],sampling:Object.freeze({anonymousBeaconRate:.1}),failurePosture:"open"}),V=["observe","enforce"],G=["classify","authenticate","authorize"];function Ke(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 Ke(o)?{edgeConfig:o,etag:a.headers.get("etag")??void 0}:null}catch{return null}}var Ge=60*1e3,ze=1440*60*1e3,S=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:S.get(r)?.etag}).then(s=>{if(s===null)return;let o=Date.now();if("notModified"in s){let i=S.get(r);i&&(i.lastSuccessAt=o);return}S.set(r,{config:s.edgeConfig,etag:s.etag,lastSuccessAt:o})}).finally(()=>{_.delete(r)});return _.set(r,a),a}async function Y(e,t,r){let n=r?.ttlMs??Ge,a=r?.maxStaleMs??ze,s=X(e.apiBaseUrl,t),o=S.get(s);if(o){let i=Date.now()-o.lastSuccessAt;return i>n&&z(e,t).catch(()=>{}),i>a?Xe(o.config):o.config}return await z(e,t).catch(()=>{}),S.get(s)?.config??k}function J(e,t){for(let r of e)if(Ye(r.pattern).test(t))return r}function Ye(e){let t=e.replace(/[.+?^${}()|[\]\\]/g,"\\$&").replace(/\*/g,".*");return new RegExp(`^${t}$`,"i")}var Je=900*1e3,q=new Map;async function qe(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=q.get(t);if(r&&Date.now()-r.fetchedAt<Je)return r.value;try{let a=await(e.fetchSecret??qe)(t);return q.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 Y({apiBaseUrl:e.apiBaseUrl,apiKey:t},e.counterpartyId,{ttlMs:e.configTtlMs})}catch{return e.fallbackConfig??k}}function We(e){return e>=80?"PLATINUM":e>=60?"GOLD":e>=40?"SILVER":"BRONZE"}var Ze="4.0.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;var Q=!1;async function ee(e,t,r,n){re=!0;try{let a=`${e}/agents/verify-access`,o=(await fetch(a,{method:"HEAD"})).headers.get("content-type")??"";if(o.startsWith("text/html")){let i=`[VerificationGateway] apiBaseUrl '${e}' returned HTML (content-type: ${o}). 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(`${i} (strictInit=true)`);console.warn(`${i} Set disableInitChecks: true on GatewayConfig to silence.`)}else t&&console.log(`[VerificationGateway] init check passed for ${e} (content-type: ${o})`);if(n){let i=await fetch(`${e}/agents/verify-access/self-check`,{headers:{Authorization:`Bearer ${n}`}});if(i.status===401){let c=await i.json().catch(()=>({})),l=`[VerificationGateway] the configured AstraSync API key was REJECTED at startup (${c.code??"api_key_invalid"}: ${c.message??"invalid key"}). Every inbound agent will be turned away until the key is rotated`+(c.remediation?.manageUrl?` at ${c.remediation.manageUrl}`:"")+".";if(r)throw new Error(`${l} (strictInit=true)`);console.error(l)}else if(i.ok){let c=await i.json().catch(()=>({}));typeof c.daysToExpiry=="number"&&c.daysToExpiry<=7?console.warn(`[VerificationGateway] the configured AstraSync API key expires in ${c.daysToExpiry} day(s) \u2014 rotate it at Settings \u2192 Developer Tools before integrations start failing.`):t&&console.log("[VerificationGateway] API key self-check passed")}}}catch(a){if(r)throw a;t&&console.log(`[VerificationGateway] init check failed (non-blocking): ${String(a)}`)}}var O=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=O.get(r);return n&&n.expiresAt>Date.now()?n.result:(n&&O.delete(r),null)}var rt=60;function nt(e,t,r,n){let a=r&&r>0?r:rt,s=ne(e,n);O.set(s,{result:t,expiresAt:Date.now()+a*1e3})}function E(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 i=Array.isArray(a)?a[0]:typeof a=="string"?a:void 0;typeof i=="string"&&n.test(i)&&(r.astraId=i)}let s=e["x-api-key"]||e["X-Api-Key"]||e["X-API-KEY"];s&&(r.apiKey=Array.isArray(s)?s[0]:s);let o=e.authorization||e.Authorization;if(o){let i=Array.isArray(o)?o[0]:o;typeof i=="string"&&(r.authorizationHeader=i,i.startsWith("Bearer ")&&(r.jwt=i.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 n=r.source??"no_credentials",a=n==="api_error",s=n==="merchant_misconfig",o=r.urls,i=s?{message:"This site's AstraSync integration is misconfigured (its API key was revoked, expired, or rotated). Your identity and the AstraSync platform are fine \u2014 contact the site operator. Retrying will not help.",registrationUrl:o?.registrationUrl??"",documentationUrl:r.remediation?.docsUrl??o?.documentationUrl??"",steps:["Report the failure to the site operator \u2014 their AstraSync API key needs rotating","Do not retry: the failure is deterministic until the operator updates their key"]}:a?{message:"Verification is temporarily unavailable. Retry with exponential backoff; if the issue persists, contact support with the correlationId.",registrationUrl:o?.registrationUrl??"",documentationUrl:o?.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:o?.registrationUrl??"",documentationUrl:o?.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:i,denialReasons:t?[t]:["No valid agent credentials provided"],failures:s?[{dimension:"verify_access.merchant_misconfigured",message:t??"The integration API key was rejected by AstraSync",guidance:i.message}]:a?[{dimension:"verify_access.api_error",message:t??"Verification temporarily unavailable",guidance:i.message}]:void 0,...s&&{misconfigured:!0},correlationId:r.correlationId,attemptId:r.attemptId,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),n.attemptId&&(a.attemptId=n.attemptId),n.considerationSet&&(a.considerationSet=n.considerationSet),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 s={"Content-Type":"application/json",...e.customHeaders};e.apiKey&&(s.Authorization=`Bearer ${e.apiKey}`,s["X-API-Key"]=e.apiKey);try{let o=await fetch(`${e.apiBaseUrl}/agents/verify-access`,{method:"POST",headers:s,body:JSON.stringify(a),signal:AbortSignal.timeout(e.verifyTimeoutMs??1e4)}),i=await o.json();return o.status===410?{success:!0,attemptId:typeof i?.attemptId=="string"?i.attemptId:void 0,access:{allowed:!1,accessLevel:"none",reason:"endpoint_deactivated",failures:[{dimension:"endpoint.deactivated",message:typeof i?.message=="string"?i.message:"Endpoint has been deactivated",guidance:typeof i?.guidance=="string"?i.guidance:"Reactivate via POST /api/endpoints/{id}/reactivate, or update the URL on the calling agent."}]}}:o.ok?i:o.status===401?{success:!1,error:i.message||i.error||"Integration API key rejected",misconfig:!0,misconfigCode:typeof i?.code=="string"?i.code:void 0,remediation:i?.remediation&&typeof i.remediation=="object"?i.remediation:void 0,correlationId:typeof i?.correlationId=="string"?i.correlationId:void 0,attemptId:typeof i?.attemptId=="string"?i.attemptId:void 0}:{success:!1,error:i.message||i.error||`API returned ${o.status}`,correlationId:typeof i?.correlationId=="string"?i.correlationId:void 0,attemptId:typeof i?.attemptId=="string"?i.attemptId:void 0}}catch(o){return{success:!1,error:`Failed to call verify-access API: ${o instanceof Error?o.message:"Unknown error"}`}}}async function B(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,n.apiKey):ee(n.apiBaseUrl,n.debug,!1,n.apiKey)),!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){n.debug&&console.log("[VerificationGateway] Returning cached result");let h={...g};return t.attemptId?h.attemptId=t.attemptId:delete h.attemptId,h}}let s={...t};!s.counterpartyUrl&&n.counterpartyUrl&&(s.counterpartyUrl=n.counterpartyUrl),!s.counterpartyType&&n.counterpartyType&&(s.counterpartyType=n.counterpartyType),n.debug&&console.log("[VerificationGateway] Calling verify-access API");let o=await ot(n,s),i=o.denialReasons,c=Array.isArray(i)&&i.length>0?i:void 0;if(!o.success)return at(n,o.error,{source:o.misconfig?"merchant_misconfig":"api_error",correlationId:o.correlationId,attemptId:o.attemptId,remediation:o.remediation,urls:a});if(!o.access?.allowed){let g=o.access?.failures;return{identityVerified:o.verificationContext?.idVerified===!0,policyAllowed:!1,accessLevel:"none",denialReasons:c??(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,attemptId:o.attemptId,recommendation:o.recommendation,recommendationReasons:o.recommendationReasons,stepUpApproval:o.stepUpApproval,settlement:o.settlement}}let l=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,u=o.developer?{astradId:o.developer.kyaOwnerId,name:o.developer.fullName,trustScore:o.developer.trustScore||0,verified:o.developer.identityVerified}:void 0,p=o.organization?{name:o.organization.name,verified:o.organization.verified,trustScore:o.organization.trustScore}:void 0,m=o.verificationContext,v=o.access?.accessLevel??"standard",f={identityVerified:o.verificationContext?.idVerified!==!1,policyAllowed:!0,accessLevel:v,agent:l,developer:u,organization:p,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,attemptId:o.attemptId,runtimeChallenge:o.runtimeChallenge,tokenGuidance:o.tokenGuidance,recommendation:o.recommendation,recommendationReasons:o.recommendationReasons,warningHeader:o.warningHeader,stepUpApproval:o.stepUpApproval,settlement:o.settlement};f.recommendation==="deny"?(f.policyAllowed=!1,f.accessLevel="none",f.denialReasons=c??f.recommendationReasons??["Access denied by AstraSync recommendation"],f.guidance=f.runtimeChallenge?{message:`Verification failed: ${f.runtimeChallenge.reason||"runtime challenge failed"}`,registrationUrl:a?.registrationUrl??"",documentationUrl:a?.documentationUrl??""}:{message:f.recommendationReasons?.[0]||"Access denied by AstraSync recommendation",registrationUrl:a?.registrationUrl??"",documentationUrl:a?.documentationUrl??""}):f.recommendation==="step_up_required"&&(f.requiresStepUp=!0,f.denialReasons=c??f.recommendationReasons??["Step-up verification required"]);let y=f.requiresStepUp===!0||f.requiresApproval===!0||f.recommendation==="step_up_required";return n.cacheTtl!==0&&f.recommendation!=="deny"&&!y&&nt(t,f,n.cacheTtl,n.counterpartyId),f}async function ae(e,t){let r=e.apiBaseUrl||te.apiBaseUrl,{counterpartyUrl:n,...a}=t,s={...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(s)}).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??"",s=[];if(t.platformHeaders)for(let[o,i]of Object.entries(t.platformHeaders))s.push(o,`${o}: ${i}`);for(let o of e){let i=o.uaPatterns?.some(p=>p.test(r)),c=o.agentCardPatterns?.some(p=>p.test(n)),l=a.length>0&&o.apiKeyFormatPatterns?.some(p=>p.test(a)),u=s.length>0&&o.headerPatterns?.some(p=>s.some(m=>p.test(m)));if(i||c||l||u){let p=r.split(/[\s/]/)[0]?.toLowerCase()??"",m=(()=>{try{return n?new URL(n).host:""}catch{return""}})(),v=p||m||a||"unknown";return{vendor:o.vendor,fingerprintKey:`${o.vendor}:${v}`,displayName:o.displayName}}}}function F(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=j(e,t),a=e.method,s=R(e.headers),o=ut(r),i=r.truncated?{bodyTruncated:!0}:{},c=d(e,"authorization"),l=c?.replace(/^Bearer\s+/i,""),p=[d(e,"x-payment-mandate")??d(e,"payment-mandate"),l].find(y=>y&&ct.test(y));if(p){let y=dt(p)?.typ;return{protocol:"vi-sd-jwt",artifacts:{viSdJwt:p},evidence:{typ:y,...i}}}let m=d(e,"signature"),v=d(e,"signature-input");if(m&&v){let y=d(e,"signature-agent"),g=d(e,"x-agent-registry"),h=v.toLowerCase();return{protocol:y?"rfc9421-web-bot-auth":g?.toLowerCase().includes("visa")||h.includes("visa")?"rfc9421-tap":g?.toLowerCase().includes("mastercard")||h.includes("mastercard")?"rfc9421-agent-pay":"rfc9421",artifacts:{rfc9421:{headers:s,method:a,url:n,...r.text&&{body:r.text}}},evidence:{...y&&{signatureAgent:y},...g&&{registryHint:g},keyid:v.match(/keyid="([^"]+)"/)?.[1],...i}}}let f=d(e,"timestamp");if(m&&!v&&(f||d(e,"idempotency-key")))return{protocol:"acp",artifacts:{acpRequest:{method:a,url:n,headers:s,...r.text&&{rawBody:r.text},...o&&{body:o}}},evidence:{hasTimestamp:!!f,...i}};if(/\/checkout[-_]sessions(\/|$)/.test(e.uri))return{protocol:"ucp",artifacts:{ucpRequest:{method:a,url:n,...o&&{body:o}}},evidence:{path:e.uri,...i}};if(o){let y=o.intent_mandate??o.intentMandate,g=o.cart_mandate??o.cartMandate,h=o.payment_mandate??o.paymentMandate;if(typeof y=="string"||typeof g=="string"||typeof h=="string")return{protocol:"ap2",artifacts:{ap2Mandates:{...typeof y=="string"&&{intent:y},...typeof g=="string"&&{cart:g},...typeof h=="string"&&{payment:h}}},evidence:{mandatesPresent:[typeof y=="string"&&"intent",typeof g=="string"&&"cart",typeof h=="string"&&"payment"].filter(Boolean),...i}}}if(c&&/^Payment\s+/i.test(c))return{protocol:"mpp",artifacts:{mppRequest:{method:a,url:n,headers:s,...r.text&&{rawBody:r.text},...o&&{body:o}}},evidence:i};if(d(e,"x-payment"))return{protocol:"x402",artifacts:{x402Request:{method:a,url:n,headers:s,...o&&{body:o}}},evidence:i}}var pt=/\.(css|js|mjs|map|png|jpe?g|gif|webp|avif|svg|ico|woff2?|ttf|otf|eot|mp4|webm|pdf|txt|xml)$/i,lt=/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(pt.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 se(e,t,r){let n=d(e,"user-agent")??"",a=R(e.headers),s=E(a,{});if(s.astraId||s.apiKey||s.jwt)return{tier:"verified",protocol:"x-astra"};let{apiKeyFormat:o,platformHeaders:i}=T(a),c={userAgent:n,apiKeyFormat:o,platformHeaders:i};if(r!=="classify"){let u=oe(e,t,L(e));if(u)return{tier:"identified",protocol:u.protocol,artifacts:u.artifacts,evidence:u.evidence,fingerprint:F(c)}}let l=F(c);return l?{tier:"identified",protocol:"platform-ua",fingerprint:l}:!n||lt.test(n)||!ft.test(n)?{tier:"anonymous-bot",evidence:{userAgent:n||"(none)"}}:!d(e,"sec-fetch-mode")&&!d(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,s;if(e.routePurpose?(a=e.routePurpose,s="route_config"):e.hasCustomPurposeExtractor?e.customPurpose&&(a=e.customPurpose,s="custom_extractor"):t?(a=t.purpose,s="header"):e.legacyPurpose?(a=e.legacyPurpose,s="legacy_header"):e.queryPurpose&&(a=e.queryPurpose,s="query"),!a){let o=r.indexOf(".");o>0?(a=r.slice(0,o),s="action_derived"):(a=mt,s="transport_default")}return{purpose:a,action:r,purposeSource:s,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 D(e,t,r,n){let a=d(t,"cloudfront-viewer-address")?.split(":")[0];await P(ae({apiBaseUrl:e.apiBaseUrl,counterpartyId:e.counterpartyId},{counterpartyUrl:e.counterpartyUrl,counterpartyType:"website",sourceIp:a,userAgent:d(t,"user-agent"),requestPath:t.uri,requestMethod:t.method,protocol:r.protocol,evidence:r.evidence,observedMetadata:b(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),s=d(t,"cloudfront-viewer-address")?.split(":")[0],o=ce({method:t.method,astraPurpose:d(t,"x-astra-purpose"),astraAction:d(t,"x-astra-action")}),i=d(t,"x-astra-attempt-id");return{...i&&/^att_[0-9a-f]{32}$/.test(i)&&{attemptId:i},credentials:E(a,{}),purpose:o.purpose,action:o.action,resource:t.uri,createSession:!0,counterpartyType:"website",counterpartyUrl:e.counterpartyUrl,callerMetadata:{sourceIp:s,userAgent:d(t,"user-agent"),referer:d(t,"referer"),host:d(t,"host"),observedMetadata:b(t)},...n&&r.artifacts&&{commerceArtifacts:r.artifacts}}}function pe(e,t){let r=w(e);return r.sessionId=t.sessionId,r.correlationId=t.correlationId,r.guidance=t.guidance,t.misconfigured?(r.reasons=["verification key misconfigured \u2014 fail-open (merchant_misconfig)"],r.misconfig=!0,r):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 le(e,t,r,n,a,s){let o=e.budgets??{},i=o.beaconMs??300,c=n.mode==="enforce"?o.verifyEnforceMs??3e3:o.verifyObserveMs??1500,l={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 u=await P(B(l,ue(e,t,r,n.depth==="authorize")),c);if(!u){let p=w(r);return p.reasons=["verify-access unreachable within budget \u2014 fail-open"],p}return pe(r,u)}case"identified":{if(n.depth==="authorize"&&r.artifacts){let p=await P(B(l,ue(e,t,r,!0)),c);if(!p){let m=w(r);return m.reasons=["verify-access unreachable within budget \u2014 fail-open"],m}return pe(r,p)}await D(e,t,r,i);let u=w(r);return n.mode==="enforce"&&n.depth!=="classify"&&(u.decision="deny",u.reasons=["agent is not registered with AstraSync"]),u}case"anonymous-bot":return de(s.anonymousBeaconRate,e.random)&&await D(e,t,{...r,protocol:r.protocol??"ua-heuristic"},i),w(r);default:return w(r)}}catch(u){let p=w(r);return p.reasons=[`edge pipeline error \u2014 fail-open: ${u instanceof Error?u.message:String(u)}`],p}}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 C={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(K(t,"x-astrasync-"),r.misconfig&&A(t,C.observedDecision,"misconfig"),n.mode!=="enforce")return r.decision!=="allow"&&A(t,C.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:s}=t.asat;r!==void 0&&A(e,C.trustScore,String(r)),n&&A(e,C.agentId,n),a&&A(e,C.developerId,a),s&&A(e,C.permissions,s)}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{$(a);let s=await W({...e,log:t}),o=await Z(e,s),i=ie(o,a.uri);if(i.skip)return a;let c=d(a,"x-forwarded-host")??d(a,"host")??new URL(e.counterpartyUrl).host,l=se(a,c,i.depth);if(l.tier==="human")return a;let u=await le({...e,log:t},a,l,i,s,o.sampling);return u.decision!=="allow"&&t("info",i.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}),me(e,a,u,i)}catch(s){return t("error","edge handler error \u2014 passing request through",{error:s instanceof Error?s.message:String(s)}),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});
|
|
1
|
+
"use strict";var pe=Object.create;var S=Object.defineProperty;var fe=Object.getOwnPropertyDescriptor;var ge=Object.getOwnPropertyNames;var ye=Object.getPrototypeOf,me=Object.prototype.hasOwnProperty;var he=(e,t)=>{for(var r in t)S(e,r,{get:t[r],enumerable:!0})},k=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of ge(t))!me.call(e,a)&&a!==r&&S(e,a,{get:()=>t[a],enumerable:!(n=fe(t,a))||n.enumerable});return e};var ve=(e,t,r)=>(r=e!=null?pe(ye(e)):{},k(t||!e||!e.__esModule?S(r,"default",{value:e,enumerable:!0}):r,e)),Ae=e=>k(S({},"__esModule",{value:!0}),e);var Ot={};he(Ot,{handler:()=>kt});module.exports=Ae(Ot);var Ht=Object.freeze({version:1,mode:"observe",depth:"classify",pathRules:[],sampling:Object.freeze({anonymousBeaconRate:.1}),failurePosture:"open"}),Dt=60*1e3,Nt=1440*60*1e3;function we(e,t){for(let r of e)if(Re(r.pattern).test(t))return r}function Re(e){let t=e.replace(/[.+?^${}()|[\]\\]/g,"\\$&").replace(/\*/g,".*");return new RegExp(`^${t}$`,"i")}var be=1,Se=new Set(["cookie","set-cookie","x-csrf-token","proxy-authorization","x-astrasync-signature","x-astrasync-secret","x-hub-signature","x-hub-signature-256","x-astra-verified-hop"]),Ie=["x-astrasync-","kya-","kya_"],Te=new Set(["authorization","x-api-key","x-goog-api-key","api-key"]);function Ee(e){return e==="signature"||e.endsWith("-signature")||e.endsWith("-signature-256")}var Ce=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"]),_e=["x-goog-","x-stainless-","x-anthropic-","x-openai-"],Ue=["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-"],Pe=64,O=1024,xe=16*1024;function I(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 ke(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 Ue)if(n.startsWith(a))return a}function Oe(e){return!!(Se.has(e)||Ie.some(t=>e.startsWith(t))||Ee(e))}function Me(e){return Ce.has(e)?!0:_e.some(t=>e.startsWith(t))}function K(e){let t={},r={},n,a=0,i=0;if(!e)return{headers:t};for(let s of Object.keys(e)){if(i>=Pe)break;let c=s.toLowerCase(),f=e[s];if(f===void 0)continue;let p=Array.isArray(f)?f.join(", "):String(f);if(Te.has(c)){let h=ke(p);h&&!n&&(n=h);continue}if(Oe(c))continue;let u=p;I(u)>O&&(u=u.slice(0,O));let g=I(c)+I(u);a+g>xe||(a+=g,i++,t[c]=u,Me(c)&&(r[c]=u))}let o={headers:t};return n&&(o.apiKeyFormat=n),Object.keys(r).length>0&&(o.platformHeaders=r),o}var Fe=[{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 He(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)),c=o.agentCardPatterns?.some(u=>u.test(n)),f=a.length>0&&o.apiKeyFormatPatterns?.some(u=>u.test(a)),p=i.length>0&&o.headerPatterns?.some(u=>i.some(g=>u.test(g)));if(s||c||f||p){let u=r.split(/[\s/]/)[0]?.toLowerCase()??"",g=(()=>{try{return n?new URL(n).host:""}catch{return""}})(),h=u||g||a||"unknown";return{vendor:o.vendor,fingerprintKey:`${o.vendor}:${h}`,displayName:o.displayName}}}}function M(e){return He(Fe,e)}function De(e){return e>=80?"PLATINUM":e>=60?"GOLD":e>=40?"SILVER":"BRONZE"}var Ne="4.1.0",jt=3600*1e3,je=new Map;function Le(e){return je.get(e)?.data}var z={apiBaseUrl:"https://astrasync.ai/api",defaultAccessLevel:"none",debug:!1},G=!1,F=!1;async function H(e,t,r,n){G=!0;try{let a=`${e}/agents/verify-access`,o=(await fetch(a,{method:"HEAD"})).headers.get("content-type")??"";if(o.startsWith("text/html")){let s=`[VerificationGateway] apiBaseUrl '${e}' returned HTML (content-type: ${o}). 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(`${s} (strictInit=true)`);console.warn(`${s} Set disableInitChecks: true on GatewayConfig to silence.`)}else t&&console.log(`[VerificationGateway] init check passed for ${e} (content-type: ${o})`);if(n){let s=await fetch(`${e}/agents/verify-access/self-check`,{headers:{Authorization:`Bearer ${n}`}});if(s.status===401){let c=await s.json().catch(()=>({})),f=`[VerificationGateway] the configured AstraSync API key was REJECTED at startup (${c.code??"api_key_invalid"}: ${c.message??"invalid key"}). Every inbound agent will be turned away until the key is rotated`+(c.remediation?.manageUrl?` at ${c.remediation.manageUrl}`:"")+".";if(r)throw new Error(`${f} (strictInit=true)`);console.error(f)}else if(s.ok){let c=await s.json().catch(()=>({}));typeof c.daysToExpiry=="number"&&c.daysToExpiry<=7?console.warn(`[VerificationGateway] the configured AstraSync API key expires in ${c.daysToExpiry} day(s) \u2014 rotate it at Settings \u2192 Developer Tools before integrations start failing.`):t&&console.log("[VerificationGateway] API key self-check passed")}}}catch(a){if(r)throw a;t&&console.log(`[VerificationGateway] init check failed (non-blocking): ${String(a)}`)}}var E=new Map;function X(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 $e(e,t){let r=X(e,t),n=E.get(r);return n&&n.expiresAt>Date.now()?n.result:(n&&E.delete(r),null)}var Be=60;function Ve(e,t,r,n){let a=r&&r>0?r:Be,i=X(e,n);E.set(i,{result:t,expiresAt:Date.now()+a*1e3})}function Y(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 Ke(e,t,r={}){let n=r.source??"no_credentials",a=n==="api_error",i=n==="merchant_misconfig",o=r.urls,s=i?{message:"This site's AstraSync integration is misconfigured (its API key was revoked, expired, or rotated). Your identity and the AstraSync platform are fine \u2014 contact the site operator. Retrying will not help.",registrationUrl:o?.registrationUrl??"",documentationUrl:r.remediation?.docsUrl??o?.documentationUrl??"",steps:["Report the failure to the site operator \u2014 their AstraSync API key needs rotating","Do not retry: the failure is deterministic until the operator updates their key"]}:a?{message:"Verification is temporarily unavailable. Retry with exponential backoff; if the issue persists, contact support with the correlationId.",registrationUrl:o?.registrationUrl??"",documentationUrl:o?.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:o?.registrationUrl??"",documentationUrl:o?.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:s,denialReasons:t?[t]:["No valid agent credentials provided"],failures:i?[{dimension:"verify_access.merchant_misconfigured",message:t??"The integration API key was rejected by AstraSync",guidance:s.message}]:a?[{dimension:"verify_access.api_error",message:t??"Verification temporarily unavailable",guidance:s.message}]:void 0,...i&&{misconfigured:!0},correlationId:r.correlationId,attemptId:r.attemptId,verifiedAt:new Date}}async function ze(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),n.attemptId&&(a.attemptId=n.attemptId),n.considerationSet&&(a.considerationSet=n.considerationSet),a.sdkVersion=Ne,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),signal:AbortSignal.timeout(e.verifyTimeoutMs??1e4)}),s=await o.json();return o.status===410?{success:!0,attemptId:typeof s?.attemptId=="string"?s.attemptId:void 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:o.status===401?{success:!1,error:s.message||s.error||"Integration API key rejected",misconfig:!0,misconfigCode:typeof s?.code=="string"?s.code:void 0,remediation:s?.remediation&&typeof s.remediation=="object"?s.remediation:void 0,correlationId:typeof s?.correlationId=="string"?s.correlationId:void 0,attemptId:typeof s?.attemptId=="string"?s.attemptId:void 0}:{success:!1,error:s.message||s.error||`API returned ${o.status}`,correlationId:typeof s?.correlationId=="string"?s.correlationId:void 0,attemptId:typeof s?.attemptId=="string"?s.attemptId:void 0}}catch(o){return{success:!1,error:`Failed to call verify-access API: ${o instanceof Error?o.message:"Unknown error"}`}}}async function D(e,t,r){let n={...z,...e},a=n.apiBaseUrl?Le(n.apiBaseUrl):void 0;if(!G&&!n.disableInitChecks&&n.apiBaseUrl&&(n.strictInit?await H(n.apiBaseUrl,n.debug,!0,n.apiKey):H(n.apiBaseUrl,n.debug,!1,n.apiKey)),!F&&(e.minTrustScore!==void 0||e.minTrustScoreForFull!==void 0)&&(F=!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 y=$e(t,n.counterpartyId);if(y){n.debug&&console.log("[VerificationGateway] Returning cached result");let R={...y};return t.attemptId?R.attemptId=t.attemptId:delete R.attemptId,R}}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 ze(n,i),s=o.denialReasons,c=Array.isArray(s)&&s.length>0?s:void 0;if(!o.success)return Ke(n,o.error,{source:o.misconfig?"merchant_misconfig":"api_error",correlationId:o.correlationId,attemptId:o.attemptId,remediation:o.remediation,urls:a});if(!o.access?.allowed){let y=o.access?.failures;return{identityVerified:o.verificationContext?.idVerified===!0,policyAllowed:!1,accessLevel:"none",denialReasons:c??(y&&y.length>0?y.map(le=>le.message):o.access?.reason?[o.access.reason]:["Access denied"]),failures:y,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,attemptId:o.attemptId,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:De(o.agent.trustScore),blockchainVerified:o.agent.blockchainStatus==="verified",status:o.agent.agentStatus}:void 0,p=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,g=o.verificationContext,h=o.access?.accessLevel??"standard",d={identityVerified:o.verificationContext?.idVerified!==!1,policyAllowed:!0,accessLevel:h,agent:f,developer:p,organization:u,appliedPolicy:o.access?.appliedPolicy,verificationContext:g,requiresStepUp:o.access?.requiresStepUp,requiresApproval:o.access?.requiresApproval,verifiedAt:new Date,cacheTtl:n.cacheTtl,sessionId:o.sessionId,correlationId:o.correlationId,attemptId:o.attemptId,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=c??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=c??d.recommendationReasons??["Step-up verification required"]);let m=d.requiresStepUp===!0||d.requiresApproval===!0||d.recommendation==="step_up_required";return n.cacheTtl!==0&&d.recommendation!=="deny"&&!m&&Ve(t,d,n.cacheTtl,n.counterpartyId),d}async function Ge(e,t){let r=e.apiBaseUrl||z.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(()=>{})}function l(e,t){return e.headers[t.toLowerCase()]}function Xe(e,t){let r=e.querystring?`?${e.querystring}`:"";return`https://${t}${e.uri}${r}`}function _(e){let{headers:t,apiKeyFormat:r,platformHeaders:n}=K(e.headers),a=e.connection??{};return{headers:t,...r&&{apiKeyFormat:r},...n&&{platformHeaders:n},...Object.keys(a).length>0&&{connection:a},schemaVersion:be}}var Ye=/^eyJ[\w-]+\.[\w-]*\.[\w-]+~/;function Je(e){let t=e.replace(/-/g,"+").replace(/_/g,"/"),r=t+"=".repeat((4-t.length%4)%4),n=atob(r),a=new Uint8Array(n.length);for(let i=0;i<n.length;i++)a[i]=n.charCodeAt(i);return new TextDecoder().decode(a)}function We(e){try{let t=e.split(".")[0];return JSON.parse(Je(t))}catch{return}}function Ze(e){if(e)try{let t=JSON.parse(e);return typeof t=="object"&&t!==null?t:void 0}catch{return}}function Qe(e,t){let r=Xe(e,t),n=e.method,a=e.headers,i=Ze(e.bodyText),o=e.bodyTruncated?{bodyTruncated:!0}:{},s=l(e,"authorization"),c=s?.replace(/^Bearer\s+/i,""),p=[l(e,"x-payment-mandate")??l(e,"payment-mandate"),c].find(d=>d&&Ye.test(d));if(p){let d=We(p)?.typ;return{protocol:"vi-sd-jwt",artifacts:{viSdJwt:p},evidence:{typ:d,...o}}}let u=l(e,"signature"),g=l(e,"signature-input");if(u&&g){let d=l(e,"signature-agent"),m=l(e,"x-agent-registry"),y=g.toLowerCase();return{protocol:d?"rfc9421-web-bot-auth":m?.toLowerCase().includes("visa")||y.includes("visa")?"rfc9421-tap":m?.toLowerCase().includes("mastercard")||y.includes("mastercard")?"rfc9421-agent-pay":"rfc9421",artifacts:{rfc9421:{headers:a,method:n,url:r,...e.bodyText&&{body:e.bodyText}}},evidence:{...d&&{signatureAgent:d},...m&&{registryHint:m},keyid:g.match(/keyid="([^"]+)"/)?.[1],...o}}}let h=l(e,"timestamp");if(u&&!g&&(h||l(e,"idempotency-key")))return{protocol:"acp",artifacts:{acpRequest:{method:n,url:r,headers:a,...e.bodyText&&{rawBody:e.bodyText},...i&&{body:i}}},evidence:{hasTimestamp:!!h,...o}};if(/\/checkout[-_]sessions(\/|$)/.test(e.uri))return{protocol:"ucp",artifacts:{ucpRequest:{method:n,url:r,...i&&{body:i}}},evidence:{path:e.uri,...o}};if(i){let d=i.intent_mandate??i.intentMandate,m=i.cart_mandate??i.cartMandate,y=i.payment_mandate??i.paymentMandate;if(typeof d=="string"||typeof m=="string"||typeof y=="string")return{protocol:"ap2",artifacts:{ap2Mandates:{...typeof d=="string"&&{intent:d},...typeof m=="string"&&{cart:m},...typeof y=="string"&&{payment:y}}},evidence:{mandatesPresent:[typeof d=="string"&&"intent",typeof m=="string"&&"cart",typeof y=="string"&&"payment"].filter(Boolean),...o}}}if(s&&/^Payment\s+/i.test(s))return{protocol:"mpp",artifacts:{mppRequest:{method:n,url:r,headers:a,...e.bodyText&&{rawBody:e.bodyText},...i&&{body:i}}},evidence:o};if(l(e,"x-payment"))return{protocol:"x402",artifacts:{x402Request:{method:n,url:r,headers:a,...i&&{body:i}}},evidence:o}}var qe=/\.(css|js|mjs|map|png|jpe?g|gif|webp|avif|svg|ico|woff2?|ttf|otf|eot|mp4|webm|pdf|txt|xml)$/i,et=/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,tt=/mozilla\/\d/i;function rt(e,t){if(qe.test(t))return{mode:e.mode,depth:e.depth,skip:!0};let r=we(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 nt(e,t,r){let n=l(e,"user-agent")??"",a=Y(e.headers,{});if(a.astraId||a.apiKey||a.jwt)return{tier:"verified",protocol:"x-astra"};let{apiKeyFormat:i,platformHeaders:o}=K(e.headers),s={userAgent:n,apiKeyFormat:i,platformHeaders:o};if(r!=="classify"){let f=Qe(e,t);if(f)return{tier:"identified",protocol:f.protocol,artifacts:f.artifacts,evidence:f.evidence,fingerprint:M(s)}}let c=M(s);return c?{tier:"identified",protocol:"platform-ua",fingerprint:c}:!n||et.test(n)||!tt.test(n)?{tier:"anonymous-bot",evidence:{userAgent:n||"(none)"}}:!l(e,"sec-fetch-mode")&&!l(e,"accept-language")?{tier:"anonymous-bot",evidence:{userAgent:n,spoofedBrowser:!0}}:{tier:"human"}}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 ot(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 it(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 st="X-Astra-Verified-Hop";function ct(e){return`${e.astraId};${e.sessionId??""};${e.checkedAt}`}var A={trustScore:"X-AstraSync-Trust-Score",agentId:"X-AstraSync-Agent-Id",developerId:"X-AstraSync-Developer-Id",permissions:"X-AstraSync-Permissions",observedDecision:"X-AstraSync-Observed-Decision"},N=["x-astrasync-","x-astra-verified-hop"];function T(){return{action:"pass",injectHeaders:{},stripHeaderPrefixes:[]}}function dt(e,t,r){let n={};if(t.misconfig&&(n[A.observedDecision]="misconfig"),r.mode!=="enforce")return t.decision!=="allow"&&(n[A.observedDecision]=t.decision),Object.assign(n,j(t),L(t)),{action:"pass",injectHeaders:n,stripHeaderPrefixes:N};switch(t.decision){case"deny":return{action:"respond",status:403,headers:{},bodyJson:ot(t,e.apiBaseUrl)};case"manual_review":return{action:"respond",status:202,headers:{"Retry-After":"30"},bodyJson:it(t)};default:return Object.assign(n,j(t),L(t)),{action:"pass",injectHeaders:n,stripHeaderPrefixes:N}}}function j(e){if(!e.asat)return{};let{trustScore:t,agentId:r,developerId:n,accessLevel:a}=e.asat;return{...t!==void 0&&{[A.trustScore]:String(t)},...r&&{[A.agentId]:r},...n&&{[A.developerId]:n},...a&&{[A.permissions]:a}}}function L(e){let t=e.asat?.agentId;return t?{[st]:ct({astraId:t,...e.sessionId&&{sessionId:e.sessionId},checkedAt:Date.now()})}:{}}var ut={GET:"data.read",HEAD:"data.read",OPTIONS:"data.read",POST:"data.write",PUT:"data.write",PATCH:"data.write",DELETE:"data.delete"},lt="data.write",pt="data";function ft(e){return ut[e.toUpperCase()]??lt}function gt(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 yt(e){let t=e.astraPurpose?gt(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=ft(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=pt,i="transport_default")}return{purpose:a,action:r,purposeSource:i,actionSource:n}}async function C(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 $(e,t,r,n){let a=C(Ge({apiBaseUrl:e.apiBaseUrl,counterpartyId:e.counterpartyId},{counterpartyUrl:e.counterpartyUrl,counterpartyType:"website",sourceIp:t.clientIp,userAgent:l(t,"user-agent"),requestPath:t.uri,requestMethod:t.method,protocol:r.protocol,evidence:r.evidence,observedMetadata:_(t)}),n);if(e.waitUntil){e.waitUntil(a);return}await a}function mt(e,t=Math.random){return e<=0?!1:e>=1?!0:t()<e}function v(e){return{tier:e.tier,decision:"allow",reasons:[],protocol:e.protocol}}function B(e,t,r,n){let a=yt({method:t.method,astraPurpose:l(t,"x-astra-purpose"),astraAction:l(t,"x-astra-action")}),i=l(t,"x-astra-attempt-id");return{...i&&/^att_[0-9a-f]{32}$/.test(i)&&{attemptId:i},credentials:Y(t.headers,{}),purpose:a.purpose,action:a.action,resource:t.uri,createSession:!0,counterpartyType:"website",counterpartyUrl:e.counterpartyUrl,callerMetadata:{sourceIp:t.clientIp,userAgent:l(t,"user-agent"),referer:l(t,"referer"),host:l(t,"host"),observedMetadata:_(t)},...n&&r.artifacts&&{commerceArtifacts:r.artifacts}}}function V(e,t){let r=v(e);return r.sessionId=t.sessionId,r.correlationId=t.correlationId,r.guidance=t.guidance,t.misconfigured?(r.reasons=["verification key misconfigured \u2014 fail-open (merchant_misconfig)"],r.misconfig=!0,r):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 ht(e,t,r,n,a,i){let o=e.budgets??{},s=o.beaconMs??300,c=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 v(r);let p=await C(D(f,B(e,t,r,n.depth==="authorize")),c);if(!p){let u=v(r);return u.reasons=["verify-access unreachable within budget \u2014 fail-open"],u}return V(r,p)}case"identified":{if(n.depth==="authorize"&&r.artifacts){let u=await C(D(f,B(e,t,r,!0)),c);if(!u){let g=v(r);return g.reasons=["verify-access unreachable within budget \u2014 fail-open"],g}return V(r,u)}await $(e,t,r,s);let p=v(r);return n.mode==="enforce"&&n.depth!=="classify"&&(p.decision="deny",p.reasons=["agent is not registered with AstraSync"]),p}case"anonymous-bot":return mt(i.anonymousBeaconRate,e.random)&&await $(e,t,{...r,protocol:r.protocol??"ua-heuristic"},s),v(r);default:return v(r)}}catch(p){let u=v(r);return u.reasons=[`edge pipeline error \u2014 fail-open: ${p instanceof Error?p.message:String(p)}`],u}}async function J(e,t,r,n){let a=rt(r,t.uri);try{if(a.skip)return{policy:a,application:T()};let i=l(t,"x-forwarded-host")??l(t,"host")??new URL(e.counterpartyUrl).host,o=nt(t,i,a.depth);if(o.tier==="human")return{policy:a,classification:o,application:T()};let s=await ht(e,t,o,a,n,r.sampling);return s.decision!=="allow"&&e.log?.("info",a.mode==="enforce"?`${s.decision.toUpperCase()}: ${t.method} ${t.uri}`:`[OBSERVE] would have been ${s.decision.toUpperCase()}: ${t.method} ${t.uri}`,{tier:s.tier,protocol:s.protocol,reasons:s.reasons}),{policy:a,classification:o,outcome:s,application:dt(e,s,a)}}catch(i){return e.log?.("error","edge evaluation error \u2014 passing request through",{error:i instanceof Error?i.message:String(i)}),{policy:a,application:T()}}}function W(e){return e.Records[0].cf.request}function w(e,t){return e.headers[t.toLowerCase()]?.[0]?.value}function vt(e){let t={};for(let[r,n]of Object.entries(e))n?.length&&(t[r]=n.map(a=>a.value).join(", "));return t}function At(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 wt(e){let t={},r=(i,o,s)=>{let c=w(e,o);c&&(t[i]=s?s(c):c)},n=w(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("countryRegionName","cloudfront-viewer-country-region-name"),r("city","cloudfront-viewer-city"),r("asn","cloudfront-viewer-asn"),r("tlsVersion","cloudfront-viewer-tls"),r("httpVersion","cloudfront-viewer-http-version"),r("timeZone","cloudfront-viewer-time-zone"),r("metroCode","cloudfront-viewer-metro-code"),r("tlsFingerprint","cloudfront-viewer-ja3-fingerprint"),r("ja4","cloudfront-viewer-ja4-fingerprint"),r("headerOrder","cloudfront-viewer-header-order",i=>i.slice(0,256)),r("headerCount","cloudfront-viewer-header-count");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"]])w(e,o)==="true"&&a.push(i);return a.length&&(t.device=a.join(",")),t}function Z(e){let t=At(e),r=wt(e),n=w(e,"cloudfront-viewer-address")?.split(":")[0];return{method:e.method,uri:e.uri,querystring:e.querystring??"",headers:vt(e.headers),...t.text!==void 0&&{bodyText:t.text},...t.truncated&&{bodyTruncated:!0},...n&&{clientIp:n},...Object.keys(r).length>0&&{connection:r}}}function Q(e,t){if(t.action==="respond")return bt(t.status,t.bodyJson,t.headers);for(let r of t.stripHeaderPrefixes)Rt(e,r);for(let[r,n]of Object.entries(t.injectHeaders))U(e,r,n);return e}function q(e){let t=e.origin?.custom?.domainName??e.origin?.s3?.domainName??void 0;if(!t)return;let r=w(e,"host");!r||r===t||(w(e,"x-forwarded-host")||U(e,"X-Forwarded-Host",r),U(e,"Host",t))}function U(e,t,r){e.headers[t.toLowerCase()]=[{key:t,value:r}]}function Rt(e,t){let r=t.toLowerCase();for(let n of Object.keys(e.headers))n.startsWith(r)&&delete e.headers[n]}function bt(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 x=Object.freeze({version:1,mode:"observe",depth:"classify",pathRules:[],sampling:Object.freeze({anonymousBeaconRate:.1}),failurePosture:"open"}),ee=["observe","enforce"],te=["classify","authenticate","authorize"];function St(e){if(typeof e!="object"||e===null)return!1;let t=e;if(t.version!==1||!ee.includes(t.mode)||!te.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&&!ee.includes(a.mode)||a.depth!==void 0&&!te.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 It(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 St(o)?{edgeConfig:o,etag:a.headers.get("etag")??void 0}:null}catch{return null}}var Tt=60*1e3,Et=1440*60*1e3,b=new Map,P=new Map;function ne(e,t){return`${e}|${t}`}function Ct(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 re(e,t){let r=ne(e.apiBaseUrl,t),n=P.get(r);if(n)return n;let a=It(e,t,{etag:b.get(r)?.etag}).then(i=>{if(i===null)return;let o=Date.now();if("notModified"in i){let s=b.get(r);s&&(s.lastSuccessAt=o);return}b.set(r,{config:i.edgeConfig,etag:i.etag,lastSuccessAt:o})}).finally(()=>{P.delete(r)});return P.set(r,a),a}async function ae(e,t,r){let n=r?.ttlMs??Tt,a=r?.maxStaleMs??Et,i=ne(e.apiBaseUrl,t),o=b.get(i);if(o){let s=Date.now()-o.lastSuccessAt;return s>n&&re(e,t).catch(()=>{}),s>a?Ct(o.config):o.config}return await re(e,t).catch(()=>{}),b.get(i)?.config??x}var _t=900*1e3,oe=new Map;async function Ut(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 ie(e){if(e.apiKey)return e.apiKey;let t=e.apiKeySecretName;if(!t)return;let r=oe.get(t);if(r&&Date.now()-r.fetchedAt<_t)return r.value;try{let a=await(e.fetchSecret??Ut)(t);return oe.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 se(e,t){try{return await ae({apiBaseUrl:e.apiBaseUrl,apiKey:t},e.counterpartyId,{ttlMs:e.configTtlMs})}catch{return e.fallbackConfig??x}}function ce(e){let t=e.log??((r,n,a)=>{console[r==="info"?"log":r](`[astrasync-edge] ${n}`,a??"")});return async function(n){let a=W(n);try{q(a);let i=await ie({...e,log:t}),o=await se(e,i),{application:s}=await J({...e,log:t},Z(a),o,i);return Q(a,s)}catch(i){return t("error","edge handler error \u2014 passing request through",{error:i instanceof Error?i.message:String(i)}),a}}}var Pt="https://astrasync.ai/api",de="",ue="",xt=void 0;(!de||!ue)&&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 kt=ce({apiBaseUrl:Pt,counterpartyId:de,counterpartyUrl:ue,apiKeySecretName:xt});0&&(module.exports={handler});
|
package/infra/RUNBOOK.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Edge adapter deployment runbook
|
|
2
2
|
|
|
3
|
-
Internal devops runbook for deploying the
|
|
3
|
+
Internal devops runbook for deploying the Trusted Agent Gateway edge shell
|
|
4
4
|
(Lambda@Edge + CloudFront) in front of an existing origin. Observe-only by
|
|
5
5
|
default — the deploy changes no site behaviour; it adds visibility.
|
|
6
6
|
|
|
@@ -55,7 +55,7 @@ default — the deploy changes no site behaviour; it adds visibility.
|
|
|
55
55
|
Railway), the platform-issued domain (`<service>.up.railway.app`) is
|
|
56
56
|
the origin.
|
|
57
57
|
|
|
58
|
-
> **Metadata capture (
|
|
58
|
+
> **Metadata capture (SDK 3.10.0):** the template now
|
|
59
59
|
> provisions a **custom `OriginRequestPolicy`** (`allViewerAndWhitelistCloudFront`)
|
|
60
60
|
> so CloudFront forwards the `CloudFront-Viewer-*` connection signals
|
|
61
61
|
> (IP / ASN / country / TLS version / HTTP version / device). Unlike the
|
|
@@ -84,6 +84,21 @@ Production rollover = the same commands with prod values
|
|
|
84
84
|
(`astrasync.ai`, `https://astrasync.ai/api`, its own secret + cert + stack
|
|
85
85
|
name) after the staging stability gate above.
|
|
86
86
|
|
|
87
|
+
## Upgrading an existing stack for fingerprint capture (adapter-lambda ≥ 2.2.0)
|
|
88
|
+
|
|
89
|
+
Stacks deployed before 2.2.0 whitelist no JA3/JA4/header-structure headers in
|
|
90
|
+
the `EdgeOriginRequestPolicy`, so those signals never reach the function.
|
|
91
|
+
Upgrading is a normal CloudFormation stack update — re-run `deploy.sh` with
|
|
92
|
+
the same exports after updating the package. Origin-request-policy changes
|
|
93
|
+
propagate without replacing the distribution (minutes, no DNS impact, no
|
|
94
|
+
downtime).
|
|
95
|
+
|
|
96
|
+
Verify after update: make one HTTPS request through the distribution and
|
|
97
|
+
confirm a `cloudfront-viewer-ja3-fingerprint` value appears in the function's
|
|
98
|
+
CloudWatch log (or in the verify-access event's `observedMetadata.connection`
|
|
99
|
+
as `tlsFingerprint`). JA3/JA4 are present only for HTTPS viewers — an HTTP
|
|
100
|
+
probe redirects first, so probe the `https://` URL directly.
|
|
101
|
+
|
|
87
102
|
## DNS cutover
|
|
88
103
|
|
|
89
104
|
Point the site domain at the `DistributionDomainName` stack output:
|
|
@@ -100,12 +115,12 @@ curl -sI "$SITE/" -A "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/126.0" |
|
|
|
100
115
|
|
|
101
116
|
# 2. Platform-agent UA — page still served (observe never blocks)…
|
|
102
117
|
curl -s "$SITE/" -A "Claude-User/1.0 (+https://anthropic.com)" -o /dev/null -w '%{http_code}\n'
|
|
103
|
-
# …and within ~1 min the visit appears in the
|
|
118
|
+
# …and within ~1 min the visit appears in the Visit Intelligence live agent feed /
|
|
104
119
|
# /admin/auto-provisioned (orphan "Auto-provisioned Anthropic Claude").
|
|
105
120
|
|
|
106
121
|
# 3. X-Astra credentialed agent — verify-access round trip:
|
|
107
122
|
curl -s "$SITE/" -H "X-Astra-Id: ASTRA-<test agent>" -H "X-Api-Key: <agent key>" -o /dev/null -w '%{http_code}\n'
|
|
108
|
-
# Check the verification appears in the
|
|
123
|
+
# Check the verification appears in the Visit Intelligence live agent feed.
|
|
109
124
|
|
|
110
125
|
# 4. Static asset — untouched, no events:
|
|
111
126
|
curl -sI "$SITE/favicon.ico" -o /dev/null -w '%{http_code}\n'
|
|
@@ -124,7 +139,7 @@ should be CloudFront-hop only.
|
|
|
124
139
|
`[OBSERVE] would have been DENY: …` and stamped on origin requests as
|
|
125
140
|
`X-AstraSync-Observed-Decision` (visible in origin/nginx logs).
|
|
126
141
|
|
|
127
|
-
## Enforce mode (later —
|
|
142
|
+
## Enforce mode (later — ships separately)
|
|
128
143
|
|
|
129
144
|
Enforcement is enabled from the dashboard (endpoint → Edge verification →
|
|
130
145
|
mode: enforce; explicit confirmation + warning alert), never from infra.
|
package/infra/deploy.sh
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
#
|
|
2
|
+
# Trusted Agent Gateway (CloudFront adapter) — build + deploy (devops-run, NOT CI).
|
|
3
3
|
# Prereqs: aws cli + sam cli authenticated against the AstraSync AWS account,
|
|
4
4
|
# us-east-1; the ACM cert and Secrets Manager secret already created
|
|
5
5
|
# (see RUNBOOK.md).
|
package/infra/template.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
AWSTemplateFormatVersion: '2010-09-09'
|
|
2
2
|
Transform: AWS::Serverless-2016-10-31
|
|
3
3
|
Description: >
|
|
4
|
-
AstraSync
|
|
4
|
+
AstraSync Trusted Agent Gateway — CloudFront/Lambda@Edge adapter —
|
|
5
5
|
Lambda@Edge origin-request shell + CloudFront distribution in front of an
|
|
6
6
|
existing origin (first install: staging.astrasync.ai; prod rollover after
|
|
7
7
|
a stability gate; partner properties deploy from the npm package).
|
|
@@ -60,7 +60,7 @@ Resources:
|
|
|
60
60
|
Type: AWS::Serverless::Function
|
|
61
61
|
Properties:
|
|
62
62
|
FunctionName: !Sub '${AWS::StackName}-fn'
|
|
63
|
-
Description:
|
|
63
|
+
Description: Trusted Agent Gateway edge interception shell (observe-only by default)
|
|
64
64
|
# Lambda@Edge constraints: no env vars, us-east-1, versioned ARN
|
|
65
65
|
# association. AutoPublishAlias gives us EdgeFunction.Version.
|
|
66
66
|
Runtime: nodejs20.x
|
|
@@ -71,13 +71,16 @@ Resources:
|
|
|
71
71
|
AutoPublishAlias: live
|
|
72
72
|
Role: !GetAtt EdgeFunctionRole.Arn
|
|
73
73
|
|
|
74
|
-
#
|
|
75
|
-
# (IP / ASN /
|
|
76
|
-
#
|
|
74
|
+
# Maximal metadata capture — capture the CloudFront-Viewer-* connection signals
|
|
75
|
+
# (IP / ASN / geo / TLS version / HTTP version / device class / JA3+JA4
|
|
76
|
+
# client fingerprints / header-structure fingerprint). The managed
|
|
77
|
+
# AllViewerExceptHostHeader policy does NOT add these; only a policy
|
|
77
78
|
# with `allViewerAndWhitelistCloudFront` does — and that also forwards the
|
|
78
79
|
# viewer Host. The edge Lambda restores origin-domain Host + X-Forwarded-Host
|
|
79
80
|
# itself (see cf.ts:normalizeOriginHost), so vhost / PaaS origin routing is
|
|
80
|
-
# unaffected.
|
|
81
|
+
# unaffected. JA3/JA4 are populated only for HTTPS viewer connections (moot
|
|
82
|
+
# here: ViewerProtocolPolicy is redirect-to-https) and are deliverable ONLY
|
|
83
|
+
# via an origin request policy, never a cache policy.
|
|
81
84
|
EdgeOriginRequestPolicy:
|
|
82
85
|
Type: AWS::CloudFront::OriginRequestPolicy
|
|
83
86
|
Properties:
|
|
@@ -92,9 +95,16 @@ Resources:
|
|
|
92
95
|
- CloudFront-Viewer-Country
|
|
93
96
|
- CloudFront-Viewer-Country-Name
|
|
94
97
|
- CloudFront-Viewer-Country-Region
|
|
98
|
+
- CloudFront-Viewer-Country-Region-Name
|
|
95
99
|
- CloudFront-Viewer-City
|
|
100
|
+
- CloudFront-Viewer-Time-Zone
|
|
101
|
+
- CloudFront-Viewer-Metro-Code
|
|
96
102
|
- CloudFront-Viewer-TLS
|
|
97
103
|
- CloudFront-Viewer-HTTP-Version
|
|
104
|
+
- CloudFront-Viewer-JA3-Fingerprint
|
|
105
|
+
- CloudFront-Viewer-JA4-Fingerprint
|
|
106
|
+
- CloudFront-Viewer-Header-Order
|
|
107
|
+
- CloudFront-Viewer-Header-Count
|
|
98
108
|
- CloudFront-Is-Desktop-Viewer
|
|
99
109
|
- CloudFront-Is-Mobile-Viewer
|
|
100
110
|
- CloudFront-Is-Tablet-Viewer
|
|
@@ -108,7 +118,7 @@ Resources:
|
|
|
108
118
|
Type: AWS::CloudFront::Distribution
|
|
109
119
|
Properties:
|
|
110
120
|
DistributionConfig:
|
|
111
|
-
Comment: !Sub 'AstraSync
|
|
121
|
+
Comment: !Sub 'AstraSync Trusted Agent Gateway edge — ${SiteDomainName}'
|
|
112
122
|
Enabled: true
|
|
113
123
|
Aliases:
|
|
114
124
|
- !Ref SiteDomainName
|
|
@@ -132,7 +142,7 @@ Resources:
|
|
|
132
142
|
# Traffic Audit is the product. Tighten per-behavior later.
|
|
133
143
|
CachePolicyId: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad # Managed-CachingDisabled
|
|
134
144
|
# Forward all viewer headers/cookies/query PLUS the CloudFront-Viewer
|
|
135
|
-
# connection signals (
|
|
145
|
+
# connection signals (maximal metadata capture). This policy forwards the viewer
|
|
136
146
|
# Host too; the edge Lambda rewrites Host to the origin domain and
|
|
137
147
|
# preserves the public host in X-Forwarded-Host, so vhost / PaaS
|
|
138
148
|
# routing still receives the hostname it serves.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astrasyncai/adapter-lambda",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "AstraSync
|
|
3
|
+
"version": "2.2.0",
|
|
4
|
+
"description": "AstraSync Trusted Agent Gateway — Lambda@Edge/CloudFront adapter",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -24,11 +24,13 @@
|
|
|
24
24
|
"test": "vitest run",
|
|
25
25
|
"test:watch": "vitest watch",
|
|
26
26
|
"clean": "rm -rf dist",
|
|
27
|
-
"
|
|
27
|
+
"check:dts-leaks": "node scripts/check-dts-leaks.mjs",
|
|
28
|
+
"prepublishOnly": "npm run clean && npm run typecheck && npm run build && npm run check:dts-leaks"
|
|
28
29
|
},
|
|
29
30
|
"keywords": [
|
|
30
31
|
"astrasync",
|
|
31
|
-
"
|
|
32
|
+
"trusted-agent-gateway",
|
|
33
|
+
"agent-gateway",
|
|
32
34
|
"kya",
|
|
33
35
|
"edge",
|
|
34
36
|
"lambda-edge",
|
|
@@ -44,7 +46,7 @@
|
|
|
44
46
|
"directory": "packages/adapter-lambda"
|
|
45
47
|
},
|
|
46
48
|
"dependencies": {
|
|
47
|
-
"@astrasyncai/verification-gateway": "^4.
|
|
49
|
+
"@astrasyncai/verification-gateway": "^4.1.0",
|
|
48
50
|
"@aws-sdk/client-secrets-manager": "^3.632.0"
|
|
49
51
|
},
|
|
50
52
|
"devDependencies": {
|