@zerogpu/browser-extension-sdk
v0.4.0
Published
ZeroGPU device-side SLM runtime SDK for browsers and Chrome (MV3) extensions
Readme
ZeroGPU Browser SDK
Device-side JavaScript SDK that downloads a small language model (SLM), registers the device with the ZeroGPU orchestrator, opens a WebSocket for tasks, and executes on-device summarization.
Building a Chrome extension?
Chrome Manifest V3 forbids remotely-hosted code, so the <script>/loader path
below can't be used. Instead, install the package and vendor the runtime into your
extension:
npm install @zerogpu/browser-extension-sdk
npx zerogpu init # vendors the runtime + scaffolds the offscreen glueSee the full Chrome Extension developer guide (install, validate, and keep the SDK up to date).
Installation
npm installBuild
npm run buildThe build produces:
- ESM (
dist/zerogpu-browser-sdk.esm.js) and CJS (dist/zerogpu-browser-sdk.cjs) bundles for npm consumers. - A UMD bundle (
dist/zerogpu-browser-sdk.umd.js) plus an immutable, content-hashed copy underdist/v/for CDN distribution. dist/loader.js— the stable bootstrap apps embed (see UMD via<script>).dist/manifest.json— points the loader at the current hashed core bundle and records the build version.
Tests
npm run testLint
npm run lintUsage
ESM
import { initZeroGpuSdk } from '@zerogpu/browser-extension-sdk';
await initZeroGpuSdk({
// Optional Telegram user ID or custom app user ID
appUserId: 'telegram-user-1234',
// Sent as header: x-edge-operator-key
edgeOperatorKey: 'your-edge-operator-key',
// Override default settings
overrides: {
orchestrator: {
// Each deployment bakes in its own orchestrator at build time
// (develop/staging/production). Override only for internal testing.
baseUrl: 'https://devices.zerogpu.ai'
},
telemetry: {
// Enable/disable detailed console logs (redacted: ids/sizes/timings only)
enableConsoleLogs: true,
// Request geolocation permission (default: false)
locationData: true
}
}
});UMD via <script> (always-latest loader)
Embed the loader URL — dist/loader.js — and you never have to think about
SDK versions again. The loader resolves and injects the latest published core
bundle at runtime, so an integrated app stays up to date with zero effort:
no version pinning, no ?v= cache-buster to bump.
<script
async
src="https://js-sdk.zerogpu.ai/dist/loader.js"
edgeOperatorKey="your-edge-operator-key"
enableConsoleLogs="true"
></script>The SDK auto-initializes when edgeOperatorKey is present
(the loader forwards every attribute to the core bundle).
For manual initialization, embed the loader without keys and call
ZeroGpuSdk.initZeroGpuSdk(...). The loader installs a small queueing stub, so
calls made before the core bundle finishes loading are replayed automatically:
<script async src="https://js-sdk.zerogpu.ai/dist/loader.js"></script>
<script>
ZeroGpuSdk.initZeroGpuSdk({
edgeOperatorKey: 'your-edge-operator-key'
});
</script>How it works: loader.js and manifest.json are served fresh (short TTL +
stale-while-revalidate); the manifest points at an immutable, content-hashed
core bundle (dist/v/zerogpu-browser-sdk.<hash>.umd.js) cached for a year. A new
release simply repoints the manifest at a new hash — and rollback is the same
operation in reverse. If the manifest is ever unreachable, the loader falls back
to the stable dist/zerogpu-browser-sdk.umd.js bundle.
Loading the core bundle directly (
dist/zerogpu-browser-sdk.umd.js) still works but pins the app to whatever build is live at load time — prefer the loader so updates are automatic.
Release channels: js-sdk.zerogpu.ai serves tagged releases only — the
production deploy is gated on a v* git tag (release tags may carry a
prerelease suffix, e.g. v2.1.8-alpha), so an auto-updating embed can never
receive an untagged branch build. Development builds live on the separate
dev-js-sdk.zerogpu.ai / staging-js-sdk.zerogpu.ai domains (deployed from
the dev/main branches) — point a test page at those to try unreleased
builds.
Configuration Options
InitOptions
| Option | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| appUserId | string | undefined | Optional user identifier (e.g. Telegram ID) for telemetry. |
| edgeOperatorKey | string | undefined | Operator key forwarded to /register as x-edge-operator-key. The orchestrator resolves the project from this key. |
| overrides | Partial<SdkConfig> | {} | Deep merge overrides for internal config. |
| customParams | { cust_param1?, cust_param2?, cust_param3? } | undefined | Operator macros attached to every request this device serves. See Custom macros. |
Off-main-thread inference (overrides.inference)
Model download, ONNX session creation and every inference run in a dedicated Worker the SDK spawns itself, so the host page's main thread never blocks (session creation alone froze it for 1–2 s per model before). The Worker is created from source bundled into the SDK — nothing extra to host — and the device protocol, outputs and the IndexedDB model cache are unchanged.
| Option | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| inference.worker | boolean | true | false forces the historical main-thread mode (e.g. an environment known to lack Workers). |
| inference.workerUrl | string | undefined | Same-origin URL of dist/zerogpu-worker.js for pages whose Content-Security-Policy forbids blob: workers. |
When a Worker cannot be created or fails to boot, the SDK falls back to the
main thread for the rest of the session and reports it once as a
guardrail.pressure_warning event with code inference_worker_unavailable.
await initZeroGpuSdk({
edgeOperatorKey: 'your-edge-operator-key',
overrides: { inference: { worker: false } } // main-thread mode
});Custom macros (cust_param1..3)
Three opaque slots for your own reporting dimensions — placement, cohort, campaign, app surface. ZeroGPU stores and counts them against every request your devices serve, never interprets them, and reports them back only to you (never to the API customer whose request the device served).
await initZeroGpuSdk({
edgeOperatorKey: 'your-edge-operator-key',
customParams: { cust_param1: 'placement_home', cust_param2: 'cohort_b' }
});
// Later — replaces the whole map; omitted slots become unset, {} clears all.
setCustomParams({ cust_param1: 'placement_settings' });Script tag: data-cust-param1, data-cust-param2, data-cust-param3 (the
loader forwards them). ZeroGpuSdk.setCustomParams(...) is queued like
initZeroGpuSdk if called before the core bundle loads. Works the same inside
Telegram Mini Apps.
- Values only. The slot name is the key, so send the value itself
(
cust_param1: '1231231313'), never a nested parameter (cust_param1: 'user-id=1231231313'). Letters, digits,.,_and-only; anything else is dropped with an always-on warning. - Strings, numbers and booleans are accepted (numbers/booleans become strings);
other keys and types are dropped with a console warning (with
enableConsoleLogs). Control characters are stripped, values are trimmed, and anything over 128 characters is truncated rather than rejected. - No personal data. Email-shaped values are dropped with an always-on warning. Values cannot be deleted individually once recorded.
setCustomParamsapplies from the next request served; no re-registration.
SdkConfig Overrides
You can override any part of the configuration. Common overrides:
orchestrator.baseUrl: URL of the ZeroGPU orchestration API.slm.modelRepoId: Hugging Face repo id used by Transformers.js (default:Xenova/t5-small).slm.modelUrl: Base URL that hosts the model assets (e.g., your CDN).slm.sampleSummaryText: Text used for the on-device self-test before registration.telemetry.locationData: Iftrue, SDK attempts to gather GPS coordinates (triggers permission prompt).telemetry.enableConsoleLogs: Force console logging on/off (defaultfalse). Logs are redacted by construction: message types, request/model/device ids, counts, byte sizes, durations and error codes only — never task text, model output, message frames or session credentials.telemetry.cameraData: Deprecated and ignored. The camera-capture path was removed from the SDK entirely (ZGA-538); no camera code exists in the shipped bundle. The flag remains in the type only so existing integrations keep compiling.
Data collected at registration
The SDK collects coarse device-capability data and sends it to the ZeroGPU registry on
POST /register so the orchestrator can decide which model (if any) fits this device:
platform, user agent, language, timezone, CPU core count, approximate memory, screen
size, battery level, connection type, page URL/referrer, and — only with the
locationData opt-in — GPS coordinates.
WebGL renderer fingerprint
The SDK reads the WebGL renderer string (WEBGL_debug_renderer_info →
UNMASKED_RENDERER_WEBGL, e.g. "Apple GPU" or "Adreno 660") and sends it in the
register payload twice, as gpuRenderer and as webglFingerprint:
- What it is: the GPU/driver identification string the browser exposes via WebGL. No canvas is rendered or hashed; the two fields currently carry the same raw string.
- What it is used for: as a hardware-capability signal for model assignment (which
model fits this GPU class), and as a device-identity anchor — a stability signal that
helps the registry recognize a device across sessions alongside the stored
deviceId(a random UUID persisted in browser storage). - What it is not: it is not used for cross-site tracking or advertising profiles, and it identifies a hardware class rather than a person — many devices share the same renderer string. Replacing this signal with per-device keypairs is planned (see the confidentiality roadmap); until then this documentation is the honest statement of what is collected.
Local Testing
Dashboard + Demo (Vite)
The Vite app under sdk/dashboard-app serves both:
/— ZeroGPU Edge SDK Demo/dashboard— ZeroGPU Fleet dashboard (analytics)
It also proxies API calls so the browser avoids CORS:
/api/*->API_UPSTREAM(defaulthttp://localhost:4000)/analytics/*->ANALYTICS_UPSTREAM(defaulthttp://localhost:5000)
cd sdk
npm run dashboard:devBuild/preview:
cd sdk
npm run dashboard:build
npm run dashboard:previewEnv config:
- Copy
sdk/dashboard-app/.env.exampletosdk/dashboard-app/.env.localand edit values. - By default it uses
/api+/analyticsso Vite can proxy and avoid browser CORS. - The demo page (
/) can also read optional defaults fromVITE_ZGPU_SDK_KEYandVITE_ZGPU_PROJECT_ID(recommended via.env.local, not committed).
Testing against a non-production orchestrator
Each deployment bakes in its own orchestrator at build time — develop, staging, or
production (https://devices.zerogpu.ai) — via the ZGPU_ORCHESTRATOR_BASE_URL build
env var set by the GitHub deploy workflows. For internal testing against a develop or
local orchestration API, override orchestrator.baseUrl directly:
import { initZeroGpuSdk } from '@zerogpu/browser-extension-sdk';
await initZeroGpuSdk({
edgeOperatorKey: 'your-edge-operator-key',
overrides: {
orchestrator: {
// e.g. https://dev.devices.zerogpu.ai or http://localhost:6000
baseUrl: 'http://localhost:6000'
},
telemetry: {
enableConsoleLogs: true // Enable detailed logging
}
}
});SDK Demo Page
Use the built-in demo page served by Vite at http://localhost:8000/ (it loads the SDK from /dist/zerogpu-browser-sdk.esm.js).
Self-Hosting the Model
If you want to serve the SLM artifacts from your own CDN (e.g., Cloudflare R2), download the Hugging Face assets and preserve the folder structure:
t5-small/
├── config.json
├── generation_config.json
├── tokenizer.json
├── tokenizer_config.json
├── special_tokens_map.json
├── spiece.model
└── onnx/
├── encoder_model_quantized.onnx
└── decoder_model_merged_quantized.onnxDownload commands
mkdir -p t5-small/onnx
cd t5-small
BASE_META="https://huggingface.co/Xenova/t5-small/resolve/main"
BASE_ONNX="https://huggingface.co/Xenova/t5-small/resolve/main/onnx"
curl -L -o config.json "$BASE_META/config.json"
curl -L -o generation_config.json "$BASE_META/generation_config.json"
curl -L -o tokenizer.json "$BASE_META/tokenizer.json"
curl -L -o tokenizer_config.json "$BASE_META/tokenizer_config.json"
curl -L -o special_tokens_map.json "$BASE_META/special_tokens_map.json"
curl -L -o spiece.model "$BASE_META/spiece.model"
curl -L -o onnx/encoder_model_quantized.onnx "$BASE_ONNX/encoder_model_quantized.onnx"
curl -L -o onnx/decoder_model_merged_quantized.onnx "$BASE_ONNX/decoder_model_merged_quantized.onnx"Upload the t5-small/ directory to your CDN unchanged and ensure responses include permissive CORS headers (Access-Control-Allow-Origin: *). Then override the SDK config:
await initZeroGpuSdk({
edgeOperatorKey: 'your-edge-operator-key',
overrides: {
slm: {
modelUrl: 'https://cdn.example.com/models/t5-small'
}
}
});slm.modelRepoId remains Xenova/t5-small so Transformers.js can reference the original repo, while the SDK rewrites every network request to your CDN.
Flow
initZeroGpuSdkresolves configuration (env defaults + overrides).- Device fingerprint + metadata are collected.
- Required SLM artifacts are downloaded to IndexedDB and loaded into Transformers.js. Timing metrics are recorded.
- The SDK calls the orchestrator
/registerendpoint with device + model metadata. - The orchestrator responds with a
wsUrl. The SDK opens a WebSocket, sends ahello, and listens forsummary_requestmessages. - Each
summary_requestexecutes against the on-device SLM (single flight enforced) and responds withsummary_responsecontaining the summarized text and rich metadata (timings, device info, model info). - If the WebSocket closes or errors, the SDK marks the state as
error. Reloading the host app re-initializes the SDK.
