By default the tracker loads from omni.karmaflow.ai. Ad-blockers (uBlock Origin, Brave, Pi-hole, etc.) ship blocklists that include known analytics hosts — your traffic from blocker-using visitors silently disappears. A first-party CNAME — routing the tracker through a subdomain on the customer's own domain — moves the request to a hostname the blocker has no reason to block.
analytics.customer.example → omni.karmaflow.ai.Host: analytics.customer.example to our Cloud Run service.https://analytics.customer.example/p/SITE_KEY/k.js.document.currentScript.src and uses it for /collect and /p/.../container.json too — no separate config.Open Web Analytics > Custom Domain for the site. The UI walks you through:
A short, generic name is best (analytics, metrics, track). Avoid names that obviously hint at analytics from a blocklist's perspective (google-analytics, gtm, etc. — those are themselves blocked).
The UI checks the subdomain isn't already in use by another tenant before letting you proceed.
You'll be shown two records:
analytics.customer.example CNAME cnames.karmaflow.ai.
_karma-verify.customer.example TXT karma-verify=<one-time token>
cnames.karmaflow.ai (which itself resolves to the Cloud Run service via a wildcard)._karma-verify.customer.example and checks the token matches what we issued.Click Verify DNS. The server runs:
dns.promises.resolveCname('analytics.customer.example') — must include cnames.karmaflow.ai.dns.promises.resolveTxt('_karma-verify.customer.example') — must include the one-time token.DNS propagation can take a few minutes. The UI shows the current resolution state and re-checks on a 30-second loop.
Once DNS verifies, the certificate manager auto-provisions a cert for the new hostname via Google Cloud Load Balancer's managed certificates (which use Let's Encrypt). This takes 10–60 minutes depending on LB warm-up.
You can poll status from the UI; once the cert is active, the new hostname starts serving 200 OK.
The site's snippet automatically switches to the new hostname on the Sites > Snippet page — copy + paste the updated <script src=...> onto the customer site. The new src looks like:
<script async src="https://analytics.customer.example/p/16efa189f624da8c/k.js"></script>
Once deployed, the tracker reads document.currentScript.src, sees the new host, and routes every subsequent call there.
Almost nothing. The HTTP handlers don't look at Host for anything except logging. The key requirement is that Cross-Origin-Resource-Policy: cross-origin is set on every public asset (k.js, container.json, /collect/*/p) — that header was always required for the third-party-domain case and stays required for the CNAME case (the customer's own domain is still a cross-origin host relative to wherever the customer's main app runs).
The Access-Control-Allow-Origin header echoes the request's Origin header dynamically, so the same allow-list logic works whether requests come in via omni.karmaflow.ai or via the customer's CNAME.
Open DevTools on the customer site. With the CNAME live, you should see:
GET https://analytics.customer.example/p/SITE_KEY/k.js → 200GET https://analytics.customer.example/p/SITE_KEY/container.json → 200POST https://analytics.customer.example/collect/SITE_KEY → 202If any of those still go to omni.karmaflow.ai, the snippet wasn't updated.
If /collect/ returns from omni.karmaflow.ai but /p/.../k.js correctly loads from analytics.customer.example, there's a cached old version of the tracker still running. The 5-minute cache on k.js clears within ~5 min for visitors who hard-reload; gradual rollout is fine for the rest.
To stop using the CNAME without deleting the configuration:
omni.karmaflow.ai.To fully unprovision: delete the CustomDomain record from the UI. The cert is left in place for 24 h in case you change your mind, then auto-revoked.
If the customer uses EV (Extended Validation) certs, our managed cert won't match their certificate authority. You can either:
If the customer's domain is behind Cloudflare with the orange-cloud proxy on, Cloudflare strips the CNAME and serves directly from their edge. Disable the proxy for the analytics subdomain ("DNS only" / grey cloud) or use Cloudflare's CNAME flattening rules to ensure the request reaches us.
customer.example without subdomain)CNAMEs at the apex aren't legal per the DNS spec. Use analytics.customer.example, never customer.example. If the customer absolutely needs apex, they need ALIAS / ANAME records (provider-specific) or Cloudflare's CNAME flattening.
If the customer ever deletes the CNAME record but leaves the verification TXT, then later registers a different cnames.karmaflow.ai mapping under a different tenant, the new tenant would inherit the existing TXT verification. The UI defends against this by requiring the TXT to be re-verified every 90 days.
A first-party CNAME makes requests look first-party for the browser too. Cookies for analytics.customer.example are sent by the browser even though the server treats them as anonymous. The tracker explicitly sends credentials: 'omit' on fetch, but sendBeacon always sends credentials — this is one of the reasons we use a text/plain blob to avoid CORS preflight regardless of CNAME setup.
If the customer is in a privacy-sensitive jurisdiction (DACH, etc.), the CNAME effectively moves the data path closer to first-party — many privacy frameworks treat first-party analytics differently from third-party. Talk to legal before claiming this changes the compliance posture, though.