@moltlaunch/sdk 2.3.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.js DELETED
@@ -1,948 +0,0 @@
1
- /**
2
- * MoltLaunch SDK
3
- * On-chain AI verification for AI agents
4
- *
5
- * @example
6
- * const { MoltLaunch } = require('@moltlaunch/sdk');
7
- * const ml = new MoltLaunch();
8
- * const result = await ml.verify({ agentId: 'my-agent', capabilities: ['trading'] });
9
- */
10
-
11
- const DEFAULT_BASE_URL = 'https://web-production-419d9.up.railway.app';
12
-
13
- class MoltLaunch {
14
- /**
15
- * Create a MoltLaunch client
16
- * @param {Object} options - Configuration options
17
- * @param {string} [options.baseUrl] - API base URL (default: production)
18
- * @param {string} [options.apiKey] - Optional API key for premium features
19
- */
20
- constructor(options = {}) {
21
- this.baseUrl = options.baseUrl || DEFAULT_BASE_URL;
22
- this.apiKey = options.apiKey || null;
23
- }
24
-
25
- /**
26
- * Get on-chain AI deployment info
27
- * @returns {Promise<OnChainInfo>}
28
- */
29
- async getOnChainInfo() {
30
- const res = await fetch(`${this.baseUrl}/api/onchain-ai`);
31
- if (!res.ok) throw new Error(`API error: ${res.status}`);
32
- return res.json();
33
- }
34
-
35
- /**
36
- * Verify an agent using on-chain AI
37
- * @param {VerifyOptions} options - Verification options
38
- * @returns {Promise<VerificationResult>}
39
- */
40
- /**
41
- * Generate a random nonce for replay protection
42
- * @returns {string}
43
- */
44
- generateNonce() {
45
- const bytes = new Uint8Array(16);
46
- if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
47
- crypto.getRandomValues(bytes);
48
- } else {
49
- // Node.js fallback
50
- const nodeCrypto = require('crypto');
51
- const buf = nodeCrypto.randomBytes(16);
52
- bytes.set(buf);
53
- }
54
- return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('');
55
- }
56
-
57
- /**
58
- * Verify an agent using on-chain AI (v3.0 with security features)
59
- * @param {VerifyOptions} options - Verification options
60
- * @returns {Promise<VerificationResult>}
61
- */
62
- async verify(options) {
63
- const {
64
- agentId,
65
- wallet,
66
- capabilities = [],
67
- codeUrl,
68
- documentation = false,
69
- testCoverage = 0,
70
- codeLines = 0,
71
- apiEndpoint,
72
- // v3.0 security options
73
- secureMode = false,
74
- nonce,
75
- timestamp,
76
- signature,
77
- validityDays = 30
78
- } = options;
79
-
80
- if (!agentId) throw new Error('agentId is required');
81
-
82
- // Build request body
83
- const body = {
84
- agentId,
85
- wallet,
86
- capabilities,
87
- codeUrl,
88
- documentation,
89
- testCoverage,
90
- codeLines,
91
- apiEndpoint
92
- };
93
-
94
- // Add v3.0 security fields if secure mode
95
- if (secureMode) {
96
- body.nonce = nonce || this.generateNonce();
97
- body.timestamp = timestamp || Math.floor(Date.now() / 1000);
98
- body.validityDays = validityDays;
99
- if (signature) body.signature = signature;
100
- }
101
-
102
- const res = await fetch(`${this.baseUrl}/api/verify/deep`, {
103
- method: 'POST',
104
- headers: {
105
- 'Content-Type': 'application/json',
106
- ...(this.apiKey && { 'Authorization': `Bearer ${this.apiKey}` })
107
- },
108
- body: JSON.stringify(body)
109
- });
110
-
111
- if (!res.ok) {
112
- const error = await res.json().catch(() => ({ error: res.statusText }));
113
- throw new Error(error.error || `API error: ${res.status}`);
114
- }
115
-
116
- const data = await res.json();
117
-
118
- return {
119
- agentId: data.agentId,
120
- verified: data.score >= 60,
121
- passed: data.passed,
122
- score: data.score,
123
- tier: data.scoreTier,
124
- features: data.features,
125
- onChainAI: data.onChainAI,
126
- attestation: data.attestation,
127
- security: data.security,
128
- raw: data
129
- };
130
- }
131
-
132
- /**
133
- * Verify with secure mode enabled (replay-protected)
134
- * @param {VerifyOptions} options - Verification options
135
- * @returns {Promise<VerificationResult>}
136
- */
137
- async verifySecure(options) {
138
- return this.verify({ ...options, secureMode: true });
139
- }
140
-
141
- /**
142
- * Check if an attestation is revoked
143
- * @param {string} attestationHash - Attestation hash
144
- * @returns {Promise<{revoked: boolean, checkedAt: string}>}
145
- */
146
- async checkRevocation(attestationHash) {
147
- const res = await fetch(`${this.baseUrl}/api/verify/revoked/${attestationHash}`);
148
- if (!res.ok) throw new Error(`API error: ${res.status}`);
149
- return res.json();
150
- }
151
-
152
- /**
153
- * Renew verification before expiry
154
- * @param {string} agentId - Agent ID
155
- * @param {object} options - Additional options
156
- * @returns {Promise<VerificationResult>}
157
- */
158
- async renew(agentId, options = {}) {
159
- const res = await fetch(`${this.baseUrl}/api/verify/renew/${agentId}`, {
160
- method: 'POST',
161
- headers: { 'Content-Type': 'application/json' },
162
- body: JSON.stringify(options)
163
- });
164
- if (!res.ok) {
165
- const error = await res.json().catch(() => ({ error: res.statusText }));
166
- throw new Error(error.error || `API error: ${res.status}`);
167
- }
168
- return res.json();
169
- }
170
-
171
- /**
172
- * Get verification status for an agent
173
- * @param {string} agentId - Agent ID
174
- * @returns {Promise<StatusResult>}
175
- */
176
- async getStatus(agentId) {
177
- const res = await fetch(`${this.baseUrl}/api/verify/status/${encodeURIComponent(agentId)}`);
178
- if (!res.ok) throw new Error(`API error: ${res.status}`);
179
- return res.json();
180
- }
181
-
182
- /**
183
- * Get verification status for multiple agents
184
- * @param {string[]} agentIds - Array of agent IDs
185
- * @returns {Promise<BatchStatusResult>}
186
- */
187
- async getStatusBatch(agentIds) {
188
- const res = await fetch(`${this.baseUrl}/api/verify/status/batch`, {
189
- method: 'POST',
190
- headers: { 'Content-Type': 'application/json' },
191
- body: JSON.stringify({ agentIds })
192
- });
193
- if (!res.ok) throw new Error(`API error: ${res.status}`);
194
- return res.json();
195
- }
196
-
197
- /**
198
- * Apply agent to a staking pool
199
- * @param {PoolApplyOptions} options - Pool application options
200
- * @returns {Promise<PoolApplyResult>}
201
- */
202
- async applyToPool(options) {
203
- const { agentId, wallet, topic, strategy } = options;
204
-
205
- const res = await fetch(`${this.baseUrl}/api/pool/apply`, {
206
- method: 'POST',
207
- headers: { 'Content-Type': 'application/json' },
208
- body: JSON.stringify({ agentId, wallet, topic, strategy })
209
- });
210
-
211
- if (!res.ok) {
212
- const error = await res.json().catch(() => ({ error: res.statusText }));
213
- throw new Error(error.error || `API error: ${res.status}`);
214
- }
215
-
216
- return res.json();
217
- }
218
-
219
- /**
220
- * Get pool information
221
- * @param {string} [topic] - Optional topic filter
222
- * @returns {Promise<PoolInfo>}
223
- */
224
- async getPools(topic) {
225
- const url = topic
226
- ? `${this.baseUrl}/api/pools/${encodeURIComponent(topic)}`
227
- : `${this.baseUrl}/api/pools`;
228
- const res = await fetch(url);
229
- if (!res.ok) throw new Error(`API error: ${res.status}`);
230
- return res.json();
231
- }
232
-
233
- /**
234
- * Get leaderboard
235
- * @returns {Promise<LeaderboardResult>}
236
- */
237
- async getLeaderboard() {
238
- const res = await fetch(`${this.baseUrl}/api/pools/leaderboard`);
239
- if (!res.ok) throw new Error(`API error: ${res.status}`);
240
- return res.json();
241
- }
242
-
243
- /**
244
- * Check API health
245
- * @returns {Promise<boolean>}
246
- */
247
- async isHealthy() {
248
- try {
249
- const res = await fetch(`${this.baseUrl}/api/health`);
250
- return res.ok;
251
- } catch {
252
- return false;
253
- }
254
- }
255
-
256
- // ==========================================
257
- // STARK PROOFS (v3.3 - Privacy-Preserving)
258
- // ==========================================
259
-
260
- /**
261
- * Generate a STARK threshold proof
262
- * Proves "score >= threshold" without revealing exact score
263
- * @param {string} agentId - Agent ID
264
- * @param {object} options - Proof options
265
- * @param {number} [options.threshold=60] - Minimum score to prove
266
- * @returns {Promise<STARKProof>}
267
- */
268
- async generateProof(agentId, options = {}) {
269
- const { threshold = 60 } = options;
270
-
271
- const res = await fetch(`${this.baseUrl}/api/stark/generate/${encodeURIComponent(agentId)}`, {
272
- method: 'POST',
273
- headers: { 'Content-Type': 'application/json' },
274
- body: JSON.stringify({ threshold })
275
- });
276
-
277
- if (!res.ok) {
278
- const error = await res.json().catch(() => ({ error: res.statusText }));
279
- throw new Error(error.error || `API error: ${res.status}`);
280
- }
281
-
282
- return res.json();
283
- }
284
-
285
- /**
286
- * Generate a consistency proof
287
- * Proves "maintained >= threshold for N periods" without revealing individual scores
288
- * @param {string} agentId - Agent ID
289
- * @param {object} options - Proof options
290
- * @param {number} [options.threshold=60] - Minimum score threshold
291
- * @param {number} [options.days=30] - Number of days to prove
292
- * @returns {Promise<ConsistencyProof>}
293
- */
294
- async generateConsistencyProof(agentId, options = {}) {
295
- const { threshold = 60, days = 30 } = options;
296
-
297
- const res = await fetch(`${this.baseUrl}/api/stark/consistency/${encodeURIComponent(agentId)}`, {
298
- method: 'POST',
299
- headers: { 'Content-Type': 'application/json' },
300
- body: JSON.stringify({ threshold, days })
301
- });
302
-
303
- if (!res.ok) {
304
- const error = await res.json().catch(() => ({ error: res.statusText }));
305
- throw new Error(error.error || `API error: ${res.status}`);
306
- }
307
-
308
- return res.json();
309
- }
310
-
311
- /**
312
- * Generate a streak proof
313
- * Proves "N+ consecutive periods at >= threshold"
314
- * @param {string} agentId - Agent ID
315
- * @param {object} options - Proof options
316
- * @param {number} [options.threshold=60] - Minimum score threshold
317
- * @param {number} [options.minStreak=7] - Minimum consecutive periods
318
- * @returns {Promise<StreakProof>}
319
- */
320
- async generateStreakProof(agentId, options = {}) {
321
- const { threshold = 60, minStreak = 7 } = options;
322
-
323
- const res = await fetch(`${this.baseUrl}/api/stark/streak/${encodeURIComponent(agentId)}`, {
324
- method: 'POST',
325
- headers: { 'Content-Type': 'application/json' },
326
- body: JSON.stringify({ threshold, minStreak })
327
- });
328
-
329
- if (!res.ok) {
330
- const error = await res.json().catch(() => ({ error: res.statusText }));
331
- throw new Error(error.error || `API error: ${res.status}`);
332
- }
333
-
334
- return res.json();
335
- }
336
-
337
- /**
338
- * Generate a stability proof
339
- * Proves "score variance <= threshold" without revealing actual variance
340
- * @param {string} agentId - Agent ID
341
- * @param {object} options - Proof options
342
- * @param {number} [options.maxVariance=100] - Maximum allowed variance
343
- * @returns {Promise<StabilityProof>}
344
- */
345
- async generateStabilityProof(agentId, options = {}) {
346
- const { maxVariance = 100 } = options;
347
-
348
- const res = await fetch(`${this.baseUrl}/api/stark/stability/${encodeURIComponent(agentId)}`, {
349
- method: 'POST',
350
- headers: { 'Content-Type': 'application/json' },
351
- body: JSON.stringify({ maxVariance })
352
- });
353
-
354
- if (!res.ok) {
355
- const error = await res.json().catch(() => ({ error: res.statusText }));
356
- throw new Error(error.error || `API error: ${res.status}`);
357
- }
358
-
359
- return res.json();
360
- }
361
-
362
- // ==========================================
363
- // EXECUTION TRACES (Behavioral Scoring)
364
- // ==========================================
365
-
366
- /**
367
- * Submit an execution trace for behavioral scoring
368
- * @param {string} agentId - Agent ID
369
- * @param {TraceData} data - Trace data
370
- * @returns {Promise<TraceResult>}
371
- */
372
- async submitTrace(agentId, data) {
373
- const res = await fetch(`${this.baseUrl}/api/traces`, {
374
- method: 'POST',
375
- headers: {
376
- 'Content-Type': 'application/json',
377
- ...(this.apiKey && { 'Authorization': `Bearer ${this.apiKey}` })
378
- },
379
- body: JSON.stringify({ agentId, ...data })
380
- });
381
-
382
- if (!res.ok) {
383
- const error = await res.json().catch(() => ({ error: res.statusText }));
384
- throw new Error(error.error || `API error: ${res.status}`);
385
- }
386
-
387
- return res.json();
388
- }
389
-
390
- /**
391
- * Get traces for an agent
392
- * @param {string} agentId - Agent ID
393
- * @param {object} [options] - Query options
394
- * @returns {Promise<TraceList>}
395
- */
396
- async getTraces(agentId, options = {}) {
397
- const { limit = 20 } = options;
398
- const res = await fetch(`${this.baseUrl}/api/traces/${encodeURIComponent(agentId)}?limit=${limit}`);
399
- if (!res.ok) throw new Error(`API error: ${res.status}`);
400
- return res.json();
401
- }
402
-
403
- /**
404
- * Get behavioral score from traces
405
- * @param {string} agentId - Agent ID
406
- * @returns {Promise<BehavioralScore>}
407
- */
408
- async getBehavioralScore(agentId) {
409
- const res = await fetch(`${this.baseUrl}/api/traces/${encodeURIComponent(agentId)}/score`);
410
- if (!res.ok) throw new Error(`API error: ${res.status}`);
411
- return res.json();
412
- }
413
-
414
- /**
415
- * Anchor a trace on-chain (requires SlotScribe integration)
416
- * @param {string} traceId - Trace ID
417
- * @returns {Promise<AnchorResult>}
418
- */
419
- async anchorTrace(traceId) {
420
- const res = await fetch(`${this.baseUrl}/api/traces/${encodeURIComponent(traceId)}/anchor`, {
421
- method: 'POST',
422
- headers: {
423
- 'Content-Type': 'application/json',
424
- ...(this.apiKey && { 'Authorization': `Bearer ${this.apiKey}` })
425
- }
426
- });
427
-
428
- if (!res.ok) {
429
- const error = await res.json().catch(() => ({ error: res.statusText }));
430
- throw new Error(error.error || `API error: ${res.status}`);
431
- }
432
-
433
- return res.json();
434
- }
435
-
436
- // ==========================================
437
- // HELPER METHODS
438
- // ==========================================
439
-
440
- /**
441
- * Quick check if an agent is verified
442
- * @param {string} agentId - Agent ID
443
- * @returns {Promise<boolean>}
444
- */
445
- async isVerified(agentId) {
446
- try {
447
- const status = await this.getStatus(agentId);
448
- return status.verified === true && status.score >= 60;
449
- } catch {
450
- return false;
451
- }
452
- }
453
-
454
- /**
455
- * Check if an agent has a specific capability at a minimum score
456
- * @param {string} agentId - Agent ID
457
- * @param {string} capability - Capability to check (e.g., "trading", "escrow")
458
- * @param {number} [minScore=60] - Minimum score required
459
- * @returns {Promise<boolean>}
460
- */
461
- async checkCapability(agentId, capability, minScore = 60) {
462
- try {
463
- const status = await this.getStatus(agentId);
464
- if (!status.verified || status.score < minScore) return false;
465
- if (!status.capabilities) return status.score >= minScore;
466
- return status.capabilities.includes(capability) && status.score >= minScore;
467
- } catch {
468
- return false;
469
- }
470
- }
471
-
472
- /**
473
- * Get proof generation cost estimate
474
- * @param {string} proofType - Type of proof (threshold, consistency, streak, stability)
475
- * @returns {Promise<CostEstimate>}
476
- */
477
- async getProofCost(proofType = 'threshold') {
478
- // Costs are near-zero for these lightweight proofs
479
- const costs = {
480
- threshold: { computeMs: 50, estimatedCost: '$0.001' },
481
- consistency: { computeMs: 120, estimatedCost: '$0.002' },
482
- streak: { computeMs: 100, estimatedCost: '$0.002' },
483
- stability: { computeMs: 80, estimatedCost: '$0.001' }
484
- };
485
- return costs[proofType] || costs.threshold;
486
- }
487
-
488
- // ==========================================
489
- // HARDWARE-ANCHORED IDENTITY (Anti-Sybil)
490
- // DePIN-Rooted Device Identity
491
- // ==========================================
492
-
493
- /**
494
- * Collect environment fingerprint for hardware-anchored identity
495
- * @returns {object} Raw fingerprint data
496
- * @private
497
- */
498
- _collectFingerprint() {
499
- const crypto = require('crypto');
500
- const os = require('os');
501
-
502
- const hardware = {
503
- platform: os.platform(),
504
- arch: os.arch(),
505
- cpus: os.cpus().length,
506
- cpuModel: os.cpus()[0]?.model || 'unknown',
507
- totalMemory: os.totalmem(),
508
- hostname: os.hostname(),
509
- };
510
-
511
- const runtime = {
512
- nodeVersion: process.version,
513
- pid: process.pid,
514
- execPath: process.execPath,
515
- cwd: process.cwd(),
516
- env: {
517
- USER: process.env.USER || process.env.USERNAME || 'unknown',
518
- HOME: process.env.HOME || process.env.USERPROFILE || 'unknown',
519
- SHELL: process.env.SHELL || 'unknown',
520
- }
521
- };
522
-
523
- // Try to get network interfaces for fingerprinting
524
- const nets = os.networkInterfaces();
525
- const networkFingerprint = Object.keys(nets).sort().map(name => {
526
- const iface = nets[name].find(n => !n.internal && n.family === 'IPv4');
527
- return iface ? `${name}:${iface.mac}` : null;
528
- }).filter(Boolean).join('|');
529
-
530
- return { hardware, runtime, networkFingerprint };
531
- }
532
-
533
- /**
534
- * Try to read TPM endorsement key hash for hardware-rooted identity
535
- * @returns {string|null} SHA-256 hash of TPM data, or null if unavailable
536
- * @private
537
- */
538
- _getTPMFingerprint() {
539
- const crypto = require('crypto');
540
- const fs = require('fs');
541
- const os = require('os');
542
-
543
- // TPM 2.0 paths (Linux)
544
- const tpmPaths = [
545
- '/sys/class/tpm/tpm0/device/description',
546
- '/sys/class/tpm/tpm0/tpm_version_major',
547
- '/sys/class/dmi/id/board_serial',
548
- '/sys/class/dmi/id/product_uuid',
549
- '/sys/class/dmi/id/chassis_serial',
550
- ];
551
-
552
- const tpmData = [];
553
- for (const p of tpmPaths) {
554
- try {
555
- const data = fs.readFileSync(p, 'utf-8').trim();
556
- if (data && data !== 'None' && data !== '') {
557
- tpmData.push(data);
558
- }
559
- } catch {}
560
- }
561
-
562
- // macOS: use IOPlatformUUID
563
- if (os.platform() === 'darwin') {
564
- try {
565
- const { execSync } = require('child_process');
566
- const uuid = execSync('ioreg -rd1 -c IOPlatformExpertDevice | grep IOPlatformUUID', { encoding: 'utf-8' });
567
- const match = uuid.match(/"([A-F0-9-]+)"/);
568
- if (match) tpmData.push(match[1]);
569
- } catch {}
570
- }
571
-
572
- if (tpmData.length === 0) return null;
573
-
574
- return crypto.createHash('sha256')
575
- .update(tpmData.join('|'))
576
- .digest('hex');
577
- }
578
-
579
- /**
580
- * Register a DePIN device attestation for hardware-rooted identity
581
- * Links agent identity to a physically verified DePIN device
582
- *
583
- * @param {object} options - DePIN registration options
584
- * @param {string} options.provider - DePIN provider name (e.g., 'io.net', 'akash', 'render')
585
- * @param {string} options.deviceId - Device ID from the DePIN provider
586
- * @param {string} [options.attestation] - Optional attestation data from the provider
587
- * @param {string} options.agentId - Agent ID to bind DePIN identity to
588
- * @returns {Promise<DePINRegistrationResult>}
589
- */
590
- async registerDePINDevice(options = {}) {
591
- const { provider, deviceId, attestation, agentId } = options;
592
-
593
- const supported = ['io.net', 'akash', 'render', 'helium', 'hivemapper', 'nosana'];
594
-
595
- if (!supported.includes(provider)) {
596
- throw new Error(`Unsupported DePIN provider. Supported: ${supported.join(', ')}`);
597
- }
598
-
599
- const res = await fetch(`${this.baseUrl}/api/identity/depin`, {
600
- method: 'POST',
601
- headers: { 'Content-Type': 'application/json' },
602
- body: JSON.stringify({
603
- agentId,
604
- depinProvider: provider,
605
- deviceId,
606
- attestation,
607
- timestamp: Date.now()
608
- })
609
- });
610
-
611
- if (!res.ok) throw new Error(`DePIN registration failed: ${res.status}`);
612
- return res.json();
613
- }
614
-
615
- /**
616
- * Get identity trust report for an agent
617
- * Shows trust ladder breakdown including DePIN and TPM attestation levels
618
- *
619
- * @param {string} agentId - Agent ID to get report for
620
- * @returns {Promise<IdentityReport>}
621
- */
622
- async getIdentityReport(agentId) {
623
- const res = await fetch(`${this.baseUrl}/api/identity/${encodeURIComponent(agentId)}/report`);
624
- if (!res.ok) throw new Error(`API error: ${res.status}`);
625
- return res.json();
626
- }
627
-
628
- /**
629
- * Generate a hardware-anchored identity hash
630
- * Combines hardware, runtime, code, and network fingerprints into a deterministic identity
631
- *
632
- * @param {object} options - Identity options
633
- * @param {boolean} [options.includeHardware=true] - Include hardware fingerprint (CPU, memory)
634
- * @param {boolean} [options.includeRuntime=true] - Include runtime fingerprint (Node version, OS)
635
- * @param {boolean} [options.includeCode=false] - Include code hash (hash of main module)
636
- * @param {string} [options.codeEntry] - Path to agent's main module for code hashing
637
- * @param {string} [options.agentId] - Agent ID to bind identity to
638
- * @param {boolean} [options.anchor=false] - Anchor identity on Solana
639
- * @returns {Promise<IdentityResult>}
640
- */
641
- async generateIdentity(options = {}) {
642
- const crypto = require('crypto');
643
- const {
644
- includeHardware = true,
645
- includeRuntime = true,
646
- includeCode = false,
647
- includeTPM = false,
648
- depinProvider,
649
- depinDeviceId,
650
- codeEntry,
651
- agentId,
652
- anchor = false
653
- } = options;
654
-
655
- const fingerprint = this._collectFingerprint();
656
- const components = [];
657
-
658
- if (includeHardware) {
659
- const hwHash = crypto.createHash('sha256')
660
- .update(JSON.stringify(fingerprint.hardware))
661
- .digest('hex');
662
- components.push(`hw:${hwHash}`);
663
- }
664
-
665
- if (includeRuntime) {
666
- const rtHash = crypto.createHash('sha256')
667
- .update(JSON.stringify(fingerprint.runtime))
668
- .digest('hex');
669
- components.push(`rt:${rtHash}`);
670
- }
671
-
672
- if (includeCode && codeEntry) {
673
- try {
674
- const fs = require('fs');
675
- const codeContent = fs.readFileSync(codeEntry, 'utf-8');
676
- const codeHash = crypto.createHash('sha256')
677
- .update(codeContent)
678
- .digest('hex');
679
- components.push(`code:${codeHash}`);
680
- } catch (e) {
681
- components.push(`code:unavailable`);
682
- }
683
- }
684
-
685
- if (fingerprint.networkFingerprint) {
686
- const netHash = crypto.createHash('sha256')
687
- .update(fingerprint.networkFingerprint)
688
- .digest('hex');
689
- components.push(`net:${netHash}`);
690
- }
691
-
692
- // TPM attestation (hardware-rooted identity - trust level 4)
693
- let tpmHash = null;
694
- if (includeTPM) {
695
- tpmHash = this._getTPMFingerprint();
696
- if (tpmHash) {
697
- components.push(`tpm:${tpmHash}`);
698
- }
699
- }
700
-
701
- // DePIN device attestation (highest trust level 5)
702
- if (depinProvider && depinDeviceId) {
703
- const depinHash = crypto.createHash('sha256')
704
- .update(`depin:${depinProvider}:${depinDeviceId}`)
705
- .digest('hex');
706
- components.push(`depin:${depinHash}`);
707
- }
708
-
709
- // Generate deterministic identity hash
710
- const identityHash = crypto.createHash('sha256')
711
- .update(components.join('|'))
712
- .digest('hex');
713
-
714
- const identity = {
715
- hash: identityHash,
716
- components: components.length,
717
- includesHardware: includeHardware,
718
- includesRuntime: includeRuntime,
719
- includesCode: includeCode && !!codeEntry,
720
- includesNetwork: !!fingerprint.networkFingerprint,
721
- includesTPM: includeTPM && !!tpmHash,
722
- tpmHash: tpmHash || null,
723
- depinProvider: depinProvider || null,
724
- depinDeviceId: depinDeviceId || null,
725
- generatedAt: new Date().toISOString(),
726
- expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString(), // 30 days
727
- agentId: agentId || null
728
- };
729
-
730
- // Register with MoltLaunch API
731
- if (agentId) {
732
- try {
733
- const res = await fetch(`${this.baseUrl}/api/identity/register`, {
734
- method: 'POST',
735
- headers: {
736
- 'Content-Type': 'application/json',
737
- ...(this.apiKey && { 'Authorization': `Bearer ${this.apiKey}` })
738
- },
739
- body: JSON.stringify({
740
- agentId,
741
- identityHash,
742
- components: components.length,
743
- includesHardware: includeHardware,
744
- includesCode: includeCode
745
- })
746
- });
747
-
748
- if (res.ok) {
749
- const registration = await res.json();
750
- identity.registered = true;
751
- identity.registrationId = registration.registrationId;
752
- } else {
753
- identity.registered = false;
754
- identity.registrationError = `API ${res.status}`;
755
- }
756
- } catch (e) {
757
- identity.registered = false;
758
- identity.registrationError = e.message;
759
- }
760
- }
761
-
762
- // Anchor on Solana if requested
763
- if (anchor && agentId) {
764
- try {
765
- const res = await fetch(`${this.baseUrl}/api/anchor/verification`, {
766
- method: 'POST',
767
- headers: { 'Content-Type': 'application/json' },
768
- body: JSON.stringify({
769
- agentId,
770
- attestationHash: identityHash
771
- })
772
- });
773
-
774
- if (res.ok) {
775
- const anchorResult = await res.json();
776
- identity.anchored = true;
777
- identity.anchorSignature = anchorResult.signature;
778
- identity.anchorExplorer = anchorResult.explorer;
779
- } else {
780
- identity.anchored = false;
781
- }
782
- } catch (e) {
783
- identity.anchored = false;
784
- identity.anchorError = e.message;
785
- }
786
- }
787
-
788
- return identity;
789
- }
790
-
791
- /**
792
- * Verify an agent's identity against their registered fingerprint
793
- * @param {string} agentId - Agent ID to verify
794
- * @returns {Promise<IdentityVerification>}
795
- */
796
- async verifyIdentity(agentId) {
797
- // Generate current fingerprint
798
- const currentIdentity = await this.generateIdentity({ agentId });
799
-
800
- // Check against registered identity
801
- try {
802
- const res = await fetch(`${this.baseUrl}/api/identity/${encodeURIComponent(agentId)}`);
803
- if (!res.ok) {
804
- return {
805
- valid: false,
806
- agentId,
807
- reason: 'No registered identity found',
808
- currentHash: currentIdentity.hash
809
- };
810
- }
811
-
812
- const registered = await res.json();
813
- const matches = registered.identityHash === currentIdentity.hash;
814
-
815
- return {
816
- valid: matches,
817
- agentId,
818
- currentHash: currentIdentity.hash,
819
- registeredHash: registered.identityHash,
820
- match: matches,
821
- registeredAt: registered.registeredAt,
822
- reason: matches ? 'Identity confirmed' : 'Identity mismatch — different hardware or code'
823
- };
824
- } catch (e) {
825
- return {
826
- valid: false,
827
- agentId,
828
- reason: e.message,
829
- currentHash: currentIdentity.hash
830
- };
831
- }
832
- }
833
-
834
- /**
835
- * Check if two agents have the same hardware fingerprint (Sybil detection)
836
- * @param {string} agentId1 - First agent
837
- * @param {string} agentId2 - Second agent
838
- * @returns {Promise<SybilCheck>}
839
- */
840
- async checkSybil(agentId1, agentId2) {
841
- try {
842
- const [id1, id2] = await Promise.all([
843
- fetch(`${this.baseUrl}/api/identity/${encodeURIComponent(agentId1)}`).then(r => r.json()),
844
- fetch(`${this.baseUrl}/api/identity/${encodeURIComponent(agentId2)}`).then(r => r.json())
845
- ]);
846
-
847
- const sameIdentity = id1.identityHash === id2.identityHash;
848
-
849
- return {
850
- agentId1,
851
- agentId2,
852
- sameIdentity,
853
- sybilRisk: sameIdentity ? 'HIGH' : 'LOW',
854
- reason: sameIdentity
855
- ? 'Same hardware fingerprint — likely same operator'
856
- : 'Different hardware fingerprints — likely different operators',
857
- recommendation: sameIdentity
858
- ? 'Do not seat at same table'
859
- : 'Safe to interact'
860
- };
861
- } catch (e) {
862
- return {
863
- agentId1,
864
- agentId2,
865
- sameIdentity: null,
866
- sybilRisk: 'UNKNOWN',
867
- reason: `Could not compare: ${e.message}`
868
- };
869
- }
870
- }
871
-
872
- /**
873
- * Check a list of agents for Sybil clusters (table seating check)
874
- * @param {string[]} agentIds - List of agent IDs to check
875
- * @returns {Promise<SybilTableCheck>}
876
- */
877
- async checkTableSybils(agentIds) {
878
- // Fetch all identities
879
- const identities = {};
880
- for (const id of agentIds) {
881
- try {
882
- const res = await fetch(`${this.baseUrl}/api/identity/${encodeURIComponent(id)}`);
883
- if (res.ok) {
884
- const data = await res.json();
885
- identities[id] = data.identityHash;
886
- }
887
- } catch {
888
- // Skip agents without identity
889
- }
890
- }
891
-
892
- // Find clusters (same identity hash)
893
- const hashToAgents = {};
894
- for (const [agentId, hash] of Object.entries(identities)) {
895
- if (!hashToAgents[hash]) hashToAgents[hash] = [];
896
- hashToAgents[hash].push(agentId);
897
- }
898
-
899
- const clusters = Object.values(hashToAgents).filter(group => group.length > 1);
900
- const flagged = clusters.flat();
901
-
902
- return {
903
- totalAgents: agentIds.length,
904
- identifiedAgents: Object.keys(identities).length,
905
- unidentifiedAgents: agentIds.filter(id => !identities[id]),
906
- sybilClusters: clusters,
907
- flaggedAgents: flagged,
908
- safe: clusters.length === 0,
909
- recommendation: clusters.length === 0
910
- ? 'No Sybil clusters detected — safe to proceed'
911
- : `${clusters.length} Sybil cluster(s) detected — ${flagged.length} agents share hardware`
912
- };
913
- }
914
- }
915
-
916
- // Scoring helpers
917
- const SCORE_TIERS = {
918
- excellent: { min: 80, max: 100, label: 'Production Ready' },
919
- good: { min: 60, max: 79, label: 'Verified' },
920
- needs_work: { min: 40, max: 59, label: 'Needs Improvement' },
921
- poor: { min: 0, max: 39, label: 'Not Ready' }
922
- };
923
-
924
- const getTier = (score) => {
925
- if (score >= 80) return 'excellent';
926
- if (score >= 60) return 'good';
927
- if (score >= 40) return 'needs_work';
928
- return 'poor';
929
- };
930
-
931
- const isVerified = (score) => score >= 60;
932
-
933
- // On-chain AI deployment info
934
- const DEPLOYMENT = {
935
- network: 'solana-devnet',
936
- vm: 'FHcy35f4NGZK9b6j5TGMYstfB6PXEtmNbMLvjfR1y2Li',
937
- weights: 'GnSxMWbZEa538vJ9Pf3veDrKP1LkzPiaaVmC4mRnM91N',
938
- program: 'FRsToriMLgDc1Ud53ngzHUZvCRoazCaGeGUuzkwoha7m'
939
- };
940
-
941
- module.exports = {
942
- MoltLaunch,
943
- SCORE_TIERS,
944
- DEPLOYMENT,
945
- getTier,
946
- isVerified,
947
- DEFAULT_BASE_URL
948
- };