Architecture · Draft

First-Party Tag Relay (Kobico Relay) — Phase 2+

Created 11 Jun 2026·Updated 17 Jun 2026

Latest change: Restore kobi_* technical IDs; fix cmp_vendor enum

Draft document — deep-dive spec incomplete; content will be updated before and during build. Do not treat as signed-off implementation detail. Pack overview

Status: Deferred — not v1 scope. v1 uses API-provisioned pixels/CAPI plus implementation guide. This doc captures the recommended later architecture, cloud choice, and cost bands for planning.

Problem

Issue v1 approach Relay approach (later)
Third-party cookie limits CAPI + manual/GTM guide First-party subdomain cookies
Client installs many pixels Per-platform guide One DNS + one script
Ad blockers Platform domains blocked Requests to metrics.client.com
Ops at scale Per-tenant GTM publish Kobico-operated fan-out

Client effort (target)

Step Who Once per tenant?
CNAME metrics.{client-domain} → Kobico edge Client DNS Yes (first-party default)
One loader snippet (or GTM import that loads it) Client site / CMS Yes
Consent banner — Kobico CMP included Client approves copy + privacy policy URL in portal Yes for EEA/UK/TR — see Consent management

Not required: Separate CMP purchase before onboarding, Meta pixel, TikTok pixel, separate GTM publish per platform, client payment to a CMP vendor.

Loader delivery modes

Mode Script URL When
First-party (default) https://metrics.{client-domain}/k.js?tenant_id=… All production Relay SKUs — best cookies, blockers, Consent Mode
Third-party (opt-in) https://tracking.kobi.com/k.js?tenant_id=… Client cannot set DNS yet, pilot, or explicit choice — portal labels “reduced tracking mode”

Same manifest, modules, and relay backend for both modes. Third-party mode uses stricter QC — do not mark tracking green on parity with first-party without review.

Locked in ADR 0005.

Architecture options

A — Kobico Relay (preferred product path)

beacon / fetch same-originClient websitek.js on metrics.client.comEdge TLS + routingTag Relay serviceMeta CAPITikTok Events APIGA4 MeasurementProtocolGoogle EnhancedConversionsConversion TrackingService
  • Browser: thin loader collects page context, click IDs, consent, event_id
  • Edge: TLS for client hostname, rate limit, WAF
  • Relay: normalize vertical templates, dedup, hash PII, fan-out (same APIs as v1 CAPI relay)

B — Hosted server-side GTM (alternative SKU)

Same CNAME; origin runs GTM server container instead of custom relay. Industry-standard; heavier to automate multi-tenant.

Kobico Relay (A) Hosted sGTM (B)
Client install CNAME + one script CNAME + GTM web snippet
Kobico maintains Code + templates GTM container versions
Agent/QC integration Native Indirect
Google ecosystem Via MP / EC APIs Native GTM tags

Recommendation: Build A as default SKU; offer B only for GTM-mature clients.


k.js micro-module architecture

Locked: ADR 0005 — Pattern A bootstrap + cached chunks; hybrid Cloudflare edge + GCP Cloud Run origin.

Runtime flow

Cloud Run relayPlatformlistenersk-core.jsManifest KVCloudflare edgeClient websiteCloud Run relayPlatformlistenersk-core.jsManifest KVCloudflare edgeClient websiteGET k.js bootstrapResolve tenant manifestk.js + manifest referenceGET k-core.js cached chunkConsent KobicoCMP or externalCMPGET listener modules ifconsentedPOST collect same-origin or relay pathDedup consent gatefan-out

Module taxonomy

Layer Size target Responsibility Repo path (illustrative)
k.js bootstrap ≤1 KB gz Read tenant_id, fetch manifest src/bootstrap/
k-core.js ≤3 KB gz Click IDs (gclid, fbclid, ttclid), Consent Mode v2, cookie policy, relay POST, event_id, window.kobiConsent bridge src/core/
Global listeners Per module Stable platform APIs — Shopify, WooCommerce, generic dataLayer src/listeners/global/
Local listeners Per module Regional e-commerce DOM integrations — ikas, Ticimax, IdeaSoft src/listeners/local/

Rules: Local listeners depend only on k-core public API — no cross-imports between listeners. Each module ships as an immutable versioned URL with long edge cache. Marketing modules load only after marketing consent; analytics modules after analytics consent.

Tenant manifest (edge)

