You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

62 KiB

Secure Storage Implementation Guide for TimeSafari App

Overview

This document outlines the implementation of secure storage for the TimeSafari app using Capacitor solutions. Two primary storage options are provided:

  1. SQLite with SQLCipher Encryption (Primary Solution):

    • Utilizes @capacitor-community/sqlite plugin with SQLCipher for 256-bit AES encryption.
    • Supports web and native platforms (iOS, Android).
    • Ideal for complex queries and relational data.
    • Platform-specific SQLCipher implementations:
      • Android: Native SQLCipher library.
      • iOS: SQLCipher as a drop-in SQLite replacement.
      • Web: WebAssembly-based SQLCipher.
  2. Capacitor Preferences API (For Small Data):

    • Built-in Capacitor solution for lightweight key-value storage.
    • Uses platform-specific secure storage (e.g., Keychain on iOS, EncryptedSharedPreferences on Android).
    • Limited to small datasets; no query capabilities.

Architecture

Directory Structure

src/services/
├── storage/                    # Storage services
│   ├── SQLiteService.ts        # Core SQLite service
│   ├── EncryptionService.ts    # SQLCipher encryption handling
│   ├── KeyManagementService.ts # Secure key management
│   ├── platforms/             # Platform-specific implementations
│   │   ├── WebStorageService.ts
│   │   ├── CapacitorStorageService.ts
│   │   └── ElectronStorageService.ts
│   ├── migrations/            # Database migration scripts
│   │   ├── 001_initial.ts     # Initial schema setup
│   │   └── 002_encryption.ts  # Encryption configuration
│   └── types/
│       └── storage.types.ts   # TypeScript type definitions
└── PlatformService.ts         # Existing platform service interface

Platform Service Integration

The storage implementation integrates with the existing PlatformService interface to provide platform-specific storage operations. This allows for consistent API usage across platforms while maintaining platform-specific optimizations.

Storage Service Interface

// src/services/PlatformService.ts
import { Account } from '../db/tables/accounts';
import { Contact } from '../db/tables/contacts';
import { Settings } from '../db/tables/settings';
import { Secret } from '../db/tables/secret';

export interface PlatformService {
  // ... existing platform methods ...

  // Storage Operations
  // Secret Database
  openSecretDatabase(): Promise<void>;
  getMasterSecret(): Promise<Secret | undefined>;
  setMasterSecret(secret: Secret): Promise<void>;
  
  // Accounts Database
  openAccountsDatabase(): Promise<void>;
  getAccountsCount(): Promise<number>;
  getAllAccounts(): Promise<Account[]>;
  getAccountByDid(did: string): Promise<Account | undefined>;
  addAccount(account: Account): Promise<void>;
  updateAccountSettings(did: string, settings: Partial<Settings>): Promise<void>;
  
  // Settings Operations
  getDefaultSettings(): Promise<Settings | undefined>;
  getAccountSettings(did: string): Promise<Settings | undefined>;
  updateSettings(key: string, changes: Partial<Settings>): Promise<void>;
  addSettings(settings: Settings): Promise<void>;
  getSettingsCount(): Promise<number>;
  
  // Contacts Operations
  getAllContacts(): Promise<Contact[]>;
  addContact(contact: Contact): Promise<void>;
  
  // Database Management
  deleteDatabase(): Promise<void>;
  importDatabase(data: Blob): Promise<void>;
  exportDatabase(): Promise<Blob>;
  
  // Migration Support
  isFirstInstall(): Promise<boolean>;
  needsMigration(): Promise<boolean>;
  performMigration(): Promise<void>;
  
  // Platform Detection
  isCapacitor(): boolean;
  isElectron(): boolean;
  isWeb(): boolean;
  getCapabilities(): {
    hasFileSystem: boolean;
    hasSecureStorage: boolean;
    hasBiometrics: boolean;
    isIOS: boolean;
    isAndroid: boolean;
  };
}

Platform-Specific Implementations

  1. Web Platform (Dexie)

    // src/services/platforms/WebPlatformService.ts
    export class WebPlatformService implements PlatformService {
      // ... existing web platform methods ...
    
      // Secret Database
      async openSecretDatabase(): Promise<void> {
        await secretDB.open();
      }
    
      async getMasterSecret(): Promise<Secret | undefined> {
        return await secretDB.secret.get(MASTER_SECRET_KEY);
      }
    
      async setMasterSecret(secret: Secret): Promise<void> {
        await secretDB.secret.put(secret);
      }
    
      // Accounts Database
      async openAccountsDatabase(): Promise<void> {
        const accountsDB = await accountsDBPromise;
        await accountsDB.open();
      }
    
      async getAccountsCount(): Promise<number> {
        const accountsDB = await accountsDBPromise;
        return await accountsDB.accounts.count();
      }
    
      // ... implement other storage methods using Dexie ...
    }
    
  2. Capacitor Platform (SQLite)

    // src/services/platforms/CapacitorPlatformService.ts
    export class CapacitorPlatformService implements PlatformService {
      private sqliteService: SQLiteService;
      private keyManagement: KeyManagementService;
    
      constructor() {
        this.sqliteService = SQLiteService.getInstance();
        this.keyManagement = KeyManagementService.getInstance();
      }
    
      // Secret Database
      async openSecretDatabase(): Promise<void> {
        await this.sqliteService.initialize({
          database: 'timesafari_secret.db',
          encrypted: true,
          version: 1
        });
      }
    
      async getMasterSecret(): Promise<Secret | undefined> {
        const result = await this.sqliteService.query<Secret>(
          'SELECT * FROM secret WHERE id = ?',
          [MASTER_SECRET_KEY]
        );
        return result.value?.[0];
      }
    
      async setMasterSecret(secret: Secret): Promise<void> {
        await this.sqliteService.query(
          'INSERT OR REPLACE INTO secret (id, secret) VALUES (?, ?)',
          [secret.id, secret.secret]
        );
      }
    
      // Accounts Database
      async openAccountsDatabase(): Promise<void> {
        await this.sqliteService.initialize({
          database: 'timesafari_accounts.db',
          encrypted: true,
          version: 1,
          key: await this.keyManagement.getEncryptionKey()
        });
      }
    
      async getAccountsCount(): Promise<number> {
        const result = await this.sqliteService.query<{ count: number }>(
          'SELECT COUNT(*) as count FROM accounts'
        );
        return result.value?.[0]?.count ?? 0;
      }
    
      // ... implement other storage methods using SQLite ...
    }
    

Usage Example

// src/services/storage/StorageService.ts
import { PlatformServiceFactory } from '../PlatformServiceFactory';

export class StorageService {
  private static instance: StorageService | null = null;
  private platformService: PlatformService;

  private constructor() {
    this.platformService = PlatformServiceFactory.getInstance();
  }

  static getInstance(): StorageService {
    if (!StorageService.instance) {
      StorageService.instance = new StorageService();
    }
    return StorageService.instance;
  }

  async initialize(): Promise<void> {
    // Initialize secret database
    await this.platformService.openSecretDatabase();
    
    // Initialize accounts database
    await this.platformService.openAccountsDatabase();
    
    // Check if migration is needed
    if (await this.platformService.needsMigration()) {
      await this.platformService.performMigration();
    }
  }

