@aamp/protocol 1.1.5 → 1.1.6

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.
Binary file
package/dist/agent.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Layer 2: Agent SDK
3
3
  */
4
- import { AccessPurpose, SignedAccessRequest, FeedbackSignal, QualityFlag, AgentIdentityManifest } from './types';
4
+ import { AccessPurpose, SignedAccessRequest, FeedbackSignal, QualityFlag, AgentIdentityManifest } from './types.js';
5
5
  export interface AccessOptions {
6
6
  adsDisplayed?: boolean;
7
7
  }
package/dist/agent.js CHANGED
@@ -1,9 +1,6 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.AAMPAgent = void 0;
4
- const crypto_1 = require("./crypto");
5
- const constants_1 = require("./constants");
6
- class AAMPAgent {
1
+ import { generateKeyPair, signData, exportPublicKey } from './crypto.js';
2
+ import { AAMP_VERSION } from './constants.js';
3
+ export class AAMPAgent {
7
4
  constructor() {
8
5
  this.keyPair = null;
9
6
  this.agentId = "pending";
@@ -13,7 +10,7 @@ class AAMPAgent {
13
10
  * @param customAgentId For PRODUCTION, this should be your domain (e.g., "bot.openai.com")
14
11
  */
15
12
  async initialize(customAgentId) {
16
- this.keyPair = await (0, crypto_1.generateKeyPair)();
13
+ this.keyPair = await generateKeyPair();
17
14
  // Use the provided ID (authentic) or generate a session ID (ephemeral)
18
15
  this.agentId = customAgentId || "agent_" + Math.random().toString(36).substring(7);
19
16
  }
@@ -21,7 +18,7 @@ class AAMPAgent {
21
18
  if (!this.keyPair)
22
19
  throw new Error("Agent not initialized. Call initialize() first.");
23
20
  const header = {
24
- v: constants_1.AAMP_VERSION,
21
+ v: AAMP_VERSION,
25
22
  ts: new Date().toISOString(),
26
23
  agent_id: this.agentId,
27
24
  resource,
@@ -30,8 +27,8 @@ class AAMPAgent {
30
27
  ads_displayed: options.adsDisplayed || false
31
28
  }
32
29
  };
33
- const signature = await (0, crypto_1.signData)(this.keyPair.privateKey, JSON.stringify(header));
34
- const publicKeyExport = await (0, crypto_1.exportPublicKey)(this.keyPair.publicKey);
30
+ const signature = await signData(this.keyPair.privateKey, JSON.stringify(header));
31
+ const publicKeyExport = await exportPublicKey(this.keyPair.publicKey);
35
32
  return { header, signature, publicKey: publicKeyExport };
36
33
  }
37
34
  /**
@@ -41,7 +38,7 @@ class AAMPAgent {
41
38
  async getIdentityManifest(contactEmail) {
42
39
  if (!this.keyPair)
43
40
  throw new Error("Agent not initialized.");
44
- const publicKey = await (0, crypto_1.exportPublicKey)(this.keyPair.publicKey);
41
+ const publicKey = await exportPublicKey(this.keyPair.publicKey);
45
42
  return {
46
43
  agent_id: this.agentId,
47
44
  public_key: publicKey,
@@ -62,8 +59,7 @@ class AAMPAgent {
62
59
  flags,
63
60
  timestamp: new Date().toISOString()
64
61
  };
65
- const signature = await (0, crypto_1.signData)(this.keyPair.privateKey, JSON.stringify(signal));
62
+ const signature = await signData(this.keyPair.privateKey, JSON.stringify(signal));
66
63
  return { signal, signature };
67
64
  }
68
65
  }
69
- exports.AAMPAgent = AAMPAgent;
@@ -13,6 +13,9 @@ export declare const HEADERS: {
13
13
  readonly ALGORITHM: "x-aamp-alg";
14
14
  readonly CONTENT_ORIGIN: "x-aamp-content-origin";
15
15
  readonly PROVENANCE_SIG: "x-aamp-provenance-sig";
16
+ readonly PROOF_TOKEN: "x-aamp-proof";
17
+ readonly PAYMENT_CREDENTIAL: "x-aamp-credential";
18
+ readonly FEEDBACK: "x-aamp-feedback";
16
19
  };
17
20
  export declare const CRYPTO_CONFIG: {
18
21
  readonly ALGORITHM_NAME: "ECDSA";
package/dist/constants.js CHANGED
@@ -1,16 +1,13 @@
1
- "use strict";
2
1
  /**
3
2
  * Layer 1: Protocol Constants
4
3
  * These values are immutable and defined by the AAMP Specification.
5
4
  */
6
- Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.MAX_CLOCK_SKEW_MS = exports.CRYPTO_CONFIG = exports.HEADERS = exports.WELL_KNOWN_AGENT_PATH = exports.AAMP_VERSION = void 0;
8
- exports.AAMP_VERSION = '1.1';
5
+ export const AAMP_VERSION = '1.1';
9
6
  // The path where Agents MUST host their public key to prove identity.
10
7
  // Example: https://bot.openai.com/.well-known/aamp-agent.json
11
- exports.WELL_KNOWN_AGENT_PATH = '/.well-known/aamp-agent.json';
8
+ export const WELL_KNOWN_AGENT_PATH = '/.well-known/aamp-agent.json';
12
9
  // HTTP Headers used for the handshake
13
- exports.HEADERS = {
10
+ export const HEADERS = {
14
11
  // Transport: The signed payload (Base64 encoded JSON of ProtocolHeader)
15
12
  PAYLOAD: 'x-aamp-payload',
16
13
  // Transport: The cryptographic signature (Hex)
@@ -23,13 +20,19 @@ exports.HEADERS = {
23
20
  ALGORITHM: 'x-aamp-alg',
24
21
  // v1.1 Addition: Provenance (Server to Agent)
25
22
  CONTENT_ORIGIN: 'x-aamp-content-origin',
26
- PROVENANCE_SIG: 'x-aamp-provenance-sig'
23
+ PROVENANCE_SIG: 'x-aamp-provenance-sig',
24
+ // v1.2 Proof of Value
25
+ PROOF_TOKEN: 'x-aamp-proof',
26
+ // v1.2 Payment Credential (The "Digital Receipt")
27
+ PAYMENT_CREDENTIAL: 'x-aamp-credential',
28
+ // v1.2 Quality Feedback (The "Dispute Token")
29
+ FEEDBACK: 'x-aamp-feedback'
27
30
  };
28
31
  // Cryptographic Settings
29
- exports.CRYPTO_CONFIG = {
32
+ export const CRYPTO_CONFIG = {
30
33
  ALGORITHM_NAME: 'ECDSA',
31
34
  CURVE: 'P-256',
32
35
  HASH: 'SHA-256',
33
36
  };
34
37
  // Tolerance
35
- exports.MAX_CLOCK_SKEW_MS = 5 * 60 * 1000; // 5 minutes
38
+ export const MAX_CLOCK_SKEW_MS = 5 * 60 * 1000; // 5 minutes
package/dist/crypto.js CHANGED
@@ -1,25 +1,18 @@
1
- "use strict";
2
1
  /**
3
2
  * Layer 1: Cryptographic Primitives
4
3
  * Implementation of ECDSA P-256 signing/verification.
5
4
  */
6
- Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.generateKeyPair = generateKeyPair;
8
- exports.signData = signData;
9
- exports.verifySignature = verifySignature;
10
- exports.exportPublicKey = exportPublicKey;
11
- exports.importPublicKey = importPublicKey;
12
- async function generateKeyPair() {
5
+ export async function generateKeyPair() {
13
6
  // Uses standard Web Crypto API (Node 19+ compatible)
14
7
  return await crypto.subtle.generateKey({ name: "ECDSA", namedCurve: "P-256" }, true, ["sign", "verify"]);
15
8
  }
16
- async function signData(privateKey, data) {
9
+ export async function signData(privateKey, data) {
17
10
  const encoder = new TextEncoder();
18
11
  const encoded = encoder.encode(data);
19
12
  const signature = await crypto.subtle.sign({ name: "ECDSA", hash: { name: "SHA-256" } }, privateKey, encoded);
20
13
  return bufToHex(signature);
21
14
  }
22
- async function verifySignature(publicKey, data, signatureHex) {
15
+ export async function verifySignature(publicKey, data, signatureHex) {
23
16
  const encoder = new TextEncoder();
24
17
  const encodedData = encoder.encode(data);
25
18
  const signatureBytes = hexToBuf(signatureHex);
@@ -28,11 +21,11 @@ async function verifySignature(publicKey, data, signatureHex) {
28
21
  console.log(` ${isValid ? "āœ…" : "āŒ"} [AAMP Crypto] Signature Result: ${isValid ? "VALID" : "INVALID"}`);
29
22
  return isValid;
30
23
  }
31
- async function exportPublicKey(key) {
24
+ export async function exportPublicKey(key) {
32
25
  const exported = await crypto.subtle.exportKey("spki", key);
33
26
  return btoa(String.fromCharCode(...new Uint8Array(exported)));
34
27
  }
35
- async function importPublicKey(keyData) {
28
+ export async function importPublicKey(keyData) {
36
29
  const binaryString = atob(keyData);
37
30
  const bytes = new Uint8Array(binaryString.length);
38
31
  for (let i = 0; i < binaryString.length; i++) {
package/dist/express.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { AccessPolicy, ContentOrigin, UnauthenticatedStrategy, IdentityCache } from './types';
1
+ import { AccessPolicy, ContentOrigin, UnauthenticatedStrategy, IdentityCache } from './types.js';
2
2
  export interface AAMPConfig {
3
3
  policy: Omit<AccessPolicy, 'version'>;
4
4
  meta: {
package/dist/express.js CHANGED
@@ -1,18 +1,15 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.AAMP = void 0;
4
1
  /**
5
2
  * Layer 3: Framework Adapters
6
3
  * Zero-friction integration for Express/Node.js.
7
4
  */
8
- const publisher_1 = require("./publisher");
9
- const types_1 = require("./types");
10
- const crypto_1 = require("./crypto");
11
- class AAMP {
5
+ import { AAMPPublisher } from './publisher.js';
6
+ import { ContentOrigin } from './types.js';
7
+ import { generateKeyPair } from './crypto.js';
8
+ export class AAMP {
12
9
  constructor(config) {
13
- this.publisher = new publisher_1.AAMPPublisher({ version: '1.1', ...config.policy }, config.strategy || 'PASSIVE', config.cache);
14
- this.origin = types_1.ContentOrigin[config.meta.origin];
15
- this.ready = (0, crypto_1.generateKeyPair)().then(keys => {
10
+ this.publisher = new AAMPPublisher({ version: '1.1', ...config.policy }, config.strategy || 'PASSIVE', config.cache);
11
+ this.origin = ContentOrigin[config.meta.origin];
12
+ this.ready = generateKeyPair().then(keys => {
16
13
  return this.publisher.initialize(keys);
17
14
  });
18
15
  }
@@ -39,7 +36,8 @@ class AAMP {
39
36
  if (!result.allowed) {
40
37
  res.status(result.status).json({
41
38
  error: result.reason,
42
- visitor_type: result.visitorType
39
+ visitor_type: result.visitorType,
40
+ proof_used: result.proofUsed
43
41
  });
44
42
  return;
45
43
  }
@@ -64,4 +62,3 @@ class AAMP {
64
62
  };
65
63
  }
66
64
  }
67
- exports.AAMP = AAMP;
package/dist/index.d.ts CHANGED
@@ -3,10 +3,10 @@
3
3
  *
4
4
  * This is the main entry point for the library.
5
5
  */
6
- export * from './types';
7
- export * from './constants';
8
- export * from './agent';
9
- export * from './publisher';
10
- export * from './crypto';
11
- export * from './express';
12
- export { AAMPNext } from './nextjs';
6
+ export * from './types.js';
7
+ export * from './constants.js';
8
+ export * from './agent.js';
9
+ export * from './publisher.js';
10
+ export * from './crypto.js';
11
+ export * from './express.js';
12
+ export { AAMPNext } from './nextjs.js';
package/dist/index.js CHANGED
@@ -1,30 +1,12 @@
1
- "use strict";
2
1
  /**
3
2
  * AAMP SDK Public API
4
3
  *
5
4
  * This is the main entry point for the library.
6
5
  */
7
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
- if (k2 === undefined) k2 = k;
9
- var desc = Object.getOwnPropertyDescriptor(m, k);
10
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
- desc = { enumerable: true, get: function() { return m[k]; } };
12
- }
13
- Object.defineProperty(o, k2, desc);
14
- }) : (function(o, m, k, k2) {
15
- if (k2 === undefined) k2 = k;
16
- o[k2] = m[k];
17
- }));
18
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
19
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
20
- };
21
- Object.defineProperty(exports, "__esModule", { value: true });
22
- exports.AAMPNext = void 0;
23
- __exportStar(require("./types"), exports);
24
- __exportStar(require("./constants"), exports);
25
- __exportStar(require("./agent"), exports);
26
- __exportStar(require("./publisher"), exports);
27
- __exportStar(require("./crypto"), exports);
28
- __exportStar(require("./express"), exports); // Node.js / Express Adapter
29
- var nextjs_1 = require("./nextjs"); // Serverless / Next.js Adapter
30
- Object.defineProperty(exports, "AAMPNext", { enumerable: true, get: function () { return nextjs_1.AAMPNext; } });
6
+ export * from './types.js';
7
+ export * from './constants.js';
8
+ export * from './agent.js';
9
+ export * from './publisher.js';
10
+ export * from './crypto.js';
11
+ export * from './express.js'; // Node.js / Express Adapter
12
+ export { AAMPNext } from './nextjs.js'; // Serverless / Next.js Adapter
package/dist/nextjs.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { AccessPolicy, ContentOrigin, UnauthenticatedStrategy, IdentityCache } from './types';
1
+ import { AccessPolicy, ContentOrigin, UnauthenticatedStrategy, IdentityCache } from './types.js';
2
2
  type NextRequest = any;
3
3
  type NextResponse = any;
4
4
  export interface AAMPConfig {
package/dist/nextjs.js CHANGED
@@ -1,24 +1,21 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.AAMPNext = void 0;
4
1
  /**
5
2
  * Layer 3: Framework Adapters
6
3
  * Serverless integration for Next.js (App Router & API Routes).
7
4
  */
8
- const publisher_1 = require("./publisher");
9
- const types_1 = require("./types");
10
- const crypto_1 = require("./crypto");
5
+ import { AAMPPublisher } from './publisher.js';
6
+ import { ContentOrigin } from './types.js';
7
+ import { generateKeyPair } from './crypto.js';
11
8
  const createJsonResponse = (body, status = 200) => {
12
9
  return new Response(JSON.stringify(body), {
13
10
  status,
14
11
  headers: { 'Content-Type': 'application/json' }
15
12
  });
16
13
  };
17
- class AAMPNext {
14
+ export class AAMPNext {
18
15
  constructor(config) {
19
- this.publisher = new publisher_1.AAMPPublisher({ version: '1.1', ...config.policy }, config.strategy || 'PASSIVE', config.cache);
20
- this.origin = types_1.ContentOrigin[config.meta.origin];
21
- this.ready = (0, crypto_1.generateKeyPair)().then(keys => this.publisher.initialize(keys));
16
+ this.publisher = new AAMPPublisher({ version: '1.1', ...config.policy }, config.strategy || 'PASSIVE', config.cache);
17
+ this.origin = ContentOrigin[config.meta.origin];
18
+ this.ready = generateKeyPair().then(keys => this.publisher.initialize(keys));
22
19
  }
23
20
  static init(config) {
24
21
  return new AAMPNext(config);
@@ -39,7 +36,8 @@ class AAMPNext {
39
36
  if (!result.allowed) {
40
37
  return createJsonResponse({
41
38
  error: result.reason,
42
- visitor_type: result.visitorType
39
+ visitor_type: result.visitorType,
40
+ proof_used: result.proofUsed
43
41
  }, result.status);
44
42
  }
45
43
  // Execute Handler
@@ -60,4 +58,3 @@ class AAMPNext {
60
58
  };
61
59
  }
62
60
  }
63
- exports.AAMPNext = AAMPNext;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Verifies a JWT (Proof Token or Payment Credential) using JWKS.
3
+ *
4
+ * @param token The JWT string
5
+ * @param jwksUrl The URL to fetch Public Keys
6
+ * @param issuer The expected issuer
7
+ * @param audience The expected audience range
8
+ */
9
+ export declare function verifyJwt(token: string, jwksUrl: string, issuer: string, audience?: string): Promise<boolean>;
package/dist/proof.js ADDED
@@ -0,0 +1,27 @@
1
+ import { createRemoteJWKSet, jwtVerify } from 'jose';
2
+ // In-memory cache for JWKS to avoid repeated fetches
3
+ // Jose's createRemoteJWKSet handles caching/cooldowns internally.
4
+ /**
5
+ * Verifies a JWT (Proof Token or Payment Credential) using JWKS.
6
+ *
7
+ * @param token The JWT string
8
+ * @param jwksUrl The URL to fetch Public Keys
9
+ * @param issuer The expected issuer
10
+ * @param audience The expected audience range
11
+ */
12
+ export async function verifyJwt(token, jwksUrl, issuer, audience) {
13
+ try {
14
+ const JWKS = createRemoteJWKSet(new URL(jwksUrl));
15
+ const { payload } = await jwtVerify(token, JWKS, {
16
+ issuer: issuer,
17
+ audience: audience // specific audience check if provided
18
+ });
19
+ // Check specific AAMP claims if we standardize them
20
+ // if (payload.type !== 'AD_IMPRESSION') return false;
21
+ return true;
22
+ }
23
+ catch (error) {
24
+ // console.error("Ad Proof Verification Failed:", error);
25
+ return false;
26
+ }
27
+ }
@@ -1,4 +1,4 @@
1
- import { AccessPolicy, ContentOrigin, EvaluationResult, IdentityCache, UnauthenticatedStrategy } from './types';
1
+ import { AccessPolicy, ContentOrigin, EvaluationResult, IdentityCache, UnauthenticatedStrategy } from './types.js';
2
2
  export declare class AAMPPublisher {
3
3
  private policy;
4
4
  private keyPair;
@@ -27,4 +27,9 @@ export declare class AAMPPublisher {
27
27
  private verifyDnsBinding;
28
28
  private isDomain;
29
29
  generateResponseHeaders(origin: ContentOrigin): Promise<Record<string, string>>;
30
+ /**
31
+ * Handling Quality Feedback (The "Dispute" Layer)
32
+ * This runs when an Agent sends 'x-aamp-feedback'.
33
+ */
34
+ private handleFeedback;
30
35
  }
package/dist/publisher.js CHANGED
@@ -1,13 +1,11 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.AAMPPublisher = void 0;
4
1
  /**
5
2
  * Layer 2: Publisher Middleware
6
3
  * Used by content owners to enforce policy, log access, and filter bots.
7
4
  */
8
- const constants_1 = require("./constants");
9
- const crypto_1 = require("./crypto");
10
- const types_1 = require("./types");
5
+ import { HEADERS, MAX_CLOCK_SKEW_MS, WELL_KNOWN_AGENT_PATH } from './constants.js';
6
+ import { exportPublicKey, signData, verifySignature } from './crypto.js';
7
+ import { AccessPurpose } from './types.js';
8
+ import { verifyJwt } from './proof.js';
11
9
  /**
12
10
  * Default In-Memory Cache (Fallback only)
13
11
  * NOT recommended for high-traffic Serverless production.
@@ -33,7 +31,7 @@ class MemoryCache {
33
31
  });
34
32
  }
35
33
  }
36
- class AAMPPublisher {
34
+ export class AAMPPublisher {
37
35
  constructor(policy, strategy = 'PASSIVE', cacheImpl) {
38
36
  this.keyPair = null;
39
37
  // Default TTL: 1 Hour
@@ -53,7 +51,11 @@ class AAMPPublisher {
53
51
  */
54
52
  async evaluateVisitor(reqHeaders, rawPayload) {
55
53
  // 1. Check for AAMP Headers
56
- const hasAamp = reqHeaders[constants_1.HEADERS.PAYLOAD] && reqHeaders[constants_1.HEADERS.SIGNATURE] && reqHeaders[constants_1.HEADERS.PUBLIC_KEY];
54
+ const hasAamp = reqHeaders[HEADERS.PAYLOAD] && reqHeaders[HEADERS.SIGNATURE] && reqHeaders[HEADERS.PUBLIC_KEY];
55
+ const feedbackToken = reqHeaders[HEADERS.FEEDBACK];
56
+ if (feedbackToken) {
57
+ await this.handleFeedback(feedbackToken, reqHeaders);
58
+ }
57
59
  if (hasAamp) {
58
60
  console.log("\nšŸ”Ž [AAMP Middleware] Detected Agent Headers. Starting Verification...");
59
61
  // It claims to be an Agent. Verify it.
@@ -129,9 +131,9 @@ class AAMPPublisher {
129
131
  */
130
132
  async handleAgent(reqHeaders, rawPayload) {
131
133
  try {
132
- const payloadHeader = reqHeaders[constants_1.HEADERS.PAYLOAD];
133
- const sigHeader = reqHeaders[constants_1.HEADERS.SIGNATURE];
134
- const keyHeader = reqHeaders[constants_1.HEADERS.PUBLIC_KEY];
134
+ const payloadHeader = reqHeaders[HEADERS.PAYLOAD];
135
+ const sigHeader = reqHeaders[HEADERS.SIGNATURE];
136
+ const keyHeader = reqHeaders[HEADERS.PUBLIC_KEY];
135
137
  const headerJson = atob(payloadHeader);
136
138
  const requestHeader = JSON.parse(headerJson);
137
139
  const signedRequest = {
@@ -140,10 +142,17 @@ class AAMPPublisher {
140
142
  publicKey: keyHeader
141
143
  };
142
144
  const agentKey = await crypto.subtle.importKey("spki", new Uint8Array(atob(keyHeader).split('').map(c => c.charCodeAt(0))), { name: "ECDSA", namedCurve: "P-256" }, true, ["verify"]);
145
+ const proofToken = reqHeaders[HEADERS.PROOF_TOKEN];
146
+ const paymentCredential = reqHeaders[HEADERS.PAYMENT_CREDENTIAL];
143
147
  // Verify Core Logic
144
- const result = await this.verifyRequestLogic(signedRequest, agentKey, headerJson);
148
+ const result = await this.verifyRequestLogic(signedRequest, agentKey, proofToken, paymentCredential, headerJson);
145
149
  if (!result.allowed) {
146
- console.log(`ā›” [AAMP Deny] Reason: ${result.reason}`);
150
+ console.log(`ā›” [AAMP BLOCK]
151
+ Agent: ${requestHeader.agent_id}
152
+ Reason: ${result.reason}
153
+ VisitorType: ${result.visitorType}
154
+ Proof: ${result.proofUsed || 'None'}
155
+ Identity Verified: ${result.identityVerified}`);
147
156
  return {
148
157
  allowed: false,
149
158
  status: 403,
@@ -151,7 +160,11 @@ class AAMPPublisher {
151
160
  visitorType: 'VERIFIED_AGENT'
152
161
  };
153
162
  }
154
- console.log(`āœ… [AAMP Allow] Verified Agent: ${requestHeader.agent_id}`);
163
+ console.log(`āœ… [AAMP ALLOW]
164
+ Agent: ${requestHeader.agent_id}
165
+ Reason: AAMP_VERIFIED
166
+ Payment Method: ${result.proofUsed}
167
+ Identity Verified: ${result.identityVerified}`);
155
168
  return {
156
169
  allowed: true,
157
170
  status: 200,
@@ -165,21 +178,21 @@ class AAMPPublisher {
165
178
  return { allowed: false, status: 400, reason: "INVALID_SIGNATURE", visitorType: 'UNIDENTIFIED_BOT' };
166
179
  }
167
180
  }
168
- async verifyRequestLogic(request, requestPublicKey, rawPayload) {
181
+ async verifyRequestLogic(request, requestPublicKey, proofToken, paymentCredential, rawPayload) {
169
182
  // 1. Replay Attack Prevention
170
183
  const requestTime = new Date(request.header.ts).getTime();
171
- if (Math.abs(Date.now() - requestTime) > constants_1.MAX_CLOCK_SKEW_MS) {
184
+ if (Math.abs(Date.now() - requestTime) > MAX_CLOCK_SKEW_MS) {
172
185
  return { allowed: false, reason: 'TIMESTAMP_INVALID', identityVerified: false };
173
186
  }
174
187
  // 2. Crypto Verification
175
188
  const signableString = rawPayload || JSON.stringify(request.header);
176
- const isCryptoValid = await (0, crypto_1.verifySignature)(requestPublicKey, signableString, request.signature);
189
+ const isCryptoValid = await verifySignature(requestPublicKey, signableString, request.signature);
177
190
  if (!isCryptoValid)
178
191
  return { allowed: false, reason: 'CRYPTO_FAIL', identityVerified: false };
179
192
  // 3. Identity Verification (DNS Binding) with Cache
180
193
  let identityVerified = false;
181
194
  const claimedDomain = request.header.agent_id;
182
- const pubKeyString = await (0, crypto_1.exportPublicKey)(requestPublicKey);
195
+ const pubKeyString = await exportPublicKey(requestPublicKey);
183
196
  console.log(` šŸ†” [AAMP Identity] Verifying DNS Binding for: ${claimedDomain}`);
184
197
  // Check Cache First
185
198
  const cachedKey = await this.cache.get(claimedDomain);
@@ -198,17 +211,56 @@ class AAMPPublisher {
198
211
  return { allowed: false, reason: 'IDENTITY_FAIL: DNS Binding could not be verified.', identityVerified: false };
199
212
  }
200
213
  // 4. Policy Check: Purpose
201
- if (request.header.purpose === types_1.AccessPurpose.CRAWL_TRAINING && !this.policy.allowTraining) {
214
+ if (request.header.purpose === AccessPurpose.CRAWL_TRAINING && !this.policy.allowTraining) {
202
215
  return { allowed: false, reason: 'POLICY_DENIED: Training not allowed.', identityVerified };
203
216
  }
204
- if (request.header.purpose === types_1.AccessPurpose.RAG_RETRIEVAL && !this.policy.allowRAG) {
217
+ if (request.header.purpose === AccessPurpose.RAG_RETRIEVAL && !this.policy.allowRAG) {
205
218
  return { allowed: false, reason: 'POLICY_DENIED: RAG not allowed.', identityVerified };
206
219
  }
207
- // 5. Policy Check: Economics
220
+ // 5. Policy Check: Economics (v1.2)
208
221
  if (this.policy.requiresPayment) {
209
- const isAdExempt = this.policy.allowAdSupportedAccess && request.header.context.ads_displayed;
210
- if (!isAdExempt) {
211
- return { allowed: false, reason: 'PAYMENT_REQUIRED: Content requires payment or ads.', identityVerified };
222
+ let paymentSatisfied = false;
223
+ // Method A: Flexible Payment Callback (DB / Custom Logic)
224
+ if (this.policy.monetization?.checkPayment) {
225
+ const isPaid = await this.policy.monetization.checkPayment(request.header.agent_id, request.header.purpose);
226
+ if (isPaid) {
227
+ console.log(` šŸ’° [AAMP Audit] Whitelist Check Passed via Callback.`);
228
+ return { allowed: true, reason: 'OK', identityVerified, proofUsed: 'WHITELIST_CALLBACK' };
229
+ }
230
+ }
231
+ // Method B: Payment Credentials (Unified JWT)
232
+ if (!paymentSatisfied && this.policy.monetization?.paymentConfig && paymentCredential) {
233
+ const { jwksUrl, issuer } = this.policy.monetization.paymentConfig;
234
+ console.log(` šŸ” [AAMP Audit] Verifying Payment Credential (Issuer: ${issuer})...`);
235
+ const isValidCredential = await verifyJwt(paymentCredential, jwksUrl, issuer);
236
+ if (isValidCredential) {
237
+ console.log(` āœ… [AAMP Audit] Credential Signature VALID.`);
238
+ return { allowed: true, reason: 'OK', identityVerified, proofUsed: 'PAYMENT_CREDENTIAL_JWT' };
239
+ }
240
+ else {
241
+ console.log(` āŒ [AAMP Audit] Credential Signature INVALID.`);
242
+ }
243
+ }
244
+ // Method C: Ad-Supported (Proof Verification)
245
+ if (!paymentSatisfied && this.policy.allowAdSupportedAccess && request.header.context.ads_displayed) {
246
+ if (proofToken && this.policy.monetization?.adNetwork) {
247
+ const { jwksUrl, issuer } = this.policy.monetization.adNetwork;
248
+ console.log(` šŸ“ŗ [AAMP Audit] Verifying Ad Proof (Issuer: ${issuer})...`);
249
+ const isValidProof = await verifyJwt(proofToken, jwksUrl, issuer);
250
+ if (isValidProof) {
251
+ console.log(` āœ… [AAMP Audit] Ad Proof Signature VALID.`);
252
+ return { allowed: true, reason: 'OK', identityVerified, proofUsed: 'AD_PROOF_JWT' };
253
+ }
254
+ else {
255
+ console.log(` āŒ [AAMP Audit] Ad Proof Signature INVALID.`);
256
+ }
257
+ }
258
+ else {
259
+ console.log(` āš ļø [AAMP Audit] Ad Proof MISSING.`);
260
+ }
261
+ }
262
+ if (!paymentSatisfied) {
263
+ return { allowed: false, reason: 'PAYMENT_REQUIRED: Whitelist, Credential, and Ad Proof checks ALL failed.', identityVerified, proofUsed: 'NONE', visitorType: 'VERIFIED_AGENT' };
212
264
  }
213
265
  }
214
266
  return { allowed: true, reason: 'OK', identityVerified };
@@ -217,7 +269,7 @@ class AAMPPublisher {
217
269
  try {
218
270
  // Allow HTTP for localhost testing
219
271
  const protocol = (domain.includes('localhost') || domain.match(/:\d+$/)) ? 'http' : 'https';
220
- const url = `${protocol}://${domain}${constants_1.WELL_KNOWN_AGENT_PATH}`;
272
+ const url = `${protocol}://${domain}${WELL_KNOWN_AGENT_PATH}`;
221
273
  console.log(` šŸŒ [AAMP DNS] Fetching Manifest: ${url} ...`);
222
274
  // In production, we need a short timeout to prevent hanging
223
275
  const controller = new AbortController();
@@ -256,11 +308,31 @@ class AAMPPublisher {
256
308
  if (!this.keyPair)
257
309
  throw new Error("Publisher keys not initialized");
258
310
  const payload = JSON.stringify({ origin, ts: Date.now() });
259
- const signature = await (0, crypto_1.signData)(this.keyPair.privateKey, payload);
311
+ const signature = await signData(this.keyPair.privateKey, payload);
260
312
  return {
261
- [constants_1.HEADERS.CONTENT_ORIGIN]: origin,
262
- [constants_1.HEADERS.PROVENANCE_SIG]: signature
313
+ [HEADERS.CONTENT_ORIGIN]: origin,
314
+ [HEADERS.PROVENANCE_SIG]: signature
263
315
  };
264
316
  }
317
+ /**
318
+ * Handling Quality Feedback (The "Dispute" Layer)
319
+ * This runs when an Agent sends 'x-aamp-feedback'.
320
+ */
321
+ async handleFeedback(token, headers) {
322
+ // NOTE: In production, you would fetch the Agent's specific key.
323
+ // For now, we assume standard Discovery or a centralized Key Set (like adNetwork).
324
+ // Ideally, the SDK config should have a 'qualityOracle' key set.
325
+ // 1. We just Decode it to Log it (Verification is optional but recommended)
326
+ try {
327
+ const parts = token.split('.');
328
+ const payload = JSON.parse(atob(parts[1]));
329
+ console.log(`\nšŸ“¢ [AAMP QUALITY ALERT] Feedback Received from ${payload.agent_id}`);
330
+ console.log(` Reason: ${payload.reason} | Score: ${payload.quality_score}`);
331
+ console.log(` Resource: ${payload.url}`);
332
+ console.log(` (Signature available for dispute evidence)`);
333
+ }
334
+ catch (e) {
335
+ console.log(` āš ļø [AAMP Warning] Malformed Feedback Token.`);
336
+ }
337
+ }
265
338
  }
266
- exports.AAMPPublisher = AAMPPublisher;
package/dist/types.d.ts CHANGED
@@ -37,12 +37,22 @@ export interface IdentityCache {
37
37
  get(key: string): Promise<string | null>;
38
38
  set(key: string, value: string, ttlSeconds: number): Promise<void>;
39
39
  }
40
+ /**
41
+ * Optional Monetization (The Settlement Layer)
42
+ */
40
43
  /**
41
44
  * Optional Monetization (The Settlement Layer)
42
45
  */
43
46
  export interface MonetizationConfig {
44
- method: 'BROKER' | 'CRYPTO' | 'PRIVATE_TREATY';
45
- location: string;
47
+ checkPayment?: (agentId: string, purpose: string) => boolean | Promise<boolean>;
48
+ adNetwork?: {
49
+ jwksUrl: string;
50
+ issuer: string;
51
+ };
52
+ paymentConfig?: {
53
+ jwksUrl: string;
54
+ issuer: string;
55
+ };
46
56
  }
47
57
  /**
48
58
  * Handling Non-AAMP Visitors
@@ -91,4 +101,13 @@ export interface EvaluationResult {
91
101
  reason: string;
92
102
  visitorType: 'VERIFIED_AGENT' | 'LIKELY_HUMAN' | 'UNIDENTIFIED_BOT';
93
103
  metadata?: any;
104
+ payment_status?: 'PAID_SUBSCRIBER' | 'AD_FUNDED' | 'UNPAID';
105
+ proofUsed?: string;
106
+ }
107
+ export interface FeedbackSignalToken {
108
+ url: string;
109
+ agent_id: string;
110
+ quality_score: number;
111
+ reason: string;
112
+ timestamp: number;
94
113
  }