32 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	TODO: Test App Native Fetcher Improvements
Created: 2025-10-31
Updated: 2025-10-31 (implementation complete: ES256K JWT generation and network security config)
Context: Review of TestNativeFetcher.java implementation for endorser API integration
Status: โ
 IMPLEMENTATION COMPLETE - All high-priority items complete. Item #1 (ES256K JWT generation) implemented using did-jwt and ethers. Item #15 (Network Security Config) created. All TypeScript/Java interfaces updated. Plugin rebuilt with updated definitions.
๐ Progress Summary
โ Completed High-Priority Items
- Item #1: JWT Algorithm Investigation - โ COMPLETE (ES256K requirement confirmed, implementation pending)
 - Item #2: SharedPreferences Persistence - โ COMPLETE
 - Item #3: Error Handling and Retry Logic - โ COMPLETE
 - Item #6: Context Management - โ COMPLETE
 
โ Critical Implementation Complete (Item #1)
- ES256K JWT Token Generation - โ
 COMPLETE: Implemented 
generateEndorserJWT()function usingdid-jwtandetherslibraries. Generates proper ES256K signed JWTs from test user zero's seed phrase. Native fetcher now receives pre-generated tokens, avoiding Java DID library complexity. 
๐ก Medium Priority Status
- Item #4: API Response Structure Validation - โ VERIFIED (structure verified, validation improvements needed - see details below)
 - Item #5: Comprehensive Logging - Partial (basic logging exists, more comprehensive needed)
 
๐ข Low Priority
- Items #7-11: All pending
 
๐ Configuration Tasks
- Item #13: Real API Calls - Ready (can now enable localhost mode, ES256K JWT generation complete)
 - Item #14: TypeScript Types - โ
 COMPLETE (plugin rebuilt, types updated, 
as anyremoved) - Item #15: Network Security Config - โ COMPLETE (config created, AndroidManifest updated)
 
๐ด High Priority
1. โ ๏ธ JWT Generation and Verification Status
- JWT Generation: โ COMPLETE - TypeScript generates ES256K signed JWTs correctly
 - JWT Verification: ๐ก PARTIAL - Generation works, but server verification fails
 
Current Status (2025-10-31):
โ JWT Generation (TypeScript):
- Using 
did-jwtlibrary with ES256K algorithm โ - Signing with Ethereum private key from seed phrase โ
 - Correct payload structure (
iat,exp,iss,sub,aud) โ - Location: 
test-user-zero.tsโgenerateEndorserJWT() 
๐ก JWT Verification Issue:
- Error: 
JWT failed verification: Error: invalid_signature: no matching public key found - Root Cause: Server cannot resolve DID to get public key for signature verification
 - Server uses: 
didJwt.verifyJWT(jwt, {resolver})which requires DID resolution - The JWT signature is cryptographically valid, but server can't verify it
 
Problem: DID did:ethr:0x0000694B58C2cC69658993A90D3840C560f2F51F may not be:
- Registered on the resolver/blockchain that the server uses
 - Accessible by the server's DID resolver
 - Configured correctly in the test API server
 
Next Steps (to be verified):
- Verify DID registration: Ensure User Zero's DID is registered on resolver server uses
 - Test with known DID: Try a DID that definitely exists in server's system
 - Check server resolver config: Verify server's DID resolver can resolve 
did:ethr:DIDs - Check test API server: If using 
localhost:3000, confirm it supports DID-based JWT verification 
Verification Status Table:
| Component | Status | Notes | 
|---|---|---|
| JWT Algorithm | โ Correct | ES256K (matches server expectation) | 
| JWT Format | โ Correct | Proper structure and claims | 
| JWT Signature | โ Valid | Signature is cryptographically valid | 
| DID Resolution | โ Failing | Server can't resolve DID to public key | 
| Overall Status | ๐ก Partial | Generation works; verification fails due to DID resolution | 
Investigation Results (See INVESTIGATION_JWT_ALGORITHM_RESULTS.md for full details):
โ CONFIRMED: Endorser-ch API uses DID-based JWTs (ES256K), NOT HMAC-SHA256.
TimeSafari Implementation (~/projects/timesafari/crowd-funder-for-time-pwa/src/libs/crypto/vc/index.ts):
- Uses 
did-jwt.createJWT()withSimpleSigner(privateKeyHex) - Signs with Ethereum private key from DID identity
 - Algorithm: ES256K (default for did-jwt library)
 
endorser-ch Verification (~/projects/timesafari/endorser-ch/src/api/services/vc/index.js):
- Uses 
did-jwt.verifyJWT(jwt, {resolver})for verification - Verifies signature using DID resolver (resolves DID to public key)
 - NO shared secret used - authentication is DID-based
 
โ IMPLEMENTATION COMPLETE:
- TypeScript generates ES256K signed JWTs using 
generateEndorserJWT() - Native fetcher receives pre-generated JWT token via 
configureNativeFetcher() - No Java DID library needed - token generated in TypeScript with 
did-jwtlibrary 
Current Architecture:
- TypeScript: 
generateEndorserJWT()โ Creates ES256K signed JWT - TypeScript: 
DailyNotification.configureNativeFetcher({ jwtToken })โ Passes token to native - Android: 
TestNativeFetcher.configure()โ Stores JWT token - Android: Background worker uses stored JWT token for API requests
 
Remaining Issue: DID resolution on server side (see verification status above)
Previous Implementation (REMOVED):
HMAC-SHA256 with shared secretโ Replaced with ES256K DID-basedJava DID library implementationโ Not needed (TypeScript generates token)
Implementation Options (NO LONGER NEEDED - TypeScript handles it):
Option 1: Use did-jwt-java Library (Recommended)
// DEPRECATED - TypeScript generates JWT now
// No longer needed in Java code
Option 2: Use web3j for Ethereum Signing
// DEPRECATED - TypeScript generates JWT now
// No longer needed in Java code
    keyPair
);
// Then encode signature according to ES256K format for JWT
References:
- Investigation Results: See 
INVESTIGATION_JWT_ALGORITHM_RESULTS.mdfor complete findings - TimeSafari Implementation: 
~/projects/timesafari/crowd-funder-for-time-pwa/src/libs/crypto/vc/index.ts-createEndorserJwtForKey()function - endorser-ch Verification: 
~/projects/timesafari/endorser-ch/src/api/services/vc/index.js-decodeAndVerifyJwt()function - did-jwt library - DID-based JWT library (JavaScript reference)
 - did-jwt-java - Java implementation (if available) or alternative Java DID libraries
 
