UNPKG

anon-identity

Version:

Decentralized identity framework with DIDs, Verifiable Credentials, and privacy-preserving selective disclosure

293 lines 10.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.IPFSActivityStorage = void 0; const kubo_rpc_client_1 = require("kubo-rpc-client"); const activity_encryption_1 = require("./activity-encryption"); class IPFSActivityStorage { constructor(config = {}) { this.manifests = new Map(); // Default to local IPFS node, can be configured for Infura, Pinata, etc. this.ipfs = (0, kubo_rpc_client_1.create)({ url: config.url || 'http://localhost:5001' }); this.encryption = new activity_encryption_1.ActivityEncryption(); this.encryptionKey = config.encryptionKey; } /** * Store a single activity to IPFS */ async storeActivity(activity) { try { let dataToStore; let encrypted = false; // Add integrity hash const activityWithHash = { ...activity, checksum: activity_encryption_1.ActivityEncryption.createActivityHash(activity) }; if (this.encryptionKey) { // Encrypt before storing const encryptedData = await this.encryption.encryptActivity(activityWithHash, this.encryptionKey); dataToStore = encryptedData; encrypted = true; } else { dataToStore = activityWithHash; } // Store to IPFS const { cid } = await this.ipfs.add(JSON.stringify(dataToStore), { pin: true }); const ipfsHash = cid.toString(); // Update activity with IPFS hash activity.ipfsHash = ipfsHash; // Update manifest await this.updateManifest(activity.agentDID, activity.parentDID, ipfsHash); return { ipfsHash, encrypted, timestamp: new Date(), checksum: activityWithHash.checksum }; } catch (error) { throw new Error(`Failed to store activity: ${error}`); } } /** * Store a batch of activities */ async storeActivityBatch(batch) { try { let dataToStore; let encrypted = false; // Add merkle root for integrity const batchWithProof = { ...batch, merkleRoot: activity_encryption_1.ActivityEncryption.createBatchMerkleRoot(batch.activities) }; if (this.encryptionKey) { // Encrypt before storing const encryptedData = await this.encryption.encryptBatch(batchWithProof, this.encryptionKey); dataToStore = encryptedData; encrypted = true; } else { dataToStore = batchWithProof; } // Store to IPFS const { cid } = await this.ipfs.add(JSON.stringify(dataToStore), { pin: true }); const ipfsHash = cid.toString(); // Update batch with IPFS hash batch.batchHash = ipfsHash; // Update all activities in batch with reference batch.activities.forEach(activity => { activity.ipfsHash = ipfsHash; }); // Update manifest await this.updateManifest(batch.agentDID, batch.parentDID, ipfsHash); return { ipfsHash, encrypted, timestamp: new Date(), checksum: batchWithProof.merkleRoot }; } catch (error) { throw new Error(`Failed to store activity batch: ${error}`); } } /** * Retrieve an activity from IPFS */ async retrieveActivity(ipfsHash) { try { const chunks = []; for await (const chunk of this.ipfs.cat(ipfsHash)) { chunks.push(chunk); } const data = Buffer.concat(chunks).toString('utf8'); const parsed = JSON.parse(data); // Check if data is encrypted if (parsed.algorithm && parsed.data && parsed.iv && parsed.tag) { if (!this.encryptionKey) { throw new Error('Data is encrypted but no encryption key provided'); } const decrypted = await this.encryption.decryptActivity(parsed, this.encryptionKey); // Verify integrity const expectedChecksum = activity_encryption_1.ActivityEncryption.createActivityHash(decrypted); if (decrypted.checksum && decrypted.checksum !== expectedChecksum) { throw new Error('Activity integrity check failed'); } return decrypted; } // Verify integrity for unencrypted data if (parsed.checksum) { const expectedChecksum = activity_encryption_1.ActivityEncryption.createActivityHash(parsed); if (parsed.checksum !== expectedChecksum) { throw new Error('Activity integrity check failed'); } } return parsed; } catch (error) { throw new Error(`Failed to retrieve activity: ${error}`); } } /** * Retrieve a batch of activities */ async retrieveActivityBatch(ipfsHash) { try { const chunks = []; for await (const chunk of this.ipfs.cat(ipfsHash)) { chunks.push(chunk); } const data = Buffer.concat(chunks).toString('utf8'); const parsed = JSON.parse(data); // Check if data is encrypted if (parsed.algorithm && parsed.data && parsed.iv && parsed.tag) { if (!this.encryptionKey) { throw new Error('Data is encrypted but no encryption key provided'); } const decrypted = await this.encryption.decryptBatch(parsed, this.encryptionKey); // Verify merkle root if (decrypted.merkleRoot) { const expectedRoot = activity_encryption_1.ActivityEncryption.createBatchMerkleRoot(decrypted.activities); if (decrypted.merkleRoot !== expectedRoot) { throw new Error('Batch integrity check failed'); } } return decrypted; } // Verify merkle root for unencrypted data if (parsed.merkleRoot) { const expectedRoot = activity_encryption_1.ActivityEncryption.createBatchMerkleRoot(parsed.activities); if (parsed.merkleRoot !== expectedRoot) { throw new Error('Batch integrity check failed'); } } return parsed; } catch (error) { throw new Error(`Failed to retrieve activity batch: ${error}`); } } /** * Pin an activity to ensure it stays available */ async pinActivity(ipfsHash) { try { await this.ipfs.pin.add(ipfsHash); } catch (error) { throw new Error(`Failed to pin activity: ${error}`); } } /** * Unpin an activity (for cleanup/archival) */ async unpinActivity(ipfsHash) { try { await this.ipfs.pin.rm(ipfsHash); } catch (error) { throw new Error(`Failed to unpin activity: ${error}`); } } /** * Get all pinned activities */ async getPinnedActivities() { try { const pins = []; for await (const pin of this.ipfs.pin.ls()) { pins.push(pin.cid.toString()); } return pins; } catch (error) { throw new Error(`Failed to list pinned activities: ${error}`); } } /** * Update the manifest for an agent */ async updateManifest(agentDID, parentDID, ipfsHash) { const manifestKey = `${parentDID}:${agentDID}`; let manifest = this.manifests.get(manifestKey); if (!manifest) { manifest = { agentDID, parentDID, created: new Date(), lastUpdated: new Date(), totalActivities: 0, ipfsHashes: [], encrypted: !!this.encryptionKey }; } manifest.ipfsHashes.push(ipfsHash); manifest.totalActivities++; manifest.lastUpdated = new Date(); this.manifests.set(manifestKey, manifest); // Store updated manifest to IPFS await this.storeManifest(manifest); } /** * Store manifest to IPFS */ async storeManifest(manifest) { try { const { cid } = await this.ipfs.add(JSON.stringify(manifest), { pin: true }); return cid.toString(); } catch (error) { throw new Error(`Failed to store manifest: ${error}`); } } /** * Get manifest for an agent */ async getManifest(agentDID, parentDID) { const manifestKey = `${parentDID}:${agentDID}`; return this.manifests.get(manifestKey) || null; } /** * Get storage statistics */ async getStorageStats() { try { const pins = await this.getPinnedActivities(); let totalSize = 0; // Note: Getting actual size requires additional IPFS API calls // This is a simplified version return { totalPinned: pins.length, totalSize, manifests: this.manifests.size }; } catch (error) { throw new Error(`Failed to get storage stats: ${error}`); } } /** * Check if IPFS node is connected */ async isConnected() { try { const id = await this.ipfs.id(); return !!id; } catch { return false; } } /** * Set or update encryption key */ setEncryptionKey(key) { this.encryptionKey = key; } } exports.IPFSActivityStorage = IPFSActivityStorage; //# sourceMappingURL=ipfs-activity-storage.js.map