Description
Add a client-side module to the extension that holds the current entitlement, verifies its signature against the bundled public key, caches it in chrome.storage.local, refreshes it before expiry, and exposes simple feature flags (for example canUseAI, aiQuotaRemaining, isPro) that the UI reads. The rest of the app should never look at raw license data, only these flags.
Why
Gating logic scattered across components is hard to reason about and easy to get wrong. A single entitlement layer gives one well-tested place that decides what is unlocked, keeps the signature check in one spot, and makes the free/Pro split a matter of reading a flag. It also degrades gracefully offline by trusting the last valid, unexpired entitlement.
Acceptance Criteria
Resources
Description
Add a client-side module to the extension that holds the current entitlement, verifies its signature against the bundled public key, caches it in
chrome.storage.local, refreshes it before expiry, and exposes simple feature flags (for examplecanUseAI,aiQuotaRemaining,isPro) that the UI reads. The rest of the app should never look at raw license data, only these flags.Why
Gating logic scattered across components is hard to reason about and easy to get wrong. A single entitlement layer gives one well-tested place that decides what is unlocked, keeps the signature check in one spot, and makes the free/Pro split a matter of reading a flag. It also degrades gracefully offline by trusting the last valid, unexpired entitlement.
Acceptance Criteria
app/src/lib/store.ts) with typed flagschrome.storage.localvia the existingstorage.tshelpersisPro,canUseAI,aiQuotaRemaining, advanced-options gateResources
app/src/lib/store.ts(Zustand) andapp/src/lib/storage.ts(storage helpers)