  async getAccountByDid(did: string): Promise<Account | undefined> {
    return await this.platformService.getAccountByDid(did);
  }

  async updateAccountSettings(did: string, settings: Partial<Settings>): Promise<void> {
    await this.platformService.updateAccountSettings(did, settings);
  }

  // ... implement other storage methods delegating to platformService ...
}

Migration Strategy

The platform service interface supports a smooth migration from Dexie to SQLite:

  1. Web Platform:

    • Continues using Dexie implementation
    • No changes to existing code
    • Maintains backward compatibility
  2. Capacitor Platform:

    • Uses SQLite with SQLCipher
    • Implements platform-specific security
    • Handles migration from Dexie if needed
  3. Migration Process:

    // src/services/storage/migration/MigrationService.ts
    export class MigrationService {
      async migrateFromDexieToSQLite(): Promise<void> {
        // 1. Export data from Dexie
        const accounts = await this.platformService.getAllAccounts();
        const settings = await this.platformService.getAllSettings();
        const contacts = await this.platformService.getAllContacts();
    
        // 2. Initialize SQLite
        await this.platformService.openAccountsDatabase();
    
        // 3. Import data to SQLite
        for (const account of accounts) {
          await this.platformService.addAccount(account);
        }
    
        // 4. Import settings and contacts
        for (const setting of settings) {
          await this.platformService.addSettings(setting);
        }
        for (const contact of contacts) {
          await this.platformService.addContact(contact);
        }
    
        // 5. Verify migration
        await this.verifyMigration();
      }
    }
    

Dependencies

Verified and updated dependencies for the implementation:

  • Node.js Dependencies:

    npm install @capacitor-community/sqlite@6.0.0
    npm install @capacitor/core@6.1.2
    npm install @capacitor/preferences@6.0.2
    npm install @jlongster/sql.js@1.10.3 --save-dev  # For web SQLCipher
    
  • Android (build.gradle):

    implementation 'net.zetetic:android-database-sqlcipher:4.6.1'
    
  • iOS (Podfile):

    pod 'SQLCipher', '~> 4.6.0'
    

Run npx cap sync after installing dependencies to update native projects.

Type Definitions

// src/services/storage/types/storage.types.ts

export interface StorageOptions {
  encrypted?: boolean; // Whether to use encryption (default: true)
  database: string;   // Database name
  version: number;    // Migration version
  key?: string;       // Encryption key (optional)
}

export interface StorageResult<T> {
  success: boolean;     // Operation success status
  error?: string;      // Error message if operation failed
  value?: T;           // Stored/retrieved value
}

export interface StorageService {
  initialize(options: StorageOptions): Promise<void>;
  setItem<T>(key: string, value: T): Promise<StorageResult<T>>;
  getItem<T>(key: string): Promise<StorageResult<T>>;
  removeItem(key: string): Promise<StorageResult<void>>;
  query<T>(sql: string, params?: any[]): Promise<StorageResult<T[]>>;
}

Implementation Details

1. SQLite Service

// src/services/storage/SQLiteService.ts
import { CapacitorSQLite, SQLiteConnection, SQLiteDBConnection } from '@capacitor-community/sqlite';
import { EncryptionService } from './EncryptionService';

export class SQLiteService implements StorageService {
  private static instance: SQLiteService;
  private connection: SQLiteConnection;
  private db?: SQLiteDBConnection;
  private encryptionService: EncryptionService;

  private constructor() {
    this.connection = new SQLiteConnection(CapacitorSQLite);
    this.encryptionService = new EncryptionService();
  }

  static getInstance(): SQLiteService {
    if (!SQLiteService.instance) {
      SQLiteService.instance = new SQLiteService();
    }
    return SQLiteService.instance;
  }

  async initialize(options: StorageOptions): Promise<void> {
    try {
      this.db = await this.connection.createConnection(
        options.database,
        options.encrypted ?? true,
        options.encrypted ? 'encryption' : 'no-encryption',
        options.version,
        false
      );

      await this.db.open();

      if (options.encrypted) {
        const encryptionKey = options.key ?? await this.encryptionService.getEncryptionKey();
        await this.db.execute(`PRAGMA key = '${encryptionKey}'`);
      }

      await this.runMigrations();
    } catch (error) {
      throw new Error(`Database initialization failed: ${(error as Error).message}`);
    }
  }

  async setItem<T>(key: string, value: T): Promise<StorageResult<T>> {
    if (!this.db) throw new Error('Database not initialized');
    try {
      const encryptedValue = await this.encryptionService.encrypt(JSON.stringify(value));
      await this.db.run(
        'INSERT OR REPLACE INTO storage (key, value) VALUES (?, ?)',
        [key, encryptedValue]
      );
      return { success: true, value };
    } catch (error) {
      return { success: false, error: (error as Error).message };
    }
  }

  async getItem<T>(key: string): Promise<StorageResult<T>> {
    if (!this.db) throw new Error('Database not initialized');
    try {
      const result = await this.db.query('SELECT value FROM storage WHERE key = ?', [key]);
      if (!result.values?.length) {
        return { success: false, error: 'Key not found' };
      }
      const decryptedValue = await this.encryptionService.decrypt(result.values[0].value);
      return { success: true, value: JSON.parse(decryptedValue) as T };
    } catch (error) {
      return { success: false, error: (error as Error).message };
    }
  }

  async removeItem(key: string): Promise<StorageResult<void>> {
    if (!this.db) throw new Error('Database not initialized');
    try {
      await this.db.run('DELETE FROM storage WHERE key = ?', [key]);
      return { success: true };
    } catch (error) {
      return { success: false, error: (error as Error).message };
    }
  }

  async query<T>(sql: string, params: any[] = []): Promise<StorageResult<T[]>> {
    if (!this.db) throw new Error('Database not initialized');
    try {
      const result = await this.db.query(sql, params);
      return { success: true, value: (result.values || []) as T[] };
    } catch (error) {
      return { success: false, error: (error as Error).message };
    }
  }

  private async runMigrations(): Promise<void> {
    if (!this.db) throw new Error('Database not initialized');
    await this.db.execute(`
      CREATE TABLE IF NOT EXISTS storage (
        key TEXT PRIMARY KEY,
        value TEXT
      )
    `);
    // Add additional migration scripts from migrations/ folder as needed
  }
}

2. Encryption Service

// src/services/storage/EncryptionService.ts
import { SQLiteDBConnection } from '@capacitor-community/sqlite';
import { Capacitor } from '@capacitor/core';

export class EncryptionService {
  private encryptionKey: string | null = null;

  async getEncryptionKey(): Promise<string> {
    if (!this.encryptionKey) {
      // In a real implementation, use platform-specific secure storage
      // This is a simplified example
      this.encryptionKey = await this.generateKey();
    }
    return this.encryptionKey;
  }

  async initialize(db: SQLiteDBConnection): Promise<void> {
    const key = await this.getEncryptionKey();
    await db.execute(`PRAGMA key = '${key}'`);
    await db.execute('PRAGMA cipher_default_kdf_iter = 64000');
    await db.execute('PRAGMA cipher_page_size = 4096');
  }

