@astrasyncai/adapter-lambda 2.9.0 → 2.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -169,18 +169,19 @@ declare function applyOutcome(options: EdgeAdapterOptions, request: CloudFrontRe
169
169
  /**
170
170
  * Time-budgeted event emission — CloudFront wrapper over the edge core's
171
171
  * telemetry. Lambda@Edge has no `waitUntil`, so every network call is
172
- * awaited under an explicit budget and DROPPED on timeout (the core's
173
- * default posture): latency to the customer's traffic is bounded, and a
174
- * slow AstraSync API costs telemetry, never page loads.
172
+ * awaited under an explicit budget: latency to the customer's traffic is
173
+ * bounded, and a slow AstraSync API costs telemetry time, never page loads.
175
174
  */
176
175
 
177
176
  /**
178
177
  * Emit an unregistered-attempt beacon for `identified` (non-crypto depths)
179
- * and sampled `anonymous-bot` traffic. Unauthenticated by design
180
- * (CSRF-exempt on the backend); the backend fingerprints the UA and
181
- * find-or-provisions orphan platform agents server-side.
178
+ * and sampled `anonymous-bot` traffic. 5.12.0: queued in the edge core's
179
+ * container-scope beacon buffer and delivered as `/edge-events` batches —
180
+ * a POST abandoned at the budget carries over to the next invocation
181
+ * instead of being lost. Authenticated with `apiKey` when given (verified
182
+ * attribution); without one the backend still records, flagged unverified.
182
183
  */
183
- declare function emitBeacon(options: EdgeAdapterOptions, request: CloudFrontRequest, classification: Classification, budgetMs: number): Promise<void>;
184
+ declare function emitBeacon(options: EdgeAdapterOptions, request: CloudFrontRequest, classification: Classification, budgetMs: number, apiKey?: string): Promise<void>;
184
185
 
185
186
  /**
186
187
  * Runtime configuration for the edge shell.
package/dist/index.d.ts CHANGED
@@ -169,18 +169,19 @@ declare function applyOutcome(options: EdgeAdapterOptions, request: CloudFrontRe
169
169
  /**
170
170
  * Time-budgeted event emission — CloudFront wrapper over the edge core's
171
171
  * telemetry. Lambda@Edge has no `waitUntil`, so every network call is
172
- * awaited under an explicit budget and DROPPED on timeout (the core's
173
- * default posture): latency to the customer's traffic is bounded, and a
174
- * slow AstraSync API costs telemetry, never page loads.
172
+ * awaited under an explicit budget: latency to the customer's traffic is
173
+ * bounded, and a slow AstraSync API costs telemetry time, never page loads.
175
174
  */
176
175
 
177
176
  /**
178
177
  * Emit an unregistered-attempt beacon for `identified` (non-crypto depths)
179
- * and sampled `anonymous-bot` traffic. Unauthenticated by design
180
- * (CSRF-exempt on the backend); the backend fingerprints the UA and
181
- * find-or-provisions orphan platform agents server-side.
178
+ * and sampled `anonymous-bot` traffic. 5.12.0: queued in the edge core's
179
+ * container-scope beacon buffer and delivered as `/edge-events` batches —
180
+ * a POST abandoned at the budget carries over to the next invocation
181
+ * instead of being lost. Authenticated with `apiKey` when given (verified
182
+ * attribution); without one the backend still records, flagged unverified.
182
183
  */
183
- declare function emitBeacon(options: EdgeAdapterOptions, request: CloudFrontRequest, classification: Classification, budgetMs: number): Promise<void>;
184
+ declare function emitBeacon(options: EdgeAdapterOptions, request: CloudFrontRequest, classification: Classification, budgetMs: number, apiKey?: string): Promise<void>;
184
185
 
185
186
  /**
186
187
  * Runtime configuration for the edge shell.
package/dist/index.js CHANGED
@@ -89,17 +89,28 @@ function decodeBody(request) {
89
89
  const text = body.encoding === "base64" ? Buffer.from(body.data, "base64").toString("utf8") : body.data;
90
90
  return { text, truncated: body.inputTruncated === true };
91
91
  }
92
+ function parseViewerAddress(addr) {
93
+ const raw = addr?.trim();
94
+ if (!raw) return void 0;
95
+ const bracketed = /^\[([^\]]+)\](?::(\d+))?$/.exec(raw);
96
+ if (bracketed) {
97
+ return { ip: bracketed[1], ...bracketed[2] && { port: bracketed[2] } };
98
+ }
99
+ const portMatch = /:(\d+)$/.exec(raw);
100
+ const ip = portMatch ? raw.slice(0, -portMatch[0].length) : raw;
101
+ if (!ip) return void 0;
102
+ return { ip, ...portMatch && { port: portMatch[1] } };
103
+ }
92
104
  function collectConnectionMetadata(request) {
93
105
  const out = {};
94
106
  const put = (key, headerName, transform) => {
95
107
  const v = getHeader(request, headerName);
96
108
  if (v) out[key] = transform ? transform(v) : v;
97
109
  };
98
- const addr = getHeader(request, "cloudfront-viewer-address");
99
- if (addr) {
100
- out.ip = addr.replace(/:\d+$/, "");
101
- const portMatch = addr.match(/:(\d+)$/);
102
- if (portMatch) out.sourcePort = portMatch[1];
110
+ const viewer = parseViewerAddress(getHeader(request, "cloudfront-viewer-address"));
111
+ if (viewer) {
112
+ out.ip = viewer.ip;
113
+ if (viewer.port) out.sourcePort = viewer.port;
103
114
  }
104
115
  put("country", "cloudfront-viewer-country");
105
116
  put("countryName", "cloudfront-viewer-country-name");
@@ -137,7 +148,7 @@ function collectConnectionMetadata(request) {
137
148
  function toNormalizedRequest(request) {
138
149
  const body = decodeBody(request);
139
150
  const connection = collectConnectionMetadata(request);
140
- const clientIp = getHeader(request, "cloudfront-viewer-address")?.split(":")[0];
151
+ const clientIp = parseViewerAddress(getHeader(request, "cloudfront-viewer-address"))?.ip;
141
152
  return {
142
153
  method: request.method,
143
154
  uri: request.uri,
@@ -253,7 +264,8 @@ function createEdgeHandler(options) {
253
264
  const apiKey = await resolveApiKey({ ...options, log });
254
265
  const config = await loadEdgeConfig(options, apiKey);
255
266
  const { application } = await (0, import_edge_core2.evaluateEdgeRequest)(
256
- { ...options, log },
267
+ // Beacon batch meta names the adapter for delivery monitoring.
268
+ { ...options, adapter: "lambda-edge", log },
257
269
  toNormalizedRequest(request),
258
270
  config,
259
271
  apiKey
@@ -292,7 +304,8 @@ function detectCommerce(request, host, body) {
292
304
  var import_edge_core5 = require("@astrasyncai/verification-gateway/edge-core");
293
305
  async function runTier(options, request, classification, policy, apiKey, capture) {
294
306
  return (0, import_edge_core5.runTier)(
295
- options,
307
+ // Beacon batch meta names the adapter for delivery monitoring.
308
+ { ...options, adapter: "lambda-edge" },
296
309
  toNormalizedRequest(request),
297
310
  classification,
298
311
  policy,
@@ -312,8 +325,15 @@ var import_edge_core7 = require("@astrasyncai/verification-gateway/edge-core");
312
325
 
313
326
  // src/events.ts
314
327
  var import_edge_core8 = require("@astrasyncai/verification-gateway/edge-core");
315
- async function emitBeacon(options, request, classification, budgetMs) {
316
- await (0, import_edge_core8.emitBeacon)(options, toNormalizedRequest(request), classification, budgetMs);
328
+ async function emitBeacon(options, request, classification, budgetMs, apiKey) {
329
+ await (0, import_edge_core8.emitBeacon)(
330
+ { ...options, adapter: "lambda-edge" },
331
+ toNormalizedRequest(request),
332
+ classification,
333
+ budgetMs,
334
+ void 0,
335
+ apiKey
336
+ );
317
337
  }
318
338
  // Annotate the CommonJS export names for ESM import in node:
319
339
  0 && (module.exports = {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../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 * @astrasyncai/adapter-lambda — AstraSync Trusted Agent Gateway,\n * CloudFront/Lambda@Edge adapter.\n *\n * Library surface: `createEdgeHandler(options)` builds a Lambda@Edge\n * origin-request handler. The deployable single-file bundle lives at\n * `dist/lambda/index.js` (see `src/lambda-entry.ts` + `infra/`).\n */\n\nexport { createEdgeHandler, type EdgeHandler } from './handler';\nexport { classify, resolvePolicy } from './classify';\nexport { detectCommerce, type DetectedCommerce } from './detect';\nexport { runTier } from './pipeline';\nexport { applyOutcome, ASAT_HEADERS } from './enforce';\nexport { buildDenyBody, buildManualReviewBody } from './messaging';\nexport { emitBeacon, withBudget, sampled } from './events';\nexport { resolveApiKey, loadEdgeConfig, _resetConfigCaches } from './config';\nexport {\n getRequest,\n getHeader,\n flattenHeaders,\n decodeBody,\n requestUrl,\n setHeader,\n stripHeadersByPrefix,\n jsonResponse,\n} from './cf';\nexport type {\n EdgeAdapterOptions,\n Classification,\n TrafficTier,\n TierOutcome,\n EdgeDecision,\n EffectivePolicy,\n} from './types';\n","/**\n * 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. The port is\n // kept as its own key (2.3.0): ephemeral-port patterns signal NAT/proxy\n // connection reuse.\n const addr = getHeader(request, 'cloudfront-viewer-address');\n if (addr) {\n out.ip = addr.replace(/:\\d+$/, '');\n const portMatch = addr.match(/:(\\d+)$/);\n if (portMatch) out.sourcePort = portMatch[1];\n }\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('latitude', 'cloudfront-viewer-latitude');\n put('longitude', 'cloudfront-viewer-longitude');\n put('postalCode', 'cloudfront-viewer-postal-code');\n put('asn', 'cloudfront-viewer-asn');\n put('tlsVersion', 'cloudfront-viewer-tls');\n // The cipher token is folded into the viewer-tls string\n // (`TLSv1.3:TLS_AES_128_GCM_SHA256:fullHandshake`). tlsVersion stays\n // verbatim (frozen capture semantics); the cipher is pulled out additively\n // so it is queryable on its own.\n {\n const cipher = getHeader(request, 'cloudfront-viewer-tls')?.split(':')[1];\n if (cipher) out.tlsCipher = cipher;\n }\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 capture: {\n sampling: { anonymousBeaconRate: number };\n /** VI uplift Part 4 — owner-configured query-VALUE capture (utm_*). */\n queryValueAllowlist?: string[];\n }\n): Promise<TierOutcome> {\n return runTierCore(\n options,\n toNormalizedRequest(request),\n classification,\n policy,\n apiKey,\n capture\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":["import_edge_core","import_edge_core","classifyCore","import_edge_core","detectCommerceCore","import_edge_core","runTierCore","import_edge_core","resolveOutcomeApplication","import_edge_core","import_edge_core","emitBeaconCore"]}
1
+ {"version":3,"sources":["../src/index.ts","../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 * @astrasyncai/adapter-lambda — AstraSync Trusted Agent Gateway,\n * CloudFront/Lambda@Edge adapter.\n *\n * Library surface: `createEdgeHandler(options)` builds a Lambda@Edge\n * origin-request handler. The deployable single-file bundle lives at\n * `dist/lambda/index.js` (see `src/lambda-entry.ts` + `infra/`).\n */\n\nexport { createEdgeHandler, type EdgeHandler } from './handler';\nexport { classify, resolvePolicy } from './classify';\nexport { detectCommerce, type DetectedCommerce } from './detect';\nexport { runTier } from './pipeline';\nexport { applyOutcome, ASAT_HEADERS } from './enforce';\nexport { buildDenyBody, buildManualReviewBody } from './messaging';\nexport { emitBeacon, withBudget, sampled } from './events';\nexport { resolveApiKey, loadEdgeConfig, _resetConfigCaches } from './config';\nexport {\n getRequest,\n getHeader,\n flattenHeaders,\n decodeBody,\n requestUrl,\n setHeader,\n stripHeadersByPrefix,\n jsonResponse,\n} from './cf';\nexport type {\n EdgeAdapterOptions,\n Classification,\n TrafficTier,\n TierOutcome,\n EdgeDecision,\n EffectivePolicy,\n} from './types';\n","/**\n * 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 // Beacon batch meta names the adapter for delivery monitoring.\n { ...options, adapter: 'lambda-edge', 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 * Split CloudFront's `cloudfront-viewer-address` (`<ip>:<port>`) into its\n * parts. CloudFront always appends the port, so the trailing `:<digits>` is\n * the port for IPv4 (`203.0.113.7:443`) and unbracketed IPv6\n * (`2001:db8::1:443`); a bracketed IPv6 (`[2001:db8::1]:443` or\n * `[2001:db8::1]`) has its brackets stripped. Shared by the connection\n * metadata and `clientIp` so both carry the same address (5.12.0 fix: the\n * old `clientIp` took everything before the FIRST `:`, so every IPv6 viewer\n * was reported as its first hextet, e.g. `2001`).\n */\nexport function parseViewerAddress(\n addr: string | undefined\n): { ip: string; port?: string } | undefined {\n const raw = addr?.trim();\n if (!raw) return undefined;\n const bracketed = /^\\[([^\\]]+)\\](?::(\\d+))?$/.exec(raw);\n if (bracketed) {\n return { ip: bracketed[1], ...(bracketed[2] && { port: bracketed[2] }) };\n }\n const portMatch = /:(\\d+)$/.exec(raw);\n const ip = portMatch ? raw.slice(0, -portMatch[0].length) : raw;\n if (!ip) return undefined;\n return { ip, ...(portMatch && { port: portMatch[1] }) };\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`. The port is kept as its own key\n // (2.3.0): ephemeral-port patterns signal NAT/proxy connection reuse.\n const viewer = parseViewerAddress(getHeader(request, 'cloudfront-viewer-address'));\n if (viewer) {\n out.ip = viewer.ip;\n if (viewer.port) out.sourcePort = viewer.port;\n }\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('latitude', 'cloudfront-viewer-latitude');\n put('longitude', 'cloudfront-viewer-longitude');\n put('postalCode', 'cloudfront-viewer-postal-code');\n put('asn', 'cloudfront-viewer-asn');\n put('tlsVersion', 'cloudfront-viewer-tls');\n // The cipher token is folded into the viewer-tls string\n // (`TLSv1.3:TLS_AES_128_GCM_SHA256:fullHandshake`). tlsVersion stays\n // verbatim (frozen capture semantics); the cipher is pulled out additively\n // so it is queryable on its own.\n {\n const cipher = getHeader(request, 'cloudfront-viewer-tls')?.split(':')[1];\n if (cipher) out.tlsCipher = cipher;\n }\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` is the viewer address without its\n * port — the same parse as the connection metadata (IPv4 unchanged from\n * earlier releases; IPv6 now the full address instead of its first hextet).\n */\nexport function toNormalizedRequest(request: CloudFrontRequest): NormalizedRequest {\n const body = decodeBody(request);\n const connection = collectConnectionMetadata(request);\n const clientIp = parseViewerAddress(getHeader(request, 'cloudfront-viewer-address'))?.ip;\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 capture: {\n sampling: { anonymousBeaconRate: number };\n /** VI uplift Part 4 — owner-configured query-VALUE capture (utm_*). */\n queryValueAllowlist?: string[];\n }\n): Promise<TierOutcome> {\n return runTierCore(\n // Beacon batch meta names the adapter for delivery monitoring.\n { ...options, adapter: 'lambda-edge' },\n toNormalizedRequest(request),\n classification,\n policy,\n apiKey,\n capture\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: latency to the customer's traffic is\n * bounded, and a slow AstraSync API costs telemetry time, 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. 5.12.0: queued in the edge core's\n * container-scope beacon buffer and delivered as `/edge-events` batches —\n * a POST abandoned at the budget carries over to the next invocation\n * instead of being lost. Authenticated with `apiKey` when given (verified\n * attribution); without one the backend still records, flagged unverified.\n */\nexport async function emitBeacon(\n options: EdgeAdapterOptions,\n request: CloudFrontRequest,\n classification: Classification,\n budgetMs: number,\n apiKey?: string\n): Promise<void> {\n await emitBeaconCore(\n { ...options, adapter: 'lambda-edge' },\n toNormalizedRequest(request),\n classification,\n budgetMs,\n undefined,\n apiKey\n );\n}\n"],"mappings":"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","names":["import_edge_core","import_edge_core","classifyCore","import_edge_core","detectCommerceCore","import_edge_core","runTierCore","import_edge_core","resolveOutcomeApplication","import_edge_core","import_edge_core","emitBeaconCore"]}
package/dist/index.mjs CHANGED
@@ -31,17 +31,28 @@ function decodeBody(request) {
31
31
  const text = body.encoding === "base64" ? Buffer.from(body.data, "base64").toString("utf8") : body.data;
32
32
  return { text, truncated: body.inputTruncated === true };
33
33
  }
34
+ function parseViewerAddress(addr) {
35
+ const raw = addr?.trim();
36
+ if (!raw) return void 0;
37
+ const bracketed = /^\[([^\]]+)\](?::(\d+))?$/.exec(raw);
38
+ if (bracketed) {
39
+ return { ip: bracketed[1], ...bracketed[2] && { port: bracketed[2] } };
40
+ }
41
+ const portMatch = /:(\d+)$/.exec(raw);
42
+ const ip = portMatch ? raw.slice(0, -portMatch[0].length) : raw;
43
+ if (!ip) return void 0;
44
+ return { ip, ...portMatch && { port: portMatch[1] } };
45
+ }
34
46
  function collectConnectionMetadata(request) {
35
47
  const out = {};
36
48
  const put = (key, headerName, transform) => {
37
49
  const v = getHeader(request, headerName);
38
50
  if (v) out[key] = transform ? transform(v) : v;
39
51
  };
40
- const addr = getHeader(request, "cloudfront-viewer-address");
41
- if (addr) {
42
- out.ip = addr.replace(/:\d+$/, "");
43
- const portMatch = addr.match(/:(\d+)$/);
44
- if (portMatch) out.sourcePort = portMatch[1];
52
+ const viewer = parseViewerAddress(getHeader(request, "cloudfront-viewer-address"));
53
+ if (viewer) {
54
+ out.ip = viewer.ip;
55
+ if (viewer.port) out.sourcePort = viewer.port;
45
56
  }
46
57
  put("country", "cloudfront-viewer-country");
47
58
  put("countryName", "cloudfront-viewer-country-name");
@@ -79,7 +90,7 @@ function collectConnectionMetadata(request) {
79
90
  function toNormalizedRequest(request) {
80
91
  const body = decodeBody(request);
81
92
  const connection = collectConnectionMetadata(request);
82
- const clientIp = getHeader(request, "cloudfront-viewer-address")?.split(":")[0];
93
+ const clientIp = parseViewerAddress(getHeader(request, "cloudfront-viewer-address"))?.ip;
83
94
  return {
84
95
  method: request.method,
85
96
  uri: request.uri,
@@ -198,7 +209,8 @@ function createEdgeHandler(options) {
198
209
  const apiKey = await resolveApiKey({ ...options, log });
199
210
  const config = await loadEdgeConfig(options, apiKey);
200
211
  const { application } = await evaluateEdgeRequest(
201
- { ...options, log },
212
+ // Beacon batch meta names the adapter for delivery monitoring.
213
+ { ...options, adapter: "lambda-edge", log },
202
214
  toNormalizedRequest(request),
203
215
  config,
204
216
  apiKey
@@ -243,7 +255,8 @@ function detectCommerce(request, host, body) {
243
255
  import { runTier as runTierCore } from "@astrasyncai/verification-gateway/edge-core";
244
256
  async function runTier(options, request, classification, policy, apiKey, capture) {
245
257
  return runTierCore(
246
- options,
258
+ // Beacon batch meta names the adapter for delivery monitoring.
259
+ { ...options, adapter: "lambda-edge" },
247
260
  toNormalizedRequest(request),
248
261
  classification,
249
262
  policy,
@@ -273,8 +286,15 @@ import {
273
286
  withBudget,
274
287
  sampled
275
288
  } from "@astrasyncai/verification-gateway/edge-core";
276
- async function emitBeacon(options, request, classification, budgetMs) {
277
- await emitBeaconCore(options, toNormalizedRequest(request), classification, budgetMs);
289
+ async function emitBeacon(options, request, classification, budgetMs, apiKey) {
290
+ await emitBeaconCore(
291
+ { ...options, adapter: "lambda-edge" },
292
+ toNormalizedRequest(request),
293
+ classification,
294
+ budgetMs,
295
+ void 0,
296
+ apiKey
297
+ );
278
298
  }
279
299
  export {
280
300
  ASAT_HEADERS,
@@ -1 +1 @@
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. The port is\n // kept as its own key (2.3.0): ephemeral-port patterns signal NAT/proxy\n // connection reuse.\n const addr = getHeader(request, 'cloudfront-viewer-address');\n if (addr) {\n out.ip = addr.replace(/:\\d+$/, '');\n const portMatch = addr.match(/:(\\d+)$/);\n if (portMatch) out.sourcePort = portMatch[1];\n }\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('latitude', 'cloudfront-viewer-latitude');\n put('longitude', 'cloudfront-viewer-longitude');\n put('postalCode', 'cloudfront-viewer-postal-code');\n put('asn', 'cloudfront-viewer-asn');\n put('tlsVersion', 'cloudfront-viewer-tls');\n // The cipher token is folded into the viewer-tls string\n // (`TLSv1.3:TLS_AES_128_GCM_SHA256:fullHandshake`). tlsVersion stays\n // verbatim (frozen capture semantics); the cipher is pulled out additively\n // so it is queryable on its own.\n {\n const cipher = getHeader(request, 'cloudfront-viewer-tls')?.split(':')[1];\n if (cipher) out.tlsCipher = cipher;\n }\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 capture: {\n sampling: { anonymousBeaconRate: number };\n /** VI uplift Part 4 — owner-configured query-VALUE capture (utm_*). */\n queryValueAllowlist?: string[];\n }\n): Promise<TierOutcome> {\n return runTierCore(\n options,\n toNormalizedRequest(request),\n classification,\n policy,\n apiKey,\n capture\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":[]}
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 // Beacon batch meta names the adapter for delivery monitoring.\n { ...options, adapter: 'lambda-edge', 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 * Split CloudFront's `cloudfront-viewer-address` (`<ip>:<port>`) into its\n * parts. CloudFront always appends the port, so the trailing `:<digits>` is\n * the port for IPv4 (`203.0.113.7:443`) and unbracketed IPv6\n * (`2001:db8::1:443`); a bracketed IPv6 (`[2001:db8::1]:443` or\n * `[2001:db8::1]`) has its brackets stripped. Shared by the connection\n * metadata and `clientIp` so both carry the same address (5.12.0 fix: the\n * old `clientIp` took everything before the FIRST `:`, so every IPv6 viewer\n * was reported as its first hextet, e.g. `2001`).\n */\nexport function parseViewerAddress(\n addr: string | undefined\n): { ip: string; port?: string } | undefined {\n const raw = addr?.trim();\n if (!raw) return undefined;\n const bracketed = /^\\[([^\\]]+)\\](?::(\\d+))?$/.exec(raw);\n if (bracketed) {\n return { ip: bracketed[1], ...(bracketed[2] && { port: bracketed[2] }) };\n }\n const portMatch = /:(\\d+)$/.exec(raw);\n const ip = portMatch ? raw.slice(0, -portMatch[0].length) : raw;\n if (!ip) return undefined;\n return { ip, ...(portMatch && { port: portMatch[1] }) };\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`. The port is kept as its own key\n // (2.3.0): ephemeral-port patterns signal NAT/proxy connection reuse.\n const viewer = parseViewerAddress(getHeader(request, 'cloudfront-viewer-address'));\n if (viewer) {\n out.ip = viewer.ip;\n if (viewer.port) out.sourcePort = viewer.port;\n }\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('latitude', 'cloudfront-viewer-latitude');\n put('longitude', 'cloudfront-viewer-longitude');\n put('postalCode', 'cloudfront-viewer-postal-code');\n put('asn', 'cloudfront-viewer-asn');\n put('tlsVersion', 'cloudfront-viewer-tls');\n // The cipher token is folded into the viewer-tls string\n // (`TLSv1.3:TLS_AES_128_GCM_SHA256:fullHandshake`). tlsVersion stays\n // verbatim (frozen capture semantics); the cipher is pulled out additively\n // so it is queryable on its own.\n {\n const cipher = getHeader(request, 'cloudfront-viewer-tls')?.split(':')[1];\n if (cipher) out.tlsCipher = cipher;\n }\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` is the viewer address without its\n * port — the same parse as the connection metadata (IPv4 unchanged from\n * earlier releases; IPv6 now the full address instead of its first hextet).\n */\nexport function toNormalizedRequest(request: CloudFrontRequest): NormalizedRequest {\n const body = decodeBody(request);\n const connection = collectConnectionMetadata(request);\n const clientIp = parseViewerAddress(getHeader(request, 'cloudfront-viewer-address'))?.ip;\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 capture: {\n sampling: { anonymousBeaconRate: number };\n /** VI uplift Part 4 — owner-configured query-VALUE capture (utm_*). */\n queryValueAllowlist?: string[];\n }\n): Promise<TierOutcome> {\n return runTierCore(\n // Beacon batch meta names the adapter for delivery monitoring.\n { ...options, adapter: 'lambda-edge' },\n toNormalizedRequest(request),\n classification,\n policy,\n apiKey,\n capture\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: latency to the customer's traffic is\n * bounded, and a slow AstraSync API costs telemetry time, 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. 5.12.0: queued in the edge core's\n * container-scope beacon buffer and delivered as `/edge-events` batches —\n * a POST abandoned at the budget carries over to the next invocation\n * instead of being lost. Authenticated with `apiKey` when given (verified\n * attribution); without one the backend still records, flagged unverified.\n */\nexport async function emitBeacon(\n options: EdgeAdapterOptions,\n request: CloudFrontRequest,\n classification: Classification,\n budgetMs: number,\n apiKey?: string\n): Promise<void> {\n await emitBeaconCore(\n { ...options, adapter: 'lambda-edge' },\n toNormalizedRequest(request),\n classification,\n budgetMs,\n undefined,\n apiKey\n );\n}\n"],"mappings":"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","names":[]}
@@ -1 +1 @@
1
- "use strict";var Ee=Object.create;var R=Object.defineProperty;var Te=Object.getOwnPropertyDescriptor;var Ue=Object.getOwnPropertyNames;var _e=Object.getPrototypeOf,Pe=Object.prototype.hasOwnProperty;var xe=(e,t)=>{for(var r in t)R(e,r,{get:t[r],enumerable:!0})},B=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of Ue(t))!Pe.call(e,a)&&a!==r&&R(e,a,{get:()=>t[a],enumerable:!(n=Te(t,a))||n.enumerable});return e};var ke=(e,t,r)=>(r=e!=null?Ee(_e(e)):{},B(t||!e||!e.__esModule?R(r,"default",{value:e,enumerable:!0}):r,e)),Oe=e=>B(R({},"__esModule",{value:!0}),e);var dr={};xe(dr,{handler:()=>cr});module.exports=Oe(dr);var Z="5.10.0",S=`astrasync-sdk/${Z}`;function Me(e){if(!e)return{"user-agent":S};if(e instanceof Headers){let r=new Headers(e);return r.has("user-agent")||r.set("user-agent",S),r}return Array.isArray(e)?e.some(([n])=>n.toLowerCase()==="user-agent")?e:[...e,["user-agent",S]]:Object.keys(e).some(r=>r.toLowerCase()==="user-agent")?e:{"user-agent":S,...e}}var C=(e,t)=>fetch(e,{...t,headers:Me(t?.headers)}),lr=Object.freeze({version:1,mode:"observe",depth:"classify",pathRules:[],sampling:Object.freeze({anonymousBeaconRate:1}),failurePosture:"open"}),pr=60*1e3,fr=1440*60*1e3;function De(e,t){for(let r of e)if(Fe(r.pattern).test(t))return r}function Fe(e){let t=e.replace(/[.+?^${}()|[\]\\]/g,"\\$&").replace(/\*/g,".*");return new RegExp(`^${t}$`,"i")}var Ne=1,Be=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"]),Le=["x-astrasync-","kya-","kya_"],He=new Set(["authorization","x-api-key","x-goog-api-key","api-key"]);function $e(e){return e==="signature"||e.endsWith("-signature")||e.endsWith("-signature-256")}var je=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"]),Ke=["x-goog-","x-stainless-","x-anthropic-","x-openai-","sec-ch-"],Ve=["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-"],Ge=64,L=1024,ze=16*1024,Xe=64,Ye=/^[a-z0-9][a-z0-9 ._/-]{0,63}$/;function ee(e){let t=e?.trim().slice(0,Xe).toLowerCase();return t&&Ye.test(t)?t:void 0}function P(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 Je(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 Ve)if(n.startsWith(a))return a}function We(e){return!!(Be.has(e)||Le.some(t=>e.startsWith(t))||$e(e))}function Qe(e){return je.has(e)?!0:Ke.some(t=>e.startsWith(t))}function te(e){let t={},r={},n,a,i=0,o=0;if(!e)return{headers:t};for(let c of Object.keys(e)){if(o>=Ge)break;let f=c.toLowerCase(),m=e[c];if(m===void 0)continue;let h=Array.isArray(m)?m.join(", "):String(m);if(He.has(f)){let g=Je(h);g&&!n&&(n=g);continue}if(f==="cookie"&&(a=h.split(";").filter(g=>g.trim().length>0).length),We(f))continue;let u=h;P(u)>L&&(u=u.slice(0,L));let d=P(f)+P(u);i+d>ze||(i+=d,o++,t[f]=u,Qe(f)&&(r[f]=u))}let s={headers:t};return n&&(s.apiKeyFormat=n),Object.keys(r).length>0&&(s.platformHeaders=r),a!==void 0&&(s.cookieCount=a),s}var qe=[{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,/\bGoogle-Extended\b/i,/\bGoogleOther\b/i,/\bGoogle-CloudVertexBot\b/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]},{vendor:"chatgpt-atlas",displayName:"ChatGPT Atlas browser",uaPatterns:[/\batlas\/\d/i],headerPatterns:[/^sec-ch-ua: .*atlas/i]},{vendor:"perplexity-comet",displayName:"Perplexity Comet browser",uaPatterns:[/\bcomet\/\d/i],headerPatterns:[/^sec-ch-ua: .*comet/i]},{vendor:"astrasync-bridge",displayName:"AstraSync MCP bridge (buyer-agent catalog fetch)",uaPatterns:[/^astrasync-bridge\//i]},{vendor:"astrasync-sdk",displayName:"AstraSync SDK",uaPatterns:[/^astrasync-sdk\//i]}];function Ze(e,t){let r=t.userAgent??"",n=t.agentCardUrl??"",a=t.apiKeyFormat??"",i=t.ja4??"",o=t.asn??"",s=[];if(t.platformHeaders)for(let[c,f]of Object.entries(t.platformHeaders))s.push(c,`${c}: ${f}`);for(let c of e){let f=c.uaPatterns?.some(l=>l.test(r)),m=c.agentCardPatterns?.some(l=>l.test(n)),h=a.length>0&&c.apiKeyFormatPatterns?.some(l=>l.test(a)),u=s.length>0&&c.headerPatterns?.some(l=>s.some(y=>l.test(y))),d=i.length>0&&c.ja4Patterns?.some(l=>l.test(i)),g=o.length>0&&c.asnList?.includes(o);if(f||m||h||u||d||g){let l=r.split(/[\s/]/)[0]?.toLowerCase()??"",y=(()=>{try{return n?new URL(n).host:""}catch{return""}})(),_=l||y||a||i||o&&`asn${o}`||"unknown";return{vendor:c.vendor,fingerprintKey:`${c.vendor}:${_}`,displayName:c.displayName}}}}function H(e){return Ze(qe,e)}function et(e){return e>=80?"PLATINUM":e>=60?"GOLD":e>=40?"SILVER":"BRONZE"}var gr=3600*1e3,tt=new Map;function rt(e){return tt.get(e)?.data}var re={apiBaseUrl:"https://astrasync.ai/api",debug:!1},ne=!1,$=!1;async function j(e,t,r,n){ne=!0;try{let a=`${e}/agents/verify-access`,o=(await C(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 C(`${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 k=new Map;function ae(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||"",e.commercePhase||"",e.checkoutSessionId||""].join("|")}function nt(e,t){let r=ae(e,t),n=k.get(r);return n&&n.expiresAt>Date.now()?n.result:(n&&k.delete(r),null)}var at=60;function ot(e,t,r,n){let a=r&&r>0?r:at,i=ae(e,n);k.set(i,{result:t,expiresAt:Date.now()+a*1e3})}function oe(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 E(e){if(e?.mcpEndpoint)return{endpoint:e.mcpEndpoint,discoveryUrl:e.mcpDiscoveryUrl,message:"If you are operating inside an MCP-capable host (Claude, ChatGPT, Cursor, or any MCP client), add the AstraSync MCP connector instead of integrating these URLs by hand: registration through the connector is KEYLESS and HUMAN-APPROVED \u2014 your human approves in an in-chat card, and you never see, hold, or transmit an API key, password, or private key."}}function it(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:[...o?.mcpEndpoint?[`Fastest path (inside an MCP host): add the AstraSync MCP connector at ${o.mcpEndpoint} \u2014 keyless, human-approved registration`]:[],"Register for an AstraSync account","Create and register your agent","Add your ASTRA-ID to request headers","Retry your request"],...E(o)&&{mcp:E(o)}};return{identityVerified:!1,policyAllowed:!1,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},...a&&{retryable:!0},correlationId:r.correlationId,attemptId:r.attemptId,verifiedAt:new Date}}async function st(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.commercePhase&&(a.commercePhase=n.commercePhase),n.checkoutSessionId&&(a.checkoutSessionId=n.checkoutSessionId),n.checkoutItems&&(a.checkoutItems=n.checkoutItems),n.buyerEmail&&(a.buyerEmail=n.buyerEmail),n.commerceArtifacts&&(a.commerceArtifacts=n.commerceArtifacts),n.attemptId&&(a.attemptId=n.attemptId),n.considerationSet&&(a.considerationSet=n.considerationSet),a.sdkVersion=Z,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 C(`${e.apiBaseUrl}/agents/verify-access`,{method:"POST",headers:i,body:JSON.stringify(a),signal:AbortSignal.timeout(e.verifyTimeoutMs??1e4)}),s=await o.json();if(o.status===410)return{success:!0,attemptId:typeof s?.attemptId=="string"?s.attemptId:void 0,access:{allowed:!1,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."}]}};if(o.status===400&&s?.error==="INVALID_AGENT_ID"){let c=typeof s?.message=="string"?s.message:"Agent identifier must be a canonical ASTRA-* id.";return{success:!0,attemptId:typeof s?.attemptId=="string"?s.attemptId:void 0,access:{allowed:!1,reason:"invalid_agent_id",failures:[{dimension:"identity.invalid_agent_id",message:c,guidance:typeof s?.guidance=="string"?s.guidance:"Present your canonical ASTRA-* id (discover it via GET /api/agents/request-registration/{requestId} after owner approval)."}]}}}return 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 K(e,t,r){let n={...re,...e},a=n.apiBaseUrl?rt(n.apiBaseUrl):void 0;if(!ne&&!n.disableInitChecks&&n.apiBaseUrl&&(n.strictInit?await j(n.apiBaseUrl,n.debug,!0,n.apiKey):j(n.apiBaseUrl,n.debug,!1,n.apiKey)),!$&&(e.minTrustScore!==void 0||e.minTrustScoreForFull!==void 0)&&($=!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 decisions (verified ID + runtime challenge + PDLSS + trust score). To gate access to an endpoint, configure the endpoint's trust_score_requirement server-side.")),n.cacheTtl!==0&&!r?.bypassCache){let l=nt(t,n.counterpartyId);if(l){n.debug&&console.log("[VerificationGateway] Returning cached result");let y={...l};return t.attemptId?y.attemptId=t.attemptId:delete y.attemptId,y}}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 st(n,i),s=o.denialReasons,c=Array.isArray(s)&&s.length>0?s:void 0;if(!o.success)return it(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 l=o.access?.failures;return{identityVerified:o.verificationContext?.idVerified===!0,policyAllowed:!1,denialReasons:c??(l&&l.length>0?l.map(Ce=>Ce.message):o.access?.reason?[o.access.reason]:["Access denied"]),failures:l,requiresStepUp:o.access?.requiresStepUp,requiresApproval:o.access?.requiresApproval,guidance:{message:o.access?.reason||"Access denied by PDLSS policy",registrationUrl:a?.registrationUrl??"",documentationUrl:a?.documentationUrl??"",...E(a)&&{mcp:E(a)}},verifiedAt:new Date,sessionId:o.sessionId,correlationId:o.correlationId,attemptId:o.attemptId,recommendation:o.recommendation,recommendationReasons:o.recommendationReasons,stepUpApproval:o.stepUpApproval,settlement:o.settlement,settlementOutcome:o.settlementOutcome,settlementToken:o.settlementToken,fulfilment:o.fulfilment}}let f=o.agent?{astraId:o.agent.astraId,name:o.agent.name,trustScore:o.agent.trustScore,trustLevel:et(o.agent.trustScore),blockchainAnchored:o.agent.blockchainStatus==="anchored",status:o.agent.agentStatus}:void 0,m=o.developer?{astradId:o.developer.kyaOwnerId,name:o.developer.fullName,trustScore:o.developer.trustScore||0,verified:o.developer.identityVerified}:void 0,h=o.organization?{name:o.organization.name,verified:o.organization.verified,trustScore:o.organization.trustScore}:void 0,u=o.verificationContext,d={identityVerified:o.verificationContext?.idVerified===!0,policyAllowed:!0,agent:f,developer:m,organization:h,appliedPolicy:o.access?.appliedPolicy,verificationContext:u,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,settlementOutcome:o.settlementOutcome,settlementToken:o.settlementToken,fulfilment:o.fulfilment};d.recommendation==="deny"?(d.policyAllowed=!1,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 g=d.requiresStepUp===!0||d.requiresApproval===!0||d.recommendation==="step_up_required";return n.cacheTtl!==0&&d.recommendation!=="deny"&&!g&&ot(t,d,n.cacheTtl,n.counterpartyId),d}async function ct(e,t){let r=e.apiBaseUrl||re.apiBaseUrl,{counterpartyUrl:n,...a}=t,i={...a,...n&&{counterpartyUrl:n},...e.counterpartyId&&{counterpartyId:e.counterpartyId}};await C(`${r}/verification-activity/unregistered-attempt`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)}).catch(()=>{})}function p(e,t){return e.headers[t.toLowerCase()]}function dt(e,t){let r=e.querystring?`?${e.querystring}`:"";return`https://${t}${e.uri}${r}`}var V=/^att_[0-9a-f]{32}$/;function ut(e){let t=p(e,"x-astra-attempt-id");if(t&&V.test(t))return t;if(e.querystring)try{let r=new URLSearchParams(e.querystring).get("astra_attempt");if(r&&V.test(r))return r}catch{}}function M(e,t){let{headers:r,apiKeyFormat:n,platformHeaders:a,cookieCount:i}=te(e.headers),o=e.connection??{},s=pt(e.querystring),c=yt(e.querystring,t);return{headers:r,...n&&{apiKeyFormat:n},...a&&{platformHeaders:a},...Object.keys(o).length>0&&{connection:o},...i!==void 0&&{cookieCount:i},...s&&{queryKeys:s},...c&&{queryParams:c},schemaVersion:Ne}}var lt=32,ie=64;function pt(e){if(!e)return;let t;try{t=new URLSearchParams(e)}catch{return}let r=[];for(let n of t.keys()){let a=n.toLowerCase().slice(0,ie);if(a&&!r.includes(a)&&(r.push(a),r.length>=lt))break}return r.length>0?r:void 0}var ft=16,gt=256;function yt(e,t){if(!e||!t||t.length===0)return;let r;try{r=new URLSearchParams(e)}catch{return}let n=o=>t.some(s=>s.endsWith("*")?o.startsWith(s.slice(0,-1)):o===s),a={},i=0;for(let[o,s]of r.entries()){let c=o.toLowerCase().slice(0,ie);if(!(!c||a[c]!==void 0||!n(c))&&(a[c]=s.slice(0,gt),++i>=ft))break}return i>0?a:void 0}var mt=/^eyJ[\w-]+\.[\w-]*\.[\w-]+~/;function ht(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 vt(e){try{let t=e.split(".")[0];return JSON.parse(ht(t))}catch{return}}function At(e){if(e)try{let t=JSON.parse(e);return typeof t=="object"&&t!==null?t:void 0}catch{return}}function wt(e,t){let r=dt(e,t),n=e.method,a=e.headers,i=At(e.bodyText),o=e.bodyTruncated?{bodyTruncated:!0}:{},s=p(e,"authorization"),c=s?.replace(/^Bearer\s+/i,""),m=[p(e,"x-payment-mandate")??p(e,"payment-mandate"),c].find(g=>g&&mt.test(g));if(m){let g=vt(m)?.typ;return{protocol:"vi-sd-jwt",artifacts:{viSdJwt:m},evidence:{typ:g,...o}}}let h=p(e,"signature"),u=p(e,"signature-input");if(h&&u){let g=p(e,"signature-agent"),l=p(e,"x-agent-registry"),y=u.toLowerCase();return{protocol:g?"rfc9421-web-bot-auth":l?.toLowerCase().includes("visa")||y.includes("visa")?"rfc9421-tap":l?.toLowerCase().includes("mastercard")||y.includes("mastercard")?"rfc9421-agent-pay":"rfc9421",artifacts:{rfc9421:{headers:a,method:n,url:r,...e.bodyText&&{body:e.bodyText}}},evidence:{...g&&{signatureAgent:g},...l&&{registryHint:l},keyid:u.match(/keyid="([^"]+)"/)?.[1],...o}}}let d=p(e,"timestamp");if(h&&!u&&(d||p(e,"idempotency-key")))return{protocol:"acp",artifacts:{acpRequest:{method:n,url:r,headers:a,...e.bodyText&&{rawBody:e.bodyText},...i&&{body:i}}},evidence:{hasTimestamp:!!d,...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 g=i.intent_mandate??i.intentMandate,l=i.cart_mandate??i.cartMandate,y=i.payment_mandate??i.paymentMandate;if(typeof g=="string"||typeof l=="string"||typeof y=="string")return{protocol:"ap2",artifacts:{ap2Mandates:{...typeof g=="string"&&{intent:g},...typeof l=="string"&&{cart:l},...typeof y=="string"&&{payment:y}}},evidence:{mandatesPresent:[typeof g=="string"&&"intent",typeof l=="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(p(e,"x-payment"))return{protocol:"x402",artifacts:{x402Request:{method:n,url:r,headers:a,...i&&{body:i}}},evidence:o}}var bt=/\.(css|js|mjs|map|png|jpe?g|gif|webp|avif|svg|ico|woff2?|ttf|otf|eot|mp4|webm|pdf|txt|xml)$/i,Rt=/^astrasync-sdk\//i,St=/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,It=/mozilla\/\d/i,Ct=/^kya_[A-Za-z0-9_-]+$/;function G(e){return typeof e=="string"&&Ct.test(e)}function Et(e,t){if(bt.test(t))return{mode:e.mode,depth:e.depth,skip:!0};let r=De(e.pathRules,t);return r?{mode:r.mode??e.mode,depth:r.depth??e.depth,skip:r.skip===!0,...r.sampling&&{sampling:r.sampling}}:{mode:e.mode,depth:e.depth,skip:!1}}function Tt(e,t,r){let n=p(e,"user-agent")??"";if(Rt.test(n))return{tier:"self"};let a=oe(e.headers,{});if(!a.astraId&&(G(a.jwt)||G(a.apiKey)))return{tier:"self",evidence:{firstPartyApiKey:!0}};if(a.astraId)return{tier:"verified",protocol:"x-astra"};let{apiKeyFormat:i,platformHeaders:o}=te(e.headers),s={userAgent:n,apiKeyFormat:i,platformHeaders:o};if(r!=="classify"){let f=wt(e,t);if(f)return{tier:"identified",protocol:f.protocol,artifacts:f.artifacts,evidence:f.evidence,fingerprint:H(s)}}let c=H(s);return c?{tier:"identified",protocol:"platform-ua",fingerprint:c}:!n||St.test(n)||!It.test(n)?{tier:"anonymous-bot",evidence:{userAgent:n||"(none)"}}:!p(e,"sec-fetch-mode")&&!p(e,"accept-language")?{tier:"anonymous-bot",evidence:{userAgent:n,spoofedBrowser:!0}}:{tier:"human"}}var Ut={"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 _t(e){try{let t=new URL(e).hostname.replace(/^www\./,"");if(t==="astrasync.ai"||t.endsWith(".astrasync.ai"))return`https://mcp.${t}/mcp`}catch{}}function Pt(e,t,r){let n=(r||t).replace(/\/api\/?$/,""),a=e.guidance?.mcp?.endpoint||_t(n),i=(e.protocol&&Ut[e.protocol])??"This request could not be verified by AstraSync.";return{success:!1,error:{code:e.tier==="verified"?"INSUFFICIENT_ACCESS":"UNAUTHORIZED",message:i,...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||`${n}/register`,documentationUrl:e.guidance?.documentationUrl||`${n}/docs/edge-adapter`,...a&&{mcp:{endpoint:a,discoveryUrl:e.guidance?.mcp?.discoveryUrl||`${a.replace(/\/mcp$/,"")}/.well-known/mcp.json`,message:e.guidance?.mcp?.message||"Inside an MCP host? Add the AstraSync MCP connector at this endpoint instead of the registration URL \u2014 registration through the connector is keyless and human-approved (your human approves in an in-chat card; you never handle credentials)."}}}}}}function xt(e){return{success:!1,error:{code:"VERIFICATION_UNAVAILABLE",message:"Agent verification is temporarily unavailable and this path enforces verification. Your identity was not evaluated and no denial was recorded \u2014 retry with exponential backoff.",failures:e.reasons,...e.correlationId&&{correlationId:e.correlationId},guidance:{message:"Retry the request with exponential backoff; the condition is transient."}}}}function kt(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 Ot="X-Astra-Verified-Hop";function Mt(e){return`${e.astraId};${e.sessionId??""};${e.checkedAt}`}var w={trustScore:"X-AstraSync-Trust-Score",agentId:"X-AstraSync-Agent-Id",developerId:"X-AstraSync-Developer-Id",observedDecision:"X-AstraSync-Observed-Decision"},z=["x-astrasync-","x-astra-verified-hop"];function x(){return{action:"pass",injectHeaders:{},stripHeaderPrefixes:[]}}function Dt(e,t,r){let n={};if(t.misconfig&&(n[w.observedDecision]="misconfig"),r.mode!=="enforce")return t.decision!=="allow"&&(n[w.observedDecision]=t.decision),Object.assign(n,X(t),Y(t)),{action:"pass",injectHeaders:n,stripHeaderPrefixes:z};switch(t.decision){case"deny":return t.unavailable?{action:"respond",status:503,headers:{"Retry-After":"30"},bodyJson:xt(t)}:{action:"respond",status:403,headers:{},bodyJson:Pt(t,e.apiBaseUrl,e.publicBaseUrl)};case"manual_review":return{action:"respond",status:202,headers:{"Retry-After":"30"},bodyJson:kt(t)};default:return Object.assign(n,X(t),Y(t)),{action:"pass",injectHeaders:n,stripHeaderPrefixes:z}}}function X(e){if(!e.asat)return{};let{trustScore:t,agentId:r,developerId:n}=e.asat;return{...t!==void 0&&{[w.trustScore]:String(t)},...r&&{[w.agentId]:r},...n&&{[w.developerId]:n}}}function Y(e){let t=e.asat?.agentId;return t?{[Ot]:Mt({astraId:t,...e.sessionId&&{sessionId:e.sessionId},checkedAt:Date.now()})}:{}}var Ft={GET:"data.read",HEAD:"data.read",OPTIONS:"data.read",POST:"data.write",PUT:"data.write",PATCH:"data.write",DELETE:"data.delete"},Nt="data.write",Bt="data";function Lt(e){return Ft[e.toUpperCase()]??Nt}function Ht(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 $t(e){let t=e.astraPurpose?Ht(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=Lt(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=Bt,i="transport_default")}return{purpose:a,action:r,purposeSource:i,actionSource:n}}async function O(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 J(e,t,r,n,a){let i=ut(t),o=ee(p(t,"x-astra-source")),s=O(ct({apiBaseUrl:e.apiBaseUrl,counterpartyId:e.counterpartyId},{counterpartyUrl:e.counterpartyUrl,counterpartyType:"website",sourceIp:t.clientIp,userAgent:p(t,"user-agent"),requestPath:t.uri,requestMethod:t.method,...i&&{attemptId:i},referer:p(t,"referer"),accept:p(t,"accept"),...o&&{declaredSource:o},protocol:r.protocol,evidence:r.evidence,observedMetadata:M(t,a)}),n);if(e.waitUntil){e.waitUntil(s);return}await s}function jt(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 W(e,t,r,n,a){let i=$t({method:t.method,astraPurpose:p(t,"x-astra-purpose"),astraAction:p(t,"x-astra-action")}),o=p(t,"x-astra-attempt-id"),s=ee(p(t,"x-astra-source"));return{...o&&/^att_[0-9a-f]{32}$/.test(o)&&{attemptId:o},credentials:oe(t.headers,{}),purpose:i.purpose,action:i.action,resource:t.uri,createSession:!0,counterpartyType:"website",counterpartyUrl:e.counterpartyUrl,callerMetadata:{sourceIp:t.clientIp,userAgent:p(t,"user-agent"),referer:p(t,"referer"),host:p(t,"host"),...s&&{declaredSource:s},observedMetadata:M(t,a)},...n&&r.artifacts&&{commerceArtifacts:r.artifacts}}}function Q(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},r)}function I(e,t,r,n){let a=v(e);return a.unavailable=!0,a.reasons=[r],n&&(a.correlationId=n),t.mode==="enforce"&&(a.decision="deny"),a}function q(e){return(e.failures??[]).some(t=>t.dimension==="verify_access.api_error")===!0&&!e.misconfigured}async function Kt(e,t,r,n,a,i){let{sampling:o,queryValueAllowlist:s}=i,c=e.budgets??{},f=c.beaconMs??300,m=n.mode==="enforce"?c.verifyEnforceMs??3e3:c.verifyObserveMs??1500,h={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 u=await O(K(h,W(e,t,r,n.depth==="authorize",s)),m);return u?q(u)?I(r,n,u.denialReasons?.[0]??"verify-access api error",u.correlationId):Q(r,u):I(r,n,"verify-access unreachable within budget")}case"identified":{if(n.depth==="authorize"&&r.artifacts){let d=await O(K(h,W(e,t,r,!0,s)),m);return d?q(d)?I(r,n,d.denialReasons?.[0]??"verify-access api error",d.correlationId):Q(r,d):I(r,n,"verify-access unreachable within budget")}await J(e,t,r,f,s);let u=v(r);return n.mode==="enforce"&&n.depth!=="classify"&&(u.decision="deny",u.reasons=["agent is not registered with AstraSync"]),u}case"anonymous-bot":return jt(o.anonymousBeaconRate,e.random)&&await J(e,t,{...r,protocol:r.protocol??"ua-heuristic"},f,s),v(r);default:return v(r)}}catch(u){let d=v(r);return d.reasons=[`edge pipeline error \u2014 fail-open: ${u instanceof Error?u.message:String(u)}`],d}}async function se(e,t,r,n){let a=Et(r,t.uri);try{if(a.skip)return{policy:a,application:x()};let i=p(t,"x-forwarded-host")??p(t,"host")??new URL(e.counterpartyUrl).host,o=Tt(t,i,a.depth);if(o.tier==="human"||o.tier==="self")return{policy:a,classification:o,application:x()};let s=await Kt(e,t,o,a,n,{sampling:a.sampling??r.sampling,queryValueAllowlist:r.queryValueAllowlist});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:x()}}}function T(e){return e.Records[0].cf.request}function A(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 Gt(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 zt(e){let t={},r=(i,o,s)=>{let c=A(e,o);c&&(t[i]=s?s(c):c)},n=A(e,"cloudfront-viewer-address");if(n){t.ip=n.replace(/:\d+$/,"");let i=n.match(/:(\d+)$/);i&&(t.sourcePort=i[1])}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("latitude","cloudfront-viewer-latitude"),r("longitude","cloudfront-viewer-longitude"),r("postalCode","cloudfront-viewer-postal-code"),r("asn","cloudfront-viewer-asn"),r("tlsVersion","cloudfront-viewer-tls");{let i=A(e,"cloudfront-viewer-tls")?.split(":")[1];i&&(t.tlsCipher=i)}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"]])A(e,o)==="true"&&a.push(i);return a.length&&(t.device=a.join(",")),t}function ce(e){let t=Gt(e),r=zt(e),n=A(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 de(e,t){if(t.action==="respond")return Yt(t.status,t.bodyJson,t.headers);for(let r of t.stripHeaderPrefixes)Xt(e,r);for(let[r,n]of Object.entries(t.injectHeaders))D(e,r,n);return e}function ue(e){let t=e.origin?.custom?.domainName??e.origin?.s3?.domainName??void 0;if(!t)return;let r=A(e,"host");!r||r===t||(A(e,"x-forwarded-host")||D(e,"X-Forwarded-Host",r),D(e,"Host",t))}function D(e,t,r){e.headers[t.toLowerCase()]=[{key:t,value:r}]}function Xt(e,t){let r=t.toLowerCase();for(let n of Object.keys(e.headers))n.startsWith(r)&&delete e.headers[n]}function Yt(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 Jt="5.10.0",U=`astrasync-sdk/${Jt}`;function Wt(e){if(!e)return{"user-agent":U};if(e instanceof Headers){let r=new Headers(e);return r.has("user-agent")||r.set("user-agent",U),r}return Array.isArray(e)?e.some(([n])=>n.toLowerCase()==="user-agent")?e:[...e,["user-agent",U]]:Object.keys(e).some(r=>r.toLowerCase()==="user-agent")?e:{"user-agent":U,...e}}var Qt=(e,t)=>fetch(e,{...t,headers:Wt(t?.headers)}),N=Object.freeze({version:1,mode:"observe",depth:"classify",pathRules:[],sampling:Object.freeze({anonymousBeaconRate:1}),failurePosture:"open"}),le=["observe","enforce"],pe=["classify","authenticate","authorize"];function fe(e){if(typeof e!="object"||e===null)return!1;let t=e.anonymousBeaconRate;return typeof t=="number"&&t>=0&&t<=1}function qt(e){if(typeof e!="object"||e===null)return!1;let t=e;if(t.version!==1||!le.includes(t.mode)||!pe.includes(t.depth)||!Array.isArray(t.pathRules))return!1;for(let r of t.pathRules){if(typeof r!="object"||r===null)return!1;let n=r;if(typeof n.pattern!="string"||n.pattern.length===0||n.mode!==void 0&&!le.includes(n.mode)||n.depth!==void 0&&!pe.includes(n.depth)||n.sampling!==void 0&&!fe(n.sampling))return!1}return!fe(t.sampling)||t.queryValueAllowlist!==void 0&&(!Array.isArray(t.queryValueAllowlist)||t.queryValueAllowlist.length>16||!t.queryValueAllowlist.every(r=>typeof r=="string"&&r.length>0&&r.length<=64))?!1:t.failurePosture==="open"}async function Zt(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 Qt(`${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 qt(o)?{edgeConfig:o,etag:a.headers.get("etag")??void 0}:null}catch{return null}}var er=60*1e3,tr=1440*60*1e3,b=new Map,F=new Map;function ye(e,t){return`${e}|${t}`}function rr(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 ge(e,t){let r=ye(e.apiBaseUrl,t),n=F.get(r);if(n)return n;let a=Zt(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(()=>{F.delete(r)});return F.set(r,a),a}async function me(e,t,r){let n=r?.ttlMs??er,a=r?.maxStaleMs??tr,i=ye(e.apiBaseUrl,t),o=b.get(i);if(o){let s=Date.now()-o.lastSuccessAt;return s>n&&ge(e,t).catch(()=>{}),s>a?rr(o.config):o.config}return await ge(e,t).catch(()=>{}),b.get(i)?.config??N}var nr=900*1e3,he=new Map;async function ar(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 ve(e){if(e.apiKey)return e.apiKey;let t=e.apiKeySecretName;if(!t)return;let r=he.get(t);if(r&&Date.now()-r.fetchedAt<nr)return r.value;try{let a=await(e.fetchSecret??ar)(t);return he.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 Ae(e,t){try{return await me({apiBaseUrl:e.apiBaseUrl,apiKey:t},e.counterpartyId,{ttlMs:e.configTtlMs})}catch{return e.fallbackConfig??N}}function we(e){let t=e.log??((r,n,a)=>{console[r==="info"?"log":r](`[astrasync-edge] ${n}`,a??"")});return async function(n){let a=T(n);try{ue(a);let i=await ve({...e,log:t}),o=await Ae(e,i),{application:s}=await se({...e,log:t},ce(a),o,i);return de(a,s)}catch(i){return t("error","edge handler error \u2014 passing request through",{error:i instanceof Error?i.message:String(i)}),a}}}var or={apiBaseUrl:"x-astrasync-api-base-url",publicBaseUrl:"x-astrasync-public-base-url",counterpartyId:"x-astrasync-counterparty-id",counterpartyUrl:"x-astrasync-counterparty-url",apiKeySecretName:"x-astrasync-api-key-secret-name"};function ir(e,t){return e.origin?.custom?.customHeaders?.[t]?.[0]?.value||void 0}function be(e,t){let r=n=>ir(e,or[n]);return{apiBaseUrl:r("apiBaseUrl")??t.apiBaseUrl,publicBaseUrl:r("publicBaseUrl")??t.publicBaseUrl,counterpartyId:r("counterpartyId")??t.counterpartyId,counterpartyUrl:r("counterpartyUrl")??t.counterpartyUrl,apiKeySecretName:r("apiKeySecretName")??t.apiKeySecretName}}function Re(e){return[e.apiBaseUrl,e.publicBaseUrl??"",e.counterpartyId,e.counterpartyUrl,e.apiKeySecretName??""].join("|")}var sr={apiBaseUrl:"https://astrasync.ai/api",publicBaseUrl:void 0,counterpartyId:"",counterpartyUrl:"",apiKeySecretName:void 0},Se=new Map,Ie=!1,cr=async e=>{let t=be(T(e),sr);(!t.counterpartyId||!t.counterpartyUrl)&&(Ie||(Ie=!0,console.error("[astrasync-edge] counterpartyId/counterpartyUrl missing \u2014 set the X-AstraSync-* origin custom headers on the distribution (see infra/template.yaml) or bake ASTRASYNC_* constants via infra/deploy.sh.")));let r=Re(t),n=Se.get(r);return n||(n=we(t),Se.set(r,n)),n(e)};0&&(module.exports={handler});
1
+ "use strict";var Ue=Object.create;var I=Object.defineProperty;var _e=Object.getOwnPropertyDescriptor;var Pe=Object.getOwnPropertyNames;var xe=Object.getPrototypeOf,ke=Object.prototype.hasOwnProperty;var Me=(e,t)=>{for(var r in t)I(e,r,{get:t[r],enumerable:!0})},L=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of Pe(t))!ke.call(e,a)&&a!==r&&I(e,a,{get:()=>t[a],enumerable:!(n=_e(t,a))||n.enumerable});return e};var De=(e,t,r)=>(r=e!=null?Ue(xe(e)):{},L(t||!e||!e.__esModule?I(r,"default",{value:e,enumerable:!0}):r,e)),Oe=e=>L(I({},"__esModule",{value:!0}),e);var wr={};Me(wr,{handler:()=>Ar});module.exports=Oe(wr);var O="5.14.0",b=`astrasync-sdk/${O}`;function Ne(e){if(!e)return{"user-agent":b};if(e instanceof Headers){let r=new Headers(e);return r.has("user-agent")||r.set("user-agent",b),r}return Array.isArray(e)?e.some(([n])=>n.toLowerCase()==="user-agent")?e:[...e,["user-agent",b]]:Object.keys(e).some(r=>r.toLowerCase()==="user-agent")?e:{"user-agent":b,...e}}var C=(e,t)=>fetch(e,{...t,headers:Ne(t?.headers)}),Ir=Object.freeze({version:1,mode:"observe",depth:"classify",pathRules:[],sampling:Object.freeze({anonymousBeaconRate:1}),failurePosture:"open"}),br=60*1e3,Rr=1440*60*1e3;function Fe(e,t){for(let r of e)if(Be(r.pattern).test(t))return r}function Be(e){let t=e.replace(/[.+?^${}()|[\]\\]/g,"\\$&").replace(/\*/g,".*");return new RegExp(`^${t}$`,"i")}var $e=1,Le=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"]),je=["x-astrasync-","kya-","kya_"],He=new Set(["authorization","x-api-key","x-goog-api-key","api-key"]);function Ke(e){return e==="signature"||e.endsWith("-signature")||e.endsWith("-signature-256")}var Ve=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"]),Ge=["x-goog-","x-stainless-","x-anthropic-","x-openai-","sec-ch-"],ze=["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-"],qe=64,j=1024,Xe=16*1024,Ye=64,We=/^[a-z0-9][a-z0-9 ._/-]{0,63}$/;function te(e){let t=e?.trim().slice(0,Ye).toLowerCase();return t&&We.test(t)?t:void 0}function k(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 Je(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 ze)if(n.startsWith(a))return a}function Qe(e){return!!(Le.has(e)||je.some(t=>e.startsWith(t))||Ke(e))}function Ze(e){return Ve.has(e)?!0:Ge.some(t=>e.startsWith(t))}function re(e){let t={},r={},n,a,i=0,o=0;if(!e)return{headers:t};for(let c of Object.keys(e)){if(o>=qe)break;let d=c.toLowerCase(),f=e[c];if(f===void 0)continue;let y=Array.isArray(f)?f.join(", "):String(f);if(He.has(d)){let h=Je(y);h&&!n&&(n=h);continue}if(d==="cookie"&&(a=y.split(";").filter(h=>h.trim().length>0).length),Qe(d))continue;let l=y;k(l)>j&&(l=l.slice(0,j));let u=k(d)+k(l);i+u>Xe||(i+=u,o++,t[d]=l,Ze(d)&&(r[d]=l))}let s={headers:t};return n&&(s.apiKeyFormat=n),Object.keys(r).length>0&&(s.platformHeaders=r),a!==void 0&&(s.cookieCount=a),s}var et=[{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,/\bGPTBot\b/i,/\bOAI-SearchBot\b/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,/\bGoogle-Extended\b/i,/\bGoogleOther\b/i,/\bGoogle-CloudVertexBot\b/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]},{vendor:"chatgpt-atlas",displayName:"ChatGPT Atlas browser",uaPatterns:[/\batlas\/\d/i],headerPatterns:[/^sec-ch-ua: .*atlas/i]},{vendor:"perplexity-comet",displayName:"Perplexity Comet browser",uaPatterns:[/\bcomet\/\d/i],headerPatterns:[/^sec-ch-ua: .*comet/i]},{vendor:"astrasync-bridge",displayName:"AstraSync MCP bridge (buyer-agent catalog fetch)",uaPatterns:[/^astrasync-bridge\//i]},{vendor:"astrasync-sdk",displayName:"AstraSync SDK",uaPatterns:[/^astrasync-sdk\//i]}];function tt(e,t){let r=t.userAgent??"",n=t.agentCardUrl??"",a=t.apiKeyFormat??"",i=t.ja4??"",o=t.asn??"",s=[];if(t.platformHeaders)for(let[c,d]of Object.entries(t.platformHeaders))s.push(c,`${c}: ${d}`);for(let c of e){let d=c.uaPatterns?.some(p=>p.test(r)),f=c.agentCardPatterns?.some(p=>p.test(n)),y=a.length>0&&c.apiKeyFormatPatterns?.some(p=>p.test(a)),l=s.length>0&&c.headerPatterns?.some(p=>s.some(m=>p.test(m))),u=i.length>0&&c.ja4Patterns?.some(p=>p.test(i)),h=o.length>0&&c.asnList?.includes(o);if(d||f||y||l||u||h){let p=r.split(/[\s/]/)[0]?.toLowerCase()??"",m=(()=>{try{return n?new URL(n).host:""}catch{return""}})(),P=p||m||a||i||o&&`asn${o}`||"unknown";return{vendor:c.vendor,fingerprintKey:`${c.vendor}:${P}`,displayName:c.displayName}}}}function H(e){return tt(et,e)}function rt(e){return e>=80?"PLATINUM":e>=60?"GOLD":e>=40?"SILVER":"BRONZE"}var Cr=3600*1e3,nt=new Map;function at(e){return nt.get(e)?.data}var ne=new Set(["identity.invalid_agent_id","identity.unknown_agent","agent.lookup"]),ae="Register your agent with AstraSync (or, inside an MCP host, add the AstraSync MCP connector for keyless, human-approved registration), then present the canonical ASTRA-* id issued after owner approval.",ot={apiBaseUrl:"https://astrasync.ai/api",debug:!1},oe=!1,K=!1;async function V(e,t,r,n,a){oe=!0;try{let i=`${e}/agents/verify-access`,s=(await C(i,{method:"HEAD"})).headers.get("content-type")??"";if(s.startsWith("text/html")){let c=`[VerificationGateway] apiBaseUrl '${e}' returned HTML (content-type: ${s}). 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(`${c} (strictInit=true)`);console.warn(`${c} Set disableInitChecks: true on GatewayConfig to silence.`)}else t&&console.log(`[VerificationGateway] init check passed for ${e} (content-type: ${s})`);if(n){let c=a?.id?`?counterpartyId=${encodeURIComponent(a.id)}`:a?.url?`?counterpartyUrl=${encodeURIComponent(a.url)}`:"",d=await C(`${e}/agents/verify-access/self-check${c}`,{headers:{Authorization:`Bearer ${n}`}});if(d.status===401){let f=await d.json().catch(()=>({})),y=`[VerificationGateway] the configured AstraSync API key was REJECTED at startup (${f.code??"api_key_invalid"}: ${f.message??"invalid key"}). Every inbound agent will be turned away until the key is rotated`+(f.remediation?.manageUrl?` at ${f.remediation.manageUrl}`:"")+".";if(r)throw new Error(`${y} (strictInit=true)`);console.error(y)}else if(d.ok){let f=await d.json().catch(()=>({}));if(f.counterparty&&f.counterparty.canVerify===!1){let y=`[VerificationGateway] the configured AstraSync API key cannot verify for this endpoint (${f.counterparty.message??"endpoint not found or owned by another account"}). Every inbound agent will be turned away until the key or counterparty is corrected.`;if(r)throw new Error(`${y} (strictInit=true)`);console.error(y)}typeof f.daysToExpiry=="number"&&f.daysToExpiry<=7?console.warn(`[VerificationGateway] the configured AstraSync API key expires in ${f.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(i){if(r)throw i;t&&console.log(`[VerificationGateway] init check failed (non-blocking): ${String(i)}`)}}var D=new Map;function se(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||"",e.commercePhase||"",e.checkoutSessionId||""].join("|")}function st(e,t){let r=se(e,t),n=D.get(r);return n&&n.expiresAt>Date.now()?n.result:(n&&D.delete(r),null)}var it=60;function ct(e,t,r,n){let a=r&&r>0?r:it,i=se(e,n);D.set(i,{result:t,expiresAt:Date.now()+a*1e3})}function ie(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 E(e){if(e?.mcpEndpoint)return{endpoint:e.mcpEndpoint,discoveryUrl:e.mcpDiscoveryUrl,message:"If you are operating inside an MCP-capable host (Claude, ChatGPT, Cursor, or any MCP client), add the AstraSync MCP connector instead of integrating these URLs by hand: registration through the connector is KEYLESS and HUMAN-APPROVED \u2014 your human approves in an in-chat card, and you never see, hold, or transmit an API key, password, or private key."}}function dt(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, rotated, or belongs to another account). 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 replacing","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:[...o?.mcpEndpoint?[`Fastest path (inside an MCP host): add the AstraSync MCP connector at ${o.mcpEndpoint} \u2014 keyless, human-approved registration`]:[],"Register for an AstraSync account","Create and register your agent","Add your ASTRA-ID to request headers","Retry your request"],...E(o)&&{mcp:E(o)}};return{identityVerified:!1,policyAllowed:!1,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},...a&&{retryable:!0},...r.httpStatus!==void 0&&{httpStatus:r.httpStatus},...r.retryAfterMs!==void 0&&{retryAfterMs:r.retryAfterMs},correlationId:r.correlationId,attemptId:r.attemptId,verifiedAt:new Date}}async function ut(e){try{let t=await e.json();return t&&typeof t=="object"?t:{}}catch{return{}}}function lt(e){let t=e?.find(r=>ne.has(r.dimension));if(t)return t.dimension==="agent.lookup"?`The presented agent identifier is not registered with AstraSync. ${ae}`:t.guidance?`${t.message} ${t.guidance}`:t.message}var pt=900*1e3;function ft(e){let t=e.headers?.get?.("retry-after");if(!t)return;let r=t.trim(),n;if(/^\d+(\.\d+)?$/.test(r))n=Number(r)*1e3;else{let a=Date.parse(r);if(Number.isNaN(a))return;n=a-Date.now()}return Math.min(pt,Math.max(0,Math.round(n)))}async function gt(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.commercePhase&&(a.commercePhase=n.commercePhase),n.checkoutSessionId&&(a.checkoutSessionId=n.checkoutSessionId),n.checkoutItems&&(a.checkoutItems=n.checkoutItems),n.buyerEmail&&(a.buyerEmail=n.buyerEmail),n.commerceArtifacts&&(a.commerceArtifacts=n.commerceArtifacts),n.attemptId&&(a.attemptId=n.attemptId),n.considerationSet&&(a.considerationSet=n.considerationSet),a.sdkVersion=O,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 C(`${e.apiBaseUrl}/agents/verify-access`,{method:"POST",headers:i,body:JSON.stringify(a),signal:AbortSignal.timeout(e.verifyTimeoutMs??1e4)}),s=await ut(o),c=ft(o);if(o.status===410)return{success:!0,attemptId:typeof s?.attemptId=="string"?s.attemptId:void 0,access:{allowed:!1,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."}]}};if(o.status===400&&s?.error==="INVALID_AGENT_ID"){let d=typeof s?.message=="string"?s.message:"Agent identifier must be a canonical ASTRA-* id.";return{success:!0,attemptId:typeof s?.attemptId=="string"?s.attemptId:void 0,access:{allowed:!1,reason:"invalid_agent_id",failures:[{dimension:"identity.invalid_agent_id",message:d,guidance:typeof s?.guidance=="string"?s.guidance:"Present your canonical ASTRA-* id (discover it via GET /api/agents/request-registration/{requestId} after owner approval)."}]}}}if(o.status===404&&r.astraId&&(s?.error==="AGENT_NOT_FOUND"||typeof s?.message=="string"&&/agent/i.test(s.message)))return{success:!0,attemptId:typeof s?.attemptId=="string"?s.attemptId:void 0,access:{allowed:!1,reason:"unknown_agent",failures:[{dimension:"identity.unknown_agent",message:"The presented agent identifier is not registered with AstraSync.",guidance:ae}]}};if(!o.ok){let d=o.status===403&&s?.error==="COUNTERPARTY_KEY_MISMATCH";return o.status===401||d?{success:!1,error:s.message||s.error||"Integration API key rejected",misconfig:!0,misconfigCode:d?"counterparty_key_mismatch":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:typeof s?.message=="string"&&s.message||typeof s?.error=="string"&&s.error||`API returned ${o.status}`,correlationId:typeof s?.correlationId=="string"?s.correlationId:void 0,attemptId:typeof s?.attemptId=="string"?s.attemptId:void 0,httpStatus:o.status,...c!==void 0&&{retryAfterMs:c}}}return s}catch(o){return{success:!1,error:`Failed to call verify-access API: ${o instanceof Error?o.message:"Unknown error"}`}}}async function G(e,t,r){let n={...ot,...e},a=n.apiBaseUrl?at(n.apiBaseUrl):void 0;if(!oe&&!n.disableInitChecks&&n.apiBaseUrl&&(n.strictInit?await V(n.apiBaseUrl,n.debug,!0,n.apiKey,{id:n.counterpartyId,url:n.counterpartyUrl??t.counterpartyUrl}):V(n.apiBaseUrl,n.debug,!1,n.apiKey,{id:n.counterpartyId,url:n.counterpartyUrl??t.counterpartyUrl})),!K&&(e.minTrustScore!==void 0||e.minTrustScoreForFull!==void 0)&&(K=!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 decisions (verified ID + runtime challenge + PDLSS + trust score). To gate access to an endpoint, configure the endpoint's trust_score_requirement server-side.")),n.cacheTtl!==0&&!r?.bypassCache){let p=st(t,n.counterpartyId);if(p){n.debug&&console.log("[VerificationGateway] Returning cached result");let m={...p};return t.attemptId?m.attemptId=t.attemptId:delete m.attemptId,m}}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 gt(n,i),s=o.denialReasons,c=Array.isArray(s)&&s.length>0?s:void 0;if(!o.success)return dt(n,o.error,{source:o.misconfig?"merchant_misconfig":"api_error",correlationId:o.correlationId,attemptId:o.attemptId,remediation:o.remediation,httpStatus:o.httpStatus,retryAfterMs:o.retryAfterMs,urls:a});if(!o.access?.allowed){let p=o.access?.failures;return{identityVerified:o.verificationContext?.idVerified===!0&&!p?.some(x=>ne.has(x.dimension)),policyAllowed:!1,denialReasons:c??(p&&p.length>0?p.map(x=>x.message):o.access?.reason?[o.access.reason]:["Access denied"]),failures:p,requiresStepUp:o.access?.requiresStepUp,requiresApproval:o.access?.requiresApproval,guidance:{message:lt(p)??(o.access?.reason||"Access denied by PDLSS policy"),registrationUrl:a?.registrationUrl??"",documentationUrl:a?.documentationUrl??"",...E(a)&&{mcp:E(a)}},verifiedAt:new Date,sessionId:o.sessionId,correlationId:o.correlationId,attemptId:o.attemptId,recommendation:o.recommendation,recommendationReasons:o.recommendationReasons,stepUpApproval:o.stepUpApproval,settlement:o.settlement,settlementOutcome:o.settlementOutcome,settlementToken:o.settlementToken,fulfilment:o.fulfilment}}let d=o.agent?{astraId:o.agent.astraId,name:o.agent.name,trustScore:o.agent.trustScore,trustLevel:rt(o.agent.trustScore),blockchainAnchored:o.agent.blockchainStatus==="anchored",status:o.agent.agentStatus}:void 0,f=o.developer?{astradId:o.developer.kyaOwnerId,name:o.developer.fullName,trustScore:o.developer.trustScore||0,verified:o.developer.identityVerified}:void 0,y=o.organization?{name:o.organization.name,verified:o.organization.verified,trustScore:o.organization.trustScore}:void 0,l=o.verificationContext,u={identityVerified:o.verificationContext?.idVerified===!0,policyAllowed:!0,agent:d,developer:f,organization:y,appliedPolicy:o.access?.appliedPolicy,verificationContext:l,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,settlementOutcome:o.settlementOutcome,settlementToken:o.settlementToken,fulfilment:o.fulfilment};u.recommendation==="deny"?(u.policyAllowed=!1,u.denialReasons=c??u.recommendationReasons??["Access denied by AstraSync recommendation"],u.guidance=u.runtimeChallenge?{message:`Verification failed: ${u.runtimeChallenge.reason||"runtime challenge failed"}`,registrationUrl:a?.registrationUrl??"",documentationUrl:a?.documentationUrl??""}:{message:u.recommendationReasons?.[0]||"Access denied by AstraSync recommendation",registrationUrl:a?.registrationUrl??"",documentationUrl:a?.documentationUrl??""}):u.recommendation==="step_up_required"&&u.identityVerified&&(u.requiresStepUp=!0,u.denialReasons=c??u.recommendationReasons??["Step-up verification required"]);let h=u.requiresStepUp===!0||u.requiresApproval===!0||u.recommendation==="step_up_required";return n.cacheTtl!==0&&u.recommendation!=="deny"&&!h&&ct(t,u,n.cacheTtl,n.counterpartyId),u}function g(e,t){return e.headers[t.toLowerCase()]}function yt(e,t){let r=e.querystring?`?${e.querystring}`:"";return`https://${t}${e.uri}${r}`}var z=/^att_[0-9a-f]{32}$/;function ht(e){let t=g(e,"x-astra-attempt-id");if(t&&z.test(t))return t;if(e.querystring)try{let r=new URLSearchParams(e.querystring).get("astra_attempt");if(r&&z.test(r))return r}catch{}}function N(e,t){let{headers:r,apiKeyFormat:n,platformHeaders:a,cookieCount:i}=re(e.headers),o=e.connection??{},s=vt(e.querystring),c=St(e.querystring,t);return{headers:r,...n&&{apiKeyFormat:n},...a&&{platformHeaders:a},...Object.keys(o).length>0&&{connection:o},...i!==void 0&&{cookieCount:i},...s&&{queryKeys:s},...c&&{queryParams:c},schemaVersion:$e}}var mt=32,ce=64;function vt(e){if(!e)return;let t;try{t=new URLSearchParams(e)}catch{return}let r=[];for(let n of t.keys()){let a=n.toLowerCase().slice(0,ce);if(a&&!r.includes(a)&&(r.push(a),r.length>=mt))break}return r.length>0?r:void 0}var At=16,wt=256;function St(e,t){if(!e||!t||t.length===0)return;let r;try{r=new URLSearchParams(e)}catch{return}let n=o=>t.some(s=>s.endsWith("*")?o.startsWith(s.slice(0,-1)):o===s),a={},i=0;for(let[o,s]of r.entries()){let c=o.toLowerCase().slice(0,ce);if(!(!c||a[c]!==void 0||!n(c))&&(a[c]=s.slice(0,wt),++i>=At))break}return i>0?a:void 0}var It=/^eyJ[\w-]+\.[\w-]*\.[\w-]+~/;function bt(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 Rt(e){try{let t=e.split(".")[0];return JSON.parse(bt(t))}catch{return}}function Ct(e){if(e)try{let t=JSON.parse(e);return typeof t=="object"&&t!==null?t:void 0}catch{return}}function Et(e,t){let r=yt(e,t),n=e.method,a=e.headers,i=Ct(e.bodyText),o=e.bodyTruncated?{bodyTruncated:!0}:{},s=g(e,"authorization"),c=s?.replace(/^Bearer\s+/i,""),f=[g(e,"x-payment-mandate")??g(e,"payment-mandate"),c].find(h=>h&&It.test(h));if(f){let h=Rt(f)?.typ;return{protocol:"vi-sd-jwt",artifacts:{viSdJwt:f},evidence:{typ:h,...o}}}let y=g(e,"signature"),l=g(e,"signature-input");if(y&&l){let h=g(e,"signature-agent"),p=g(e,"x-agent-registry"),m=l.toLowerCase();return{protocol:h?"rfc9421-web-bot-auth":p?.toLowerCase().includes("visa")||m.includes("visa")?"rfc9421-tap":p?.toLowerCase().includes("mastercard")||m.includes("mastercard")?"rfc9421-agent-pay":"rfc9421",artifacts:{rfc9421:{headers:a,method:n,url:r,...e.bodyText&&{body:e.bodyText}}},evidence:{...h&&{signatureAgent:h},...p&&{registryHint:p},keyid:l.match(/keyid="([^"]+)"/)?.[1],...o}}}let u=g(e,"timestamp");if(y&&!l&&(u||g(e,"idempotency-key")))return{protocol:"acp",artifacts:{acpRequest:{method:n,url:r,headers:a,...e.bodyText&&{rawBody:e.bodyText},...i&&{body:i}}},evidence:{hasTimestamp:!!u,...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 h=i.intent_mandate??i.intentMandate,p=i.cart_mandate??i.cartMandate,m=i.payment_mandate??i.paymentMandate;if(typeof h=="string"||typeof p=="string"||typeof m=="string")return{protocol:"ap2",artifacts:{ap2Mandates:{...typeof h=="string"&&{intent:h},...typeof p=="string"&&{cart:p},...typeof m=="string"&&{payment:m}}},evidence:{mandatesPresent:[typeof h=="string"&&"intent",typeof p=="string"&&"cart",typeof m=="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(g(e,"x-payment"))return{protocol:"x402",artifacts:{x402Request:{method:n,url:r,headers:a,...i&&{body:i}}},evidence:o}}var Tt=/\.(css|js|mjs|map|png|jpe?g|gif|webp|avif|svg|ico|woff2?|ttf|otf|eot|mp4|webm|pdf|txt|xml)$/i,Ut=/^astrasync-sdk\//i,_t=/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,Pt=/mozilla\/\d/i,xt=/^kya_[A-Za-z0-9_-]+$/;function q(e){return typeof e=="string"&&xt.test(e)}function kt(e,t){if(Tt.test(t))return{mode:e.mode,depth:e.depth,skip:!0};let r=Fe(e.pathRules,t);return r?{mode:r.mode??e.mode,depth:r.depth??e.depth,skip:r.skip===!0,...r.sampling&&{sampling:r.sampling}}:{mode:e.mode,depth:e.depth,skip:!1}}function Mt(e,t,r){let n=g(e,"user-agent")??"";if(Ut.test(n))return{tier:"self"};let a=ie(e.headers,{});if(!a.astraId&&(q(a.jwt)||q(a.apiKey)))return{tier:"self",evidence:{firstPartyApiKey:!0}};if(a.astraId)return{tier:"verified",protocol:"x-astra"};let{apiKeyFormat:i,platformHeaders:o}=re(e.headers),s={userAgent:n,apiKeyFormat:i,platformHeaders:o};if(r!=="classify"){let d=Et(e,t);if(d)return{tier:"identified",protocol:d.protocol,artifacts:d.artifacts,evidence:d.evidence,fingerprint:H(s)}}let c=H(s);return c?{tier:"identified",protocol:"platform-ua",fingerprint:c}:!n||_t.test(n)||!Pt.test(n)?{tier:"anonymous-bot",evidence:{userAgent:n||"(none)"}}:!g(e,"sec-fetch-mode")&&!g(e,"accept-language")?{tier:"anonymous-bot",evidence:{userAgent:n,spoofedBrowser:!0}}:{tier:"human"}}var Dt={"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){try{let t=new URL(e).hostname.replace(/^www\./,"");if(t==="astrasync.ai"||t.endsWith(".astrasync.ai"))return`https://mcp.${t}/mcp`}catch{}}function Nt(e,t,r){let n=(r||t).replace(/\/api\/?$/,""),a=e.guidance?.mcp?.endpoint||Ot(n),i=(e.protocol&&Dt[e.protocol])??"This request could not be verified by AstraSync.";return{success:!1,error:{code:e.tier==="verified"?"INSUFFICIENT_ACCESS":"UNAUTHORIZED",message:i,...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||`${n}/register`,documentationUrl:e.guidance?.documentationUrl||`${n}/docs/edge-adapter`,...a&&{mcp:{endpoint:a,discoveryUrl:e.guidance?.mcp?.discoveryUrl||`${a.replace(/\/mcp$/,"")}/.well-known/mcp.json`,message:e.guidance?.mcp?.message||"Inside an MCP host? Add the AstraSync MCP connector at this endpoint instead of the registration URL \u2014 registration through the connector is keyless and human-approved (your human approves in an in-chat card; you never handle credentials)."}}}}}}function Ft(e){return{success:!1,error:{code:"VERIFICATION_UNAVAILABLE",message:"Agent verification is temporarily unavailable and this path enforces verification. Your identity was not evaluated and no denial was recorded \u2014 retry with exponential backoff.",failures:e.reasons,...e.correlationId&&{correlationId:e.correlationId},guidance:{message:"Retry the request with exponential backoff; the condition is transient."}}}}function Bt(e){return{success:!1,status:"manual_review",message:"This action requires human approval. Poll the step-up approval endpoint and retry once approved.",...e.stepUp&&{stepUpApproval:e.stepUp},...e.correlationId&&{correlationId:e.correlationId}}}var $t="X-Astra-Verified-Hop";function Lt(e){return`${e.astraId};${e.sessionId??""};${e.checkedAt}`}var w={trustScore:"X-AstraSync-Trust-Score",agentId:"X-AstraSync-Agent-Id",developerId:"X-AstraSync-Developer-Id",observedDecision:"X-AstraSync-Observed-Decision"},X=["x-astrasync-","x-astra-verified-hop"];function M(){return{action:"pass",injectHeaders:{},stripHeaderPrefixes:[]}}function jt(e,t,r){let n={};if(t.misconfig&&(n[w.observedDecision]="misconfig"),r.mode!=="enforce")return t.decision!=="allow"&&(n[w.observedDecision]=t.decision),Object.assign(n,Y(t),W(t)),{action:"pass",injectHeaders:n,stripHeaderPrefixes:X};switch(t.decision){case"deny":return t.unavailable?{action:"respond",status:503,headers:{"Retry-After":"30"},bodyJson:Ft(t)}:{action:"respond",status:403,headers:{},bodyJson:Nt(t,e.apiBaseUrl,e.publicBaseUrl)};case"manual_review":return{action:"respond",status:202,headers:{"Retry-After":"30"},bodyJson:Bt(t)};default:return Object.assign(n,Y(t),W(t)),{action:"pass",injectHeaders:n,stripHeaderPrefixes:X}}}function Y(e){if(!e.asat)return{};let{trustScore:t,agentId:r,developerId:n}=e.asat;return{...t!==void 0&&{[w.trustScore]:String(t)},...r&&{[w.agentId]:r},...n&&{[w.developerId]:n}}}function W(e){let t=e.asat?.agentId;return t?{[$t]:Lt({astraId:t,...e.sessionId&&{sessionId:e.sessionId},checkedAt:Date.now()})}:{}}var Ht={GET:"data.read",HEAD:"data.read",OPTIONS:"data.read",POST:"data.write",PUT:"data.write",PATCH:"data.write",DELETE:"data.delete"},Kt="data.write",Vt="data";function Gt(e){return Ht[e.toUpperCase()]??Kt}function zt(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 qt(e){let t=e.astraPurpose?zt(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=Gt(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=Vt,i="transport_default")}return{purpose:a,action:r,purposeSource:i,actionSource:n}}async function T(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)}}var Xt=(()=>{let e=globalThis.crypto;return e?.randomUUID?e.randomUUID():`c-${Date.now().toString(36)}-${Math.random().toString(36).slice(2,12)}`})(),Yt=e=>`${e.apiBaseUrl}\0${e.apiKey??""}\0${e.counterparty}\0${e.adapter??""}`,Wt=class{constructor(e={}){this.queue=[],this.droppedSinceLast=0,this.droppedTotal=0,this.deliveredTotal=0,this.enqueuedTotal=0,this.backoffUntil=0,this.collapse=new Map,this.rejectedKeys=new Set,this.opts={maxItems:e.maxItems??1e3,maxBatch:e.maxBatch??100,maxAttempts:e.maxAttempts??3,collapseWindowMs:e.collapseWindowMs??6e4,collapseMaxKeys:e.collapseMaxKeys??1e3,requestTimeoutMs:e.requestTimeoutMs??1e4,retryBackoffMs:e.retryBackoffMs??1e3,holdRepeats:e.holdRepeats??!1,containerId:e.containerId??Xt,now:e.now??Date.now}}enqueue(e,t,r){let n=this.opts.now();if(r){let a=this.collapse.get(r);if(a&&n-a.firstSeenAt<this.opts.collapseWindowMs){for(let o of[a.first,a.held])if(o&&!o.inFlight&&this.queue.includes(o)){o.item.repeatCount=(o.item.repeatCount??1)+1;return}if(this.opts.holdRepeats){a.held=this.push(e,{...t,repeatCount:1},a.firstSeenAt+this.opts.collapseWindowMs);return}a.first=this.push(e,{...t,repeatCount:1},n);return}if(this.collapse.delete(r),this.collapse.size>=this.opts.collapseMaxKeys){let o=this.collapse.keys().next().value;o!==void 0&&this.collapse.delete(o)}let i={firstSeenAt:n};this.collapse.set(r,i),i.first=this.push(e,t,n);return}this.push(e,t,n)}flush(){return this.inFlight||(this.inFlight=this.drain().finally(()=>{this.inFlight=void 0})),this.inFlight}stats(){return{queueDepth:this.queue.length,droppedSinceLast:this.droppedSinceLast,droppedTotal:this.droppedTotal,deliveredTotal:this.deliveredTotal,enqueuedTotal:this.enqueuedTotal,flushing:this.inFlight!==void 0}}push(e,t,r){for(;this.queue.length>=this.opts.maxItems;)this.dropOldest();let n={dest:e,destKey:Yt(e),item:t,attempts:0,eligibleAt:r,inFlight:!1};return this.queue.push(n),this.enqueuedTotal+=1,n}dropOldest(){this.queue.shift()&&this.countDropped(1)}countDropped(e){this.droppedSinceLast+=e,this.droppedTotal+=e}async drain(){for(;;){let e=this.opts.now();if(e<this.backoffUntil)return;let t=this.takeBatch(e);if(t.length===0||!await this.send(t))return}}takeBatch(e){let t=this.queue.find(a=>a.eligibleAt<=e);if(!t)return[];let r=[],n=[];for(let a of this.queue)r.length<this.opts.maxBatch&&a.destKey===t.destKey&&a.eligibleAt<=e?(a.inFlight=!0,r.push(a)):n.push(a);return this.queue=n,r}async send(e){let t=e[0].dest,r=t.apiKey&&!this.rejectedKeys.has(t.apiKey)?t.apiKey:void 0,n=this.droppedSinceLast,a={droppedSinceLast:n,queueDepth:this.queue.length,adapter:t.adapter??"edge-core",version:O,containerId:this.opts.containerId,enqueuedTotal:this.enqueuedTotal,deliveredTotal:this.deliveredTotal,droppedTotal:this.droppedTotal},i=typeof AbortController<"u"?new AbortController:void 0,o=await T(C(`${t.apiBaseUrl}/verification-activity/edge-events`,{method:"POST",headers:{"Content-Type":"application/json",...r&&{Authorization:`Bearer ${r}`}},body:JSON.stringify({events:e.map(c=>c.item),meta:a}),...i&&{signal:i.signal}}),this.opts.requestTimeoutMs);o||i?.abort();let s=o?.status;for(let c of e)c.inFlight=!1;if(s!==void 0&&s>=200&&s<300)return this.droppedSinceLast=Math.max(0,this.droppedSinceLast-n),this.deliveredTotal+=e.length,!0;if(s===401&&r)return this.rejectedKeys.add(r),this.requeue(e,!1),!0;if(s===429){let c=Number(o?.headers.get("retry-after")),d=Number.isFinite(c)&&c>0?Math.min(c,120)*1e3:3e4;return this.backoffUntil=this.opts.now()+d,this.requeue(e,!1),!1}return s!==void 0&&s>=400&&s<500?(this.countDropped(e.length),!0):(this.backoffUntil=this.opts.now()+this.opts.retryBackoffMs,this.requeue(e,!0),!1)}requeue(e,t){let r=[];for(let n of e)t&&(n.attempts+=1),n.attempts>=this.opts.maxAttempts?this.countDropped(1):r.push(n);for(this.queue=[...r,...this.queue];this.queue.length>this.opts.maxItems;)this.dropOldest()}},Jt=new Wt;function Qt(){return Jt}async function J(e,t,r,n,a,i){let o=ht(t),s=te(g(t,"x-astra-source")),c=g(t,"user-agent"),d={...e.counterpartyId&&{counterpartyId:e.counterpartyId},...e.counterpartyUrl&&{counterpartyUrl:e.counterpartyUrl},counterpartyType:"website",sourceIp:t.clientIp,userAgent:c,requestPath:t.uri,requestMethod:t.method,...o&&{attemptId:o},referer:g(t,"referer"),accept:g(t,"accept"),...s&&{declaredSource:s},protocol:r.protocol,evidence:r.evidence,observedMetadata:N(t,a),observedAt:new Date().toISOString()};for(let l of Object.keys(d))d[l]===void 0&&delete d[l];let f=Qt();f.enqueue({apiBaseUrl:e.apiBaseUrl,...i&&{apiKey:i},counterparty:e.counterpartyId||e.counterpartyUrl,...e.adapter&&{adapter:e.adapter}},d,t.clientIp?[e.counterpartyId||e.counterpartyUrl,t.clientIp,c??"",t.method,t.uri,o??""].join("\0"):void 0);let y=f.flush();if(e.waitUntil){e.waitUntil(y);return}await T(y,n)}function Zt(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 Q(e,t,r,n,a){let i=qt({method:t.method,astraPurpose:g(t,"x-astra-purpose"),astraAction:g(t,"x-astra-action")}),o=g(t,"x-astra-attempt-id"),s=te(g(t,"x-astra-source"));return{...o&&/^att_[0-9a-f]{32}$/.test(o)&&{attemptId:o},credentials:ie(t.headers,{}),purpose:i.purpose,action:i.action,resource:t.uri,createSession:!0,counterpartyType:"website",counterpartyUrl:e.counterpartyUrl,callerMetadata:{sourceIp:t.clientIp,userAgent:g(t,"user-agent"),referer:g(t,"referer"),host:g(t,"host"),...s&&{declaredSource:s},observedMetadata:N(t,a)},...n&&r.artifacts&&{commerceArtifacts:r.artifacts}}}function Z(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},r)}function R(e,t,r,n){let a=v(e);return a.unavailable=!0,a.reasons=[r],n&&(a.correlationId=n),t.mode==="enforce"&&(a.decision="deny"),a}function ee(e){return(e.failures??[]).some(t=>t.dimension==="verify_access.api_error")===!0&&!e.misconfigured}async function er(e,t,r,n,a,i){let{sampling:o,queryValueAllowlist:s}=i,c=e.budgets??{},d=c.beaconMs??300,f=n.mode==="enforce"?c.verifyEnforceMs??3e3:c.verifyObserveMs??1500,y={apiBaseUrl:e.apiBaseUrl,...a&&{apiKey:a},counterpartyId:e.counterpartyId,disableInitChecks:!0};try{switch(r.tier){case"verified":{if(n.depth==="classify")return v(r);let l=await T(G(y,Q(e,t,r,n.depth==="authorize",s)),f);return l?ee(l)?R(r,n,l.denialReasons?.[0]??"verify-access api error",l.correlationId):Z(r,l):R(r,n,"verify-access unreachable within budget")}case"identified":{if(n.depth==="authorize"&&r.artifacts){let u=await T(G(y,Q(e,t,r,!0,s)),f);return u?ee(u)?R(r,n,u.denialReasons?.[0]??"verify-access api error",u.correlationId):Z(r,u):R(r,n,"verify-access unreachable within budget")}await J(e,t,r,d,s,a);let l=v(r);return n.mode==="enforce"&&n.depth!=="classify"&&(l.decision="deny",l.reasons=["agent is not registered with AstraSync"]),l}case"anonymous-bot":return Zt(o.anonymousBeaconRate,e.random)&&await J(e,t,{...r,protocol:r.protocol??"ua-heuristic"},d,s,a),v(r);default:return v(r)}}catch(l){let u=v(r);return u.reasons=[`edge pipeline error \u2014 fail-open: ${l instanceof Error?l.message:String(l)}`],u}}async function de(e,t,r,n){let a=kt(r,t.uri);try{if(a.skip)return{policy:a,application:M()};let i=g(t,"x-forwarded-host")??g(t,"host")??new URL(e.counterpartyUrl).host,o=Mt(t,i,a.depth);if(o.tier==="human"||o.tier==="self")return{policy:a,classification:o,application:M()};let s=await er(e,t,o,a,n,{sampling:a.sampling??r.sampling,queryValueAllowlist:r.queryValueAllowlist});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:jt(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:M()}}}function U(e){return e.Records[0].cf.request}function A(e,t){return e.headers[t.toLowerCase()]?.[0]?.value}function tr(e){let t={};for(let[r,n]of Object.entries(e))n?.length&&(t[r]=n.map(a=>a.value).join(", "));return t}function rr(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 ue(e){let t=e?.trim();if(!t)return;let r=/^\[([^\]]+)\](?::(\d+))?$/.exec(t);if(r)return{ip:r[1],...r[2]&&{port:r[2]}};let n=/:(\d+)$/.exec(t),a=n?t.slice(0,-n[0].length):t;if(a)return{ip:a,...n&&{port:n[1]}}}function nr(e){let t={},r=(i,o,s)=>{let c=A(e,o);c&&(t[i]=s?s(c):c)},n=ue(A(e,"cloudfront-viewer-address"));n&&(t.ip=n.ip,n.port&&(t.sourcePort=n.port)),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("latitude","cloudfront-viewer-latitude"),r("longitude","cloudfront-viewer-longitude"),r("postalCode","cloudfront-viewer-postal-code"),r("asn","cloudfront-viewer-asn"),r("tlsVersion","cloudfront-viewer-tls");{let i=A(e,"cloudfront-viewer-tls")?.split(":")[1];i&&(t.tlsCipher=i)}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"]])A(e,o)==="true"&&a.push(i);return a.length&&(t.device=a.join(",")),t}function le(e){let t=rr(e),r=nr(e),n=ue(A(e,"cloudfront-viewer-address"))?.ip;return{method:e.method,uri:e.uri,querystring:e.querystring??"",headers:tr(e.headers),...t.text!==void 0&&{bodyText:t.text},...t.truncated&&{bodyTruncated:!0},...n&&{clientIp:n},...Object.keys(r).length>0&&{connection:r}}}function pe(e,t){if(t.action==="respond")return or(t.status,t.bodyJson,t.headers);for(let r of t.stripHeaderPrefixes)ar(e,r);for(let[r,n]of Object.entries(t.injectHeaders))F(e,r,n);return e}function fe(e){let t=e.origin?.custom?.domainName??e.origin?.s3?.domainName??void 0;if(!t)return;let r=A(e,"host");!r||r===t||(A(e,"x-forwarded-host")||F(e,"X-Forwarded-Host",r),F(e,"Host",t))}function F(e,t,r){e.headers[t.toLowerCase()]=[{key:t,value:r}]}function ar(e,t){let r=t.toLowerCase();for(let n of Object.keys(e.headers))n.startsWith(r)&&delete e.headers[n]}function or(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 sr="5.14.0",_=`astrasync-sdk/${sr}`;function ir(e){if(!e)return{"user-agent":_};if(e instanceof Headers){let r=new Headers(e);return r.has("user-agent")||r.set("user-agent",_),r}return Array.isArray(e)?e.some(([n])=>n.toLowerCase()==="user-agent")?e:[...e,["user-agent",_]]:Object.keys(e).some(r=>r.toLowerCase()==="user-agent")?e:{"user-agent":_,...e}}var cr=(e,t)=>fetch(e,{...t,headers:ir(t?.headers)}),$=Object.freeze({version:1,mode:"observe",depth:"classify",pathRules:[],sampling:Object.freeze({anonymousBeaconRate:1}),failurePosture:"open"}),ge=["observe","enforce"],ye=["classify","authenticate","authorize"];function he(e){if(typeof e!="object"||e===null)return!1;let t=e.anonymousBeaconRate;return typeof t=="number"&&t>=0&&t<=1}function dr(e){if(typeof e!="object"||e===null)return!1;let t=e;if(t.version!==1||!ge.includes(t.mode)||!ye.includes(t.depth)||!Array.isArray(t.pathRules))return!1;for(let r of t.pathRules){if(typeof r!="object"||r===null)return!1;let n=r;if(typeof n.pattern!="string"||n.pattern.length===0||n.mode!==void 0&&!ge.includes(n.mode)||n.depth!==void 0&&!ye.includes(n.depth)||n.sampling!==void 0&&!he(n.sampling))return!1}return!he(t.sampling)||t.queryValueAllowlist!==void 0&&(!Array.isArray(t.queryValueAllowlist)||t.queryValueAllowlist.length>16||!t.queryValueAllowlist.every(r=>typeof r=="string"&&r.length>0&&r.length<=64))?!1:t.failurePosture==="open"}async function ur(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 cr(`${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 dr(o)?{edgeConfig:o,etag:a.headers.get("etag")??void 0}:null}catch{return null}}var lr=60*1e3,pr=1440*60*1e3,S=new Map,B=new Map;function ve(e,t){return`${e}|${t}`}function fr(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 me(e,t){let r=ve(e.apiBaseUrl,t),n=B.get(r);if(n)return n;let a=ur(e,t,{etag:S.get(r)?.etag}).then(i=>{if(i===null)return;let o=Date.now();if("notModified"in i){let s=S.get(r);s&&(s.lastSuccessAt=o);return}S.set(r,{config:i.edgeConfig,etag:i.etag,lastSuccessAt:o})}).finally(()=>{B.delete(r)});return B.set(r,a),a}async function Ae(e,t,r){let n=r?.ttlMs??lr,a=r?.maxStaleMs??pr,i=ve(e.apiBaseUrl,t),o=S.get(i);if(o){let s=Date.now()-o.lastSuccessAt;return s>n&&me(e,t).catch(()=>{}),s>a?fr(o.config):o.config}return await me(e,t).catch(()=>{}),S.get(i)?.config??$}var gr=900*1e3,we=new Map;async function yr(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 Se(e){if(e.apiKey)return e.apiKey;let t=e.apiKeySecretName;if(!t)return;let r=we.get(t);if(r&&Date.now()-r.fetchedAt<gr)return r.value;try{let a=await(e.fetchSecret??yr)(t);return we.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 Ie(e,t){try{return await Ae({apiBaseUrl:e.apiBaseUrl,apiKey:t},e.counterpartyId,{ttlMs:e.configTtlMs})}catch{return e.fallbackConfig??$}}function be(e){let t=e.log??((r,n,a)=>{console[r==="info"?"log":r](`[astrasync-edge] ${n}`,a??"")});return async function(n){let a=U(n);try{fe(a);let i=await Se({...e,log:t}),o=await Ie(e,i),{application:s}=await de({...e,adapter:"lambda-edge",log:t},le(a),o,i);return pe(a,s)}catch(i){return t("error","edge handler error \u2014 passing request through",{error:i instanceof Error?i.message:String(i)}),a}}}var hr={apiBaseUrl:"x-astrasync-api-base-url",publicBaseUrl:"x-astrasync-public-base-url",counterpartyId:"x-astrasync-counterparty-id",counterpartyUrl:"x-astrasync-counterparty-url",apiKeySecretName:"x-astrasync-api-key-secret-name"};function mr(e,t){return e.origin?.custom?.customHeaders?.[t]?.[0]?.value||void 0}function Re(e,t){let r=n=>mr(e,hr[n]);return{apiBaseUrl:r("apiBaseUrl")??t.apiBaseUrl,publicBaseUrl:r("publicBaseUrl")??t.publicBaseUrl,counterpartyId:r("counterpartyId")??t.counterpartyId,counterpartyUrl:r("counterpartyUrl")??t.counterpartyUrl,apiKeySecretName:r("apiKeySecretName")??t.apiKeySecretName}}function Ce(e){return[e.apiBaseUrl,e.publicBaseUrl??"",e.counterpartyId,e.counterpartyUrl,e.apiKeySecretName??""].join("|")}var vr={apiBaseUrl:"https://astrasync.ai/api",publicBaseUrl:void 0,counterpartyId:"",counterpartyUrl:"",apiKeySecretName:void 0},Ee=new Map,Te=!1,Ar=async e=>{let t=Re(U(e),vr);(!t.counterpartyId||!t.counterpartyUrl)&&(Te||(Te=!0,console.error("[astrasync-edge] counterpartyId/counterpartyUrl missing \u2014 set the X-AstraSync-* origin custom headers on the distribution (see infra/template.yaml) or bake ASTRASYNC_* constants via infra/deploy.sh.")));let r=Ce(t),n=Ee.get(r);return n||(n=be(t),Ee.set(r,n)),n(e)};0&&(module.exports={handler});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrasyncai/adapter-lambda",
3
- "version": "2.9.0",
3
+ "version": "2.11.0",
4
4
  "description": "AstraSync Trusted Agent Gateway — Lambda@Edge/CloudFront adapter",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -47,7 +47,7 @@
47
47
  "directory": "packages/adapter-lambda"
48
48
  },
49
49
  "dependencies": {
50
- "@astrasyncai/verification-gateway": "^5.10.0",
50
+ "@astrasyncai/verification-gateway": "^5.14.0",
51
51
  "@aws-sdk/client-secrets-manager": "^3.632.0"
52
52
  },
53
53
  "devDependencies": {