# Secure Storage Implementation Guide for TimeSafari App ## Overview This document outlines the implementation of secure storage for the TimeSafari app using Capacitor solutions. The implementation focuses on: 1. **Platform-Specific Storage Solutions**: - Web: wa-sqlite with IndexedDB backend - iOS: SQLCipher with Keychain integration - Android: SQLCipher with Keystore integration - Electron: SQLite with secure storage 2. **Key Features**: - Encrypted storage using SQLCipher - Platform-specific security features - Migration support from existing implementations - Consistent API across platforms ## Quick Start ### 1. Installation ```bash # Core dependencies npm install @capacitor-community/sqlite@6.0.0 npm install @wa-sqlite/sql.js@0.8.12 npm install @wa-sqlite/sql.js-httpvfs@0.8.12 # Platform-specific dependencies npm install @capacitor/preferences@6.0.2 npm install @capacitor-community/biometric-auth@5.0.0 ``` ### 2. Basic Usage ```typescript // src/services/storage/StorageService.ts import { PlatformServiceFactory } from '../PlatformServiceFactory'; import { StorageError, StorageErrorCodes } from './errors/StorageError'; export class StorageService { private static instance: StorageService; private platformService: PlatformService; private constructor() { this.platformService = PlatformServiceFactory.create(); } static getInstance(): StorageService { if (!StorageService.instance) { StorageService.instance = new StorageService(); } return StorageService.instance; } async initialize(): Promise { try { // Initialize databases await this.platformService.openSecretDatabase(); await this.platformService.openAccountsDatabase(); // Check for migration if (await this.platformService.needsMigration()) { await this.handleMigration(); } } catch (error) { throw new StorageError( 'Failed to initialize storage service', StorageErrorCodes.INITIALIZATION_FAILED, error ); } } private async handleMigration(): Promise { try { // Show migration UI const shouldMigrate = await this.showMigrationPrompt(); if (!shouldMigrate) return; // Perform migration await this.platformService.performMigration(); // Verify migration await this.verifyMigration(); } catch (error) { // Handle migration failure await this.handleMigrationError(error); } } // Example: Adding an account async addAccount(account: Account): Promise { try { await this.platformService.addAccount(account); } catch (error) { if (error instanceof StorageError) { throw error; } throw new StorageError( 'Failed to add account', StorageErrorCodes.QUERY_FAILED, error ); } } // Example: Retrieving an account async getAccountByDid(did: string): Promise { try { return await this.platformService.getAccountByDid(did); } catch (error) { if (error instanceof StorageError) { throw error; } throw new StorageError( 'Failed to retrieve account', StorageErrorCodes.QUERY_FAILED, error ); } } } // Usage example: const storageService = StorageService.getInstance(); await storageService.initialize(); try { const account = await storageService.getAccountByDid('did:example:123'); if (!account) { await storageService.addAccount({ did: 'did:example:123', publicKeyHex: '0x123...', // ... other account properties }); } } catch (error) { if (error instanceof StorageError) { console.error(`Storage error: ${error.code}`, error.message); } else { console.error('Unexpected error:', error); } } ``` ### 3. Platform Detection ```typescript // src/services/storage/PlatformDetection.ts import { Capacitor } from '@capacitor/core'; import { StorageError, StorageErrorCodes } from './errors/StorageError'; export class PlatformDetection { static isNativePlatform(): boolean { return Capacitor.isNativePlatform(); } static getPlatform(): 'ios' | 'android' | 'web' | 'electron' { if (Capacitor.isNativePlatform()) { return Capacitor.getPlatform() as 'ios' | 'android'; } return window.electron ? 'electron' : 'web'; } static async getCapabilities(): Promise { try { const platform = this.getPlatform(); return { hasFileSystem: platform !== 'web', hasSecureStorage: platform !== 'web', hasBiometrics: await this.checkBiometrics(), isIOS: platform === 'ios', isAndroid: platform === 'android' }; } catch (error) { throw new StorageError( 'Failed to detect platform capabilities', StorageErrorCodes.INITIALIZATION_FAILED, error ); } } private static async checkBiometrics(): Promise { if (!this.isNativePlatform()) return false; try { const { BiometricAuth } = await import('@capacitor-community/biometric-auth'); const available = await BiometricAuth.isAvailable(); return available.has; } catch (error) { console.warn('Biometric check failed:', error); return false; } } } // Usage example: try { const capabilities = await PlatformDetection.getCapabilities(); if (capabilities.hasSecureStorage) { // Use platform-specific secure storage await initializeSecureStorage(); } else { // Fall back to web storage await initializeWebStorage(); } } catch (error) { if (error instanceof StorageError) { console.error(`Platform detection error: ${error.code}`, error.message); } else { console.error('Unexpected error during platform detection:', error); } } ``` ### 4. Platform-Specific Implementations #### Web Platform (wa-sqlite) ```typescript // src/services/platforms/web/WebSQLiteService.ts export class WebSQLiteService implements PlatformService { private db: SQLite.Database | null = null; private vfs: IDBBatchAtomicVFS | null = null; private initialized = false; async initialize(): Promise { if (this.initialized) return; try { // 1. Initialize SQLite const sqlite3 = await this.initializeSQLite(); // 2. Set up VFS await this.setupVFS(sqlite3); // 3. Open database await this.openDatabase(); // 4. Set up schema await this.setupSchema(); // 5. Optimize performance await this.optimizePerformance(); this.initialized = true; } catch (error) { throw new StorageError( 'Failed to initialize web SQLite', StorageErrorCodes.INITIALIZATION_FAILED, error ); } } private async initializeSQLite(): Promise { try { return await SQLite.init({ locateFile: file => `https://cdn.jsdelivr.net/npm/@wa-sqlite/sql.js@0.8.12/dist/${file}` }); } catch (error) { throw new StorageError( 'Failed to load SQLite WebAssembly', StorageErrorCodes.WASM_LOAD_FAILED, error ); } } private async setupVFS(sqlite3: SQLite.SqlJsStatic): Promise { try { this.vfs = new IDBBatchAtomicVFS('timesafari'); await this.vfs.registerVFS(sqlite3); } catch (error) { throw new StorageError( 'Failed to set up IndexedDB VFS', StorageErrorCodes.VFS_SETUP_FAILED, error ); } } async openDatabase(): Promise { if (!this.vfs) { throw new StorageError( 'VFS not initialized', StorageErrorCodes.INITIALIZATION_FAILED ); } try { this.db = await this.vfs.openDatabase('timesafari.db'); await this.setupPragmas(); } catch (error) { throw new StorageError( 'Failed to open database', StorageErrorCodes.INITIALIZATION_FAILED, error ); } } private async setupPragmas(): Promise { if (!this.db) return; try { await this.db.exec(` PRAGMA journal_mode = WAL; PRAGMA synchronous = NORMAL; PRAGMA foreign_keys = ON; PRAGMA busy_timeout = 5000; `); } catch (error) { throw new StorageError( 'Failed to set up database pragmas', StorageErrorCodes.INITIALIZATION_FAILED, error ); } } async close(): Promise { if (this.db) { await this.db.close(); this.db = null; } this.initialized = false; } } // Migration strategy for web platform export class WebMigrationService { async migrate(): Promise { // 1. Check prerequisites await this.checkPrerequisites(); // 2. Create backup const backup = await this.createBackup(); // 3. Perform migration try { await this.performMigration(backup); } catch (error) { // 4. Handle failure await this.handleMigrationFailure(error, backup); } // 5. Verify migration await this.verifyMigration(backup); } private async checkPrerequisites(): Promise { // Check IndexedDB availability if (!window.indexedDB) { throw new StorageError( 'IndexedDB not available', StorageErrorCodes.INITIALIZATION_FAILED ); } // Check storage quota const quota = await navigator.storage.estimate(); if (quota.quota && quota.usage && quota.usage > quota.quota * 0.9) { throw new StorageError( 'Insufficient storage space', StorageErrorCodes.INITIALIZATION_FAILED ); } } private async createBackup(): Promise { const backup = { timestamp: Date.now(), accounts: await this.dexieDB.accounts.toArray(), settings: await this.dexieDB.settings.toArray(), contacts: await this.dexieDB.contacts.toArray() }; // Store backup in IndexedDB await this.storeBackup(backup); return backup; } } ``` #### Native Platform (iOS/Android) ```typescript // src/services/platforms/native/NativeSQLiteService.ts export class NativeSQLiteService implements PlatformService { private db: SQLiteConnection | null = null; private initialized = false; async initialize(): Promise { if (this.initialized) return; try { // 1. Check platform capabilities await this.checkPlatformCapabilities(); // 2. Initialize SQLite with encryption await this.initializeEncryptedDatabase(); // 3. Set up schema await this.setupSchema(); this.initialized = true; } catch (error) { throw new StorageError( 'Failed to initialize native SQLite', StorageErrorCodes.INITIALIZATION_FAILED, error ); } } private async checkPlatformCapabilities(): Promise { const { Capacitor } = await import('@capacitor/core'); if (!Capacitor.isNativePlatform()) { throw new StorageError( 'Not running on native platform', StorageErrorCodes.INITIALIZATION_FAILED ); } } private async initializeEncryptedDatabase(): Promise { const { SQLite } = await import('@capacitor-community/sqlite'); this.db = await SQLite.createConnection( 'timesafari', false, 'encryption', 1, false ); await this.db.open(); } async setupSchema(): Promise { if (!this.db) { throw new StorageError( 'Database not initialized', StorageErrorCodes.INITIALIZATION_FAILED ); } try { await this.db.execute(` CREATE TABLE IF NOT EXISTS accounts ( did TEXT PRIMARY KEY, public_key_hex TEXT NOT NULL, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL ); CREATE TABLE IF NOT EXISTS settings ( key TEXT PRIMARY KEY, value TEXT NOT NULL, updated_at INTEGER NOT NULL ); CREATE TABLE IF NOT EXISTS contacts ( did TEXT PRIMARY KEY, name TEXT NOT NULL, public_key_hex TEXT NOT NULL, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL ); `); } catch (error) { throw new StorageError( 'Failed to set up database schema', StorageErrorCodes.INITIALIZATION_FAILED, error ); } } async close(): Promise { if (this.db) { await this.db.close(); this.db = null; } this.initialized = false; } } ``` ### 5. Error Handling ```typescript // src/services/storage/errors/StorageError.ts export enum StorageErrorCodes { INITIALIZATION_FAILED = 'STORAGE_INIT_FAILED', QUERY_FAILED = 'STORAGE_QUERY_FAILED', MIGRATION_FAILED = 'STORAGE_MIGRATION_FAILED', ENCRYPTION_FAILED = 'STORAGE_ENCRYPTION_FAILED', DECRYPTION_FAILED = 'STORAGE_DECRYPTION_FAILED', INVALID_DATA = 'STORAGE_INVALID_DATA', DATABASE_CORRUPTED = 'STORAGE_DB_CORRUPTED', INSUFFICIENT_PERMISSIONS = 'STORAGE_INSUFFICIENT_PERMISSIONS', STORAGE_FULL = 'STORAGE_FULL', CONCURRENT_ACCESS = 'STORAGE_CONCURRENT_ACCESS' } export class StorageError extends Error { constructor( message: string, public code: StorageErrorCodes, public originalError?: unknown ) { super(message); this.name = 'StorageError'; } static isStorageError(error: unknown): error is StorageError { return error instanceof StorageError; } static fromUnknown(error: unknown, context: string): StorageError { if (this.isStorageError(error)) { return error; } return new StorageError( `${context}: ${error instanceof Error ? error.message : String(error)}`, StorageErrorCodes.QUERY_FAILED, error ); } } // Error recovery strategies export class StorageErrorRecovery { static async handleError(error: StorageError): Promise { switch (error.code) { case StorageErrorCodes.DATABASE_CORRUPTED: await this.handleCorruptedDatabase(); break; case StorageErrorCodes.STORAGE_FULL: await this.handleStorageFull(); break; case StorageErrorCodes.CONCURRENT_ACCESS: await this.handleConcurrentAccess(); break; default: throw error; // Re-throw unhandled errors } } private static async handleCorruptedDatabase(): Promise { // 1. Attempt to repair try { await this.repairDatabase(); } catch { // 2. If repair fails, restore from backup await this.restoreFromBackup(); } } private static async handleStorageFull(): Promise { // 1. Clean up temporary files await this.cleanupTempFiles(); // 2. If still full, notify user const isStillFull = await this.checkStorageFull(); if (isStillFull) { throw new StorageError( 'Storage is full. Please free up space.', StorageErrorCodes.STORAGE_FULL ); } } private static async handleConcurrentAccess(): Promise { // Implement retry logic with exponential backoff await this.retryWithBackoff(async () => { // Attempt operation again }); } } ``` ### 6. Testing Strategy ```typescript // src/services/storage/__tests__/StorageService.test.ts import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { StorageService } from '../StorageService'; import { StorageError, StorageErrorCodes } from '../errors/StorageError'; import { PlatformDetection } from '../PlatformDetection'; describe('StorageService', () => { let storageService: StorageService; beforeEach(async () => { storageService = StorageService.getInstance(); await storageService.initialize(); }); afterEach(async () => { // Clean up test data await cleanupTestData(); }); describe('Account Operations', () => { it('should add and retrieve an account', async () => { const account = { did: 'did:test:123', publicKeyHex: '0x123...', // ... other properties }; await storageService.addAccount(account); const retrieved = await storageService.getAccountByDid(account.did); expect(retrieved).toBeDefined(); expect(retrieved?.did).toBe(account.did); }); it('should handle duplicate accounts', async () => { const account = { did: 'did:test:123', publicKeyHex: '0x123...', }; await storageService.addAccount(account); await expect( storageService.addAccount(account) ).rejects.toThrow(StorageError); }); }); describe('Error Handling', () => { it('should handle database corruption', async () => { // Simulate database corruption await simulateDatabaseCorruption(); await expect( storageService.getAccountByDid('did:test:123') ).rejects.toThrow(StorageError); // Verify recovery const recovered = await storageService.getAccountByDid('did:test:123'); expect(recovered).toBeDefined(); }); it('should handle concurrent access', async () => { const promises = Array(5).fill(null).map(() => storageService.addAccount({ did: `did:test:${Math.random()}`, publicKeyHex: '0x123...', }) ); const results = await Promise.allSettled(promises); const errors = results.filter(r => r.status === 'rejected'); expect(errors.length).toBeLessThan(promises.length); }); }); describe('Platform-Specific Tests', () => { it('should use correct storage implementation', async () => { const capabilities = await PlatformDetection.getCapabilities(); if (capabilities.hasSecureStorage) { // Verify native storage implementation expect(storageService.getImplementation()).toBe('native'); } else { // Verify web storage implementation expect(storageService.getImplementation()).toBe('web'); } }); it('should handle platform transitions', async () => { // Simulate platform change (e.g., web to native) await simulatePlatformChange(); // Verify data persistence const account = await storageService.getAccountByDid('did:test:123'); expect(account).toBeDefined(); }); }); }); // Helper functions for testing async function cleanupTestData(): Promise { // Implementation } async function simulateDatabaseCorruption(): Promise { // Implementation } async function simulatePlatformChange(): Promise { // Implementation } ``` #### Additional Platform-Specific Tests ```typescript // src/services/storage/__tests__/WebSQLiteService.spec.ts import { WebSQLiteService } from '../platforms/web/WebSQLiteService'; import { StorageError, StorageErrorCodes } from '../errors/StorageError'; describe('WebSQLiteService', () => { let service: WebSQLiteService; beforeEach(async () => { service = new WebSQLiteService(); await service.initialize(); }); afterEach(async () => { await service.close(); }); it('should initialize successfully', async () => { expect(service.isInitialized()).toBe(true); }); it('should handle IndexedDB errors', async () => { // Mock IndexedDB failure const mockIndexedDB = jest.spyOn(window, 'indexedDB', 'get'); mockIndexedDB.mockImplementation(() => undefined); await expect(service.initialize()).rejects.toThrow( new StorageError( 'IndexedDB not available', StorageErrorCodes.INITIALIZATION_FAILED ) ); }); it('should migrate data correctly', async () => { // Set up test data const testAccount = createTestAccount(); await dexieDB.accounts.add(testAccount); // Perform migration await service.migrate(); // Verify migration const migratedAccount = await service.getAccountByDid(testAccount.did); expect(migratedAccount).toEqual(testAccount); }); }); // Integration tests describe('StorageService Integration', () => { it('should handle concurrent access', async () => { const service1 = StorageService.getInstance(); const service2 = StorageService.getInstance(); // Simulate concurrent access const [result1, result2] = await Promise.all([ service1.addAccount(testAccount1), service2.addAccount(testAccount2) ]); // Verify both operations succeeded expect(result1).toBeDefined(); expect(result2).toBeDefined(); }); it('should recover from errors', async () => { const service = StorageService.getInstance(); // Simulate database corruption await simulateDatabaseCorruption(); // Attempt recovery await service.recover(); // Verify data integrity const accounts = await service.getAllAccounts(); expect(accounts).toBeDefined(); }); }); ``` ### 7. Troubleshooting Guide #### Detailed Recovery Procedures 1. **Database Corruption Recovery** ```typescript async recoverFromCorruption(): Promise { // 1. Stop all database operations await this.stopDatabaseOperations(); // 2. Create backup of corrupted database const backup = await this.createEmergencyBackup(); // 3. Attempt repair try { await this.repairDatabase(); } catch (error) { // 4. Restore from backup if repair fails await this.restoreFromBackup(backup); } } ``` 2. **Migration Recovery** ```typescript async recoverFromFailedMigration(): Promise { // 1. Identify migration stage const stage = await this.getMigrationStage(); // 2. Execute appropriate recovery switch (stage) { case 'backup': await this.recoverFromBackupStage(); break; case 'migration': await this.recoverFromMigrationStage(); break; case 'verification': await this.recoverFromVerificationStage(); break; } } ``` 3. **Performance Troubleshooting** - Monitor database size and growth - Check query performance with EXPLAIN - Review indexing strategy - Monitor memory usage - Check for connection leaks ## Success Criteria 1. **Functionality** - [ ] All CRUD operations work correctly - [ ] Migration process completes successfully - [ ] Error handling works as expected - [ ] Platform-specific features function correctly 2. **Performance** - [ ] Database operations complete within acceptable time - [ ] Memory usage remains stable - [ ] IndexedDB quota usage is monitored - [ ] Concurrent operations work correctly 3. **Security** - [ ] Data is properly encrypted - [ ] Keys are securely stored - [ ] Platform-specific security features work - [ ] No sensitive data leaks 4. **Testing** - [ ] All unit tests pass - [ ] Integration tests complete successfully - [ ] Edge cases are handled - [ ] Error recovery works as expected ## Appendix ### A. Database Schema ```sql -- Accounts Table CREATE TABLE accounts ( did TEXT PRIMARY KEY, public_key_hex TEXT NOT NULL, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL ); -- Settings Table CREATE TABLE settings ( key TEXT PRIMARY KEY, value TEXT NOT NULL, updated_at INTEGER NOT NULL ); -- Contacts Table CREATE TABLE contacts ( did TEXT PRIMARY KEY, name TEXT NOT NULL, public_key_hex TEXT NOT NULL, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL ); -- Indexes CREATE INDEX idx_accounts_created_at ON accounts(created_at); CREATE INDEX idx_contacts_created_at ON contacts(created_at); CREATE INDEX idx_settings_updated_at ON settings(updated_at); ``` ### B. Error Codes Reference | Code | Description | Recovery Action | |------|-------------|-----------------| | `STORAGE_INIT_FAILED` | Database initialization failed | Check permissions, storage space | | `STORAGE_QUERY_FAILED` | Database query failed | Verify query, check connection | | `STORAGE_MIGRATION_FAILED` | Data migration failed | Use backup, manual migration | | `STORAGE_ENCRYPTION_FAILED` | Data encryption failed | Check key management | | `STORAGE_DECRYPTION_FAILED` | Data decryption failed | Verify encryption key | | `STORAGE_INVALID_DATA` | Invalid data format | Validate input data | | `STORAGE_DB_CORRUPTED` | Database corruption detected | Use backup, repair | | `STORAGE_INSUFFICIENT_PERMISSIONS` | Missing required permissions | Request permissions | | `STORAGE_FULL` | Storage quota exceeded | Clean up, increase quota | | `STORAGE_CONCURRENT_ACCESS` | Concurrent access conflict | Implement retry logic | ### C. Platform Capabilities Matrix | Feature | Web | iOS | Android | Electron | |---------|-----|-----|---------|----------| | SQLite | wa-sqlite | SQLCipher | SQLCipher | SQLite | | Encryption | SQLCipher | SQLCipher | SQLCipher | SQLCipher | | Secure Storage | IndexedDB | Keychain | Keystore | Secure Storage | | Biometrics | No | Yes | Yes | No | | File System | Limited | Full | Full | Full | | Background Sync | No | Yes | Yes | Yes | | Storage Quota | Yes | No | No | No | | Multi-tab Support | Yes | N/A | N/A | Yes | ### D. Usage Examples #### Before (Using Dexie.js) ```typescript // src/services/storage/legacy/AccountService.ts import Dexie from 'dexie'; class AccountDatabase extends Dexie { accounts: Dexie.Table; settings: Dexie.Table; contacts: Dexie.Table; constructor() { super('TimeSafariDB'); this.version(1).stores({ accounts: 'did, publicKeyHex, createdAt, updatedAt', settings: 'key, value, updatedAt', contacts: 'did, name, publicKeyHex, createdAt, updatedAt' }); } } export class AccountService { private db: AccountDatabase; constructor() { this.db = new AccountDatabase(); } // Account Management async addAccount(account: Account): Promise { try { await this.db.accounts.add(account); } catch (error) { if (error instanceof Dexie.ConstraintError) { throw new Error('Account already exists'); } throw error; } } async getAccount(did: string): Promise { return await this.db.accounts.get(did); } // Settings Management async updateSetting(key: string, value: string): Promise { await this.db.settings.put({ key, value, updatedAt: Date.now() }); } async getSetting(key: string): Promise { const setting = await this.db.settings.get(key); return setting?.value; } // Contact Management async addContact(contact: Contact): Promise { await this.db.contacts.add(contact); } async getContacts(): Promise { return await this.db.contacts.toArray(); } } // Usage Example const accountService = new AccountService(); // Add an account await accountService.addAccount({ did: 'did:example:123', publicKeyHex: '0x123...', createdAt: Date.now(), updatedAt: Date.now() }); // Update settings await accountService.updateSetting('theme', 'dark'); // Add a contact await accountService.addContact({ did: 'did:example:456', name: 'Alice', publicKeyHex: '0x456...', createdAt: Date.now(), updatedAt: Date.now() }); ``` #### After (Using Platform Service) ```typescript // src/services/storage/AccountService.ts import { StorageService } from './StorageService'; import { StorageError, StorageErrorCodes } from './errors/StorageError'; import { PlatformDetection } from './PlatformDetection'; export class AccountService { private static instance: AccountService; private storageService: StorageService; private constructor() { this.storageService = StorageService.getInstance(); } static getInstance(): AccountService { if (!AccountService.instance) { AccountService.instance = new AccountService(); } return AccountService.instance; } async initialize(): Promise { try { // Initialize storage with platform-specific implementation await this.storageService.initialize(); // Check for migration if needed if (await this.storageService.needsMigration()) { await this.handleMigration(); } } catch (error) { throw StorageError.fromUnknown(error, 'Failed to initialize account service'); } } // Account Management with Platform-Specific Features async addAccount(account: Account): Promise { try { // Check platform capabilities const capabilities = await PlatformDetection.getCapabilities(); // Add platform-specific metadata const enhancedAccount = { ...account, platform: capabilities.isIOS ? 'ios' : capabilities.isAndroid ? 'android' : capabilities.hasSecureStorage ? 'electron' : 'web', secureStorage: capabilities.hasSecureStorage, biometricsEnabled: capabilities.hasBiometrics }; await this.storageService.addAccount(enhancedAccount); // If platform supports biometrics, offer to enable it if (capabilities.hasBiometrics) { await this.offerBiometricSetup(account.did); } } catch (error) { if (error instanceof StorageError) { throw error; } throw new StorageError( 'Failed to add account', StorageErrorCodes.QUERY_FAILED, error ); } } async getAccount(did: string): Promise { try { const account = await this.storageService.getAccountByDid(did); // Verify account integrity if (account) { await this.verifyAccountIntegrity(account); } return account; } catch (error) { throw StorageError.fromUnknown(error, `Failed to get account ${did}`); } } // Settings Management with Encryption async updateSetting(key: string, value: string): Promise { try { const capabilities = await PlatformDetection.getCapabilities(); // Encrypt sensitive settings if platform supports it const processedValue = capabilities.hasSecureStorage ? await this.encryptSetting(value) : value; await this.storageService.updateSettings({ key, value: processedValue, updatedAt: Date.now() }); } catch (error) { throw StorageError.fromUnknown(error, `Failed to update setting ${key}`); } } async getSetting(key: string): Promise { try { const setting = await this.storageService.getAccountSettings(key); if (setting?.value) { const capabilities = await PlatformDetection.getCapabilities(); // Decrypt if the setting was encrypted return capabilities.hasSecureStorage ? await this.decryptSetting(setting.value) : setting.value; } return undefined; } catch (error) { throw StorageError.fromUnknown(error, `Failed to get setting ${key}`); } } // Contact Management with Platform Integration async addContact(contact: Contact): Promise { try { const capabilities = await PlatformDetection.getCapabilities(); // Add platform-specific features const enhancedContact = { ...contact, platform: capabilities.isIOS ? 'ios' : capabilities.isAndroid ? 'android' : capabilities.hasSecureStorage ? 'electron' : 'web', syncEnabled: capabilities.hasBackgroundSync }; await this.storageService.addContact(enhancedContact); // If platform supports background sync, schedule contact sync if (capabilities.hasBackgroundSync) { await this.scheduleContactSync(contact.did); } } catch (error) { throw StorageError.fromUnknown(error, 'Failed to add contact'); } } async getContacts(): Promise { try { const contacts = await this.storageService.getAllContacts(); // Verify contact data integrity await Promise.all(contacts.map(contact => this.verifyContactIntegrity(contact) )); return contacts; } catch (error) { throw StorageError.fromUnknown(error, 'Failed to get contacts'); } } // Platform-Specific Helper Methods private async offerBiometricSetup(did: string): Promise { const { BiometricAuth } = await import('@capacitor-community/biometric-auth'); const available = await BiometricAuth.isAvailable(); if (available.has) { // Show biometric setup prompt // Implementation depends on UI framework } } private async verifyAccountIntegrity(account: Account): Promise { // Verify account data integrity // Implementation depends on security requirements } private async verifyContactIntegrity(contact: Contact): Promise { // Verify contact data integrity // Implementation depends on security requirements } private async encryptSetting(value: string): Promise { // Encrypt sensitive settings // Implementation depends on encryption requirements return value; // Placeholder } private async decryptSetting(value: string): Promise { // Decrypt sensitive settings // Implementation depends on encryption requirements return value; // Placeholder } private async scheduleContactSync(did: string): Promise { // Schedule background sync for contacts // Implementation depends on platform capabilities } } // Usage Example const accountService = AccountService.getInstance(); // Initialize with platform detection await accountService.initialize(); try { // Add an account with platform-specific features await accountService.addAccount({ did: 'did:example:123', publicKeyHex: '0x123...', createdAt: Date.now(), updatedAt: Date.now() }); // Update settings with encryption if available await accountService.updateSetting('theme', 'dark'); await accountService.updateSetting('apiKey', 'sensitive-data'); // Add a contact with platform integration await accountService.addContact({ did: 'did:example:456', name: 'Alice', publicKeyHex: '0x456...', createdAt: Date.now(), updatedAt: Date.now() }); // Retrieve data with integrity verification const account = await accountService.getAccount('did:example:123'); const contacts = await accountService.getContacts(); const theme = await accountService.getSetting('theme'); console.log('Account:', account); console.log('Contacts:', contacts); console.log('Theme:', theme); } catch (error) { if (error instanceof StorageError) { console.error(`Storage error: ${error.code}`, error.message); } else { console.error('Unexpected error:', error); } } ``` Key improvements in the new implementation: 1. **Platform Awareness**: - Automatically detects platform capabilities - Uses platform-specific features (biometrics, secure storage) - Handles platform transitions gracefully 2. **Enhanced Security**: - Encrypts sensitive data when platform supports it - Verifies data integrity - Uses platform-specific secure storage 3. **Better Error Handling**: - Consistent error types and codes - Platform-specific error recovery - Detailed error messages 4. **Migration Support**: - Automatic migration detection - Data integrity verification - Backup and recovery 5. **Platform Integration**: - Background sync for contacts - Biometric authentication - Secure storage for sensitive data 6. **Type Safety**: - Strong typing throughout - Platform capability type checking - Error type narrowing 7. **Singleton Pattern**: - Single instance management - Consistent state across the app - Resource sharing 8. **Extensibility**: - Easy to add new platform features - Modular design - Clear separation of concerns