locqr/sdk/claude.md

191 lines
6.3 KiB
Markdown

# 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<Credential>`
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.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.