# Chrome Extension Integration Guide This guide adds the licensing system to the **existing** Lovable Unlimited extension without changing or breaking any of its current behaviour. Everything is additive: five new files, three small edits. --- ## 1. Copy the new files into the extension root From `/extension` in this project, copy into the folder that contains `manifest.json`: | File | Role | | --- | --- | | `license-client.js` | Networking, fingerprinting, storage, heartbeat. No dependencies. | | `license-guard.js` | **Replaces** the existing `license-guard.js`. Re-implements all six `pk*` globals. | | `license-ui.js` | Header badge, countdown, activation overlay, premium lockdown. | | `license-ui.css` | Styles for the badge and the overlay. | | `background-license.js` | Service-worker startup check, alarms, toolbar badge. | > Back up the original `license-guard.js` first. The replacement exposes exactly the same function names and signatures, so no other file needs to change. If the extension ships a `dist/` mirror, copy the same five files there as well and repeat the manifest edit for `dist/manifest.json`. --- ## 2. Point the client at your server Open `license-client.js` and edit the two constants at the top: ```js var DEFAULT_API_BASE = 'https://license.example.com/api'; // your API base URL var CONFIG = { apiBase: DEFAULT_API_BASE, productSlug: 'lovable-unlimited', // must match the product slug in the admin panel heartbeatSeconds: 6 * 60 * 60, // silent re-check interval offlineGraceSeconds: 24 * 60 * 60, // tolerated offline window requestTimeoutMs: 15000 }; ``` --- ## 3. Update `manifest.json` A ready-made file is provided at `extension/manifest.json`. If you prefer to patch your own, make these four changes: **a) Add the `alarms` permission** ```json "permissions": ["storage", "activeTab", "scripting", "tabs", "sidePanel", "cookies", "alarms"] ``` **b) Add your licensing host** ```json "host_permissions": [ "https://*.lovable.dev/*", "https://api.lovable.dev/*", "https://lovable.dev/*", "https://lovable-api.com/*", "https://lov.powerkits.net/*", "https://license.example.com/*" ] ``` **c) Insert the three scripts into the content-script list** Order matters. They must come **after** `extension-config.js` and **before** `lovable-auth.js`: ```json "js": [ "security-hardening.js", "extension-config.js", "license-client.js", "license-guard.js", "license-ui.js", "lovable-auth.js", "lovable-feature-api.js", "user-messages.js", "jszip.min.js", "hwFingerprint.js", "content-bridge.js", "sounds.js", "content-templates.js", "content.js" ], "css": ["theme.css", "floating.css", "license-ui.css"] ``` **d) Expose the stylesheet** Add `"license-ui.css"` to `web_accessible_resources[0].resources` so the side panel can load it through `chrome.runtime.getURL`. --- ## 4. Wire the service worker Add **one line at the very top** of `background.js`: ```js importScripts('license-client.js', 'license-guard.js', 'background-license.js'); ``` That is the entire background change. It gives you: - a license check on `onStartup` and `onInstalled`, - a silent `chrome.alarms` heartbeat every 6 hours, - an instant lock broadcast to every open tab when the server reports `revoked`, - a toolbar badge that turns red when the license is not usable and amber in the last 7 days, - the `LMS_REQUIRE_LICENSE` message gate. --- ## 5. Wire the side panel In `sidepanel.html`, add the stylesheet inside ``: ```html ``` and the two scripts immediately **before** the closing ``, ahead of `sidepanel.js`: ```html ``` The badge automatically mounts itself next to `.sp-brand` inside `.sp-header`. Nothing else in the panel markup has to change. --- ## 6. Mark your premium controls Every element carrying `data-premium` (or the class `premium-feature`) is disabled and dimmed while the license is not usable: ```html
...
``` You can change the selector without touching the library: ```html ``` --- ## 7. Gate the premium logic itself UI dimming is cosmetic. Gate the real work with the guard — the same call the old code already used: ```js async function runPremiumAction() { const ok = await pkEnsureActiveLicense(); if (!ok) { LicenseUI.showActivation(); return; } // ... existing premium logic, untouched } ``` Inside the service worker, or from any context: ```js chrome.runtime.sendMessage({ type: 'LMS_REQUIRE_LICENSE' }, (res) => { if (!res || !res.ok) return; // proceed }); ``` When you call your own backend, attach the device headers: ```js const headers = await pkLicenseUploadHeaders(); // { 'X-License-Key': '...', 'X-License-Device': '...', 'X-Extension-Version': '1.0' } ``` And when a server answer says the license is no longer good: ```js if (pkShouldLockoutFromValidation(response)) { await pkRevokeLicenseStorage(); } ``` --- ## 8. Compatibility notes **The six original globals are preserved exactly.** | Global | Behaviour in the new guard | | --- | --- | | `pkInvalidateAssertCache()` | Clears the 60-second verdict cache | | `pkEnsureActiveLicense()` | `Promise`, never throws | | `pkRevokeLicenseStorage()` | Wipes local license data and shows the activation screen | | `pkShouldLockoutFromValidation(r)` | Accepts a response object, a status string or an HTTP code | | `pkLicenseUploadHeaders()` | `Promise` of request headers | | `pkLocalLicenseReady()` | Synchronous boolean for render paths | **Storage keys are reused, not replaced.** `ql_license_valid`, `ql_license_key` and `ql_hw_fingerprint` keep their meaning, so an already-activated installation keeps working. Three new keys are added: `lms_license_state`, `lms_last_check`, `lms_activated_at`. **`hwFingerprint.js` is respected.** If `window.getHardwareFingerprint()` exists, the client uses it; otherwise it computes its own SHA-256 fingerprint from ~15 independent signals. Either way the value is cached in `ql_hw_fingerprint`. **`extension-config.js` integrity checks are untouched.** The guard registers its globals through `window._pkS.register` when that API is present, so `_pkCrossCheck` keeps passing. --- ## 9. Test checklist 1. Load the unpacked extension — the activation overlay appears, premium controls are dimmed. 2. Generate a key in the admin panel, paste it — the overlay closes and the header shows `Licensed • 29d 23h 59m 58s`, ticking every second. 3. Reload the extension — it stays unlocked (startup verification passes). 4. Copy the same key into a second browser profile — activation fails with **License already activated on another device.** 5. Revoke the key in the admin panel and wait for the next heartbeat (or press *Re-check*) — the extension locks instantly. 6. Generate a key valid for 2 minutes — watch the countdown reach zero, the badge switch to **License Expired**, and every premium feature disable itself. 7. Turn off networking — the extension keeps working within the 24 h grace window and the badge shows the offline marker. 8. Check **Devices** in the admin panel — fingerprint, IP, browser, OS, extension version and activation time are all recorded. --- ## 10. Rollback Restore the original `license-guard.js`, remove the four other files, and revert the manifest and the two markup edits. No database or storage migration is involved.