  async encrypt(value: string): Promise<string> {
    if (Capacitor.isNativePlatform()) {
      // Use platform-specific encryption (e.g., iOS Keychain, Android Keystore)
      return value; // Placeholder for native encryption
    }
    // Web Crypto API for web platform
    const encoder = new TextEncoder();
    const key = await this.getWebCryptoKey();
    const iv = crypto.getRandomValues(new Uint8Array(12));
    const encrypted = await crypto.subtle.encrypt(
      { name: 'AES-GCM', iv },
      key,
      encoder.encode(value)
    );
    return btoa(String.fromCharCode(...new Uint8Array(iv), ...new Uint8Array(encrypted)));
  }

  async decrypt(value: string): Promise<string> {
    if (Capacitor.isNativePlatform()) {
      // Use platform-specific decryption
      return value; // Placeholder for native decryption
    }
    // Web Crypto API for web platform
    const data = Uint8Array.from(atob(value), c => c.charCodeAt(0));
    const iv = data.slice(0, 12);
    const encrypted = data.slice(12);
    const key = await this.getWebCryptoKey();
    const decrypted = await crypto.subtle.decrypt(
      { name: 'AES-GCM', iv },
      key,
      encrypted
    );
    return new TextDecoder().decode(decrypted);
  }

  private async generateKey(): Promise<string> {
    const key = crypto.getRandomValues(new Uint8Array(32));
    return btoa(String.fromCharCode(...key));
  }

  private async getWebCryptoKey(): Promise<CryptoKey> {
    const key = await this.getEncryptionKey();
    return crypto.subtle.importKey(
      'raw',
      Uint8Array.from(atob(key), c => c.charCodeAt(0)),
      { name: 'AES-GCM' },
      false,
      ['encrypt', 'decrypt']
    );
  }
}

3. Preferences Service

// src/services/storage/PreferencesService.ts
import { Preferences } from '@capacitor/preferences';
import { StorageService, StorageResult } from './types/storage.types';

export class PreferencesService implements StorageService {
  async initialize(): Promise<void> {
    // No initialization needed for Preferences API
  }

  async setItem<T>(key: string, value: T): Promise<StorageResult<T>> {
    try {
      await Preferences.set({ key, value: JSON.stringify(value) });
      return { success: true, value };
    } catch (error) {
      return { success: false, error: (error as Error).message };
    }
  }

  async getItem<T>(key: string): Promise<StorageResult<T>> {
    try {
      const { value } = await Preferences.get({ key });
      if (!value) {
        return { success: false, error: 'Key not found' };
      }
      return { success: true, value: JSON.parse(value) as T };
    } catch (error) {
      return { success: false, error: (error as Error).message };
    }
  }

  async removeItem(key: string): Promise<StorageResult<void>> {
    try {
      await Preferences.remove({ key });
      return { success: true };
    } catch (error) {
      return { success: false, error: (error as Error).message };
    }
  }

  async query<T>(): Promise<StorageResult<T[]>> {
    return { success: false, error: 'Query not supported in Preferences API' };
  }
}

4. Key Management Service

// src/services/storage/KeyManagementService.ts
import { Capacitor } from '@capacitor/core';

interface SecureKeyOptions {
  useBiometrics: boolean;
  keySize: number;
  keyAlgorithm: 'AES-GCM' | 'AES-CBC';
}

export class KeyManagementService {
  private static instance: KeyManagementService;

  private constructor() {}

  static getInstance(): KeyManagementService {
    if (!KeyManagementService.instance) {
      KeyManagementService.instance = new KeyManagementService();
    }
    return KeyManagementService.instance;
  }

  async generateSecureKey(options: SecureKeyOptions): Promise<string> {
    const key = await this.generateRandomKey(options.keySize);
    if (Capacitor.isNativePlatform()) {
      return Capacitor.getPlatform() === 'ios'
        ? this.encryptWithSecureEnclave(key, options)
        : this.encryptWithAndroidKeystore(key, options);
    }
    return this.encryptWithWebCrypto(key, options);
  }

  private async generateRandomKey(keySize: number): Promise<string> {
    const key = crypto.getRandomValues(new Uint8Array(keySize / 8));
    return btoa(String.fromCharCode(...key));
  }

  private async encryptWithSecureEnclave(key: string, options: SecureKeyOptions): Promise<string> {
    // iOS Secure Enclave implementation (placeholder)
    // Use Keychain with biometric protection
    return key; // Implement platform-specific code
  }

  private async encryptWithAndroidKeystore(key: string, options: SecureKeyOptions): Promise<string> {
    // Android Keystore implementation (placeholder)
    // Use EncryptedSharedPreferences with biometric protection
    return key; // Implement platform-specific code
  }

  private async encryptWithWebCrypto(key: string, options: SecureKeyOptions): Promise<string> {
    // Web Crypto API implementation
    const encoder = new TextEncoder();
    const cryptoKey = await crypto.subtle.generateKey(
      { name: options.keyAlgorithm, length: options.keySize },
      true,
      ['encrypt', 'decrypt']
    );
    const exportedKey = await crypto.subtle.exportKey('raw', cryptoKey);
    return btoa(String.fromCharCode(...new Uint8Array(exportedKey)));
  }
}

5. Biometric Service

// src/services/storage/BiometricService.ts
import { Capacitor } from '@capacitor/core';

export class BiometricService {
  async authenticate(): Promise<boolean> {
    if (!Capacitor.isNativePlatform()) {
      return true; // Web fallback (no biometric auth)
    }
    try {
      // Use Capacitor Biometric plugin (e.g., @capacitor-community/biometric-auth)
      // Placeholder for actual implementation
      return true;
    } catch (error) {
      console.error('Biometric authentication failed:', error);
      return false;
    }
  }
}

Migration Strategy

Two-Day Implementation Timeline (Revised)

Day 1: Core Implementation and Basic Security

Morning (4 hours):

  1. Essential Setup (1 hour)

    # Install core dependencies
    npm install @capacitor-community/sqlite@6.0.0 @capacitor/core@6.1.2
    npx cap sync
    
  2. Core Services Implementation (3 hours)

    • Implement basic SQLiteService with encryption support
    • Create simplified KeyManagementService for platform-specific key storage
    • Set up initial database schema
    // Priority 1: Core tables
    export const initialSchema = `
      CREATE TABLE IF NOT EXISTS accounts (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        did TEXT UNIQUE,
        publicKeyHex TEXT,
        identity TEXT
      );
      CREATE TABLE IF NOT EXISTS secret (
        id INTEGER PRIMARY KEY,
        secret TEXT
      );
    `;
    

Afternoon (4 hours):

  1. Platform-Specific Security (2 hours)

    • Implement basic platform detection
    • Set up platform-specific key storage:
      • iOS: Basic Keychain integration
      • Android: Basic Keystore integration
      • Web: Web Crypto API with IndexedDB
    • Defer advanced features (Secure Enclave, StrongBox) to post-migration
  2. Migration Utilities (2 hours)

    • Create basic data export from Dexie
    • Implement simple data import to SQLite
    • Add basic validation
    // Simplified migration utility
    export class MigrationUtils {
      async migrateData(): Promise<void> {
        // 1. Export from Dexie
        const data = await this.exportFromDexie();
    
        // 2. Initialize SQLite
        await this.initializeSQLite();
    
        // 3. Import data
        await this.importToSQLite(data);
    
        // 4. Basic validation
        await this.validateMigration();
      }
    }
    

