anon-identity
Version:
Decentralized identity framework with DIDs, Verifiable Credentials, and privacy-preserving selective disclosure
293 lines • 10.3 kB
JavaScript
;
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