feat(docs): complete P2.6 type safety cleanup and P2.7 system invariants
P2.6: Type Safety Cleanup - Replaced 'any' return types in vite-plugin.ts with concrete types (UserConfig, transform return type) - Documented TypeScript mixin 'any[]' exception in PlatformServiceMixin.ts - Audit confirmed: zero 'any' in codebase except documented TS mixin limitation - All external boundaries use 'unknown', all data payloads use 'Record<string, unknown>' P2.7: System Invariants Documentation - Created SYSTEM_INVARIANTS.md documenting all 6 enforced invariants - Added to docs/00-INDEX.md under Policy & Contracts section - Each invariant includes: What, Why, How, Where Progress Docs Updates: - Updated 00-STATUS.md: marked P2.6/P2.7 complete, added type safety invariant note - Updated 01-CHANGELOG-WORK.md: added 2025-12-22 entries for P2.6/P2.7 - Updated 03-TEST-RUNS.md: added P2.6 type safety audit test run - Updated P2-DESIGN.md: marked P2.6 acceptance criteria complete - Updated SYSTEM_INVARIANTS.md: added Type Safety Notes section Baseline Tag: - Created v1.0.11-p0-p1.4-p1.5-p2.6-p2.7-complete TypeScript compilation: ✅ PASSES Build: ✅ PASSES CI: ✅ All checks pass
This commit is contained in:
698
docs/_archive/2025-legacy-doc/BACKGROUND_DATA_FETCHING_PLAN.md
Normal file
698
docs/_archive/2025-legacy-doc/BACKGROUND_DATA_FETCHING_PLAN.md
Normal file
@@ -0,0 +1,698 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user