# JavaScript SDK The SDK is a JavaScript library embedded in participating websites. It wraps the browser messaging layer between the page and the extension, exposing a clean API to website developers. Without it, a site could handle Port messaging directly — the SDK makes that unnecessary. The SDK has no logic of its own. It does not perform cryptography, hold state, or make network requests. It is a thin bridge. --- ## Initialisation The website developer initialises the SDK with the site's registration certificate, obtained from the LOCQR backend admin panel when registering the domain. The cert is a static value that changes only on renewal. ```js locqr.init({ cert: 'BASE64_ENCODED_CERT' }); ``` The cert is not embedded in the page DOM. Passing it at initialisation is sufficient; the SDK forwards it to the extension at connect time. --- ## Communication model The SDK communicates with the extension service worker directly via `chrome.runtime.connect(extensionId)` — no content script is involved. This opens a long-lived bidirectional **Port**. The extension reads the real page URL from `sender.tab.url` (browser-provided; cannot be spoofed by the page). ``` website code ↔ SDK ↔ chrome.runtime Port ↔ service worker ``` All SDK↔extension communication for the lifetime of the page uses this Port. The full Port message protocol (message types, `LocqrError`, `Credential` schemas) is defined in `../interfaces.md`. --- ## Activation After `locqr.init()`, the SDK opens the Port and sends the cert. The extension verifies it and responds with the outcome. `locqr.ready` is a Promise that resolves with the authorised feature set on success, or rejects with the verification error. ```js try { const { features } = await locqr.ready; // features: e.g. ['login'] } catch (err) { // site not registered, cert invalid, or account error } ``` The SDK exposes only the methods corresponding to the declared features. A site whose cert does not include `"vault"` does not get `locqr.encrypt()`. --- ## API surface ### `locqr.init(options)` Initialises the SDK. Must be called before any other method. ```ts locqr.init({ cert: string }); ``` ### `locqr.ready → Promise<{ features: string[] }>` Resolves when the extension has verified the site and returned an authorised feature set. Rejects with an error object if the extension is not installed, the site is not registered, the cert is invalid, or the account is not in good standing. ```ts type LocqrError = { code: 'NOT_INSTALLED' | 'NOT_REGISTERED' | 'CERT_INVALID' | 'ACCOUNT_ERROR' | 'RUN_FAILED'; reason?: string; // sub-reason for CERT_INVALID / ACCOUNT_ERROR / RUN_FAILED security?: boolean; // true for security-class errors } ``` `NOT_INSTALLED` is the only code produced before a Port is opened. All other codes arrive from the extension over the Port after the connection succeeds. ### `locqr.requestCredential() → Promise` Signals to the extension that a credential is needed for the current site. Triggers Phase 1 of the run. Resolves when the credential is delivered at the end of Phase 4. Rejects if the run fails or is aborted. ```js const { username, password } = await locqr.requestCredential(); ``` ```ts type Credential = { username: string; password: string; // totp and further fields deferred } ``` ### `locqr.abort()` Cancels an in-flight run, any phase. A no-op if no run is active. Any pending `requestCredential()` Promise rejects through the normal `run_error` round-trip this triggers, not synchronously from calling this. ```js locqr.abort(); ``` ### `locqr.reportSession(options)` ```ts locqr.reportSession({ active: boolean }); ``` Tells the extension whether this site currently considers the user to have an active session — purely advisory, shown as a badge/copy variant on the popup's `idle` screen (`extension/gui.md`). Never affects verification or any run's security checks; those are unconditional regardless of what a site reports here. Call it any time after `init()`, and again whenever the site's own session state changes (including its own logout flow, with `active: false`) — LOCQR has no session mechanism of its own and doesn't persist this across a page reload; the site's existing session handling (cookie, server-checked session, whatever it already has) is what this should be sourced from. ### `locqr.on(event, handler)` / `locqr.off(event, handler)` Registers and removes handlers for extension-initiated events. | Event | When | Payload | |---|---|---| | `ready` | Verification complete | `{ features: string[] }` | | `run:started` | Phase 1 complete; QR displayed | `{}` | | `run:delivered` | Credential delivered | `{ credential: Credential }` | | `run:error` | Run failed | `{ error: LocqrError }` | `run:delivered` fires alongside the `requestCredential()` Promise resolving. Sites using the event model do not need to call `requestCredential()`. Both models are valid; mixing them on the same run is not. --- ## GYBBR: vault API (deferred) For GYBBR, the SDK will expose cryptographic vault operations once the `"vault"` feature is declared in the site's cert. The website requests operations by key ID and receives results; key material never leaves the extension. Anticipated surface (not yet designed): ```js const ciphertext = await locqr.encrypt({ keyId: 'k1', data: plaintext }); const plaintext = await locqr.decrypt({ keyId: 'k1', data: ciphertext }); ``` Extension-initiated vault notifications (e.g. identity key available) will be delivered as events via `locqr.on`. Exact method signatures and event names are deferred to the GYBBR design phase. --- ## Static properties The SDK exposes stable reference values as static properties, available immediately after the script loads — before `locqr.init()` and regardless of whether the extension is installed. Website developers can use these to build install prompts, help text, or footer links without hardcoding URLs themselves. ```js locqr.installLinks // { chrome: string, firefox: string, ... } locqr.mobileLinks // { android: string, ios: string } locqr.website // LOCQR public website URL ``` `locqr.ready` rejecting with `code: 'NOT_INSTALLED'` is the signal to surface these links. How they are presented is entirely the website's decision; the SDK provides the data only. DOM helpers, install-prompt components, etc. are deferred. `ios` within `mobileLinks` is present as a placeholder but empty until the iOS app exists. --- ## Distribution How the SDK is packaged and distributed (npm, CDN script tag, bundled asset) is deferred. The extension ID baked into the SDK is stable for the lifetime of the Store-published extension; it changes only on a full re-publication, which is an extraordinary event. Development builds use a different ID configured via `locqr.init({ cert, extensionId: DEV_ID })`. --- ## Security notes - The SDK never receives key material. It receives only operation results (credentials, ciphertext, plaintext of things the site requested decrypted). - The real page URL is read by the extension from `sender.tab.url` — the page cannot supply or spoof it. - Feature enforcement is in the extension. The SDK surface is a reflection of what the cert permits, not an independent gate. - Once a credential is passed to the website via `requestCredential()`, it has left the LOCQR security boundary. What the site does with it is out of scope.