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
| Pattern | Example | Cache |
|---|---|---|
| Versioned (immutable) | /sdk/v1.4.2/spotlight.min.js | max-age=31536000, immutable — cached forever at edge |
| Latest (rolling) | /sdk/latest/spotlight.min.js | max-age=60, stale-while-revalidate=300 |
| Manifest | /sdk/manifest.json | Same 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
Pin a version (recommended for production)
<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.2never 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)
<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
lateston staging.
The manifest
GET /sdk/manifest.json returns:
{
"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
latestchanges unexpectedly.
How versions get published
make build-sdk (which calls scripts/build-sdk.sh):
- Builds the bundles via rollup, outputs to
frontend-sdk/dist/. - Reads the version from
frontend-sdk/package.json. - Copies the bundles into
frontend-sdk/dist/v{version}/(one per semver) andfrontend-sdk/dist/latest/. - 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 publishedThe 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.2but the page still loads1.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.0but the page is loading1.4.2→ someone's runninglatestin production, which is probably not what they want.
The signal chain
- SDK sends
X-Spotlight-Bundle-Versionon every request. SdkVersionObservermiddleware in the backend reads it and increments the OTEL counterspotlight.sdk.bundle_version_seenlabelled bytenant_idversion.
- Counter → Prometheus → Grafana. The "Product Overview" dashboard has a "bundle versions in the wild" panel.
- 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:
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.errorsmetric withkind=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.2from 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 isv2.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.