Day 2: Migration and Testing

Morning (4 hours):

  1. Migration Implementation (2 hours)

    • Implement migration script
    • Add basic error handling
    • Create rollback capability
    export class DatabaseMigration {
      async migrate(): Promise<void> {
        try {
          // 1. Backup existing data
          await this.backupExistingData();
    
          // 2. Perform migration
          await this.migrationUtils.migrateData();
    
          // 3. Verify migration
          const isValid = await this.verifyMigration();
          if (!isValid) {
            await this.rollback();
            throw new Error('Migration validation failed');
          }
        } catch (error) {
          await this.rollback();
          throw error;
        }
      }
    }
    
  2. Basic Security Integration (2 hours)

    • Implement basic biometric check
    • Add simple key derivation
    • Set up basic encryption
    export class SecurityService {
      async initialize(): Promise<void> {
        // Basic security setup
        await this.setupEncryption();
        await this.setupBiometrics();
      }
    }
    

Afternoon (4 hours):

  1. Testing and Validation (2 hours)

    • Unit tests for core functionality
    • Basic integration tests
    • Platform-specific tests
    describe('Database Migration', () => {
      it('should migrate accounts successfully', async () => {
        // Basic migration test
      });
    
      it('should handle encryption correctly', async () => {
        // Basic encryption test
      });
    });
    
  2. Documentation and Cleanup (2 hours)

    • Update documentation
    • Clean up code
    • Create basic backup/restore functionality

Post-Migration Features (To Be Implemented Later)

  1. Enhanced Security

    • Advanced platform-specific features (Secure Enclave, StrongBox)
    • Sophisticated key rotation
    • Advanced biometric integration
  2. Advanced Backup

    • Encrypted backup system
    • Recovery key management
    • Cross-device backup
  3. Performance Optimization

    • Query optimization
    • Index management
    • Caching strategies

Critical Path Items (Detailed Breakdown)

Build System Integration (Day 1 Morning)

  1. Vite Configuration for Capacitor

    // vite.config.ts
    import { defineConfig } from 'vite';
    import { Capacitor } from '@capacitor/core';
    
    export default defineConfig({
      // ... existing config ...
      build: {
        // Ensure proper bundling for Capacitor
        target: 'es2015',
        rollupOptions: {
          output: {
            // Handle platform-specific code
            format: 'es',
            manualChunks: {
              'capacitor-core': ['@capacitor/core'],
              'sqlite': ['@capacitor-community/sqlite']
            }
          }
        }
      },
      plugins: [
        // ... existing plugins ...
        {
          name: 'capacitor-platform',
          config(config, { command }) {
            // Add platform-specific environment variables
            return {
              define: {
                'process.env.CAPACITOR_PLATFORM': JSON.stringify(Capacitor.getPlatform())
              }
            };
          }
        }
      ]
    });
    
  2. Platform Detection and Conditional Imports

    // src/services/storage/platform-detection.ts
    import { Capacitor } from '@capacitor/core';
    
    export const isNativePlatform = Capacitor.isNativePlatform();
    export const platform = Capacitor.getPlatform();
    
    // Conditional imports for platform-specific code
    export const getPlatformService = async () => {
      if (!isNativePlatform) {
        return null; // Web platform uses existing implementation
      }
    
      switch (platform) {
        case 'ios':
          return (await import('./platforms/ios')).IOSStorageService;
        case 'android':
          return (await import('./platforms/android')).AndroidStorageService;
        default:
          throw new Error(`Unsupported platform: ${platform}`);
      }
    };
    

Day 1 Critical Path

  1. Core Database Setup (Morning)

    • SQLite plugin installation and configuration
    • Basic database schema implementation
    • Platform detection integration
    • Vite build configuration for Capacitor
    • Basic encryption setup
  2. Platform-Specific Implementation (Afternoon)

    • iOS Keychain integration
      • Basic key storage
      • Error handling
      • Platform detection
    • Android Keystore integration
      • Basic key storage
      • Error handling
      • Platform detection
    • Web platform detection
      • Skip implementation for web
      • Maintain existing Dexie implementation
  3. Migration Utilities (Afternoon)

    • Data export from Dexie
      • Account data
      • Secret data
      • Basic validation
    • SQLite import
      • Table creation
      • Data import
      • Basic error handling

Day 2 Critical Path

  1. Migration Implementation (Morning)

    • Migration script
      // src/services/storage/migration/MigrationScript.ts
      export class MigrationScript {
        async execute(): Promise<void> {
          // 1. Platform check
          if (!isNativePlatform) {
            console.log('Skipping migration on web platform');
            return;
          }
      
          // 2. Backup
          await this.backupExistingData();
      
          // 3. Migration
          await this.performMigration();
      
          // 4. Verification
          await this.verifyMigration();
        }
      }
      
    • Rollback mechanism
    • Error handling
    • Platform-specific validation
  2. Security Integration (Morning)

    • Basic biometric check
    • Key derivation
    • Platform-specific encryption
    • Error handling for security features
  3. Testing and Validation (Afternoon)

    • Unit tests
      // src/services/storage/__tests__/StorageService.spec.ts
      describe('StorageService', () => {
        it('should use SQLite on native platforms', async () => {
          if (isNativePlatform) {
            const service = await getPlatformService();
            expect(service).toBeDefined();
            // ... more tests
          }
        });
      
        it('should skip migration on web', async () => {
          if (!isNativePlatform) {
            const migration = new MigrationScript();
            await migration.execute();
            // Verify web implementation unchanged
          }
        });
      });
      
    • Integration tests
    • Platform-specific tests
    • Migration validation
  4. Documentation and Cleanup (Afternoon)

    • Update documentation
    • Code cleanup
    • Basic backup/restore
    • Build system verification

