@astrasyncai/adapter-lambda 2.8.0 → 2.10.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 +14 -9
- package/dist/index.d.ts +14 -9
- package/dist/index.js +32 -12
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +32 -12
- package/dist/index.mjs.map +1 -1
- package/dist/lambda/index.js +1 -1
- package/infra/template.yaml +24 -1
- package/package.json +3 -3
package/dist/index.d.mts
CHANGED
|
@@ -138,8 +138,12 @@ declare function detectCommerce(request: CloudFrontRequest, host: string, body:
|
|
|
138
138
|
* credential the BACKEND rejected is a deny, but an unreachable backend is
|
|
139
139
|
* not a rejection).
|
|
140
140
|
*/
|
|
141
|
-
declare function runTier(options: EdgeAdapterOptions, request: CloudFrontRequest, classification: Classification, policy: EffectivePolicy, apiKey: string | undefined,
|
|
142
|
-
|
|
141
|
+
declare function runTier(options: EdgeAdapterOptions, request: CloudFrontRequest, classification: Classification, policy: EffectivePolicy, apiKey: string | undefined, capture: {
|
|
142
|
+
sampling: {
|
|
143
|
+
anonymousBeaconRate: number;
|
|
144
|
+
};
|
|
145
|
+
/** VI uplift Part 4 — owner-configured query-VALUE capture (utm_*). */
|
|
146
|
+
queryValueAllowlist?: string[];
|
|
143
147
|
}): Promise<TierOutcome>;
|
|
144
148
|
|
|
145
149
|
/**
|
|
@@ -165,18 +169,19 @@ declare function applyOutcome(options: EdgeAdapterOptions, request: CloudFrontRe
|
|
|
165
169
|
/**
|
|
166
170
|
* Time-budgeted event emission — CloudFront wrapper over the edge core's
|
|
167
171
|
* telemetry. Lambda@Edge has no `waitUntil`, so every network call is
|
|
168
|
-
* awaited under an explicit budget
|
|
169
|
-
*
|
|
170
|
-
* 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.
|
|
171
174
|
*/
|
|
172
175
|
|
|
173
176
|
/**
|
|
174
177
|
* Emit an unregistered-attempt beacon for `identified` (non-crypto depths)
|
|
175
|
-
* and sampled `anonymous-bot` traffic.
|
|
176
|
-
*
|
|
177
|
-
*
|
|
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.
|
|
178
183
|
*/
|
|
179
|
-
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>;
|
|
180
185
|
|
|
181
186
|
/**
|
|
182
187
|
* Runtime configuration for the edge shell.
|
package/dist/index.d.ts
CHANGED
|
@@ -138,8 +138,12 @@ declare function detectCommerce(request: CloudFrontRequest, host: string, body:
|
|
|
138
138
|
* credential the BACKEND rejected is a deny, but an unreachable backend is
|
|
139
139
|
* not a rejection).
|
|
140
140
|
*/
|
|
141
|
-
declare function runTier(options: EdgeAdapterOptions, request: CloudFrontRequest, classification: Classification, policy: EffectivePolicy, apiKey: string | undefined,
|
|
142
|
-
|
|
141
|
+
declare function runTier(options: EdgeAdapterOptions, request: CloudFrontRequest, classification: Classification, policy: EffectivePolicy, apiKey: string | undefined, capture: {
|
|
142
|
+
sampling: {
|
|
143
|
+
anonymousBeaconRate: number;
|
|
144
|
+
};
|
|
145
|
+
/** VI uplift Part 4 — owner-configured query-VALUE capture (utm_*). */
|
|
146
|
+
queryValueAllowlist?: string[];
|
|
143
147
|
}): Promise<TierOutcome>;
|
|
144
148
|
|
|
145
149
|
/**
|
|
@@ -165,18 +169,19 @@ declare function applyOutcome(options: EdgeAdapterOptions, request: CloudFrontRe
|
|
|
165
169
|
/**
|
|
166
170
|
* Time-budgeted event emission — CloudFront wrapper over the edge core's
|
|
167
171
|
* telemetry. Lambda@Edge has no `waitUntil`, so every network call is
|
|
168
|
-
* awaited under an explicit budget
|
|
169
|
-
*
|
|
170
|
-
* 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.
|
|
171
174
|
*/
|
|
172
175
|
|
|
173
176
|
/**
|
|
174
177
|
* Emit an unregistered-attempt beacon for `identified` (non-crypto depths)
|
|
175
|
-
* and sampled `anonymous-bot` traffic.
|
|
176
|
-
*
|
|
177
|
-
*
|
|
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.
|
|
178
183
|
*/
|
|
179
|
-
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>;
|
|
180
185
|
|
|
181
186
|
/**
|
|
182
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
|
|
99
|
-
if (
|
|
100
|
-
out.ip =
|
|
101
|
-
|
|
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")?.
|
|
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
|
-
|
|
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
|
|
@@ -290,14 +302,15 @@ function detectCommerce(request, host, body) {
|
|
|
290
302
|
|
|
291
303
|
// src/pipeline.ts
|
|
292
304
|
var import_edge_core5 = require("@astrasyncai/verification-gateway/edge-core");
|
|
293
|
-
async function runTier(options, request, classification, policy, apiKey,
|
|
305
|
+
async function runTier(options, request, classification, policy, apiKey, capture) {
|
|
294
306
|
return (0, import_edge_core5.runTier)(
|
|
295
|
-
|
|
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,
|
|
299
312
|
apiKey,
|
|
300
|
-
|
|
313
|
+
capture
|
|
301
314
|
);
|
|
302
315
|
}
|
|
303
316
|
|
|
@@ -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)(
|
|
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 sampling: { anonymousBeaconRate: number }\n): Promise<TierOutcome> {\n return runTierCore(\n options,\n toNormalizedRequest(request),\n classification,\n policy,\n apiKey,\n sampling\n );\n}\n","/**\n * Decision → CloudFront result wiring (enforcement + ASAT trust-attestation\n * headers). The decision logic (observe shadow stamps, misconfig fail-open,\n * enforce-mode response bodies) lives in the edge core; this module\n * executes the core's abstract outcome application against CloudFront\n * request/response primitives.\n */\n\nimport type { CloudFrontRequest, CloudFrontRequestResult } from 'aws-lambda';\nimport {\n applyOutcome as resolveOutcomeApplication,\n ASAT_HEADERS,\n} from '@astrasyncai/verification-gateway/edge-core';\nimport { applyCoreOutcome } from './cf';\nimport type { EdgeAdapterOptions, EffectivePolicy, TierOutcome } from './types';\n\nexport { ASAT_HEADERS };\n\n/**\n * Apply the outcome. Always strips inbound spoofed `x-astrasync-*` headers\n * first (both modes — trust headers must only ever originate here).\n *\n * Observe mode: returns the request untouched apart from header hygiene and\n * an `X-AstraSync-Observed-Decision` stamp for origin-side log correlation.\n *\n * Enforce mode: ALLOW → request + ASAT headers; DENY → 403 machine-readable\n * body; MANUAL_REVIEW → 202 with step-up poll info.\n */\nexport function applyOutcome(\n options: EdgeAdapterOptions,\n request: CloudFrontRequest,\n outcome: TierOutcome,\n policy: EffectivePolicy\n): CloudFrontRequestResult {\n return applyCoreOutcome(request, resolveOutcomeApplication(options, outcome, policy));\n}\n","/**\n * Protocol-specific machine-readable messaging for enforce-mode\n * responses. The message catalogue and body builders live in the edge core\n * (shared across every edge platform adapter); re-exported here so the\n * adapter's public API is unchanged.\n */\n\nexport {\n buildDenyBody,\n buildManualReviewBody,\n type DenyBody,\n type ManualReviewBody,\n} from '@astrasyncai/verification-gateway/edge-core';\n","/**\n * Time-budgeted event emission — CloudFront wrapper over the edge core's\n * telemetry. Lambda@Edge has no `waitUntil`, so every network call is\n * awaited under an explicit budget and DROPPED on timeout (the core's\n * default posture): latency to the customer's traffic is bounded, and a\n * slow AstraSync API costs telemetry, never page loads.\n */\n\nimport type { CloudFrontRequest } from 'aws-lambda';\nimport {\n emitBeacon as emitBeaconCore,\n withBudget,\n sampled,\n} from '@astrasyncai/verification-gateway/edge-core';\nimport { toNormalizedRequest } from './cf';\nimport type { Classification, EdgeAdapterOptions } from './types';\n\nexport { withBudget, sampled };\n\n/**\n * Emit an unregistered-attempt beacon for `identified` (non-crypto depths)\n * and sampled `anonymous-bot` traffic. Unauthenticated by design\n * (CSRF-exempt on the backend); the backend fingerprints the UA and\n * find-or-provisions orphan platform agents server-side.\n */\nexport async function emitBeacon(\n options: EdgeAdapterOptions,\n request: CloudFrontRequest,\n classification: Classification,\n budgetMs: number\n): Promise<void> {\n await emitBeaconCore(options, toNormalizedRequest(request), classification, budgetMs);\n}\n"],"mappings":"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","names":["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
|
|
41
|
-
if (
|
|
42
|
-
out.ip =
|
|
43
|
-
|
|
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")?.
|
|
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
|
-
|
|
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
|
|
@@ -241,14 +253,15 @@ function detectCommerce(request, host, body) {
|
|
|
241
253
|
|
|
242
254
|
// src/pipeline.ts
|
|
243
255
|
import { runTier as runTierCore } from "@astrasyncai/verification-gateway/edge-core";
|
|
244
|
-
async function runTier(options, request, classification, policy, apiKey,
|
|
256
|
+
async function runTier(options, request, classification, policy, apiKey, capture) {
|
|
245
257
|
return runTierCore(
|
|
246
|
-
|
|
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,
|
|
250
263
|
apiKey,
|
|
251
|
-
|
|
264
|
+
capture
|
|
252
265
|
);
|
|
253
266
|
}
|
|
254
267
|
|
|
@@ -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(
|
|
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,
|
package/dist/index.mjs.map
CHANGED
|
@@ -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 sampling: { anonymousBeaconRate: number }\n): Promise<TierOutcome> {\n return runTierCore(\n options,\n toNormalizedRequest(request),\n classification,\n policy,\n apiKey,\n sampling\n );\n}\n","/**\n * Decision → CloudFront result wiring (enforcement + ASAT trust-attestation\n * headers). The decision logic (observe shadow stamps, misconfig fail-open,\n * enforce-mode response bodies) lives in the edge core; this module\n * executes the core's abstract outcome application against CloudFront\n * request/response primitives.\n */\n\nimport type { CloudFrontRequest, CloudFrontRequestResult } from 'aws-lambda';\nimport {\n applyOutcome as resolveOutcomeApplication,\n ASAT_HEADERS,\n} from '@astrasyncai/verification-gateway/edge-core';\nimport { applyCoreOutcome } from './cf';\nimport type { EdgeAdapterOptions, EffectivePolicy, TierOutcome } from './types';\n\nexport { ASAT_HEADERS };\n\n/**\n * Apply the outcome. Always strips inbound spoofed `x-astrasync-*` headers\n * first (both modes — trust headers must only ever originate here).\n *\n * Observe mode: returns the request untouched apart from header hygiene and\n * an `X-AstraSync-Observed-Decision` stamp for origin-side log correlation.\n *\n * Enforce mode: ALLOW → request + ASAT headers; DENY → 403 machine-readable\n * body; MANUAL_REVIEW → 202 with step-up poll info.\n */\nexport function applyOutcome(\n options: EdgeAdapterOptions,\n request: CloudFrontRequest,\n outcome: TierOutcome,\n policy: EffectivePolicy\n): CloudFrontRequestResult {\n return applyCoreOutcome(request, resolveOutcomeApplication(options, outcome, policy));\n}\n","/**\n * Protocol-specific machine-readable messaging for enforce-mode\n * responses. The message catalogue and body builders live in the edge core\n * (shared across every edge platform adapter); re-exported here so the\n * adapter's public API is unchanged.\n */\n\nexport {\n buildDenyBody,\n buildManualReviewBody,\n type DenyBody,\n type ManualReviewBody,\n} from '@astrasyncai/verification-gateway/edge-core';\n","/**\n * Time-budgeted event emission — CloudFront wrapper over the edge core's\n * telemetry. Lambda@Edge has no `waitUntil`, so every network call is\n * awaited under an explicit budget and DROPPED on timeout (the core's\n * default posture): latency to the customer's traffic is bounded, and a\n * slow AstraSync API costs telemetry, never page loads.\n */\n\nimport type { CloudFrontRequest } from 'aws-lambda';\nimport {\n emitBeacon as emitBeaconCore,\n withBudget,\n sampled,\n} from '@astrasyncai/verification-gateway/edge-core';\nimport { toNormalizedRequest } from './cf';\nimport type { Classification, EdgeAdapterOptions } from './types';\n\nexport { withBudget, sampled };\n\n/**\n * Emit an unregistered-attempt beacon for `identified` (non-crypto depths)\n * and sampled `anonymous-bot` traffic. Unauthenticated by design\n * (CSRF-exempt on the backend); the backend fingerprints the UA and\n * find-or-provisions orphan platform agents server-side.\n */\nexport async function emitBeacon(\n options: EdgeAdapterOptions,\n request: CloudFrontRequest,\n classification: Classification,\n budgetMs: number\n): Promise<void> {\n await emitBeaconCore(options, toNormalizedRequest(request), classification, budgetMs);\n}\n"],"mappings":"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","names":[]}
|
|
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":[]}
|
package/dist/lambda/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var Re=Object.create;var R=Object.defineProperty;var Se=Object.getOwnPropertyDescriptor;var Ie=Object.getOwnPropertyNames;var Ce=Object.getPrototypeOf,Ee=Object.prototype.hasOwnProperty;var Te=(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 Ie(t))!Ee.call(e,a)&&a!==r&&R(e,a,{get:()=>t[a],enumerable:!(n=Se(t,a))||n.enumerable});return e};var Ue=(e,t,r)=>(r=e!=null?Re(Ce(e)):{},B(t||!e||!e.__esModule?R(r,"default",{value:e,enumerable:!0}):r,e)),Pe=e=>B(R({},"__esModule",{value:!0}),e);var er={};Te(er,{handler:()=>qt});module.exports=Pe(er);var Q="5.7.0",S=`astrasync-sdk/${Q}`;function _e(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:_e(t?.headers)}),rr=Object.freeze({version:1,mode:"observe",depth:"classify",pathRules:[],sampling:Object.freeze({anonymousBeaconRate:.1}),failurePosture:"open"}),nr=60*1e3,ar=1440*60*1e3;function xe(e,t){for(let r of e)if(ke(r.pattern).test(t))return r}function ke(e){let t=e.replace(/[.+?^${}()|[\]\\]/g,"\\$&").replace(/\*/g,".*");return new RegExp(`^${t}$`,"i")}var Oe=1,Me=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"]),Fe=["x-astrasync-","kya-","kya_"],De=new Set(["authorization","x-api-key","x-goog-api-key","api-key"]);function Ne(e){return e==="signature"||e.endsWith("-signature")||e.endsWith("-signature-256")}var Be=new Set(["openai-organization","openai-project","openai-version","openai-processing-ms","anthropic-version","anthropic-beta","x-goog-user-project","x-goog-api-client","user-agent","x-agent-card-url","x-agent-provider"]),He=["x-goog-","x-stainless-","x-anthropic-","x-openai-","sec-ch-"],je=["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-"],$e=64,H=1024,Le=16*1024;function _(e){let t=0;for(let r=0;r<e.length;r++){let n=e.charCodeAt(r);n<128?t+=1:n<2048?t+=2:n>=55296&&n<=56319?(t+=4,r++):t+=3}return t}function Ke(e){if(!e)return;let t=e.trim(),r=t.indexOf(" ");if(r>0){let a=t.slice(0,r).toLowerCase();(a==="bearer"||a==="basic"||a==="token")&&(t=t.slice(r+1).trim())}let n=t.toLowerCase();for(let a of je)if(n.startsWith(a))return a}function Ve(e){return!!(Me.has(e)||Fe.some(t=>e.startsWith(t))||Ne(e))}function Ge(e){return Be.has(e)?!0:He.some(t=>e.startsWith(t))}function q(e){let t={},r={},n,a,i=0,o=0;if(!e)return{headers:t};for(let c of Object.keys(e)){if(o>=$e)break;let p=c.toLowerCase(),u=e[c];if(u===void 0)continue;let y=Array.isArray(u)?u.join(", "):String(u);if(De.has(p)){let f=Ke(y);f&&!n&&(n=f);continue}if(p==="cookie"&&(a=y.split(";").filter(f=>f.trim().length>0).length),Ve(p))continue;let h=y;_(h)>H&&(h=h.slice(0,H));let l=_(p)+_(h);i+l>Le||(i+=l,o++,t[p]=h,Ge(p)&&(r[p]=h))}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 ze=[{vendor:"claude",displayName:"Anthropic Claude",uaPatterns:[/claude/i,/anthropic/i],agentCardPatterns:[/anthropic\.com/i,/claude\.ai/i],apiKeyFormatPatterns:[/^sk-ant/i],headerPatterns:[/^anthropic-version/i,/^anthropic-beta/i,/^x-anthropic-/i]},{vendor:"chatgpt",displayName:"OpenAI ChatGPT",uaPatterns:[/chatgpt/i,/openai/i,/gpt-?\d/i],agentCardPatterns:[/openai\.com/i,/chatgpt\.com/i],apiKeyFormatPatterns:[/^sk-proj/i,/^sk-svcacct/i],headerPatterns:[/^openai-organization/i,/^openai-project/i,/^openai-version/i]},{vendor:"gemini",displayName:"Google Gemini",uaPatterns:[/gemini/i,/google-gemini/i,/bard/i],agentCardPatterns:[/gemini\.google\.com/i,/ai\.google\.dev/i],apiKeyFormatPatterns:[/^aiza/i],headerPatterns:[/^x-goog-user-project/i,/^x-goog-api-client/i]},{vendor:"cursor",displayName:"Cursor",uaPatterns:[/cursor/i],agentCardPatterns:[/cursor\.sh/i,/cursor\.com/i]},{vendor:"goose",displayName:"Block Goose",uaPatterns:[/goose/i,/block-goose/i],agentCardPatterns:[/block\.xyz\/goose/i,/goose\.tools/i]},{vendor:"perplexity",displayName:"Perplexity",uaPatterns:[/perplexity/i],agentCardPatterns:[/perplexity\.ai/i],apiKeyFormatPatterns:[/^pplx-/i]},{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-sdk",displayName:"AstraSync SDK",uaPatterns:[/^astrasync-sdk\//i]}];function Xe(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,p]of Object.entries(t.platformHeaders))s.push(c,`${c}: ${p}`);for(let c of e){let p=c.uaPatterns?.some(d=>d.test(r)),u=c.agentCardPatterns?.some(d=>d.test(n)),y=a.length>0&&c.apiKeyFormatPatterns?.some(d=>d.test(a)),h=s.length>0&&c.headerPatterns?.some(d=>s.some(m=>d.test(m))),l=i.length>0&&c.ja4Patterns?.some(d=>d.test(i)),f=o.length>0&&c.asnList?.includes(o);if(p||u||y||h||l||f){let d=r.split(/[\s/]/)[0]?.toLowerCase()??"",m=(()=>{try{return n?new URL(n).host:""}catch{return""}})(),P=d||m||a||i||o&&`asn${o}`||"unknown";return{vendor:c.vendor,fingerprintKey:`${c.vendor}:${P}`,displayName:c.displayName}}}}function j(e){return Xe(ze,e)}function Ye(e){return e>=80?"PLATINUM":e>=60?"GOLD":e>=40?"SILVER":"BRONZE"}var or=3600*1e3,Je=new Map;function We(e){return Je.get(e)?.data}var ee={apiBaseUrl:"https://astrasync.ai/api",debug:!1},te=!1,$=!1;async function L(e,t,r,n){te=!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(()=>({})),p=`[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(`${p} (strictInit=true)`);console.error(p)}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 re(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 Ze(e,t){let r=re(e,t),n=k.get(r);return n&&n.expiresAt>Date.now()?n.result:(n&&k.delete(r),null)}var Qe=60;function qe(e,t,r,n){let a=r&&r>0?r:Qe,i=re(e,n);k.set(i,{result:t,expiresAt:Date.now()+a*1e3})}function ne(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 et(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 tt(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=Q,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();return o.status===410?{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."}]}}: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={...ee,...e},a=n.apiBaseUrl?We(n.apiBaseUrl):void 0;if(!te&&!n.disableInitChecks&&n.apiBaseUrl&&(n.strictInit?await L(n.apiBaseUrl,n.debug,!0,n.apiKey):L(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 d=Ze(t,n.counterpartyId);if(d){n.debug&&console.log("[VerificationGateway] Returning cached result");let m={...d};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 tt(n,i),s=o.denialReasons,c=Array.isArray(s)&&s.length>0?s:void 0;if(!o.success)return et(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 d=o.access?.failures;return{identityVerified:o.verificationContext?.idVerified===!0,policyAllowed:!1,denialReasons:c??(d&&d.length>0?d.map(be=>be.message):o.access?.reason?[o.access.reason]:["Access denied"]),failures:d,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 p=o.agent?{astraId:o.agent.astraId,name:o.agent.name,trustScore:o.agent.trustScore,trustLevel:Ye(o.agent.trustScore),blockchainVerified:o.agent.blockchainStatus==="verified",status:o.agent.agentStatus}:void 0,u=o.developer?{astradId:o.developer.kyaOwnerId,name:o.developer.fullName,trustScore:o.developer.trustScore||0,verified:o.developer.identityVerified}:void 0,y=o.organization?{name:o.organization.name,verified:o.organization.verified,trustScore:o.organization.trustScore}:void 0,h=o.verificationContext,l={identityVerified:o.verificationContext?.idVerified===!0,policyAllowed:!0,agent:p,developer:u,organization:y,appliedPolicy:o.access?.appliedPolicy,verificationContext:h,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};l.recommendation==="deny"?(l.policyAllowed=!1,l.denialReasons=c??l.recommendationReasons??["Access denied by AstraSync recommendation"],l.guidance=l.runtimeChallenge?{message:`Verification failed: ${l.runtimeChallenge.reason||"runtime challenge failed"}`,registrationUrl:a?.registrationUrl??"",documentationUrl:a?.documentationUrl??""}:{message:l.recommendationReasons?.[0]||"Access denied by AstraSync recommendation",registrationUrl:a?.registrationUrl??"",documentationUrl:a?.documentationUrl??""}):l.recommendation==="step_up_required"&&(l.requiresStepUp=!0,l.denialReasons=c??l.recommendationReasons??["Step-up verification required"]);let f=l.requiresStepUp===!0||l.requiresApproval===!0||l.recommendation==="step_up_required";return n.cacheTtl!==0&&l.recommendation!=="deny"&&!f&&qe(t,l,n.cacheTtl,n.counterpartyId),l}async function rt(e,t){let r=e.apiBaseUrl||ee.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 g(e,t){return e.headers[t.toLowerCase()]}function nt(e,t){let r=e.querystring?`?${e.querystring}`:"";return`https://${t}${e.uri}${r}`}function M(e){let{headers:t,apiKeyFormat:r,platformHeaders:n,cookieCount:a}=q(e.headers),i=e.connection??{},o=it(e.querystring);return{headers:t,...r&&{apiKeyFormat:r},...n&&{platformHeaders:n},...Object.keys(i).length>0&&{connection:i},...a!==void 0&&{cookieCount:a},...o&&{queryKeys:o},schemaVersion:Oe}}var at=32,ot=64;function it(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,ot);if(a&&!r.includes(a)&&(r.push(a),r.length>=at))break}return r.length>0?r:void 0}var st=/^eyJ[\w-]+\.[\w-]*\.[\w-]+~/;function ct(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 dt(e){try{let t=e.split(".")[0];return JSON.parse(ct(t))}catch{return}}function ut(e){if(e)try{let t=JSON.parse(e);return typeof t=="object"&&t!==null?t:void 0}catch{return}}function lt(e,t){let r=nt(e,t),n=e.method,a=e.headers,i=ut(e.bodyText),o=e.bodyTruncated?{bodyTruncated:!0}:{},s=g(e,"authorization"),c=s?.replace(/^Bearer\s+/i,""),u=[g(e,"x-payment-mandate")??g(e,"payment-mandate"),c].find(f=>f&&st.test(f));if(u){let f=dt(u)?.typ;return{protocol:"vi-sd-jwt",artifacts:{viSdJwt:u},evidence:{typ:f,...o}}}let y=g(e,"signature"),h=g(e,"signature-input");if(y&&h){let f=g(e,"signature-agent"),d=g(e,"x-agent-registry"),m=h.toLowerCase();return{protocol:f?"rfc9421-web-bot-auth":d?.toLowerCase().includes("visa")||m.includes("visa")?"rfc9421-tap":d?.toLowerCase().includes("mastercard")||m.includes("mastercard")?"rfc9421-agent-pay":"rfc9421",artifacts:{rfc9421:{headers:a,method:n,url:r,...e.bodyText&&{body:e.bodyText}}},evidence:{...f&&{signatureAgent:f},...d&&{registryHint:d},keyid:h.match(/keyid="([^"]+)"/)?.[1],...o}}}let l=g(e,"timestamp");if(y&&!h&&(l||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:!!l,...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 f=i.intent_mandate??i.intentMandate,d=i.cart_mandate??i.cartMandate,m=i.payment_mandate??i.paymentMandate;if(typeof f=="string"||typeof d=="string"||typeof m=="string")return{protocol:"ap2",artifacts:{ap2Mandates:{...typeof f=="string"&&{intent:f},...typeof d=="string"&&{cart:d},...typeof m=="string"&&{payment:m}}},evidence:{mandatesPresent:[typeof f=="string"&&"intent",typeof d=="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 pt=/\.(css|js|mjs|map|png|jpe?g|gif|webp|avif|svg|ico|woff2?|ttf|otf|eot|mp4|webm|pdf|txt|xml)$/i,ft=/^astrasync-sdk\//i,gt=/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,yt=/mozilla\/\d/i,mt=/^kya_[A-Za-z0-9_-]+$/;function V(e){return typeof e=="string"&&mt.test(e)}function ht(e,t){if(pt.test(t))return{mode:e.mode,depth:e.depth,skip:!0};let r=xe(e.pathRules,t);return r?{mode:r.mode??e.mode,depth:r.depth??e.depth,skip:r.skip===!0}:{mode:e.mode,depth:e.depth,skip:!1}}function vt(e,t,r){let n=g(e,"user-agent")??"";if(ft.test(n))return{tier:"self"};let a=ne(e.headers,{});if(!a.astraId&&(V(a.jwt)||V(a.apiKey)))return{tier:"self",evidence:{firstPartyApiKey:!0}};if(a.astraId||a.apiKey||a.jwt)return{tier:"verified",protocol:"x-astra"};let{apiKeyFormat:i,platformHeaders:o}=q(e.headers),s={userAgent:n,apiKeyFormat:i,platformHeaders:o};if(r!=="classify"){let p=lt(e,t);if(p)return{tier:"identified",protocol:p.protocol,artifacts:p.artifacts,evidence:p.evidence,fingerprint:j(s)}}let c=j(s);return c?{tier:"identified",protocol:"platform-ua",fingerprint:c}:!n||gt.test(n)||!yt.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 At={"vi-sd-jwt":"This request presented Visa Intelligent Commerce credentials, but the agent is not verified with AstraSync. Register the agent to pair your VI credentials with a verified identity and mandate.","rfc9421-agent-pay":"This request carried a Mastercard Agent Pay signature, but the agent is not verified with AstraSync. Register the agent to pair Agent Pay credentials with a verified identity and mandate.","rfc9421-tap":"This request carried a Visa TAP signature, but the agent is not verified with AstraSync. Register the agent to pair TAP credentials with a verified identity and mandate.","rfc9421-web-bot-auth":"This request carried a Web Bot Auth signature, but the agent is not verified with AstraSync. Register the agent to link your signed-agent identity to a verified principal.",rfc9421:"This request carried an RFC 9421 HTTP message signature, but the agent is not verified with AstraSync. Register the agent to link the signing identity to a verified principal.",acp:"This request used the Agentic Commerce Protocol, but the agent is not verified with AstraSync. Register the agent to transact on this property.",ucp:"This request used a UCP checkout session, but the agent is not verified with AstraSync. Register the agent to transact on this property.",ap2:"This request presented AP2 mandates, but the agent is not verified with AstraSync. Register the agent to pair your mandates with a verified identity.",mpp:"This request presented MPP payment credentials, but the agent is not verified with AstraSync. Register the agent to transact on this property.",x402:"This request used x402 payment headers, but the agent is not verified with AstraSync. Register the agent to transact on this property.","platform-ua":"This traffic was identified as a platform AI agent without AstraSync credentials. Register the agent to access this property with a verified identity.","x-astra":"The presented AstraSync credentials failed verification or were denied by this property\u2019s access policy."};function wt(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 bt(e,t,r){let n=(r||t).replace(/\/api\/?$/,""),a=e.guidance?.mcp?.endpoint||wt(n),i=(e.protocol&&At[e.protocol])??"This request could not be verified by AstraSync.";return{success:!1,error:{code:e.tier==="verified"?"INSUFFICIENT_ACCESS":"UNAUTHORIZED",message: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 Rt(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 St(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 It="X-Astra-Verified-Hop";function Ct(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"},G=["x-astrasync-","x-astra-verified-hop"];function x(){return{action:"pass",injectHeaders:{},stripHeaderPrefixes:[]}}function Et(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,z(t),X(t)),{action:"pass",injectHeaders:n,stripHeaderPrefixes:G};switch(t.decision){case"deny":return t.unavailable?{action:"respond",status:503,headers:{"Retry-After":"30"},bodyJson:Rt(t)}:{action:"respond",status:403,headers:{},bodyJson:bt(t,e.apiBaseUrl,e.publicBaseUrl)};case"manual_review":return{action:"respond",status:202,headers:{"Retry-After":"30"},bodyJson:St(t)};default:return Object.assign(n,z(t),X(t)),{action:"pass",injectHeaders:n,stripHeaderPrefixes:G}}}function z(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 X(e){let t=e.asat?.agentId;return t?{[It]:Ct({astraId:t,...e.sessionId&&{sessionId:e.sessionId},checkedAt:Date.now()})}:{}}var Tt={GET:"data.read",HEAD:"data.read",OPTIONS:"data.read",POST:"data.write",PUT:"data.write",PATCH:"data.write",DELETE:"data.delete"},Ut="data.write",Pt="data";function _t(e){return Tt[e.toUpperCase()]??Ut}function xt(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 kt(e){let t=e.astraPurpose?xt(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=_t(e.method),n="method_table");let a,i;if(e.routePurpose?(a=e.routePurpose,i="route_config"):e.hasCustomPurposeExtractor?e.customPurpose&&(a=e.customPurpose,i="custom_extractor"):t?(a=t.purpose,i="header"):e.legacyPurpose?(a=e.legacyPurpose,i="legacy_header"):e.queryPurpose&&(a=e.queryPurpose,i="query"),!a){let o=r.indexOf(".");o>0?(a=r.slice(0,o),i="action_derived"):(a=Pt,i="transport_default")}return{purpose:a,action:r,purposeSource:i,actionSource:n}}async function 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 Y(e,t,r,n){let a=O(rt({apiBaseUrl:e.apiBaseUrl,counterpartyId:e.counterpartyId},{counterpartyUrl:e.counterpartyUrl,counterpartyType:"website",sourceIp:t.clientIp,userAgent:g(t,"user-agent"),requestPath:t.uri,requestMethod:t.method,protocol:r.protocol,evidence:r.evidence,observedMetadata:M(t)}),n);if(e.waitUntil){e.waitUntil(a);return}await a}function Ot(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 J(e,t,r,n){let a=kt({method:t.method,astraPurpose:g(t,"x-astra-purpose"),astraAction:g(t,"x-astra-action")}),i=g(t,"x-astra-attempt-id");return{...i&&/^att_[0-9a-f]{32}$/.test(i)&&{attemptId:i},credentials:ne(t.headers,{}),purpose:a.purpose,action:a.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"),observedMetadata:M(t)},...n&&r.artifacts&&{commerceArtifacts:r.artifacts}}}function W(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 Z(e){return(e.failures??[]).some(t=>t.dimension==="verify_access.api_error")===!0&&!e.misconfigured}async function Mt(e,t,r,n,a,i){let o=e.budgets??{},s=o.beaconMs??300,c=n.mode==="enforce"?o.verifyEnforceMs??3e3:o.verifyObserveMs??1500,p={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(p,J(e,t,r,n.depth==="authorize")),c);return u?Z(u)?I(r,n,u.denialReasons?.[0]??"verify-access api error",u.correlationId):W(r,u):I(r,n,"verify-access unreachable within budget")}case"identified":{if(n.depth==="authorize"&&r.artifacts){let y=await O(K(p,J(e,t,r,!0)),c);return y?Z(y)?I(r,n,y.denialReasons?.[0]??"verify-access api error",y.correlationId):W(r,y):I(r,n,"verify-access unreachable within budget")}await Y(e,t,r,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 Ot(i.anonymousBeaconRate,e.random)&&await Y(e,t,{...r,protocol:r.protocol??"ua-heuristic"},s),v(r);default:return v(r)}}catch(u){let y=v(r);return y.reasons=[`edge pipeline error \u2014 fail-open: ${u instanceof Error?u.message:String(u)}`],y}}async function ae(e,t,r,n){let a=ht(r,t.uri);try{if(a.skip)return{policy:a,application:x()};let i=g(t,"x-forwarded-host")??g(t,"host")??new URL(e.counterpartyUrl).host,o=vt(t,i,a.depth);if(o.tier==="human"||o.tier==="self")return{policy:a,classification:o,application:x()};let s=await Mt(e,t,o,a,n,r.sampling);return s.decision!=="allow"&&e.log?.("info",a.mode==="enforce"?`${s.decision.toUpperCase()}: ${t.method} ${t.uri}`:`[OBSERVE] would have been ${s.decision.toUpperCase()}: ${t.method} ${t.uri}`,{tier:s.tier,protocol:s.protocol,reasons:s.reasons}),{policy:a,classification:o,outcome:s,application:Et(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 Ft(e){let t={};for(let[r,n]of Object.entries(e))n?.length&&(t[r]=n.map(a=>a.value).join(", "));return t}function Dt(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 Nt(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 oe(e){let t=Dt(e),r=Nt(e),n=A(e,"cloudfront-viewer-address")?.split(":")[0];return{method:e.method,uri:e.uri,querystring:e.querystring??"",headers:Ft(e.headers),...t.text!==void 0&&{bodyText:t.text},...t.truncated&&{bodyTruncated:!0},...n&&{clientIp:n},...Object.keys(r).length>0&&{connection:r}}}function ie(e,t){if(t.action==="respond")return Ht(t.status,t.bodyJson,t.headers);for(let r of t.stripHeaderPrefixes)Bt(e,r);for(let[r,n]of Object.entries(t.injectHeaders))F(e,r,n);return e}function se(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 Bt(e,t){let r=t.toLowerCase();for(let n of Object.keys(e.headers))n.startsWith(r)&&delete e.headers[n]}function Ht(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.7.0",U=`astrasync-sdk/${jt}`;function $t(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 Lt=(e,t)=>fetch(e,{...t,headers:$t(t?.headers)}),N=Object.freeze({version:1,mode:"observe",depth:"classify",pathRules:[],sampling:Object.freeze({anonymousBeaconRate:.1}),failurePosture:"open"}),ce=["observe","enforce"],de=["classify","authenticate","authorize"];function Kt(e){if(typeof e!="object"||e===null)return!1;let t=e;if(t.version!==1||!ce.includes(t.mode)||!de.includes(t.depth)||!Array.isArray(t.pathRules))return!1;for(let n of t.pathRules){if(typeof n!="object"||n===null)return!1;let a=n;if(typeof a.pattern!="string"||a.pattern.length===0||a.mode!==void 0&&!ce.includes(a.mode)||a.depth!==void 0&&!de.includes(a.depth))return!1}let r=t.sampling;return typeof r!="object"||r===null||typeof r.anonymousBeaconRate!="number"||r.anonymousBeaconRate<0||r.anonymousBeaconRate>1?!1:t.failurePosture==="open"}async function Vt(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 Lt(`${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 Kt(o)?{edgeConfig:o,etag:a.headers.get("etag")??void 0}:null}catch{return null}}var Gt=60*1e3,zt=1440*60*1e3,b=new Map,D=new Map;function le(e,t){return`${e}|${t}`}function Xt(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 ue(e,t){let r=le(e.apiBaseUrl,t),n=D.get(r);if(n)return n;let a=Vt(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(()=>{D.delete(r)});return D.set(r,a),a}async function pe(e,t,r){let n=r?.ttlMs??Gt,a=r?.maxStaleMs??zt,i=le(e.apiBaseUrl,t),o=b.get(i);if(o){let s=Date.now()-o.lastSuccessAt;return s>n&&ue(e,t).catch(()=>{}),s>a?Xt(o.config):o.config}return await ue(e,t).catch(()=>{}),b.get(i)?.config??N}var Yt=900*1e3,fe=new Map;async function Jt(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 ge(e){if(e.apiKey)return e.apiKey;let t=e.apiKeySecretName;if(!t)return;let r=fe.get(t);if(r&&Date.now()-r.fetchedAt<Yt)return r.value;try{let a=await(e.fetchSecret??Jt)(t);return fe.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 ye(e,t){try{return await pe({apiBaseUrl:e.apiBaseUrl,apiKey:t},e.counterpartyId,{ttlMs:e.configTtlMs})}catch{return e.fallbackConfig??N}}function me(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{se(a);let i=await ge({...e,log:t}),o=await ye(e,i),{application:s}=await ae({...e,log:t},oe(a),o,i);return ie(a,s)}catch(i){return t("error","edge handler error \u2014 passing request through",{error:i instanceof Error?i.message:String(i)}),a}}}var Wt={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 Zt(e,t){return e.origin?.custom?.customHeaders?.[t]?.[0]?.value||void 0}function he(e,t){let r=n=>Zt(e,Wt[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 ve(e){return[e.apiBaseUrl,e.publicBaseUrl??"",e.counterpartyId,e.counterpartyUrl,e.apiKeySecretName??""].join("|")}var Qt={apiBaseUrl:"https://astrasync.ai/api",publicBaseUrl:void 0,counterpartyId:"",counterpartyUrl:"",apiKeySecretName:void 0},Ae=new Map,we=!1,qt=async e=>{let t=he(T(e),Qt);(!t.counterpartyId||!t.counterpartyUrl)&&(we||(we=!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=ve(t),n=Ae.get(r);return n||(n=me(t),Ae.set(r,n)),n(e)};0&&(module.exports={handler});
|
|
1
|
+
"use strict";var _e=Object.create;var b=Object.defineProperty;var Ue=Object.getOwnPropertyDescriptor;var Pe=Object.getOwnPropertyNames;var xe=Object.getPrototypeOf,ke=Object.prototype.hasOwnProperty;var Me=(e,t)=>{for(var r in t)b(e,r,{get:t[r],enumerable:!0})},$=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of Pe(t))!ke.call(e,a)&&a!==r&&b(e,a,{get:()=>t[a],enumerable:!(n=Ue(t,a))||n.enumerable});return e};var Oe=(e,t,r)=>(r=e!=null?_e(xe(e)):{},$(t||!e||!e.__esModule?b(r,"default",{value:e,enumerable:!0}):r,e)),De=e=>$(b({},"__esModule",{value:!0}),e);var Ar={};Me(Ar,{handler:()=>vr});module.exports=De(Ar);var D="5.12.0",I=`astrasync-sdk/${D}`;function Ne(e){if(!e)return{"user-agent":I};if(e instanceof Headers){let r=new Headers(e);return r.has("user-agent")||r.set("user-agent",I),r}return Array.isArray(e)?e.some(([n])=>n.toLowerCase()==="user-agent")?e:[...e,["user-agent",I]]:Object.keys(e).some(r=>r.toLowerCase()==="user-agent")?e:{"user-agent":I,...e}}var C=(e,t)=>fetch(e,{...t,headers:Ne(t?.headers)}),Sr=Object.freeze({version:1,mode:"observe",depth:"classify",pathRules:[],sampling:Object.freeze({anonymousBeaconRate:1}),failurePosture:"open"}),br=60*1e3,Ir=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 Le=1,$e=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-"],Xe=64,j=1024,qe=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!!($e.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,s=0;if(!e)return{headers:t};for(let c of Object.keys(e)){if(s>=Xe)break;let u=c.toLowerCase(),y=e[c];if(y===void 0)continue;let h=Array.isArray(y)?y.join(", "):String(y);if(He.has(u)){let g=Je(h);g&&!n&&(n=g);continue}if(u==="cookie"&&(a=h.split(";").filter(g=>g.trim().length>0).length),Qe(u))continue;let l=h;k(l)>j&&(l=l.slice(0,j));let d=k(u)+k(l);i+d>qe||(i+=d,s++,t[u]=l,Ze(u)&&(r[u]=l))}let o={headers:t};return n&&(o.apiKeyFormat=n),Object.keys(r).length>0&&(o.platformHeaders=r),a!==void 0&&(o.cookieCount=a),o}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??"",s=t.asn??"",o=[];if(t.platformHeaders)for(let[c,u]of Object.entries(t.platformHeaders))o.push(c,`${c}: ${u}`);for(let c of e){let u=c.uaPatterns?.some(p=>p.test(r)),y=c.agentCardPatterns?.some(p=>p.test(n)),h=a.length>0&&c.apiKeyFormatPatterns?.some(p=>p.test(a)),l=o.length>0&&c.headerPatterns?.some(p=>o.some(m=>p.test(m))),d=i.length>0&&c.ja4Patterns?.some(p=>p.test(i)),g=s.length>0&&c.asnList?.includes(s);if(u||y||h||l||d||g){let p=r.split(/[\s/]/)[0]?.toLowerCase()??"",m=(()=>{try{return n?new URL(n).host:""}catch{return""}})(),P=p||m||a||i||s&&`asn${s}`||"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 Rr=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.",st={apiBaseUrl:"https://astrasync.ai/api",debug:!1},se=!1,K=!1;async function V(e,t,r,n){se=!0;try{let a=`${e}/agents/verify-access`,s=(await C(a,{method:"HEAD"})).headers.get("content-type")??"";if(s.startsWith("text/html")){let o=`[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(`${o} (strictInit=true)`);console.warn(`${o} Set disableInitChecks: true on GatewayConfig to silence.`)}else t&&console.log(`[VerificationGateway] init check passed for ${e} (content-type: ${s})`);if(n){let o=await C(`${e}/agents/verify-access/self-check`,{headers:{Authorization:`Bearer ${n}`}});if(o.status===401){let c=await o.json().catch(()=>({})),u=`[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(`${u} (strictInit=true)`);console.error(u)}else if(o.ok){let c=await o.json().catch(()=>({}));typeof c.daysToExpiry=="number"&&c.daysToExpiry<=7?console.warn(`[VerificationGateway] the configured AstraSync API key expires in ${c.daysToExpiry} day(s) \u2014 rotate it at Settings \u2192 Developer Tools before integrations start failing.`):t&&console.log("[VerificationGateway] API key self-check passed")}}}catch(a){if(r)throw a;t&&console.log(`[VerificationGateway] init check failed (non-blocking): ${String(a)}`)}}var O=new Map;function oe(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 ot(e,t){let r=oe(e,t),n=O.get(r);return n&&n.expiresAt>Date.now()?n.result:(n&&O.delete(r),null)}var it=60;function ct(e,t,r,n){let a=r&&r>0?r:it,i=oe(e,n);O.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 o=Array.isArray(a)?a[0]:typeof a=="string"?a:void 0;typeof o=="string"&&n.test(o)&&(r.astraId=o)}let i=e["x-api-key"]||e["X-Api-Key"]||e["X-API-KEY"];i&&(r.apiKey=Array.isArray(i)?i[0]:i);let s=e.authorization||e.Authorization;if(s){let o=Array.isArray(s)?s[0]:s;typeof o=="string"&&(r.authorizationHeader=o,o.startsWith("Bearer ")&&(r.jwt=o.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",s=r.urls,o=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:s?.registrationUrl??"",documentationUrl:r.remediation?.docsUrl??s?.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:s?.registrationUrl??"",documentationUrl:s?.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:s?.registrationUrl??"",documentationUrl:s?.documentationUrl??"",steps:[...s?.mcpEndpoint?[`Fastest path (inside an MCP host): add the AstraSync MCP connector at ${s.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(s)&&{mcp:E(s)}};return{identityVerified:!1,policyAllowed:!1,guidance:o,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:o.message}]:a?[{dimension:"verify_access.api_error",message:t??"Verification temporarily unavailable",guidance:o.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=D,n.callerMetadata||n.clientIp||n.userAgent){let s={...n.clientIp&&{sourceIp:n.clientIp},...n.userAgent&&{userAgent:n.userAgent},...n.callerMetadata};Object.keys(s).length>0&&(a.callerMetadata=s)}let i={"Content-Type":"application/json",...e.customHeaders};e.apiKey&&(i.Authorization=`Bearer ${e.apiKey}`,i["X-API-Key"]=e.apiKey);try{let s=await C(`${e.apiBaseUrl}/agents/verify-access`,{method:"POST",headers:i,body:JSON.stringify(a),signal:AbortSignal.timeout(e.verifyTimeoutMs??1e4)}),o=await ut(s),c=ft(s);if(s.status===410)return{success:!0,attemptId:typeof o?.attemptId=="string"?o.attemptId:void 0,access:{allowed:!1,reason:"endpoint_deactivated",failures:[{dimension:"endpoint.deactivated",message:typeof o?.message=="string"?o.message:"Endpoint has been deactivated",guidance:typeof o?.guidance=="string"?o.guidance:"Reactivate via POST /api/endpoints/{id}/reactivate, or update the URL on the calling agent."}]}};if(s.status===400&&o?.error==="INVALID_AGENT_ID"){let u=typeof o?.message=="string"?o.message:"Agent identifier must be a canonical ASTRA-* id.";return{success:!0,attemptId:typeof o?.attemptId=="string"?o.attemptId:void 0,access:{allowed:!1,reason:"invalid_agent_id",failures:[{dimension:"identity.invalid_agent_id",message:u,guidance:typeof o?.guidance=="string"?o.guidance:"Present your canonical ASTRA-* id (discover it via GET /api/agents/request-registration/{requestId} after owner approval)."}]}}}return s.status===404&&r.astraId&&(o?.error==="AGENT_NOT_FOUND"||typeof o?.message=="string"&&/agent/i.test(o.message))?{success:!0,attemptId:typeof o?.attemptId=="string"?o.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}]}}:s.ok?o:s.status===401?{success:!1,error:o.message||o.error||"Integration API key rejected",misconfig:!0,misconfigCode:typeof o?.code=="string"?o.code:void 0,remediation:o?.remediation&&typeof o.remediation=="object"?o.remediation:void 0,correlationId:typeof o?.correlationId=="string"?o.correlationId:void 0,attemptId:typeof o?.attemptId=="string"?o.attemptId:void 0}:{success:!1,error:typeof o?.message=="string"&&o.message||typeof o?.error=="string"&&o.error||`API returned ${s.status}`,correlationId:typeof o?.correlationId=="string"?o.correlationId:void 0,attemptId:typeof o?.attemptId=="string"?o.attemptId:void 0,httpStatus:s.status,...c!==void 0&&{retryAfterMs:c}}}catch(s){return{success:!1,error:`Failed to call verify-access API: ${s instanceof Error?s.message:"Unknown error"}`}}}async function G(e,t,r){let n={...st,...e},a=n.apiBaseUrl?at(n.apiBaseUrl):void 0;if(!se&&!n.disableInitChecks&&n.apiBaseUrl&&(n.strictInit?await V(n.apiBaseUrl,n.debug,!0,n.apiKey):V(n.apiBaseUrl,n.debug,!1,n.apiKey)),!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=ot(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 s=await gt(n,i),o=s.denialReasons,c=Array.isArray(o)&&o.length>0?o:void 0;if(!s.success)return dt(n,s.error,{source:s.misconfig?"merchant_misconfig":"api_error",correlationId:s.correlationId,attemptId:s.attemptId,remediation:s.remediation,httpStatus:s.httpStatus,retryAfterMs:s.retryAfterMs,urls:a});if(!s.access?.allowed){let p=s.access?.failures;return{identityVerified:s.verificationContext?.idVerified===!0&&!p?.some(x=>ne.has(x.dimension)),policyAllowed:!1,denialReasons:c??(p&&p.length>0?p.map(x=>x.message):s.access?.reason?[s.access.reason]:["Access denied"]),failures:p,requiresStepUp:s.access?.requiresStepUp,requiresApproval:s.access?.requiresApproval,guidance:{message:lt(p)??(s.access?.reason||"Access denied by PDLSS policy"),registrationUrl:a?.registrationUrl??"",documentationUrl:a?.documentationUrl??"",...E(a)&&{mcp:E(a)}},verifiedAt:new Date,sessionId:s.sessionId,correlationId:s.correlationId,attemptId:s.attemptId,recommendation:s.recommendation,recommendationReasons:s.recommendationReasons,stepUpApproval:s.stepUpApproval,settlement:s.settlement,settlementOutcome:s.settlementOutcome,settlementToken:s.settlementToken,fulfilment:s.fulfilment}}let u=s.agent?{astraId:s.agent.astraId,name:s.agent.name,trustScore:s.agent.trustScore,trustLevel:rt(s.agent.trustScore),blockchainAnchored:s.agent.blockchainStatus==="anchored",status:s.agent.agentStatus}:void 0,y=s.developer?{astradId:s.developer.kyaOwnerId,name:s.developer.fullName,trustScore:s.developer.trustScore||0,verified:s.developer.identityVerified}:void 0,h=s.organization?{name:s.organization.name,verified:s.organization.verified,trustScore:s.organization.trustScore}:void 0,l=s.verificationContext,d={identityVerified:s.verificationContext?.idVerified===!0,policyAllowed:!0,agent:u,developer:y,organization:h,appliedPolicy:s.access?.appliedPolicy,verificationContext:l,requiresStepUp:s.access?.requiresStepUp,requiresApproval:s.access?.requiresApproval,verifiedAt:new Date,cacheTtl:n.cacheTtl,sessionId:s.sessionId,correlationId:s.correlationId,attemptId:s.attemptId,runtimeChallenge:s.runtimeChallenge,tokenGuidance:s.tokenGuidance,recommendation:s.recommendation,recommendationReasons:s.recommendationReasons,warningHeader:s.warningHeader,stepUpApproval:s.stepUpApproval,settlement:s.settlement,settlementOutcome:s.settlementOutcome,settlementToken:s.settlementToken,fulfilment:s.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.identityVerified&&(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&&ct(t,d,n.cacheTtl,n.counterpartyId),d}function f(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=f(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),s=e.connection??{},o=vt(e.querystring),c=St(e.querystring,t);return{headers:r,...n&&{apiKeyFormat:n},...a&&{platformHeaders:a},...Object.keys(s).length>0&&{connection:s},...i!==void 0&&{cookieCount:i},...o&&{queryKeys:o},...c&&{queryParams:c},schemaVersion:Le}}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=s=>t.some(o=>o.endsWith("*")?s.startsWith(o.slice(0,-1)):s===o),a={},i=0;for(let[s,o]of r.entries()){let c=s.toLowerCase().slice(0,ce);if(!(!c||a[c]!==void 0||!n(c))&&(a[c]=o.slice(0,wt),++i>=At))break}return i>0?a:void 0}var bt=/^eyJ[\w-]+\.[\w-]*\.[\w-]+~/;function It(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(It(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),s=e.bodyTruncated?{bodyTruncated:!0}:{},o=f(e,"authorization"),c=o?.replace(/^Bearer\s+/i,""),y=[f(e,"x-payment-mandate")??f(e,"payment-mandate"),c].find(g=>g&&bt.test(g));if(y){let g=Rt(y)?.typ;return{protocol:"vi-sd-jwt",artifacts:{viSdJwt:y},evidence:{typ:g,...s}}}let h=f(e,"signature"),l=f(e,"signature-input");if(h&&l){let g=f(e,"signature-agent"),p=f(e,"x-agent-registry"),m=l.toLowerCase();return{protocol:g?"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:{...g&&{signatureAgent:g},...p&&{registryHint:p},keyid:l.match(/keyid="([^"]+)"/)?.[1],...s}}}let d=f(e,"timestamp");if(h&&!l&&(d||f(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,...s}};if(/\/checkout[-_]sessions(\/|$)/.test(e.uri))return{protocol:"ucp",artifacts:{ucpRequest:{method:n,url:r,...i&&{body:i}}},evidence:{path:e.uri,...s}};if(i){let g=i.intent_mandate??i.intentMandate,p=i.cart_mandate??i.cartMandate,m=i.payment_mandate??i.paymentMandate;if(typeof g=="string"||typeof p=="string"||typeof m=="string")return{protocol:"ap2",artifacts:{ap2Mandates:{...typeof g=="string"&&{intent:g},...typeof p=="string"&&{cart:p},...typeof m=="string"&&{payment:m}}},evidence:{mandatesPresent:[typeof g=="string"&&"intent",typeof p=="string"&&"cart",typeof m=="string"&&"payment"].filter(Boolean),...s}}}if(o&&/^Payment\s+/i.test(o))return{protocol:"mpp",artifacts:{mppRequest:{method:n,url:r,headers:a,...e.bodyText&&{rawBody:e.bodyText},...i&&{body:i}}},evidence:s};if(f(e,"x-payment"))return{protocol:"x402",artifacts:{x402Request:{method:n,url:r,headers:a,...i&&{body:i}}},evidence:s}}var Tt=/\.(css|js|mjs|map|png|jpe?g|gif|webp|avif|svg|ico|woff2?|ttf|otf|eot|mp4|webm|pdf|txt|xml)$/i,_t=/^astrasync-sdk\//i,Ut=/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 X(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=f(e,"user-agent")??"";if(_t.test(n))return{tier:"self"};let a=ie(e.headers,{});if(!a.astraId&&(X(a.jwt)||X(a.apiKey)))return{tier:"self",evidence:{firstPartyApiKey:!0}};if(a.astraId)return{tier:"verified",protocol:"x-astra"};let{apiKeyFormat:i,platformHeaders:s}=re(e.headers),o={userAgent:n,apiKeyFormat:i,platformHeaders:s};if(r!=="classify"){let u=Et(e,t);if(u)return{tier:"identified",protocol:u.protocol,artifacts:u.artifacts,evidence:u.evidence,fingerprint:H(o)}}let c=H(o);return c?{tier:"identified",protocol:"platform-ua",fingerprint:c}:!n||Ut.test(n)||!Pt.test(n)?{tier:"anonymous-bot",evidence:{userAgent:n||"(none)"}}:!f(e,"sec-fetch-mode")&&!f(e,"accept-language")?{tier:"anonymous-bot",evidence:{userAgent:n,spoofedBrowser:!0}}:{tier:"human"}}var Ot={"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 Dt(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||Dt(n),i=(e.protocol&&Ot[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 Lt="X-Astra-Verified-Hop";function $t(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"},q=["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:q};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:q}}}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?{[Lt]:$t({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 Xt(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 s=r.indexOf(".");s>0?(a=r.slice(0,s),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 qt=e=>`${e.apiBaseUrl}\0${e.apiKey??""}\0${e.counterparty}\0${e.adapter??""}`,Yt=class{constructor(e={}){this.queue=[],this.droppedSinceLast=0,this.droppedTotal=0,this.deliveredTotal=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,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}let s=this.push(e,{...t,repeatCount:1},a.firstSeenAt+this.opts.collapseWindowMs);a.held=s;return}if(this.collapse.delete(r),this.collapse.size>=this.opts.collapseMaxKeys){let s=this.collapse.keys().next().value;s!==void 0&&this.collapse.delete(s)}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,flushing:this.inFlight!==void 0}}push(e,t,r){for(;this.queue.length>=this.opts.maxItems;)this.dropOldest();let n={dest:e,destKey:qt(e),item:t,attempts:0,eligibleAt:r,inFlight:!1};return this.queue.push(n),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:D},i=typeof AbortController<"u"?new AbortController:void 0,s=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);s||i?.abort();let o=s?.status;for(let c of e)c.inFlight=!1;if(o!==void 0&&o>=200&&o<300)return this.droppedSinceLast=Math.max(0,this.droppedSinceLast-n),this.deliveredTotal+=e.length,!0;if(o===401&&r)return this.rejectedKeys.add(r),this.requeue(e,!1),!0;if(o===429){let c=Number(s?.headers.get("retry-after")),u=Number.isFinite(c)&&c>0?Math.min(c,120)*1e3:3e4;return this.backoffUntil=this.opts.now()+u,this.requeue(e,!1),!1}return o!==void 0&&o>=400&&o<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()}},Wt=new Yt;function Jt(){return Wt}async function J(e,t,r,n,a,i){let s=ht(t),o=te(f(t,"x-astra-source")),c=f(t,"user-agent"),u={...e.counterpartyId&&{counterpartyId:e.counterpartyId},...e.counterpartyUrl&&{counterpartyUrl:e.counterpartyUrl},counterpartyType:"website",sourceIp:t.clientIp,userAgent:c,requestPath:t.uri,requestMethod:t.method,...s&&{attemptId:s},referer:f(t,"referer"),accept:f(t,"accept"),...o&&{declaredSource:o},protocol:r.protocol,evidence:r.evidence,observedMetadata:N(t,a),observedAt:new Date().toISOString()};for(let l of Object.keys(u))u[l]===void 0&&delete u[l];let y=Jt();y.enqueue({apiBaseUrl:e.apiBaseUrl,...i&&{apiKey:i},counterparty:e.counterpartyId||e.counterpartyUrl,...e.adapter&&{adapter:e.adapter}},u,t.clientIp?[e.counterpartyId||e.counterpartyUrl,t.clientIp,c??"",t.method,t.uri,s??""].join("\0"):void 0);let h=y.flush();if(e.waitUntil){e.waitUntil(h);return}await T(h,n)}function Qt(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=Xt({method:t.method,astraPurpose:f(t,"x-astra-purpose"),astraAction:f(t,"x-astra-action")}),s=f(t,"x-astra-attempt-id"),o=te(f(t,"x-astra-source"));return{...s&&/^att_[0-9a-f]{32}$/.test(s)&&{attemptId:s},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:f(t,"user-agent"),referer:f(t,"referer"),host:f(t,"host"),...o&&{declaredSource:o},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 Zt(e,t,r,n,a,i){let{sampling:s,queryValueAllowlist:o}=i,c=e.budgets??{},u=c.beaconMs??300,y=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 l=await T(G(h,Q(e,t,r,n.depth==="authorize",o)),y);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 d=await T(G(h,Q(e,t,r,!0,o)),y);return d?ee(d)?R(r,n,d.denialReasons?.[0]??"verify-access api error",d.correlationId):Z(r,d):R(r,n,"verify-access unreachable within budget")}await J(e,t,r,u,o,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 Qt(s.anonymousBeaconRate,e.random)&&await J(e,t,{...r,protocol:r.protocol??"ua-heuristic"},u,o,a),v(r);default:return v(r)}}catch(l){let d=v(r);return d.reasons=[`edge pipeline error \u2014 fail-open: ${l instanceof Error?l.message:String(l)}`],d}}async function de(e,t,r,n){let a=kt(r,t.uri);try{if(a.skip)return{policy:a,application:M()};let i=f(t,"x-forwarded-host")??f(t,"host")??new URL(e.counterpartyUrl).host,s=Mt(t,i,a.depth);if(s.tier==="human"||s.tier==="self")return{policy:a,classification:s,application:M()};let o=await Zt(e,t,s,a,n,{sampling:a.sampling??r.sampling,queryValueAllowlist:r.queryValueAllowlist});return o.decision!=="allow"&&e.log?.("info",a.mode==="enforce"?`${o.decision.toUpperCase()}: ${t.method} ${t.uri}`:`[OBSERVE] would have been ${o.decision.toUpperCase()}: ${t.method} ${t.uri}`,{tier:o.tier,protocol:o.protocol,reasons:o.reasons}),{policy:a,classification:s,outcome:o,application:jt(e,o,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 _(e){return e.Records[0].cf.request}function A(e,t){return e.headers[t.toLowerCase()]?.[0]?.value}function er(e){let t={};for(let[r,n]of Object.entries(e))n?.length&&(t[r]=n.map(a=>a.value).join(", "));return t}function tr(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 rr(e){let t={},r=(i,s,o)=>{let c=A(e,s);c&&(t[i]=o?o(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,s]of[["desktop","cloudfront-is-desktop-viewer"],["mobile","cloudfront-is-mobile-viewer"],["tablet","cloudfront-is-tablet-viewer"],["smarttv","cloudfront-is-smarttv-viewer"]])A(e,s)==="true"&&a.push(i);return a.length&&(t.device=a.join(",")),t}function le(e){let t=tr(e),r=rr(e),n=ue(A(e,"cloudfront-viewer-address"))?.ip;return{method:e.method,uri:e.uri,querystring:e.querystring??"",headers:er(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 ar(t.status,t.bodyJson,t.headers);for(let r of t.stripHeaderPrefixes)nr(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 nr(e,t){let r=t.toLowerCase();for(let n of Object.keys(e.headers))n.startsWith(r)&&delete e.headers[n]}function ar(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.12.0",U=`astrasync-sdk/${sr}`;function or(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 ir=(e,t)=>fetch(e,{...t,headers:or(t?.headers)}),L=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 cr(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 dr(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 ir(`${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 s=(await a.json()).data?.edgeConfig;return cr(s)?{edgeConfig:s,etag:a.headers.get("etag")??void 0}:null}catch{return null}}var ur=60*1e3,lr=1440*60*1e3,S=new Map,B=new Map;function ve(e,t){return`${e}|${t}`}function pr(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=dr(e,t,{etag:S.get(r)?.etag}).then(i=>{if(i===null)return;let s=Date.now();if("notModified"in i){let o=S.get(r);o&&(o.lastSuccessAt=s);return}S.set(r,{config:i.edgeConfig,etag:i.etag,lastSuccessAt:s})}).finally(()=>{B.delete(r)});return B.set(r,a),a}async function Ae(e,t,r){let n=r?.ttlMs??ur,a=r?.maxStaleMs??lr,i=ve(e.apiBaseUrl,t),s=S.get(i);if(s){let o=Date.now()-s.lastSuccessAt;return o>n&&me(e,t).catch(()=>{}),o>a?pr(s.config):s.config}return await me(e,t).catch(()=>{}),S.get(i)?.config??L}var fr=900*1e3,we=new Map;async function gr(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<fr)return r.value;try{let a=await(e.fetchSecret??gr)(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 be(e,t){try{return await Ae({apiBaseUrl:e.apiBaseUrl,apiKey:t},e.counterpartyId,{ttlMs:e.configTtlMs})}catch{return e.fallbackConfig??L}}function Ie(e){let t=e.log??((r,n,a)=>{console[r==="info"?"log":r](`[astrasync-edge] ${n}`,a??"")});return async function(n){let a=_(n);try{fe(a);let i=await Se({...e,log:t}),s=await be(e,i),{application:o}=await de({...e,adapter:"lambda-edge",log:t},le(a),s,i);return pe(a,o)}catch(i){return t("error","edge handler error \u2014 passing request through",{error:i instanceof Error?i.message:String(i)}),a}}}var yr={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 hr(e,t){return e.origin?.custom?.customHeaders?.[t]?.[0]?.value||void 0}function Re(e,t){let r=n=>hr(e,yr[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 mr={apiBaseUrl:"https://astrasync.ai/api",publicBaseUrl:void 0,counterpartyId:"",counterpartyUrl:"",apiKeySecretName:void 0},Ee=new Map,Te=!1,vr=async e=>{let t=Re(_(e),mr);(!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=Ie(t),Ee.set(r,n)),n(e)};0&&(module.exports={handler});
|
package/infra/template.yaml
CHANGED
|
@@ -30,7 +30,10 @@ Parameters:
|
|
|
30
30
|
Description: >
|
|
31
31
|
Secrets Manager secret (us-east-1) holding the merchant kya_* API key.
|
|
32
32
|
Used for IAM scoping AND delivered to the function as an origin custom
|
|
33
|
-
header (Lambda@Edge forbids environment variables).
|
|
33
|
+
header (Lambda@Edge forbids environment variables). For Marketplace
|
|
34
|
+
Quick Launch installs, point this at the secret the AstraSyncApiKey
|
|
35
|
+
deployment parameter created (and leave ApiKeyValue blank) — the
|
|
36
|
+
${ApiKeySecretName}* IAM resource pattern below covers it unchanged.
|
|
34
37
|
ApiBaseUrl:
|
|
35
38
|
Type: String
|
|
36
39
|
Default: https://astrasync.ai/api
|
|
@@ -58,13 +61,33 @@ Parameters:
|
|
|
58
61
|
Default: ''
|
|
59
62
|
Description: >
|
|
60
63
|
Public URL registered for this endpoint. Blank = https://<SiteDomainName>.
|
|
64
|
+
ApiKeyValue:
|
|
65
|
+
Type: String
|
|
66
|
+
Default: ''
|
|
67
|
+
NoEcho: true
|
|
68
|
+
Description: >
|
|
69
|
+
One-click installs (AWS Marketplace quick-create): paste the kya_* API
|
|
70
|
+
key shown once in the AstraSync dashboard — the stack creates the
|
|
71
|
+
Secrets Manager secret named by ApiKeySecretName from it. Leave blank
|
|
72
|
+
for legacy installs where the secret already exists.
|
|
61
73
|
|
|
62
74
|
Conditions:
|
|
63
75
|
HasPublicBaseUrl: !Not [!Equals [!Ref PublicBaseUrl, '']]
|
|
64
76
|
HasCounterpartyId: !Not [!Equals [!Ref CounterpartyId, '']]
|
|
65
77
|
HasCounterpartyUrl: !Not [!Equals [!Ref CounterpartyUrl, '']]
|
|
78
|
+
CreateApiKeySecret: !Not [!Equals [!Ref ApiKeyValue, '']]
|
|
66
79
|
|
|
67
80
|
Resources:
|
|
81
|
+
# Created only for one-click installs (ApiKeyValue supplied). The key never
|
|
82
|
+
# rides a URL: quick-create pre-fills everything EXCEPT this NoEcho field.
|
|
83
|
+
EdgeApiKeySecret:
|
|
84
|
+
Type: AWS::SecretsManager::Secret
|
|
85
|
+
Condition: CreateApiKeySecret
|
|
86
|
+
Properties:
|
|
87
|
+
Name: !Ref ApiKeySecretName
|
|
88
|
+
Description: AstraSync edge adapter API key (created by the quick-create stack)
|
|
89
|
+
SecretString: !Ref ApiKeyValue
|
|
90
|
+
|
|
68
91
|
EdgeFunctionRole:
|
|
69
92
|
Type: AWS::IAM::Role
|
|
70
93
|
Properties:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astrasyncai/adapter-lambda",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.10.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.
|
|
50
|
+
"@astrasyncai/verification-gateway": "^5.12.0",
|
|
51
51
|
"@aws-sdk/client-secrets-manager": "^3.632.0"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
@@ -56,6 +56,6 @@
|
|
|
56
56
|
"eslint": "^8.56.0",
|
|
57
57
|
"tsup": "^8.0.1",
|
|
58
58
|
"typescript": "^5.3.3",
|
|
59
|
-
"vitest": "^4.
|
|
59
|
+
"vitest": "^4.1.11"
|
|
60
60
|
}
|
|
61
61
|
}
|