From b4cb3d5638ef4368b7940579facd8959c60c7638 Mon Sep 17 00:00:00 2001 From: Matthew Raymer Date: Thu, 2 Oct 2025 09:20:43 +0000 Subject: [PATCH] consolidate: Merge all analysis documents into single BACKGROUND_DATA_FETCHING_PLAN.md - Consolidated DATABASE_ACCESS_CLARIFICATION.md content into main plan - Consolidated ACTIVE_DID_CHANGE_REQUIREMENTS.md content into main plan - Consolidated TIMESAFARI_INTEGRATION_ANALYSIS.md content into main plan - Enhanced main document with Option A architecture overview - Added comprehensive TimeSafari integration patterns section - Added critical requirement section for activeDid change detection - Added event-based solution implementation details - Updated README.md to reference single consolidated document - Eliminated unnecessary document proliferation as requested The BACKGROUND_DATA_FETCHING_PLAN.md now serves as the single source of truth for all implementation guidance, containing Option A architecture, TimeSafari integration patterns, activeDid change management, and platform-specific details. --- README.md | 3 +- doc/ACTIVE_DID_CHANGE_REQUIREMENTS.md | 204 --------------- doc/BACKGROUND_DATA_FETCHING_PLAN.md | 111 +++++++- doc/DATABASE_ACCESS_CLARIFICATION.md | 208 --------------- doc/TIMESAFARI_INTEGRATION_ANALYSIS.md | 343 ------------------------- 5 files changed, 107 insertions(+), 762 deletions(-) delete mode 100644 doc/ACTIVE_DID_CHANGE_REQUIREMENTS.md delete mode 100644 doc/DATABASE_ACCESS_CLARIFICATION.md delete mode 100644 doc/TIMESAFARI_INTEGRATION_ANALYSIS.md diff --git a/README.md b/README.md index 74a18a2..497ca33 100644 --- a/README.md +++ b/README.md @@ -534,8 +534,7 @@ MIT License - see [LICENSE](LICENSE) file for details. - **Verification Checklist**: [doc/VERIFICATION_CHECKLIST.md](doc/VERIFICATION_CHECKLIST.md) - Regular verification process - **UI Requirements**: [doc/UI_REQUIREMENTS.md](doc/UI_REQUIREMENTS.md) - Complete UI component requirements - **UI Integration Examples**: [examples/ui-integration-examples.ts](examples/ui-integration-examples.ts) - Ready-to-use UI components -- **Background Data Fetching Plan**: [doc/BACKGROUND_DATA_FETCHING_PLAN.md](doc/BACKGROUND_DATA_FETCHING_PLAN.md) - Host-provided activeDid architecture -- **Database Access Clarification**: [doc/DATABASE_ACCESS_CLARIFICATION.md](doc/DATABASE_ACCESS_CLARIFICATION.md) - Option A implementation details +- **Background Data Fetching Plan**: [doc/BACKGROUND_DATA_FETCHING_PLAN.md](doc/BACKGROUND_DATA_FETCHING_PLAN.md) - Complete Option A implementation guide ### Community diff --git a/doc/ACTIVE_DID_CHANGE_REQUIREMENTS.md b/doc/ACTIVE_DID_CHANGE_REQUIREMENTS.md deleted file mode 100644 index 2d33a75..0000000 --- a/doc/ACTIVE_DID_CHANGE_REQUIREMENTS.md +++ /dev/null @@ -1,204 +0,0 @@ -# ActiveDid Change Requirements - -**Author**: Matthew Raymer -**Version**: 1.0.0 -**Created**: 2025-10-02 12:00:00 UTC -**Last Updated**: 2025-10-02 12:00:00 UTC - -## Critical Requirement: Plugin Must Know When activeDid Changes - -**CONFIRMED**: The plugin **must** be notified when activeDid changes to maintain data integrity and security. - -## Why This Is Critical - -### **Security Implications** -- **Authentication Isolation**: Each activeDid has different authentication tokens -- **Data Privacy**: Cached content belongs to specific user identity -- **Authorization**: API calls must use correct activeDid for proper authorization - -### **Data Integrity Issues** -- **Cache Pollution**: Wrong user's data could leak between identities -- **Stale Authentication**: Old JWT tokens become invalid for new identity -- **Background Tasks**: Tasks running with wrong identity context - -### **User Experience Problems** -```typescript -// PROBLEMATIC SCENARIO WITHOUT CHANGE NOTIFICATION: -// 1. User A schedules notification for "my offers" -// 2. User switches to User B -// 3. Plugin doesn't know about change -// 4. Plugin delivers User A's personal offer data to User B ❌ -``` - -## Solution: Event-Based Change Notification - -### **TimeSafari Host Application** -```typescript -// In PlatformServiceMixin.ts - CRITICAL PATTERN -async $updateActiveDid(newDid: string | null): Promise { - // ... existing activeDid update logic ... - - // MUST notify Daily Notification Plugin - await this.$notifyDailyNotificationService(newDid); -} - -async $notifyDailyNotificationService(newActiveDid: string): Promise { - const event = new CustomEvent('activeDidChanged', { - detail: { activeDid: newActiveDid } - }); - document.dispatchEvent(event); -} -``` - -### **Plugin Side Implementation** -```typescript -// In Daily Notification Plugin - MANDATORY LISTENING -export class EnhancedDailyNotificationPlugin { - private currentActiveDid: string | null = null; - - constructor() { - this.setupActiveDidChangeListener(); - } - - private setupActiveDidChangeListener(): void { - document.addEventListener('activeDidChanged', (event: CustomEvent) => { - this.handleActiveDidChange(event.detail.activeDid); - }); - } - - private async handleActiveDidChange(newActiveDid: string): Promise { - if (newActiveDid !== this.currentActiveDid) { - logger.info(`[Plugin] ActiveDid changing from ${this.currentActiveDid} to ${newActiveDid}`); - - // CRITICAL ACTIONS REQUIRED: - await this.clearCacheForNewIdentity(); - await this.refreshAuthenticationForNewIdentity(newActiveDid); - await this.updateBackgroundTaskIdentity(newActiveDid); - - this.currentActiveDid = newActiveDid; - } - } - - async clearCacheForNewIdentity(): Promise { - // Clear all cached content to prevent data leakage - await this.clearStoredContent(); - await this.clearAuthenticationTokens(); - } - - async refreshAuthenticationForNewIdentity(activeDid: string): Promise { - // Generate new JWT tokens with correct activeDid - const newJwt = await this.generateJWT(activeDid); - this.currentAuthToken = newJwt; - } - - async updateBackgroundTaskIdentity(activeDid: string): Promise { - // Update background tasks to use new identity - await this.pauseBackgroundTasks(); - await this.configureBackgroundTasksForIdentity(activeDid); - await this.resumeBackgroundTasks(); - } -} -``` - -## Implementation Requirements - -### **Host Application Responsibilities** -1. **Event Dispatch**: Must dispatch `activeDidChanged` events -2. **Timing**: Dispatch immediately after updating active_identity table -3. **Reliability**: Event must reach all plugin listeners - -### **Plugin Responsibilities** -1. **Event Listening**: Must listen for `activeDidChanged` events -2. **Cache Clearing**: Must clear all cached content for new identity -3. **Authentication Refresh**: Must generate new tokens with updated activeDid -4. **Background Task Update**: Must restart background tasks with new context -5. **Error Handling**: Must handle failures gracefully during identity transition - -## Testing Requirements - -### **Integration Tests** -```typescript -describe('ActiveDid Change Handling', () => { - it('should clear cache when activeDid changes', async () => { - // Set up initial identity - await plugin.setActiveDidFromHost('did:alice:123'); - await plugin.cacheContentData({ /* Alice's data */ }); - - // Simulate identity change - const event = new CustomEvent('activeDidChanged', { - detail: { activeDid: 'did:bob:456' } - }); - document.dispatchEvent(event); - - // Verify cache is cleared - expect(await plugin.getCachedContent()).toBeNull(); - }); - - it('should refresh authentication tokens', async () => { - // Set initial identity - const aliceToken = await plugin.generateJWT('did:alice:123'); - - // Change identity - document.dispatchEvent(new CustomEvent('activeDidChanged', { - detail: { activeDid: 'did:bob:456' } - })); - - // Verify new token with correct identity - const bobToken = await plugin.getCurrentAuthToken(); - expect(bobToken.payload.sub).toBe('did:bob:456'); - }); - - it('should handle multiple rapid identity changes', async () => { - // Test rapid switching between identities - const identities = ['did:alice:123', 'did:bob:456', 'did:alice:123']; - - for (const identity of identities) { - document.dispatchEvent(new CustomEvent('activeDidChanged', { - detail: { activeDid: identity } - })); - await new Promise(resolve => setTimeout(resolve, 100)); // Brief pause - } - - // Ensure plugin ends up in correct state - expect(plugin.currentActiveDid).toBe('did:alice:123'); - }); -}); -``` - -### **Edge Case Testing** -- **Network Failure**: Identity changes during network failures -- **Background Mode**: Identity changes when app is backgrounded -- **Rapid Switches**: Multiple identity changes in quick succession -- **Invalid activeDid**: Change to empty or invalid activeDid - -## Platform-Specific Considerations - -### **Android/Electron** -- Plugin can detect activeDid changes in background via database polling -- Backup mechanism if event-based notification fails - -### **Web** -- Event-based notification is primary mechanism -- Fallback to periodic activeDid checking - -### **iOS** -- Background tasks can check activeDid changes -- Event-based notification for foreground changes - -## Performance Considerations - -### **Change Detection Optimization** -- Debounce rapid identity changes -- Batch cache clearing operations -- Minimize background task restarts - -### **Resource Cleanup** -- Clear authentication tokens immediately -- Clean up cached content efficiently -- Avoid memory leaks during transitions - ---- - -**Status**: Critical requirement documented - Implementation required -**Next Steps**: Add activeDid change listeners to plugin implementation -**Security Impact**: HIGH - Prevents data leakage between user identities diff --git a/doc/BACKGROUND_DATA_FETCHING_PLAN.md b/doc/BACKGROUND_DATA_FETCHING_PLAN.md index 62beeb0..c3f9ac8 100644 --- a/doc/BACKGROUND_DATA_FETCHING_PLAN.md +++ b/doc/BACKGROUND_DATA_FETCHING_PLAN.md @@ -3,26 +3,48 @@ **Author**: Matthew Raymer **Version**: 1.0.0 **Created**: 2025-10-02 07:47:04 UTC -**Last Updated**: 2025-10-02 12:30:00 UTC +**Last Updated**: 2025-10-02 12:45:00 UTC ## Overview -This document outlines the implementation plan for background data fetching in the Capacitor Daily Notification Plugin, replacing web push implementations with native platform solutions. The plan covers Android, iOS, and cross-platform considerations for API data fetching with authentication. +This document outlines the **complete implementation plan** for background data fetching in the Capacitor Daily Notification Plugin, replacing web push implementations with native platform solutions. This consolidated plan includes: -## Platform Architecture Overview +- **Option A Architecture**: Host always provides activeDid approach +- **TimeSafari Integration**: PlatformServiceMixin coordination patterns +- **Cross-Platform Implementation**: Android/Electron, iOS, Web specifications +- **Database Access Patterns**: Clear separation between host and plugin storage +- **ActiveDid Change Management**: Event-based notification and cache invalidation +- **Security Requirements**: Data isolation and authentication patterns + +This document consolidates all previous analysis documents to provide a single source of truth for implementation. + +## Consolidated Architecture: Option A Platform Overview ``` -JavaScript Layer → Native Bridge → Native Background Executor +TimeSafari Host App + ↓ (provides activeDid) +Daily Notification Plugin → Native Background Executor ↓ HTTP Client + Auth ↓ API Server Response ↓ - Parse & Cache Data + Parse & Cache Data (plugin storage) ↓ Trigger Notifications ``` +### **Option A: Host Always Provides activeDid** + +**Core Principle**: Host application queries its own database and provides activeDid to plugin. + +### **Why Option A Is Superior:** +1. **Clear Separation**: Host owns identity management, plugin owns notifications +2. **No Database Conflicts**: Zero shared database access between host and plugin +3. **Security Isolation**: Plugin data physically separated from user data +4. **Platform Independence**: Works consistently regardless of host's database technology +5. **Simplified Implementation**: Fewer moving parts, clearer debugging + ## Android Implementation Strategy ### A. Background Execution Framework @@ -252,6 +274,85 @@ Based on TimeSafari's optimization patterns: - **Cache cleanup** on memory pressure - **Resource lifecycle** management +## Critical Requirement: Plugin Must Know When activeDid Changes + +### **Security Implications of Missing ActiveDid Change Detection** + +**Without immediate activeDid change detection, the plugin faces severe risks:** +- **Cross-User Data Exposure**: Plugin fetches notifications for wrong user after identity switch +- **Unauthorized API Access**: JWT tokens valid for incorrect user context +- **Background Task Errors**: Content fetching operates with wrong identity + +### **Event-Based Solution** + +**Host Application Responsibility**: Dispatch `activeDidChanged` event +```typescript +// TimeSafari PlatformServiceMixin modification +async $updateActiveDid(newDid: string | null): Promise { + // Update TimeSafari's active_identity table (existing logic) + await this.$dbExec( + "UPDATE active_identity SET activeDid = ?, lastUpdated = datetime('now') WHERE id = 1", + [newDid || ""] + ); + + // CRITICAL: Notify plugin of change IMMEDIATELY + document.dispatchEvent(new CustomEvent('activeDidChanged', { + detail: { activeDid: newDid } + })); +} +``` + +**Plugin Responsibility**: Listen and respond to changes +```typescript +// Plugin service layer implementation +plugin.onActiveDidChange(async (newActiveDid) => { + // 1. Clear all cached content for previous identity + await plugin.clearCacheForNewIdentity(); + + // 2. Refresh authentication tokens with new activeDid + await plugin.refreshAuthenticationForNewIdentity(newActiveDid); + + // 3. Restart background tasks with correct identity + await plugin.updateBackgroundTaskIdentity(newActiveDid); + + logger.info(`[DailyNotificationService] ActiveDid updated to: ${newActiveDid}`); +}); +``` + +## TimeSafari Integration Patterns + +### **ActiveDid Management Analysis** + +**TimeSafari's Database Architecture:** +- **Table**: `active_identity` (single row with `id = 1`) +- **Content**: `activeDid TEXT`, `lastUpdated DATETIME` +- **Purpose**: Single source of truth for active user identity + +**Access via PlatformServiceMixin:** +```typescript +// Retrieving activeDid in TimeSafari components +const activeIdentity = await this.$getActiveIdentity(); +const activeDid = activeIdentity.activeDid; + +// Updating activeDid via PlatformServiceMixin +async $updateActiveDid(newDid: string | null): Promise { + await this.$dbExec( + "UPDATE active_identity SET activeDid = ?, lastUpdated = datetime('now') WHERE id = 1", + [newDid || ""] + ); +} +``` + +### **Plugin Hosting Strategy** + +**Existing Plugin Usage**: TimeSafari already uses Capacitor plugins extensively via `CapacitorPlatformService.ts` + +**Recommended Integration Architecture:** +- Plugin integrates as standard Capacitor plugin +- Host provides activeDid via event-driven pattern +- Plugin manages own isolated storage +- Clear separation of responsibilities maintained + ## Integration Points ### Enhanced Plugin Interface for Host Application Integration diff --git a/doc/DATABASE_ACCESS_CLARIFICATION.md b/doc/DATABASE_ACCESS_CLARIFICATION.md deleted file mode 100644 index 3d83d0e..0000000 --- a/doc/DATABASE_ACCESS_CLARIFICATION.md +++ /dev/null @@ -1,208 +0,0 @@ -# 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 diff --git a/doc/TIMESAFARI_INTEGRATION_ANALYSIS.md b/doc/TIMESAFARI_INTEGRATION_ANALYSIS.md deleted file mode 100644 index 14bcac4..0000000 --- a/doc/TIMESAFARI_INTEGRATION_ANALYSIS.md +++ /dev/null @@ -1,343 +0,0 @@ -# TimeSafari Daily Notification Plugin Integration Analysis - -**Author**: Matthew Raymer -**Version**: 1.0.0 -**Created**: 2025-10-02 11:00:00 UTC -**Last Updated**: 2025-10-02 11:00:00 UTC - -## Overview - -This document analyzes how the Daily Notification Plugin would integrate with TimeSafari, focusing on `activeDid` access and plugin hosting architecture. - -## ActiveDid Management in TimeSafari - -### Database Schema: `active_identity` Table - -**Table Structure:** -```sql -CREATE TABLE active_identity ( - id INTEGER PRIMARY KEY DEFAULT 1, - activeDid TEXT NOT NULL, - lastUpdated DATETIME DEFAULT (datetime('now')) -); -``` - -**Key Characteristics:** -- Single row table with `id = 1` always -- `activeDid` contains the currently active user's DID -- `lastUpdated` tracks when the active identity was switched -- Single source of truth for current user identity - -### Access Methods - -**1. Platform Service Level:** -```typescript -// In CapacitorPlatformService, WebPlatformService, ElectronPlatformService -async updateActiveDid(did: string): Promise { - await this.dbExec( - "INSERT OR REPLACE INTO active_identity (id, activeDid, lastUpdated) VALUES (1, ?, ?)", - [did, new Date().toISOString()] - ); -} - -async getActiveIdentity(): Promise<{ activeDid: string }> { - const result = await this.dbQuery( - "SELECT activeDid FROM active_identity WHERE id = 1" - ); - return { - activeDid: (result?.values?.[0]?.[0] as string) || "" - }; -} -``` - -**2. Vue Mixin Level:** -```typescript -// In PlatformServiceMixin.ts -async $updateActiveDid(newDid: string | null): Promise { - await this.$dbExec( - "UPDATE active_identity SET activeDid = ?, lastUpdated = datetime('now') WHERE id = 1", - [newDid || ""] - ); -} - -async $getActiveIdentity(): Promise<{ activeDid: string }> { - const result = await this.$dbExec( - "SELECT activeDid FROM active_identity WHERE id = 1" - ); - return { activeDid: result.activeDid || "" }; -} -``` - -**3. Component Level Usage:** -```typescript -// In Vue components -const activeIdentity = await this.$getActiveIdentity(); -const activeDid = activeIdentity.activeDid || ""; -``` - -## Plugin Integration Options - -### Option 1: Host Application Manages activeDid - -**Pros:** -- Simple integration -- Plugin doesn't need database access -- Clear separation of concerns - -**Implementation:** -```typescript -// In TimeSafari application -import { DailyNotification } from '@timesafari/daily-notification-plugin'; - -// On app initialization or user switch -async function initializeNotifications() { - const activeIdentity = await this.$getActiveIdentity(); - const activeDid = activeIdentity.activeDid; - - if (activeDid) { - await DailyNotification.setActiveDid(activeDid); - await DailyNotification.configure({ - apiServer: 'https://endorser.ch', - activeDid: activeDid, - // ... other config - }); - } -} -``` - -**When to Use:** -- Host application already has database access -- Plugin should be stateless regarding identity -- Simple notification scheduling only - -### Option 2: Plugin Looks Up activeDid - -**Pros:** -- Plugin manages its own identity context -- Automatic activeDid synchronization -- Background tasks have direct access - -**Implementation:** -```typescript -// Plugin has database access via @capacitor-community/sqlite -interface EnhancedDailyNotificationPlugin { - async initializeFromTimeSafari(): Promise { - // Plugin accesses TimeSafari's active_identity table - const result = await this.dbQuery( - "SELECT activeDid FROM active_identity WHERE id = 1" - ); - const activeDid = result?.values?.[0]?.[0] as string; - - await this.setActiveDid(activeDid); - await this.scheduleBackgroundTasks(); - } -} -``` - -**When to Use:** -- Plugin needs autonomous background operation -- Host application integration should be minimal -- Plugin operates independently of app lifecycle - -### Option 3: Hybrid Approach (Recommended) - -**Implementation:** -```typescript -// Host provides activeDid when app is active -// Plugin looks up activeDid for background operations - -interface HybridDailyNotificationPlugin { - // Host-initiated activeDid setting - async setActiveDidFromHost(activeDid: string): Promise; - - // Plugin-initiated activeDid lookup for background - async refreshActiveDidFromDatabase(): Promise; - - // Automatic mode that uses both approaches - async enableAutoActiveDidMode(): Promise; -} - -// In TimeSafari app -async function initializeNotifications() { - const activeIdentity = await this.$getActiveIdentity(); - const activeDid = activeIdentity.activeDid; - - if (activeDid) { - await DailyNotification.setActiveDidFromHost(activeDid); - await DailyNotification.enableAutoActiveDidMode(); - } -} -``` - -## Recommended Plugin Hosting Architecture - -### Service Layer Integration - -**Location**: `src/services/DailyNotificationService.ts` - -```typescript -// src/services/DailyNotificationService.ts -import { DailyNotification } from '@timesafari/daily-notification-plugin'; -import { PlatformServiceFactory } from './PlatformServiceFactory'; - -export class DailyNotificationService { - private plugin: DailyNotification; - private platform = PlatformServiceFactory.getInstance(); - private currentActiveDid: string | null = null; - - async initialize(): Promise { - // Initialize plugin with current active identity - const activeIdentity = await this.platform.getActiveIdentity(); - this.currentActiveDid = activeIdentity.activeDid; - - if (this.currentActiveDid) { - await this.plugin.setActiveDid(this.currentActiveDid); - await this.plugin.configure({ - apiServer: import.meta.env.VITE_ENDORSER_API_SERVER, - storageType: 'plugin-managed', - notifications: { - offers: true, - projects: true, - people: true, - items: true - } - }); - - // Listen for identity changes - this.setupIdentityChangeListener(); - } - } - - private setupIdentityChangeListener(): void { - // Subscribe to identity changes in TimeSafari - document.addEventListener('activeDidChanged', (event: CustomEvent) => { - this.handleActiveDidChange(event.detail.activeDid); - }); - } - - private async handleActiveDidChange(newActiveDid: string): Promise { - if (newActiveDid !== this.currentActiveDid) { - this.currentActiveDid = newActiveDid; - await this.plugin.setActiveDid(newActiveDid); - logger.info(`[DailyNotificationService] ActiveDid updated to: ${newActiveDid}`); - -``` - -### PlatformServiceMixin Integration - -**Location**: `src/utils/PlatformServiceMixin.ts` - -```typescript -// Add to PlatformServiceMixin.ts -async $notifyDailyNotificationService(): Promise { - // Trigger notification service update when activeDid changes - const event = new CustomEvent('activeDidChanged', { - detail: { activeDid: this.currentActiveDid } - }); - document.dispatchEvent(event); -} - -// Modify existing $updateActiveDid method -async $updateActiveDid(newDid: string | null): Promise { - // ... existing logic ... - - // Notify DailyNotification service - await this.$notifyDailyNotificationService(); -} -``` - -### App Initialization Integration - -**Location**: `src/main.ts` or App component - -```typescript -// src/main.ts -import { DailyNotificationService } from '@/services/DailyNotificationService'; - -async function initializeNotifications() { - try { - const notificationService = new DailyNotificationService(); - await notificationService.initialize(); - - logger.info('[App] Daily notification service initialized successfully'); - } catch (error) { - logger.error('[App] Failed to initialize notification service:', error); - } -} - -// Call after platform service initialization -initializePlatform().then(async () => { - await initializeNotifications(); - // ... rest of app initialization -}); -``` - -## Cross-Platform Considerations - -### Android (Capacitor) -- **Database Access**: Plugin uses `@capacitor-community/sqlite` -- **identity Access**: Can read TimeSafari's active_identity table directly -- **Background Tasks**: WorkManager can query active_identity independently - -### Web (absurd-sql) -- **Database Access**: Plugin delegates to host application -- **identity Access**: Host provides activeDid, plugin doesn't access database -- **Background Tasks**: Service Worker coordination with host database - -### iOS (Capacitor) -- **Database Access**: Plugin uses CapacitorSQLite -- **identity Access**: Can read TimeSafari's active_identity table -- **Background Tasks**: BGTaskScheduler coordination - -### Electron -- **Database Access**: Plugin uses `@capacitor-community/sqlite` + filesystem -- **identity Access**: Direct file-based database access -- **Background Tasks**: Electron main process coordination - -## Security Considerations - -### activeDid Isolation -- Plugin should not modify active_identity table -- Plugin should only read current activeDid -- Identity changes should be initiated by TimeSafari host application - -### Token Management -- JWT tokens should use activeDid for both `iss` and `sub` -- Token generation should happen in plugin context -- Host application should not handle authentication tokens - -### Background Execution -- Background tasks should validate activeDid hasn't changed -- Implement cache invalidation when activeDid changes -- Handle edge cases where activeDid becomes empty/invalid - -## Implementation Priority - -### Phase 1: Basic Integration -1. Host-managed activeDid (Option 1) -2. Service layer integration -3. Basic notification scheduling - -### Phase 2: Enhanced Integration -1. Hybrid activeDid management (Option 3) -2. Background database access -3. Automatic identity synchronization - -### Phase 3: Advanced Features -1. Cross-platform optimization -2. Advanced background task coordination -3. Performance monitoring and analytics - -## Success Criteria - -- [ ] Plugin correctly receives activeDid from TimeSafari host -- [ ] Background notifications work across identity changes -- [] Cross-platform consistency maintained -- [ ] Minimal impact on TimeSafari performance -- [ ] Secure isolation between plugin and host database operations - ---- - -**Status**: Architectural analysis complete - Ready for implementation planning -**Next Steps**: Choose integration approach and begin Phase 1 implementation -**Dependencies**: TimeSafari active_identity table access, PlatformServiceMixin integration