@kya-os/verifier 1.3.4-canary.9 → 1.4.1

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/core.js DELETED
@@ -1,736 +0,0 @@
1
- import { importJWK, jwtVerify } from "jose";
2
- import { canonicalize } from "json-canonicalize";
3
- import { AGENT_HEADERS, ERROR_HTTP_STATUS, VERIFIER_ERROR_CODES } from "@kya-os/contracts/verifier";
4
- /**
5
- * Isomorphic verifier core for XMCP-I proof validation
6
- *
7
- * This is the heart of the trust system - it verifies that AI agents
8
- * are who they claim to be and have the authority to perform actions.
9
- */
10
- export class VerifierCore {
11
- constructor(config = {}) {
12
- this.didCache = new Map();
13
- this.delegationCache = new Map();
14
- this.config = {
15
- ktaBaseUrl: config.ktaBaseUrl || "https://knowthat.ai",
16
- enableDelegationCheck: config.enableDelegationCheck ?? true,
17
- clockSkewTolerance: config.clockSkewTolerance ?? 120,
18
- sessionTimeout: config.sessionTimeout ?? 1800, // 30 minutes
19
- proofMaxAge: config.proofMaxAge ?? 300, // 5 minutes
20
- allowMockData: config.allowMockData ?? false,
21
- didCacheTtl: config.didCacheTtl ?? 300, // 5 minutes
22
- delegationCacheTtl: config.delegationCacheTtl ?? 60, // 1 minute
23
- };
24
- }
25
- /**
26
- * Verify a detached proof and return verification result
27
- *
28
- * This is the main entry point for proof verification. It performs
29
- * a comprehensive validation of the agent's identity and authorization.
30
- */
31
- async verify(context) {
32
- const startTime = Date.now();
33
- try {
34
- // 1. Validate proof structure
35
- const structureError = this.validateProofStructure(context.proof);
36
- if (structureError) {
37
- this.logVerificationAttempt(context, false, structureError.code);
38
- return this.createErrorResult(structureError);
39
- }
40
- // 2. Verify timestamp and session validity
41
- const timestampError = this.validateTimestamp(context.proof.meta, context.timestamp);
42
- if (timestampError) {
43
- this.logVerificationAttempt(context, false, timestampError.code);
44
- return this.createErrorResult(timestampError);
45
- }
46
- // 3. Verify audience matches
47
- const audienceError = this.validateAudience(context.proof.meta, context.audience);
48
- if (audienceError) {
49
- this.logVerificationAttempt(context, false, audienceError.code);
50
- return this.createErrorResult(audienceError);
51
- }
52
- // 4. Verify Ed25519 signature
53
- const signatureError = await this.verifySignature(context.proof);
54
- if (signatureError) {
55
- this.logVerificationAttempt(context, false, signatureError.code);
56
- return this.createErrorResult(signatureError);
57
- }
58
- // 5. Check delegation if enabled and present
59
- if (this.config.enableDelegationCheck &&
60
- context.proof.meta.delegationRef) {
61
- const delegationError = await this.verifyDelegation(context.proof.meta.delegationRef, context.proof.meta.did, context.proof.meta.kid);
62
- if (delegationError) {
63
- this.logVerificationAttempt(context, false, delegationError.code);
64
- return this.createErrorResult(delegationError);
65
- }
66
- }
67
- // 6. Generate trusted headers and context
68
- const headers = this.generateHeaders(context.proof.meta);
69
- const agentContext = this.generateAgentContext(context.proof.meta);
70
- const duration = Date.now() - startTime;
71
- this.logVerificationAttempt(context, true, "SUCCESS", duration);
72
- return {
73
- success: true,
74
- headers,
75
- agentContext,
76
- };
77
- }
78
- catch (error) {
79
- const duration = Date.now() - startTime;
80
- this.logVerificationAttempt(context, false, "UNEXPECTED_ERROR", duration);
81
- return this.createErrorResult({
82
- code: "XMCP_I_EVERIFY",
83
- message: error instanceof Error ? error.message : "Verification failed",
84
- httpStatus: 500,
85
- details: {
86
- reason: "Unexpected error during verification",
87
- remediation: "Check proof format and try again",
88
- },
89
- });
90
- }
91
- }
92
- /**
93
- * Validate proof structure with comprehensive checks
94
- */
95
- validateProofStructure(proof) {
96
- if (!proof.jws || typeof proof.jws !== "string") {
97
- return {
98
- code: "XMCP_I_EBADPROOF",
99
- message: "Invalid proof: missing or invalid JWS",
100
- httpStatus: 400,
101
- details: {
102
- reason: "JWS field is required and must be a string",
103
- expected: "string",
104
- received: typeof proof.jws,
105
- },
106
- };
107
- }
108
- if (!proof.meta || typeof proof.meta !== "object") {
109
- return {
110
- code: "XMCP_I_EBADPROOF",
111
- message: "Invalid proof: missing or invalid meta",
112
- httpStatus: 400,
113
- details: {
114
- reason: "Meta field is required and must be an object",
115
- expected: "object",
116
- received: typeof proof.meta,
117
- },
118
- };
119
- }
120
- const requiredFields = [
121
- "did",
122
- "kid",
123
- "ts",
124
- "nonce",
125
- "audience",
126
- "sessionId",
127
- "requestHash",
128
- "responseHash",
129
- ];
130
- for (const field of requiredFields) {
131
- if (!proof.meta[field]) {
132
- return {
133
- code: "XMCP_I_EBADPROOF",
134
- message: `Invalid proof: missing required field '${field}'`,
135
- httpStatus: 400,
136
- details: {
137
- reason: `Field '${field}' is required in proof meta`,
138
- expected: "non-empty value",
139
- received: proof.meta[field],
140
- },
141
- };
142
- }
143
- }
144
- // Validate hash format
145
- const hashRegex = /^sha256:[a-f0-9]{64}$/;
146
- if (!hashRegex.test(proof.meta.requestHash)) {
147
- return {
148
- code: "XMCP_I_EBADPROOF",
149
- message: "Invalid proof: malformed requestHash",
150
- httpStatus: 400,
151
- details: {
152
- reason: "requestHash must be in format 'sha256:<64-char-hex>'",
153
- expected: "sha256:[a-f0-9]{64}",
154
- received: proof.meta.requestHash,
155
- },
156
- };
157
- }
158
- if (!hashRegex.test(proof.meta.responseHash)) {
159
- return {
160
- code: "XMCP_I_EBADPROOF",
161
- message: "Invalid proof: malformed responseHash",
162
- httpStatus: 400,
163
- details: {
164
- reason: "responseHash must be in format 'sha256:<64-char-hex>'",
165
- expected: "sha256:[a-f0-9]{64}",
166
- received: proof.meta.responseHash,
167
- },
168
- };
169
- }
170
- // Validate DID format
171
- if (!proof.meta.did.startsWith("did:")) {
172
- return {
173
- code: "XMCP_I_EBADPROOF",
174
- message: "Invalid proof: malformed DID",
175
- httpStatus: 400,
176
- details: {
177
- reason: "DID must start with 'did:'",
178
- expected: "did:*",
179
- received: proof.meta.did,
180
- },
181
- };
182
- }
183
- return null;
184
- }
185
- /**
186
- * Validate timestamp with configurable clock skew tolerance
187
- */
188
- validateTimestamp(meta, currentTimestamp) {
189
- // Validate meta.ts is not NaN (prevents client-controlled NaN attacks)
190
- if (!Number.isFinite(meta.ts)) {
191
- return {
192
- code: VERIFIER_ERROR_CODES.PROOF_INVALID_TS,
193
- message: "Invalid proof: timestamp is not a valid number",
194
- httpStatus: ERROR_HTTP_STATUS[VERIFIER_ERROR_CODES.PROOF_INVALID_TS],
195
- details: {
196
- reason: "Proof timestamp must be a finite number",
197
- received: meta.ts,
198
- remediation: "Ensure proof timestamp is properly formatted",
199
- },
200
- };
201
- }
202
- const now = currentTimestamp || Math.floor(Date.now() / 1000);
203
- // Validate server timestamp is valid (defense in depth)
204
- if (!Number.isFinite(now)) {
205
- return {
206
- code: VERIFIER_ERROR_CODES.SERVER_TIME_INVALID,
207
- message: "Server timestamp validation failed",
208
- httpStatus: ERROR_HTTP_STATUS[VERIFIER_ERROR_CODES.SERVER_TIME_INVALID],
209
- details: {
210
- reason: "Server time is not valid",
211
- remediation: "Contact system administrator",
212
- },
213
- };
214
- }
215
- const skew = this.config.clockSkewTolerance;
216
- // Check for future timestamps
217
- if (meta.ts > now + skew) {
218
- return {
219
- code: VERIFIER_ERROR_CODES.PROOF_FUTURE_TS,
220
- message: "Invalid proof: timestamp is in the future",
221
- httpStatus: ERROR_HTTP_STATUS[VERIFIER_ERROR_CODES.PROOF_FUTURE_TS],
222
- details: {
223
- reason: `Timestamp ${meta.ts} is ${meta.ts - now}s in the future (skew: ${skew}s)`,
224
- expected: `≤ ${now + skew}`,
225
- received: meta.ts,
226
- remediation: "Check client clock sync",
227
- },
228
- };
229
- }
230
- // Check for past timestamps (clock skew exceeded)
231
- if (meta.ts < now - skew) {
232
- return {
233
- code: VERIFIER_ERROR_CODES.PROOF_SKEW_EXCEEDED,
234
- message: "Invalid proof: timestamp outside acceptable range",
235
- httpStatus: ERROR_HTTP_STATUS[VERIFIER_ERROR_CODES.PROOF_SKEW_EXCEEDED],
236
- details: {
237
- reason: `Timestamp ${meta.ts} is ${now - meta.ts}s in the past (skew: ${skew}s)`,
238
- expected: `≥ ${now - skew}`,
239
- received: meta.ts,
240
- remediation: "Check NTP sync; adjust XMCP_I_TS_SKEW_SEC if needed",
241
- },
242
- };
243
- }
244
- // Check proof age if configured
245
- if (this.config.proofMaxAge) {
246
- const age = now - meta.ts;
247
- if (age > this.config.proofMaxAge) {
248
- return {
249
- code: VERIFIER_ERROR_CODES.PROOF_TOO_OLD,
250
- message: "Invalid proof: proof is too old",
251
- httpStatus: ERROR_HTTP_STATUS[VERIFIER_ERROR_CODES.PROOF_TOO_OLD],
252
- details: {
253
- reason: `Proof age ${age}s exceeds maximum ${this.config.proofMaxAge}s`,
254
- expected: `Less than ${this.config.proofMaxAge}s old`,
255
- received: `${age}s old`,
256
- remediation: "Generate a fresh proof",
257
- },
258
- };
259
- }
260
- }
261
- return null;
262
- }
263
- /**
264
- * Validate audience matches expected value
265
- */
266
- validateAudience(meta, expectedAudience) {
267
- if (meta.audience !== expectedAudience) {
268
- return {
269
- code: "XMCP_I_EHANDSHAKE",
270
- message: "Invalid proof: audience mismatch",
271
- httpStatus: 401,
272
- details: {
273
- reason: "Proof audience does not match request audience",
274
- expected: expectedAudience,
275
- received: meta.audience,
276
- },
277
- };
278
- }
279
- return null;
280
- }
281
- /**
282
- * Verify Ed25519 signature using JOSE with proper detached JWS handling
283
- *
284
- * This is the cryptographic heart of the verification process.
285
- * It ensures the proof was signed by the claimed identity.
286
- */
287
- async verifySignature(proof) {
288
- try {
289
- // For testing with mock data, skip actual signature verification
290
- if (this.config.allowMockData && proof.meta.did.startsWith("did:test:")) {
291
- // Parse JWS components for basic validation
292
- const jwsParts = proof.jws.split(".");
293
- if (jwsParts.length !== 3 || jwsParts[1] !== "") {
294
- return {
295
- code: "XMCP_I_EBADPROOF",
296
- message: "Invalid detached JWS format",
297
- httpStatus: 403,
298
- details: {
299
- reason: "Detached JWS must have format 'header..signature'",
300
- expected: "header..signature",
301
- received: `${jwsParts.length} parts, payload: '${jwsParts[1]}'`,
302
- },
303
- };
304
- }
305
- const [headerB64] = jwsParts;
306
- const header = JSON.parse(Buffer.from(headerB64, "base64url").toString());
307
- if (header.alg !== "EdDSA") {
308
- return {
309
- code: "XMCP_I_EBADPROOF",
310
- message: "Invalid JWS: unsupported algorithm",
311
- httpStatus: 403,
312
- details: {
313
- reason: "Only EdDSA algorithm is supported",
314
- expected: "EdDSA",
315
- received: header.alg,
316
- },
317
- };
318
- }
319
- if (header.kid !== proof.meta.kid) {
320
- return {
321
- code: "XMCP_I_EBADPROOF",
322
- message: "Invalid JWS: key ID mismatch",
323
- httpStatus: 403,
324
- details: {
325
- reason: "JWS header kid must match proof meta kid",
326
- expected: proof.meta.kid,
327
- received: header.kid,
328
- },
329
- };
330
- }
331
- // Mock signature verification passes for test DIDs
332
- return null;
333
- }
334
- // Parse JWS components - detached JWS has format: header..signature
335
- const jwsParts = proof.jws.split(".");
336
- if (jwsParts.length !== 3 || jwsParts[1] !== "") {
337
- return {
338
- code: "XMCP_I_EBADPROOF",
339
- message: "Invalid detached JWS format",
340
- httpStatus: 403,
341
- details: {
342
- reason: "Detached JWS must have format 'header..signature'",
343
- expected: "header..signature",
344
- received: `${jwsParts.length} parts, payload: '${jwsParts[1]}'`,
345
- },
346
- };
347
- }
348
- const [headerB64, , signatureB64] = jwsParts;
349
- // Parse and validate header
350
- const header = JSON.parse(Buffer.from(headerB64, "base64url").toString());
351
- if (header.alg !== "EdDSA") {
352
- return {
353
- code: "XMCP_I_EBADPROOF",
354
- message: "Invalid JWS: unsupported algorithm",
355
- httpStatus: 403,
356
- details: {
357
- reason: "Only EdDSA algorithm is supported",
358
- expected: "EdDSA",
359
- received: header.alg,
360
- },
361
- };
362
- }
363
- if (header.kid !== proof.meta.kid) {
364
- return {
365
- code: "XMCP_I_EBADPROOF",
366
- message: "Invalid JWS: key ID mismatch",
367
- httpStatus: 403,
368
- details: {
369
- reason: "JWS header kid must match proof meta kid",
370
- expected: proof.meta.kid,
371
- received: header.kid,
372
- },
373
- };
374
- }
375
- // Fetch public key from DID document with caching
376
- const publicKey = await this.fetchPublicKeyWithCache(proof.meta.did, proof.meta.kid);
377
- if (!publicKey) {
378
- return {
379
- code: "XMCP_I_EBADPROOF",
380
- message: "Unable to resolve public key",
381
- httpStatus: 403,
382
- details: {
383
- reason: "Could not fetch public key from DID document",
384
- remediation: "Ensure DID document is accessible and contains the key",
385
- },
386
- };
387
- }
388
- // Create canonical payload for detached signature verification
389
- // This MUST match the exact format used by the runtime
390
- const canonicalPayload = this.createCanonicalPayload(proof.meta);
391
- const payloadB64 = Buffer.from(canonicalPayload, "utf8").toString("base64url");
392
- // Reconstruct complete JWS for verification
393
- const completeJWS = `${headerB64}.${payloadB64}.${signatureB64}`;
394
- // Verify signature using JOSE
395
- const jwk = await importJWK(publicKey);
396
- await jwtVerify(completeJWS, jwk, {
397
- algorithms: ["EdDSA"],
398
- });
399
- return null;
400
- }
401
- catch (error) {
402
- return {
403
- code: "XMCP_I_EBADPROOF",
404
- message: "Signature verification failed",
405
- httpStatus: 403,
406
- details: {
407
- reason: error instanceof Error ? error.message : "Unknown error",
408
- remediation: "Check proof signature and public key",
409
- },
410
- };
411
- }
412
- }
413
- /**
414
- * Create canonical payload that matches runtime implementation
415
- * Uses JSON Canonicalization Scheme (JCS) RFC 8785 for deterministic ordering
416
- */
417
- createCanonicalPayload(meta) {
418
- // Create payload object with required fields
419
- const payload = {
420
- audience: meta.audience,
421
- nonce: meta.nonce,
422
- requestHash: meta.requestHash,
423
- responseHash: meta.responseHash,
424
- sessionId: meta.sessionId,
425
- ts: meta.ts,
426
- };
427
- // Add optional fields
428
- if (meta.scopeId) {
429
- payload.scopeId = meta.scopeId;
430
- }
431
- if (meta.delegationRef) {
432
- payload.delegationRef = meta.delegationRef;
433
- }
434
- // Use RFC 8785 compliant JCS canonicalization
435
- return canonicalize(payload);
436
- }
437
- /**
438
- * Verify delegation status via KTA with caching
439
- */
440
- async verifyDelegation(delegationRef, did, kid) {
441
- try {
442
- // Check cache first
443
- const cached = this.delegationCache.get(delegationRef);
444
- if (cached && cached.expiresAt > Date.now()) {
445
- return this.validateDelegationResponse(cached.response, did, kid);
446
- }
447
- if (this.config.allowMockData) {
448
- // Mock delegation for testing
449
- if (delegationRef.startsWith("mock:")) {
450
- const mockResponse = {
451
- active: delegationRef !== "mock:revoked",
452
- did,
453
- kid,
454
- scopes: ["*"],
455
- };
456
- return this.validateDelegationResponse(mockResponse, did, kid);
457
- }
458
- // For other delegation refs in test mode, assume they're valid
459
- const mockResponse = {
460
- active: true,
461
- did,
462
- kid,
463
- scopes: ["*"],
464
- };
465
- return this.validateDelegationResponse(mockResponse, did, kid);
466
- }
467
- const response = await fetch(`${this.config.ktaBaseUrl}/api/v1/delegations/${encodeURIComponent(delegationRef)}`, {
468
- method: "GET",
469
- headers: {
470
- "Content-Type": "application/json",
471
- "User-Agent": "XMCP-I-Verifier/1.0",
472
- },
473
- // Add timeout for production reliability
474
- signal: AbortSignal.timeout(5000),
475
- });
476
- if (!response || !response.ok) {
477
- if (response && response.status === 404) {
478
- return {
479
- code: "XMCP_I_EBADPROOF",
480
- message: "Delegation not found",
481
- httpStatus: 403,
482
- details: {
483
- reason: "Delegation reference not found in KTA",
484
- remediation: "Check delegation reference and ensure it exists",
485
- },
486
- };
487
- }
488
- throw new Error(`KTA API error: ${response.status}`);
489
- }
490
- const delegation = (await response.json());
491
- // Cache the response
492
- this.delegationCache.set(delegationRef, {
493
- response: delegation,
494
- expiresAt: Date.now() + this.config.delegationCacheTtl * 1000,
495
- });
496
- return this.validateDelegationResponse(delegation, did, kid);
497
- }
498
- catch (error) {
499
- // Treat delegation check failures as verification failures
500
- console.warn("Delegation verification failed:", error);
501
- return {
502
- code: "XMCP_I_EBADPROOF",
503
- message: "Delegation verification failed",
504
- httpStatus: 403,
505
- details: {
506
- reason: error instanceof Error ? error.message : "Unknown error",
507
- remediation: "Check KTA connectivity and delegation status",
508
- },
509
- };
510
- }
511
- }
512
- /**
513
- * Validate delegation response
514
- */
515
- validateDelegationResponse(delegation, expectedDid, expectedKeyId) {
516
- if (!delegation.active) {
517
- return {
518
- code: "XMCP_I_EBADPROOF",
519
- message: "Delegation revoked or inactive",
520
- httpStatus: 403,
521
- details: {
522
- reason: "Delegation is not active",
523
- remediation: "Renew delegation or use direct identity",
524
- },
525
- };
526
- }
527
- if (delegation.did !== expectedDid || delegation.kid !== expectedKeyId) {
528
- return {
529
- code: "XMCP_I_EBADPROOF",
530
- message: "Delegation identity mismatch",
531
- httpStatus: 403,
532
- details: {
533
- reason: "Delegation does not match proof identity",
534
- expected: `${expectedDid}#${expectedKeyId}`,
535
- received: `${delegation.did}#${delegation.kid}`,
536
- },
537
- };
538
- }
539
- if (delegation.expiresAt && delegation.expiresAt < Date.now() / 1000) {
540
- return {
541
- code: "XMCP_I_EBADPROOF",
542
- message: "Delegation expired",
543
- httpStatus: 403,
544
- details: {
545
- reason: "Delegation has expired",
546
- remediation: "Renew delegation",
547
- },
548
- };
549
- }
550
- return null;
551
- }
552
- /**
553
- * Fetch public key from DID document with caching
554
- */
555
- async fetchPublicKeyWithCache(did, kid) {
556
- const cacheKey = `${did}#${kid}`;
557
- const cached = this.didCache.get(cacheKey);
558
- if (cached && cached.expiresAt > Date.now()) {
559
- return this.extractPublicKey(cached.document, kid);
560
- }
561
- const didDoc = await this.fetchDIDDocument(did);
562
- if (!didDoc) {
563
- return null;
564
- }
565
- // Cache the DID document
566
- this.didCache.set(cacheKey, {
567
- document: didDoc,
568
- expiresAt: Date.now() + this.config.didCacheTtl * 1000,
569
- });
570
- return this.extractPublicKey(didDoc, kid);
571
- }
572
- /**
573
- * Fetch DID document from well-known endpoint
574
- */
575
- async fetchDIDDocument(did) {
576
- try {
577
- if (this.config.allowMockData && did.startsWith("did:test:")) {
578
- // Mock DID document for testing
579
- return {
580
- id: did,
581
- verificationMethod: [
582
- {
583
- id: `#key-test-1`,
584
- type: "Ed25519VerificationKey2020",
585
- controller: did,
586
- publicKeyJwk: {
587
- kty: "OKP",
588
- crv: "Ed25519",
589
- x: "mock-public-key-data",
590
- },
591
- },
592
- ],
593
- };
594
- }
595
- // Convert DID to well-known URL for did:web
596
- if (!did.startsWith("did:web:")) {
597
- throw new Error("Only did:web is supported");
598
- }
599
- const domain = did.replace("did:web:", "").replace(/:/g, "/");
600
- const didDocUrl = `https://${domain}/.well-known/did.json`;
601
- const response = await fetch(didDocUrl, {
602
- headers: {
603
- Accept: "application/did+json, application/json",
604
- "User-Agent": "XMCP-I-Verifier/1.0",
605
- },
606
- // Add timeout for production reliability
607
- signal: AbortSignal.timeout(5000),
608
- });
609
- if (!response.ok) {
610
- throw new Error(`Failed to fetch DID document: ${response.status}`);
611
- }
612
- return await response.json();
613
- }
614
- catch (error) {
615
- console.warn("Failed to fetch DID document:", error);
616
- return null;
617
- }
618
- }
619
- /**
620
- * Extract public key from DID document
621
- */
622
- extractPublicKey(didDoc, kid) {
623
- try {
624
- // Find verification method
625
- const verificationMethod = didDoc.verificationMethod?.find((vm) => vm.id === `#${kid}` || vm.id === `${didDoc.id}#${kid}`);
626
- if (!verificationMethod) {
627
- throw new Error(`Key ${kid} not found in DID document`);
628
- }
629
- // Convert to JWK format if needed
630
- if (verificationMethod.publicKeyJwk) {
631
- return verificationMethod.publicKeyJwk;
632
- }
633
- if (verificationMethod.publicKeyMultibase) {
634
- // Convert multibase to JWK (simplified for Ed25519)
635
- // This is a placeholder - real implementation would use proper multibase decoding
636
- return {
637
- kty: "OKP",
638
- crv: "Ed25519",
639
- x: verificationMethod.publicKeyMultibase.slice(1), // Remove multibase prefix
640
- };
641
- }
642
- throw new Error("Unsupported public key format");
643
- }
644
- catch (error) {
645
- console.warn("Failed to extract public key:", error);
646
- return null;
647
- }
648
- }
649
- /**
650
- * Generate trusted headers for successful verification
651
- */
652
- generateHeaders(meta) {
653
- const headers = {
654
- [AGENT_HEADERS.DID]: meta.did,
655
- [AGENT_HEADERS.KEY_ID]: meta.kid,
656
- [AGENT_HEADERS.SESSION]: meta.sessionId,
657
- [AGENT_HEADERS.CONFIDENCE]: "verified",
658
- [AGENT_HEADERS.VERIFIED_AT]: Math.floor(Date.now() / 1000).toString(),
659
- };
660
- if (meta.scopeId) {
661
- headers[AGENT_HEADERS.SCOPES] = meta.scopeId;
662
- }
663
- if (meta.delegationRef) {
664
- headers[AGENT_HEADERS.DELEGATION_REF] = meta.delegationRef;
665
- }
666
- // Add registry URL for traceability
667
- headers[AGENT_HEADERS.REGISTRY] = `${this.config.ktaBaseUrl}/agents/${encodeURIComponent(meta.did)}`;
668
- return headers;
669
- }
670
- /**
671
- * Generate agent context for MCP recipients
672
- */
673
- generateAgentContext(meta) {
674
- return {
675
- did: meta.did,
676
- kid: meta.kid,
677
- subject: meta.delegationRef ? meta.did : undefined,
678
- scopes: meta.scopeId ? meta.scopeId.split(",") : [],
679
- session: meta.sessionId,
680
- confidence: "verified",
681
- delegationRef: meta.delegationRef,
682
- registry: `${this.config.ktaBaseUrl}/agents/${encodeURIComponent(meta.did)}`,
683
- verifiedAt: Math.floor(Date.now() / 1000),
684
- };
685
- }
686
- /**
687
- * Create error result from structured error
688
- */
689
- createErrorResult(error) {
690
- return {
691
- success: false,
692
- error: {
693
- code: error.code,
694
- message: error.message,
695
- details: error.details,
696
- httpStatus: error.httpStatus ||
697
- ERROR_HTTP_STATUS[error.code] ||
698
- 500,
699
- },
700
- };
701
- }
702
- /**
703
- * Log verification attempt for security monitoring
704
- */
705
- logVerificationAttempt(context, success, reason, duration) {
706
- // In production, this would integrate with your logging system
707
- const logEntry = {
708
- timestamp: new Date().toISOString(),
709
- did: context.proof?.meta?.did || "unknown",
710
- audience: context.audience,
711
- success,
712
- reason,
713
- duration,
714
- sessionId: context.proof?.meta?.sessionId,
715
- };
716
- // For now, just console.log - in production, use structured logging
717
- console.log("XMCP-I Verification:", JSON.stringify(logEntry));
718
- }
719
- /**
720
- * Clean up expired cache entries
721
- */
722
- cleanupCache() {
723
- const now = Date.now();
724
- for (const [key, value] of this.didCache.entries()) {
725
- if (value.expiresAt <= now) {
726
- this.didCache.delete(key);
727
- }
728
- }
729
- for (const [key, value] of this.delegationCache.entries()) {
730
- if (value.expiresAt <= now) {
731
- this.delegationCache.delete(key);
732
- }
733
- }
734
- }
735
- }
736
- //# sourceMappingURL=core.js.map