@astrasyncai/verification-gateway 2.3.4 → 2.3.7
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/adapter-interface/interface.d.mts +2 -2
- package/dist/adapter-interface/interface.d.ts +2 -2
- package/dist/adapters/express.d.mts +2 -2
- package/dist/adapters/express.d.ts +2 -2
- package/dist/adapters/express.js +59 -21
- package/dist/adapters/express.js.map +1 -1
- package/dist/adapters/express.mjs +58 -18
- package/dist/adapters/express.mjs.map +1 -1
- package/dist/adapters/mcp.d.mts +245 -0
- package/dist/adapters/mcp.d.ts +245 -0
- package/dist/adapters/mcp.js +589 -0
- package/dist/adapters/mcp.js.map +1 -0
- package/dist/adapters/mcp.mjs +555 -0
- package/dist/adapters/mcp.mjs.map +1 -0
- package/dist/adapters/nextjs.d.mts +2 -2
- package/dist/adapters/nextjs.d.ts +2 -2
- package/dist/adapters/nextjs.js +57 -3
- package/dist/adapters/nextjs.js.map +1 -1
- package/dist/adapters/nextjs.mjs +57 -3
- package/dist/adapters/nextjs.mjs.map +1 -1
- package/dist/adapters/sdk.d.mts +2 -2
- package/dist/adapters/sdk.d.ts +2 -2
- package/dist/adapters/sdk.js +3 -1
- package/dist/adapters/sdk.js.map +1 -1
- package/dist/adapters/sdk.mjs +3 -1
- package/dist/adapters/sdk.mjs.map +1 -1
- package/dist/agent/index.d.mts +2 -2
- package/dist/agent/index.d.ts +2 -2
- package/dist/browser/background.js +9 -1
- package/dist/browser/background.js.map +1 -1
- package/dist/browser/background.mjs +9 -1
- package/dist/browser/background.mjs.map +1 -1
- package/dist/browser/browser-adapter.d.mts +2 -2
- package/dist/browser/browser-adapter.d.ts +2 -2
- package/dist/cli/index.d.mts +2 -2
- package/dist/cli/index.d.ts +2 -2
- package/dist/cursor/cursor-adapter.d.mts +2 -2
- package/dist/cursor/cursor-adapter.d.ts +2 -2
- package/dist/cursor/extension.d.mts +2 -2
- package/dist/cursor/extension.d.ts +2 -2
- package/dist/cursor/extension.js +9 -1
- package/dist/cursor/extension.js.map +1 -1
- package/dist/cursor/extension.mjs +9 -1
- package/dist/cursor/extension.mjs.map +1 -1
- package/dist/{express-DtvJ6BGt.d.mts → express-D9oRsseg.d.mts} +17 -14
- package/dist/{express-CraCA8_t.d.ts → express-DMSIl20m.d.ts} +17 -14
- package/dist/gateway/gateway.d.mts +2 -2
- package/dist/gateway/gateway.d.ts +2 -2
- package/dist/gateway/gateway.js +9 -1
- package/dist/gateway/gateway.js.map +1 -1
- package/dist/gateway/gateway.mjs +9 -1
- package/dist/gateway/gateway.mjs.map +1 -1
- package/dist/git-trigger/git-hooks.d.mts +2 -2
- package/dist/git-trigger/git-hooks.d.ts +2 -2
- package/dist/{index-BZ85CeEr.d.mts → index-Bn_7eGjb.d.mts} +1 -1
- package/dist/{index--KzVRa32.d.ts → index-BtU9yFda.d.ts} +1 -1
- package/dist/{index-BzAFmemy.d.ts → index-EwUWXC5T.d.ts} +1 -1
- package/dist/{index-SEgnWzkf.d.mts → index-YNPs800Z.d.mts} +1 -1
- package/dist/index.d.mts +7 -7
- package/dist/index.d.ts +7 -7
- package/dist/index.js +93 -20
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +93 -20
- package/dist/index.mjs.map +1 -1
- package/dist/local-evaluator/evaluator.d.mts +2 -2
- package/dist/local-evaluator/evaluator.d.ts +2 -2
- package/dist/{nextjs-B8o9C0t6.d.ts → nextjs-B5ZBpHra.d.ts} +8 -2
- package/dist/{nextjs-DZHAn9j-.d.mts → nextjs-BLtjRbc-.d.mts} +8 -2
- package/dist/{sdk-CRSUFQH2.d.mts → sdk-BhkxvqnK.d.mts} +1 -1
- package/dist/{sdk-BQ3olp3v.d.ts → sdk-YmE3RG8n.d.ts} +1 -1
- package/dist/transport/index.d.mts +2 -2
- package/dist/transport/index.d.ts +2 -2
- package/dist/{types-osMd_dpT.d.ts → types-BecRpozv.d.ts} +1 -1
- package/dist/{types-JMgPake9.d.mts → types-Bxqj1sKY.d.mts} +48 -6
- package/dist/{types-JMgPake9.d.ts → types-Bxqj1sKY.d.ts} +48 -6
- package/dist/{types-aN1UHhyy.d.mts → types-DxY5zt4z.d.mts} +1 -1
- package/dist/ui/index.d.mts +1 -1
- package/dist/ui/index.d.ts +1 -1
- package/package.json +6 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/access-levels.ts","../../src/verify.ts","../../src/transport/http.ts","../../src/pdlss-pre-check.ts","../../src/adapters/express.ts"],"sourcesContent":["/**\n * AstraSync Universal Verification Gateway - Access Level Definitions\n *\n * Defines the hierarchy and capabilities of each access level.\n */\n\nimport type { AccessLevel, TrustLevel } from './types';\n\n/**\n * Access level hierarchy (higher number = more access)\n */\nexport const ACCESS_LEVEL_HIERARCHY: Record<AccessLevel, number> = {\n none: 0,\n guidance: 1,\n 'read-only': 2,\n standard: 3,\n full: 4,\n internal: 5,\n};\n\n/**\n * Access level descriptions for UI\n */\nexport const ACCESS_LEVEL_DESCRIPTIONS: Record<AccessLevel, string> = {\n none: 'No access - credentials required',\n guidance: 'Guidance mode - registration information provided',\n 'read-only': 'Read-only access - can browse but not modify',\n standard: 'Standard access - normal operations per PDLSS policy',\n full: 'Full access - all operations for high-trust agents',\n internal: 'Internal access - organization member privileges',\n};\n\n/**\n * Default trust score thresholds for access levels\n */\nexport const DEFAULT_TRUST_THRESHOLDS: Record<AccessLevel, number> = {\n none: 0,\n guidance: 0,\n 'read-only': 20,\n standard: 40,\n full: 70,\n internal: 0, // Internal is based on org membership, not score\n};\n\n/**\n * Trust level score ranges\n */\nexport const TRUST_LEVEL_RANGES: Record<TrustLevel, { min: number; max: number }> = {\n BRONZE: { min: 0, max: 39 },\n SILVER: { min: 40, max: 59 },\n GOLD: { min: 60, max: 79 },\n PLATINUM: { min: 80, max: 100 },\n};\n\n/**\n * Determine trust level from score\n */\nexport function getTrustLevel(score: number): TrustLevel {\n if (score >= 80) return 'PLATINUM';\n if (score >= 60) return 'GOLD';\n if (score >= 40) return 'SILVER';\n return 'BRONZE';\n}\n\n/**\n * Check if access level A is greater than or equal to access level B\n */\nexport function hasMinimumAccess(actual: AccessLevel, required: AccessLevel): boolean {\n return ACCESS_LEVEL_HIERARCHY[actual] >= ACCESS_LEVEL_HIERARCHY[required];\n}\n\n/**\n * Get the highest access level for a given trust score\n */\nexport function getAccessLevelForScore(\n trustScore: number,\n thresholds: Record<AccessLevel, number> = DEFAULT_TRUST_THRESHOLDS\n): AccessLevel {\n if (trustScore >= thresholds.full) return 'full';\n if (trustScore >= thresholds.standard) return 'standard';\n if (trustScore >= thresholds['read-only']) return 'read-only';\n return 'guidance';\n}\n\n/**\n * Determine access level from verification result\n */\nexport function determineAccessLevel(\n verified: boolean,\n trustScore: number,\n isOrgMember: boolean,\n customThresholds?: Partial<Record<AccessLevel, number>>\n): AccessLevel {\n if (!verified) {\n return 'guidance';\n }\n\n if (isOrgMember) {\n return 'internal';\n }\n\n const thresholds = {\n ...DEFAULT_TRUST_THRESHOLDS,\n ...customThresholds,\n };\n\n return getAccessLevelForScore(trustScore, thresholds);\n}\n\n/**\n * Access capabilities per level\n */\nexport interface AccessCapabilities {\n canRead: boolean;\n canWrite: boolean;\n canDelete: boolean;\n canAdmin: boolean;\n canAccessInternal: boolean;\n maxTransactionValue?: number;\n allowedPurposes?: string[];\n}\n\n/**\n * Get capabilities for an access level\n */\nexport function getCapabilities(accessLevel: AccessLevel): AccessCapabilities {\n switch (accessLevel) {\n case 'none':\n return {\n canRead: false,\n canWrite: false,\n canDelete: false,\n canAdmin: false,\n canAccessInternal: false,\n };\n case 'guidance':\n return {\n canRead: false,\n canWrite: false,\n canDelete: false,\n canAdmin: false,\n canAccessInternal: false,\n };\n case 'read-only':\n return {\n canRead: true,\n canWrite: false,\n canDelete: false,\n canAdmin: false,\n canAccessInternal: false,\n };\n case 'standard':\n return {\n canRead: true,\n canWrite: true,\n canDelete: false,\n canAdmin: false,\n canAccessInternal: false,\n };\n case 'full':\n return {\n canRead: true,\n canWrite: true,\n canDelete: true,\n canAdmin: false,\n canAccessInternal: false,\n };\n case 'internal':\n return {\n canRead: true,\n canWrite: true,\n canDelete: true,\n canAdmin: true,\n canAccessInternal: true,\n };\n default:\n return {\n canRead: false,\n canWrite: false,\n canDelete: false,\n canAdmin: false,\n canAccessInternal: false,\n };\n }\n}\n","/**\n * AstraSync Universal Verification Gateway - Core Verification Logic\n *\n * This module handles the core verification logic, calling the AstraSync API\n * and processing the response into a standardized VerificationResult.\n */\n\nimport type {\n GatewayConfig,\n AgentCredentials,\n VerificationRequest,\n VerificationResult,\n VerifiedAgent,\n VerifiedDeveloper,\n VerifiedOrganization,\n GuidanceInfo,\n AccessLevel,\n EnhancedVerificationResult,\n TokenGuidance,\n RuntimeChallengeResult,\n} from './types';\nimport { getTrustLevel, ACCESS_LEVEL_HIERARCHY } from './access-levels';\n\n/**\n * Default configuration values\n *\n * apiBaseUrl matches the OpenAPI authoritative server (https://astrasync.ai/api\n * for prod, https://staging.astrasync.ai/api for staging). Always include the\n * /api path prefix when overriding — registration / docs URLs are derived by\n * stripping it.\n */\nconst DEFAULT_CONFIG: Partial<GatewayConfig> = {\n apiBaseUrl: 'https://astrasync.ai/api',\n defaultAccessLevel: 'guidance',\n // minTrustScore + minTrustScoreForFull deprecated in v2.3.0 — server decides.\n cacheTtl: 300, // 5 minutes\n debug: false,\n};\n\n/**\n * Init self-test state. Fires once per process on first verify() call to warn\n * if apiBaseUrl is pointing at the wrong host (e.g. a marketing site that\n * 200s with text/html instead of the API).\n */\nlet initCheckPerformed = false;\n\n/** One-shot guard for v2.3.0 deprecation warning. */\nlet deprecationWarningShown = false;\n\nasync function performInitCheck(apiBaseUrl: string, debug?: boolean): Promise<void> {\n initCheckPerformed = true;\n try {\n const probeUrl = `${apiBaseUrl}/agents/verify-access`;\n // HEAD mirrors GET semantics (running the full request pipeline without a\n // body) so the response carries the same content-type the marketing 404\n // would return. OPTIONS often gets short-circuited by CORS-preflight\n // handlers and returns no content-type, defeating the check.\n const response = await fetch(probeUrl, { method: 'HEAD' });\n const contentType = response.headers.get('content-type') ?? '';\n if (contentType.startsWith('text/html')) {\n console.warn(\n `[VerificationGateway] apiBaseUrl '${apiBaseUrl}' returned HTML (content-type: ${contentType}). ` +\n `This usually means apiBaseUrl is pointing at a marketing site instead of the API. ` +\n `Expected: 'https://astrasync.ai/api' (prod) or 'https://staging.astrasync.ai/api' (staging). ` +\n `Set disableInitChecks: true on GatewayConfig to silence this warning.`\n );\n } else if (debug) {\n console.log(\n `[VerificationGateway] init check passed for ${apiBaseUrl} (content-type: ${contentType})`\n );\n }\n } catch (err) {\n if (debug) {\n console.log(`[VerificationGateway] init check failed (non-blocking): ${String(err)}`);\n }\n }\n}\n\n/**\n * Simple in-memory cache for verification results\n */\nconst verificationCache = new Map<string, { result: VerificationResult; expiresAt: number }>();\n\n/**\n * Generate cache key from credentials\n */\nfunction getCacheKey(credentials: AgentCredentials): string {\n return `${credentials.astraId || ''}-${credentials.apiKey || ''}-${credentials.jwt || ''}`;\n}\n\n/**\n * Check if cached result is still valid\n */\nfunction getCachedResult(credentials: AgentCredentials): VerificationResult | null {\n const key = getCacheKey(credentials);\n const cached = verificationCache.get(key);\n\n if (cached && cached.expiresAt > Date.now()) {\n return cached.result;\n }\n\n if (cached) {\n verificationCache.delete(key);\n }\n\n return null;\n}\n\n/**\n * Cache a verification result\n */\nfunction cacheResult(\n credentials: AgentCredentials,\n result: VerificationResult,\n ttlSeconds: number\n): void {\n const key = getCacheKey(credentials);\n verificationCache.set(key, {\n result,\n expiresAt: Date.now() + ttlSeconds * 1000,\n });\n}\n\n/**\n * Clear the verification cache\n */\nexport function clearCache(): void {\n verificationCache.clear();\n}\n\n/**\n * Extract agent credentials from various sources\n */\nexport function extractCredentials(\n headers: Record<string, string | string[] | undefined>,\n query?: Record<string, string | undefined>\n): AgentCredentials {\n const credentials: AgentCredentials = {};\n\n // Check for ASTRA-ID in headers (case-insensitive). Accepts the historical\n // X-Astra-Id name plus the X-Astra-AgentId / x-astra-agent-id alias the\n // partner asked for in #9a — both surface the same field.\n const astraIdHeader =\n headers['x-astra-id'] ||\n headers['X-Astra-Id'] ||\n headers['X-ASTRA-ID'] ||\n headers['x-astra-agentid'] ||\n headers['X-Astra-AgentId'] ||\n headers['x-astra-agent-id'] ||\n headers['X-Astra-Agent-Id'] ||\n headers['X-ASTRA-AGENT-ID'];\n if (astraIdHeader) {\n credentials.astraId = Array.isArray(astraIdHeader) ? astraIdHeader[0] : astraIdHeader;\n }\n\n // Check for API key in headers\n const apiKeyHeader = headers['x-api-key'] || headers['X-Api-Key'] || headers['X-API-KEY'];\n if (apiKeyHeader) {\n credentials.apiKey = Array.isArray(apiKeyHeader) ? apiKeyHeader[0] : apiKeyHeader;\n }\n\n // Check Authorization header for Bearer token\n const authHeader = headers['authorization'] || headers['Authorization'];\n if (authHeader) {\n const authValue = Array.isArray(authHeader) ? authHeader[0] : authHeader;\n credentials.authorizationHeader = authValue;\n\n if (authValue.startsWith('Bearer ')) {\n credentials.jwt = authValue.slice(7);\n }\n }\n\n // Check query parameters as fallback\n if (query) {\n if (query.astraId && !credentials.astraId) {\n credentials.astraId = query.astraId;\n }\n if (query.apiKey && !credentials.apiKey) {\n credentials.apiKey = query.apiKey;\n }\n }\n\n return credentials;\n}\n\n/**\n * Check if credentials are present\n */\nexport function hasCredentials(credentials: AgentCredentials): boolean {\n return !!(credentials.astraId || credentials.apiKey || credentials.jwt);\n}\n\n/**\n * Create guidance response for unverified agents\n */\nfunction createGuidanceResponse(config: GatewayConfig, reason?: string): VerificationResult {\n const guidance: GuidanceInfo = {\n message:\n 'This service verifies AI agents before granting access. Please register your agent with AstraSync.',\n registrationUrl: `${config.apiBaseUrl.replace('/api', '')}/register`,\n documentationUrl: `${config.apiBaseUrl.replace('/api', '')}/docs/agent-access`,\n steps: [\n 'Register for an AstraSync account',\n 'Create and register your agent',\n 'Add your ASTRA-ID to request headers',\n 'Retry your request',\n ],\n };\n\n return {\n verified: false,\n accessLevel: 'guidance',\n guidance,\n denialReasons: reason ? [reason] : ['No valid agent credentials provided'],\n verifiedAt: new Date(),\n };\n}\n\n/**\n * Call the AstraSync verify-access API\n */\nasync function callVerifyAccessAPI(\n config: GatewayConfig,\n request: VerificationRequest\n): Promise<{\n success: boolean;\n access?: {\n allowed: boolean;\n /**\n * Server-decided access level. Read verbatim — do NOT remap client-side.\n * The server resolves this from endpoint policy + agent trust score using\n * the canonical thresholds (see backend `apps/backend/src/utils/access-levels.ts`).\n */\n accessLevel?: AccessLevel;\n reason?: string;\n requiresStepUp?: boolean;\n requiresApproval?: boolean;\n appliedPolicy?: {\n boundaryName: string;\n policyVersion: string;\n };\n counterparty?: {\n id: string;\n name: string;\n trustScoreRequirement: number;\n };\n };\n agent?: {\n kyaAgentId: string;\n astraId: string;\n name: string;\n trustScore: number;\n trustLevel: string;\n agentStatus: string;\n blockchainStatus: string;\n };\n developer?: {\n kyaOwnerId: string;\n fullName: string;\n email: string;\n identityVerified: boolean;\n trustScore: number;\n };\n organization?: {\n name: string;\n verified: boolean;\n trustScore: number;\n };\n /**\n * Structured explanation of the verification decision. Tells the merchant\n * WHY (id verified? challenge passed? request within PDLSS? trust score?)\n * without exposing thresholds, scope lists, or other-tenant counterparty\n * membership. Empty `attestations` unless the endpoint's access policy\n * declared `required_attestations`.\n */\n verificationContext?: {\n idVerified: boolean;\n runtimeChallenge: {\n status: 'passed' | 'skipped' | 'failed' | 'timeout' | 'not_supported';\n checkedAt: string | null;\n };\n pdlssCheck: {\n result: 'within' | 'exceeded' | 'denied' | 'not_evaluated';\n purpose: 'approved' | 'denied';\n scope: 'approved' | 'denied';\n };\n dynamicTrustScore: number;\n attestations: Array<{\n type: string;\n status: 'passed' | 'failed';\n validUntil?: string;\n proofType: 'reference' | 'zkp';\n proof: string;\n }>;\n };\n error?: string;\n}> {\n const { credentials, ...requestData } = request;\n\n // Build the request body. agentId is omitted when not provided so the\n // server treats it as an anonymous canonical-flow call (Branch A/B/C).\n const body: Record<string, unknown> = {\n ...(credentials.astraId && { agentId: credentials.astraId }),\n purpose: requestData.purpose || 'general',\n };\n\n // Add optional fields\n if (requestData.action) body.action = requestData.action;\n if (requestData.resourceType) body.resourceType = requestData.resourceType;\n if (requestData.resource) body.resource = requestData.resource;\n if (requestData.jurisdiction) body.jurisdiction = requestData.jurisdiction;\n if (requestData.transactionValue) body.transactionValue = requestData.transactionValue;\n if (requestData.currency) body.currency = requestData.currency;\n if (requestData.isSubAgentRequest) body.isSubAgentRequest = requestData.isSubAgentRequest;\n if (requestData.parentAgentId) body.parentAgentId = requestData.parentAgentId;\n if (requestData.subAgentDepth !== undefined) body.subAgentDepth = requestData.subAgentDepth;\n // Handshake Protocol v10 additions\n if (requestData.enableRuntimeChallenge)\n body.enableRuntimeChallenge = requestData.enableRuntimeChallenge;\n if (requestData.createSession) body.createSession = requestData.createSession;\n if (requestData.durationRequired) body.durationRequired = requestData.durationRequired;\n if (requestData.counterpartyType) body.counterpartyType = requestData.counterpartyType;\n if (requestData.counterpartyUrl) body.counterpartyUrl = requestData.counterpartyUrl;\n if (config.counterpartyId) body.counterpartyId = config.counterpartyId;\n if (requestData.runtimeChallengeOptions)\n body.runtimeChallengeOptions = requestData.runtimeChallengeOptions;\n\n // Forward caller metadata when present. Merges the legacy top-level\n // clientIp/userAgent into the nested block for backward compatibility.\n if (requestData.callerMetadata || requestData.clientIp || requestData.userAgent) {\n const meta = {\n ...(requestData.clientIp && { sourceIp: requestData.clientIp }),\n ...(requestData.userAgent && { userAgent: requestData.userAgent }),\n ...requestData.callerMetadata,\n };\n if (Object.keys(meta).length > 0) body.callerMetadata = meta;\n }\n\n // Build headers\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...config.customHeaders,\n };\n\n // verify-access requires authentication. The backend's authenticate middleware\n // accepts either a JWT or an API key (starts with kya_) via `Authorization: Bearer <token>`.\n // Credential-supplied auth header (e.g. the agent's own token) takes priority.\n if (credentials.authorizationHeader) {\n headers['Authorization'] = credentials.authorizationHeader;\n } else if (config.apiKey) {\n headers['Authorization'] = `Bearer ${config.apiKey}`;\n }\n // Legacy header kept for compatibility with any middleware that reads it directly.\n if (config.apiKey) {\n headers['X-API-Key'] = config.apiKey;\n }\n\n try {\n const response = await fetch(`${config.apiBaseUrl}/agents/verify-access`, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n });\n\n const data = await response.json();\n\n if (!response.ok) {\n return {\n success: false,\n error: data.message || data.error || `API returned ${response.status}`,\n };\n }\n\n return data;\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n return {\n success: false,\n error: `Failed to call verify-access API: ${message}`,\n };\n }\n}\n\n/**\n * Main verification function\n */\nexport async function verify(\n config: GatewayConfig,\n request: VerificationRequest\n): Promise<VerificationResult> {\n const mergedConfig = { ...DEFAULT_CONFIG, ...config };\n\n // One-time init self-test — fire-and-forget, never blocks verify().\n if (!initCheckPerformed && !mergedConfig.disableInitChecks && mergedConfig.apiBaseUrl) {\n void performInitCheck(mergedConfig.apiBaseUrl, mergedConfig.debug);\n }\n\n // Deprecation warning for v2.3.0 removed config fields. Fires once per process.\n if (\n !deprecationWarningShown &&\n (config.minTrustScore !== undefined || config.minTrustScoreForFull !== undefined)\n ) {\n deprecationWarningShown = true;\n console.warn(\n '[VerificationGateway] minTrustScore / minTrustScoreForFull are deprecated in v2.3.0 ' +\n 'and have no effect. Server is now the single source of truth for access-level decisions ' +\n '(the SDK reads access.accessLevel from the verify-access response). To gate access ' +\n \"to an endpoint, configure the endpoint's trust_score_requirement server-side.\"\n );\n }\n\n // v2.3.0: anonymous traffic no longer short-circuits here. We forward the\n // request to the server with no agentId; the server applies the endpoint's\n // unverifiedAgentPolicy and returns advisory. createGuidanceResponse remains\n // as the offline fallback if the API itself fails (handled below).\n\n // Check cache first\n if (mergedConfig.cacheTtl && mergedConfig.cacheTtl > 0) {\n const cached = getCachedResult(request.credentials);\n if (cached) {\n if (mergedConfig.debug) {\n console.log('[VerificationGateway] Returning cached result');\n }\n return cached;\n }\n }\n\n // Inject counterparty info from config if not already set in request\n const enrichedRequest = { ...request };\n if (!enrichedRequest.counterpartyUrl && mergedConfig.counterpartyUrl) {\n enrichedRequest.counterpartyUrl = mergedConfig.counterpartyUrl;\n }\n if (!enrichedRequest.counterpartyType && mergedConfig.counterpartyType) {\n enrichedRequest.counterpartyType = mergedConfig.counterpartyType;\n }\n\n // Call the API\n if (mergedConfig.debug) {\n console.log('[VerificationGateway] Calling verify-access API');\n }\n\n const apiResponse = await callVerifyAccessAPI(mergedConfig, enrichedRequest);\n\n // Handle API errors\n if (!apiResponse.success) {\n return createGuidanceResponse(mergedConfig, apiResponse.error);\n }\n\n // Check access result\n if (!apiResponse.access?.allowed) {\n const result: EnhancedVerificationResult = {\n verified: false,\n accessLevel: 'guidance',\n denialReasons: apiResponse.access?.reason ? [apiResponse.access.reason] : ['Access denied'],\n requiresStepUp: apiResponse.access?.requiresStepUp,\n requiresApproval: apiResponse.access?.requiresApproval,\n guidance: {\n message: apiResponse.access?.reason || 'Access denied by PDLSS policy',\n registrationUrl: `${mergedConfig.apiBaseUrl?.replace('/api', '')}/register`,\n documentationUrl: `${mergedConfig.apiBaseUrl?.replace('/api', '')}/docs/pdlss`,\n },\n verifiedAt: new Date(),\n // Extract sessionId so decisions can be recorded for denials too\n sessionId: (apiResponse as Record<string, unknown>).sessionId as string | undefined,\n recommendation: (apiResponse as Record<string, unknown>)\n .recommendation as EnhancedVerificationResult['recommendation'],\n recommendationReasons: (apiResponse as Record<string, unknown>).recommendationReasons as\n | string[]\n | undefined,\n };\n\n return result;\n }\n\n // Build successful result\n const agent: VerifiedAgent | undefined = apiResponse.agent\n ? {\n astraId: apiResponse.agent.astraId,\n name: apiResponse.agent.name,\n trustScore: apiResponse.agent.trustScore,\n trustLevel: getTrustLevel(apiResponse.agent.trustScore),\n blockchainVerified: apiResponse.agent.blockchainStatus === 'verified',\n status: apiResponse.agent.agentStatus as VerifiedAgent['status'],\n }\n : undefined;\n\n const developer: VerifiedDeveloper | undefined = apiResponse.developer\n ? {\n astradId: apiResponse.developer.kyaOwnerId,\n name: apiResponse.developer.fullName,\n trustScore: apiResponse.developer.trustScore || 0,\n verified: apiResponse.developer.identityVerified,\n }\n : undefined;\n\n const organization: VerifiedOrganization | undefined = apiResponse.organization\n ? {\n name: apiResponse.organization.name,\n verified: apiResponse.organization.verified,\n trustScore: apiResponse.organization.trustScore,\n }\n : undefined;\n\n // Verification context — structured \"why\" the merchant gets in v2.2.4+.\n // Carries appliedPolicy (no UUIDs), pdlssCheck summary, dynamic trust score,\n // and policy-driven attestations. Replaces the old over-sharing `pdlss` block.\n const verificationContext = apiResponse.verificationContext;\n\n // Server is the single source of truth for access level. SDK reads\n // apiResponse.access.accessLevel verbatim — no client-side trust-score remap.\n // Fallback to 'standard' if the server response is missing the field (older\n // backend without the v2.3.0 contract); it covers the verified-access case.\n const accessLevel: AccessLevel = apiResponse.access?.accessLevel ?? 'standard';\n\n const result: EnhancedVerificationResult = {\n verified: true,\n accessLevel,\n agent,\n developer,\n organization,\n appliedPolicy: apiResponse.access?.appliedPolicy,\n verificationContext,\n requiresStepUp: apiResponse.access?.requiresStepUp,\n requiresApproval: apiResponse.access?.requiresApproval,\n verifiedAt: new Date(),\n cacheTtl: mergedConfig.cacheTtl,\n // Handshake Protocol v10 enhanced fields (present when backend returns them)\n sessionId: (apiResponse as Record<string, unknown>).sessionId as string | undefined,\n runtimeChallenge: (apiResponse as Record<string, unknown>).runtimeChallenge as\n | RuntimeChallengeResult\n | undefined,\n tokenGuidance: (apiResponse as Record<string, unknown>).tokenGuidance as\n | TokenGuidance\n | undefined,\n recommendation: (apiResponse as Record<string, unknown>)\n .recommendation as EnhancedVerificationResult['recommendation'],\n recommendationReasons: (apiResponse as Record<string, unknown>).recommendationReasons as\n | string[]\n | undefined,\n };\n\n // Enforce AstraSync recommendation\n if (result.recommendation === 'deny') {\n result.verified = false;\n result.accessLevel = 'none';\n result.denialReasons = result.recommendationReasons || [\n 'Access denied by AstraSync recommendation',\n ];\n if (result.runtimeChallenge) {\n result.guidance = {\n message: `Verification failed: ${result.runtimeChallenge.reason || 'runtime challenge failed'}`,\n registrationUrl: `${mergedConfig.apiBaseUrl?.replace('/api', '')}/register`,\n documentationUrl: `${mergedConfig.apiBaseUrl?.replace('/api', '')}/docs/runtime-challenge`,\n };\n }\n } else if (result.recommendation === 'step_up_required') {\n result.requiresStepUp = true;\n if (ACCESS_LEVEL_HIERARCHY[result.accessLevel] > ACCESS_LEVEL_HIERARCHY['read-only']) {\n result.accessLevel = 'read-only';\n }\n result.denialReasons = result.recommendationReasons || ['Step-up verification required'];\n }\n\n // Cache the result (skip caching denials — agent may fix challenge endpoint and retry)\n if (mergedConfig.cacheTtl && mergedConfig.cacheTtl > 0 && result.recommendation !== 'deny') {\n cacheResult(request.credentials, result, mergedConfig.cacheTtl);\n }\n\n return result;\n}\n\n/**\n * Record a counterparty's grant/deny decision for a verification session.\n * Fire-and-forget — errors are silently swallowed.\n */\nexport async function recordDecision(\n config: GatewayConfig,\n sessionId: string,\n decision: 'granted' | 'denied',\n reason?: string\n): Promise<void> {\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n if (config.apiKey) {\n headers['Authorization'] = `Bearer ${config.apiKey}`;\n headers['X-API-Key'] = config.apiKey;\n }\n\n await fetch(`${config.apiBaseUrl}/agents/verify-access/${sessionId}/decision`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ decision, reason }),\n }).catch(() => {\n /* fire-and-forget */\n });\n}\n\n/**\n * Verify an agent AND automatically record the grant/deny decision.\n *\n * This is the recommended entry point for counterparties that call verify()\n * directly (e.g. MCP servers) rather than using createMiddleware().\n * It adds createSession: true, then fire-and-forgets the decision.\n */\nexport async function verifyAndRecord(\n config: GatewayConfig,\n request: VerificationRequest\n): Promise<VerificationResult> {\n const mergedConfig = { ...DEFAULT_CONFIG, ...config };\n const result = await verify(mergedConfig, { ...request, createSession: true });\n const sessionId = (result as EnhancedVerificationResult).sessionId;\n\n if (sessionId) {\n if (result.verified) {\n recordDecision(mergedConfig, sessionId, 'granted').catch(() => {});\n } else {\n recordDecision(mergedConfig, sessionId, 'denied', result.denialReasons?.[0]).catch(() => {});\n }\n }\n\n return result;\n}\n\n/**\n * Report an unregistered agent attempt (no AstraSync credentials).\n * Called by SDK adapters when an agent is redirected to /docs/agent-access.\n * Fire-and-forget — errors are silently swallowed.\n */\nexport async function reportUnregisteredAttempt(\n config: GatewayConfig,\n data: {\n counterpartyUrl: string;\n counterpartyType?: string;\n sourceIp?: string;\n userAgent?: string;\n requestPath?: string;\n requestMethod?: string;\n }\n): Promise<void> {\n const apiBaseUrl = config.apiBaseUrl || DEFAULT_CONFIG.apiBaseUrl!;\n\n await fetch(`${apiBaseUrl}/verification-activity/unregistered-attempt`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(data),\n }).catch(() => {\n /* fire-and-forget */\n });\n}\n\n/**\n * Report a counterparty-side PDLSS pre-check failure.\n * Called by SDK adapters when the agent's requested PDLSS exceeds\n * counterparty-defined maximums BEFORE calling verify-access.\n * Fire-and-forget — errors are silently swallowed.\n */\nexport async function reportCounterpartyPreCheckFailure(\n config: GatewayConfig,\n data: {\n agentId: string;\n counterpartyUrl: string;\n counterpartyType?: string;\n failures: Array<{\n field: string;\n requested: string | number;\n limit: string | number | string[];\n message: string;\n }>;\n requestPath?: string;\n requestMethod?: string;\n }\n): Promise<void> {\n const apiBaseUrl = config.apiBaseUrl || DEFAULT_CONFIG.apiBaseUrl!;\n\n await fetch(`${apiBaseUrl}/verification-activity/counterparty-pre-check-failure`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(data),\n }).catch(() => {\n /* fire-and-forget */\n });\n}\n\n/**\n * Quick verification - just check if credentials are valid\n */\nexport async function quickVerify(\n config: GatewayConfig,\n credentials: AgentCredentials\n): Promise<{ verified: boolean; accessLevel: AccessLevel; reason?: string }> {\n const result = await verify(config, {\n credentials,\n purpose: 'verification',\n });\n\n return {\n verified: result.verified,\n accessLevel: result.accessLevel,\n reason: result.denialReasons?.[0],\n };\n}\n","/**\n * HTTP Transport Adapter\n *\n * Maps AstraSync credentials to/from HTTP headers (X-Astra-* convention).\n */\n\nimport type { AstraSyncCredentials } from '../types';\n\nconst HEADER_PREFIX = 'X-Astra-';\n\n/**\n * Inject AstraSync credentials into HTTP headers.\n */\nexport function setHttpHeaders(\n headers: Record<string, string>,\n credentials: AstraSyncCredentials,\n): Record<string, string> {\n const result = { ...headers };\n\n result[`${HEADER_PREFIX}ID`] = credentials.agentId;\n\n if (credentials.verifyUrl) {\n result[`${HEADER_PREFIX}Verify`] = credentials.verifyUrl;\n }\n\n if (credentials.challengeUrl) {\n result[`${HEADER_PREFIX}Challenge`] = credentials.challengeUrl;\n }\n\n if (credentials.pdlss?.purpose) {\n const purposeValue = credentials.pdlss.purpose.action\n ? `${credentials.pdlss.purpose.category}:${credentials.pdlss.purpose.action}`\n : credentials.pdlss.purpose.category;\n result[`${HEADER_PREFIX}Purpose`] = purposeValue;\n }\n\n if (credentials.pdlss?.duration?.maxSessionDuration) {\n result[`${HEADER_PREFIX}Duration`] = String(credentials.pdlss.duration.maxSessionDuration);\n }\n\n if (credentials.pdlss?.scope?.jurisdiction) {\n result[`${HEADER_PREFIX}Scope`] = credentials.pdlss.scope.jurisdiction;\n }\n\n return result;\n}\n\n/**\n * Extract AstraSync credentials from HTTP headers.\n */\nexport function extractHttpCredentials(\n headers: Record<string, string | string[] | undefined>,\n): AstraSyncCredentials | null {\n const getValue = (key: string): string | undefined => {\n const v = headers[key] ?? headers[key.toLowerCase()];\n return Array.isArray(v) ? v[0] : v;\n };\n\n const agentId = getValue(`${HEADER_PREFIX}ID`) ?? getValue('x-astra-id');\n if (!agentId) return null;\n\n const credentials: AstraSyncCredentials = { agentId };\n\n const verifyUrl = getValue(`${HEADER_PREFIX}Verify`) ?? getValue('x-astra-verify');\n if (verifyUrl) credentials.verifyUrl = verifyUrl;\n\n const challengeUrl = getValue(`${HEADER_PREFIX}Challenge`) ?? getValue('x-astra-challenge');\n if (challengeUrl) credentials.challengeUrl = challengeUrl;\n\n const purpose = getValue(`${HEADER_PREFIX}Purpose`) ?? getValue('x-astra-purpose');\n if (purpose) {\n const [category, action] = purpose.split(':');\n credentials.pdlss = {\n ...credentials.pdlss,\n purpose: { category, action },\n };\n }\n\n const duration = getValue(`${HEADER_PREFIX}Duration`) ?? getValue('x-astra-duration');\n if (duration) {\n credentials.pdlss = {\n ...credentials.pdlss,\n duration: { maxSessionDuration: parseInt(duration, 10) },\n };\n }\n\n const scope = getValue(`${HEADER_PREFIX}Scope`) ?? getValue('x-astra-scope');\n if (scope) {\n credentials.pdlss = {\n ...credentials.pdlss,\n scope: { jurisdiction: scope },\n };\n }\n\n return credentials;\n}\n","/**\n * Counterparty-side PDLSS pre-check.\n *\n * Compares the agent's requested PDLSS dimensions (from X-Astra-* headers)\n * against the counterparty-defined maximums on the route config.\n * Returns an array of failures — empty means all checks passed.\n *\n * This runs BEFORE calling verify-access on AstraSync. If it fails,\n * the request is rejected immediately without calling the platform.\n */\n\nimport type { RouteAccessConfig, AstraSyncCredentials, CounterpartyPreCheckFailure } from './types';\n\nexport function performCounterpartyPreCheck(\n routeConfig: RouteAccessConfig,\n astraCreds: AstraSyncCredentials | null,\n purpose: string | undefined,\n): CounterpartyPreCheckFailure[] {\n const failures: CounterpartyPreCheckFailure[] = [];\n\n // Check purpose against allowedPurposes whitelist\n if (routeConfig.allowedPurposes && routeConfig.allowedPurposes.length > 0 && purpose) {\n if (!routeConfig.allowedPurposes.includes(purpose)) {\n failures.push({\n field: 'purpose',\n requested: purpose,\n limit: routeConfig.allowedPurposes,\n message: `Purpose \"${purpose}\" is not in the allowed list: [${routeConfig.allowedPurposes.join(', ')}]`,\n });\n }\n }\n\n // Check purpose against requiredPurposes (legacy field — agent must declare one of these)\n if (routeConfig.requiredPurposes && routeConfig.requiredPurposes.length > 0 && purpose) {\n if (!routeConfig.requiredPurposes.includes(purpose)) {\n failures.push({\n field: 'purpose',\n requested: purpose,\n limit: routeConfig.requiredPurposes,\n message: `Purpose \"${purpose}\" is not in the required list: [${routeConfig.requiredPurposes.join(', ')}]`,\n });\n }\n }\n\n // Check duration against maxDuration\n if (routeConfig.maxDuration && astraCreds?.pdlss?.duration?.maxSessionDuration) {\n const requested = astraCreds.pdlss.duration.maxSessionDuration;\n if (requested > routeConfig.maxDuration) {\n failures.push({\n field: 'duration',\n requested,\n limit: routeConfig.maxDuration,\n message: `Requested duration ${requested}s exceeds maximum ${routeConfig.maxDuration}s`,\n });\n }\n }\n\n // Check jurisdiction against allowedJurisdictions\n if (\n routeConfig.allowedJurisdictions &&\n routeConfig.allowedJurisdictions.length > 0 &&\n astraCreds?.pdlss?.scope?.jurisdiction\n ) {\n const requested = astraCreds.pdlss.scope.jurisdiction;\n if (!routeConfig.allowedJurisdictions.includes(requested)) {\n failures.push({\n field: 'jurisdiction',\n requested,\n limit: routeConfig.allowedJurisdictions,\n message: `Jurisdiction \"${requested}\" is not in the allowed list: [${routeConfig.allowedJurisdictions.join(', ')}]`,\n });\n }\n }\n\n return failures;\n}\n","/**\n * AstraSync Universal Verification Gateway - Express Middleware\n *\n * Express.js middleware for verifying AI agents on API endpoints.\n *\n * @example\n * ```typescript\n * import express from 'express';\n * import { createMiddleware } from '@astrasyncai/verification-gateway/express';\n *\n * const app = express();\n *\n * app.use(createMiddleware({\n * apiBaseUrl: 'https://astrasync.ai/api',\n * routes: [\n * { pattern: '/api/public/*', method: '*', minAccessLevel: 'none' },\n * { pattern: '/api/data/*', method: 'GET', minAccessLevel: 'read-only' },\n * { pattern: '/api/data/*', method: '*', minAccessLevel: 'standard' },\n * { pattern: '/api/admin/*', method: '*', minAccessLevel: 'internal' },\n * ],\n * }));\n * ```\n */\n\nimport type { Request, Response, NextFunction, RequestHandler } from 'express';\nimport type {\n ExpressMiddlewareOptions,\n AgentCredentials,\n VerificationResult,\n EnhancedVerificationResult,\n RouteAccessConfig,\n AccessLevel,\n AstraSyncCredentials,\n} from '../types';\nimport {\n verify,\n extractCredentials,\n recordDecision,\n reportCounterpartyPreCheckFailure,\n} from '../verify';\nimport { hasMinimumAccess } from '../access-levels';\nimport { extractHttpCredentials } from '../transport/http';\nimport { performCounterpartyPreCheck } from '../pdlss-pre-check';\n\n/**\n * Extend Express Request with verification result\n */\ndeclare global {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n namespace Express {\n interface Request {\n agentVerification?: VerificationResult;\n }\n }\n}\n\n/**\n * Default credential extractor\n */\nfunction defaultExtractCredentials(req: Request): AgentCredentials {\n return extractCredentials(\n req.headers as Record<string, string | string[] | undefined>,\n req.query as Record<string, string | undefined>\n );\n}\n\n/**\n * Extract extended AstraSync credentials (X-Astra-* headers) from Express request.\n * Returns null if no AstraSync headers are present.\n */\nexport function extractAstraSyncCredentials(req: Request): AstraSyncCredentials | null {\n return extractHttpCredentials(req.headers as Record<string, string | string[] | undefined>);\n}\n\n/**\n * Default purpose extractor.\n *\n * Priority:\n * 1. Agent's declared PDLSS purpose from X-Astra-Purpose header (e.g. \"read_data:search\")\n * 2. Explicit x-purpose header\n * 3. Query parameter ?purpose=\n * 4. HTTP method → PDLSS category fallback\n */\nfunction defaultExtractPurpose(req: Request): string | undefined {\n // 1. Check agent's declared PDLSS purpose (X-Astra-Purpose header)\n const astraPurpose = req.headers['x-astra-purpose'];\n if (astraPurpose) {\n const value = Array.isArray(astraPurpose) ? astraPurpose[0] : astraPurpose;\n // Extract category from \"category:action\" format — the verify API expects the category\n const category = value.split(':')[0];\n return category;\n }\n\n // 2. Try explicit purpose header\n const purposeHeader = req.headers['x-purpose'] || req.headers['X-Purpose'];\n if (purposeHeader) {\n return Array.isArray(purposeHeader) ? purposeHeader[0] : purposeHeader;\n }\n\n // 3. Try query parameter\n if (req.query.purpose && typeof req.query.purpose === 'string') {\n return req.query.purpose;\n }\n\n // 4. Infer from HTTP method using PDLSS-compatible categories\n switch (req.method) {\n case 'GET':\n return 'read_data';\n case 'POST':\n return 'write_data';\n case 'PUT':\n case 'PATCH':\n return 'write_data';\n case 'DELETE':\n return 'delete_data';\n default:\n return 'general';\n }\n}\n\n/**\n * Match a route pattern against a path\n */\nfunction matchRoute(pattern: string, path: string): boolean {\n // Convert pattern to regex\n const regexPattern = pattern.replace(/\\*/g, '.*').replace(/\\//g, '\\\\/');\n\n const regex = new RegExp(`^${regexPattern}$`);\n return regex.test(path);\n}\n\n/**\n * Find the route configuration for a request\n */\nfunction findRouteConfig(\n routes: RouteAccessConfig[],\n path: string,\n method: string\n): RouteAccessConfig | undefined {\n return routes.find((route) => {\n const methodMatches =\n route.method === '*' || route.method.toUpperCase() === method.toUpperCase();\n const pathMatches = matchRoute(route.pattern, path);\n return methodMatches && pathMatches;\n });\n}\n\n/**\n * Default denied handler\n */\nfunction defaultOnDenied(result: VerificationResult, _req: Request, res: Response): void {\n const statusCode = result.verified ? 403 : 401;\n\n res.status(statusCode).json({\n success: false,\n error: {\n code: result.verified ? 'INSUFFICIENT_ACCESS' : 'UNAUTHORIZED',\n message: result.denialReasons?.[0] || 'Access denied',\n accessLevel: result.accessLevel,\n guidance: result.guidance,\n },\n });\n}\n\n/**\n * Create Express middleware for agent verification\n */\nexport function createMiddleware(options: ExpressMiddlewareOptions): RequestHandler {\n const {\n routes = [],\n extractCredentials: customExtractCredentials,\n extractPurpose: customExtractPurpose,\n skipPaths = [],\n onDenied = defaultOnDenied,\n recordDecisions,\n enableRuntimeChallenge = true,\n ...config\n } = options;\n\n return async (req: Request, res: Response, next: NextFunction): Promise<void> => {\n try {\n // Check if path should be skipped\n const shouldSkip = skipPaths.some((pattern) => matchRoute(pattern, req.path));\n if (shouldSkip) {\n return next();\n }\n\n // Find route configuration\n const routeConfig = findRouteConfig(routes, req.path, req.method);\n\n // If no route config, skip verification (allow through)\n if (!routeConfig) {\n return next();\n }\n\n // If route requires 'none' access, skip verification\n if (routeConfig.minAccessLevel === 'none') {\n return next();\n }\n\n // Extract credentials\n const credentials = customExtractCredentials\n ? customExtractCredentials(req)\n : defaultExtractCredentials(req);\n\n // v2.3.0: anonymous traffic no longer short-circuits client-side.\n // The server applies the endpoint's `unverifiedAgentPolicy` (deny /\n // allow_partial / allow_full) and emits the verification event +\n // blockchain record per the canonical flow. SDK forwards verbatim.\n\n // Extract purpose\n const purpose = customExtractPurpose ? customExtractPurpose(req) : defaultExtractPurpose(req);\n\n // Extract full AstraSync credentials (includes PDLSS from X-Astra-* headers)\n const astraCreds = extractAstraSyncCredentials(req);\n\n // Auto-detect counterparty URL from the request if not explicitly configured.\n // Since the SDK is installed at this endpoint, we always know the origin.\n const counterpartyUrl = config.counterpartyUrl || `${req.protocol}://${req.get('host')}`;\n\n // Step 2: Counterparty-side PDLSS pre-check — compare agent's requested PDLSS\n // against counterparty-defined maximums on the route config.\n // Rejects immediately if outside limits, BEFORE calling verify-access.\n const preCheckFailures = performCounterpartyPreCheck(routeConfig, astraCreds, purpose);\n if (preCheckFailures.length > 0) {\n const result: VerificationResult = {\n verified: false,\n accessLevel: 'none',\n denialReasons: preCheckFailures.map((f) => f.message),\n guidance: {\n message: 'Request exceeds counterparty-defined PDLSS limits.',\n registrationUrl: `${config.apiBaseUrl?.replace('/api', '')}/register`,\n documentationUrl: `${config.apiBaseUrl?.replace('/api', '')}/docs/pdlss`,\n },\n verifiedAt: new Date(),\n };\n\n req.agentVerification = result;\n\n // Fire-and-forget: notify AstraSync of the pre-check failure\n reportCounterpartyPreCheckFailure(config, {\n agentId: astraCreds?.agentId || credentials.astraId || 'unknown',\n counterpartyUrl,\n counterpartyType: config.counterpartyType || 'api',\n failures: preCheckFailures,\n requestPath: req.path,\n requestMethod: req.method,\n }).catch(() => {});\n\n onDenied(result, req, res);\n return;\n }\n\n // Step 3: Call AstraSync verify-access with runtime challenge enabled\n const shouldRecordDecisions = recordDecisions !== false;\n const forwardedFor = req.headers['x-forwarded-for'];\n const forwardedForStr = Array.isArray(forwardedFor) ? forwardedFor.join(', ') : forwardedFor;\n // X-Forwarded-For's first entry is the original client. Fall back to req.ip\n // (which Express already resolves via trust proxy settings when configured).\n const originalClientIp = forwardedForStr ? forwardedForStr.split(',')[0].trim() : req.ip;\n const agentCardUrl =\n typeof req.headers['x-astrasync-agent-card'] === 'string'\n ? (req.headers['x-astrasync-agent-card'] as string)\n : undefined;\n\n const result = await verify(config, {\n credentials,\n purpose,\n action: req.method.toLowerCase(),\n resource: req.path,\n createSession: shouldRecordDecisions,\n counterpartyUrl,\n counterpartyType: config.counterpartyType || 'api',\n enableRuntimeChallenge,\n durationRequired: astraCreds?.pdlss?.duration?.maxSessionDuration,\n callerMetadata: {\n sourceIp: originalClientIp,\n userAgent: req.headers['user-agent'] as string | undefined,\n referer: req.headers.referer as string | undefined,\n host: req.headers.host as string | undefined,\n forwardedFor: forwardedForStr,\n agentCardUrl,\n },\n });\n\n // Attach result to request\n req.agentVerification = result;\n const sessionId = (result as EnhancedVerificationResult).sessionId;\n\n // Check if access level is sufficient\n if (!hasMinimumAccess(result.accessLevel, routeConfig.minAccessLevel)) {\n if (shouldRecordDecisions && sessionId) {\n recordDecision(config, sessionId, 'denied', result.denialReasons?.[0]).catch(() => {});\n }\n onDenied(result, req, res);\n return;\n }\n\n // Check trust score requirement if specified\n if (routeConfig.minTrustScore && result.agent) {\n if (result.agent.trustScore < routeConfig.minTrustScore) {\n result.denialReasons = [\n `Trust score ${result.agent.trustScore} is below required ${routeConfig.minTrustScore}`,\n ];\n if (shouldRecordDecisions && sessionId) {\n recordDecision(config, sessionId, 'denied', result.denialReasons[0]).catch(() => {});\n }\n onDenied(result, req, res);\n return;\n }\n }\n\n // All checks passed — record grant decision\n if (shouldRecordDecisions && sessionId) {\n recordDecision(config, sessionId, 'granted').catch(() => {});\n }\n next();\n } catch (error) {\n // Log error and continue (fail open by default)\n console.error('[VerificationGateway] Middleware error:', error);\n next();\n }\n };\n}\n\n/**\n * Create a middleware that requires a specific access level\n */\nexport function requireAccess(\n minAccessLevel: AccessLevel,\n options: ExpressMiddlewareOptions\n): RequestHandler {\n return createMiddleware({\n ...options,\n routes: [{ pattern: '*', method: '*', minAccessLevel }],\n });\n}\n\n/**\n * Create a middleware that only verifies (doesn't block)\n */\nexport function verifyOnly(\n options: Omit<ExpressMiddlewareOptions, 'routes' | 'onDenied'>\n): RequestHandler {\n return createMiddleware({\n ...options,\n routes: [{ pattern: '*', method: '*', minAccessLevel: 'none' }],\n });\n}\n"],"mappings":";AAWO,IAAM,yBAAsD;AAAA,EACjE,MAAM;AAAA,EACN,UAAU;AAAA,EACV,aAAa;AAAA,EACb,UAAU;AAAA,EACV,MAAM;AAAA,EACN,UAAU;AACZ;AAuCO,SAAS,cAAc,OAA2B;AACvD,MAAI,SAAS,GAAI,QAAO;AACxB,MAAI,SAAS,GAAI,QAAO;AACxB,MAAI,SAAS,GAAI,QAAO;AACxB,SAAO;AACT;AAKO,SAAS,iBAAiB,QAAqB,UAAgC;AACpF,SAAO,uBAAuB,MAAM,KAAK,uBAAuB,QAAQ;AAC1E;;;ACtCA,IAAM,iBAAyC;AAAA,EAC7C,YAAY;AAAA,EACZ,oBAAoB;AAAA;AAAA,EAEpB,UAAU;AAAA;AAAA,EACV,OAAO;AACT;AAOA,IAAI,qBAAqB;AAGzB,IAAI,0BAA0B;AAE9B,eAAe,iBAAiB,YAAoB,OAAgC;AAClF,uBAAqB;AACrB,MAAI;AACF,UAAM,WAAW,GAAG,UAAU;AAK9B,UAAM,WAAW,MAAM,MAAM,UAAU,EAAE,QAAQ,OAAO,CAAC;AACzD,UAAM,cAAc,SAAS,QAAQ,IAAI,cAAc,KAAK;AAC5D,QAAI,YAAY,WAAW,WAAW,GAAG;AACvC,cAAQ;AAAA,QACN,qCAAqC,UAAU,kCAAkC,WAAW;AAAA,MAI9F;AAAA,IACF,WAAW,OAAO;AAChB,cAAQ;AAAA,QACN,+CAA+C,UAAU,mBAAmB,WAAW;AAAA,MACzF;AAAA,IACF;AAAA,EACF,SAAS,KAAK;AACZ,QAAI,OAAO;AACT,cAAQ,IAAI,2DAA2D,OAAO,GAAG,CAAC,EAAE;AAAA,IACtF;AAAA,EACF;AACF;AAKA,IAAM,oBAAoB,oBAAI,IAA+D;AAK7F,SAAS,YAAY,aAAuC;AAC1D,SAAO,GAAG,YAAY,WAAW,EAAE,IAAI,YAAY,UAAU,EAAE,IAAI,YAAY,OAAO,EAAE;AAC1F;AAKA,SAAS,gBAAgB,aAA0D;AACjF,QAAM,MAAM,YAAY,WAAW;AACnC,QAAM,SAAS,kBAAkB,IAAI,GAAG;AAExC,MAAI,UAAU,OAAO,YAAY,KAAK,IAAI,GAAG;AAC3C,WAAO,OAAO;AAAA,EAChB;AAEA,MAAI,QAAQ;AACV,sBAAkB,OAAO,GAAG;AAAA,EAC9B;AAEA,SAAO;AACT;AAKA,SAAS,YACP,aACA,QACA,YACM;AACN,QAAM,MAAM,YAAY,WAAW;AACnC,oBAAkB,IAAI,KAAK;AAAA,IACzB;AAAA,IACA,WAAW,KAAK,IAAI,IAAI,aAAa;AAAA,EACvC,CAAC;AACH;AAYO,SAAS,mBACd,SACA,OACkB;AAClB,QAAM,cAAgC,CAAC;AAKvC,QAAM,gBACJ,QAAQ,YAAY,KACpB,QAAQ,YAAY,KACpB,QAAQ,YAAY,KACpB,QAAQ,iBAAiB,KACzB,QAAQ,iBAAiB,KACzB,QAAQ,kBAAkB,KAC1B,QAAQ,kBAAkB,KAC1B,QAAQ,kBAAkB;AAC5B,MAAI,eAAe;AACjB,gBAAY,UAAU,MAAM,QAAQ,aAAa,IAAI,cAAc,CAAC,IAAI;AAAA,EAC1E;AAGA,QAAM,eAAe,QAAQ,WAAW,KAAK,QAAQ,WAAW,KAAK,QAAQ,WAAW;AACxF,MAAI,cAAc;AAChB,gBAAY,SAAS,MAAM,QAAQ,YAAY,IAAI,aAAa,CAAC,IAAI;AAAA,EACvE;AAGA,QAAM,aAAa,QAAQ,eAAe,KAAK,QAAQ,eAAe;AACtE,MAAI,YAAY;AACd,UAAM,YAAY,MAAM,QAAQ,UAAU,IAAI,WAAW,CAAC,IAAI;AAC9D,gBAAY,sBAAsB;AAElC,QAAI,UAAU,WAAW,SAAS,GAAG;AACnC,kBAAY,MAAM,UAAU,MAAM,CAAC;AAAA,IACrC;AAAA,EACF;AAGA,MAAI,OAAO;AACT,QAAI,MAAM,WAAW,CAAC,YAAY,SAAS;AACzC,kBAAY,UAAU,MAAM;AAAA,IAC9B;AACA,QAAI,MAAM,UAAU,CAAC,YAAY,QAAQ;AACvC,kBAAY,SAAS,MAAM;AAAA,IAC7B;AAAA,EACF;AAEA,SAAO;AACT;AAYA,SAAS,uBAAuB,QAAuB,QAAqC;AAC1F,QAAM,WAAyB;AAAA,IAC7B,SACE;AAAA,IACF,iBAAiB,GAAG,OAAO,WAAW,QAAQ,QAAQ,EAAE,CAAC;AAAA,IACzD,kBAAkB,GAAG,OAAO,WAAW,QAAQ,QAAQ,EAAE,CAAC;AAAA,IAC1D,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL,UAAU;AAAA,IACV,aAAa;AAAA,IACb;AAAA,IACA,eAAe,SAAS,CAAC,MAAM,IAAI,CAAC,qCAAqC;AAAA,IACzE,YAAY,oBAAI,KAAK;AAAA,EACvB;AACF;AAKA,eAAe,oBACb,QACA,SAyEC;AACD,QAAM,EAAE,aAAa,GAAG,YAAY,IAAI;AAIxC,QAAM,OAAgC;AAAA,IACpC,GAAI,YAAY,WAAW,EAAE,SAAS,YAAY,QAAQ;AAAA,IAC1D,SAAS,YAAY,WAAW;AAAA,EAClC;AAGA,MAAI,YAAY,OAAQ,MAAK,SAAS,YAAY;AAClD,MAAI,YAAY,aAAc,MAAK,eAAe,YAAY;AAC9D,MAAI,YAAY,SAAU,MAAK,WAAW,YAAY;AACtD,MAAI,YAAY,aAAc,MAAK,eAAe,YAAY;AAC9D,MAAI,YAAY,iBAAkB,MAAK,mBAAmB,YAAY;AACtE,MAAI,YAAY,SAAU,MAAK,WAAW,YAAY;AACtD,MAAI,YAAY,kBAAmB,MAAK,oBAAoB,YAAY;AACxE,MAAI,YAAY,cAAe,MAAK,gBAAgB,YAAY;AAChE,MAAI,YAAY,kBAAkB,OAAW,MAAK,gBAAgB,YAAY;AAE9E,MAAI,YAAY;AACd,SAAK,yBAAyB,YAAY;AAC5C,MAAI,YAAY,cAAe,MAAK,gBAAgB,YAAY;AAChE,MAAI,YAAY,iBAAkB,MAAK,mBAAmB,YAAY;AACtE,MAAI,YAAY,iBAAkB,MAAK,mBAAmB,YAAY;AACtE,MAAI,YAAY,gBAAiB,MAAK,kBAAkB,YAAY;AACpE,MAAI,OAAO,eAAgB,MAAK,iBAAiB,OAAO;AACxD,MAAI,YAAY;AACd,SAAK,0BAA0B,YAAY;AAI7C,MAAI,YAAY,kBAAkB,YAAY,YAAY,YAAY,WAAW;AAC/E,UAAM,OAAO;AAAA,MACX,GAAI,YAAY,YAAY,EAAE,UAAU,YAAY,SAAS;AAAA,MAC7D,GAAI,YAAY,aAAa,EAAE,WAAW,YAAY,UAAU;AAAA,MAChE,GAAG,YAAY;AAAA,IACjB;AACA,QAAI,OAAO,KAAK,IAAI,EAAE,SAAS,EAAG,MAAK,iBAAiB;AAAA,EAC1D;AAGA,QAAM,UAAkC;AAAA,IACtC,gBAAgB;AAAA,IAChB,GAAG,OAAO;AAAA,EACZ;AAKA,MAAI,YAAY,qBAAqB;AACnC,YAAQ,eAAe,IAAI,YAAY;AAAA,EACzC,WAAW,OAAO,QAAQ;AACxB,YAAQ,eAAe,IAAI,UAAU,OAAO,MAAM;AAAA,EACpD;AAEA,MAAI,OAAO,QAAQ;AACjB,YAAQ,WAAW,IAAI,OAAO;AAAA,EAChC;AAEA,MAAI;AACF,UAAM,WAAW,MAAM,MAAM,GAAG,OAAO,UAAU,yBAAyB;AAAA,MACxE,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,IAAI;AAAA,IAC3B,CAAC;AAED,UAAM,OAAO,MAAM,SAAS,KAAK;AAEjC,QAAI,CAAC,SAAS,IAAI;AAChB,aAAO;AAAA,QACL,SAAS;AAAA,QACT,OAAO,KAAK,WAAW,KAAK,SAAS,gBAAgB,SAAS,MAAM;AAAA,MACtE;AAAA,IACF;AAEA,WAAO;AAAA,EACT,SAAS,OAAO;AACd,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU;AACzD,WAAO;AAAA,MACL,SAAS;AAAA,MACT,OAAO,qCAAqC,OAAO;AAAA,IACrD;AAAA,EACF;AACF;AAKA,eAAsB,OACpB,QACA,SAC6B;AAC7B,QAAM,eAAe,EAAE,GAAG,gBAAgB,GAAG,OAAO;AAGpD,MAAI,CAAC,sBAAsB,CAAC,aAAa,qBAAqB,aAAa,YAAY;AACrF,SAAK,iBAAiB,aAAa,YAAY,aAAa,KAAK;AAAA,EACnE;AAGA,MACE,CAAC,4BACA,OAAO,kBAAkB,UAAa,OAAO,yBAAyB,SACvE;AACA,8BAA0B;AAC1B,YAAQ;AAAA,MACN;AAAA,IAIF;AAAA,EACF;AAQA,MAAI,aAAa,YAAY,aAAa,WAAW,GAAG;AACtD,UAAM,SAAS,gBAAgB,QAAQ,WAAW;AAClD,QAAI,QAAQ;AACV,UAAI,aAAa,OAAO;AACtB,gBAAQ,IAAI,+CAA+C;AAAA,MAC7D;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAGA,QAAM,kBAAkB,EAAE,GAAG,QAAQ;AACrC,MAAI,CAAC,gBAAgB,mBAAmB,aAAa,iBAAiB;AACpE,oBAAgB,kBAAkB,aAAa;AAAA,EACjD;AACA,MAAI,CAAC,gBAAgB,oBAAoB,aAAa,kBAAkB;AACtE,oBAAgB,mBAAmB,aAAa;AAAA,EAClD;AAGA,MAAI,aAAa,OAAO;AACtB,YAAQ,IAAI,iDAAiD;AAAA,EAC/D;AAEA,QAAM,cAAc,MAAM,oBAAoB,cAAc,eAAe;AAG3E,MAAI,CAAC,YAAY,SAAS;AACxB,WAAO,uBAAuB,cAAc,YAAY,KAAK;AAAA,EAC/D;AAGA,MAAI,CAAC,YAAY,QAAQ,SAAS;AAChC,UAAMA,UAAqC;AAAA,MACzC,UAAU;AAAA,MACV,aAAa;AAAA,MACb,eAAe,YAAY,QAAQ,SAAS,CAAC,YAAY,OAAO,MAAM,IAAI,CAAC,eAAe;AAAA,MAC1F,gBAAgB,YAAY,QAAQ;AAAA,MACpC,kBAAkB,YAAY,QAAQ;AAAA,MACtC,UAAU;AAAA,QACR,SAAS,YAAY,QAAQ,UAAU;AAAA,QACvC,iBAAiB,GAAG,aAAa,YAAY,QAAQ,QAAQ,EAAE,CAAC;AAAA,QAChE,kBAAkB,GAAG,aAAa,YAAY,QAAQ,QAAQ,EAAE,CAAC;AAAA,MACnE;AAAA,MACA,YAAY,oBAAI,KAAK;AAAA;AAAA,MAErB,WAAY,YAAwC;AAAA,MACpD,gBAAiB,YACd;AAAA,MACH,uBAAwB,YAAwC;AAAA,IAGlE;AAEA,WAAOA;AAAA,EACT;AAGA,QAAM,QAAmC,YAAY,QACjD;AAAA,IACE,SAAS,YAAY,MAAM;AAAA,IAC3B,MAAM,YAAY,MAAM;AAAA,IACxB,YAAY,YAAY,MAAM;AAAA,IAC9B,YAAY,cAAc,YAAY,MAAM,UAAU;AAAA,IACtD,oBAAoB,YAAY,MAAM,qBAAqB;AAAA,IAC3D,QAAQ,YAAY,MAAM;AAAA,EAC5B,IACA;AAEJ,QAAM,YAA2C,YAAY,YACzD;AAAA,IACE,UAAU,YAAY,UAAU;AAAA,IAChC,MAAM,YAAY,UAAU;AAAA,IAC5B,YAAY,YAAY,UAAU,cAAc;AAAA,IAChD,UAAU,YAAY,UAAU;AAAA,EAClC,IACA;AAEJ,QAAM,eAAiD,YAAY,eAC/D;AAAA,IACE,MAAM,YAAY,aAAa;AAAA,IAC/B,UAAU,YAAY,aAAa;AAAA,IACnC,YAAY,YAAY,aAAa;AAAA,EACvC,IACA;AAKJ,QAAM,sBAAsB,YAAY;AAMxC,QAAM,cAA2B,YAAY,QAAQ,eAAe;AAEpE,QAAM,SAAqC;AAAA,IACzC,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,eAAe,YAAY,QAAQ;AAAA,IACnC;AAAA,IACA,gBAAgB,YAAY,QAAQ;AAAA,IACpC,kBAAkB,YAAY,QAAQ;AAAA,IACtC,YAAY,oBAAI,KAAK;AAAA,IACrB,UAAU,aAAa;AAAA;AAAA,IAEvB,WAAY,YAAwC;AAAA,IACpD,kBAAmB,YAAwC;AAAA,IAG3D,eAAgB,YAAwC;AAAA,IAGxD,gBAAiB,YACd;AAAA,IACH,uBAAwB,YAAwC;AAAA,EAGlE;AAGA,MAAI,OAAO,mBAAmB,QAAQ;AACpC,WAAO,WAAW;AAClB,WAAO,cAAc;AACrB,WAAO,gBAAgB,OAAO,yBAAyB;AAAA,MACrD;AAAA,IACF;AACA,QAAI,OAAO,kBAAkB;AAC3B,aAAO,WAAW;AAAA,QAChB,SAAS,wBAAwB,OAAO,iBAAiB,UAAU,0BAA0B;AAAA,QAC7F,iBAAiB,GAAG,aAAa,YAAY,QAAQ,QAAQ,EAAE,CAAC;AAAA,QAChE,kBAAkB,GAAG,aAAa,YAAY,QAAQ,QAAQ,EAAE,CAAC;AAAA,MACnE;AAAA,IACF;AAAA,EACF,WAAW,OAAO,mBAAmB,oBAAoB;AACvD,WAAO,iBAAiB;AACxB,QAAI,uBAAuB,OAAO,WAAW,IAAI,uBAAuB,WAAW,GAAG;AACpF,aAAO,cAAc;AAAA,IACvB;AACA,WAAO,gBAAgB,OAAO,yBAAyB,CAAC,+BAA+B;AAAA,EACzF;AAGA,MAAI,aAAa,YAAY,aAAa,WAAW,KAAK,OAAO,mBAAmB,QAAQ;AAC1F,gBAAY,QAAQ,aAAa,QAAQ,aAAa,QAAQ;AAAA,EAChE;AAEA,SAAO;AACT;AAMA,eAAsB,eACpB,QACA,WACA,UACA,QACe;AACf,QAAM,UAAkC,EAAE,gBAAgB,mBAAmB;AAC7E,MAAI,OAAO,QAAQ;AACjB,YAAQ,eAAe,IAAI,UAAU,OAAO,MAAM;AAClD,YAAQ,WAAW,IAAI,OAAO;AAAA,EAChC;AAEA,QAAM,MAAM,GAAG,OAAO,UAAU,yBAAyB,SAAS,aAAa;AAAA,IAC7E,QAAQ;AAAA,IACR;AAAA,IACA,MAAM,KAAK,UAAU,EAAE,UAAU,OAAO,CAAC;AAAA,EAC3C,CAAC,EAAE,MAAM,MAAM;AAAA,EAEf,CAAC;AACH;AA6DA,eAAsB,kCACpB,QACA,MAae;AACf,QAAM,aAAa,OAAO,cAAc,eAAe;AAEvD,QAAM,MAAM,GAAG,UAAU,yDAAyD;AAAA,IAChF,QAAQ;AAAA,IACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,IAC9C,MAAM,KAAK,UAAU,IAAI;AAAA,EAC3B,CAAC,EAAE,MAAM,MAAM;AAAA,EAEf,CAAC;AACH;;;AChqBA,IAAM,gBAAgB;AA0Cf,SAAS,uBACd,SAC6B;AAC7B,QAAM,WAAW,CAAC,QAAoC;AACpD,UAAM,IAAI,QAAQ,GAAG,KAAK,QAAQ,IAAI,YAAY,CAAC;AACnD,WAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI;AAAA,EACnC;AAEA,QAAM,UAAU,SAAS,GAAG,aAAa,IAAI,KAAK,SAAS,YAAY;AACvE,MAAI,CAAC,QAAS,QAAO;AAErB,QAAM,cAAoC,EAAE,QAAQ;AAEpD,QAAM,YAAY,SAAS,GAAG,aAAa,QAAQ,KAAK,SAAS,gBAAgB;AACjF,MAAI,UAAW,aAAY,YAAY;AAEvC,QAAM,eAAe,SAAS,GAAG,aAAa,WAAW,KAAK,SAAS,mBAAmB;AAC1F,MAAI,aAAc,aAAY,eAAe;AAE7C,QAAM,UAAU,SAAS,GAAG,aAAa,SAAS,KAAK,SAAS,iBAAiB;AACjF,MAAI,SAAS;AACX,UAAM,CAAC,UAAU,MAAM,IAAI,QAAQ,MAAM,GAAG;AAC5C,gBAAY,QAAQ;AAAA,MAClB,GAAG,YAAY;AAAA,MACf,SAAS,EAAE,UAAU,OAAO;AAAA,IAC9B;AAAA,EACF;AAEA,QAAM,WAAW,SAAS,GAAG,aAAa,UAAU,KAAK,SAAS,kBAAkB;AACpF,MAAI,UAAU;AACZ,gBAAY,QAAQ;AAAA,MAClB,GAAG,YAAY;AAAA,MACf,UAAU,EAAE,oBAAoB,SAAS,UAAU,EAAE,EAAE;AAAA,IACzD;AAAA,EACF;AAEA,QAAM,QAAQ,SAAS,GAAG,aAAa,OAAO,KAAK,SAAS,eAAe;AAC3E,MAAI,OAAO;AACT,gBAAY,QAAQ;AAAA,MAClB,GAAG,YAAY;AAAA,MACf,OAAO,EAAE,cAAc,MAAM;AAAA,IAC/B;AAAA,EACF;AAEA,SAAO;AACT;;;AClFO,SAAS,4BACd,aACA,YACA,SAC+B;AAC/B,QAAM,WAA0C,CAAC;AAGjD,MAAI,YAAY,mBAAmB,YAAY,gBAAgB,SAAS,KAAK,SAAS;AACpF,QAAI,CAAC,YAAY,gBAAgB,SAAS,OAAO,GAAG;AAClD,eAAS,KAAK;AAAA,QACZ,OAAO;AAAA,QACP,WAAW;AAAA,QACX,OAAO,YAAY;AAAA,QACnB,SAAS,YAAY,OAAO,kCAAkC,YAAY,gBAAgB,KAAK,IAAI,CAAC;AAAA,MACtG,CAAC;AAAA,IACH;AAAA,EACF;AAGA,MAAI,YAAY,oBAAoB,YAAY,iBAAiB,SAAS,KAAK,SAAS;AACtF,QAAI,CAAC,YAAY,iBAAiB,SAAS,OAAO,GAAG;AACnD,eAAS,KAAK;AAAA,QACZ,OAAO;AAAA,QACP,WAAW;AAAA,QACX,OAAO,YAAY;AAAA,QACnB,SAAS,YAAY,OAAO,mCAAmC,YAAY,iBAAiB,KAAK,IAAI,CAAC;AAAA,MACxG,CAAC;AAAA,IACH;AAAA,EACF;AAGA,MAAI,YAAY,eAAe,YAAY,OAAO,UAAU,oBAAoB;AAC9E,UAAM,YAAY,WAAW,MAAM,SAAS;AAC5C,QAAI,YAAY,YAAY,aAAa;AACvC,eAAS,KAAK;AAAA,QACZ,OAAO;AAAA,QACP;AAAA,QACA,OAAO,YAAY;AAAA,QACnB,SAAS,sBAAsB,SAAS,qBAAqB,YAAY,WAAW;AAAA,MACtF,CAAC;AAAA,IACH;AAAA,EACF;AAGA,MACE,YAAY,wBACZ,YAAY,qBAAqB,SAAS,KAC1C,YAAY,OAAO,OAAO,cAC1B;AACA,UAAM,YAAY,WAAW,MAAM,MAAM;AACzC,QAAI,CAAC,YAAY,qBAAqB,SAAS,SAAS,GAAG;AACzD,eAAS,KAAK;AAAA,QACZ,OAAO;AAAA,QACP;AAAA,QACA,OAAO,YAAY;AAAA,QACnB,SAAS,iBAAiB,SAAS,kCAAkC,YAAY,qBAAqB,KAAK,IAAI,CAAC;AAAA,MAClH,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AACT;;;AChBA,SAAS,0BAA0B,KAAgC;AACjE,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,IAAI;AAAA,EACN;AACF;AAMO,SAAS,4BAA4B,KAA2C;AACrF,SAAO,uBAAuB,IAAI,OAAwD;AAC5F;AAWA,SAAS,sBAAsB,KAAkC;AAE/D,QAAM,eAAe,IAAI,QAAQ,iBAAiB;AAClD,MAAI,cAAc;AAChB,UAAM,QAAQ,MAAM,QAAQ,YAAY,IAAI,aAAa,CAAC,IAAI;AAE9D,UAAM,WAAW,MAAM,MAAM,GAAG,EAAE,CAAC;AACnC,WAAO;AAAA,EACT;AAGA,QAAM,gBAAgB,IAAI,QAAQ,WAAW,KAAK,IAAI,QAAQ,WAAW;AACzE,MAAI,eAAe;AACjB,WAAO,MAAM,QAAQ,aAAa,IAAI,cAAc,CAAC,IAAI;AAAA,EAC3D;AAGA,MAAI,IAAI,MAAM,WAAW,OAAO,IAAI,MAAM,YAAY,UAAU;AAC9D,WAAO,IAAI,MAAM;AAAA,EACnB;AAGA,UAAQ,IAAI,QAAQ;AAAA,IAClB,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAKA,SAAS,WAAW,SAAiB,MAAuB;AAE1D,QAAM,eAAe,QAAQ,QAAQ,OAAO,IAAI,EAAE,QAAQ,OAAO,KAAK;AAEtE,QAAM,QAAQ,IAAI,OAAO,IAAI,YAAY,GAAG;AAC5C,SAAO,MAAM,KAAK,IAAI;AACxB;AAKA,SAAS,gBACP,QACA,MACA,QAC+B;AAC/B,SAAO,OAAO,KAAK,CAAC,UAAU;AAC5B,UAAM,gBACJ,MAAM,WAAW,OAAO,MAAM,OAAO,YAAY,MAAM,OAAO,YAAY;AAC5E,UAAM,cAAc,WAAW,MAAM,SAAS,IAAI;AAClD,WAAO,iBAAiB;AAAA,EAC1B,CAAC;AACH;AAKA,SAAS,gBAAgB,QAA4B,MAAe,KAAqB;AACvF,QAAM,aAAa,OAAO,WAAW,MAAM;AAE3C,MAAI,OAAO,UAAU,EAAE,KAAK;AAAA,IAC1B,SAAS;AAAA,IACT,OAAO;AAAA,MACL,MAAM,OAAO,WAAW,wBAAwB;AAAA,MAChD,SAAS,OAAO,gBAAgB,CAAC,KAAK;AAAA,MACtC,aAAa,OAAO;AAAA,MACpB,UAAU,OAAO;AAAA,IACnB;AAAA,EACF,CAAC;AACH;AAKO,SAAS,iBAAiB,SAAmD;AAClF,QAAM;AAAA,IACJ,SAAS,CAAC;AAAA,IACV,oBAAoB;AAAA,IACpB,gBAAgB;AAAA,IAChB,YAAY,CAAC;AAAA,IACb,WAAW;AAAA,IACX;AAAA,IACA,yBAAyB;AAAA,IACzB,GAAG;AAAA,EACL,IAAI;AAEJ,SAAO,OAAO,KAAc,KAAe,SAAsC;AAC/E,QAAI;AAEF,YAAM,aAAa,UAAU,KAAK,CAAC,YAAY,WAAW,SAAS,IAAI,IAAI,CAAC;AAC5E,UAAI,YAAY;AACd,eAAO,KAAK;AAAA,MACd;AAGA,YAAM,cAAc,gBAAgB,QAAQ,IAAI,MAAM,IAAI,MAAM;AAGhE,UAAI,CAAC,aAAa;AAChB,eAAO,KAAK;AAAA,MACd;AAGA,UAAI,YAAY,mBAAmB,QAAQ;AACzC,eAAO,KAAK;AAAA,MACd;AAGA,YAAM,cAAc,2BAChB,yBAAyB,GAAG,IAC5B,0BAA0B,GAAG;AAQjC,YAAM,UAAU,uBAAuB,qBAAqB,GAAG,IAAI,sBAAsB,GAAG;AAG5F,YAAM,aAAa,4BAA4B,GAAG;AAIlD,YAAM,kBAAkB,OAAO,mBAAmB,GAAG,IAAI,QAAQ,MAAM,IAAI,IAAI,MAAM,CAAC;AAKtF,YAAM,mBAAmB,4BAA4B,aAAa,YAAY,OAAO;AACrF,UAAI,iBAAiB,SAAS,GAAG;AAC/B,cAAMC,UAA6B;AAAA,UACjC,UAAU;AAAA,UACV,aAAa;AAAA,UACb,eAAe,iBAAiB,IAAI,CAAC,MAAM,EAAE,OAAO;AAAA,UACpD,UAAU;AAAA,YACR,SAAS;AAAA,YACT,iBAAiB,GAAG,OAAO,YAAY,QAAQ,QAAQ,EAAE,CAAC;AAAA,YAC1D,kBAAkB,GAAG,OAAO,YAAY,QAAQ,QAAQ,EAAE,CAAC;AAAA,UAC7D;AAAA,UACA,YAAY,oBAAI,KAAK;AAAA,QACvB;AAEA,YAAI,oBAAoBA;AAGxB,0CAAkC,QAAQ;AAAA,UACxC,SAAS,YAAY,WAAW,YAAY,WAAW;AAAA,UACvD;AAAA,UACA,kBAAkB,OAAO,oBAAoB;AAAA,UAC7C,UAAU;AAAA,UACV,aAAa,IAAI;AAAA,UACjB,eAAe,IAAI;AAAA,QACrB,CAAC,EAAE,MAAM,MAAM;AAAA,QAAC,CAAC;AAEjB,iBAASA,SAAQ,KAAK,GAAG;AACzB;AAAA,MACF;AAGA,YAAM,wBAAwB,oBAAoB;AAClD,YAAM,eAAe,IAAI,QAAQ,iBAAiB;AAClD,YAAM,kBAAkB,MAAM,QAAQ,YAAY,IAAI,aAAa,KAAK,IAAI,IAAI;AAGhF,YAAM,mBAAmB,kBAAkB,gBAAgB,MAAM,GAAG,EAAE,CAAC,EAAE,KAAK,IAAI,IAAI;AACtF,YAAM,eACJ,OAAO,IAAI,QAAQ,wBAAwB,MAAM,WAC5C,IAAI,QAAQ,wBAAwB,IACrC;AAEN,YAAM,SAAS,MAAM,OAAO,QAAQ;AAAA,QAClC;AAAA,QACA;AAAA,QACA,QAAQ,IAAI,OAAO,YAAY;AAAA,QAC/B,UAAU,IAAI;AAAA,QACd,eAAe;AAAA,QACf;AAAA,QACA,kBAAkB,OAAO,oBAAoB;AAAA,QAC7C;AAAA,QACA,kBAAkB,YAAY,OAAO,UAAU;AAAA,QAC/C,gBAAgB;AAAA,UACd,UAAU;AAAA,UACV,WAAW,IAAI,QAAQ,YAAY;AAAA,UACnC,SAAS,IAAI,QAAQ;AAAA,UACrB,MAAM,IAAI,QAAQ;AAAA,UAClB,cAAc;AAAA,UACd;AAAA,QACF;AAAA,MACF,CAAC;AAGD,UAAI,oBAAoB;AACxB,YAAM,YAAa,OAAsC;AAGzD,UAAI,CAAC,iBAAiB,OAAO,aAAa,YAAY,cAAc,GAAG;AACrE,YAAI,yBAAyB,WAAW;AACtC,yBAAe,QAAQ,WAAW,UAAU,OAAO,gBAAgB,CAAC,CAAC,EAAE,MAAM,MAAM;AAAA,UAAC,CAAC;AAAA,QACvF;AACA,iBAAS,QAAQ,KAAK,GAAG;AACzB;AAAA,MACF;AAGA,UAAI,YAAY,iBAAiB,OAAO,OAAO;AAC7C,YAAI,OAAO,MAAM,aAAa,YAAY,eAAe;AACvD,iBAAO,gBAAgB;AAAA,YACrB,eAAe,OAAO,MAAM,UAAU,sBAAsB,YAAY,aAAa;AAAA,UACvF;AACA,cAAI,yBAAyB,WAAW;AACtC,2BAAe,QAAQ,WAAW,UAAU,OAAO,cAAc,CAAC,CAAC,EAAE,MAAM,MAAM;AAAA,YAAC,CAAC;AAAA,UACrF;AACA,mBAAS,QAAQ,KAAK,GAAG;AACzB;AAAA,QACF;AAAA,MACF;AAGA,UAAI,yBAAyB,WAAW;AACtC,uBAAe,QAAQ,WAAW,SAAS,EAAE,MAAM,MAAM;AAAA,QAAC,CAAC;AAAA,MAC7D;AACA,WAAK;AAAA,IACP,SAAS,OAAO;AAEd,cAAQ,MAAM,2CAA2C,KAAK;AAC9D,WAAK;AAAA,IACP;AAAA,EACF;AACF;AAKO,SAAS,cACd,gBACA,SACgB;AAChB,SAAO,iBAAiB;AAAA,IACtB,GAAG;AAAA,IACH,QAAQ,CAAC,EAAE,SAAS,KAAK,QAAQ,KAAK,eAAe,CAAC;AAAA,EACxD,CAAC;AACH;AAKO,SAAS,WACd,SACgB;AAChB,SAAO,iBAAiB;AAAA,IACtB,GAAG;AAAA,IACH,QAAQ,CAAC,EAAE,SAAS,KAAK,QAAQ,KAAK,gBAAgB,OAAO,CAAC;AAAA,EAChE,CAAC;AACH;","names":["result","result"]}
|
|
1
|
+
{"version":3,"sources":["../../src/access-levels.ts","../../src/verify.ts","../../src/transport/http.ts","../../src/pdlss-pre-check.ts","../../src/adapters/express.ts"],"sourcesContent":["/**\n * AstraSync Universal Verification Gateway - Access Level Definitions\n *\n * Defines the hierarchy and capabilities of each access level.\n */\n\nimport type { AccessLevel, TrustLevel } from './types';\n\n/**\n * Access level hierarchy (higher number = more access)\n */\nexport const ACCESS_LEVEL_HIERARCHY: Record<AccessLevel, number> = {\n none: 0,\n guidance: 1,\n 'read-only': 2,\n standard: 3,\n full: 4,\n internal: 5,\n};\n\n/**\n * Access level descriptions for UI\n */\nexport const ACCESS_LEVEL_DESCRIPTIONS: Record<AccessLevel, string> = {\n none: 'No access - credentials required',\n guidance: 'Guidance mode - registration information provided',\n 'read-only': 'Read-only access - can browse but not modify',\n standard: 'Standard access - normal operations per PDLSS policy',\n full: 'Full access - all operations for high-trust agents',\n internal: 'Internal access - organization member privileges',\n};\n\n/**\n * Default trust score thresholds for access levels\n */\nexport const DEFAULT_TRUST_THRESHOLDS: Record<AccessLevel, number> = {\n none: 0,\n guidance: 0,\n 'read-only': 20,\n standard: 40,\n full: 70,\n internal: 0, // Internal is based on org membership, not score\n};\n\n/**\n * Trust level score ranges\n */\nexport const TRUST_LEVEL_RANGES: Record<TrustLevel, { min: number; max: number }> = {\n BRONZE: { min: 0, max: 39 },\n SILVER: { min: 40, max: 59 },\n GOLD: { min: 60, max: 79 },\n PLATINUM: { min: 80, max: 100 },\n};\n\n/**\n * Determine trust level from score\n */\nexport function getTrustLevel(score: number): TrustLevel {\n if (score >= 80) return 'PLATINUM';\n if (score >= 60) return 'GOLD';\n if (score >= 40) return 'SILVER';\n return 'BRONZE';\n}\n\n/**\n * Check if access level A is greater than or equal to access level B\n */\nexport function hasMinimumAccess(actual: AccessLevel, required: AccessLevel): boolean {\n return ACCESS_LEVEL_HIERARCHY[actual] >= ACCESS_LEVEL_HIERARCHY[required];\n}\n\n/**\n * Get the highest access level for a given trust score\n */\nexport function getAccessLevelForScore(\n trustScore: number,\n thresholds: Record<AccessLevel, number> = DEFAULT_TRUST_THRESHOLDS\n): AccessLevel {\n if (trustScore >= thresholds.full) return 'full';\n if (trustScore >= thresholds.standard) return 'standard';\n if (trustScore >= thresholds['read-only']) return 'read-only';\n return 'guidance';\n}\n\n/**\n * Determine access level from verification result\n */\nexport function determineAccessLevel(\n verified: boolean,\n trustScore: number,\n isOrgMember: boolean,\n customThresholds?: Partial<Record<AccessLevel, number>>\n): AccessLevel {\n if (!verified) {\n return 'guidance';\n }\n\n if (isOrgMember) {\n return 'internal';\n }\n\n const thresholds = {\n ...DEFAULT_TRUST_THRESHOLDS,\n ...customThresholds,\n };\n\n return getAccessLevelForScore(trustScore, thresholds);\n}\n\n/**\n * Access capabilities per level\n */\nexport interface AccessCapabilities {\n canRead: boolean;\n canWrite: boolean;\n canDelete: boolean;\n canAdmin: boolean;\n canAccessInternal: boolean;\n maxTransactionValue?: number;\n allowedPurposes?: string[];\n}\n\n/**\n * Get capabilities for an access level\n */\nexport function getCapabilities(accessLevel: AccessLevel): AccessCapabilities {\n switch (accessLevel) {\n case 'none':\n return {\n canRead: false,\n canWrite: false,\n canDelete: false,\n canAdmin: false,\n canAccessInternal: false,\n };\n case 'guidance':\n return {\n canRead: false,\n canWrite: false,\n canDelete: false,\n canAdmin: false,\n canAccessInternal: false,\n };\n case 'read-only':\n return {\n canRead: true,\n canWrite: false,\n canDelete: false,\n canAdmin: false,\n canAccessInternal: false,\n };\n case 'standard':\n return {\n canRead: true,\n canWrite: true,\n canDelete: false,\n canAdmin: false,\n canAccessInternal: false,\n };\n case 'full':\n return {\n canRead: true,\n canWrite: true,\n canDelete: true,\n canAdmin: false,\n canAccessInternal: false,\n };\n case 'internal':\n return {\n canRead: true,\n canWrite: true,\n canDelete: true,\n canAdmin: true,\n canAccessInternal: true,\n };\n default:\n return {\n canRead: false,\n canWrite: false,\n canDelete: false,\n canAdmin: false,\n canAccessInternal: false,\n };\n }\n}\n","/**\n * AstraSync Universal Verification Gateway - Core Verification Logic\n *\n * This module handles the core verification logic, calling the AstraSync API\n * and processing the response into a standardized VerificationResult.\n */\n\nimport type {\n GatewayConfig,\n AgentCredentials,\n VerificationRequest,\n VerificationResult,\n VerifiedAgent,\n VerifiedDeveloper,\n VerifiedOrganization,\n GuidanceInfo,\n AccessLevel,\n EnhancedVerificationResult,\n TokenGuidance,\n RuntimeChallengeResult,\n} from './types';\nimport { getTrustLevel, ACCESS_LEVEL_HIERARCHY } from './access-levels';\n\n/**\n * Default configuration values\n *\n * apiBaseUrl matches the OpenAPI authoritative server (https://astrasync.ai/api\n * for prod, https://staging.astrasync.ai/api for staging). Always include the\n * /api path prefix when overriding — registration / docs URLs are derived by\n * stripping it.\n */\nconst DEFAULT_CONFIG: Partial<GatewayConfig> = {\n apiBaseUrl: 'https://astrasync.ai/api',\n defaultAccessLevel: 'guidance',\n // minTrustScore + minTrustScoreForFull deprecated in v2.3.0 — server decides.\n cacheTtl: 300, // 5 minutes\n debug: false,\n};\n\n/**\n * Init self-test state. Fires once per process on first verify() call to warn\n * if apiBaseUrl is pointing at the wrong host (e.g. a marketing site that\n * 200s with text/html instead of the API).\n */\nlet initCheckPerformed = false;\n\n/** One-shot guard for v2.3.0 deprecation warning. */\nlet deprecationWarningShown = false;\n\nasync function performInitCheck(apiBaseUrl: string, debug?: boolean): Promise<void> {\n initCheckPerformed = true;\n try {\n const probeUrl = `${apiBaseUrl}/agents/verify-access`;\n // HEAD mirrors GET semantics (running the full request pipeline without a\n // body) so the response carries the same content-type the marketing 404\n // would return. OPTIONS often gets short-circuited by CORS-preflight\n // handlers and returns no content-type, defeating the check.\n const response = await fetch(probeUrl, { method: 'HEAD' });\n const contentType = response.headers.get('content-type') ?? '';\n if (contentType.startsWith('text/html')) {\n console.warn(\n `[VerificationGateway] apiBaseUrl '${apiBaseUrl}' returned HTML (content-type: ${contentType}). ` +\n `This usually means apiBaseUrl is pointing at a marketing site instead of the API. ` +\n `Expected: 'https://astrasync.ai/api' (prod) or 'https://staging.astrasync.ai/api' (staging). ` +\n `Set disableInitChecks: true on GatewayConfig to silence this warning.`\n );\n } else if (debug) {\n console.log(\n `[VerificationGateway] init check passed for ${apiBaseUrl} (content-type: ${contentType})`\n );\n }\n } catch (err) {\n if (debug) {\n console.log(`[VerificationGateway] init check failed (non-blocking): ${String(err)}`);\n }\n }\n}\n\n/**\n * Simple in-memory cache for verification results\n */\nconst verificationCache = new Map<string, { result: VerificationResult; expiresAt: number }>();\n\n/**\n * Generate cache key from credentials\n */\nfunction getCacheKey(credentials: AgentCredentials): string {\n return `${credentials.astraId || ''}-${credentials.apiKey || ''}-${credentials.jwt || ''}`;\n}\n\n/**\n * Check if cached result is still valid\n */\nfunction getCachedResult(credentials: AgentCredentials): VerificationResult | null {\n const key = getCacheKey(credentials);\n const cached = verificationCache.get(key);\n\n if (cached && cached.expiresAt > Date.now()) {\n return cached.result;\n }\n\n if (cached) {\n verificationCache.delete(key);\n }\n\n return null;\n}\n\n/**\n * Cache a verification result\n */\nfunction cacheResult(\n credentials: AgentCredentials,\n result: VerificationResult,\n ttlSeconds: number\n): void {\n const key = getCacheKey(credentials);\n verificationCache.set(key, {\n result,\n expiresAt: Date.now() + ttlSeconds * 1000,\n });\n}\n\n/**\n * Clear the verification cache\n */\nexport function clearCache(): void {\n verificationCache.clear();\n}\n\n/**\n * Extract agent credentials from various sources\n */\nexport function extractCredentials(\n headers: Record<string, string | string[] | undefined>,\n query?: Record<string, string | undefined>\n): AgentCredentials {\n const credentials: AgentCredentials = {};\n\n // Check for ASTRA-ID in headers (case-insensitive). Accepts the historical\n // X-Astra-Id name plus the X-Astra-AgentId / x-astra-agent-id alias the\n // partner asked for in #9a — both surface the same field.\n const astraIdHeader =\n headers['x-astra-id'] ||\n headers['X-Astra-Id'] ||\n headers['X-ASTRA-ID'] ||\n headers['x-astra-agentid'] ||\n headers['X-Astra-AgentId'] ||\n headers['x-astra-agent-id'] ||\n headers['X-Astra-Agent-Id'] ||\n headers['X-ASTRA-AGENT-ID'];\n if (astraIdHeader) {\n credentials.astraId = Array.isArray(astraIdHeader) ? astraIdHeader[0] : astraIdHeader;\n }\n\n // Check for API key in headers\n const apiKeyHeader = headers['x-api-key'] || headers['X-Api-Key'] || headers['X-API-KEY'];\n if (apiKeyHeader) {\n credentials.apiKey = Array.isArray(apiKeyHeader) ? apiKeyHeader[0] : apiKeyHeader;\n }\n\n // Check Authorization header for Bearer token\n const authHeader = headers['authorization'] || headers['Authorization'];\n if (authHeader) {\n const authValue = Array.isArray(authHeader) ? authHeader[0] : authHeader;\n credentials.authorizationHeader = authValue;\n\n if (authValue.startsWith('Bearer ')) {\n credentials.jwt = authValue.slice(7);\n }\n }\n\n // Check query parameters as fallback\n if (query) {\n if (query.astraId && !credentials.astraId) {\n credentials.astraId = query.astraId;\n }\n if (query.apiKey && !credentials.apiKey) {\n credentials.apiKey = query.apiKey;\n }\n }\n\n return credentials;\n}\n\n/**\n * Check if credentials are present\n */\nexport function hasCredentials(credentials: AgentCredentials): boolean {\n return !!(credentials.astraId || credentials.apiKey || credentials.jwt);\n}\n\n/**\n * Create guidance response for unverified agents\n */\nfunction createGuidanceResponse(config: GatewayConfig, reason?: string): VerificationResult {\n const guidance: GuidanceInfo = {\n message:\n 'This service verifies AI agents before granting access. Please register your agent with AstraSync.',\n registrationUrl: `${config.apiBaseUrl.replace('/api', '')}/register`,\n documentationUrl: `${config.apiBaseUrl.replace('/api', '')}/docs/agent-access`,\n steps: [\n 'Register for an AstraSync account',\n 'Create and register your agent',\n 'Add your ASTRA-ID to request headers',\n 'Retry your request',\n ],\n };\n\n return {\n verified: false,\n accessLevel: 'guidance',\n guidance,\n denialReasons: reason ? [reason] : ['No valid agent credentials provided'],\n verifiedAt: new Date(),\n };\n}\n\n/**\n * Call the AstraSync verify-access API\n */\nasync function callVerifyAccessAPI(\n config: GatewayConfig,\n request: VerificationRequest\n): Promise<{\n success: boolean;\n access?: {\n allowed: boolean;\n /**\n * Server-decided access level. Read verbatim — do NOT remap client-side.\n * The server resolves this from endpoint policy + agent trust score using\n * the canonical thresholds (see backend `apps/backend/src/utils/access-levels.ts`).\n */\n accessLevel?: AccessLevel;\n reason?: string;\n /**\n * Aggregated denial failures (v2.9.8+). Empty / absent when allowed.\n * Each entry is `{ dimension, message, guidance? }` — see\n * `AccessFailure` for the contract.\n */\n failures?: Array<{ dimension: string; message: string; guidance?: string }>;\n requiresStepUp?: boolean;\n requiresApproval?: boolean;\n appliedPolicy?: {\n boundaryName: string;\n policyVersion: string;\n };\n counterparty?: {\n id: string;\n name: string;\n trustScoreRequirement: number;\n };\n };\n agent?: {\n kyaAgentId: string;\n astraId: string;\n name: string;\n trustScore: number;\n trustLevel: string;\n agentStatus: string;\n blockchainStatus: string;\n };\n developer?: {\n kyaOwnerId: string;\n fullName: string;\n email: string;\n identityVerified: boolean;\n trustScore: number;\n };\n organization?: {\n name: string;\n verified: boolean;\n trustScore: number;\n };\n /**\n * Structured explanation of the verification decision. Tells the merchant\n * WHY (id verified? challenge passed? request within PDLSS? trust score?)\n * without exposing thresholds, scope lists, or other-tenant counterparty\n * membership. Empty `attestations` unless the endpoint's access policy\n * declared `required_attestations`.\n */\n verificationContext?: {\n idVerified: boolean;\n runtimeChallenge: {\n status: 'passed' | 'skipped' | 'failed' | 'timeout' | 'not_supported';\n checkedAt: string | null;\n };\n pdlssCheck: {\n result: 'within' | 'exceeded' | 'denied' | 'not_evaluated';\n purpose: 'approved' | 'denied';\n scope: 'approved' | 'denied';\n };\n dynamicTrustScore: number;\n attestations: Array<{\n type: string;\n status: 'passed' | 'failed';\n validUntil?: string;\n proofType: 'reference' | 'zkp';\n proof: string;\n }>;\n };\n error?: string;\n}> {\n const { credentials, ...requestData } = request;\n\n // Build the request body. agentId is omitted when not provided so the\n // server treats it as an anonymous canonical-flow call (Branch A/B/C).\n const body: Record<string, unknown> = {\n ...(credentials.astraId && { agentId: credentials.astraId }),\n purpose: requestData.purpose || 'general',\n };\n\n // Add optional fields\n if (requestData.action) body.action = requestData.action;\n if (requestData.resourceType) body.resourceType = requestData.resourceType;\n if (requestData.resource) body.resource = requestData.resource;\n if (requestData.jurisdiction) body.jurisdiction = requestData.jurisdiction;\n if (requestData.transactionValue) body.transactionValue = requestData.transactionValue;\n if (requestData.currency) body.currency = requestData.currency;\n if (requestData.isSubAgentRequest) body.isSubAgentRequest = requestData.isSubAgentRequest;\n if (requestData.parentAgentId) body.parentAgentId = requestData.parentAgentId;\n if (requestData.subAgentDepth !== undefined) body.subAgentDepth = requestData.subAgentDepth;\n // Handshake Protocol v10 additions\n if (requestData.enableRuntimeChallenge)\n body.enableRuntimeChallenge = requestData.enableRuntimeChallenge;\n if (requestData.createSession) body.createSession = requestData.createSession;\n if (requestData.durationRequired) body.durationRequired = requestData.durationRequired;\n if (requestData.counterpartyType) body.counterpartyType = requestData.counterpartyType;\n if (requestData.counterpartyUrl) body.counterpartyUrl = requestData.counterpartyUrl;\n if (config.counterpartyId) body.counterpartyId = config.counterpartyId;\n if (requestData.runtimeChallengeOptions)\n body.runtimeChallengeOptions = requestData.runtimeChallengeOptions;\n\n // Forward caller metadata when present. Merges the legacy top-level\n // clientIp/userAgent into the nested block for backward compatibility.\n if (requestData.callerMetadata || requestData.clientIp || requestData.userAgent) {\n const meta = {\n ...(requestData.clientIp && { sourceIp: requestData.clientIp }),\n ...(requestData.userAgent && { userAgent: requestData.userAgent }),\n ...requestData.callerMetadata,\n };\n if (Object.keys(meta).length > 0) body.callerMetadata = meta;\n }\n\n // Build headers\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...config.customHeaders,\n };\n\n // verify-access requires authentication. The backend's authenticate middleware\n // accepts either a JWT or an API key (starts with kya_) via `Authorization: Bearer <token>`.\n // Credential-supplied auth header (e.g. the agent's own token) takes priority.\n if (credentials.authorizationHeader) {\n headers['Authorization'] = credentials.authorizationHeader;\n } else if (config.apiKey) {\n headers['Authorization'] = `Bearer ${config.apiKey}`;\n }\n // Legacy header kept for compatibility with any middleware that reads it directly.\n if (config.apiKey) {\n headers['X-API-Key'] = config.apiKey;\n }\n\n try {\n const response = await fetch(`${config.apiBaseUrl}/agents/verify-access`, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n });\n\n const data = await response.json();\n\n if (!response.ok) {\n return {\n success: false,\n error: data.message || data.error || `API returned ${response.status}`,\n };\n }\n\n return data;\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n return {\n success: false,\n error: `Failed to call verify-access API: ${message}`,\n };\n }\n}\n\n/**\n * Main verification function\n */\nexport async function verify(\n config: GatewayConfig,\n request: VerificationRequest\n): Promise<VerificationResult> {\n const mergedConfig = { ...DEFAULT_CONFIG, ...config };\n\n // One-time init self-test — fire-and-forget, never blocks verify().\n if (!initCheckPerformed && !mergedConfig.disableInitChecks && mergedConfig.apiBaseUrl) {\n void performInitCheck(mergedConfig.apiBaseUrl, mergedConfig.debug);\n }\n\n // Deprecation warning for v2.3.0 removed config fields. Fires once per process.\n if (\n !deprecationWarningShown &&\n (config.minTrustScore !== undefined || config.minTrustScoreForFull !== undefined)\n ) {\n deprecationWarningShown = true;\n console.warn(\n '[VerificationGateway] minTrustScore / minTrustScoreForFull are deprecated in v2.3.0 ' +\n 'and have no effect. Server is now the single source of truth for access-level decisions ' +\n '(the SDK reads access.accessLevel from the verify-access response). To gate access ' +\n \"to an endpoint, configure the endpoint's trust_score_requirement server-side.\"\n );\n }\n\n // v2.3.0: anonymous traffic no longer short-circuits here. We forward the\n // request to the server with no agentId; the server applies the endpoint's\n // unverifiedAgentPolicy and returns advisory. createGuidanceResponse remains\n // as the offline fallback if the API itself fails (handled below).\n\n // Check cache first\n if (mergedConfig.cacheTtl && mergedConfig.cacheTtl > 0) {\n const cached = getCachedResult(request.credentials);\n if (cached) {\n if (mergedConfig.debug) {\n console.log('[VerificationGateway] Returning cached result');\n }\n return cached;\n }\n }\n\n // Inject counterparty info from config if not already set in request\n const enrichedRequest = { ...request };\n if (!enrichedRequest.counterpartyUrl && mergedConfig.counterpartyUrl) {\n enrichedRequest.counterpartyUrl = mergedConfig.counterpartyUrl;\n }\n if (!enrichedRequest.counterpartyType && mergedConfig.counterpartyType) {\n enrichedRequest.counterpartyType = mergedConfig.counterpartyType;\n }\n\n // Call the API\n if (mergedConfig.debug) {\n console.log('[VerificationGateway] Calling verify-access API');\n }\n\n const apiResponse = await callVerifyAccessAPI(mergedConfig, enrichedRequest);\n\n // Handle API errors\n if (!apiResponse.success) {\n return createGuidanceResponse(mergedConfig, apiResponse.error);\n }\n\n // Check access result\n if (!apiResponse.access?.allowed) {\n // v2.9.8 (defect M1): aggregated failures across every gate that\n // denied. Surface them on the result so the integrator can see every\n // blocker in one go instead of the previous fail-fast cascade.\n const aggregatedFailures = (apiResponse.access as Record<string, unknown> | undefined)\n ?.failures as Array<{ dimension: string; message: string; guidance?: string }> | undefined;\n const result: EnhancedVerificationResult = {\n verified: false,\n accessLevel: 'guidance',\n denialReasons:\n aggregatedFailures && aggregatedFailures.length > 0\n ? aggregatedFailures.map((f) => f.message)\n : apiResponse.access?.reason\n ? [apiResponse.access.reason]\n : ['Access denied'],\n failures: aggregatedFailures,\n requiresStepUp: apiResponse.access?.requiresStepUp,\n requiresApproval: apiResponse.access?.requiresApproval,\n guidance: {\n message: apiResponse.access?.reason || 'Access denied by PDLSS policy',\n registrationUrl: `${mergedConfig.apiBaseUrl?.replace('/api', '')}/register`,\n documentationUrl: `${mergedConfig.apiBaseUrl?.replace('/api', '')}/docs/pdlss`,\n },\n verifiedAt: new Date(),\n // Extract sessionId so decisions can be recorded for denials too\n sessionId: (apiResponse as Record<string, unknown>).sessionId as string | undefined,\n recommendation: (apiResponse as Record<string, unknown>)\n .recommendation as EnhancedVerificationResult['recommendation'],\n recommendationReasons: (apiResponse as Record<string, unknown>).recommendationReasons as\n | string[]\n | undefined,\n };\n\n return result;\n }\n\n // Build successful result\n const agent: VerifiedAgent | undefined = apiResponse.agent\n ? {\n astraId: apiResponse.agent.astraId,\n name: apiResponse.agent.name,\n trustScore: apiResponse.agent.trustScore,\n trustLevel: getTrustLevel(apiResponse.agent.trustScore),\n blockchainVerified: apiResponse.agent.blockchainStatus === 'verified',\n status: apiResponse.agent.agentStatus as VerifiedAgent['status'],\n }\n : undefined;\n\n const developer: VerifiedDeveloper | undefined = apiResponse.developer\n ? {\n astradId: apiResponse.developer.kyaOwnerId,\n name: apiResponse.developer.fullName,\n trustScore: apiResponse.developer.trustScore || 0,\n verified: apiResponse.developer.identityVerified,\n }\n : undefined;\n\n const organization: VerifiedOrganization | undefined = apiResponse.organization\n ? {\n name: apiResponse.organization.name,\n verified: apiResponse.organization.verified,\n trustScore: apiResponse.organization.trustScore,\n }\n : undefined;\n\n // Verification context — structured \"why\" the merchant gets in v2.2.4+.\n // Carries appliedPolicy (no UUIDs), pdlssCheck summary, dynamic trust score,\n // and policy-driven attestations. Replaces the old over-sharing `pdlss` block.\n const verificationContext = apiResponse.verificationContext;\n\n // Server is the single source of truth for access level. SDK reads\n // apiResponse.access.accessLevel verbatim — no client-side trust-score remap.\n // Fallback to 'standard' if the server response is missing the field (older\n // backend without the v2.3.0 contract); it covers the verified-access case.\n const accessLevel: AccessLevel = apiResponse.access?.accessLevel ?? 'standard';\n\n const result: EnhancedVerificationResult = {\n verified: true,\n accessLevel,\n agent,\n developer,\n organization,\n appliedPolicy: apiResponse.access?.appliedPolicy,\n verificationContext,\n requiresStepUp: apiResponse.access?.requiresStepUp,\n requiresApproval: apiResponse.access?.requiresApproval,\n verifiedAt: new Date(),\n cacheTtl: mergedConfig.cacheTtl,\n // Handshake Protocol v10 enhanced fields (present when backend returns them)\n sessionId: (apiResponse as Record<string, unknown>).sessionId as string | undefined,\n runtimeChallenge: (apiResponse as Record<string, unknown>).runtimeChallenge as\n | RuntimeChallengeResult\n | undefined,\n tokenGuidance: (apiResponse as Record<string, unknown>).tokenGuidance as\n | TokenGuidance\n | undefined,\n recommendation: (apiResponse as Record<string, unknown>)\n .recommendation as EnhancedVerificationResult['recommendation'],\n recommendationReasons: (apiResponse as Record<string, unknown>).recommendationReasons as\n | string[]\n | undefined,\n };\n\n // Enforce AstraSync recommendation\n if (result.recommendation === 'deny') {\n result.verified = false;\n result.accessLevel = 'none';\n result.denialReasons = result.recommendationReasons || [\n 'Access denied by AstraSync recommendation',\n ];\n if (result.runtimeChallenge) {\n result.guidance = {\n message: `Verification failed: ${result.runtimeChallenge.reason || 'runtime challenge failed'}`,\n registrationUrl: `${mergedConfig.apiBaseUrl?.replace('/api', '')}/register`,\n documentationUrl: `${mergedConfig.apiBaseUrl?.replace('/api', '')}/docs/runtime-challenge`,\n };\n }\n } else if (result.recommendation === 'step_up_required') {\n result.requiresStepUp = true;\n if (ACCESS_LEVEL_HIERARCHY[result.accessLevel] > ACCESS_LEVEL_HIERARCHY['read-only']) {\n result.accessLevel = 'read-only';\n }\n result.denialReasons = result.recommendationReasons || ['Step-up verification required'];\n }\n\n // Cache the result (skip caching denials — agent may fix challenge endpoint and retry)\n if (mergedConfig.cacheTtl && mergedConfig.cacheTtl > 0 && result.recommendation !== 'deny') {\n cacheResult(request.credentials, result, mergedConfig.cacheTtl);\n }\n\n return result;\n}\n\n/**\n * Record a counterparty's grant/deny decision for a verification session.\n * Fire-and-forget — errors are silently swallowed.\n */\nexport async function recordDecision(\n config: GatewayConfig,\n sessionId: string,\n decision: 'granted' | 'denied',\n reason?: string\n): Promise<void> {\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n if (config.apiKey) {\n headers['Authorization'] = `Bearer ${config.apiKey}`;\n headers['X-API-Key'] = config.apiKey;\n }\n\n await fetch(`${config.apiBaseUrl}/agents/verify-access/${sessionId}/decision`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ decision, reason }),\n }).catch(() => {\n /* fire-and-forget */\n });\n}\n\n/**\n * Fetch the per-route policy for an endpoint from the AstraSync backend.\n * v2.9.7 moved policy authority into the dashboard — the SDK no longer\n * accepts `routes` from merchant-side source code, it fetches them from\n * here on init (and refreshes periodically).\n *\n * Returns `null` when the request fails for any reason — the caller decides\n * how to fall back (the middleware allows-all when no policy is loaded so\n * a misconfigured init doesn't take down the merchant's API).\n */\nexport async function fetchRoutes(\n config: GatewayConfig,\n counterpartyId: string\n): Promise<RouteAccessConfigShape[] | null> {\n if (!counterpartyId) return null;\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n if (config.apiKey) {\n headers['Authorization'] = `Bearer ${config.apiKey}`;\n headers['X-API-Key'] = config.apiKey;\n }\n try {\n const response = await fetch(\n `${config.apiBaseUrl}/endpoints/${encodeURIComponent(counterpartyId)}/routes`,\n { method: 'GET', headers }\n );\n if (!response.ok) return null;\n const body = (await response.json()) as { data?: { routes?: RouteAccessConfigShape[] } };\n return body.data?.routes ?? [];\n } catch {\n return null;\n }\n}\n\n/**\n * Minimal shape of an EndpointRoute as the SDK consumes it. Mirrors the\n * server's `EndpointRoute` type and the SDK's `RouteAccessConfig` — same\n * JSON moves between server and SDK unchanged.\n */\nexport interface RouteAccessConfigShape {\n pattern: string;\n method: string;\n minAccessLevel: 'none' | 'guidance' | 'read-only' | 'standard' | 'full' | 'internal';\n minTrustScore?: number;\n requiredPurposes?: string[];\n allowedPurposes?: string[];\n allowedJurisdictions?: string[];\n maxDuration?: number;\n maxTransactionValue?: number;\n}\n\n/**\n * Verify an agent AND automatically record the grant/deny decision.\n *\n * This is the recommended entry point for counterparties that call verify()\n * directly (e.g. MCP servers) rather than using createMiddleware().\n * It adds createSession: true, then fire-and-forgets the decision.\n */\nexport async function verifyAndRecord(\n config: GatewayConfig,\n request: VerificationRequest\n): Promise<VerificationResult> {\n const mergedConfig = { ...DEFAULT_CONFIG, ...config };\n const result = await verify(mergedConfig, { ...request, createSession: true });\n const sessionId = (result as EnhancedVerificationResult).sessionId;\n\n if (sessionId) {\n if (result.verified) {\n recordDecision(mergedConfig, sessionId, 'granted').catch(() => {});\n } else {\n recordDecision(mergedConfig, sessionId, 'denied', result.denialReasons?.[0]).catch(() => {});\n }\n }\n\n return result;\n}\n\n/**\n * Report an unregistered agent attempt (no AstraSync credentials).\n * Called by SDK adapters when an agent is redirected to /docs/agent-access.\n * Fire-and-forget — errors are silently swallowed.\n */\nexport async function reportUnregisteredAttempt(\n config: GatewayConfig,\n data: {\n counterpartyUrl: string;\n counterpartyType?: string;\n sourceIp?: string;\n userAgent?: string;\n requestPath?: string;\n requestMethod?: string;\n }\n): Promise<void> {\n const apiBaseUrl = config.apiBaseUrl || DEFAULT_CONFIG.apiBaseUrl!;\n\n await fetch(`${apiBaseUrl}/verification-activity/unregistered-attempt`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(data),\n }).catch(() => {\n /* fire-and-forget */\n });\n}\n\n/**\n * Report a counterparty-side PDLSS pre-check failure.\n * Called by SDK adapters when the agent's requested PDLSS exceeds\n * counterparty-defined maximums BEFORE calling verify-access.\n * Fire-and-forget — errors are silently swallowed.\n */\nexport async function reportCounterpartyPreCheckFailure(\n config: GatewayConfig,\n data: {\n agentId: string;\n counterpartyUrl: string;\n counterpartyType?: string;\n failures: Array<{\n field: string;\n requested: string | number;\n limit: string | number | string[];\n message: string;\n }>;\n requestPath?: string;\n requestMethod?: string;\n }\n): Promise<void> {\n const apiBaseUrl = config.apiBaseUrl || DEFAULT_CONFIG.apiBaseUrl!;\n\n await fetch(`${apiBaseUrl}/verification-activity/counterparty-pre-check-failure`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(data),\n }).catch(() => {\n /* fire-and-forget */\n });\n}\n\n/**\n * Quick verification - just check if credentials are valid\n */\nexport async function quickVerify(\n config: GatewayConfig,\n credentials: AgentCredentials\n): Promise<{ verified: boolean; accessLevel: AccessLevel; reason?: string }> {\n const result = await verify(config, {\n credentials,\n purpose: 'verification',\n });\n\n return {\n verified: result.verified,\n accessLevel: result.accessLevel,\n reason: result.denialReasons?.[0],\n };\n}\n","/**\n * HTTP Transport Adapter\n *\n * Maps AstraSync credentials to/from HTTP headers (X-Astra-* convention).\n */\n\nimport type { AstraSyncCredentials } from '../types';\n\nconst HEADER_PREFIX = 'X-Astra-';\n\n/**\n * Inject AstraSync credentials into HTTP headers.\n */\nexport function setHttpHeaders(\n headers: Record<string, string>,\n credentials: AstraSyncCredentials,\n): Record<string, string> {\n const result = { ...headers };\n\n result[`${HEADER_PREFIX}ID`] = credentials.agentId;\n\n if (credentials.verifyUrl) {\n result[`${HEADER_PREFIX}Verify`] = credentials.verifyUrl;\n }\n\n if (credentials.challengeUrl) {\n result[`${HEADER_PREFIX}Challenge`] = credentials.challengeUrl;\n }\n\n if (credentials.pdlss?.purpose) {\n const purposeValue = credentials.pdlss.purpose.action\n ? `${credentials.pdlss.purpose.category}:${credentials.pdlss.purpose.action}`\n : credentials.pdlss.purpose.category;\n result[`${HEADER_PREFIX}Purpose`] = purposeValue;\n }\n\n if (credentials.pdlss?.duration?.maxSessionDuration) {\n result[`${HEADER_PREFIX}Duration`] = String(credentials.pdlss.duration.maxSessionDuration);\n }\n\n if (credentials.pdlss?.scope?.jurisdiction) {\n result[`${HEADER_PREFIX}Scope`] = credentials.pdlss.scope.jurisdiction;\n }\n\n return result;\n}\n\n/**\n * Extract AstraSync credentials from HTTP headers.\n */\nexport function extractHttpCredentials(\n headers: Record<string, string | string[] | undefined>,\n): AstraSyncCredentials | null {\n const getValue = (key: string): string | undefined => {\n const v = headers[key] ?? headers[key.toLowerCase()];\n return Array.isArray(v) ? v[0] : v;\n };\n\n const agentId = getValue(`${HEADER_PREFIX}ID`) ?? getValue('x-astra-id');\n if (!agentId) return null;\n\n const credentials: AstraSyncCredentials = { agentId };\n\n const verifyUrl = getValue(`${HEADER_PREFIX}Verify`) ?? getValue('x-astra-verify');\n if (verifyUrl) credentials.verifyUrl = verifyUrl;\n\n const challengeUrl = getValue(`${HEADER_PREFIX}Challenge`) ?? getValue('x-astra-challenge');\n if (challengeUrl) credentials.challengeUrl = challengeUrl;\n\n const purpose = getValue(`${HEADER_PREFIX}Purpose`) ?? getValue('x-astra-purpose');\n if (purpose) {\n const [category, action] = purpose.split(':');\n credentials.pdlss = {\n ...credentials.pdlss,\n purpose: { category, action },\n };\n }\n\n const duration = getValue(`${HEADER_PREFIX}Duration`) ?? getValue('x-astra-duration');\n if (duration) {\n credentials.pdlss = {\n ...credentials.pdlss,\n duration: { maxSessionDuration: parseInt(duration, 10) },\n };\n }\n\n const scope = getValue(`${HEADER_PREFIX}Scope`) ?? getValue('x-astra-scope');\n if (scope) {\n credentials.pdlss = {\n ...credentials.pdlss,\n scope: { jurisdiction: scope },\n };\n }\n\n return credentials;\n}\n","/**\n * Counterparty-side PDLSS pre-check.\n *\n * Compares the agent's requested PDLSS dimensions (from X-Astra-* headers)\n * against the counterparty-defined maximums on the route config.\n * Returns an array of failures — empty means all checks passed.\n *\n * This runs BEFORE calling verify-access on AstraSync. If it fails,\n * the request is rejected immediately without calling the platform.\n */\n\nimport type { RouteAccessConfig, AstraSyncCredentials, CounterpartyPreCheckFailure } from './types';\n\nexport function performCounterpartyPreCheck(\n routeConfig: RouteAccessConfig,\n astraCreds: AstraSyncCredentials | null,\n purpose: string | undefined,\n): CounterpartyPreCheckFailure[] {\n const failures: CounterpartyPreCheckFailure[] = [];\n\n // Check purpose against allowedPurposes whitelist\n if (routeConfig.allowedPurposes && routeConfig.allowedPurposes.length > 0 && purpose) {\n if (!routeConfig.allowedPurposes.includes(purpose)) {\n failures.push({\n field: 'purpose',\n requested: purpose,\n limit: routeConfig.allowedPurposes,\n message: `Purpose \"${purpose}\" is not in the allowed list: [${routeConfig.allowedPurposes.join(', ')}]`,\n });\n }\n }\n\n // Check purpose against requiredPurposes (legacy field — agent must declare one of these)\n if (routeConfig.requiredPurposes && routeConfig.requiredPurposes.length > 0 && purpose) {\n if (!routeConfig.requiredPurposes.includes(purpose)) {\n failures.push({\n field: 'purpose',\n requested: purpose,\n limit: routeConfig.requiredPurposes,\n message: `Purpose \"${purpose}\" is not in the required list: [${routeConfig.requiredPurposes.join(', ')}]`,\n });\n }\n }\n\n // Check duration against maxDuration\n if (routeConfig.maxDuration && astraCreds?.pdlss?.duration?.maxSessionDuration) {\n const requested = astraCreds.pdlss.duration.maxSessionDuration;\n if (requested > routeConfig.maxDuration) {\n failures.push({\n field: 'duration',\n requested,\n limit: routeConfig.maxDuration,\n message: `Requested duration ${requested}s exceeds maximum ${routeConfig.maxDuration}s`,\n });\n }\n }\n\n // Check jurisdiction against allowedJurisdictions\n if (\n routeConfig.allowedJurisdictions &&\n routeConfig.allowedJurisdictions.length > 0 &&\n astraCreds?.pdlss?.scope?.jurisdiction\n ) {\n const requested = astraCreds.pdlss.scope.jurisdiction;\n if (!routeConfig.allowedJurisdictions.includes(requested)) {\n failures.push({\n field: 'jurisdiction',\n requested,\n limit: routeConfig.allowedJurisdictions,\n message: `Jurisdiction \"${requested}\" is not in the allowed list: [${routeConfig.allowedJurisdictions.join(', ')}]`,\n });\n }\n }\n\n return failures;\n}\n","/**\n * AstraSync Universal Verification Gateway - Express Middleware\n *\n * Express.js middleware for verifying AI agents on API endpoints.\n *\n * @example\n * ```typescript\n * import express from 'express';\n * import { createMiddleware } from '@astrasyncai/verification-gateway/express';\n *\n * const app = express();\n *\n * app.use(createMiddleware({\n * apiBaseUrl: 'https://astrasync.ai/api',\n * routes: [\n * { pattern: '/api/public/*', method: '*', minAccessLevel: 'none' },\n * { pattern: '/api/data/*', method: 'GET', minAccessLevel: 'read-only' },\n * { pattern: '/api/data/*', method: '*', minAccessLevel: 'standard' },\n * { pattern: '/api/admin/*', method: '*', minAccessLevel: 'internal' },\n * ],\n * }));\n * ```\n */\n\nimport type { Request, Response, NextFunction, RequestHandler } from 'express';\nimport type {\n ExpressMiddlewareOptions,\n AgentCredentials,\n VerificationResult,\n EnhancedVerificationResult,\n RouteAccessConfig,\n AstraSyncCredentials,\n} from '../types';\nimport {\n verify,\n extractCredentials,\n recordDecision,\n reportCounterpartyPreCheckFailure,\n fetchRoutes,\n} from '../verify';\nimport { hasMinimumAccess } from '../access-levels';\nimport { extractHttpCredentials } from '../transport/http';\nimport { performCounterpartyPreCheck } from '../pdlss-pre-check';\n\n/**\n * Extend Express Request with verification result\n */\ndeclare global {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n namespace Express {\n interface Request {\n agentVerification?: VerificationResult;\n }\n }\n}\n\n/**\n * Default credential extractor\n */\nfunction defaultExtractCredentials(req: Request): AgentCredentials {\n return extractCredentials(\n req.headers as Record<string, string | string[] | undefined>,\n req.query as Record<string, string | undefined>\n );\n}\n\n/**\n * Extract extended AstraSync credentials (X-Astra-* headers) from Express request.\n * Returns null if no AstraSync headers are present.\n */\nexport function extractAstraSyncCredentials(req: Request): AstraSyncCredentials | null {\n return extractHttpCredentials(req.headers as Record<string, string | string[] | undefined>);\n}\n\n/**\n * Default purpose extractor.\n *\n * Priority:\n * 1. Agent's declared PDLSS purpose from X-Astra-Purpose header (e.g. \"read_data:search\")\n * 2. Explicit x-purpose header\n * 3. Query parameter ?purpose=\n * 4. HTTP method → PDLSS category fallback\n */\nfunction defaultExtractPurpose(req: Request): string | undefined {\n // 1. Check agent's declared PDLSS purpose (X-Astra-Purpose header)\n const astraPurpose = req.headers['x-astra-purpose'];\n if (astraPurpose) {\n const value = Array.isArray(astraPurpose) ? astraPurpose[0] : astraPurpose;\n // Extract category from \"category:action\" format — the verify API expects the category\n const category = value.split(':')[0];\n return category;\n }\n\n // 2. Try explicit purpose header\n const purposeHeader = req.headers['x-purpose'] || req.headers['X-Purpose'];\n if (purposeHeader) {\n return Array.isArray(purposeHeader) ? purposeHeader[0] : purposeHeader;\n }\n\n // 3. Try query parameter\n if (req.query.purpose && typeof req.query.purpose === 'string') {\n return req.query.purpose;\n }\n\n // 4. Infer from HTTP method using PDLSS-compatible categories\n switch (req.method) {\n case 'GET':\n return 'read_data';\n case 'POST':\n return 'write_data';\n case 'PUT':\n case 'PATCH':\n return 'write_data';\n case 'DELETE':\n return 'delete_data';\n default:\n return 'general';\n }\n}\n\n/**\n * Match a route pattern against a path\n */\nfunction matchRoute(pattern: string, path: string): boolean {\n // Convert pattern to regex\n const regexPattern = pattern.replace(/\\*/g, '.*').replace(/\\//g, '\\\\/');\n\n const regex = new RegExp(`^${regexPattern}$`);\n return regex.test(path);\n}\n\n/**\n * Find the route configuration for a request\n */\nfunction findRouteConfig(\n routes: RouteAccessConfig[],\n path: string,\n method: string\n): RouteAccessConfig | undefined {\n return routes.find((route) => {\n const methodMatches =\n route.method === '*' || route.method.toUpperCase() === method.toUpperCase();\n const pathMatches = matchRoute(route.pattern, path);\n return methodMatches && pathMatches;\n });\n}\n\n/**\n * Default denied handler\n */\nfunction defaultOnDenied(result: VerificationResult, _req: Request, res: Response): void {\n const statusCode = result.verified ? 403 : 401;\n\n res.status(statusCode).json({\n success: false,\n error: {\n code: result.verified ? 'INSUFFICIENT_ACCESS' : 'UNAUTHORIZED',\n message: result.denialReasons?.[0] || 'Access denied',\n accessLevel: result.accessLevel,\n guidance: result.guidance,\n },\n });\n}\n\n/**\n * Refresh interval for the remote-fetched route policy. Default: every 5\n * minutes. Override via `routesRefreshMs` on the middleware options. Each\n * middleware instance keeps its own cache; multiple instances pointing at the\n * same endpoint will each fetch independently.\n */\nconst DEFAULT_ROUTES_REFRESH_MS = 5 * 60 * 1000;\n\n/**\n * Create Express middleware for agent verification.\n *\n * v2.9.7 moved per-route policy authority out of merchant-side source code\n * into the AstraSync dashboard. `createMiddleware` no longer accepts a\n * `routes` array — it fetches the endpoint's stored policy via\n * `GET /endpoints/:counterpartyId/routes` on init and refreshes\n * periodically. Policy edits in the dashboard take effect on the next\n * refresh (or sooner if the operator manually restarts the SDK).\n *\n * `counterpartyId` is required: the SDK can't know which endpoint's policy\n * to fetch without it. Local-development workflows that don't have an\n * AstraSync endpoint registered can omit it — the middleware logs a\n * one-time warning and falls through (allows all) until a policy is\n * fetchable.\n */\nexport function createMiddleware(options: ExpressMiddlewareOptions): RequestHandler {\n const {\n extractCredentials: customExtractCredentials,\n extractPurpose: customExtractPurpose,\n skipPaths = [],\n onDenied = defaultOnDenied,\n recordDecisions,\n enableRuntimeChallenge = true,\n routesRefreshMs = DEFAULT_ROUTES_REFRESH_MS,\n ...config\n } = options;\n\n // Per-middleware-instance route cache. Populated on first fetch; refreshed\n // every `routesRefreshMs`. Until first successful fetch we hold an empty\n // array which means \"no per-route gating active\" — the middleware falls\n // through. Pre-fix the array came from merchant source, which both broke\n // the auth-boundary story (defect 24) and silently disagreed with the\n // dashboard.\n let cachedRoutes: RouteAccessConfig[] = [];\n let lastFetchAt = 0;\n let refreshing: Promise<void> | null = null;\n let warnedNoCounterparty = false;\n\n async function refreshRoutes(): Promise<void> {\n if (!config.counterpartyId) {\n if (!warnedNoCounterparty) {\n // eslint-disable-next-line no-console\n console.warn(\n '[VerificationGateway] No counterpartyId configured — falling through (allow all). ' +\n 'Per-route policy lives in the AstraSync dashboard now; register the endpoint and ' +\n 'set counterpartyId in your middleware config to enforce policy.'\n );\n warnedNoCounterparty = true;\n }\n return;\n }\n const fetched = await fetchRoutes(config, config.counterpartyId);\n if (fetched) {\n cachedRoutes = fetched;\n lastFetchAt = Date.now();\n }\n }\n\n // Eager first fetch so the first request after init has policy loaded.\n // Errors are swallowed (handled by fetchRoutes returning null).\n refreshing = refreshRoutes().finally(() => {\n refreshing = null;\n });\n\n return async (req: Request, res: Response, next: NextFunction): Promise<void> => {\n try {\n // Check if path should be skipped\n const shouldSkip = skipPaths.some((pattern) => matchRoute(pattern, req.path));\n if (shouldSkip) {\n return next();\n }\n\n // Wait for in-flight init fetch (only on the very first request) so we\n // don't admit traffic with no policy loaded.\n if (refreshing) {\n await refreshing.catch(() => {});\n }\n // Time-based refresh: kick off a background refresh if the cache is\n // stale. Doesn't block the current request — readers see whatever is\n // cached at this moment; the next request will see the refreshed copy.\n if (config.counterpartyId && Date.now() - lastFetchAt > routesRefreshMs) {\n refreshing = refreshRoutes().finally(() => {\n refreshing = null;\n });\n }\n\n // Find route configuration\n const routeConfig = findRouteConfig(cachedRoutes, req.path, req.method);\n\n // If no route config, skip verification (allow through)\n if (!routeConfig) {\n return next();\n }\n\n // If route requires 'none' access, skip verification\n if (routeConfig.minAccessLevel === 'none') {\n return next();\n }\n\n // Extract credentials\n const credentials = customExtractCredentials\n ? customExtractCredentials(req)\n : defaultExtractCredentials(req);\n\n // v2.3.0: anonymous traffic no longer short-circuits client-side.\n // The server applies the endpoint's `unverifiedAgentPolicy` (deny /\n // allow_partial / allow_full) and emits the verification event +\n // blockchain record per the canonical flow. SDK forwards verbatim.\n\n // Extract purpose\n const purpose = customExtractPurpose ? customExtractPurpose(req) : defaultExtractPurpose(req);\n\n // Extract full AstraSync credentials (includes PDLSS from X-Astra-* headers)\n const astraCreds = extractAstraSyncCredentials(req);\n\n // Auto-detect counterparty URL from the request if not explicitly configured.\n // Since the SDK is installed at this endpoint, we always know the origin.\n const counterpartyUrl = config.counterpartyUrl || `${req.protocol}://${req.get('host')}`;\n\n // Step 2: Counterparty-side PDLSS pre-check — compare agent's requested PDLSS\n // against counterparty-defined maximums on the route config.\n // Rejects immediately if outside limits, BEFORE calling verify-access.\n const preCheckFailures = performCounterpartyPreCheck(routeConfig, astraCreds, purpose);\n if (preCheckFailures.length > 0) {\n const result: VerificationResult = {\n verified: false,\n accessLevel: 'none',\n denialReasons: preCheckFailures.map((f) => f.message),\n guidance: {\n message: 'Request exceeds counterparty-defined PDLSS limits.',\n registrationUrl: `${config.apiBaseUrl?.replace('/api', '')}/register`,\n documentationUrl: `${config.apiBaseUrl?.replace('/api', '')}/docs/pdlss`,\n },\n verifiedAt: new Date(),\n };\n\n req.agentVerification = result;\n\n // Fire-and-forget: notify AstraSync of the pre-check failure\n reportCounterpartyPreCheckFailure(config, {\n agentId: astraCreds?.agentId || credentials.astraId || 'unknown',\n counterpartyUrl,\n counterpartyType: config.counterpartyType || 'api',\n failures: preCheckFailures,\n requestPath: req.path,\n requestMethod: req.method,\n }).catch(() => {});\n\n onDenied(result, req, res);\n return;\n }\n\n // Step 3: Call AstraSync verify-access with runtime challenge enabled\n const shouldRecordDecisions = recordDecisions !== false;\n const forwardedFor = req.headers['x-forwarded-for'];\n const forwardedForStr = Array.isArray(forwardedFor) ? forwardedFor.join(', ') : forwardedFor;\n // X-Forwarded-For's first entry is the original client. Fall back to req.ip\n // (which Express already resolves via trust proxy settings when configured).\n const originalClientIp = forwardedForStr ? forwardedForStr.split(',')[0].trim() : req.ip;\n const agentCardUrl =\n typeof req.headers['x-astrasync-agent-card'] === 'string'\n ? (req.headers['x-astrasync-agent-card'] as string)\n : undefined;\n\n const result = await verify(config, {\n credentials,\n purpose,\n action: req.method.toLowerCase(),\n resource: req.path,\n createSession: shouldRecordDecisions,\n counterpartyUrl,\n counterpartyType: config.counterpartyType || 'api',\n enableRuntimeChallenge,\n durationRequired: astraCreds?.pdlss?.duration?.maxSessionDuration,\n callerMetadata: {\n sourceIp: originalClientIp,\n userAgent: req.headers['user-agent'] as string | undefined,\n referer: req.headers.referer as string | undefined,\n host: req.headers.host as string | undefined,\n forwardedFor: forwardedForStr,\n agentCardUrl,\n },\n });\n\n // Attach result to request\n req.agentVerification = result;\n const sessionId = (result as EnhancedVerificationResult).sessionId;\n\n // Check if access level is sufficient\n if (!hasMinimumAccess(result.accessLevel, routeConfig.minAccessLevel)) {\n if (shouldRecordDecisions && sessionId) {\n recordDecision(config, sessionId, 'denied', result.denialReasons?.[0]).catch(() => {});\n }\n onDenied(result, req, res);\n return;\n }\n\n // Check trust score requirement if specified\n if (routeConfig.minTrustScore && result.agent) {\n if (result.agent.trustScore < routeConfig.minTrustScore) {\n result.denialReasons = [\n `Trust score ${result.agent.trustScore} is below required ${routeConfig.minTrustScore}`,\n ];\n if (shouldRecordDecisions && sessionId) {\n recordDecision(config, sessionId, 'denied', result.denialReasons[0]).catch(() => {});\n }\n onDenied(result, req, res);\n return;\n }\n }\n\n // All checks passed — record grant decision\n if (shouldRecordDecisions && sessionId) {\n recordDecision(config, sessionId, 'granted').catch(() => {});\n }\n next();\n } catch (error) {\n // Log error and continue (fail open by default)\n console.error('[VerificationGateway] Middleware error:', error);\n next();\n }\n };\n}\n\n// `requireAccess` and `verifyOnly` were removed in v2.9.7. Both injected a\n// local `routes` array into the merchant's source code, which is the exact\n// dual-config attack vector defect 24 closed. Per-route policy now lives in\n// the AstraSync dashboard (gated by team.role admin auth + audit + alerts).\n// To enforce a minimum tier, set the route's `minAccessLevel` in the\n// dashboard. For \"verify but don't block,\" set every route to `none` there.\n"],"mappings":"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","names":["result","result"]}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { Request, Response, RequestHandler } from 'express';
|
|
2
|
+
import { a as AccessLevel, G as GatewayConfig, V as VerificationResult } from '../types-Bxqj1sKY.mjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* MCP server-side helpers — companion to `transport/mcp.ts` (which handles the
|
|
6
|
+
* agent-side `_meta.astrasync` block).
|
|
7
|
+
*
|
|
8
|
+
* Surfaces a body-aware policy hook the existing `createMiddleware` couldn't
|
|
9
|
+
* provide — MCP traffic is JSON-RPC over a single endpoint (`/mcp`), so the
|
|
10
|
+
* default route-pattern gating is too coarse: every request looks the same
|
|
11
|
+
* URL-wise, but `initialize` is low-risk handshake while `tools/call` of a
|
|
12
|
+
* payment tool is high-risk. Cohort-3 beta merchants flagged this 🟡 in the
|
|
13
|
+
* v2.9.5 round.
|
|
14
|
+
*
|
|
15
|
+
* What lives here:
|
|
16
|
+
* - `parseMcpJsonRpc(body)` — peels JSON-RPC method + tool name + agent
|
|
17
|
+
* id without committing to a particular MCP
|
|
18
|
+
* server framework.
|
|
19
|
+
* - `mcpToPdlss(parsed)` — canonical mapping JSON-RPC method → PDLSS
|
|
20
|
+
* purpose / action / resource. Doc-stable
|
|
21
|
+
* so audits can correlate.
|
|
22
|
+
* - `mcpRiskTier(parsed)` — recommended `minAccessLevel` per method
|
|
23
|
+
* so a single MCP middleware can split
|
|
24
|
+
* `initialize` / `tools/list` (low gate)
|
|
25
|
+
* from `tools/call` (high gate).
|
|
26
|
+
* - `MCP_VERIFIED_HOP_HEADER` — header convention for the dedupe pattern
|
|
27
|
+
* when an MCP tool calls an inner REST hop.
|
|
28
|
+
* - `serialize/parseVerifiedHop` helpers.
|
|
29
|
+
*
|
|
30
|
+
* The Express MCP adapter is in `adapters/mcp.ts` and consumes these.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Header carrying upstream verify-access proof so an inner-hop REST endpoint
|
|
35
|
+
* can dedupe (skip verify-access when the same ASTRA-id was already verified
|
|
36
|
+
* a few ms earlier). Value format:
|
|
37
|
+
*
|
|
38
|
+
* {astraId};{sessionId};{checkedAt-ms}
|
|
39
|
+
*
|
|
40
|
+
* Receivers MUST validate that `checkedAt` is recent (≤ 60s window
|
|
41
|
+
* recommended) and that `astraId` matches the agent identity claimed on the
|
|
42
|
+
* inner hop. The header alone is NOT proof-of-identity — it's a dedupe
|
|
43
|
+
* advisory. Pair with the existing X-Astra-Id auth.
|
|
44
|
+
*/
|
|
45
|
+
declare const MCP_VERIFIED_HOP_HEADER = "X-Astra-Verified-Hop";
|
|
46
|
+
interface VerifiedHopMarker {
|
|
47
|
+
astraId: string;
|
|
48
|
+
sessionId?: string;
|
|
49
|
+
checkedAt: number;
|
|
50
|
+
}
|
|
51
|
+
declare function serializeVerifiedHop(marker: VerifiedHopMarker): string;
|
|
52
|
+
declare function parseVerifiedHop(value: string | undefined | null): VerifiedHopMarker | null;
|
|
53
|
+
/**
|
|
54
|
+
* Returns true when `marker.astraId` matches the inner-hop's claimed
|
|
55
|
+
* ASTRA-id AND the marker is recent enough. Inner-hop middleware uses this
|
|
56
|
+
* to skip a duplicate verify-access call.
|
|
57
|
+
*/
|
|
58
|
+
declare function isVerifiedHopValidFor(marker: VerifiedHopMarker | null, expectedAstraId: string, opts?: {
|
|
59
|
+
maxAgeMs?: number;
|
|
60
|
+
now?: number;
|
|
61
|
+
}): boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Output of `parseMcpJsonRpc`. Self-describing so the middleware doesn't have
|
|
64
|
+
* to re-introspect the body to figure out gating.
|
|
65
|
+
*/
|
|
66
|
+
interface ParsedMcpRequest {
|
|
67
|
+
/** JSON-RPC method (e.g. `tools/call`, `initialize`, `tools/list`). */
|
|
68
|
+
method: string;
|
|
69
|
+
/** Set when method === 'tools/call'; the tool name from `params.name`. */
|
|
70
|
+
toolName?: string;
|
|
71
|
+
/** Initialize-specific protocolVersion handshake info, when present. */
|
|
72
|
+
protocolVersion?: string;
|
|
73
|
+
/**
|
|
74
|
+
* Agent id read from the body, in priority order:
|
|
75
|
+
* 1. `params._meta.astrasync.agentId` (the canonical SDK location, see
|
|
76
|
+
* `transport/mcp.ts → setMcpMeta`)
|
|
77
|
+
* 2. `params.arguments.agent_id` (legacy / hand-written tool callers)
|
|
78
|
+
* Header-supplied id (X-Astra-Id) is read separately by the adapter and
|
|
79
|
+
* compared to this for the mismatch check.
|
|
80
|
+
*/
|
|
81
|
+
agentIdFromBody?: string;
|
|
82
|
+
/** True for handshake methods that must succeed before any tool call. */
|
|
83
|
+
isInitialize: boolean;
|
|
84
|
+
/** True for `tools/call`. */
|
|
85
|
+
isToolCall: boolean;
|
|
86
|
+
/** True for low-risk introspection (`tools/list`, `prompts/list`, etc.). */
|
|
87
|
+
isIntrospection: boolean;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Peel the JSON-RPC envelope. Returns `null` if the body isn't a JSON-RPC
|
|
91
|
+
* request (callers can short-circuit with a 400 or treat as untyped traffic).
|
|
92
|
+
*
|
|
93
|
+
* Accepts both single-request and notification shapes. Batch requests are
|
|
94
|
+
* NOT supported here — the verify-access contract is single-agent-per-call;
|
|
95
|
+
* a batch body should be split before policy gating.
|
|
96
|
+
*/
|
|
97
|
+
declare function parseMcpJsonRpc(body: unknown): ParsedMcpRequest | null;
|
|
98
|
+
/**
|
|
99
|
+
* PDLSS mapping for an MCP request. The platform's PDLSS taxonomy is
|
|
100
|
+
* `purpose / action / resource`; for MCP traffic the audit-useful dimensions
|
|
101
|
+
* are the JSON-RPC method and (for `tools/call`) the tool name. Doc-stable so
|
|
102
|
+
* dashboards and audits can correlate consistently across cohort-3 partners.
|
|
103
|
+
*
|
|
104
|
+
* Rules:
|
|
105
|
+
* - `purpose` is always `mcp_invoke` for MCP traffic — sets the high-level
|
|
106
|
+
* PDLSS bucket.
|
|
107
|
+
* - `action` is the JSON-RPC method, optionally `:tool_name` suffixed for
|
|
108
|
+
* `tools/call`. Lets PDLSS allowlist specific tools.
|
|
109
|
+
* - `resource` is `mcp:tool/<name>` for `tools/call`, `mcp:method/<method>`
|
|
110
|
+
* otherwise. Lets PDLSS scope on tool identity.
|
|
111
|
+
*/
|
|
112
|
+
interface McpPdlssMapping {
|
|
113
|
+
purpose: string;
|
|
114
|
+
action: string;
|
|
115
|
+
resource: string;
|
|
116
|
+
}
|
|
117
|
+
declare function mcpToPdlss(parsed: ParsedMcpRequest): McpPdlssMapping;
|
|
118
|
+
/**
|
|
119
|
+
* Recommended minimum access level per method type. The MCP middleware uses
|
|
120
|
+
* this to split low-risk handshake / introspection traffic from high-risk
|
|
121
|
+
* tool execution — defect (a) from the cohort-3 review.
|
|
122
|
+
*
|
|
123
|
+
* - `initialize` / `notifications/initialized` → `none` (handshake must work for unregistered probes)
|
|
124
|
+
* - `tools/list` / `prompts/list` / `resources/list` → `none` (introspection is public-surface)
|
|
125
|
+
* - `ping` → `none`
|
|
126
|
+
* - `resources/read` → `read-only`
|
|
127
|
+
* - `tools/call` → `standard` (default — overridable per-tool)
|
|
128
|
+
* - everything else → `standard` (least-privilege fallback)
|
|
129
|
+
*/
|
|
130
|
+
declare function mcpRiskTier(parsed: ParsedMcpRequest): AccessLevel;
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* AstraSync Universal Verification Gateway — MCP middleware
|
|
134
|
+
*
|
|
135
|
+
* Express-shaped middleware tailored to the JSON-RPC body of an MCP
|
|
136
|
+
* (Model Context Protocol) endpoint. Closes the cohort-3 gaps the default
|
|
137
|
+
* `createMiddleware` couldn't:
|
|
138
|
+
*
|
|
139
|
+
* (a) **Body-aware gating**. All MCP traffic targets the same `/mcp` URL.
|
|
140
|
+
* The default route-pattern matcher can't tell `initialize` (low risk)
|
|
141
|
+
* from `tools/call start_checkout` (high risk). This middleware peels
|
|
142
|
+
* the JSON-RPC body and applies a per-method risk tier.
|
|
143
|
+
*
|
|
144
|
+
* (b) **PDLSS mapping**. Forwards `purpose=mcp_invoke`,
|
|
145
|
+
* `action=method[:tool]`, `resource=mcp:tool/<name>` so audit traces
|
|
146
|
+
* are stable across cohort-3 partners. See `transport/mcp-server.ts`
|
|
147
|
+
* for the exact mapping.
|
|
148
|
+
*
|
|
149
|
+
* (c) **Inner-hop dedupe**. Outbound responses set
|
|
150
|
+
* `X-Astra-Verified-Hop` so a downstream REST endpoint that the tool
|
|
151
|
+
* calls can skip a duplicate verify-access. The receiving REST
|
|
152
|
+
* middleware checks `parseVerifiedHop` and skips when valid.
|
|
153
|
+
*
|
|
154
|
+
* (d) **Header-vs-body identity precedence**. Reads ASTRA-id from
|
|
155
|
+
* `X-Astra-Id` first, body second. If both are present and disagree,
|
|
156
|
+
* returns a structured 400 by default (configurable). Pre-fix,
|
|
157
|
+
* integrators had to re-discover this on their own.
|
|
158
|
+
*
|
|
159
|
+
* Usage:
|
|
160
|
+
*
|
|
161
|
+
* ```typescript
|
|
162
|
+
* import express from 'express';
|
|
163
|
+
* import { createMcpMiddleware } from '@astrasyncai/verification-gateway/mcp';
|
|
164
|
+
*
|
|
165
|
+
* const app = express();
|
|
166
|
+
* app.use(express.json());
|
|
167
|
+
*
|
|
168
|
+
* app.post(
|
|
169
|
+
* '/mcp',
|
|
170
|
+
* createMcpMiddleware({
|
|
171
|
+
* apiBaseUrl: 'https://astrasync.ai/api',
|
|
172
|
+
* apiKey: process.env.ASTRASYNC_API_KEY,
|
|
173
|
+
* // Optional per-tool overrides — tools not listed get the default tier
|
|
174
|
+
* // from `mcpRiskTier` (`tools/call` → 'standard').
|
|
175
|
+
* toolGates: {
|
|
176
|
+
* start_checkout: 'standard',
|
|
177
|
+
* confirm_purchase: 'full',
|
|
178
|
+
* browse_catalog: 'read-only',
|
|
179
|
+
* },
|
|
180
|
+
* }),
|
|
181
|
+
* yourMcpServerHandler,
|
|
182
|
+
* );
|
|
183
|
+
* ```
|
|
184
|
+
*/
|
|
185
|
+
|
|
186
|
+
declare global {
|
|
187
|
+
namespace Express {
|
|
188
|
+
interface Request {
|
|
189
|
+
mcpRequest?: ParsedMcpRequest;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
interface McpMiddlewareOptions extends GatewayConfig {
|
|
194
|
+
/**
|
|
195
|
+
* Per-tool override for the minimum access level. Tools not listed inherit
|
|
196
|
+
* the default tier from `mcpRiskTier` (`tools/call` → `'standard'`). Use
|
|
197
|
+
* for high-risk tools that demand `'full'` or low-risk read-only tools.
|
|
198
|
+
*/
|
|
199
|
+
toolGates?: Record<string, AccessLevel>;
|
|
200
|
+
/**
|
|
201
|
+
* Per-method override (e.g. tighten `tools/list` to `'read-only'` if you
|
|
202
|
+
* don't want unregistered probes seeing your tool catalogue). Matches by
|
|
203
|
+
* exact JSON-RPC method string.
|
|
204
|
+
*/
|
|
205
|
+
methodGates?: Record<string, AccessLevel>;
|
|
206
|
+
/**
|
|
207
|
+
* What to do when the agent id supplied in the X-Astra-Id header
|
|
208
|
+
* disagrees with the agent id in the JSON-RPC body
|
|
209
|
+
* (`params._meta.astrasync.agentId` or `params.arguments.agent_id`).
|
|
210
|
+
*
|
|
211
|
+
* - `'reject'` (default) — return 400 `AGENT_ID_MISMATCH`. Safest.
|
|
212
|
+
* - `'prefer-header'` — log + verify against the header value. Keeps
|
|
213
|
+
* bodies that were authored before X-Astra-Id was the canonical
|
|
214
|
+
* identity slot working.
|
|
215
|
+
* - `'prefer-body'` — log + verify against the body value. Useful in
|
|
216
|
+
* reverse-proxy setups that strip auth headers.
|
|
217
|
+
*/
|
|
218
|
+
onAgentIdMismatch?: 'reject' | 'prefer-header' | 'prefer-body';
|
|
219
|
+
/** Skip verification + dedupe entirely. For testing. */
|
|
220
|
+
skip?: boolean;
|
|
221
|
+
/** Custom denied handler. Defaults to a structured JSON-RPC error response. */
|
|
222
|
+
onDenied?: (result: VerificationResult, req: Request, res: Response) => void;
|
|
223
|
+
/**
|
|
224
|
+
* If `false`, don't trust an inbound `X-Astra-Verified-Hop` header (always
|
|
225
|
+
* call verify-access). Default `true` — recommended for inner-hop endpoints
|
|
226
|
+
* called by your own MCP tools.
|
|
227
|
+
*/
|
|
228
|
+
trustVerifiedHop?: boolean;
|
|
229
|
+
/** Window for accepting an upstream verified-hop marker. Default 60_000ms. */
|
|
230
|
+
verifiedHopMaxAgeMs?: number;
|
|
231
|
+
/**
|
|
232
|
+
* Automatically record grant/deny decisions for every MCP call. Default
|
|
233
|
+
* `true` — matches the express adapter.
|
|
234
|
+
*/
|
|
235
|
+
recordDecisions?: boolean;
|
|
236
|
+
/** Forward runtime challenge (default `true`). */
|
|
237
|
+
enableRuntimeChallenge?: boolean;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Create the MCP middleware. Attach AFTER `express.json()` — the body must
|
|
241
|
+
* already be a parsed object.
|
|
242
|
+
*/
|
|
243
|
+
declare function createMcpMiddleware(options: McpMiddlewareOptions): RequestHandler;
|
|
244
|
+
|
|
245
|
+
export { MCP_VERIFIED_HOP_HEADER, type McpMiddlewareOptions, type ParsedMcpRequest, createMcpMiddleware, isVerifiedHopValidFor, mcpRiskTier, mcpToPdlss, parseMcpJsonRpc, parseVerifiedHop, serializeVerifiedHop };
|