- Added explicit coverage for activeDid-aware retry policy enhancements - Specified Android DailyNotificationFetchWorker.java modifications for activeDid change detection - Specified web callback-registry.ts enhancements for authentication refresh - Added platform policy unification (android 1min→1hour vs web 1sec→1min standardization) - Added integration with existing circuit breaker and error handling systems This addresses the gap where scheduled event retry enhancements were only mentioned at high level but lacked specific implementation steps.
699 lines
27 KiB
Markdown
699 lines
27 KiB
Markdown
# Background Data Fetching Implementation Plan
|
|
|
|
**Author**: Matthew Raymer
|
|
**Version**: 1.0.0
|
|
**Created**: 2025-10-02 07:47:04 UTC
|
|
**Last Updated**: 2025-10-02 12:45:00 UTC
|
|
|
|
## Overview
|
|
|
|
This document outlines the **enhancement plan** for background data fetching in the Daily Notification Plugin to support TimeSafari integration with Option A architecture. This plan builds upon our existing implementation and adds:
|
|
|
|
- **Option A Enhancement**: ActiveDid-aware authentication on existing infrastructure
|
|
- **TimeSafari Integration**: PlatformServiceMixin coordination with current plugin
|
|
- **Authentication Layer**: JWT/DID support over existing HTTP callback system
|
|
- **Database Enhancement**: Extend current storage with activeDid management
|
|
- **Event & Change Management**: Identity change detection on existing notification system
|
|
- **API Integration**: Endorser.ch endpoints through current ContentFetchConfig
|
|
|
|
This document serves as the enhancement roadmap for adding TimeSafari capabilities to our existing, working plugin.
|
|
|
|
## Current Implementation Baseline
|
|
|
|
### ✅ Already Implemented & Working
|
|
- **Android Storage**: SharedPreferences + SQLite with migration (`DailyNotificationStorage.java`, `DailyNotificationDatabase.java`)
|
|
- **Web Storage**: IndexedDB with comprehensive service worker (`sw.ts`, `IndexedDBManager`)
|
|
- **Callback System**: HTTP/local callbacks with circuit breaker (`callback-registry.ts`)
|
|
- **Configuration**: Database path, TTL, retention settings (`ConfigureOptions`)
|
|
- **ETag Support**: Conditional HTTP requests (`DailyNotificationETagManager.java`)
|
|
- **Dual Scheduling**: Content fetch + user notification separation
|
|
- **Cross-Platform API**: Unified TypeScript interface (`DailyNotificationPlugin`)
|
|
|
|
### ⚠️ Enhancement Required for TimeSafari
|
|
- **ActiveDid Integration**: Add activeDid-awareness to existing authentication
|
|
- **JWT Generation**: Enhance HTTP layer with DID-based tokens
|
|
- **Identity Change Detection**: Add event listeners to existing callback system
|
|
- **Endorser.ch APIs**: Extend `ContentFetchConfig` with TimeSafari endpoints
|
|
- **Platform Auth**: Add Android Keystore/iOS Keychain to existing storage
|
|
|
|
## Consolidated Architecture: Option A Platform Overview
|
|
|
|
```
|
|
TimeSafari Host App
|
|
↓ (provides activeDid)
|
|
Daily Notification Plugin → Native Background Executor
|
|
↓
|
|
HTTP Client + Auth
|
|
↓
|
|
API Server Response
|
|
↓
|
|
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
|
|
|
|
- **Use WorkManager** for reliable background HTTP requests
|
|
- **Enhance** existing Native HTTP clients (already implemented):
|
|
- Extend `DailyNotificationETagManager.java` with JWT headers
|
|
- Add JWT authentication to `DailyNotificationFetcher.java`
|
|
- **Handle Android-specific constraints**: Doze mode, app standby, battery optimization
|
|
|
|
### B. Authentication Enhancement - Extend Current Infrastructure
|
|
|
|
**Goal**: Enhance existing `DailyNotificationETagManager.java` and `DailyNotificationFetcher.java` with JWT authentication
|
|
|
|
```kotlin
|
|
// Enhance existing Android infrastructure with JWT authentication
|
|
class DailyNotificationJWTManager {
|
|
private val storage: DailyNotificationStorage
|
|
private val currentActiveDid: String? = null
|
|
|
|
// Add JWT generation to existing fetcher
|
|
fun generateJWTForActiveDid(activeDid: String, expiresInSeconds: Int): String {
|
|
val payload = mapOf(
|
|
"exp" to (System.currentTimeMillis() / 1000 + expiresInSeconds),
|
|
"iat" to (System.currentTimeMillis() / 1000),
|
|
"iss" to activeDid,
|
|
"aud" to "timesafari.notifications",
|
|
"sub" to activeDid
|
|
)
|
|
return signWithDID(payload, activeDid)
|
|
}
|
|
|
|
// Enhance existing HTTP client with JWT headers
|
|
fun enhanceHttpClientWithJWT(connection: HttpURLConnection, activeDid: String) {
|
|
val jwt = generateJWTForActiveDid(activeDid, 60)
|
|
connection.setRequestProperty("Authorization", "Bearer $jwt")
|
|
connection.setRequestProperty("Content-Type", "application/json")
|
|
}
|
|
}
|
|
|
|
### C. HTTP Request Enhancement - Extend Existing Fetcher
|
|
|
|
**Goal**: Enhance existing `DailyNotificationFetcher.java` with Endorser.ch API support
|
|
|
|
```kotlin
|
|
// Enhance existing DailyNotificationFetcher.java with TimeSafari APIs
|
|
class EnhancedDailyNotificationFetcher : DailyNotificationFetcher {
|
|
private val jwtManager: DailyNotificationJWTManager
|
|
|
|
suspend fun fetchEndorserOffers(activeDid: String, afterId: String?): Result {
|
|
val connection = HttpURLConnection("$apiServer/api/v2/report/offers")
|
|
|
|
// Add JWT authentication to existing connection
|
|
jwtManager.enhanceHttpClientWithJWT(connection, activeDid)
|
|
|
|
// Add Endorser.ch specific parameters
|
|
connection.setRequestProperty("recipientDid", activeDid)
|
|
if (afterId != null) {
|
|
connection.setRequestProperty("afterId", afterId)
|
|
}
|
|
|
|
// Use existing storeAndScheduleNotification method
|
|
return fetchAndStoreContent(connection)
|
|
}
|
|
}
|
|
```
|
|
|
|
## iOS Implementation Strategy
|
|
|
|
### A. Background Execution Framework
|
|
|
|
- **Use BGTaskScheduler** for background HTTP requests
|
|
- **Replace axios** with native iOS HTTP clients:
|
|
- URLSession for HTTP requests
|
|
- Combine framework for async/await patterns
|
|
|
|
### B. Authentication Implementation
|
|
|
|
```swift
|
|
// JWT Generation in iOS
|
|
class JWTHelper {
|
|
func generateJWT(userDid: String, expiresInSeconds: Int) -> String {
|
|
let payload: [String: Any] = [
|
|
"exp": Int(Date().timeIntervalSince1970) + expiresInSeconds,
|
|
"iat": Int(Date().timeIntervalSince1970),
|
|
"iss": userDid
|
|
]
|
|
return signWithDID(payload, userDid)
|
|
}
|
|
}
|
|
```
|
|
|
|
### C. HTTP Request Implementation
|
|
|
|
```swift
|
|
// Background HTTP Task
|
|
class DataFetchTask {
|
|
func fetchData() async {
|
|
let jwt = generateJWT(userDid: activeDid, expiresInSeconds: 60)
|
|
var request = URLRequest(url: apiURL)
|
|
request.setValue("Bearer \(jwt)", forHTTPHeaderField: "Authorization")
|
|
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
|
|
do {
|
|
let (data, _) = try await URLSession.shared.data(for: request)
|
|
let offersResponse = try JSONDecoder().decode(OffersResponse.self, from: data)
|
|
await scheduleNotification(with: offersResponse.data)
|
|
} catch {
|
|
// Handle errors
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## Data Models & Type Safety
|
|
|
|
### Shared TypeScript Interfaces
|
|
|
|
```typescript
|
|
// Definitions for native bridge
|
|
interface OffersResponse {
|
|
data: OfferSummaryRecord[];
|
|
hitLimit: boolean;
|
|
}
|
|
|
|
interface OfferSummaryRecord {
|
|
jwtId: string;
|
|
handleId: string;
|
|
issuedAt: string;
|
|
offeredByDid: string;
|
|
recipientDid: string;
|
|
unit: string;
|
|
amount: number;
|
|
// ... other fields
|
|
}
|
|
```
|
|
|
|
### Native Implementations
|
|
|
|
- **Kotlin sealed classes** for type-safe responses
|
|
- **Swift Codable structs** for JSON parsing
|
|
- **Shared error handling** patterns
|
|
|
|
## Configuration Management
|
|
|
|
### Plugin Configuration
|
|
|
|
```typescript
|
|
interface PluginConfig {
|
|
apiServer: string;
|
|
jwtExpirationSeconds: number;
|
|
requestTimeoutMs: number;
|
|
retryAttempts: number;
|
|
activeDid: string; // Simplified to single active DID
|
|
lastKnownOfferId?: string;
|
|
lastKnownPlanId?: string;
|
|
}
|
|
```
|
|
|
|
### Platform-Specific Settings
|
|
|
|
- **Android**: Manage API keys in `AndroidManifest.xml`, use SharedPreferences for runtime config
|
|
- **iOS**: Use `Info.plist` for static config, UserDefaults for runtime settings
|
|
|
|
## Error Handling & Resilience
|
|
|
|
### Network Error Handling
|
|
|
|
- **Connectivity checks** before making requests
|
|
- **Exponential backoff** for retry scenarios
|
|
- **Circuit breaker pattern** for API failures
|
|
- **Graceful degradation** when offline
|
|
|
|
### Authentication Error Handling
|
|
|
|
- **Token refresh** mechanisms
|
|
- **Fallback to anonymous** requests when authentication fails
|
|
- **Secure credential storage** using platform keychains
|
|
|
|
## Cache & State Management
|
|
|
|
### Data Persistence
|
|
|
|
#### **Platform-Specific Storage Architecture**
|
|
|
|
**Android/Electron Platforms:**
|
|
- **@capacitor-community/sqlite** plugin integration for native SQLite access
|
|
- **Shared plugin database** - Plugin manages its own SQLite database instance
|
|
- **Direct SQL execution** via plugin's `dbExec()` methods for complex queries
|
|
- **Background worker integration** for database operations during content fetch
|
|
|
|
**Web Platform:**
|
|
- **absurd-sql** for SQLite support in browser (managed by host application)
|
|
- **Plugin delegation pattern** - Plugin provides SQL queries, host executes them
|
|
- **IndexedDB fallback** for basic caching when SQLite unavailable
|
|
|
|
**iOS Platform:**
|
|
- **Core Data integration** via native Swift implementation
|
|
- **Background task compatibility** with iOS background refresh constraints
|
|
|
|
### State Synchronization
|
|
|
|
- **JavaScript → Native** configuration updates
|
|
- **Native → JavaScript** status reporting
|
|
- **Cross-platform state consistency**
|
|
- **Background ↔ Foreground** state synchronization
|
|
- **Database logging** for audit trails and debugging
|
|
|
|
### Enhanced Caching Strategy
|
|
|
|
Based on TimeSafari's optimization patterns:
|
|
|
|
- **Batch-oriented processing** for API requests to reduce overhead
|
|
- **Intelligent batching** with configurable timing (max 100ms wait, max 10 items)
|
|
- **Memory-optimized caching** with automatic cleanup (keep last 1000 log entries)
|
|
- **Request deduplication** to prevent redundant API calls
|
|
- **Performance monitoring** with operation timing and metrics collection
|
|
|
|
## Performance Optimizations
|
|
|
|
### Request Optimization
|
|
|
|
- **Deduplication** of identical requests
|
|
- **Batch requests** when possible
|
|
- **Intelligent polling** based on user activity
|
|
|
|
### Memory Management
|
|
|
|
- **Background memory limits** enforcement
|
|
- **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<void> {
|
|
// 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<void> {
|
|
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
|
|
|
|
#### **Database Integration Patterns**
|
|
|
|
**Android/Electron: Host-Provided activeDid Approach (Option A)**
|
|
```typescript
|
|
// Host queries its own database and provides activeDid
|
|
const activeIdentity = await this.$getActiveIdentity(); // Uses host's CapacitorSQLite
|
|
await plugin.setActiveDidFromHost(activeIdentity.activeDid);
|
|
|
|
// Plugin configures its own isolated database
|
|
await plugin.configureDatabase({
|
|
platform: 'android',
|
|
storageType: 'plugin-managed' // Plugin owns its storage
|
|
});
|
|
|
|
// Set up activeDid change listener for future changes
|
|
plugin.onActiveDidChange(async (newActiveDid) => {
|
|
await plugin.clearCacheForNewIdentity();
|
|
await plugin.refreshAuthenticationForNewIdentity(newActiveDid);
|
|
logger.info(`[TimeSafari] ActiveDid changed to: ${newActiveDid}`);
|
|
});
|
|
```
|
|
|
|
**Web: Host-Provided activeDid Approach (Option A)**
|
|
```typescript
|
|
// Host queries its absurd-sql database and provides activeDid
|
|
const activeIdentity = await this.$getActiveIdentity(); // Uses host's absurd-sql
|
|
await plugin.setActiveDidFromHost(activeIdentity.activeDid);
|
|
|
|
// Plugin uses host-delegated storage for its own data
|
|
await plugin.configureDatabase({
|
|
platform: 'web',
|
|
storageType: 'host-managed' // Plugin delegates to host for storage
|
|
});
|
|
|
|
// Plugin operates independently with provided activeDid
|
|
const results = await plugin.executeContentFetch(contentConfig);
|
|
```
|
|
|
|
**iOS: Host-Provided activeDid Approach (Option A)**
|
|
```typescript
|
|
// Host queries its CapacitorSQLite database and provides activeDid
|
|
const activeIdentity = await this.$getActiveIdentity(); // Uses host's CapacitorSQLite
|
|
await plugin.setActiveDidFromHost(activeIdentity.activeDid);
|
|
|
|
// Plugin configures its own Core Data storage
|
|
await plugin.configureDatabase({
|
|
platform: 'ios',
|
|
storageType: 'plugin-managed' // Plugin owns Core Data storage
|
|
});
|
|
|
|
// Plugin operates with provided activeDid, no database sharing needed
|
|
const results = await plugin.executeBackgroundContentFetch();
|
|
```
|
|
|
|
#### **Enhancement Required: Extend Current Plugin Interface**
|
|
|
|
**Current Interface** (already implemented):
|
|
```typescript
|
|
interface DailyNotificationPlugin {
|
|
configure(options: ConfigureOptions): Promise<void>;
|
|
scheduleContentFetch(config: ContentFetchConfig): Promise<void>;
|
|
scheduleUserNotification(config: UserNotificationConfig): Promise<void>;
|
|
// ... existing methods (see definitions.ts)
|
|
}
|
|
```
|
|
|
|
**Enhancement Required** (add to existing interface):
|
|
```typescript
|
|
interface EnhancedDailyNotificationPlugin extends DailyNotificationPlugin {
|
|
// Enhanced configuration with activeDid support
|
|
configure(options: ConfigureOptions & {
|
|
activeDidIntegration?: {
|
|
platform: 'android' | 'ios' | 'web' | 'electron';
|
|
storageType: 'plugin-managed' | 'host-managed';
|
|
};
|
|
}): Promise<void>;
|
|
|
|
// Host-provided activeDid Management (Option A Implementation)
|
|
setActiveDidFromHost(activeDid: string): Promise<void>;
|
|
|
|
// Critical: ActiveDid Change Handling
|
|
onActiveDidChange(callback: (newActiveDid: string) => Promise<void>): void;
|
|
refreshAuthenticationForNewIdentity(activeDid: string): Promise<void>;
|
|
}
|
|
```
|
|
|
|
### Background Scheduling with Hybrid activeDid Management
|
|
|
|
- **Integrate** with existing WorkManager/BGTaskScheduler
|
|
- **Coordinate** API fetch timing with notification schedules
|
|
- **Handle** app lifecycle events (background/foreground)
|
|
- **Implement** host-provided activeDid access (Option A):
|
|
- **Always**: Host provides activeDid via `setActiveDidFromHost()`
|
|
- **No Database Sharing**: Plugin never accesses TimeSafari's active_identity table
|
|
- **Critical**: Plugin **MUST** know when activeDid changes for:
|
|
- **Event-Based Notification**: Host dispatches `activeDidChanged` events
|
|
- **Cache Invalidation**: Clear cached content when user switches identity
|
|
- **Token Refresh**: Generate new JWT tokens with updated active Did
|
|
- **Background Task Coordination**: Restart tasks with new identity context
|
|
- **Maintain** clear separation: Host owns identity management, plugin owns notifications
|
|
|
|
## Migration & Testing Strategy
|
|
|
|
### Gradual Migration
|
|
|
|
1. **Phase 1**: Implement basic HTTP + JWT authentication
|
|
2. **Phase 2**: Add caching and state management
|
|
3. **Phase 3**: Integrate with notification scheduling
|
|
4. **Phase 4**: Add passkey authentication support
|
|
|
|
### Testing Approach with Host-Provided activeDid Management
|
|
|
|
- **Unit tests** for JWT generation and HTTP clients with activeDid
|
|
- **Integration tests** for API endpoint interactions using TimeSafari active_identity patterns
|
|
- **Host-provided activeDid testing**:
|
|
- Test `setActiveDidFromHost()` with TimeSafari PlatformServiceMixin
|
|
- Test host event dispatch and plugin event listening
|
|
- **Critical**: Test `onActiveDidChange()` listener with identity switches
|
|
- Test cache invalidation and token refresh during activeDid changes
|
|
- Verify database isolation between host and plugin
|
|
- **Background testing** on real devices (doze mode, app backgrounding)
|
|
- **Authentication testing** with actual DID credentials from TimeSafari active_identity table
|
|
- **Cross-platform testing** for Android/Electron (SQLite access) vs Web (host delegation) patterns
|
|
|
|
## API Endpoints to Support
|
|
|
|
### Offers to User Endpoint
|
|
|
|
```
|
|
GET {apiServer}/api/v2/report/offers?recipientDid={userDid}&afterId={jwtId}&beforeId={jwtId}
|
|
```
|
|
|
|
**Response Structure:**
|
|
```json
|
|
{
|
|
"data": Array<OfferSummaryRecord>,
|
|
"hitLimit": boolean
|
|
}
|
|
```
|
|
|
|
### Offers to User Projects Endpoint
|
|
|
|
```
|
|
GET {apiServer}/api/v2/report/offersToPlansOwnedByMe?afterId={jwtId}&beforeId={jwtId}
|
|
```
|
|
|
|
**Response Structure:**
|
|
```json
|
|
{
|
|
"data": Array<OfferToPlanSummaryRecord>,
|
|
"hitLimit": boolean
|
|
}
|
|
```
|
|
|
|
## Authentication Implementation Strategy
|
|
|
|
### Option 1: Simple DID Authentication (Basic)
|
|
|
|
- Generate traditional JWT using DID signing
|
|
- Short-lived tokens (60 seconds)
|
|
- Suitable for basic notification data fetching
|
|
- Use `did-jwt` library for token generation and verification
|
|
- Based on TimeSafari's existing JWT implementation patterns
|
|
|
|
### Option 2: Enhanced Passkey Authentication (Advanced)
|
|
|
|
- Leverage device biometrics/security keys
|
|
- Longer-lived tokens with automatic refresh
|
|
- Support for cached authentication state
|
|
- Better user experience for frequent polling
|
|
- Integrate with SimpleWebAuthn for cross-platform biometric support
|
|
- Support JWANT tokens (JWT + WebAuthn) for enhanced security
|
|
|
|
## Platform-Specific Considerations
|
|
|
|
### Android Considerations
|
|
|
|
- Use OkHttp or native Android HTTP clients
|
|
- Handle certificate pinning if required
|
|
- Support Android Keystore for secure key storage
|
|
- Handle biometric prompt integration for passkeys
|
|
|
|
### iOS Considerations
|
|
|
|
- Use URLSession for HTTP requests
|
|
- Support iOS Keychain for authentication tokens
|
|
- Handle Face ID/Touch ID integration for passkeys
|
|
- Support certificate pinning if required
|
|
- Use BGTaskScheduler for reliable background execution
|
|
- Handle iOS-specific background refresh restrictions
|
|
- Support Core Data for notification metadata persistence
|
|
|
|
## Data Flow Integration Points
|
|
|
|
### Token Generation
|
|
|
|
- Accept activeDid as input
|
|
- Generate JWT authentication token using DID signing
|
|
- Include activeDid as both issuer (`iss`) and subject (`sub`)
|
|
- Return token for immediate use or caching
|
|
|
|
### Request Execution
|
|
|
|
- Construct full API URLs with query parameters
|
|
- Apply authentication headers
|
|
- Execute HTTP requests with proper error handling
|
|
- Return structured response data
|
|
|
|
### Caching Strategy
|
|
|
|
- Support token caching with expiration management
|
|
- Implement request deduplication for same endpoints
|
|
- Support cache invalidation for authentication failures
|
|
|
|
## Implementation Phases
|
|
|
|
### Phase 1: Extend Core Infrastructure (Building on Existing)
|
|
|
|
- **Extend existing** Android `DailyNotificationFetcher.java` with JWT authentication
|
|
- **Enhance existing** iOS implementation (when added) with authentication layer
|
|
- **Add JWT generation** to existing `DailyNotificationETagManager.java`
|
|
- **Enhance current** `ConfigureOptions` with activeDid integration
|
|
- **Build on existing** error handling (circuit breaker already implemented)
|
|
|
|
### Phase 2: ActiveDid Integration & TimeSafari API Enhancement
|
|
|
|
- **Add** host-provided activeDid management to existing plugin interface
|
|
- **Extend** existing `configure()` method with activeDid options
|
|
- **Enhance** existing `ContentFetchConfig` with Endorser.ch API endpoints
|
|
- **Add** `setActiveDidFromHost()` and `onActiveDidChange()` to existing interface
|
|
- **Integrate** existing `callback-registry.ts` with activeDid-aware callbacks
|
|
- **Enhance** existing platform storage:
|
|
- **Android**: Extend existing SQLite with activeDid-aware JWT storage
|
|
- **Web**: Enhance existing IndexedDB with activeDid support (no host delegation needed initially)
|
|
|
|
### Phase 3: Background Enhancement & TimeSafari Coordination
|
|
|
|
- **Enhance** existing WorkManager integration with activeDid-aware workers
|
|
- **Coordinate** existing notification scheduling with TimeSafari PlatformServiceMixin
|
|
- **Extend** existing app lifecycle handling with activeDid change detection
|
|
- **Enhance** existing state synchronization with identity management
|
|
- **Critical: Enhance retry policies** for activeDid changes:
|
|
- **Android**: Modify `DailyNotificationFetchWorker.java` retry logic to detect activeDid changes during retry sequence
|
|
- **Web**: Enhance `callback-registry.ts` to refresh authentication before retry attempts
|
|
- **Unify**: Standardize retry delays across platforms (Android 1min→1hour vs Web 1sec→1min)
|
|
- **Integrate activeDid change detection** into existing circuit breaker and error handling systems
|
|
|
|
### Phase 4: TimeSafari Integration & Advanced Features
|
|
|
|
- **Integrate** with TimeSafari's existing PlatformServiceMixin patterns
|
|
- **Add** Endorser.ch API endpoint support to existing `ContentFetchConfig`
|
|
- **Implement** DID-based authentication alongside existing callback system
|
|
- **Enhance** existing testing with TimeSafari-specific scenarios
|
|
|
|
## Current Scheduled Event Update Policies
|
|
|
|
### ✅ **Existing Consistent Policies**
|
|
- **Retry Logic**: Exponential backoff with platform-specific limits (Android: 5 retries, Web: 5 retries)
|
|
- **Circuit Breaker**: Opens after 5 consecutive failures
|
|
- **Fallback Content**: Uses cached/emergency content when all retries fail
|
|
- **ETag Updates**: Conditional requests with 304 Not Modified handling
|
|
- **Error Classification**: Network/Storage errors retryable, Permission/Config errors not retryable
|
|
|
|
### ⚠️ **Enhancement Required for TimeSafari Integration**
|
|
- **ActiveDid Change Detection**: Handle identity switches during scheduled events
|
|
- **Authentication Refresh**: Update JWT tokens for ongoing retry attempts
|
|
- **Cache Invalidation**: Clear cached content when activeDid changes
|
|
- **Platform Policy Unification**: Standardize retry delays and fallback mechanisms
|
|
|
|
### **TimeSafari-Aware Update Policy**
|
|
```typescript
|
|
interface TimeSafariUpdatePolicy extends ContentFetchConfig {
|
|
activeDidAwareRetry?: {
|
|
maxRetriesDuringActiveDidChange: number; // More retries during identity change
|
|
authenticationRefreshDelay: number; // Time to refresh auth before retry
|
|
cacheInvalidationOnChange: boolean; // Clear cache when activeDid changes
|
|
};
|
|
}
|
|
```
|
|
|
|
## Success Criteria
|
|
|
|
- [ ] **Functional Requirements**: API data fetching works reliably in background with activeDid awareness
|
|
- [ ] **Performance Requirements**: Requests complete within 30 seconds, including authentication refresh
|
|
- [ ] **Security Requirements**: ActiveDid-based authentication with token refresh during retries
|
|
- [ ] **Reliability Requirements**: Enhanced retry policies that handle activeDid changes gracefully
|
|
- [ ] **Integration Requirements**: Seamless integration with existing plugin APIs + TimeSafari patterns
|
|
- [ ] **Testing Requirements**: Comprehensive test coverage including activeDid change scenarios
|
|
- [ ] **Authentication Requirements**: DID-based JWT with automatic refresh during scheduled events
|
|
- [ ] **Optimization Requirements**: Intelligent retry policies based on error type and activeDid state
|
|
- [ ] **Logging Requirements**: Structured logging with activeDid context and retry state tracking
|
|
- [ ] **Cross-Platform Requirements**: Unified activeDid-aware retry and fallback mechanisms
|
|
|
|
## Risks & Mitigation
|
|
|
|
### Technical Risks
|
|
|
|
- **Background execution limits**: Mitigated by using platform-specific background task systems
|
|
- **Authentication complexity**: Mitigated by implementing gradual migration path
|
|
- **Cross-platform consistency**: Mitigated by shared interfaces and careful API design
|
|
|
|
### Timeline Risks
|
|
|
|
- **Platform-specific complexity**: Mitigated by prioritizing Android first, then iOS
|
|
- **Testing complexity**: Mitigated by automated testing and CI/CD integration
|
|
- **Integration challenges**: Mitigated by maintaining backward compatibility
|
|
|
|
---
|
|
|
|
**Status**: Enhancement plan for existing implementation (Option A) - Ready for implementation
|
|
**Next Steps**: Begin Phase 1 - enhance existing Android HTTP infrastructure with JWT authentication
|
|
**Dependencies**: Existing plugin infrastructure, Android Studio, Capacitor CLI, TimeSafari PlatformServiceMixin
|
|
**Enhancement Approach**:
|
|
- **Build on Existing**: Leverage current SQLite, IndexedDB, callback system, and dual scheduling
|
|
- **Option A Integration**: Add activeDid management to existing configuration and HTTP layers
|
|
- **TimeSafari Enhancement**: Extend current ContentFetchConfig with Endorser.ch API endpoints
|
|
- **Authentication Layer**: Add JWT/DID authentication over existing HTTP infrastructure
|
|
- **Event Integration**: Enhance existing callback system with activeDid change detection
|