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
      }
    }