TechFabricTechFabric
Getting started

Tag-loader (drop-in script)

Run TechFabric Experiments on a marketing site or via Google Tag Manager without touching application code.

The tag-loader is a single <script> build of the web SDK. It auto-creates an anonymous subject id, fetches the signed manifest, and applies declarative DOM ops from each running experiment, governed delivery without application code.

The local static-site path is continuously smoke-tested by pnpm run smoke:new-project:browser, which creates a new project, publishes a static manifest + tag-loader, opens a tiny site in headless Chrome, verifies a DOM operation, and asserts an fx_exposure event reaches window.dataLayer.

The fastest install is to load the bundle directly from the Fabric-operated CDN, no build pipeline, no upload step. This is the same model Google Analytics uses. Paste the snippet and ship.

<style id="fx-anti-flicker">
  html[data-fx-loading] body { visibility: hidden; }
</style>
<script>
  document.documentElement.setAttribute('data-fx-loading', '');
  window.__FX__ = {
    manifestUrl: 'https://manifest.experiments.techfabric.com/<orgId>/manifest',
    beaconEndpoint: 'https://ingest.experiments.techfabric.com/<orgId>/v1/exposure',
    beaconFlushIntervalMs: 5000,
    antiFlickerStyleId: 'fx-anti-flicker',
  };
</script>
<script
  src="https://cdn.fabric.pro/v/0/experiments.tag.global.js"
  async
></script>

URL scheme:

PathBehavior
/v/0.2.1/experiments.tag.global.jsPinned exact version. Immutable, 1-year browser cache.
/v/0/experiments.tag.global.jsMajor-pinned. Auto-updates within 0.x releases.
/v/latest/experiments.tag.global.jsAlways newest. Use for prototyping; pin a major in production.

Subresource Integrity (SRI) hashes for every published version are exposed at https://cdn.fabric.pro/index.json:

<script
  src="https://cdn.fabric.pro/v/0.2.1/experiments.tag.global.js"
  integrity="sha384-…"          {/* paste from index.json */}
  crossorigin="anonymous"
  async
></script>

Install, self-hosted (full control)

If you'd rather not load JavaScript from a third-party origin (CSP-strict shops, regulated industries, sites with strict supply-chain reviews), host the bundle yourself. Two ways:

Via the CLI, fx publish --static ./dist-fx writes the signed manifest, JWKS, revocation record, and tag-loader bundle into a directory you upload to your CDN. When deploying beneath a CDN path prefix, set manifestJwksUrl and manifestRevocationsUrl to the exported files. The self-host page provides a complete example.

Via npm, install @fabricorg/experiments-web and copy the file from node_modules/@fabricorg/experiments-web/dist/experiments.tag.global.js into your asset pipeline.

Either way the snippet becomes.

<script src="https://cdn.example.com/experiments.tag.global.js" async></script>

How it works

window.__FX__ is the only global the loader expects. Set it before the loader script tag so the bootstrap reads it on first execution.

The loader exposes window.fx. A Promise<ClientApi> you can await from later scripts:

<script>
  window.fx?.then(client => {
    if (client.treatment('homepage-cta') === 'treatment') {
      console.log('user is in treatment');
    }
  });
</script>

What's auto-wired

  • Anonymous subject id: cookie _fx_sid (365d, samesite=lax). Override with __FX__.cookieName or pass __FX__.subjectId directly for logged-in users.
  • DOM ops: variants with domOps (replaceText, setAttr, addClass, injectCSS, etc.) apply on assignment.
  • Triggers: when a manifest experiment declares a trigger (urlMatch, waitForSelector, event), activation defers until the trigger fires.
  • Exposures: pushed to window.dataLayer so GTM tags can listen for the fx_exposure event. Set __FX__.noDefaultAdapter = true to suppress.
  • Warehouse telemetry: exposures are batched to beaconEndpoint; calls to client.track() are sent to the sibling /conversion endpoint. The edge acknowledges only after Cloudflare Queues accepts the batch, then writes an idempotent NDJSON object to R2 with retries and dead-letter handling.
  • Anti-flicker: the optional guard style is removed on success, failure, or a two-second safety timeout, so a manifest outage cannot leave the page hidden.
  • QA inspector: add ?fxdebug=1 to load the separate, read-only inspector asset. It shows manifest version, experiment state, eligibility, and assignment without increasing the normal loader bundle.
  • Preview: when ?fxpreview=<expId>:<variant>&fxtoken=<jwt> is in the URL, the SDK verifies the short-lived token with public JWKS from /.well-known/jwks/<orgId> and forces the variant in this browser only. Preview assignments are deliberately not exposure-tracked.

Configuration reference

FieldTypeDefaultNotes
manifestUrlstringrequiredOrigin must be CORS-friendly
manifestJwksUrlstringderived from manifestUrlPublic JWKS used to verify signed manifests; set explicitly for a CDN path prefix
manifestRevocationsUrlstringderived from manifestUrlRevoked manifest-version record; set explicitly for a CDN path prefix
subjectIdstringautoOverride the anonymous id
cookieNamestring_fx_sidAnonymous-id cookie name
cookiePrefixstring'fx.'Sticky-bucket key prefix
previewJwksUrlstringderived from manifestUrlOptional public JWKS URL for preview token verification
beaconEndpointstringnoneURL for batched exposure POSTs via sendBeacon
beaconFlushIntervalMsnumbernonePeriodic exposure/conversion flush; pagehide flushing is always enabled
antiFlickerStyleIdstringnoneID of the temporary guard style removed after bootstrap
inspectorbooleanfalseLoad the read-only QA inspector without a query parameter
inspectorUrlstringbeside tag-loaderOverride the inspector asset URL
noDefaultAdapterbooleanfalseSuppress the default dataLayer.push
onExposurefunctionnoneCustom exposure handler
onRecipeFailurefunctionnoneFires only when variant code throws, additive to onExposure
onActivationFailurefunctionnoneFires when an activation trigger rejects, throws, or times out
excludedbool | functionnoneTruthy → SDK is no-op (bots, opt-outs, consent)
maxErrorStackLengthnumber1000Cap on failure.stack chars sent to handlers

Realistic install (with adapters)

<script>
  window.__FX__ = {
    manifestUrl: 'https://manifest.example.com/acme/manifest',
    cookiePrefix: 'acme.fx.',
    excluded: () => /bot|crawler/i.test(navigator.userAgent),
    onRecipeFailure: (rec) => {
      window.dataLayer = window.dataLayer || [];
      window.dataLayer.push({
        event: 'fx_recipe_failure',
        fx_experiment_id: rec.experimentId,
        fx_variant_key: rec.variantKey,
        fx_failure_message: rec.failure?.message,
      });
    },
  };
</script>
<script src="https://cdn.example.com/experiments.tag.global.js" async></script>

Preview tokens are bearer credentials in URLs. They are signed by Studio/API and verified with public keys, so no preview secret belongs in customer HTML. Still set:

Referrer-Policy: no-referrer

…on pages that may receive preview links so the token isn't leaked via the Referer header to third-party origins.

See also

On this page