# Database Access Pattern Clarification **Author**: Matthew Raymer **Version**: 1.0.0 **Created**: 2025-10-02 12:15:00 UTC **Last Updated**: 2025-10-02 12:15:00 UTC ## Current Problem: Unclear Database Access Patterns The current hybrid scheme has **conflicting** database access patterns that would be difficult to implement practically. ## Reality Check: What Actually Works ### **Option A: Host Always Provides activeDid (Recommended)** **Implementation**: Host application **always** provides activeDid to plugin **Android/Electron:** ```typescript // Host queries its own database const activeIdentity = await this.$getActiveIdentity(); // Uses host's database access await plugin.setActiveDidFromHost(activeIdentity.activeDid); // Plugin uses its own isolated database await plugin.configureDatabase({ platform: 'android', storageType: 'plugin-managed', // Plugin owns its storage encryption: false }); ``` **Web:** ```typescript // Host queries its absurd-sql database const activeIdentity = await this.$getActiveIdentity(); await plugin.setActiveDidFromHost(activeIdentity.activeDid); // Plugin uses host-delegated storage await plugin.configureDatabase({ platform: 'web', storageType: 'host-managed' // Plugin delegates to host }); ``` **iOS:** ```typescript // Host uses its CapacitorSQLite const activeIdentity = await this.$getActiveIdentity(); await plugin.setActiveDidFromHost(activeIdentity.activeDid); // Plugin uses Core Data await plugin.configureDatabase({ platform: 'ios', storageType: 'plugin-managed' // Plugin owns Core Data }); ``` ### **Option B: Plugin Polls Host Database (Backup Only)** **Implementation**: Plugin periodically checks for activeDid changes **Android/Electron:** ```typescript // Plugin could theoretically access host database await plugin.configureDatabase({ platform: 'android', storageType: 'hybrid', hostDatabaseConnection: 'shared_connection', // Requires host coordination pollingInterval: 30000 // Check every 30 seconds }); ``` **Issues with Option B:** - Requires careful database connection sharing - Potential for database locking conflicts - Race conditions during activeDid changes - Security concerns with plugin accessing host data ## **Recommended Implementation: Option A** ### **Why Option A Works Better:** 1. **Clear Separation**: Host owns identity management, plugin owns notification storage 2. **Security**: No shared database access between host and plugin 3. **Reliability**: Fewer moving parts, clearer failure modes 4. **Maintainability**: Easier to test and debug ### **Implementation Details:** #### **Host Application Responsibilities:** ```typescript class TimeSafariNotificationService { async initialize(): Promise { // 1. Host queries its own database for activeDid const activeIdentity = await this.$getActiveIdentity(); // 2. Host provides activeDid to plugin immediately await this.plugin.setActiveDidFromHost(activeIdentity.activeDid); // 3. Host sets up change listener this.plugin.onActiveDidChange((newActiveDid) => { this.handleActiveDidChange(newActiveDid); }); // 4. Plugin configures its own independent storage await this.plugin.configureDatabase({ platform: this.getCurrentPlatform(), storageType: 'plugin-managed' }); } // TimeSafari PlatformServiceMixin integration async $updateActiveDid(newDid: string): Promise { // Update TimeSafari's active_identity table await this.$dbExec( "UPDATE active_identity SET activeDid = ?, lastUpdated = ? WHERE id = 1", [newDid, new Date().toISOString()] ); // Immediately notify plugin of change await this.plugin.refreshActiveDidFromHost(newDid); } } ``` #### **Plugin Responsibilities:** ```typescript class DailyNotificationPlugin { private currentActiveDid: string | null = null; async setActiveDidFromHost(activeDid: string): Promise { this.currentActiveDid = activeDid; await this.clearCacheForIdentityChange(); await this.refreshAuthenticationTokens(); } async onActiveDidChange(callback: (newActiveDid: string) => Promise): Promise { this.activeDidChangeCallback = callback; } private async refreshActiveDidFromHost(newActiveDid: string): Promise { await this.refreshAuthenticationForNewIdentity(newActiveDid); this.activeDidChangeCallback?.(newActiveDid); } } ``` ### **Database Isolation:** **Plugin Storage (Independent):** - Cached notification content - Authentication tokens - Background task state - Plugin configuration **Host Storage (TimeSafari Owns):** - active_identity table - User settings - Application data - Authentication credentials ## **Backup Pattern for Background Tasks** For cases where plugin needs to handle activeDid changes when app is closed: ### **Token-Based Backup:** ```typescript interface PluginConfig { activeDid: string; refreshToken: string; // Encrypted token that can decode new activeDid fallbackExpiryTime: number; // When to stop using fallback } // Plugin can validate if activeDid hasn't expired await plugin.validateActiveDidNotExpired(config); ``` ### **Lightweight Backup Check:** ```typescript // Plugin could make lightweight API call to check current activeDid async checkCurrentActiveDid(): Promise { try { const response = await this.fetch('/api/internal/active-identity', { headers: { 'Authorization': 'Bearer ' + this.backgroundToken } }); return response.json().activeDid; } catch (error) { return null; // Fail safely } } ``` ## **Key Takeaway** **The host application should ALWAYS provide activeDid to the plugin.** The plugin should never directly access TimeSafari's database unless there's a very specific architectural reason (which there isn't in this case). This approach: - ✅ Maintains clear separation of concerns - ✅ Avoids database access conflicts - ✅ Ensures reliable activeDid change detection - ✅ Simplifies implementation and testing - ✅ Maintains security isolation --- **Status**: Clarification complete - Option A recommended **Next Steps**: Update implementation plan to use host-provided activeDid only **Impact**: Simplified architecture with clearer responsibilities