@aamp/protocol 1.1.6 → 1.1.8

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/package.json CHANGED
@@ -1,9 +1,15 @@
1
1
  {
2
2
  "name": "@aamp/protocol",
3
- "version": "1.1.6",
3
+ "version": "1.1.8",
4
4
  "description": "TypeScript reference implementation of AAMP v1.1",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
+ "exports": {
8
+ ".": "./dist/index.js",
9
+ "./dist/nextjs": "./dist/nextjs.js",
10
+ "./dist/agent": "./dist/agent.js",
11
+ "./dist/types": "./dist/types.js"
12
+ },
7
13
  "type": "module",
8
14
  "scripts": {
9
15
  "build": "tsc",
package/src/publisher.ts CHANGED
@@ -69,63 +69,56 @@ export class AAMPPublisher {
69
69
 
70
70
  /**
71
71
  * Main Entry Point: Evaluate ANY visitor (Human, Bot, or Agent)
72
+ * STAGE 1: IDENTITY (Strict)
73
+ * STAGE 2: POLICY (Permissions)
74
+ * STAGE 3: ACCESS (HQ Content)
72
75
  */
73
76
  async evaluateVisitor(
74
77
  reqHeaders: Record<string, string | undefined>,
75
78
  rawPayload?: string
76
79
  ): Promise<EvaluationResult> {
80
+ console.log(`\n--- [AAMP LOG START] New Request ---`);
77
81
 
78
- // 1. Check for AAMP Headers
79
- const hasAamp = reqHeaders[HEADERS.PAYLOAD] && reqHeaders[HEADERS.SIGNATURE] && reqHeaders[HEADERS.PUBLIC_KEY];
82
+ // --- STAGE 1: IDENTITY VERIFICATION ---
83
+ console.log(`[IDENTITY] šŸ” Checking Identity Headers...`);
80
84
 
81
- const feedbackToken = reqHeaders[HEADERS.FEEDBACK];
82
- if (feedbackToken) {
83
- await this.handleFeedback(feedbackToken, reqHeaders);
84
- }
85
+ const hasAamp = reqHeaders[HEADERS.PAYLOAD] && reqHeaders[HEADERS.SIGNATURE] && reqHeaders[HEADERS.PUBLIC_KEY];
85
86
 
86
87
  if (hasAamp) {
87
- console.log("\nšŸ”Ž [AAMP Middleware] Detected Agent Headers. Starting Verification...");
88
- // It claims to be an Agent. Verify it.
89
- return await this.handleAgent(reqHeaders, rawPayload);
88
+ // It claims to be an Agent. Verify it STRICTLY.
89
+ return await this.handleAgentStrict(reqHeaders, rawPayload);
90
90
  }
91
91
 
92
- // 2. It's not an AAMP Agent. Apply Strategy.
92
+ // If NO AAMP Headers -> FAIL IDENTITY immediately.
93
+ console.log(`[IDENTITY] āŒ FAILED. No AAMP Headers found.`);
94
+
95
+ // For now, retaining the legacy "Passive/Hybrid" switch just to avoid breaking browser demos completely
96
+ // BUT logging it as a specific "Identity Fail" flow.
93
97
  if (this.unauthenticatedStrategy === 'STRICT') {
98
+ console.log(`[IDENTITY] ā›” BLOCKING. Strategy is STRICT.`);
94
99
  return {
95
100
  allowed: false,
96
101
  status: 401,
97
- reason: "STRICT_MODE: Only AAMP verified agents allowed.",
102
+ reason: "IDENTITY_REQUIRED: Missing AAMP Headers.",
98
103
  visitorType: 'UNIDENTIFIED_BOT'
99
104
  };
100
105
  }
101
106
 
102
- if (this.unauthenticatedStrategy === 'PASSIVE') {
103
- return {
104
- allowed: true,
105
- status: 200,
106
- reason: "PASSIVE_MODE: Allowed without verification.",
107
- visitorType: 'LIKELY_HUMAN'
108
- };
109
- }
110
-
111
- // 3. HYBRID MODE: Heuristic Analysis (The "Lazy Bot" Filter)
107
+ console.log(`[IDENTITY] āš ļø SKIPPED (Legacy Mode). Checking Browser Heuristics...`);
112
108
  const isHuman = this.performBrowserHeuristics(reqHeaders);
113
-
114
109
  if (isHuman) {
115
- return {
116
- allowed: true,
117
- status: 200,
118
- reason: "BROWSER_VERIFIED: Heuristics passed.",
119
- visitorType: 'LIKELY_HUMAN'
120
- };
121
- } else {
122
- return {
123
- allowed: false,
124
- status: 403,
125
- reason: "BOT_DETECTED: Request lacks browser signatures and AAMP headers.",
126
- visitorType: 'UNIDENTIFIED_BOT'
127
- };
110
+ console.log(`[POLICY] šŸ‘¤ ALLOWED. Browser Heuristics Passed.`);
111
+ return { allowed: true, status: 200, reason: "BROWSER_VERIFIED", visitorType: 'LIKELY_HUMAN' };
128
112
  }
113
+
114
+ console.log(`[IDENTITY] āŒ FAILED. Not a Browser, No Headers.`);
115
+ console.log(`[ACCESS] ā›” BLOCKED.`);
116
+ return {
117
+ allowed: false,
118
+ status: 403,
119
+ reason: "IDENTITY_FAIL: No Identity, No Browser.",
120
+ visitorType: 'UNIDENTIFIED_BOT'
121
+ };
129
122
  }
130
123
 
131
124
  /**
@@ -161,17 +154,24 @@ export class AAMPPublisher {
161
154
  }
162
155
 
163
156
  /**
164
- * Handle AAMP Protocol Logic
157
+ * Handle AAMP Protocol Logic (Strict Mode)
165
158
  */
166
- private async handleAgent(reqHeaders: Record<string, string | undefined>, rawPayload?: string): Promise<EvaluationResult> {
159
+ private async handleAgentStrict(reqHeaders: Record<string, string | undefined>, rawPayload?: string): Promise<EvaluationResult> {
160
+ let agentId = "UNKNOWN";
161
+
167
162
  try {
163
+ // 1. Decode Headers
168
164
  const payloadHeader = reqHeaders[HEADERS.PAYLOAD]!;
169
165
  const sigHeader = reqHeaders[HEADERS.SIGNATURE]!;
170
166
  const keyHeader = reqHeaders[HEADERS.PUBLIC_KEY]!;
171
167
 
172
168
  const headerJson = atob(payloadHeader);
173
169
  const requestHeader = JSON.parse(headerJson);
170
+ agentId = requestHeader.agent_id;
174
171
 
172
+ console.log(`[IDENTITY] šŸ†” Claimed ID: ${agentId}`);
173
+
174
+ // 2. Crypto & DNS Verification
175
175
  const signedRequest: SignedAccessRequest = {
176
176
  header: requestHeader,
177
177
  signature: sigHeader,
@@ -186,151 +186,173 @@ export class AAMPPublisher {
186
186
  ["verify"]
187
187
  );
188
188
 
189
+ // Verify Core Logic (DNS + Crypto)
190
+ const verification = await this.verifyRequestLogic(signedRequest, agentKey);
191
+
192
+ if (!verification.identityVerified) {
193
+ console.log(`[IDENTITY] āŒ FAILED. Reason: ${verification.reason}`);
194
+ console.log(`[ACCESS] ā›” BLOCKED.`);
195
+ return { allowed: false, status: 403, reason: verification.reason, visitorType: 'UNIDENTIFIED_BOT' };
196
+ }
197
+
198
+ console.log(`[IDENTITY] āœ… PASSED. DNS Binding Verified.`);
199
+
200
+ // --- STAGE 2: POLICY ENFORCEMENT ---
201
+ console.log(`[POLICY] šŸ“œ Checking Permissions for ${agentId}...`);
202
+
189
203
  const proofToken = reqHeaders[HEADERS.PROOF_TOKEN];
190
204
  const paymentCredential = reqHeaders[HEADERS.PAYMENT_CREDENTIAL];
191
205
 
192
- // Verify Core Logic
193
- const result = await this.verifyRequestLogic(signedRequest, agentKey, proofToken, paymentCredential, headerJson);
194
-
195
- if (!result.allowed) {
196
- console.log(`ā›” [AAMP BLOCK]
197
- Agent: ${requestHeader.agent_id}
198
- Reason: ${result.reason}
199
- VisitorType: ${result.visitorType}
200
- Proof: ${result.proofUsed || 'None'}
201
- Identity Verified: ${result.identityVerified}`);
202
-
203
- return {
204
- allowed: false,
205
- status: 403,
206
- reason: result.reason,
207
- visitorType: 'VERIFIED_AGENT'
208
- };
206
+ const policyResult = await this.checkPolicyStrict(requestHeader, proofToken, paymentCredential);
207
+
208
+ if (!policyResult.allowed) {
209
+ console.log(`[POLICY] ā›” DENIED. Reason: ${policyResult.reason}`);
210
+ console.log(`[ACCESS] ā›” BLOCKED.`);
211
+ return policyResult;
209
212
  }
210
213
 
211
- console.log(`āœ… [AAMP ALLOW]
212
- Agent: ${requestHeader.agent_id}
213
- Reason: AAMP_VERIFIED
214
- Payment Method: ${result.proofUsed}
215
- Identity Verified: ${result.identityVerified}`);
214
+ // --- STAGE 3: ACCESS GRANT ---
215
+ console.log(`[POLICY] āœ… PASSED. Requirements Met.`);
216
+ console.log(`[ACCESS] šŸ”“ GRANTED. Unlocking HQ Content.`);
216
217
 
217
218
  return {
218
219
  allowed: true,
219
220
  status: 200,
220
221
  reason: "AAMP_VERIFIED",
221
222
  visitorType: 'VERIFIED_AGENT',
222
- metadata: requestHeader
223
+ metadata: requestHeader,
224
+ proofUsed: policyResult.proofUsed
223
225
  };
224
226
 
225
227
  } catch (e) {
226
- console.error(e);
228
+ console.error(`[AAMP ERROR]`, e);
227
229
  return { allowed: false, status: 400, reason: "INVALID_SIGNATURE", visitorType: 'UNIDENTIFIED_BOT' };
228
230
  }
229
231
  }
230
232
 
231
- private async verifyRequestLogic(
232
- request: SignedAccessRequest,
233
- requestPublicKey: CryptoKey,
234
- proofToken?: string,
235
- paymentCredential?: string,
236
- rawPayload?: string
237
- ): Promise<VerificationResult> {
238
-
239
- // 1. Replay Attack Prevention
240
- const requestTime = new Date(request.header.ts).getTime();
241
- if (Math.abs(Date.now() - requestTime) > MAX_CLOCK_SKEW_MS) {
242
- return { allowed: false, reason: 'TIMESTAMP_INVALID', identityVerified: false };
243
- }
244
-
245
- // 2. Crypto Verification
246
- const signableString = rawPayload || JSON.stringify(request.header);
247
- const isCryptoValid = await verifySignature(requestPublicKey, signableString, request.signature);
248
- if (!isCryptoValid) return { allowed: false, reason: 'CRYPTO_FAIL', identityVerified: false };
249
-
250
- // 3. Identity Verification (DNS Binding) with Cache
251
- let identityVerified = false;
252
- const claimedDomain = request.header.agent_id;
253
- const pubKeyString = await exportPublicKey(requestPublicKey);
254
-
255
- console.log(` šŸ†” [AAMP Identity] Verifying DNS Binding for: ${claimedDomain}`);
256
-
257
- // Check Cache First
258
- const cachedKey = await this.cache.get(claimedDomain);
259
-
260
- if (cachedKey === pubKeyString) {
261
- console.log(" ⚔ [AAMP Cache] Identity found in cache.");
262
- identityVerified = true;
263
- } else if (this.isDomain(claimedDomain)) {
264
- // Cache Miss: Perform DNS Fetch
265
- identityVerified = await this.verifyDnsBinding(claimedDomain, pubKeyString);
266
- if (identityVerified) {
267
- await this.cache.set(claimedDomain, pubKeyString, this.CACHE_TTL_SECONDS);
268
- }
269
- }
233
+ // Legacy handler kept for interface compatibility (deprecated)
234
+ private async handleAgent(reqHeaders: Record<string, string | undefined>, rawPayload?: string): Promise<EvaluationResult> {
235
+ return this.handleAgentStrict(reqHeaders, rawPayload);
236
+ }
270
237
 
271
- if (this.policy.requireIdentityBinding && !identityVerified) {
272
- return { allowed: false, reason: 'IDENTITY_FAIL: DNS Binding could not be verified.', identityVerified: false };
273
- }
238
+ /**
239
+ * STAGE 2: POLICY ENFORCEMENT CHECK
240
+ */
241
+ private async checkPolicyStrict(
242
+ requestHeader: any,
243
+ proofToken?: string,
244
+ paymentCredential?: string
245
+ ): Promise<EvaluationResult> {
274
246
 
275
- // 4. Policy Check: Purpose
276
- if (request.header.purpose === AccessPurpose.CRAWL_TRAINING && !this.policy.allowTraining) {
277
- return { allowed: false, reason: 'POLICY_DENIED: Training not allowed.', identityVerified };
247
+ // 1. Policy Check: Purpose Ban (e.g. No Training)
248
+ if (requestHeader.purpose === AccessPurpose.CRAWL_TRAINING && !this.policy.allowTraining) {
249
+ return { allowed: false, status: 403, reason: 'POLICY_DENIED: Training not allowed.', visitorType: 'VERIFIED_AGENT' };
278
250
  }
279
- if (request.header.purpose === AccessPurpose.RAG_RETRIEVAL && !this.policy.allowRAG) {
280
- return { allowed: false, reason: 'POLICY_DENIED: RAG not allowed.', identityVerified };
251
+ if (requestHeader.purpose === AccessPurpose.RAG_RETRIEVAL && !this.policy.allowRAG) {
252
+ return { allowed: false, status: 403, reason: 'POLICY_DENIED: RAG not allowed.', visitorType: 'VERIFIED_AGENT' };
281
253
  }
282
254
 
283
- // 5. Policy Check: Economics (v1.2)
255
+ // 2. Policy Check: Economics (v1.2) - Payment & Ads
284
256
  if (this.policy.requiresPayment) {
285
257
  let paymentSatisfied = false;
286
258
 
287
259
  // Method A: Flexible Payment Callback (DB / Custom Logic)
288
260
  if (this.policy.monetization?.checkPayment) {
289
- const isPaid = await this.policy.monetization.checkPayment(request.header.agent_id, request.header.purpose);
261
+ const isPaid = await this.policy.monetization.checkPayment(requestHeader.agent_id, requestHeader.purpose);
290
262
  if (isPaid) {
291
- console.log(` šŸ’° [AAMP Audit] Whitelist Check Passed via Callback.`);
292
- return { allowed: true, reason: 'OK', identityVerified, proofUsed: 'WHITELIST_CALLBACK' };
263
+ console.log(`[POLICY] šŸ’° Payment Verified via Callback.`);
264
+ return { allowed: true, status: 200, reason: 'OK', visitorType: 'VERIFIED_AGENT', proofUsed: 'WHITELIST_CALLBACK' };
293
265
  }
294
266
  }
295
267
 
296
268
  // Method B: Payment Credentials (Unified JWT)
297
269
  if (!paymentSatisfied && this.policy.monetization?.paymentConfig && paymentCredential) {
298
270
  const { jwksUrl, issuer } = this.policy.monetization.paymentConfig;
299
- console.log(` šŸ” [AAMP Audit] Verifying Payment Credential (Issuer: ${issuer})...`);
271
+ console.log(`[POLICY] šŸ” Verifying Payment Credential (Issuer: ${issuer})...`);
300
272
 
301
273
  const isValidCredential = await verifyJwt(paymentCredential, jwksUrl, issuer);
302
274
  if (isValidCredential) {
303
- console.log(` āœ… [AAMP Audit] Credential Signature VALID.`);
304
- return { allowed: true, reason: 'OK', identityVerified, proofUsed: 'PAYMENT_CREDENTIAL_JWT' };
275
+ console.log(`[POLICY] āœ… Credential Signature VALID.`);
276
+ return { allowed: true, status: 200, reason: 'OK', visitorType: 'VERIFIED_AGENT', proofUsed: 'PAYMENT_CREDENTIAL_JWT' };
305
277
  } else {
306
- console.log(` āŒ [AAMP Audit] Credential Signature INVALID.`);
278
+ console.log(`[POLICY] āŒ Credential Signature INVALID.`);
307
279
  }
308
280
  }
309
281
 
310
282
  // Method C: Ad-Supported (Proof Verification)
311
- if (!paymentSatisfied && this.policy.allowAdSupportedAccess && request.header.context.ads_displayed) {
283
+ if (!paymentSatisfied && this.policy.allowAdSupportedAccess && requestHeader.context?.ads_displayed) {
312
284
  if (proofToken && this.policy.monetization?.adNetwork) {
313
285
  const { jwksUrl, issuer } = this.policy.monetization.adNetwork;
314
- console.log(` šŸ“ŗ [AAMP Audit] Verifying Ad Proof (Issuer: ${issuer})...`);
286
+ console.log(`[POLICY] šŸ“ŗ Verifying Ad Proof (Issuer: ${issuer})...`);
315
287
 
316
288
  const isValidProof = await verifyJwt(proofToken, jwksUrl, issuer);
317
289
  if (isValidProof) {
318
- console.log(` āœ… [AAMP Audit] Ad Proof Signature VALID.`);
319
- return { allowed: true, reason: 'OK', identityVerified, proofUsed: 'AD_PROOF_JWT' };
290
+ console.log(`[POLICY] āœ… Ad Proof Signature VALID.`);
291
+ return { allowed: true, status: 200, reason: 'OK', visitorType: 'VERIFIED_AGENT', proofUsed: 'AD_PROOF_JWT' };
320
292
  } else {
321
- console.log(` āŒ [AAMP Audit] Ad Proof Signature INVALID.`);
293
+ console.log(`[POLICY] āŒ Ad Proof Signature INVALID.`);
322
294
  }
323
295
  } else {
324
- console.log(` āš ļø [AAMP Audit] Ad Proof MISSING.`);
296
+ console.log(`[POLICY] āš ļø Ad Proof MISSING.`);
325
297
  }
326
298
  }
327
299
 
328
- if (!paymentSatisfied) {
329
- return { allowed: false, reason: 'PAYMENT_REQUIRED: Whitelist, Credential, and Ad Proof checks ALL failed.', identityVerified, proofUsed: 'NONE', visitorType: 'VERIFIED_AGENT' };
300
+ return {
301
+ allowed: false,
302
+ status: 402,
303
+ reason: 'PAYMENT_REQUIRED: Whitelist, Credential, and Ad Proof checks ALL failed.',
304
+ visitorType: 'VERIFIED_AGENT',
305
+ proofUsed: 'NONE'
306
+ };
307
+ }
308
+
309
+ // If no payment required, allow.
310
+ return { allowed: true, status: 200, reason: 'OK', visitorType: 'VERIFIED_AGENT' };
311
+ }
312
+
313
+ private async verifyRequestLogic(
314
+ request: SignedAccessRequest,
315
+ requestPublicKey: CryptoKey,
316
+ ): Promise<VerificationResult> {
317
+
318
+ // 1. Replay Attack Prevention
319
+ const requestTime = new Date(request.header.ts).getTime();
320
+ if (Math.abs(Date.now() - requestTime) > MAX_CLOCK_SKEW_MS) {
321
+ return { allowed: false, reason: 'TIMESTAMP_INVALID: Clock skew too large.', identityVerified: false };
322
+ }
323
+
324
+ // 2. Crypto Verification
325
+ const signableString = JSON.stringify(request.header);
326
+ const isCryptoValid = await verifySignature(requestPublicKey, signableString, request.signature);
327
+ if (!isCryptoValid) return { allowed: false, reason: 'CRYPTO_FAIL: Signature invalid.', identityVerified: false };
328
+
329
+ // 3. Identity Verification (DNS Binding) with Cache
330
+ let identityVerified = false;
331
+ const claimedDomain = request.header.agent_id;
332
+ const pubKeyString = await exportPublicKey(requestPublicKey);
333
+
334
+ console.log(`[IDENTITY] šŸ” Verifying DNS Binding for: ${claimedDomain}`);
335
+
336
+ // Check Cache First
337
+ const cachedKey = await this.cache.get(claimedDomain);
338
+
339
+ if (cachedKey === pubKeyString) {
340
+ console.log("[IDENTITY] ⚔ Cache Hit. Identity Verified.");
341
+ identityVerified = true;
342
+ } else if (this.isDomain(claimedDomain)) {
343
+ // Cache Miss: Perform DNS Fetch
344
+ identityVerified = await this.verifyDnsBinding(claimedDomain, pubKeyString);
345
+ if (identityVerified) {
346
+ await this.cache.set(claimedDomain, pubKeyString, this.CACHE_TTL_SECONDS);
330
347
  }
331
348
  }
332
349
 
333
- return { allowed: true, reason: 'OK', identityVerified };
350
+ if (this.policy.requireIdentityBinding && !identityVerified) {
351
+ return { allowed: false, reason: 'IDENTITY_FAIL: DNS Binding could not be verified.', identityVerified: false };
352
+ }
353
+
354
+ // Return verified status so handleAgentStrict can proceed to Policy Check
355
+ return { allowed: true, reason: 'OK', identityVerified: identityVerified };
334
356
  }
335
357
 
336
358
  private async verifyDnsBinding(domain: string, requestKeySpki: string): Promise<boolean> {
package/src/types.ts CHANGED
@@ -128,7 +128,7 @@ export interface FeedbackSignal {
128
128
  // Result of the full evaluation pipeline
129
129
  export interface EvaluationResult {
130
130
  allowed: boolean;
131
- status: 200 | 400 | 401 | 403;
131
+ status: 200 | 400 | 401 | 402 | 403;
132
132
  reason: string;
133
133
  visitorType: 'VERIFIED_AGENT' | 'LIKELY_HUMAN' | 'UNIDENTIFIED_BOT';
134
134
  metadata?: any;
Binary file
package/dist/agent.d.ts DELETED
@@ -1,30 +0,0 @@
1
- /**
2
- * Layer 2: Agent SDK
3
- */
4
- import { AccessPurpose, SignedAccessRequest, FeedbackSignal, QualityFlag, AgentIdentityManifest } from './types.js';
5
- export interface AccessOptions {
6
- adsDisplayed?: boolean;
7
- }
8
- export declare class AAMPAgent {
9
- private keyPair;
10
- agentId: string;
11
- /**
12
- * Initialize the Agent Identity (Ephemeral or Persisted)
13
- * @param customAgentId For PRODUCTION, this should be your domain (e.g., "bot.openai.com")
14
- */
15
- initialize(customAgentId?: string): Promise<void>;
16
- createAccessRequest(resource: string, purpose: AccessPurpose, options?: AccessOptions): Promise<SignedAccessRequest>;
17
- /**
18
- * Helper: Generate the JSON file you must host on your domain
19
- * Host this at: https://{agentId}/.well-known/aamp-agent.json
20
- */
21
- getIdentityManifest(contactEmail?: string): Promise<AgentIdentityManifest>;
22
- /**
23
- * NEW IN V1.1: Quality Feedback Loop
24
- * Allows the Agent to report spam or verify quality of a resource.
25
- */
26
- generateFeedback(resource: string, score: number, flags: QualityFlag[]): Promise<{
27
- signal: FeedbackSignal;
28
- signature: string;
29
- }>;
30
- }
package/dist/agent.js DELETED
@@ -1,65 +0,0 @@
1
- import { generateKeyPair, signData, exportPublicKey } from './crypto.js';
2
- import { AAMP_VERSION } from './constants.js';
3
- export class AAMPAgent {
4
- constructor() {
5
- this.keyPair = null;
6
- this.agentId = "pending";
7
- }
8
- /**
9
- * Initialize the Agent Identity (Ephemeral or Persisted)
10
- * @param customAgentId For PRODUCTION, this should be your domain (e.g., "bot.openai.com")
11
- */
12
- async initialize(customAgentId) {
13
- this.keyPair = await generateKeyPair();
14
- // Use the provided ID (authentic) or generate a session ID (ephemeral)
15
- this.agentId = customAgentId || "agent_" + Math.random().toString(36).substring(7);
16
- }
17
- async createAccessRequest(resource, purpose, options = {}) {
18
- if (!this.keyPair)
19
- throw new Error("Agent not initialized. Call initialize() first.");
20
- const header = {
21
- v: AAMP_VERSION,
22
- ts: new Date().toISOString(),
23
- agent_id: this.agentId,
24
- resource,
25
- purpose,
26
- context: {
27
- ads_displayed: options.adsDisplayed || false
28
- }
29
- };
30
- const signature = await signData(this.keyPair.privateKey, JSON.stringify(header));
31
- const publicKeyExport = await exportPublicKey(this.keyPair.publicKey);
32
- return { header, signature, publicKey: publicKeyExport };
33
- }
34
- /**
35
- * Helper: Generate the JSON file you must host on your domain
36
- * Host this at: https://{agentId}/.well-known/aamp-agent.json
37
- */
38
- async getIdentityManifest(contactEmail) {
39
- if (!this.keyPair)
40
- throw new Error("Agent not initialized.");
41
- const publicKey = await exportPublicKey(this.keyPair.publicKey);
42
- return {
43
- agent_id: this.agentId,
44
- public_key: publicKey,
45
- contact_email: contactEmail
46
- };
47
- }
48
- /**
49
- * NEW IN V1.1: Quality Feedback Loop
50
- * Allows the Agent to report spam or verify quality of a resource.
51
- */
52
- async generateFeedback(resource, score, flags) {
53
- if (!this.keyPair)
54
- throw new Error("Agent not initialized.");
55
- const signal = {
56
- target_resource: resource,
57
- agent_id: this.agentId,
58
- quality_score: Math.max(0, Math.min(1, score)),
59
- flags,
60
- timestamp: new Date().toISOString()
61
- };
62
- const signature = await signData(this.keyPair.privateKey, JSON.stringify(signal));
63
- return { signal, signature };
64
- }
65
- }
@@ -1,25 +0,0 @@
1
- /**
2
- * Layer 1: Protocol Constants
3
- * These values are immutable and defined by the AAMP Specification.
4
- */
5
- export declare const AAMP_VERSION = "1.1";
6
- export declare const WELL_KNOWN_AGENT_PATH = "/.well-known/aamp-agent.json";
7
- export declare const HEADERS: {
8
- readonly PAYLOAD: "x-aamp-payload";
9
- readonly SIGNATURE: "x-aamp-signature";
10
- readonly PUBLIC_KEY: "x-aamp-public-key";
11
- readonly AGENT_ID: "x-aamp-agent-id";
12
- readonly TIMESTAMP: "x-aamp-timestamp";
13
- readonly ALGORITHM: "x-aamp-alg";
14
- readonly CONTENT_ORIGIN: "x-aamp-content-origin";
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";
19
- };
20
- export declare const CRYPTO_CONFIG: {
21
- readonly ALGORITHM_NAME: "ECDSA";
22
- readonly CURVE: "P-256";
23
- readonly HASH: "SHA-256";
24
- };
25
- export declare const MAX_CLOCK_SKEW_MS: number;
package/dist/constants.js DELETED
@@ -1,38 +0,0 @@
1
- /**
2
- * Layer 1: Protocol Constants
3
- * These values are immutable and defined by the AAMP Specification.
4
- */
5
- export const AAMP_VERSION = '1.1';
6
- // The path where Agents MUST host their public key to prove identity.
7
- // Example: https://bot.openai.com/.well-known/aamp-agent.json
8
- export const WELL_KNOWN_AGENT_PATH = '/.well-known/aamp-agent.json';
9
- // HTTP Headers used for the handshake
10
- export const HEADERS = {
11
- // Transport: The signed payload (Base64 encoded JSON of ProtocolHeader)
12
- PAYLOAD: 'x-aamp-payload',
13
- // Transport: The cryptographic signature (Hex)
14
- SIGNATURE: 'x-aamp-signature',
15
- // Transport: The Agent's Public Key (Base64 SPKI)
16
- PUBLIC_KEY: 'x-aamp-public-key',
17
- // Informational / Legacy (Optional if Payload is present)
18
- AGENT_ID: 'x-aamp-agent-id',
19
- TIMESTAMP: 'x-aamp-timestamp',
20
- ALGORITHM: 'x-aamp-alg',
21
- // v1.1 Addition: Provenance (Server to Agent)
22
- CONTENT_ORIGIN: 'x-aamp-content-origin',
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'
30
- };
31
- // Cryptographic Settings
32
- export const CRYPTO_CONFIG = {
33
- ALGORITHM_NAME: 'ECDSA',
34
- CURVE: 'P-256',
35
- HASH: 'SHA-256',
36
- };
37
- // Tolerance
38
- export const MAX_CLOCK_SKEW_MS = 5 * 60 * 1000; // 5 minutes
package/dist/crypto.d.ts DELETED
@@ -1,9 +0,0 @@
1
- /**
2
- * Layer 1: Cryptographic Primitives
3
- * Implementation of ECDSA P-256 signing/verification.
4
- */
5
- export declare function generateKeyPair(): Promise<CryptoKeyPair>;
6
- export declare function signData(privateKey: CryptoKey, data: string): Promise<string>;
7
- export declare function verifySignature(publicKey: CryptoKey, data: string, signatureHex: string): Promise<boolean>;
8
- export declare function exportPublicKey(key: CryptoKey): Promise<string>;
9
- export declare function importPublicKey(keyData: string): Promise<CryptoKey>;
package/dist/crypto.js DELETED
@@ -1,44 +0,0 @@
1
- /**
2
- * Layer 1: Cryptographic Primitives
3
- * Implementation of ECDSA P-256 signing/verification.
4
- */
5
- export async function generateKeyPair() {
6
- // Uses standard Web Crypto API (Node 19+ compatible)
7
- return await crypto.subtle.generateKey({ name: "ECDSA", namedCurve: "P-256" }, true, ["sign", "verify"]);
8
- }
9
- export async function signData(privateKey, data) {
10
- const encoder = new TextEncoder();
11
- const encoded = encoder.encode(data);
12
- const signature = await crypto.subtle.sign({ name: "ECDSA", hash: { name: "SHA-256" } }, privateKey, encoded);
13
- return bufToHex(signature);
14
- }
15
- export async function verifySignature(publicKey, data, signatureHex) {
16
- const encoder = new TextEncoder();
17
- const encodedData = encoder.encode(data);
18
- const signatureBytes = hexToBuf(signatureHex);
19
- console.log(" šŸ” [AAMP Crypto] Verifying ECDSA P-256 Signature...");
20
- const isValid = await crypto.subtle.verify({ name: "ECDSA", hash: { name: "SHA-256" } }, publicKey, signatureBytes, encodedData);
21
- console.log(` ${isValid ? "āœ…" : "āŒ"} [AAMP Crypto] Signature Result: ${isValid ? "VALID" : "INVALID"}`);
22
- return isValid;
23
- }
24
- export async function exportPublicKey(key) {
25
- const exported = await crypto.subtle.exportKey("spki", key);
26
- return btoa(String.fromCharCode(...new Uint8Array(exported)));
27
- }
28
- export async function importPublicKey(keyData) {
29
- const binaryString = atob(keyData);
30
- const bytes = new Uint8Array(binaryString.length);
31
- for (let i = 0; i < binaryString.length; i++) {
32
- bytes[i] = binaryString.charCodeAt(i);
33
- }
34
- return await crypto.subtle.importKey("spki", bytes, { name: "ECDSA", namedCurve: "P-256" }, true, ["verify"]);
35
- }
36
- // Helpers
37
- function bufToHex(buffer) {
38
- return Array.from(new Uint8Array(buffer))
39
- .map(b => b.toString(16).padStart(2, '0'))
40
- .join('');
41
- }
42
- function hexToBuf(hex) {
43
- return new Uint8Array(hex.match(/.{1,2}/g).map(byte => parseInt(byte, 16)));
44
- }
package/dist/express.d.ts DELETED
@@ -1,22 +0,0 @@
1
- import { AccessPolicy, ContentOrigin, UnauthenticatedStrategy, IdentityCache } from './types.js';
2
- export interface AAMPConfig {
3
- policy: Omit<AccessPolicy, 'version'>;
4
- meta: {
5
- origin: keyof typeof ContentOrigin;
6
- paymentPointer?: string;
7
- };
8
- strategy?: UnauthenticatedStrategy;
9
- cache?: IdentityCache;
10
- }
11
- export declare class AAMP {
12
- private publisher;
13
- private origin;
14
- private ready;
15
- private constructor();
16
- static init(config: AAMPConfig): AAMP;
17
- /**
18
- * Express Middleware
19
- */
20
- middleware(): (req: any, res: any, next: any) => Promise<void>;
21
- discoveryHandler(): (req: any, res: any) => void;
22
- }