# Plugin feedback: `configureNativeFetcher` — optional JWT pool for background API calls **Date:** 2026-03-27 PST **Target repo:** `@timesafari/daily-notification-plugin` (daily-notification-plugin) **Consuming app:** crowd-funder-for-time-pwa (TimeSafari) **Related app plan:** `doc/plan-background-jwt-pool-and-expiry.md` (Phase B, Option B1) --- ## Summary The host app’s **`NativeNotificationContentFetcher`** (`TimeSafariNativeFetcher` on Android) calls Endorser with a Bearer JWT set via **`configureNativeFetcher`**. For **background** prefetch, the token must stay valid until WorkManager runs (often **minutes later**); Endorser may also reject **duplicate** JWT strings across days. The **app** will mint a **pool** of distinct JWTs (see app plan) and needs the plugin to **accept and persist** that pool so native code can select a token **without JavaScript** at prefetch time. **Requested change (plugin):** extend **`configureNativeFetcher`** to accept an optional **JWT pool** alongside the existing **`jwtToken`**, persist it in the same storage the host already relies on (e.g. SharedPreferences / app group), and document how **`NativeNotificationContentFetcher`** implementations should read it. --- ## Motivation | Issue | Why plugin support helps | |-------|---------------------------| | Single short-lived `jwtToken` | Expires before background fetch | | Server duplicate-JWT rules | Need many distinct bearer strings over time | | No JS in WorkManager | Pool must be readable **only** from native | --- ## Proposed API (TypeScript / Capacitor) **Extend** existing `configureNativeFetcher` options (names indicative — align with plugin naming conventions): ```ts configureNativeFetcher(options: { apiBaseUrl: string; activeDid: string; /** Primary token; keep for backward compatibility and Phase A (single long-lived JWT). */ jwtToken: string; /** * Optional. Distinct JWT strings for background use (e.g. one per day slot). * If omitted, behavior matches today (single jwtToken only). */ jwtTokens?: string[]; }); ``` **Alternatives** (if size limits matter for bridge payload): - `jwtTokenPoolJson: string` — JSON array string of JWT strings (single string across the bridge). **Validation (plugin):** - If `jwtTokens` present: length **≤** a sane cap (host will use ~100; plugin may enforce max e.g. 128). - Empty array: treat as “no pool” (same as omitting). --- ## Android 1. **Parse** new fields in `DailyNotificationPlugin.configureNativeFetcher` (or equivalent). 2. **Persist** pool under the same prefs namespace used for other TimeSafari / dual-schedule data, or a **documented** key prefix (e.g. `jwt_token_pool` as JSON array string). 3. **Document** for host implementers: `NativeNotificationContentFetcher` should: - Prefer **pool entry** for `fetchContent` when pool is non-empty (selection policy is **host** responsibility — e.g. day index % length), **or** - Expose a small helper the host fetcher calls to resolve “current” bearer. 4. **Clear** pool when `configureNativeFetcher` is called with a new identity / empty pool / logout path (coordinate with host). 5. **Backward compatibility:** if only `jwtToken` is sent, behavior **unchanged** from current release. --- ## iOS When `configureNativeFetcher` exists on iOS, mirror Android: accept optional pool, persist, document read path for native fetcher. --- ## Versioning & release - Bump **plugin semver** (minor: new optional fields). - Publish package; consuming app bumps **`@timesafari/daily-notification-plugin`** and updates `nativeFetcherConfig.ts` to pass `jwtTokens` when Phase B ships. --- ## References (host app) | Topic | Location | |--------|----------| | End-to-end plan (Phase A/B, pool sizing) | `doc/plan-background-jwt-pool-and-expiry.md` | | Android fetcher | `android/.../TimeSafariNativeFetcher.java` | | Current configure call | `src/services/notifications/nativeFetcherConfig.ts` | | JWT options (expired token context) | `doc/endorser-jwt-background-prefetch-options.md` | --- *This document is intended to be copied or linked from PRs in **daily-notification-plugin**; keep app-specific details in the app plan.*