Resolved at onboarding and stored in edge KV/D1; referenced by bootstrap:

Field Example Purpose
tenant_id uuid Identity
delivery_mode first_party CNAME vs third-party loader
vertical ecommerce_tr Event schema / templates
platform ikas Primary local listener key
global_hooks ["woocommerce"] Optional extra global listeners
enabled_modules ["core","listener-ikas","ga4"] Edge-resolved load list
consent_profile tr_kvkk Legal strictness
cmp_mode kobi_cmp See Consent management

platform selects the primary regional adapter; vertical drives event names. Edge derives enabled_modules from plan SKU + platform + vertical — onboarding does not hand-pick ten flags.

Delivery pattern — Pattern A (default)

  1. Browser loads k.js (bootstrap).
  2. Bootstrap fetches manifest from edge (short TTL / ETag).
  3. Loads k-core.js (static, globally cached).
  4. Resolves consent (Kobico CMP default or external CMP bridge).
  5. Lazy-loads optional modules (listener-global-shopify.js, listener-local-ikas.js, …).
  6. Relay POST — first-party: same-origin on metrics.client.com; third-party: configured relay endpoint.

Not used for MVP: edge-stitched single file per tenant (Pattern B) or per-tenant CI bundles (Pattern C).

Phased module rollout

Relay ships incrementally — clients need not wait for every platform adapter:

Sub-phase Delivers Client experience
2a k-core + relay + Kobico CMP CNAME + one script; CAPI fan-out; implementation pack still useful for gaps
2b Global listeners Shopify / WooCommerce auto events
2c Local listeners ikas, Ticimax, IdeaSoft
2d Bundled / partner CMP options See Global CMP partnerships

Performance and cost budgets (planning)

Metric Target
k-core gzipped ≤ 3 KB
Typical extra modules after consent ≤ 2–3 cached chunks
Total JS before consent Bootstrap + k-core only
Manifest fetch Edge KV — no GCP round-trip
Relay POST Cloud Run — same fan-out as v1 CTS

Protects client PageSpeed / SEO and keeps edge transfer bounded at Growth scale (~50 tenants, ~10M req/mo — see Cost estimation).

API / scope compatibility

Relay fan-out uses the same endpoints as v1 — no special “server-side tagging scope”:

Destination API Already in onboarding?
Meta Conversions API (/{pixel-id}/events) ✅ Pixel + system user token
TikTok Events API (/event/track/) ✅ Pixel + OAuth
GA4 Measurement Protocol ✅ If client grants Admin / measurement ID
Google Ads Enhanced Conversions / offline upload ✅ Conversion actions
Internal CRM Webhook / CTS ✅ Phase 4

GTM Tag Manager API is only needed for option B (container publish) — not for Kobico Relay.


Yes. If Kobico Relay sets first-party cookies and fans out events to Meta, TikTok, GA4, and Google Ads, consent must be enforced before non-essential processing. A DNS record and loader alone do not satisfy GDPR, UK GDPR, or KVKK — the relay becomes part of the client’s tracking stack and must respect visitor choice.

This is separate from portal consents (ToS, data-sharing, Meta OAuth) in onboarding provisioning spec §7. Relay needs website visitor cookie consent in addition.

Responsibility split

Party Role
Client Privacy policy, lawful basis, banner copy approval, DPA with Kobico — per vision & scope
Kobico Implement consent gating in k.js + relay; Consent Mode v2; block fan-out when denied; audit-friendly logs
End user Accept / reject / customize via banner or existing CMP

Relay must not treat server-side fan-out as a loophole: if marketing consent is denied in the browser, the relay drops or anonymizes ad-personalization payloads (CAPI with restricted parameters, no new marketing cookies).

Category Examples Default (EEA/UK) Fan-out when denied
Necessary Relay session, security, consent storage Always on N/A — no ad platforms
Analytics GA4 page views, key events Opt-in Skip GA4 MP; optional aggregate internal metrics only
Marketing Meta CAPI, TikTok Events API, Google Ads EC, remarketing cookies Opt-in Block platform fan-out; no _fbp / ad cookies

Tenant config: consent_profile on tenant_registryeea_strict | uk_strict | tr_kvkk | row_light (still document; no silent opt-in where law requires opt-in).

CMP deployment modes (pick per tenant at onboarding)

