nucypher-experimental-taco-storage
Version:
TypeScript SDK for encrypted data storage with TACo (Threshold Access Control), supporting multiple storage providers including IPFS and SQLite
264 lines • 10.5 kB
JavaScript
;
/**
* Main TACo Storage SDK class
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.TacoStorage = void 0;
const uuid_1 = require("uuid");
const taco_1 = require("@nucypher/taco");
const encryption_1 = require("./encryption");
const types_1 = require("../types");
/**
* Main TACo Storage SDK class providing encrypted storage and retrieval
*/
class TacoStorage {
constructor(adapter, tacoConfig, provider) {
this.adapter = adapter;
this.encryptionService = new encryption_1.TacoEncryptionService(tacoConfig);
this.provider = provider;
}
/**
* Initialize the TacoStorage instance and its components
* @returns Promise that resolves when initialization is complete
*/
async initialize() {
// Initialize the storage adapter
await this.adapter.initialize();
// Initialize the encryption service
await this.encryptionService.initialize();
}
/**
* Store data with encryption and access control
* @param data - Data to store
* @param signer - Ethereum signer for encryption
* @param options - Storage options
* @returns Promise resolving to storage result
*/
async store(data, signer, options = {}) {
if (!data || data.length === 0) {
throw new types_1.TacoStorageError(types_1.TacoStorageErrorType.INVALID_CONFIG, 'Data cannot be empty');
}
try {
const id = options.id || (0, uuid_1.v4)();
const contentType = options.contentType || 'application/octet-stream';
const now = new Date();
// Create access conditions
let accessConditions = options.condition;
if (!accessConditions) {
const expiresAt = options.expiresAt || new Date(now.getTime() + 24 * 60 * 60 * 1000); // 24 hours from now
accessConditions =
this.encryptionService.createTimeCondition(expiresAt);
}
// Encrypt the data
const encryptionResult = await this.encryptionService.encrypt(data, accessConditions, this.provider, signer);
// Serialize the ThresholdMessageKit for storage
const serializedMessageKit = encryptionResult.messageKit.toBytes();
// Create metadata
const metadata = {
id,
contentType,
size: data.length,
createdAt: now,
...(options.metadata && { metadata: options.metadata }),
encryptionMetadata: {
messageKit: Array.from(serializedMessageKit),
conditions: encryptionResult.conditions,
},
};
// Store encrypted data using the adapter
const result = await this.adapter.store(serializedMessageKit, metadata);
return result;
}
catch (error) {
if (error instanceof types_1.TacoStorageError) {
throw error;
}
throw new types_1.TacoStorageError(types_1.TacoStorageErrorType.STORAGE_ERROR, `Failed to store data: ${error.message}`, error);
}
}
/**
* Retrieve and decrypt data
* @param id - Unique identifier for the data
* @param signer - Ethereum signer for decryption
* @returns Promise resolving to decrypted data and metadata
*/
async retrieve(id, signer) {
if (!id || typeof id !== 'string') {
throw new types_1.TacoStorageError(types_1.TacoStorageErrorType.INVALID_CONFIG, 'ID must be a non-empty string');
}
try {
// Retrieve encrypted data and metadata from adapter
const { encryptedData, metadata } = await this.adapter.retrieve(id);
// Reconstruct ThresholdMessageKit from stored data
const messageKitBytes = new Uint8Array(metadata.encryptionMetadata.messageKit);
const messageKit = taco_1.ThresholdMessageKit.fromBytes(messageKitBytes);
// Decrypt the data
const decryptedData = await this.encryptionService.decrypt(messageKit, this.provider);
return {
data: decryptedData,
metadata,
};
}
catch (error) {
if (error instanceof types_1.TacoStorageError) {
throw error;
}
throw new types_1.TacoStorageError(types_1.TacoStorageErrorType.RETRIEVAL_ERROR, `Failed to retrieve data: ${error.message}`, error);
}
}
/**
* Delete stored data
* @param id - Unique identifier for the data
* @returns Promise resolving to success status
*/
async delete(id) {
if (!id || typeof id !== 'string') {
throw new types_1.TacoStorageError(types_1.TacoStorageErrorType.INVALID_CONFIG, 'ID must be a non-empty string');
}
try {
return await this.adapter.delete(id);
}
catch (error) {
throw new types_1.TacoStorageError(types_1.TacoStorageErrorType.STORAGE_ERROR, `Failed to delete data: ${error.message}`, error);
}
}
/**
* Check if data exists
* @param id - Unique identifier for the data
* @returns Promise resolving to existence status
*/
async exists(id) {
if (!id || typeof id !== 'string') {
throw new types_1.TacoStorageError(types_1.TacoStorageErrorType.INVALID_CONFIG, 'ID must be a non-empty string');
}
try {
return await this.adapter.exists(id);
}
catch (error) {
throw new types_1.TacoStorageError(types_1.TacoStorageErrorType.STORAGE_ERROR, `Failed to check existence: ${error.message}`, error);
}
}
/**
* Get metadata for stored data without decrypting
* @param id - Unique identifier for the data
* @returns Promise resolving to storage metadata
*/
async getMetadata(id) {
if (!id || typeof id !== 'string') {
throw new types_1.TacoStorageError(types_1.TacoStorageErrorType.INVALID_CONFIG, 'ID must be a non-empty string');
}
try {
const { metadata } = await this.adapter.retrieve(id);
return metadata;
}
catch (error) {
if (error instanceof types_1.TacoStorageError) {
throw error;
}
throw new types_1.TacoStorageError(types_1.TacoStorageErrorType.RETRIEVAL_ERROR, `Failed to get metadata: ${error.message}`, error);
}
}
/**
* List stored data IDs (if supported by adapter)
* @param limit - Maximum number of IDs to return
* @param offset - Number of IDs to skip
* @returns Promise resolving to array of IDs
*/
async list(limit, offset) {
if (!this.adapter.list) {
throw new types_1.TacoStorageError(types_1.TacoStorageErrorType.ADAPTER_ERROR, 'List operation not supported by this adapter');
}
try {
return await this.adapter.list(limit, offset);
}
catch (error) {
throw new types_1.TacoStorageError(types_1.TacoStorageErrorType.RETRIEVAL_ERROR, `Failed to list data: ${error.message}`, error);
}
}
/**
* Get health status of the storage system
* @returns Promise resolving to health information
*/
async getHealth() {
try {
return await this.adapter.getHealth();
}
catch (error) {
return {
healthy: false,
details: {
error: error.message,
},
};
}
}
/**
* Clean up resources
*/
async cleanup() {
try {
await this.adapter.cleanup();
}
catch (error) {
throw new types_1.TacoStorageError(types_1.TacoStorageErrorType.ADAPTER_ERROR, `Failed to cleanup: ${error.message}`, error);
}
}
/**
* Create a new TacoStorage instance with IPFS adapter
* @param config - TACo configuration
* @param provider - Ethereum provider
* @param ipfsConfig - IPFS adapter configuration
* @returns Promise resolving to initialized TacoStorage instance
*/
static async createWithIPFS(config, provider, ipfsConfig) {
const { IPFSAdapter } = require('../adapters/ipfs/index');
const adapter = new IPFSAdapter(ipfsConfig);
const storage = new TacoStorage(adapter, config, provider);
await storage.initialize();
return storage;
}
/**
* Create a new TacoStorage instance with Kubo IPFS adapter
* @param config - TACo configuration
* @param provider - Ethereum provider
* @param kuboConfig - Kubo adapter configuration
* @returns Promise resolving to initialized TacoStorage instance
*/
static async createWithKubo(config, provider, kuboConfig) {
const { KuboAdapter } = require('../adapters/ipfs/index');
const adapter = new KuboAdapter(kuboConfig);
const storage = new TacoStorage(adapter, config, provider);
await storage.initialize();
return storage;
}
/**
* Create a new TacoStorage instance with Helia IPFS adapter
* @param config - TACo configuration
* @param provider - Ethereum provider
* @param heliaConfig - Helia adapter configuration
* @returns Promise resolving to initialized TacoStorage instance
*/
static async createWithHelia(config, provider, heliaConfig) {
const { HeliaAdapter } = require('../adapters/ipfs/index');
const adapter = new HeliaAdapter(heliaConfig);
const storage = new TacoStorage(adapter, config, provider);
await storage.initialize();
return storage;
}
/**
* Create a new TacoStorage instance with SQLite adapter
* @param config - TACo configuration
* @param provider - Ethereum provider
* @param sqliteConfig - SQLite adapter configuration
* @returns Promise resolving to initialized TacoStorage instance
*/
static async createWithSQLite(config, provider, sqliteConfig) {
const { SQLiteAdapter } = require('../adapters/sqlite');
const adapter = new SQLiteAdapter(sqliteConfig);
const storage = new TacoStorage(adapter, config, provider);
await storage.initialize();
return storage;
}
}
exports.TacoStorage = TacoStorage;
//# sourceMappingURL=storage.js.map