UNPKG

nucypher-experimental-taco-storage

Version:

TypeScript SDK for encrypted data storage with TACo (Threshold Access Control), supporting multiple storage providers including IPFS and SQLite

235 lines 8.09 kB
"use strict"; /** * Kubo IPFS storage adapter implementation using kubo-rpc-client */ Object.defineProperty(exports, "__esModule", { value: true }); exports.KuboAdapter = void 0; const base_1 = require("./base"); const types_1 = require("../../types"); const kubo_rpc_client_1 = require("kubo-rpc-client"); /** * Kubo IPFS storage adapter for connecting to external Kubo IPFS nodes via RPC */ class KuboAdapter extends base_1.BaseIPFSAdapter { constructor(config = {}) { super(config); this.client = null; const kuboConfig = config; this.shouldPin = kuboConfig.pin ?? true; this.timeout = kuboConfig.timeout ?? 30000; // 30 second default timeout this.url = kuboConfig.url || 'http://localhost:5001'; // Client will be created in initialize() using static import } /** * Initialize the Kubo adapter by testing connectivity and validating configuration */ async initialize() { try { // Create Kubo RPC client using static import this.client = (0, kubo_rpc_client_1.create)({ url: this.url, timeout: this.timeout, }); // Test IPFS connectivity by getting node ID and version info const [id, version] = await Promise.all([ this.client.id(), this.client.version(), ]); // Validate that we can communicate with the IPFS node if (!id || !version) { throw new Error('Invalid response from IPFS node'); } } catch (error) { throw new types_1.TacoStorageError(types_1.TacoStorageErrorType.ADAPTER_ERROR, 'Failed to initialize Kubo IPFS adapter - check that IPFS node is running and accessible', error); } } generateReference(id) { return this.formatReference(id); } /** * Ensure client is initialized before use */ ensureClient() { if (!this.client) { throw new types_1.TacoStorageError(types_1.TacoStorageErrorType.ADAPTER_ERROR, 'Kubo IPFS adapter not initialized. Call initialize() first.'); } return this.client; } // Implementation of abstract methods from BaseIPFSAdapter async addContent(data) { const client = this.ensureClient(); const result = await client.add(data, { pin: this.shouldPin, cidVersion: 1, }); return result.cid.toString(); } async getContent(hash) { const client = this.ensureClient(); const stream = client.cat(hash, { timeout: this.timeout }); const chunks = []; for await (const chunk of stream) { chunks.push(chunk); } return Buffer.concat(chunks); } async pinContent(hash) { if (this.shouldPin) { const client = this.ensureClient(); await client.pin.add(hash); } } async unpinContent(hash) { if (this.shouldPin) { const client = this.ensureClient(); await client.pin.rm(hash); } } async contentExists(hash) { try { const client = this.ensureClient(); await client.files.stat(`/ipfs/${hash}`, { timeout: 5000 }); return true; } catch { return false; } } async getIPFSHealth() { try { const client = this.ensureClient(); const nodeInfo = await client.id(); return { connected: true, nodeId: nodeInfo.id.toString(), }; } catch { return { connected: false }; } } /** * Store encrypted data and metadata on IPFS */ async store(encryptedData, metadata) { this.validateData(encryptedData); try { // Create a structured object containing both data and metadata const dataPackage = { data: Array.from(encryptedData), // Convert Uint8Array to regular array for JSON metadata: { ...metadata, createdAt: metadata.createdAt.toISOString(), encryptionMetadata: { ...metadata.encryptionMetadata, }, }, }; // Add to IPFS const ipfsHash = await this.addContent(new TextEncoder().encode(JSON.stringify(dataPackage))); return { id: metadata.id, reference: this.generateReference(ipfsHash), metadata: this.createIPFSMetadata(ipfsHash, metadata), }; } catch (error) { throw new types_1.TacoStorageError(types_1.TacoStorageErrorType.STORAGE_ERROR, `Failed to store data on IPFS: ${error.message}`, error); } } /** * Retrieve encrypted data and metadata from IPFS */ async retrieve(id) { this.validateId(id); try { const ipfsHash = this.validateAndParseReference(id); // Retrieve from IPFS const content = await this.getContent(ipfsHash); const dataPackage = JSON.parse(new TextDecoder().decode(content)); // Reconstruct the original data and metadata const encryptedData = new Uint8Array(dataPackage.data); const metadata = { ...dataPackage.metadata, createdAt: new Date(dataPackage.metadata.createdAt), encryptionMetadata: { ...dataPackage.metadata.encryptionMetadata, encryptedKey: new Uint8Array(dataPackage.metadata.encryptionMetadata.encryptedKey), capsule: new Uint8Array(dataPackage.metadata.encryptionMetadata.capsule), }, }; return { encryptedData, metadata }; } catch (error) { if (error.code === 'ERR_NOT_FOUND') { throw new types_1.TacoStorageError(types_1.TacoStorageErrorType.NOT_FOUND, `Data not found for ID: ${id}`, error); } throw new types_1.TacoStorageError(types_1.TacoStorageErrorType.RETRIEVAL_ERROR, `Failed to retrieve data from IPFS: ${error.message}`, error); } } /** * Delete data from IPFS (unpin if pinned) */ async delete(id) { this.validateId(id); try { const ipfsHash = this.parseReference(id); await this.unpinContent(ipfsHash); return true; } catch (error) { // IPFS doesn't really "delete" content, just unpins it // Return true even if unpin fails, as the content might not have been pinned return true; } } /** * Check if data exists on IPFS */ async exists(id) { this.validateId(id); try { const ipfsHash = this.validateAndParseReference(id); return await this.contentExists(ipfsHash); } catch { return false; } } /** * Get IPFS node health status */ async getHealth() { try { const client = this.ensureClient(); const nodeId = await client.id(); const version = await client.version(); return { healthy: true, details: { nodeId: nodeId.id, version: version.version, addresses: nodeId.addresses, }, }; } catch (error) { return { healthy: false, details: { error: error.message, }, }; } } /** * Clean up IPFS client resources */ async cleanup() { // IPFS HTTP client doesn't require explicit cleanup // This method is here for interface compliance } } exports.KuboAdapter = KuboAdapter; //# sourceMappingURL=kubo.js.map