Mode Client has Kobico delivers Default?
2 — Kobico CMP Nothing required Included banner with k.js — TR + EN MVP; client approves copy in portal Yes — Relay SKU default
1 — Client CMP Cookiebot, OneTrust, iubenda, CookieYes, etc. k.js listens to CMP API / dataLayer / TCF __tcfapi When client already runs a CMP
4 — Kobico-bundled CMP Legal/compliance prefers named vendor Kobico holds portfolio license; no separate client CMP invoice — cost in Relay bundle When partnership or resell active
3 — GTM hybrid GTM shop GTM import: CMP first, then k.js on consent update Agency clients

Product rule: Onboarding must not require “purchase a CMP elsewhere, then return.” Default path is Mode 2 (Kobico CMP included). Modes 1 and 4 are opt-in via portal Consent provider selector.

Default recommendation: cmp_mode = kobi_cmp at Relay enablement. Mode 1 when client BYO CMP is detected or selected. Mode 4 when leadership closes a bundled global CMP offer (partnerships).

Meta TikTok GA4AdsTag Relayk.js loaderClient or KobicoCMPClient websiteSite visitorMeta TikTok GA4AdsTag Relayk.js loaderClient or KobicoCMPClient websiteSite visitorNo ad platform fan-out whenmarketing deniedalt[Marketing granted][Marketing denied]Load first-party k.js scriptRead consent or show KobicobannerBanner if no prior choiceAccept reject or customizeconsent.analytics andconsent.marketingApply ConsentMode v2 andcookie policyEvent with consent snapshotand event_idFan-out per categoryNecessary pings only or norelay

Phase A — Tenant config (onboarding Relay SKU)

Field Purpose
delivery_mode first_party | third_party
consent_profile eea_strict / uk_strict / tr_kvkk / row_light
cmp_mode kobi_cmp (default) | client_cmp | kobi_bundled_cmp | gtm_hybrid
cmp_vendor kobi | cookiebot | onetrust | iubenda | …
platform Primary local listener — ikas, ticimax, ideasoft, …
vertical Event schema — tourism, ecommerce_tr, …
privacy_policy_url Required — linked from banner
banner_locale tr, en, de, …
google_consent_mode v2 enabled (default true for EEA/UK)

Store website consent config on onboarding_record.tracking_relay; link to portal consent_record for contractual data-sharing — two layers, one audit trail.

Phase B — Loader (k.js) behavior

  1. Default denied for analytics + marketing in EEA/UK until CMP fires consent_update.
  2. Expose window.kobiConsent bridge for client CMP integrations.
  3. Set Google Consent Mode v2 defaults (ad_storage, analytics_storage, ad_user_data, ad_personalization) before any hit.
  4. Set first-party cookies only when category allows (e.g. _fbp only if marketing granted).
  5. Queue events in memory until consent known; flush or purge on deny.
  6. Attach consent_snapshot (categories + CMP version + timestamp) on each relay POST — relay logs, does not re-interpret legal text.

Phase C — Relay server gates

Check Action if fail
consent.marketing !== true Drop Meta / TikTok / Ads fan-out; return 204
consent.analytics !== true Drop GA4 MP
Missing consent_snapshot on EEA tenant Treat as denied (strict)
Event contains PII + marketing denied Strip / hash per data & tracking privacy rules

Optional: persist anonymized consent metrics to BigQuery (relay_consent_events) for ops — not a substitute for CMP records.

Phase D — Platform-specific signals

Platform Relay behavior
Google Consent Mode v2 in loader; Enhanced Conversions only if ad_user_data granted
Meta CAPI data_processing_options / LDU where required; no pixel-equivalent cookies without marketing consent
TikTok Events API with consent flags per TikTok docs for region
GA4 MP with consent-aware parameters; respect analytics_storage

Pin exact parameter names at implementation — platform docs change; QC checker validates consent fields present for EEA tenants.

Phase E — Client CMP integration cookbook (Mode 1)

Document in implementation guide v2 — one snippet per major CMP:

CMP Integration hook
Cookiebot Cookiebot.consent + CookieConsent event
OneTrust OnetrustActiveGroups + OneTrustGroupsUpdated
iubenda _iub.cs.api
TCF v2 __tcfapi('getTCData') → map purposes to analytics/marketing

Kobico maintains a compatibility matrix; unknown CMP → default Kobico CMP or HITL onboarding step — never block Relay on external CMP procurement.