Build System Integration Details

  1. Vite Configuration for Capacitor

    // vite.config.ts
    import { defineConfig } from 'vite';
    import { Capacitor } from '@capacitor/core';
    
    const capacitorConfig = {
      // Platform-specific entry points
      input: {
        main: 'src/main.ts',
        capacitor: 'src/capacitor.ts'
      },
      // Platform-specific output
      output: {
        format: 'es',
        dir: 'dist',
        entryFileNames: (chunkInfo) => {
          return chunkInfo.name === 'capacitor' 
            ? 'capacitor/[name].[hash].js'
            : '[name].[hash].js';
        }
      }
    };
    
    export default defineConfig({
      // ... existing config ...
      build: {
        ...capacitorConfig,
        rollupOptions: {
          external: [
            // External dependencies for Capacitor
            '@capacitor/core',
            '@capacitor-community/sqlite'
          ],
          output: {
            globals: {
              '@capacitor/core': 'Capacitor',
              '@capacitor-community/sqlite': 'CapacitorSQLite'
            }
          }
        }
      }
    });
    
  2. Platform-Specific Entry Point

    // src/capacitor.ts
    import { Capacitor } from '@capacitor/core';
    import { SQLiteService } from './services/storage/SQLiteService';
    
    // Only initialize Capacitor-specific services on native platforms
    if (Capacitor.isNativePlatform()) {
      const initializeCapacitor = async () => {
        const sqliteService = SQLiteService.getInstance();
        await sqliteService.initialize({
          database: 'timesafari.db',
          encrypted: true,
          version: 1
        });
      };
    
      initializeCapacitor().catch(console.error);
    }
    
  3. Build Scripts

    // package.json
    {
      "scripts": {
        "build": "vite build",
        "build:capacitor": "vite build --mode capacitor",
        "build:ios": "vite build --mode capacitor --platform ios",
        "build:android": "vite build --mode capacitor --platform android",
        "cap:sync": "npm run build:capacitor && npx cap sync",
        "cap:ios": "npm run build:ios && npx cap sync ios",
        "cap:android": "npm run build:android && npx cap sync android"
      }
    }
    
  4. Environment Configuration

    // src/config/environment.ts
    import { Capacitor } from '@capacitor/core';
    
    export const environment = {
      isNative: Capacitor.isNativePlatform(),
      platform: Capacitor.getPlatform(),
      storage: {
        type: Capacitor.isNativePlatform() ? 'sqlite' : 'dexie',
        // Platform-specific configuration
        sqlite: {
          database: 'timesafari.db',
          encrypted: true,
          version: 1
        }
      }
    };
    

Success Criteria (Updated)

  1. Build System

    • Vite configuration properly handles Capacitor builds
    • Platform-specific code is correctly bundled
    • Web implementation remains unchanged
    • Build scripts work for all platforms
  2. Day 1

    • SQLite database operational on native platforms
    • Basic encryption working
    • Migration utilities ready
    • Platform detection working
    • Build system integration complete
  3. Day 2

    • Successful migration on native platforms
    • Basic security implemented
    • Core functionality tested
    • Rollback capability verified
    • Web implementation unaffected

Security Considerations

  • Key Management: Keys stored in iOS Keychain, Android Keystore, or Web Crypto API. Never stored in plaintext.
  • Data Protection: 256-bit AES-GCM encryption via SQLCipher. Data bound to device and user authentication.
  • Platform-Specific:
    • iOS: Secure Enclave and Keychain with Face ID/Touch ID.
    • Android: Android Keystore with BiometricPrompt.
    • Web: Web Crypto API with secure storage fallback.

Performance Considerations

  • Use transactions for batch operations.
  • Implement proper indexing for query performance.
  • Cache frequently accessed data.
  • Monitor memory usage and optimize large datasets.

Future Improvements

  • Key rotation support.
  • Backup and restore capabilities.
  • Enhanced biometric options.
  • Cross-device synchronization.

Maintenance

  • Regular security audits.
  • Platform-specific updates.
  • Dependency management with semantic versioning.
  • Performance monitoring.

Platform-Specific Implementation Details

iOS Implementation

  1. Secure Enclave Integration

    // src/services/storage/platforms/ios/SecureEnclaveService.ts
    import { Capacitor } from '@capacitor/core';
    import { Keychain } from '@capacitor-community/native-keychain';
    
    export class SecureEnclaveService {
      private static readonly KEYCHAIN_SERVICE = 'com.timesafari.securestorage';
      private static readonly KEYCHAIN_ACCESS_GROUP = 'group.com.timesafari.securestorage';
    
      async storeKey(key: string, options: SecureKeyOptions): Promise<void> {
        const accessControl = {
          // Require device to be unlocked
          accessible: Keychain.Accessible.WHEN_UNLOCKED,
          // Use Secure Enclave
          accessControl: Keychain.AccessControl.BIOMETRY_ANY,
          // Require user presence
          authenticationType: Keychain.AuthenticationType.BIOMETRICS,
          // Keychain access group for app extension sharing
          accessGroup: this.KEYCHAIN_ACCESS_GROUP
        };
    
        await Keychain.set({
          key: 'sqlite_encryption_key',
          value: key,
          service: this.KEYCHAIN_SERVICE,
          ...accessControl
        });
      }
    
      async retrieveKey(): Promise<string> {
        const result = await Keychain.get({
          key: 'sqlite_encryption_key',
          service: this.KEYCHAIN_SERVICE
        });
    
        if (!result.value) {
          throw new Error('Encryption key not found in Keychain');
        }
    
        return result.value;
      }
    }
    
  2. Face ID/Touch ID Integration

    // src/services/storage/platforms/ios/BiometricService.ts
    import { BiometricAuth } from '@capacitor-community/biometric-auth';
    
    export class IOSBiometricService {
      async authenticate(): Promise<boolean> {
        const available = await BiometricAuth.isAvailable();
        if (!available.has) {
          return false;
        }
    
        try {
          const result = await BiometricAuth.verify({
            reason: 'Authenticate to access secure data',
            title: 'TimeSafari Authentication',
            subtitle: 'Verify your identity',
            description: 'Use Face ID or Touch ID to access your secure data',
            negativeButtonText: 'Cancel'
          });
    
          return result.verified;
        } catch (error) {
          console.error('Biometric authentication failed:', error);
          return false;
        }
      }
    }
    

Android Implementation

  1. Android Keystore Integration

    // src/services/storage/platforms/android/KeystoreService.ts
    import { AndroidKeystore } from '@capacitor-community/android-keystore';
    
    export class AndroidKeystoreService {
      private static readonly KEY_ALIAS = 'timesafari_sqlite_key';
    
      async storeKey(key: string, options: SecureKeyOptions): Promise<void> {
        const keyGenParameterSpec = {
          keyAlias: this.KEY_ALIAS,
          purposes: ['ENCRYPT', 'DECRYPT'],
          blockModes: ['GCM'],
          encryptionPaddings: ['NoPadding'],
          keySize: 256,
          userAuthenticationRequired: true,
          userAuthenticationValidityDurationSeconds: -1,
          // Use StrongBox if available
          isStrongBoxBacked: true
        };
    
        await AndroidKeystore.generateKey(keyGenParameterSpec);
        await AndroidKeystore.encrypt({
          keyAlias: this.KEY_ALIAS,
          data: key
        });
      }
    
      async retrieveKey(): Promise<string> {
        const result = await AndroidKeystore.decrypt({
          keyAlias: this.KEY_ALIAS
        });
    
        return result.decryptedData;
      }
    }
    
  2. Biometric Integration

    // src/services/storage/platforms/android/BiometricService.ts
    import { BiometricAuth } from '@capacitor-community/biometric-auth';
    
    export class AndroidBiometricService {
      async authenticate(): Promise<boolean> {
        const available = await BiometricAuth.isAvailable();
        if (!available.has) {
          return false;
        }
    
        try {
          const result = await BiometricAuth.verify({
            reason: 'Authenticate to access secure data',
            title: 'TimeSafari Authentication',
            subtitle: 'Verify your identity',
            description: 'Use biometric authentication to access your secure data',
            negativeButtonText: 'Cancel',
            // Android-specific options
            allowDeviceCredential: true
          });
    
          return result.verified;
        } catch (error) {
          console.error('Biometric authentication failed:', error);
          return false;
        }
      }
    }
    