Impact: CRITICAL - Current HMAC-SHA256 implementation will be rejected by API server. All authentication will fail until DID-based ES256K signing is implemented.
โ ARCHITECTURAL DECISION: Generate JWT in TypeScript (host app), pass token to native fetcher
Rationale:
- TimeSafari already has 
did-jwtlibrary available in TypeScript - TimeSafari can access DID private keys via Capacitor SQLite (
retrieveFullyDecryptedAccount()) - Plugin SPI pattern is designed for host app to provide implementations
 - Avoids complexity of finding/implementing Java DID libraries
 - Keeps cryptography in TypeScript where libraries are mature
 
Updated Implementation Approach:
Option A: Generate JWT in TypeScript, Pass Token (RECOMMENDED)
// In HomeView.vue or TimeSafari app
import { createEndorserJwtForDid } from '@/libs/endorserServer';
// Generate JWT in TypeScript using existing TimeSafari infrastructure
const account = await retrieveFullyDecryptedAccount(activeDid);
const jwtToken = await createEndorserJwtForDid(activeDid, {
  exp: Math.floor(Date.now() / 1000) + 60,
  iat: Math.floor(Date.now() / 1000),
  iss: activeDid
});
// Pass JWT token to native fetcher (no private key needed)
await DailyNotification.configureNativeFetcher({
  apiBaseUrl: 'http://10.0.2.2:3000',
  activeDid: activeDid,
  jwtToken: jwtToken  // Pre-generated token, not secret
});
// In TestNativeFetcher.java - Just use the token, don't generate it
private volatile String jwtToken; // Pre-generated token from TypeScript
public void configure(String apiBaseUrl, String activeDid, String jwtToken) {
    this.apiBaseUrl = apiBaseUrl;
    this.activeDid = activeDid;
    this.jwtToken = jwtToken; // Use pre-generated token
}
private String getAuthorizationHeader() {
    return "Bearer " + jwtToken;
}
Benefits:
- โ No Java DID library needed
 - โ
 Uses existing TimeSafari infrastructure (
did-jwt,retrieveFullyDecryptedAccount) - โ Keeps cryptography in TypeScript (where it's proven to work)
 - โ Native fetcher just uses token (simple, no crypto complexity)
 - โ Token can be refreshed in TypeScript when needed
 
Next Actions:
- Update 
configureNativeFetcher()TypeScript signature to acceptjwtTokeninstead ofjwtSecret - Update Java 
configureNativeFetcher()to acceptjwtTokenparameter - Remove JWT generation code from 
TestNativeFetcher.generateJWTToken() - Use pre-generated token in 
getAuthorizationHeader()method - Update 
HomeView.vueto generate JWT using TimeSafari'screateEndorserJwtForDid() - For TimeSafari integration: Generate JWT in host app using account data from SQLite
 - Test with real endorser-ch API to verify tokens are accepted
 
Testing:
- โ Verified algorithm with endorser-ch source code - ES256K confirmed
 - โ
 Generate JWT in TypeScript using 
did-jwtlibrary (ES256K signing) - โ
 Pass pre-generated JWT token to native fetcher via 
configureNativeFetcher() - โ ๏ธ Verify tokens are accepted by the endorser API endpoint (pending real API testing)
 - Implement token refresh mechanism in TypeScript when tokens expire (60 seconds)
 
Implementation Checklist:
1. Update TypeScript Interface (src/definitions.ts):
- โ
 Change 
jwtSecret: stringโjwtToken: stringinconfigureNativeFetcher()signature - โ Update JSDoc to reflect that token is pre-generated, not a secret
 - โ
 Rebuild plugin to update 
dist/definitions.d.ts 
2. Update Java Plugin (android/plugin/.../DailyNotificationPlugin.java):
- โ
 Change 
String jwtSecret = call.getString("jwtSecret")โString jwtToken = call.getString("jwtToken") - โ Update validation message: "Missing required parameters: apiBaseUrl, activeDid, and jwtToken are required"
 - โ
 Update 
fetcher.configure(apiBaseUrl, activeDid, jwtToken)call - โ Update Javadoc to reflect token-based approach
 
3. Update Native Interface (android/plugin/.../NativeNotificationContentFetcher.java):
- โ
 Change 
default void configure(String apiBaseUrl, String activeDid, String jwtSecret)โdefault void configure(String apiBaseUrl, String activeDid, String jwtToken) - โ Update Javadoc to explain token is pre-generated by TypeScript
 
4. Update TestNativeFetcher (test-apps/.../TestNativeFetcher.java):
- โ
 Change 
private volatile String jwtSecret;โprivate volatile String jwtToken; - โ
 Update 
configure()method signature and implementation - โ
 Remove entire 
generateJWTToken()method (HMAC-SHA256 implementation removed) - โ
 Update 
fetchContentWithRetry()to usejwtTokendirectly - โ
 Remove JWT-related constants (
JWT_EXPIRATION_MINUTES) - no longer needed - โ
 Remove HMAC-SHA256 imports (
Mac,SecretKeySpec,Base64,MessageDigest) - not used elsewhere 
5. Update HomeView.vue (test-apps/.../HomeView.vue):
- โ
 Implement ES256K JWT generation using 
did-jwtandetherslibraries - โ
 Generate JWT before calling 
configureNativeFetcher()usinggenerateEndorserJWT() - โ
 Update 
configureNativeFetcher()call to usejwtTokeninstead ofjwtSecret - โ
 Remove 
as anytype assertion after plugin rebuild 
6. ES256K JWT Generation Implementation (test-apps/.../test-user-zero.ts):
- โ
 Add 
did-jwt@^7.0.0andethers@^6.0.0dependencies - โ
 Implement 
generateEndorserJWT()function:- Derives Ethereum private key from seed phrase using 
ethers.HDNodeWallet - Uses 
did-jwt.SimpleSignerfor ES256K signing - Creates proper ES256K signed JWTs matching TimeSafari's pattern
 
 - Derives Ethereum private key from seed phrase using 
 - โ
 Export 
generateEndorserJWTfor use in HomeView.vue 
6. For TimeSafari Production Integration:
- In TimeSafari app, generate JWT using 
createEndorserJwtForKey()with account from SQLite - Call 
DailyNotification.configureNativeFetcher()with generated token - Implement token refresh logic when tokens expire (60 seconds)
 - Handle token expiration errors and regenerate tokens
 
7. Documentation Updates:
- Update 
docs/NATIVE_FETCHER_CONFIGURATION.mdto reflect token-based approach - Document how to generate tokens in TypeScript using TimeSafari infrastructure
 - Remove references to 
jwtSecretand HMAC-SHA256 
2. Implement SharedPreferences Persistence for Configuration
- Status: โ COMPLETE
 
Current State: โ
 Implemented - getStarredPlanIds() and getLastAcknowledgedJwtId() use SharedPreferences
Location: TestNativeFetcher.java โ getStarredPlanIds(), getLastAcknowledgedJwtId()
Implementation Status: โ COMPLETE
- โ
 SharedPreferences initialized in constructor with 
PREFS_NAME = "DailyNotificationPrefs" - โ
 
getStarredPlanIds()- Loads JSON array from SharedPreferences - โ
 
getLastAcknowledgedJwtId()- Loads JWT ID from SharedPreferences - โ
 
updateStarredPlanIds()- Saves plan IDs to SharedPreferences - โ
 
updateLastAckedJwtId()- Saves JWT ID to SharedPreferences - โ
 Context passed to constructor and stored as 
appContext 
Impact: โ Proper state management and pagination support enabled
3. Add Error Handling and Retry Logic
- Status: โ COMPLETE
 
Current State: โ
 Implemented - fetchContentWithRetry() with exponential backoff
Location: TestNativeFetcher.java โ fetchContentWithRetry()
Implementation Status:
- โ
 Retry logic for network failures (transient errors) - 
fetchContentWithRetry()withMAX_RETRIES = 3 - โ
 Distinguish retryable vs non-retryable errors - 
shouldRetry()method checks response codes and retry count - โ Log error details for debugging - Detailed logging with retry counts and error messages
 - โ
 Exponential backoff for retries - 
RETRY_DELAY_MS * (1 << retryCount)implemented 
Suggested Implementation:
private static final int MAX_RETRIES = 3;
private static final int RETRY_DELAY_MS = 1000;
private CompletableFuture<List<NotificationContent>> fetchContentWithRetry(
    FetchContext context, int retryCount) {
    // ... existing fetch logic ...
    
    if (responseCode == 401 || responseCode == 403) {
        // Non-retryable - authentication issue
        Log.e(TAG, "Authentication failed - check credentials");
        return CompletableFuture.completedFuture(Collections.emptyList());
    }
    
    if (responseCode >= 500 && retryCount < MAX_RETRIES) {
        // Retryable - server error
        Log.w(TAG, "Server error " + responseCode + ", retrying... (" + retryCount + "/" + MAX_RETRIES + ")");
        Thread.sleep(RETRY_DELAY_MS * (1 << retryCount)); // Exponential backoff
        return fetchContentWithRetry(context, retryCount + 1);
    }
}
Impact: Improves reliability for transient network issues
๐ก Medium Priority
4. Validate API Response Structure (Need to Verify Actual Structure)
- Status: โ VERIFIED - Endpoint structure verified, parser validation needs improvement
 
Current State: Parser handles both flat and nested structures, but could add more validation
Location: TestNativeFetcher.java โ parseApiResponse()
โ Endpoint Structure Verified:
- 
โ Check endorser-ch endpoint implementation:
- โ
 Found 
/api/v2/report/plansLastUpdatedBetweenhandler in endorser-ch source:~/projects/timesafari/endorser-ch/src/api/controllers/report-router.js - โ
 Verified actual response structure - Returns 
{ data: [...], hitLimit: boolean }where data items haveplanandwrappedClaimBeforeproperties - โ
 Response matches 
PlanSummaryAndPreviousClaimstructure - Items haveplan.handleId,plan.jwtId, andwrappedClaimBeforeproperties 
 - โ
 Found 
 - 
โ Current parser handles:
- โ
 Both flat structure (
jwtId,planId) and nested structure (plan.jwtId,plan.handleId) - โ ๏ธ Missing: Does NOT extract 
wrappedClaimBeforefrom response (exists in API response but not used in parser) 
 - โ
 Both flat structure (
 
โ Verification Results:
Null Checks Status:
- โ
 Uses 
.has()checks before accessing fields (planId, jwtId) - Lines 502, 511 - โ
 Checks 
item.has("plan")before accessing plan object - Lines 504, 513 - โ
 Checks 
dataArray != nullafter getting it - Line 491 - โ Handles null values safely (planId and jwtId can be null) - Lines 499-518
 - โ Wrapped in try-catch for error handling - Line 485, 570
 - โ ๏ธ Missing: No check for 
root.has("data")before callinggetAsJsonArray("data")(line 490) - Could throwIllegalStateExceptionif "data" field missing or wrong type - โ ๏ธ Potential Issue: 
getAsJsonObject("plan")(lines 505, 514) could throw if "plan" exists but is not a JsonObject (currently caught by try-catch) 
Structure Validation Status:
- โ ๏ธ Missing: No validation that root has "data" field before accessing (line 490)
 - โ ๏ธ Missing: No warning logged if "data" field is missing or wrong type (would be caught by try-catch, but no specific warning)
 - โ Handles missing/null data gracefully (creates default notification if contents empty) - Line 554-568
 - โ Handles parse errors gracefully (returns empty list on exception) - Line 570-573
 
Nested Structure Handling:
- โ
 Handle nested structures correctly - Parser handles both 
item.plan.handleIdanditem.planIdformats - โ
 Handles both flat (
jwtId,planId) and nested (plan.jwtId,plan.handleId) structures 
Remaining Work:
- Add 
root.has("data")check before accessing data array (line 490 - currently could throw if "data" missing) - Add null check for 
planobject before accessing nested fields (line 505, 514 - currently relies on try-catch) - Log warnings for unexpected response formats (missing "data" field, wrong types)
 - Extract 
wrappedClaimBeforefrom response items (API provides this but parser doesn't use it) 
Suggested Improvements:
private List<NotificationContent> parseApiResponse(String responseBody, FetchContext context) {
    try {
        JsonParser parser = new JsonParser();
        JsonObject root = parser.parse(responseBody).getAsJsonObject();
        
        // Validate response structure
        if (!root.has("data")) {
            Log.w(TAG, "API response missing 'data' field");
            return Collections.emptyList();
        }
        
        JsonArray dataArray = root.getAsJsonArray("data");
        if (dataArray == null) {
            Log.w(TAG, "API response 'data' field is not an array");
            return Collections.emptyList();
        }
        
        // ... rest of parsing with null checks ...
    }
}
Impact: Prevents crashes on unexpected API responses
5. Add Comprehensive Logging for Debugging
- Status: Partial - Basic logging exists, more comprehensive logging needed
 
Current State: Basic logging present (Log.d/i/e/w), but could be more detailed and structured
Location: Throughout TestNativeFetcher.java
Current Implementation:
- โ Uses Log.d/i/e/w for different severity levels
 - โ Some structured tags (e.g., "TestNativeFetcher")
 - โ Logs error details and retry information
 
Required Changes:
- Add more comprehensive log levels (DEBUG, INFO, WARN, ERROR) - Basic levels exist but could be more systematic
 - Log request/response details (truncated for sensitive data) - Some logging exists, could be more comprehensive
 - Log timing information for performance monitoring - Not currently implemented (timing would be helpful)
 - Add structured logging tags for filtering - Basic tags exist, could be more structured with consistent prefixes
 
Suggested Logging Points:
- Configuration received
 - Fetch start/end with timing
 - HTTP request details (URL, method, headers - sanitized)
 - Response code and size
 - Parse success/failure
 - Number of notifications created
 
Example:
Log.d(TAG, String.format("FETCH_START trigger=%s scheduledTime=%d fetchTime=%d", 
    context.trigger, context.scheduledTime, context.fetchTime));
    
long startTime = System.currentTimeMillis();
// ... radius logic ...
long duration = System.currentTimeMillis() - startTime;
Log.i(TAG, String.format("FETCH_COMPLETE duration=%dms items=%d", 
    duration, contents.size()));
6. Implement Proper Context Management
- Status: โ COMPLETE
 
Current State: โ Context passed to constructor and stored
Location: TestNativeFetcher.java constructor, TestApplication.java
Implementation Status:
- โ
 Pass 
Applicationcontext toTestNativeFetcherconstructor - Done inTestApplication.onCreate() - โ
 Store context for SharedPreferences access - Stored as 
appContextand used for SharedPreferences - โ
 Using application context (no leaks) - 
context.getApplicationContext()used 
Example:
// In TestNativeFetcher:
private final Context appContext;
public TestNativeFetcher(Context context) {
    this.appContext = context.getApplicationContext(); // Use app context
    // ... initialize SharedPreferences ...
}
// In TestApplication.onCreate():
Context context = getApplicationContext();
TestNativeFetcher fetcher = new TestNativeFetcher(context);
๐ข Low Priority / Nice to Have
7. Add Unit Tests
- Status: Not started
 
Location: Create TestNativeFetcher/CONTENT_FETCHERTest.java
Coverage Needed:
- JWT token generation (verify signature format)
 - API response parsing (valid and invalid responses)
 - Error handling (network failures, invalid responses)
 - Configuration updates
 - SharedPreferences integration
 
Test Framework: JUnit 4/5 + Mockito
8. Extract JWT Generation to Utility Class
- Status: Not started
 
Current State: JWT generation code embedded in fetcher
Benefit: Reusable across other parts of the app, easier to test
Location: Create test-apps/daily-notification-test/android/app/src/main/java/com/timesafari/dailynotification/test/JwtUtils.java
9. Add Request/Response Interceptors for Development
- Status: Not started
 
Purpose: Log full request/response for API debugging
Implementation:
- Add methods to log full HTTP requests/responses (with data sanitization)
 
Note: Only enable in debug builds for security
10. Support API Response Caching
- Status: Not started
 
Current State: Always fetches from API
Enhancement:
- Cache successful responses with TTL
 - Use cached data if available and fresh
 
Use Case: Reduce API calls when multiple notifications scheduled close together
11. Add Metrics/Telemetry
- Status: Not started
 
Purpose: Track fetch success rates, latency, error types
Implementation:
- Record metrics to SharedPreferences or analytics service
 - Track: success/failure rate, average response time, error distribution
 - Log summary periodically
 
12. โ MOVED TO HIGH PRIORITY: Verify API Response Structure
This item has been moved to High Priority item #4 - Validate API Response Structure
Action Required (covered in item #4 above):
- โ Check endorser-ch source code for actual endpoint response structure
 - โ
 Compare with 
PlansLastUpdatedResponseandPlanSummaryWithPreviousClaiminterfaces in codebase - โ Update parser if structure differs - Parser handles both flat and nested structures
 - Add validation for required fields - Additional validation still needed
 - Test with real API responses from endorser-ch server - Pending real API testing
 
Location:
- Check endorser-ch source: 
src/orserver/directories for endpoint handler - Check 
src/definitions.tsin this codebase for expected interface structure - Test with real responses from running endorser-ch server
 
๐ Configuration & Setup Tasks
13. Enable Real API Calls in Test Config
- Status: Ready to enable - ES256K JWT generation complete, network config ready
 
Current State: TEST_USER_ZERO_CONFIG.api.serverMode = "mock"
Ready to Enable: ES256K JWT generation is complete, network security config is in place
Action:
- Change 
serverModeto"localhost"intest-user-zero.ts(line 28) for emulator testing - Test API calls to 
http://10.0.2.2:3000with real ES256K signed tokens - Verify tokens are accepted by endorser-ch API
 - Update to 
"staging"or"production"for real environment testing when ready 
Location: test-apps/daily-notification-test/src/config/test-user-zero.ts (line 28)
Note: JWT generation now uses generateEndorserJWT() which creates proper ES256K signed tokens from the seed phrase.
14. Update TypeScript Type Definitions
- Status: โ VERIFIED - Type definitions exist and are being used, may need updates after ES256K changes
 
Current State: Using as any type assertion for configureNativeFetcher in HomeView.vue
Verification Results:
- โ
 
configureNativeFetcher()method added to plugin interface (src/definitions.ts) - Verified: Line 376 - โ
 Method signature exists with 
apiBaseUrl,activeDid,jwtSecretparameters - โ
 Method is being called in 
HomeView.vue- Line 474 (onMountedhook) - โ
 Currently uses 
(DailyNotification as any).configureNativeFetchertype assertion - Line 454 
Remaining Work:
- Rebuild plugin package to ensure TypeScript definitions are up to date in 
dist/ - Remove 
as anytype assertion inHomeView.vue(after confirming types are available) - โ ๏ธ CRITICAL: Update method signature after ES256K implementation (remove 
jwtSecret, add private key mechanism) 
Location: test-apps/daily-notification-test/src/views/HomeView.vue - Currently uses (DailyNotification as any).configureNativeFetcher
15. Add Network Security Configuration
- Status: โ COMPLETE - Network security config created and AndroidManifest.xml updated
 
Purpose: Allow HTTP connections in Android emulator (for localhost:3000)
Verification Results:
- โ
 Checked: 
network_security_config.xmldoes NOT exist inandroid/app/src/main/res/xml/ - โ
 Checked: No 
networkSecurityConfigattribute inAndroidManifest.xml - โ
 Checked: No 
usesCleartextTrafficattribute inAndroidManifest.xml 
Implementation Status:
- โ
 Created 
android/app/src/main/res/xml/network_security_config.xmlallowing cleartext traffic for10.0.2.2,localhost, and127.0.0.1 - โ
 Added 
android:networkSecurityConfig="@xml/network_security_config"to<application>tag inAndroidManifest.xml - โ Documented that this is safe for development (emulator-only access)
 
Files Modified:
test-apps/daily-notification-test/android/app/src/main/res/xml/network_security_config.xml(created)test-apps/daily-notification-test/android/app/src/main/AndroidManifest.xml(updated line 10)
Note: Required for HTTP connections (not HTTPS) to http://10.0.2.2:3000 from Android emulator
๐งช Testing Checklist
Before considering this implementation complete:
- โ ๏ธ CRITICAL: JWT tokens are accepted by endorser API (pending ES256K implementation - current HMAC-SHA256 will fail)
 - API responses are correctly parsed into notifications
 - Notifications appear at scheduled times
 - Prefetch occurs 5 minutes before notification
 - โ Error handling works for network failures - Retry logic implemented
 - โ SharedPreferences persists starred plan IDs - Implementation complete
 - Pagination works with 
afterIdparameter (SharedPreferences ready, needs API testing) - Multiple notifications can be scheduled and fetched
 - Background fetches work when app is closed
 - โ App recovers gracefully from API errors - Error handling and retry logic implemented
 
๐ Related Documentation
docs/NATIVE_FETCHER_CONFIGURATION.md- Native fetcher configuration guidesrc/types/content-fetcher.ts- TypeScript type definitionssrc/definitions.ts-PlansLastUpdatedResponseandPlanSummaryWithPreviousClaiminterfacesexamples/native-fetcher-android.kt- Example Android implementation- Investigation Results: 
INVESTIGATION_JWT_ALGORITHM_RESULTS.md- โ Complete investigation findings - endorser-ch Repository - CONFIRMED: Uses 
did-jwt.verifyJWT()with DID resolver for ES256K verification- JWT verification: 
~/projects/timesafari/endorser-ch/src/api/services/vc/index.js-decodeAndVerifyJwt() /api/v2/report/plansLastUpdatedBetweenendpoint handler:~/projects/timesafari/endorser-ch/src/api/controllers/report-router.js
 - JWT verification: 
 - did-jwt Library - DID-based JWT implementation (JavaScript reference)
 - TimeSafari Repository: 
~/projects/timesafari/crowd-funder-for-time-pwa/src/libs/crypto/vc/index.ts- CONFIRMED: Uses 
createEndorserJwtForKey()withdid-jwt.createJWT()and ES256K signing - Signs with Ethereum private key from DID identity
 - Reference implementation for production JWT generation
 
 - CONFIRMED: Uses 
 
Notes
- JWT Implementation: โ
 CONFIRMED - TimeSafari repository investigation complete. Endorser-ch API uses DID-based JWTs (ES256K) for API authentication. Current HMAC-SHA256 implementation must be replaced. See 
INVESTIGATION_JWT_ALGORITHM_RESULTS.mdfor complete findings. - Context Dependency: Consider whether 
TestNativeFetchershould be a singleton or if context should be injected differently - Error Strategy: Decide on error handling strategy - should fetches fail silently (current), or propagate errors for user notification?
 - State Management: Consider whether starred plan IDs should be managed by the app or fetched from API user profile
 - Investigation Status: โ Item #1 (JWT algorithm) investigation COMPLETE. Confirmed ES256K requirement. Implementation changes required before proceeding with other items.
 
โ Verification Summary (2025-10-31)
Items Verified Through Code Inspection:
- 
Item #4 (API Response Structure):
- โ Endpoint structure confirmed in endorser-ch source
 - โ Parser handles both flat and nested structures
 - โ
 Null checks exist for most fields (uses 
.has()checks) - โ ๏ธ Missing: 
root.has("data")check before accessing data array - โ ๏ธ Missing: 
wrappedClaimBeforeextraction from response 
 - 
Item #14 (TypeScript Types):
- โ
 Method exists in 
src/definitions.ts(Line 376) - โ
 Method signature includes 
apiBaseUrl,activeDid,jwtSecret - โ
 Method being called in 
HomeView.vue(Line 474,onMountedhook) - โ ๏ธ Currently uses 
as anytype assertion (needs plugin rebuild for proper types) 
 - โ
 Method exists in 
 - 
Item #15 (Network Security Config):
- โ
 Confirmed: 
network_security_config.xmldoes NOT exist - โ
 Confirmed: No 
networkSecurityConfigattribute in AndroidManifest.xml - โ
 Confirmed: No 
usesCleartextTrafficattribute in AndroidManifest.xml - Needs to be created for HTTP connections to 
http://10.0.2.2:3000 
 - โ
 Confirmed: 
 
Last Updated: 2025-10-31 (Checkboxes updated - Items 4, 14, 15 verified through code inspection)
Next Review: After ES256K JWT signing implementation is complete