Global CMP partnerships and bundled resell

Partnership program (active — discuss with leadership / board): Pursue formal partnerships with global CMP vendors (Cookiebot, OneTrust, iubenda, CookieYes, and peers) for portfolio pricing, co-marketing, TR/EU localization, and support escalation. Partnership is on the table alongside Kobico CMP — not instead of it.

Track Kobico action Client sees
Kobico CMP (default) Build + operate banner on metrics.client.com Consent included in Relay SKU
Partnership CMP Signed reseller/OEM agreement with vendor Same — included; vendor named in subprocessor list
Bundled resell (interim) Kobico subscribes at portfolio level until partnership closes No separate CMP checkout — cost absorbed in media-tech / Relay bundle
Client BYO Integration only (client_cmp) Client keeps existing contract

If a vendor declines partnership, Kobico may still bundle that CMP at no incremental client charge (Pattern: portfolio seat allocation) rather than sending clients to procure consent tooling separately. Model fully loaded CMP cost (~€2–8/site/mo at SMB tiers) into existing €5–15/site/mo Relay planning band.

Exec checklist (partnerships): target vendor list, DPA/subprocessor terms, white-label vs co-brand, TCF registration responsibilities, TR locale coverage, rev-share vs flat portfolio fee.

Phase F — Kobico CMP (Mode 2) MVP scope

In scope Out of scope (v2 Relay MVP)
TR + EN banner strings (legal review) Full IAB TCF registration as CMP
Accept all / reject non-essential / customize (3 categories) Geo-IP auto-law detection (use tenant consent_profile first)
Consent cookie on metrics.client.com or registrable domain Replacing enterprise CMP for Fortune 500 clients
Re-open preferences link in implementation guide footer snippet

Banner UI: small bundle served from same subdomain as k.js; theme tokens from tenant brand colors.

Phase G — Ops, QC, and HITL

Signal Threshold Action
EEA tenant with zero consent grants but high event volume Any Block relay — config bug; System Ops alert
Consent grant rate unusually low < 5% over 7d Notify client — banner placement/copy
Relay fan-out without snapshot (EEA) > 0 QC fail; do not mark tracking healthy
Client changes privacy policy URL Re-acknowledge in portal (lightweight)

Add ONB-14 (future): Relay enabled but privacy_policy_url or consent_profile missing → block tracking_relay.active.

Workstream Duration Notes
Consent gating in loader + relay +1.5 weeks Included in Relay MVP row — now explicit
Client CMP integration matrix +1 week Top 4 CMPs + TCF stub
Kobico CMP (default, Mode 2) +1.5–2 weeks UI + locales + legal copy templates
Global CMP partnership / bundle ops Ongoing (exec) Portfolio licensing; not blocking 2a
Consent health dashboard +0.5 week Overlap with relay health row

Infra: Kobico CMP adds negligible cost (static assets on same edge as k.js). Third-party CMP seats absorbed in bundle when cmp_mode = kobi_bundled_cmp — see Global CMP partnerships.


Cloud provider recommendation

Default: GCP for compute + Cloudflare for edge (hybrid).

Layer Service Why
Static assets (k.js, k-core, modules) Cloudflare CDN Global cache; immutable versioned URLs
Tenant manifest Cloudflare Workers + KV/D1 Sub-ms manifest; no GCP hop for loader
Multi-tenant custom hostnames Cloudflare (SSL for SaaS / proxied CNAME) Cheap per-client TLS at 50–200+ tenants; global edge; DDoS; WAF
Relay logic GCP Cloud Run Same as orchestrator, connectors, CTS, Secret Manager, logging → BQ — one IAM and ops model (GCP topology)
Certificates (pilot only) GCP Certificate Manager + Global HTTPS LB OK for <15 tenants all-GCP pilot — fewer vendors
Secrets / tokens GCP Secret Manager Pixel tokens already planned here
Telemetry Cloud Logging → BigQuery Aligns with System Ops + QC

Why not AWS / Azure as primary?

  • Rest of Kobico Digital Ads is GCP-illustrative (Vertex, Cloud Run, BQ).
  • Splitting relay to Lambda@Edge or Azure Front Door adds second billing, IAM, and runbooks for marginal savings at pilot scale.
  • Revisit only if entire Kobico platform standardizes elsewhere.