Web Implementation

  1. Web Crypto API Integration
    // src/services/storage/platforms/web/WebCryptoService.ts
    export class WebCryptoService {
      private static readonly KEY_NAME = 'timesafari_sqlite_key';
    
      async storeKey(key: string): Promise<void> {
        // Generate a master key for encrypting the SQLite key
        const masterKey = await crypto.subtle.generateKey(
          { name: 'AES-GCM', length: 256 },
          true,
          ['encrypt', 'decrypt']
        );
    
        // Encrypt the SQLite key
        const iv = crypto.getRandomValues(new Uint8Array(12));
        const encryptedKey = await crypto.subtle.encrypt(
          { name: 'AES-GCM', iv },
          masterKey,
          new TextEncoder().encode(key)
        );
    
        // Store encrypted key in IndexedDB
        const db = await this.getSecureDB();
        await db.put('keys', {
          name: this.KEY_NAME,
          iv,
          data: encryptedKey
        });
    
        // Export and store master key in secure storage
        const exportedKey = await crypto.subtle.exportKey('raw', masterKey);
        await this.storeMasterKey(exportedKey);
      }
    
      private async getSecureDB(): Promise<IDBDatabase> {
        // Implementation using IndexedDB with encryption
      }
    
      private async storeMasterKey(key: ArrayBuffer): Promise<void> {
        // Store in secure storage (e.g., localStorage with encryption)
      }
    }
    

Key Rotation and Backup Process

Key Rotation

  1. Automatic Key Rotation

    // src/services/storage/KeyRotationService.ts
    export class KeyRotationService {
      private static readonly ROTATION_INTERVAL = 30 * 24 * 60 * 60 * 1000; // 30 days
    
      async checkAndRotateKey(): Promise<void> {
        const lastRotation = await this.getLastRotationDate();
        if (Date.now() - lastRotation > this.ROTATION_INTERVAL) {
          await this.rotateKey();
        }
      }
    
      private async rotateKey(): Promise<void> {
        // 1. Generate new key
        const newKey = await this.generateNewKey();
    
        // 2. Re-encrypt database with new key
        await this.reencryptDatabase(newKey);
    
        // 3. Store new key securely
        await this.storeNewKey(newKey);
    
        // 4. Update rotation timestamp
        await this.updateRotationDate();
      }
    
      private async reencryptDatabase(newKey: string): Promise<void> {
        const sqliteService = SQLiteService.getInstance();
    
        // Export all data
        const data = await sqliteService.exportAllData();
    
        // Create new database with new key
        await sqliteService.initialize({
          database: 'timesafari_new.db',
          encrypted: true,
          version: 1,
          key: newKey
        });
    
        // Import data to new database
        await sqliteService.importData(data);
    
        // Verify data integrity
        await this.verifyDataIntegrity();
    
        // Replace old database with new one
        await sqliteService.replaceDatabase('timesafari_new.db', 'timesafari.db');
      }
    }
    
  2. Manual Key Rotation

    // src/services/storage/KeyRotationService.ts
    export class KeyRotationService {
      async manualRotateKey(): Promise<void> {
        // Require user authentication
        const biometrics = new BiometricService();
        const authenticated = await biometrics.authenticate();
        if (!authenticated) {
          throw new Error('Authentication required for key rotation');
        }
    
        await this.rotateKey();
      }
    }
    

Backup Process

  1. Secure Backup

    // src/services/storage/BackupService.ts
    export class BackupService {
      async createBackup(): Promise<BackupResult> {
        // 1. Export database
        const data = await this.exportDatabase();
    
        // 2. Export encryption keys
        const keys = await this.exportKeys();
    
        // 3. Create encrypted backup
        const backup = await this.encryptBackup(data, keys);
    
        // 4. Store backup securely
        return this.storeBackup(backup);
      }
    
      private async exportDatabase(): Promise<DatabaseExport> {
        const sqliteService = SQLiteService.getInstance();
        return {
          version: await sqliteService.getVersion(),
          tables: await sqliteService.exportTables(),
          metadata: await sqliteService.getMetadata()
        };
      }
    
      private async exportKeys(): Promise<KeyExport> {
        const keyManagement = KeyManagementService.getInstance();
        return {
          sqliteKey: await keyManagement.exportKey(),
          backupKey: await this.generateBackupKey()
        };
      }
    
      private async encryptBackup(
        data: DatabaseExport,
        keys: KeyExport
      ): Promise<EncryptedBackup> {
        // Encrypt data with backup key
        const encryptedData = await this.encryptData(data, keys.backupKey);
    
        // Encrypt backup key with user's recovery key
        const encryptedBackupKey = await this.encryptBackupKey(
          keys.backupKey,
          await this.getRecoveryKey()
        );
    
        return {
          data: encryptedData,
          backupKey: encryptedBackupKey,
          timestamp: Date.now(),
          version: '1.0'
        };
      }
    }
    
  2. Restore Process

    // src/services/storage/BackupService.ts
    export class BackupService {
      async restoreBackup(backup: EncryptedBackup): Promise<void> {
        // 1. Verify backup integrity
        await this.verifyBackup(backup);
    
        // 2. Decrypt backup key
        const backupKey = await this.decryptBackupKey(
          backup.backupKey,
          await this.getRecoveryKey()
        );
    
        // 3. Decrypt data
        const data = await this.decryptData(backup.data, backupKey);
    
        // 4. Restore database
        await this.restoreDatabase(data);
    
        // 5. Verify restoration
        await this.verifyRestoration();
      }
    }
    

Integration with Existing Security Model

  1. Account Security Integration

    // src/services/security/AccountSecurityService.ts
    export class AccountSecurityService {
      private storageService: StorageService;
      private keyManagement: KeyManagementService;
    
      async initialize(): Promise<void> {
        this.storageService = await StorageServiceFactory.create();
        this.keyManagement = KeyManagementService.getInstance();
    
        // Link SQLite encryption to account security
        await this.linkAccountSecurity();
      }
    
      private async linkAccountSecurity(): Promise<void> {
        const account = await this.storageService.getActiveAccount();
        if (account) {
          // Derive SQLite key from account credentials
          const sqliteKey = await this.deriveSQLiteKey(account);
    
          // Store derived key securely
          await this.keyManagement.storeKey(sqliteKey, {
            useBiometrics: true,
            keySize: 256,
            keyAlgorithm: 'AES-GCM'
          });
        }
      }
    
      private async deriveSQLiteKey(account: Account): Promise<string> {
        // Use account credentials to derive SQLite key
        const input = `${account.did}:${account.publicKeyHex}`;
        return this.keyManagement.deriveKey(input);
      }
    }
    
  2. Biometric Integration

    // src/services/security/BiometricSecurityService.ts
    export class BiometricSecurityService {
      async setupBiometricProtection(): Promise<void> {
        const biometrics = new BiometricService();
        const available = await biometrics.isAvailable();
    
        if (available) {
          // Enable biometric protection for SQLite
          await this.keyManagement.updateKeyProtection({
            useBiometrics: true,
            requireAuthentication: true
          });
    
          // Update app settings
          await this.storageService.updateSettings({
            biometricProtection: true,
            lastBiometricSetup: Date.now()
          });
        }
      }
    }
    
  3. Migration from Existing Security

    // src/services/security/SecurityMigrationService.ts
    export class SecurityMigrationService {
      async migrateToNewSecurity(): Promise<void> {
        // 1. Export existing secure data
        const existingData = await this.exportExistingData();
    
        // 2. Initialize new security model
        await this.initializeNewSecurity();
    
        // 3. Import data with new security
        await this.importWithNewSecurity(existingData);
    
        // 4. Verify migration
        await this.verifySecurityMigration();
      }
    
      private async exportExistingData(): Promise<SecureData> {
        // Export data from existing secure storage
        const accounts = await this.exportAccounts();
        const secrets = await this.exportSecrets();
        const settings = await this.exportSettings();
    
        return { accounts, secrets, settings };
      }
    
      private async initializeNewSecurity(): Promise<void> {
        // Set up new security infrastructure
        await this.keyManagement.initialize();
        await this.storageService.initialize({
          database: 'timesafari.db',
          encrypted: true,
          version: 1
        });
      }
    }
    

