Skip to content

Versioned SDK hosting

The SDK bundle is served from a versioned URL scheme so Cloudflare's edge can cache forever on pinned versions, and a publish rolls through in under a minute on the rolling pointer.

Two URL patterns, one rule: no silent fallback to latest when a pinned version is missing. If you ask for /sdk/v99.0.0/… and that version was never built, the response is 404. That's deliberate — a mismatch between "what I pinned" and "what the page is actually running" hides bugs and breaks audit trails.

URL patterns

PatternExampleCache
Versioned (immutable)/sdk/v1.4.2/spotlight.min.jsmax-age=31536000, immutable — cached forever at edge
Latest (rolling)/sdk/latest/spotlight.min.jsmax-age=60, stale-while-revalidate=300
Manifest/sdk/manifest.jsonSame short TTL as latest

The admin.min.js bundle is served the same way. A Spotlight platform-admin's browser lazy-loads it after config fetch; tenant embeds never touch it.

Choosing a pattern

html
<script src="https://spotlight.example.com/sdk/v1.4.2/spotlight.min.js"
        data-api-key="sk_live_…" async></script>
  • Cached at every edge forever — page loads are fast, cold-cache cost is paid once per CDN node.
  • Bumping the SDK is a one-line change in your page.
  • Immutable: the bytes behind v1.4.2 never change once published.
  • If you try to embed a version that doesn't exist, the script tag errors out loudly — you see it in the browser console and in your own error reporting.

Roll with latest (fine for the demo, risky for production)

html
<script src="https://spotlight.example.com/sdk/latest/spotlight.min.js"
        data-api-key="sk_live_…" async></script>
  • Every publish hits your page within ~1 minute.
  • Means you accept every SDK change without reviewing it first.
  • Fine for the Spotlight demo site; risky for an e-commerce checkout. Pin in production, use latest on staging.

The manifest

GET /sdk/manifest.json returns:

json
{
  "latest": "1.4.2",
  "versions": ["0.1.0", "0.2.0", "1.0.0", "1.4.2"],
  "built_at": "2026-04-20T10:29:50Z"
}

Useful for:

  • A dashboard widget showing "current SDK version".
  • CI checking "is the version the tenant expects actually published?"
  • Customer observability — scrape the JSON, alert when latest changes unexpectedly.

How versions get published

make build-sdk (which calls scripts/build-sdk.sh):

  1. Builds the bundles via rollup, outputs to frontend-sdk/dist/.
  2. Reads the version from frontend-sdk/package.json.
  3. Copies the bundles into frontend-sdk/dist/v{version}/ (one per semver) and frontend-sdk/dist/latest/.
  4. Regenerates dist/manifest.json.

The sdk-cdn compose service mounts frontend-sdk/dist/ read- only and serves it via nginx with the correct cache headers.

Retention

Every version built is kept on disk for now. Production will move this hosting to S3 + CloudFront where retention is policy-driven; see TODO.md under "Infrastructure".

Knowing which version each site is actually running

Every request the SDK makes carries two version headers:

X-Spotlight-Bundle-Version: 1.4.2     # the bytes on the page
X-Spotlight-SDK-Version:    1.4.2     # the version the tenant has published

The first is injected at rollup build time (__SPOTLIGHT_BUNDLE_VERSION__frontend-sdk/package.json version). The second is what the tenant's admin marked as "published" via the SDKVersionService.

Most of the time these agree. When they don't:

  • Tenant published 1.4.2 but the page still loads 1.2.0 → an old embed pin that hasn't been updated. That's the signal to email the customer with a changelog and a one-line upgrade diff.
  • Tenant published 1.2.0 but the page is loading 1.4.2 → someone's running latest in production, which is probably not what they want.

The signal chain

  1. SDK sends X-Spotlight-Bundle-Version on every request.
  2. SdkVersionObserver middleware in the backend reads it and increments the OTEL counter spotlight.sdk.bundle_version_seen labelled by tenant_id
    • version.
  3. Counter → Prometheus → Grafana. The "Product Overview" dashboard has a "bundle versions in the wild" panel.
  4. Logs go to Loki too, so an admin can query by tenant + version + URL.

Emailing customers on an upgrade

There's no built-in automation for this yet — it's on the roadmap. For now the admin queries the Prometheus counter:

promql
sum by (tenant_id, version) (increase(
  spotlight_sdk_bundle_version_seen_total[7d]
))

→ "tenants running version X in the last 7 days" → pull their contact from Admin.Users → email.

If something pinned breaks your embed

Symptoms:

  • Browser console: Failed to load resource: 404
  • The Spotlight overlay never appears
  • SDK error counter in Grafana ticks up — check the spotlight.sdk.errors metric with kind=bundle_load_failed

Fix: update the pinned version in your page to one that's in the manifest, deploy, done.

Why no fallback

A fallback would mean:

  • Customer pins v1.4.2.
  • We drop v1.4.2 from the manifest (delete, rotate, whatever).
  • Page silently starts running v2.0.0 — possibly with breaking changes, different event shapes, different security posture.
  • Customer's audit log says v1.4.2; what's actually running is v2.0.0. Divergence between attested and actual.

Failing loudly is safer. If you've pinned a version, the URL is a contract. Breaking the contract should hurt.

Spotlight