Why not Workers-only (no GCP)?

  • Cloudflare Workers can host the full relay (very cost-effective).
  • Downside: platform tokens, CTS, and tenant registry live on GCP — cross-cloud latency, dual deploy, harder trace from ad event → agent QC.
  • Compromise: Cloudflare edge (SSL, cache, WAF) → GCP Cloud Run origin — best performance/cost/ops balance.

Region

  • Relay origin: europe-west1 (match GCP topology) unless tenant data residency requires otherwise.
  • Cloudflare: automatic anycast edge (client-facing latency minimized globally).

Cost estimation

Planning bands only. Verify against Cloud Run, Cloud Load Balancing, Cloudflare plans, and Certificate Manager before build. Prices USD, order-of-magnitude, relay infra only (excludes Vertex, ads APIs, ERP).

Traffic assumptions

Profile Tenants Page views / tenant / month Relay requests / tenant / month¹
Pilot 5 25,000 ~100,000
Growth 50 50,000 ~200,000
Scale 200 100,000 ~400,000

¹ Loader load + page/events + retries. Fan-out to Meta/TikTok/GA4 is outbound from Cloud Run (small payloads; egress cost negligible vs ingress).

Monthly infrastructure (USD)

Profile Total requests/mo Hybrid (CF + GCP Run) All-GCP (LB + Run + certs)
Pilot (5 tenants) ~0.5M $25–45 $40–70
Growth (50 tenants) ~10M $80–150 $180–320
Scale (200 tenants) ~80M $350–650 $900–1,600

Hybrid breakdown (Growth example, ~10M requests/mo):

Line item Est. monthly
Cloud Run (relay + fan-out CPU) $35–70
Cloudflare Pro/Business + custom hostnames (~50) $25–55
GCP logging / BQ relay telemetry (light) $10–25
Secret Manager, Pub/Sub (optional async buffer) $5–15
Total ~$80–150

All-GCP premium drivers: Global HTTPS LB (~$18+ base + LCU), Certificate Manager per hostname, no edge cache for k.js unless Cloud CDN added.

Marginal cost per tenant (steady state, hybrid)

Tenant traffic Est. infra / tenant / month
Low (25k page views) $1–3
Medium (50k page views) $2–5
High (200k page views) $6–12

Pass-through pricing to clients (optional SKU): media-tech fee €5–15 / site / month easily covers infra at Growth scale.

One-time engineering (build)

Workstream Duration Notes
Relay service (ingest, dedup, fan-out) 4–5 weeks Cloud Run, vertical templates
k-core + bootstrap (Pattern A) 2 weeks Manifest service, edge routing
Consent (Kobico CMP default + client CMP matrix) 2–3 weeks See Consent management
Edge onboarding (CNAME verify, TLS, tenant routing) 2 weeks Cloudflare for SaaS
Global + local listeners (phased 2b/2c) 2–4 weeks After 2a pilot — per platform
Dashboard health (volume, EMQ proxy, consent) 1 week Ties to data & tracking
Hosted sGTM path (optional B) +2 weeks Only if product requires
Total MVP (2a — core + relay + Kobico CMP) ~10–12 weeks 1–2 engineers; 2b/2c additive

Opportunity cost vs v1 guide: v1 avoids this build; relay pays off at ~20+ active sites or when client IT friction dominates onboarding SLA.


Multi-tenant design notes

Concern Approach
Host routing Host: metrics.client.comtenant_id in registry
Third-party loader Shared hostname tracking.kobi.com — opt-in only
Isolation Per-tenant rate limits; no cross-tenant tokens in memory
k.js delivery Pattern A — bootstrap + cached k-core + lazy modules
Manifest Edge KV/D1 — vertical, platform, enabled_modules
Consent Kobico CMP default; client/partner CMP via bridge — Consent management
Dedup Browser event_id + server id → Meta/TikTok requirements
Domain verify (platform) Still separate from CNAME — onboarding

Phasing

Phase Tracking delivery
v1 API pixels + CAPI + implementation guide (manual / GTM import template)
v2a Relay core — CNAME (or third-party opt-in) + k-core + Kobico CMP + server fan-out
v2b + Global listeners (Shopify, WooCommerce)
v2c + Local listeners (ikas, Ticimax, IdeaSoft)
v2d + Bundled / partner global CMP options
v2+ alt Hosted sGTM for GTM-native clients

See Roadmap — Phase 2+ optional.