Detailed Platform Implementations

Web Platform (Dexie)

  1. Database Initialization

    // src/services/platforms/WebPlatformService.ts
    export class WebPlatformService implements PlatformService {
      private secretDB: Dexie;
      private accountsDB: Dexie | null = null;
    
      constructor() {
        // Initialize secret database
        this.secretDB = new Dexie('TimeSafariSecret');
        this.secretDB.version(1).stores({
          secret: 'id, secret'
        });
      }
    
      async openAccountsDatabase(): Promise<void> {
        if (!this.accountsDB) {
          this.accountsDB = new Dexie('TimeSafariAccounts');
          this.accountsDB.version(1).stores({
            accounts: '++id, dateCreated, derivationPath, did, $identity, $mnemonic, publicKeyHex',
            settings: '++id, accountDid, *keys',
            contacts: '++id, did, name, *keys'
          });
    
          // Apply encryption using master secret
          const secret = await this.getMasterSecret();
          if (secret) {
            encrypted(this.accountsDB, { secretKey: secret.secret });
          }
        }
        await this.accountsDB.open();
      }
    
      async getAccountByDid(did: string): Promise<Account | undefined> {
        if (!this.accountsDB) {
          throw new Error('Accounts database not initialized');
        }
        return await this.accountsDB.accounts
          .where('did')
          .equals(did)
          .first();
      }
    
      // ... other methods ...
    }
    
  2. Error Handling

    // src/services/platforms/WebPlatformService.ts
    export class WebPlatformService implements PlatformService {
      private async handleDatabaseError(operation: string, error: unknown): Promise<never> {
        const errorMessage = error instanceof Error ? error.message : 'Unknown error';
        logger.error(`Database error during ${operation}:`, error);
    
        if (error instanceof DexieError) {
          switch (error.name) {
            case 'QuotaExceededError':
              throw new Error('Storage quota exceeded. Please clear some space and try again.');
            case 'InvalidTableError':
              throw new Error('Database schema mismatch. Please try clearing your browser data.');
            case 'ConstraintError':
              throw new Error('Operation would violate database constraints.');
            default:
              throw new Error(`Database error: ${errorMessage}`);
          }
        }
    
        throw new Error(`Failed to ${operation}: ${errorMessage}`);
      }
    
      async addAccount(account: Account): Promise<void> {
        try {
          if (!this.accountsDB) {
            throw new Error('Accounts database not initialized');
          }
          await this.accountsDB.accounts.add(account);
        } catch (error) {
          await this.handleDatabaseError('add account', error);
        }
      }
    }
    

Capacitor Platform (SQLite)

  1. Database Initialization with Security

    // src/services/platforms/CapacitorPlatformService.ts
    export class CapacitorPlatformService implements PlatformService {
      private sqliteService: SQLiteService;
      private keyManagement: KeyManagementService;
      private biometricService: BiometricService;
    
      constructor() {
        this.sqliteService = SQLiteService.getInstance();
        this.keyManagement = KeyManagementService.getInstance();
        this.biometricService = BiometricService.getInstance();
      }
    
      async openAccountsDatabase(): Promise<void> {
        try {
          // Check biometric authentication if required
          if (await this.shouldRequireBiometrics()) {
            const authenticated = await this.biometricService.authenticate();
            if (!authenticated) {
              throw new Error('Biometric authentication required');
            }
          }
    
          // Get encryption key from secure storage
          const key = await this.keyManagement.getEncryptionKey();
    
          // Initialize SQLite with encryption
          await this.sqliteService.initialize({
            database: 'timesafari_accounts.db',
            encrypted: true,
            version: 1,
            key
          });
    
          // Set up database schema
          await this.setupDatabaseSchema();
        } catch (error) {
          await this.handleDatabaseError('open accounts database', error);
        }
      }
    
      private async setupDatabaseSchema(): Promise<void> {
        const schema = `
          CREATE TABLE IF NOT EXISTS accounts (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            dateCreated TEXT NOT NULL,
            derivationPath TEXT,
            did TEXT UNIQUE NOT NULL,
            identity TEXT,
            mnemonic TEXT,
            publicKeyHex TEXT NOT NULL
          );
          CREATE TABLE IF NOT EXISTS settings (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            accountDid TEXT NOT NULL,
            key TEXT NOT NULL,
            value TEXT,
            FOREIGN KEY (accountDid) REFERENCES accounts(did)
          );
          CREATE TABLE IF NOT EXISTS contacts (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            did TEXT UNIQUE NOT NULL,
            name TEXT,
            keys TEXT
          );
        `;
        await this.sqliteService.execute(schema);
      }
    
      // ... other methods ...
    }
    
  2. Platform-Specific Security

    // src/services/platforms/CapacitorPlatformService.ts
    export class CapacitorPlatformService implements PlatformService {
      private async shouldRequireBiometrics(): Promise<boolean> {
        const capabilities = this.getCapabilities();
        if (!capabilities.hasBiometrics) {
          return false;
        }
    
        // Check user preferences
        const settings = await this.getDefaultSettings();
        return settings?.requireBiometrics ?? false;
      }
    
      private async getPlatformKey(): Promise<string> {
        const capabilities = this.getCapabilities();
    
        if (capabilities.isIOS) {
          // Use iOS Keychain with Secure Enclave
          return await this.keyManagement.getIOSKey({
            useSecureEnclave: true,
            requireBiometrics: await this.shouldRequireBiometrics()
          });
        } else if (capabilities.isAndroid) {
          // Use Android Keystore
          return await this.keyManagement.getAndroidKey({
            useStrongBox: true,
            requireBiometrics: await this.shouldRequireBiometrics()
          });
        }
    
        throw new Error('Unsupported platform for secure key storage');
      }
    }
    

Usage Examples

  1. Account Management

    // src/services/AccountService.ts
    export class AccountService {
      constructor(private platformService: PlatformService) {}
    
      async createNewAccount(mnemonic: string): Promise<Account> {
        try {
          // Generate account details
          const [address, privateHex, publicHex, derivationPath] = deriveAddress(mnemonic);
          const account: Account = {
            dateCreated: new Date().toISOString(),
            derivationPath,
            did: `did:eth:${address}`,
            identity: JSON.stringify({ address, privateHex, publicHex }),
            mnemonic,
            publicKeyHex: publicHex
          };
    
          // Save account
          await this.platformService.addAccount(account);
    
          // Set as active account
          await this.platformService.updateSettings('activeDid', { value: account.did });
    
          return account;
        } catch (error) {
          if (error instanceof Error) {
            throw new Error(`Failed to create account: ${error.message}`);
          }
          throw error;
        }
      }
    
      async getActiveAccount(): Promise<Account | undefined> {
        const settings = await this.platformService.getDefaultSettings();
        if (!settings?.activeDid) {
          return undefined;
        }
        return await this.platformService.getAccountByDid(settings.activeDid);
      }
    }
    
  2. Settings Management

    // src/services/SettingsService.ts
    export class SettingsService {
      constructor(private platformService: PlatformService) {}
    
      async updateAccountSettings(did: string, changes: Partial<Settings>): Promise<void> {
        try {
          // Verify account exists
          const account = await this.platformService.getAccountByDid(did);
          if (!account) {
            throw new Error(`Account ${did} not found`);
          }
    
          // Update settings
          await this.platformService.updateAccountSettings(did, changes);
    
          // Log changes for audit
          await this.logSettingsChange(did, changes);
        } catch (error) {
          await this.handleSettingsError('update account settings', error);
        }
      }
    
      private async logSettingsChange(did: string, changes: Partial<Settings>): Promise<void> {
        // Implementation for audit logging
      }
    }
    

Error Handling and Edge Cases

  1. Common Error Scenarios

    // src/services/storage/errors/StorageError.ts
    export class StorageError extends Error {
      constructor(
        message: string,
        public readonly code: string,
        public readonly originalError?: unknown
      ) {
        super(message);
        this.name = 'StorageError';
      }
    }
    
    export const StorageErrorCodes = {
      DATABASE_NOT_INITIALIZED: 'DB_NOT_INIT',
      ENCRYPTION_FAILED: 'ENC_FAILED',
      DECRYPTION_FAILED: 'DEC_FAILED',
      QUOTA_EXCEEDED: 'QUOTA_EXCEEDED',
      BIOMETRIC_REQUIRED: 'BIOMETRIC_REQUIRED',
      MIGRATION_FAILED: 'MIGRATION_FAILED',
      INVALID_DATA: 'INVALID_DATA'
    } as const;
    
  2. Error Recovery Strategies

    // src/services/storage/error-recovery/ErrorRecoveryService.ts
    export class ErrorRecoveryService {
      constructor(private platformService: PlatformService) {}
    
      async handleStorageError(error: StorageError): Promise<void> {
        switch (error.code) {
          case StorageErrorCodes.DATABASE_NOT_INITIALIZED:
            await this.reinitializeDatabase();
            break;
    
          case StorageErrorCodes.ENCRYPTION_FAILED:
          case StorageErrorCodes.DECRYPTION_FAILED:
            await this.handleEncryptionError();
            break;
    
          case StorageErrorCodes.QUOTA_EXCEEDED:
            await this.handleQuotaExceeded();
            break;
    
          case StorageErrorCodes.BIOMETRIC_REQUIRED:
            await this.handleBiometricRequired();
            break;
    
          case StorageErrorCodes.MIGRATION_FAILED:
            await this.handleMigrationFailure();
            break;
    
          default:
            throw error; // Re-throw unknown errors
        }
      }
    
      private async reinitializeDatabase(): Promise<void> {
        try {
          // Attempt to reinitialize with backup
          const backup = await this.platformService.exportDatabase();
          await this.platformService.deleteDatabase();
          await this.platformService.importDatabase(backup);
        } catch (error) {
          // If reinitialization fails, try clean initialization
          await this.platformService.deleteDatabase();
          await this.platformService.openAccountsDatabase();
        }
      }
    
      private async handleEncryptionError(): Promise<void> {
        // Attempt to recover encryption key
        const newKey = await this.keyManagement.regenerateKey();
        await this.platformService.setMasterSecret({
          id: MASTER_SECRET_KEY,
          secret: newKey
        });
      }
    
      private async handleQuotaExceeded(): Promise<void> {
        // Implement cleanup strategy
        const accounts = await this.platformService.getAllAccounts();
        const oldAccounts = accounts.filter(acc => 
          new Date(acc.dateCreated).getTime() < Date.now() - 30 * 24 * 60 * 60 * 1000
        );
    
        for (const account of oldAccounts) {
          await this.platformService.deleteAccount(account.did);
        }
      }
    }
    
  3. Edge Cases and Mitigations

    a. Concurrent Access

    // src/services/storage/ConcurrencyManager.ts
    export class ConcurrencyManager {
      private locks: Map<string, Promise<void>> = new Map();
    
      async withLock<T>(key: string, operation: () => Promise<T>): Promise<T> {
        const existingLock = this.locks.get(key);
        if (existingLock) {
          await existingLock;
        }
    
        const lock = new Promise<void>((resolve) => {
          this.locks.set(key, lock);
        });
    
        try {
          return await operation();
        } finally {
          this.locks.delete(key);
          lock.resolve();
        }
      }
    }
    

    b. Data Integrity

    // src/services/storage/DataIntegrityService.ts
    export class DataIntegrityService {
      async verifyDataIntegrity(): Promise<boolean> {
        const accounts = await this.platformService.getAllAccounts();
    
        for (const account of accounts) {
          // Verify account data structure
          if (!this.isValidAccount(account)) {
            await this.repairAccount(account);
            continue;
          }
    
          // Verify settings exist
          const settings = await this.platformService.getAccountSettings(account.did);
          if (!settings) {
            await this.createDefaultSettings(account.did);
          }
    
          // Verify contacts
          const contacts = await this.platformService.getAllContacts();
          const invalidContacts = contacts.filter(c => !this.isValidContact(c));
          for (const contact of invalidContacts) {
            await this.repairContact(contact);
          }
        }
    
        return true;
      }
    
      private isValidAccount(account: Account): boolean {
        return (
          account.did &&
          account.publicKeyHex &&
          account.dateCreated &&
          (!account.identity || this.isValidJSON(account.identity))
        );
      }
    
      private async repairAccount(account: Account): Promise<void> {
        // Implementation for account repair
      }
    }
    

    c. Platform Transitions

    // src/services/storage/PlatformTransitionService.ts
    export class PlatformTransitionService {
      async handlePlatformTransition(): Promise<void> {
        const capabilities = this.platformService.getCapabilities();
    
        if (capabilities.isIOS) {
          await this.handleIOSTransition();
        } else if (capabilities.isAndroid) {
          await this.handleAndroidTransition();
        }
      }
    
      private async handleIOSTransition(): Promise<void> {
        // Handle iOS-specific transitions
        // e.g., moving from Keychain to Secure Enclave
      }
    
      private async handleAndroidTransition(): Promise<void> {
        // Handle Android-specific transitions
        // e.g., upgrading to StrongBox
      }
    }