Skip to content
Docs

Self-hosting

Environment variables

Every DT_* token found in the Go sources under services/ and packages/, plus the portal variables, is described here. Defaults are the values compiled into the binary; the development stack overrides several of them (ports 2xxxx and 1808x).

Durations are Go durations (30s, 5m) unless the name ends in _SECONDS or _MS. Boolean switches are enabled by exactly 1 unless stated otherwise.

Shared by several services

Connection settings and switches read by more than one binary.

VariableDefaultDescription
DT_ADDR127.0.0.1:18080 (edge), :18081 (ingest), :18082 (api), :18083 (reporting); workers listen on 18084Listen address of the service. Each binary has its own default.
DT_NATS_URLnats://127.0.0.1:24222NATS server used by workers (and shared helpers). Creates or binds the JetStream streams on start.
DT_REDIS_ADDR127.0.0.1:26379Redis address used by workers (dedupe tokens, counters, rate limits).
DT_REDIS_URLRedis URL used by the API for shared per-API-key rate-limit buckets across replicas and for cap reports. Unset: in-process buckets.
DT_CH_ADDR127.0.0.1:29000ClickHouse native address used by workers.
DT_CH_USERdtClickHouse user used by workers. DT_ENV=production refuses the default and dt users: create a named user with a password (the production profile does).
DT_CH_PASSWORDdtClickHouse password used by workers.
DT_CH_DATABASEdtrackierClickHouse database used by workers.
DT_INTERNAL_TOKENShared secret for service-to-service calls (API to reporting proxy, API to ingest for fraud restatement, workers to reporting, call conversion). Set the same value on every service whenever they are not on loopback.
DT_SECRETS_KEY_PREVIOUSRetired DT_SECRETS_KEY values, comma-separated, newest first. Sealed values are tagged with the id of their key; services open old ones with these while workers rotate-secrets re-seals everything with DT_SECRETS_KEY. Each entry 32+ characters in production. See Self-hosting, Secrets and key rotation.
DT_REDIS_PASSWORDPassword for every Redis client (requirepass / ACL user password). Required in production unless every Redis URL carries one: DT_ENV=production refuses unauthenticated Redis. Unset in the dev stack.
DT_REDIS_USERNAMERedis ACL user name used with DT_REDIS_PASSWORD. Unset: the default user.
DT_REDIS_TLS01 enables TLS for Redis addresses given as host:port (a rediss:// URL enables it on its own).
DT_REDIS_TLS_CAPEM file of the CA that signed the Redis server certificate (enables TLS).
DT_REDIS_TLS_SERVER_NAMEServer name to verify in the Redis TLS certificate when it differs from the address host.
DT_NATS_USERNATS user for every NATS client when the URL carries no credentials (with DT_NATS_PASSWORD). DT_ENV=production refuses a NATS URL without credentials.
DT_NATS_PASSWORDPassword of DT_NATS_USER. In the production compose profile it is also the server password embedded in every DT_*NATS_URL.
DT_NATS_TOKENNATS token authentication for every NATS client (instead of user/password).
DT_NATS_TLS01 requires TLS to NATS (a tls:// URL does the same).
DT_NATS_TLS_CAPEM file of the CA that signed the NATS server certificate (enables TLS).
DT_SECRETS_KEYKey that seals stored secrets (payment methods, provider tokens, import source credentials, BI connectors) with AES-GCM. A development fallback is used when unset; always set it in production.
DT_ALLOW_PRIVATE_DESTINATIONSSet to 1 to let outbound requests (postbacks, buyer URLs, list syncs, data-locker endpoints, import sources) reach private or loopback addresses. Off by default as SSRF protection; needed for local MinIO and localhost testing.
DT_SMTP_ADDR127.0.0.1:21025SMTP relay for system email when a tenant has no email provider of its own.
DT_SMTP_FROMnoreply@dtrackier.localEnvelope and From address of system email.
DT_PLATFORM_LOGO_URLPublic http(s) URL of the Metrixed logo PNG shown in the header of platform email (mail clients cannot render SVG); unset, the header is the name in text. scripts/brand-assets.mjs writes the image to apps/www/public/brand/metrixed-email-header.png.
DT_PUBLIC_URLhttp://127.0.0.1:18080Public base URL of the tracking host; used to build signed click and unsubscribe links in campaigns and communications.
DT_PORTAL_URLBase URL of the portals, prefixed to relative links in notification emails.
DT_PORTAL_BASE_URLhttp://localhost:3100Base URL of the admin portal; default origin for the public status page and invitation links.
DT_PORTAL_PARTNER_URLOrigin of the partner portal used in partner invitation and password-reset links. Default: the tenant's first public partner portal domain (development: http://partner.localhost:3101).
DT_PORTAL_ADVERTISER_URLOrigin of the advertiser portal used in advertiser invitation and password-reset links. Default: the tenant's first public advertiser portal domain (development: http://advertiser.localhost:3102).
DT_REPORTING_URLhttp://127.0.0.1:18083Reporting service URL used by workers (automation rules read metrics through it).
DT_NOTIFY_FALLBACK_TOComma list of mailboxes that receive notification email when a tenant has no recipient employees.
DT_ENVdevelopmentDeployment mode. production makes every service refuse to start with dev defaults (dev passwords, superuser DSNs, short or default DT_INTERNAL_TOKEN, missing DT_SECRETS_KEY).
DT_JS_MAX_BYTES_WP-85: prefix of the JetStream byte caps, DT_JS_MAX_BYTES_ (DT_CLICKS, DT, DT_CONVERSIONS, DT_POSTBACK_IN, DT_MOBILE, DT_INGEST_RECOVERY): "48GiB", "500MB", bytes; 0 = uncapped. Every stream is created with its cap and discard new (a full stream refuses publishes; the edge / ingest spools keep the events). The caps reserve NATS storage: their sum must fit max_file_store (else halved, then uncapped, with a log error). Defaults (dev / CI, ~1.7 GiB in total): 768 MiB, 384 MiB, 192 MiB, 192 MiB, 64 MiB, 128 MiB; production: docs/ops/SIZING.md section 6.
DT_JS_MAX_BYTES_DT_CLICKS768MiBWP-85: byte cap of the edge click stream DT_CLICKS (see DT_JS_MAX_BYTES_). Production default 16GiB (deploy/production/docker-compose.yml): ~7 days at 100 M clicks a month.
DT_JS_MAX_BYTES_DEFAULT64MiBWP-85: byte cap of a dt.* stream without its own DT_JS_MAX_BYTES_.

API (services/api, port 18082)

The control-plane API. Database role must be dt_app so row-level security applies.

VariableDefaultDescription
DT_API_DATABASE_URLpostgres://dt_app:dt_app@127.0.0.1:25432/dtrackierPostgres connection string of the API. Must use the dt_app role; every request runs in a transaction with app.tenant_id set.
DT_API_DEFAULT_TENANTTenant slug that answers public requests (portal branding, partner signup) arriving on hosts not mapped to any tenant. For single-tenant installs; unset in multi-tenant deployments.
DT_SAAS_SIGNUPSelf-service workspace signup (POST /v1/workspaces/signup). On by default outside production; production needs on plus DT_SAAS_PORTAL_DOMAIN. off disables it everywhere.
DT_SAAS_PORTAL_DOMAINlocalhost (dev)Base domain for new workspaces' portal hosts: .partner. and .advertiser.. Required in production for signup.
DT_SAAS_ADMIN_URLDT_PORTAL_BASE_URLAdmin portal origin used in workspace verification links (/verify-workspace).
DT_SAAS_SIGNUP_ORIGINSComma-separated origins (the marketing site) allowed to call the workspace signup endpoints cross-origin. Outside production http://localhost:* and http://127.0.0.1:* are also allowed.
DT_SAAS_SIGNUP_PER_IP_HOUR10Workspace signups accepted per client IP per hour (counted from stored signups).
DT_SAAS_SIGNUP_PER_IP_MIN5Workspace signup token bucket per client IP per minute.
DT_SAAS_TRIAL_DAYS14Trial length recorded on new workspaces (informational; trial expiry is not enforced automatically).
DT_ORG_SELF_SIGNUP0WP-63 owner-decision flag for self-serve organization signup. Organizations are created by platform operators; the flag only changes the org sign-in page hint (1 = say signup exists). Off by default.
DT_SAAS_TRIAL_LIMITSJSON limits for new workspaces, e.g. {"seats":3,"domains":1,"clicks_per_month":100000}. Unset: unlimited until an operator sets limits.
DT_API_TRUSTED_PROXIESComma-separated CIDRs/IPs of reverse proxies (portal servers, load balancer) whose X-Forwarded-For is trusted for the client IP used in rate limits, IP allowlists and audit logs. Unset: the socket peer is used.
DT_API_PUBLIC_URLPublic URL of the API, used for SSO redirect and callback URLs (SAML/OIDC).
DT_ANTHROPIC_API_KEYAnthropic API key for the in-app AI assistant (admin ⌘J and partner portal). Unset: the assistant is hidden. The key stays on the API server; browsers talk to /v1/assistant/chat.
DT_MCP_PUBLIC_URLPublic origin MCP clients reach the API on (e.g. https://app.example.com); used for the MCP endpoint URL shown in the portals and the protected-resource metadata. Unset: the request host.
DT_MCP_RATE_PER_MINUTE120Requests per minute one MCP token may send to /v1/mcp (429 beyond).
DT_AI_MODELclaude-opus-5-5Claude model used by the AI assistant.
DT_AI_PROVIDERSet to fake to run the assistant with a deterministic offline provider (demos and portal e2e tests) instead of the Anthropic API.
DT_ANTHROPIC_BASE_URLOverride the Anthropic API base URL for the assistant (an egress proxy or gateway). Unset: the public Anthropic API.
DT_API_REPORTING_URLReporting service the API proxies /reports, /logs, /dashboard and conversion search to.
DT_API_INGEST_URLhttp://127.0.0.1:18081Ingest base URL used by the link/postback testers and for fraud restatements.
DT_API_INGEST_PUBLIC_URLPublic ingest origin shown in app S2S/SDK setup instructions.
DT_API_EDGE_URLhttp://127.0.0.1:18080Edge base URL used by the link tester. Testers refuse any other host.
DT_API_WORKERS_URLWorkers base URL for webhook replay and the firehose SSE proxy.
DT_API_REDIS_ADDRRedis used by call tracking for number-pool publishing and lease lookup.
DT_API_S3_ENDPOINT127.0.0.1:29002S3-compatible endpoint for creative uploads (bucket dt-creatives).
DT_API_S3_KEYdtminioAccess key for creative storage.
DT_API_S3_SECRETdtminio-secretSecret key for creative storage.
DT_API_S3_SSLSet to 1 to use TLS towards the creative storage endpoint.
DT_API_S3_PUBLIC_ENDPOINTEndpoint used in links handed to browsers (anonymous pub/ objects and presigned priv/ URLs) when it differs from the internal endpoint.
DT_API_S3_PUBLIC_SSLSet to 1 when the public creative endpoint uses TLS.
DT_CALLS_INGEST_URLhttp://localhost:18081Ingest origin embedded in the dynamic number insertion snippet and used to create call conversions (also read by the calls worker).
DT_TWILIO_BASE_URLOverrides the Twilio REST base URL (tests and proxies).
DT_TAX_ONLINESet to 1 to run live VIES and HMRC VAT checks. Without it the tax checker is offline.
DT_HEALTH_TARGETSedge, ingest, api, reporting, workers on 127.0.0.1:18080-18084Comma list name=url of the services probed by Settings, System health (their /readyz).
DT_STATUS_TENANTTenant id that owns the public status page served as /v1/status/public/default.

Edge redirect (services/edge-redirect, port 18080)

The click path. Reads config from a Postgres-backed snapshot cached in Redis and memory.

VariableDefaultDescription
DT_EDGE_PLAN_REFRESH10sHow often the edge reloads the set of workspaces whose traffic is blocked (Redis dt:saas:blocked_tenants). Go duration, min 1s.
DT_EDGE_PG_DSNPostgres DSN from which the edge builds its config snapshot. Unset: only the snapshot file or the loopback /internal/snapshot endpoint feed it.
DT_EDGE_REDIS_ADDRRedis for the snapshot cache, caps, SmartLink round-robin counters and pending deep-link payloads.
DT_EDGE_REDIS_TIMEOUT_MS50Per-call Redis budget on the click path (1-200 ms; dedupe and click caps are one call per click). Past it the click still redirects: duplicate detection degrades open (counted unique) and an offer with a hard click cap serves its fallback, recorded as caps_degraded (dt_edge_state_degraded_total). WP-83: 50 ms (was 5 in code, 20 in every stack): on a 2-vCPU node 20 ms served 2.4 % of duplicates as unique (nightly oracle large, client p99 10 ms); 50 ms served 0.05 % with p99 22 ms on a loaded 2-CPU stack. Only clicks whose reply is late wait longer.
DT_EDGE_REDIS_BREAK_AFTER32Consecutive Redis transport failures before the edge stops calling Redis for 1 s (circuit breaker). Too low and one short stall of a busy Redis turns into a second of degraded clicks.
DT_EDGE_NATS_URLnats://127.0.0.1:24222NATS server that receives click events (stream DT_CLICKS).
DT_EDGE_SNAPSHOT_FILEconfig_snapshot JSON loaded at boot (development and replay).
DT_EDGE_REGIONRegion tag written to every click event as edge_region.
DT_EDGE_TRUST_PROXYSet to 1 to honour X-Forwarded-For for the visitor IP. Only behind a trusted proxy.
DT_EDGE_GOGC400Go GC target percent for the redirect process (higher = less GC CPU, more heap). Ignored when GOGC is set; 100 restores the Go default.
DT_EDGE_PORTAL_UPSTREAMSPortal upstreams for portal-type custom domains reaching the edge through the TLS proxy, as type=url pairs: partner_portal=http://partner:3000,advertiser_portal=http://advertiser:3000,admin_portal=http://admin:3000. A request whose host is a tracking domain of that type is proxied there with its Host kept; everything else is a click. Unset = no portal routing.
DT_EDGE_TLS_ADDRHTTPS listen address. With it set the edge terminates TLS using on-demand ACME certificates stored in Postgres; HTTP-01 is answered on the plain listener.
DT_ACME_DIRECTORYstagingACME directory: empty or staging for the Let's Encrypt staging CA, prod for production, a URL for a custom CA, none to disable.
DT_ACME_EMAILContact email registered with the ACME account.
DT_EDGE_SPOOL_DIR<tmp>/metrixed-spool/edge-<port>Directory of the click spool (WP-74): every click event is appended here before the redirect returns and published from it; unacknowledged events are republished at the next start. One directory per edge instance (locked); give it a persistent volume in production.

Ingest (services/ingest, port 18081)

Postbacks, pixels, S2S events, mobile SDK endpoints and integrations.

VariableDefaultDescription
DT_CAPS_EPOCH1Set to 0 to turn off the cap counter epoch check (ingest, edge and the workers keeper). On (default), a tenant whose cap counters Redis lost defers hard-capped conversions and sends hard-capped clicks to the fallback until the workers rebuilt the counters.
DT_INGEST_PG_DSNPostgres DSN for conversion keys, API keys, mobile apps/devices and the config snapshot.
DT_INGEST_REDIS_ADDRRedis for click cache, conversion dedupe, caps and mobile state.
DT_INGEST_NATS_URLnats://127.0.0.1:24222NATS server that receives conversion and mobile events.
DT_INGEST_CH_URLClickHouse endpoint used as the fallback for click lookups when Redis misses.
DT_INGEST_CONVERSION_RULES1WP-80: set to 0 to turn off conversion rules (reject / hold / mark at conversion time, rule sets with event_scope conversion).
DT_INGEST_SKU_PAYOUTS1WP-80: set to 0 to turn off per-SKU pricing of basket lines (offers.sku_payouts).
DT_INGEST_CH_USERdtClickHouse user for ingest.
DT_INGEST_CH_PASSWORDdtClickHouse password for ingest.
DT_INGEST_CH_DBdtrackierClickHouse database for ingest.
DT_INGEST_SNAPSHOT_FILEconfig_snapshot JSON loaded at boot (development and replay).
DT_INGEST_API_KEYSStatic ingest API keys as rawkey=tenant_id pairs separated by commas (development and replay). Normally keys come from Postgres.
DT_INGEST_TRUST_PROXYSet to 1 to honour X-Forwarded-For.
DT_INGEST_CLICK_WAIT_MS2000Upper bound for re-reading a click that a conversion names when the lookup failed (Redis timeout, circuit open) or the click id is one of ours minted in the last 5 minutes and still on its way into the click cache. 0 disables (miss = unmatched no_click at once).
DT_INGEST_RECENT_CLICKS50000WP-84: how many clicks this ingest process keeps in memory from the click stream it consumes (the click-cache consumer remembers each click before writing it to Redis). A postback for one of them is matched without a Redis read, and a postback that arrives before its click is woken the moment the consumer takes it instead of re-reading Redis on a timer (dt_ingest_recent_click_hits_total, dt_ingest_click_wait_woken_total, gauge dt_ingest_recent_clicks). About 600 bytes per click; oldest evicted first; 0 disables. With several ingest replicas each holds its share of the stream and the others fall back to Redis.
DT_CLICK_CACHE_TTLWP-85: fixed hot window of the click cache for every tenant ("10d", "36h", "90s" or seconds). Unset (default): per tenant, the longest conversion window of its offers (session duration or CTIT max, the shorter; + 1 day for late postbacks), clamped to DT_CLICK_CACHE_TTL_MIN..DT_CLICK_CACHE_TTL_MAX; an offer without a window gives the maximum. A click expires at its click time + the window; conversions on older clicks resolve from ClickHouse (docs/03 section 7, docs/ops/SIZING.md section 5).
DT_CLICK_CACHE_TTL_MAX45dWP-85: longest hot window of the click cache (the pre-WP-85 fixed 45 days). Must stay below the ClickHouse clicks TTL (90 days).
DT_CLICK_CACHE_TTL_MIN1dWP-85: shortest hot window, and the conclusive-miss floor: a Redis miss for a click id minted less than this long ago is final; for an older id ClickHouse is the store of record and a lookup it cannot answer defers the postback (never no_click). Lowering it is safe; raise it only after the old value's worth of time.
DT_CLICK_CACHE_LATE_TTL1hWP-85: how long a click that reaches the cache already past its hot window (backdated, replayed, a long spool drain) is kept, and how long a click found in ClickHouse is written back (resends hit Redis). 0: not cached (dev / oracle: forces the ClickHouse path).
DT_CLICK_CACHE_FORMATcompactWP-85: json keeps writing the pre-WP-85 JSON click-cache values (a rolling deploy with ingest replicas older than WP-85: they read only JSON). Default: the compact format (every WP-85 replica reads both).
DT_INGEST_DURABLE_CLAIMS1WP-86: 0 turns off the claim of record (conversions decided on Redis claims alone, conversion_keys written asynchronously — the pre-WP-86 path). Default on when Postgres is configured: every new conversion's dedupe claims are confirmed in Postgres conversion_keys (one statement) before it is created, so a Redis that lost its data never turns a resend into a second conversion; Postgres not answering defers the postback (durable_claim_unavailable).
DT_INGEST_CLAIMS_SYNC_COMMIToffWP-86: on makes each claim-of-record commit wait for the WAL flush (+~5 ms per new conversion on the dev box). Default off: commits are visible at once, a Postgres crash can lose the last ~0.6 s of claims (which matters only if Redis loses the same keys too).
DT_INGEST_CLAIMS_TIMEOUT_MS1000WP-86: budget of one claim-of-record statement on the request path; past it the postback is deferred (durable_claim_unavailable). A starved node waits rather than defers: a deferral also queues the offer's later postbacks behind it.
DT_INGEST_CLAIMS_POOL16WP-86: connections of the claim-of-record pool (its own pool, synchronous_commit off; they count against the Postgres connection limit, per ingest replica).
DT_CONV_HOT_TTL45dWP-86: how long Redis keeps conversion state, the transaction index and the dedupe claims (was 400 d); past it ClickHouse (state, txn ids) and Postgres (claims) are the source. Accepts 45d or a Go duration. Rolling deploy: 400d until every ingest is WP-86.
DT_CONV_STATE_FORMATcompactWP-86: json keeps writing JSON conversion state (a rolling deploy with ingest replicas older than WP-86: they read only JSON). Default: the compact format (WP-86 replicas read both).
DT_CONV_EPOCH1WP-86: 0 turns off the conversion dedupe epoch marker (ingest then treats a Redis miss as conclusive within DT_CONV_HOT_TTL; the workers stop keeping the marker). Default on: a miss of anything written before the marker's since (Redis lost data) goes to ClickHouse / Postgres. Deploy the workers first, or set 0 on ingest for the first roll.
DT_CONV_COLD_SETTLE10mWP-86: a conversion minted less than this ago that ClickHouse does not have, and Redis cannot vouch for, is "unknown" (deferral / 503), not "not found": its events may not have reached ClickHouse yet. WP-87: also how long a resend after a Redis loss waits for a holder of record whose process is gone before adopting it (was 2 h).
DT_INGEST_COLD_OLD_TIMEOUT_MS60WP-85: budget of the ClickHouse lookup of a click older than DT_CLICK_CACHE_TTL_MIN (waiting for a limiter slot included); past it the postback is deferred (cold_click_unavailable) and its replay retries with 2 s. Clicks younger than the floor keep the 8 ms best-effort fallback.
DT_INGEST_CONSUMER_SUFFIXPrivate instance only (accuracy harness): suffix for the JetStream click consumers so a second ingest on the shared NATS gets its own copy of the click stream.
DT_INGEST_SDK_FILEpackages/web-sdk/dist/dtk.jsFile served as /sdk.js (the web SDK build).
DT_INGEST_SDK_ORIGINSComma list of origins allowed by CORS on /p and /sdk/v1/cookie (web SDK).
DT_INGEST_PAYOUT_HOOKSet to 0 to switch off the payout-engine hook (overrides, tiers, FX locking) in ingest.
DT_INGEST_SHOPIFY_SECRETHMAC secret that verifies Shopify webhooks on /integrations/shopify/webhook.
DT_INGEST_SHOPIFY_TENANTTenant that receives Shopify conversions.
DT_INGEST_BIGCOMMERCE_SECRETShared secret expected in X-DTK-Webhook-Secret on the BigCommerce webhook.
DT_INGEST_BIGCOMMERCE_TENANTTenant that receives BigCommerce conversions.
DT_INGEST_BIGCOMMERCE_API_URLBigCommerce store API base URL used to read the cart metafield that carries the click id.
DT_INGEST_BIGCOMMERCE_API_TOKENBigCommerce API token for the metafield lookup.
DT_INGEST_MOBILE_MEMORYSet to 1 to keep mobile state in memory instead of Postgres/Redis (replay and tests).
DT_INGEST_MOBILE_NO_NATSSet to 1 to skip publishing mobile events to NATS (replay and tests).
DT_INGEST_DNI_REDIS_ADDRRedis for dynamic number insertion leases; falls back to DT_INGEST_REDIS_ADDR.
DT_INGEST_Prefix shared by all ingest settings listed above. Ingest settings are always spelled DT_INGEST_, never the generic variable.
DT_INGEST_RECOVERY_WORKERS8Deferred conversions and state repairs the ingest replays at once from its recovery queue (one conversion's items stay in order).
DT_INGEST_THROTTLE_HOOKSet to 0 to disable the ingest postback-throttling hook (it is a no-op for networks without the add-on).
DT_INGEST_CLAIM_OWNER1WP-79: bind every dedupe claim to this process (an epoch kept alive by the Redis heartbeat dt:ingest:owner:, 5 s lease). A retry after an ingest crash adopts the dead attempt's claim at once with the same conversion id; behind a live attempt it is queued. 0 = claims without an owner (WP-74: adopted only after 30 s, a retry before that is answered duplicate).
DT_INGEST_REDIS_TIMEOUT_MS100WP-82: cap on every ingest Redis call on the request path (the circuit opens after failures spanning twice this). Ingest is not the redirect: on a 2-vCPU node a 0.3 ms Redis reply arrives 26 ms late at p99 because it waits for a CPU, and the old 20 ms cap held the circuit open (96 % of a sustained load deferred). Recovery replays, the owner heartbeat and the recovery health probe get 500 ms per call regardless.
DT_INGEST_CONFIG_LOAD_MS3000WP-82: budget of the synchronous load of a tenant's payout-engine / throttle configuration (one load per tenant and snapshot version; concurrent conversions wait for it). A configuration that cannot be loaded defers the inbound to the recovery queue instead of pricing it without overrides or tier multipliers.
DT_INGEST_ORDER_HOLD_MS60000WP-82 / WP-87: the bound of arrival order. No postback waits longer than this (from its own arrival) behind queued conversions that could move the same cap window, tier or ladder, and a queued conversion stops holding (reserving room for) later arrivals this long after it was queued. Past it the conversion is decided with the counters as they are and records order_hold_expired (event annotation, inbound log reason, dt_ingest_order_hold_expired_total). 0 = no arrival-order fence.
DT_INGEST_ORDER_SCOPE1WP-87: 0 restores the WP-82 rule (every later postback of an order-dependent offer waits behind any queued conversion of the offer) and turns the first-arrival order of transaction copies off — the escape hatch. Default: a later postback waits only when its decision can depend on a queued one (count windows reserve room for the queued conversions; money windows, revenue / sales tiers and customer-value ladders wait per window).
DT_INGEST_REDIS_PATIENCE_MS2000WP-87: how long the request path keeps retrying a Redis (or claim-of-record) call whose first try failed before the postback is deferred and answered queued (it was 300 ms); an attempt that needed a retry gives its later calls the same budget. A slower answer instead of a queued one; a Redis that stays away still defers after it.
DT_REDIS_KEY_FORMATshortWP-87: legacy keeps writing the pre-WP-87 key names of the click cache, conversion state, transaction index and dedupe claims (dt:click:, dt:conv:, dt:convtxn:, dt:convdedupe:) — set it on the new build during a rolling deploy with older ingest replicas (they read only the old names), then drop it. Default: the short names dt:ck:, dt:cs:, dt:ci:, dt:cd:.
DT_REDIS_LEGACY_KEYSWP-87: reads also look at the old key names (same round trip) until they have expired — automatically off once the first short-name process (Redis marker dt:keyfmt:short) started longer ago than max(DT_CONV_HOT_TTL, 46 d) + 1 d. 0 turns the fallback off at once, 1 keeps it on.
DT_INGEST_CLICKCACHE_WORKERS8Concurrent writers of the click cache (dt.click.created.v1 → Redis, WP-74). One writer capped the cache at ~1 300 clicks/s; behind it, postbacks for fresh clicks are deferred.
DT_INGEST_SPOOL_DIR<tmp>/metrixed-spool/ingest-<port>Directory of the conversion-event spool (WP-74): conversion events are appended here before the postback is answered and published from it; unacknowledged events are republished at the next start. One directory per ingest instance (locked); persistent volume in production.

Reporting (services/reporting, port 18083)

Reached only through the API; reads ClickHouse.

VariableDefaultDescription
DT_REPORTING_PG_DSNpostgres://dt_app:dt_app@127.0.0.1:25432/dtrackierPostgres DSN for saved reports, FX rates and entity names.
DT_REPORTING_CH_ADDR127.0.0.1:29000ClickHouse native address.
DT_REPORTING_CH_USERdtClickHouse user.
DT_REPORTING_CH_PASSWORDdtClickHouse password.
DT_REPORTING_CH_DBdtrackierClickHouse database.
DT_REPORT_CACHE_MB128Report result cache budget in MB (rows of analytics queries keyed by tenant, audience, data watermark and query). 0 disables the cache.
DT_REPORT_CACHE_TTL10mLongest a cached report result is kept (Go duration). New or restated data invalidates earlier through the watermark.
DT_REPORT_CACHE_WM_MS1000How long a tenant's data watermark (ClickHouse report_watermark) is reused before it is read again, in milliseconds: the most a cached result can lag new data.
DT_REPORTING_NO_DAILYSet to 1 to stop the planner from reading the agg_daily rollup (every aggregate report reads agg_hourly). Kill switch; results are identical either way.
DT_REPORTING_NO_DAILY_TZSet to 1 to stop the planner from reading the agg_daily_tz rollup for non-UTC tenants (their reports read agg_hourly or raw as before). Kill switch; results are identical either way.
DT_REPORTING_S3_ENDPOINT127.0.0.1:29002S3-compatible endpoint where report exports are stored and presigned.
DT_REPORTING_S3_BUCKETdt-exportsBucket for report exports.
DT_REPORTING_S3_KEYdtminioAccess key for exports.
DT_REPORTING_S3_SECRETdtminio-secretSecret key for exports.
DT_REPORTING_S3_SSLSet to 1 for TLS towards the export endpoint.

Workers (services/workers, port 18084)

Stream consumers and schedulers in one binary.

VariableDefaultDescription
DT_DOMAIN_DEV_EDGE_ADDR127.0.0.1:18080Development only: host:port of the local edge used to health-check *.localhost tracking domains over plain HTTP (they have no certificate).
DT_SAAS_USAGE_EVERY1mHow often the plan-usage job counts each subscribed workspace's monthly clicks and republishes the edge block list (Go duration, min 5s).
DT_WP71B_TZSYNC_EVERY1mHow often the tz-sync job registers each tenant's reporting time zone in ClickHouse tenant_tz_join for the tenant-zone daily rollup (Go duration, min 1s).
DT_CAP_EPOCH_EVERY2sHow often the workers check every tenant's cap counter epoch marker in Redis (and rebuild lost counters).
DT_CAP_EPOCH_SETTLE10sWait after a lost cap counter set is detected before counting from ClickHouse and Postgres, so events in flight are included.
DT_CONV_EPOCH_EVERY2sWP-86: how often the workers check every tenant's conversion dedupe epoch marker and renew a missing or stale one.
DT_CONV_KEYS_RETENTION400dWP-86: conversion_keys rows (the claim of record) older than this are deleted by the hourly retention pass; 0 keeps them forever.
DT_CONV_KEYS_TRIM_EVERY1hWP-86: interval of the conversion_keys retention pass.
DT_WORKERS_DATABASE_URLpostgres://dt_app:dt_app@127.0.0.1:25432/dtrackierPostgres connection string of the workers (role dt_app).
DT_CHW_BATCH_SIZE5000Rows per ClickHouse insert batch in the event writers.
DT_CHW_FLUSH_MS1000ClickHouse writer flush window: a batch is inserted when it reaches DT_CHW_BATCH_SIZE or this long after its first event. WP-84: before, a partial pull was inserted at once (a quick writer inserted every few ms: hundreds of small parts, ClickHouse the largest CPU user of a 2-vCPU node); now one insert per window per table.
DT_PB_TIMEOUT_MS10000Timeout of one outgoing postback HTTP request.
DT_PB_CONCURRENCY512Concurrent outgoing postbacks.
DT_PB_MAX_INFLIGHT4096Postback messages processed concurrently per consumer (sliding window that keeps the dispatcher at ≥ 2k/s with slow endpoints without backlog growth).
DT_PB_DISABLE_AFTER20Consecutive failed deliveries after which a postback is disabled and a notification is raised.
DT_WH_DISABLE_AFTER20Consecutive failed deliveries after which a webhook is disabled.
DT_FX_SOURCEnoneExchange-rate source for the ledger worker: none or frankfurter.
DT_FX_BASEUSDBase currency requested from the FX source.
DT_LEDGER_DEFAULT_LOCK_DAYS0Tenant default for how many days approved earnings stay locked before release, unless a partner sets billing.lock_days.
DT_PAYRAILS_TICK_SECONDS15Poll period of the payout-rails executor.
DT_DUNNING_TICK_SECONDS3600Poll period of the invoice dunning scheduler.
DT_LIST_SYNC_EVERY1mPoll period of URL-backed list sync (duration).
DT_SCHEDULED_ACTIONS_EVERY15sPoll period of scheduled actions (duration).
DT_FRAUD_QUEUE_WORKERS8Conversions the fraud review queue writes at once (one conversion's events stay in order).
DT_DATA_HEALTH_HINT_EVERY1mHow often aggregates of tenant-days with a failed ClickHouse insert (reported by the writers) are compared with raw and repaired, today included.
DT_CAP_RECONCILE_HOUR3UTC hour of the nightly Redis-to-ClickHouse cap reconciliation. -1 turns it off.
DT_WEEKLY_SUMMARY_EVERY1hHow often the partner weekly summary worker checks whether last week's summaries are due (Go duration; it sends on Mondays from 06:00 UTC, once per partner and ISO week).
DT_WEEKLY_SUMMARY_ANY_DAYSet to 1 to send last week's partner summaries on any day (operations and end-to-end tests); still once per partner and week.
DT_ADV_NOTIFY_EVERY5mHow often the advertiser notification worker checks cap alerts, due and overdue invoices and inbound postback failures (Go duration).
DT_FRAUD_BATCH_HOUR3UTC hour of the nightly fraud batch.
DT_FRAUD_BATCH_ON_STARTSet to 1 to also run the fraud batch when the worker starts.
DT_FRAUD_WINDOW_DAYS7Days of history scanned by the fraud batch.
DT_LOCKER_INTERVAL60sPoll period of the data-locker exporter (duration).
DT_LOCKER_DISABLESet to 1 to disable the data-locker worker.
DT_LEADGEN_DISABLESet to 1 to disable the lead-distribution workers.
DT_LEADGEN_SYNC_INTERVAL2sPoll period of the lead outbox to ClickHouse sync.
DT_LEADGEN_RETURNS_INTERVAL1mPoll period of automatic lead return approval.
DT_CALLS_DISABLESet to 1 to disable the call-tracking worker.
DT_CALLS_INTERVALPoll period of the call conversion sync (duration).
DT_SAFE_BROWSING_KEYGoogle Safe Browsing API key used by tracking-domain health checks. Unset: the check is skipped.
DT_IDEMPOTENCY_RETENTION_HOURSHow long Idempotency-Key responses are kept and replayed (hours, default 24); older keys are purged hourly.
DT_THROTTLE_HOLD_EVERY_SHow often (seconds, default 60) held conversions of the postback throttling add-on are checked for an expired hold (auto-release or auto-scrub).
DT_THROTTLE_INGEST_URLhttp://127.0.0.1:18081Ingest origin the API and workers call to release a throttled conversion (postback throttling add-on).
DT_HOLDS_DISABLEWP-80: set to 1 to stop the conversion hold timer (held conversions stay extended until released by hand).
DT_HOLDS_EVERY10sWP-80: how often the hold timer releases due holds (Go duration).
DT_HOLDS_PLACEHOLDER_TTL24hWP-83: age after which a cancelled placeholder hold row that was never filled in (a conversion moved out of extended that no conversion rule held) is deleted; the reaper runs at most hourly (Go duration).
DT_HOLDS_INGEST_URLhttp://127.0.0.1:18081WP-80: ingest origin the hold timer calls to release a hold; falls back to DT_THROTTLE_INGEST_URL.
DT_JS_MONITOR_EVERY_S15WP-85: how often the workers read every dt.* JetStream stream: gauges dt_jetstream_stream_{bytes,max_bytes,messages,fill_ratio}, reconcile of uncapped streams, reclaim.
DT_JS_RECLAIM_AT0.8WP-85: fill ratio of a stream's cap at which the workers purge the messages every durable consumer has acknowledged (never unconsumed ones; a stream with metadata dt.reclaim=off is skipped). dt_jetstream_reclaimed_messages_total.
DT_JS_RECONCILE1WP-85: 0 leaves stream configs alone; default: a dt.* stream without a byte cap (created before WP-85 or by another service) gets its DT_JS_MAX_BYTES_* cap and discard new, never below what it holds + 10 %.

Install attribution and Apple/Google postback verification.

VariableDefaultDescription
DT_MOBILE_DISABLESet to 1 to disable the mobile workers (ClickHouse writers, install/event postbacks, uninstall and erase jobs).
DT_MOBILE_JOBS_INTERVALPoll period of the uninstall and erase jobs (duration).
DT_SKAN_PUBLIC_KEYSApple SKAdNetwork public keys as version=base64 pairs, for example 4.0=...,3.0=...,default=.... Unset entries keep Apple's published keys.
DT_SKAN_ACCEPT_UNVERIFIEDSet to 1 to accept SKAN postbacks whose signature does not verify (testing only).
DT_AAK_KEYS_URLURL from which AdAttributionKit verification keys are fetched.
DT_AAK_JWKSInline JWKS used to verify AdAttributionKit JWS postbacks instead of fetching DT_AAK_KEYS_URL.

Tracking domains and TLS

DNS verification, health checks and certificates.

VariableDefaultDescription
DT_DOMAIN_CNAME_TARGETSCNAME targets shown in the DNS wizard, as region=host pairs with an optional default=host, for example us=track-us.example.com,default=track.example.com.
DT_DNS_RESOLVERSComma list of resolvers used for DNS verification instead of the system resolver.
DT_TLS_ASK_TOKENSecret of the on-demand TLS ask endpoint GET /v1/internal/tls-ask (the TLS proxy sends it in the query). The endpoint answers yes only for a DNS-verified domain of a live workspace or a platform portal subdomain; unset = always no. gen-secrets.sh generates it.
DT_DOMAIN_TLS_PROBE_ADDRhost:port of the TLS proxy (proxy:443 in the production profile). The domain worker promotes a verified domain from ssl_pending to active once a TLS handshake there with the domain as SNI returns a certificate naming it (the proxy issues it on demand). Unset = promote only from the edge ACME cache.
DT_DOMAIN_TLS_PROBE_INSECURE1 skips the chain check of DT_DOMAIN_TLS_PROBE_ADDR (the proxy uses its internal CA: staging, the deploy drill). The certificate must still name the domain.
DT_DOMAIN_VERIFY_SECONDS30Period of the domain verification worker.
DT_DOMAIN_HEALTH_SECONDS300Period of the domain health worker (TLS expiry, latency, DNSBL).
DT_CF_ZONE_IDCloudflare zone id; with DT_CF_API_TOKEN enables the Cloudflare for SaaS custom hostname provider.
DT_CF_API_TOKENCloudflare API token for custom hostnames.

Observability and status page

Tracing, business gauges, the synthetic canary and the public status page.

VariableDefaultDescription
DT_OTEL_ENDPOINTOTLP/HTTP trace endpoint (host:port, or a full URL). Unset: tracing is off.
DT_OBS_TENANT_LIMITMaximum distinct tenant label values in metrics before the rest collapse into one bucket.
DT_OBS_DISABLESet to 1 to disable the observability worker (consumer lag and business gauges).
DT_OBS_LAG_SECONDS10Consumer-lag poll period.
DT_OBS_BIZ_SECONDS60Business gauge poll period.
DT_OBS_BIZ_WINDOW_SECONDS300Trailing window of the business gauges.
DT_OBS_OPS_SECONDS60Poll period of the caps / fraud / online-reconcile gauges (dt_cap_, dt_fraud_, dt_reconcile_*).
DT_OBS_OPS_WINDOW_SECONDS3600Trailing window for the cap-alert and fraud gauges.
DT_PPROF_ADDRWP-84: serve Go CPU / heap / allocation profiles (net/http/pprof) of edge, ingest or workers on this address. Loopback only (127.0.0.1:port, [::1]:port, localhost:port; anything else is refused with a warning). Unset: off. Used to size a node (docs/ops/SIZING.md).
DT_SYNTH_TENANTCanary tenant that owns the synthetic monitor and the incidents it opens. Unset: the monitor is off.
DT_SYNTH_LINKCanary click path on the edge, for example /c/<offer_hash>/<partner_hash>. Required for the monitor.
DT_SYNTH_HOSTHost header (the tenant tracking domain) sent with the canary click and postback.
DT_SYNTH_REGIONSEdge endpoints to probe as name=url pairs, for example eu=https://eu.edge,us=https://us.edge.
DT_SYNTH_EDGE_URLhttp://127.0.0.1:18080Edge URL used when DT_SYNTH_REGIONS is unset.
DT_SYNTH_INGEST_URLhttp://127.0.0.1:18081Ingest base URL for the canary postback.
DT_SYNTH_POSTBACK_PATH/p?click_id={click_id}&txn_id={txn_id}Canary postback path template.
DT_SYNTH_TLS_HOSTShost:port list whose certificate expiry is exported as dt_synth_cert_expiry_seconds.
DT_SYNTH_INTERVAL_SECONDS30Seconds between synthetic probes.
DT_SYNTH_INTERVALDocumented spelling of the probe interval in code comments; the variable actually read is DT_SYNTH_INTERVAL_SECONDS.
DT_DATA_HEALTH_DISABLESet to 1 to stop the data health self-audit (WP-75) in the workers.
DT_DATA_HEALTH_AUTO_REPAIR1Set to 0 to detect only: derived data (aggregates, ledger balances, raised cap counters) is then not repaired automatically.
DT_DATA_HEALTH_EVERY_MINUTES60Period of the data health pass over today and yesterday.
DT_DATA_HEALTH_DAILY_HOUR2UTC hour (1-23) from which the daily data health pass over the last 35 days runs.
DT_TC_BACKFILL_DISABLESet to 1 to stop the tenant-currency backfill (WP-77) in the workers: conversions stored before per-conversion tenant-currency amounts keep being converted per report row.
DT_TC_BACKFILL_EVERY_MINUTES60Pause between tenant-currency backfill passes (each finishes interrupted batches first, then the oldest months).
DT_TC_BACKFILL_BATCHES12Tenant x month batches per tenant-currency backfill pass (0 = all).
DT_SYNTH_FAIL_AFTER2Consecutive probe failures before an incident opens.
DT_STATUS_PAGE_URLOrigin that serves /status-public; used in subscriber and incident emails. Defaults to DT_PORTAL_BASE_URL.
DT_STATUS_DISABLESet to 1 to disable the status-notify worker.

NATS JetStream streams

Not environment variables: stream names that appear in code and logs.

VariableDefaultDescription
DT_CLICKSJetStream stream for dt.click.> created by the edge.
DT_INGEST_RECOVERYIngest recovery queue (WP-57d): postbacks deferred and conversion-state writes queued while Redis is unavailable, replayed when it answers again (dt.ingest.recovery..{deferred,repair}).
DT_CONVERSIONSIngest-owned stream for conversion events when the workers stream does not already cover them.
DT_POSTBACK_INIngest-owned stream for dt.postback_in.logged.v1.
DT_EVENTSCatch-all stream the workers create for subject families no other stream covers. The main workers stream is called DT.
DT_MOBILEJetStream stream for dt.mobile.>.
DT_MIGRATIONJetStream stream for dt.migration.> (forwarding of conversions to a legacy platform).

Developer and test tooling

Used by scripts, Makefile targets and Go tests only; never set in production.

VariableDefaultDescription
DT_E2E_PORTPlaywright: port of a worktree portal build to test (also DT_PORTAL_PORT); the shared dev portals own 3100-3102.
DT_E2E_PG_DBPlaywright: Postgres database the e2e helpers and teardown use (your wp-db.sh database), never the shared one.
NEXT_PUBLIC_DT_SIGNUP_APIBuild-time: API base the marketing site's /signup form posts to (default https://app.metrixed.com/v1).
NEXT_PUBLIC_DT_SIGNUP_URLBuild-time: link to the marketing site's signup page from the admin /verify-workspace error state.
DT_OPERATOR_PASSWORDPassword read by services/api/cmd/operator create instead of prompting (16+ characters).
DT_OPERATOR_DATABASE_URLDSN for services/api/cmd/operator (defaults to DT_API_DATABASE_URL).
DT_MCP_URLdtrackier-mcp (stdio bridge): the MCP endpoint to forward to, e.g. https://app.example.com/v1/mcp.
DT_MCP_TOKENdtrackier-mcp (stdio bridge): the MCP token (dtm_…) sent as the bearer token.
DT_LOAD_TOOLmake load: which load generator to use (k6 via pinned docker image, or the built-in Go generator when k6 is unavailable).
DT_CH_DBdtrackierClickHouse database targeted by scripts/ch-apply.sh, scripts/ch-backfill-daily.sh and the perf/chaos fixtures (e.g. dtrackier_perf, dtrackier_chaos).
DT_INGEST_MSGID_SALTDev/replay only: prefix added to JetStream message ids so fixtures with deterministic ids (e.g. SKAN transaction ids) can be replayed inside the 2-minute dedupe window. Never set in production: it disables idempotent publishing across restarts.
DT_INGEST_CLICKCACHE_DELAY_MSTest knob: the click-cache consumer stores each click this long after its timestamp, to reproduce a click stream that lags behind postbacks (WP-57c). Never in production.
DT_WORKERS_ONLYpostbacks: run only a side-effect-free partner-postback probe of this build next to the real workers (make reconcile phase postback; docs/17-qa/RECONCILIATION.md). Never in production.
DT_PB_PROBE_URL_PREFIXPostback probe: only postbacks whose rendered URL starts with this fire (the reconcile catcher). Required with DT_WORKERS_ONLY=postbacks.
DT_PB_PROBE_SUFFIX-probePostback probe: suffix of its own JetStream consumer names and once-guard namespace.
DT_RECON_REDIS_PAUSE_CMDmake reconcile phase chaos (opt-in): shell command that stalls the private stack's OWN Redis (e.g. docker pause ). Never the shared Redis.
DT_RECON_REDIS_RESUME_CMDmake reconcile phase chaos: shell command that releases that Redis again.
DT_RECON_PB_CATCHER127.0.0.1:28195make reconcile: listen address of the partner-postback catcher (phase postback).
DT_PG_DBDatabase name exported by scripts/wp-db.sh for a work-package database; Go tests default to dtrackier_test (scripts/test-db.sh), never the shared dtrackier.
DT_TEST_CH_DBdtrackier_testClickHouse database Go tests write throwaway rows to (scripts/test-db.sh).
DT_PURGE_PG_DSNpostgres://dt:dt@127.0.0.1:25432/dtrackiermake dev-purge-test-tenants: Postgres DSN (compose superuser) of the shared dev DB.
DT_PURGE_CH_URLhttp://127.0.0.1:28123make dev-purge-test-tenants: ClickHouse HTTP URL.
DT_PURGE_CH_DBdtrackiermake dev-purge-test-tenants: ClickHouse database.
DT_PURGE_REDIS_ADDR127.0.0.1:26379make dev-purge-test-tenants: Redis address (empty skips Redis).
DT_TEST_GUARDfailmake test fails when tenants appear in the shared dtrackier DB while it runs; warn only logs them.
DT_PG_PORT25432Postgres host port used by scripts.
DT_REDIS_PORT26379Redis host port used by dev-services.sh.
DT_MINIO_PORT29002MinIO host port used by make up health checks.
DT_TEST_PG_DSNPostgres DSN used by Go tests that need a database; tests skip when unset.
DT_TEST_REDIS_ADDRRedis address used by Go tests that need Redis.
DT_LOCKER_TEST_S3127.0.0.1:29002MinIO endpoint used by the data-locker integration test.
DT_LEDGER_ORACLESet to 1 by make ledger-oracle to run the ledger oracle test.
DT_LEDGER_BENCHNumber of events for the WP-76 ledger posting benchmark (services/workers/internal/ledger); unset skips it. Prints posted events/s.
DT_LEDGER_BENCH_SERIALSet to 1 to run the ledger benchmark with one transaction per event, for a before/after comparison.
DT_REPORTING_ORACLESet to 1 by make reporting-oracle to run the reporting oracle tests.
DT_UPDATE_FIXTURESet to 1 to rewrite expected fixture outputs in the SKAN fixture tests.
DT_UPDATE_REPORTMETASet to 1 to regenerate the report catalogue in go-shared/reportmeta from the engine registry.
DT_UPDATE_VECTORSSet to 1 to regenerate sdks/test-vectors/signing.json.
DT_AUDIT_501Set to 1 to print the list of API operations that still answer 501 (the enforced gate is TestNoUnimplementedOperations).
DT_STAGEOnly appears as a Snowflake stage name (DT_STAGE) in data-connector tests; not a setting.
DT_URL_EDGE_REDIRECThttp://127.0.0.1:18080Base URL of a running edge for tools/replay (pattern DT_URL_, with the service name upper-cased and dashes as underscores).
DT_URL_INGESThttp://127.0.0.1:18081Base URL of a running ingest for tools/replay and the replay Make targets.
DT_REPLAY_NATS_URLNATS server tools/replay uses to count published events when verifying a fixture.
DT_REPLAY_NATS_SUBJECTSubject whose message count tools/replay checks after a fixture run.
DT_REPLAY_NATS_STREAMStream tools/replay reads the event count from.
DT_API_KEYConvention used in this documentation and by the admin API explorer's Copy as cURL for an API key in your shell. Not read by any service.
DT_API_URLBase URL of the API for the portals (baked at build time in the admin app, default http://127.0.0.1:18082; read at runtime by the partner app).
DT_RECON_PHASESstream,concurrency,matrixWhich phases make reconcile runs: stream (labelled clicks/postbacks reconciled at every layer), concurrency, matrix (redirect decisions, smartlink weights).
DT_RECON_N240Number of labelled clicks in the make reconcile stream (the smartlink sample is 3x this).
DT_RECON_SEEDtime-basedRandom seed of the make reconcile click mix; printed by every run so a failure can be replayed.
DT_ORACLE_PROFILEsmallmake oracle (WP-73 differential oracle): scenario size, small (~5k events, CI) or large (≥300k events, nightly).
DT_ORACLE_RATE90 small / 900 largemake oracle: requests per second the scenario is sent at through the edge and ingest (all tenants together).
DT_ORACLE_QUIESCE_MAX30mmake oracle / make reconcile (WP-78): longest wait for a quiet pipeline (spool empty, every consumer caught up, ledger stable) before comparing.
DT_ORACLE_STALL_MAX5mmake oracle (WP-79): a spool or JetStream consumer whose backlog has not moved for this long fails O-quiesce naming it, and the comparisons run; a durable with a backlog and no active consumer is reported (O-consumers) and not awaited.
DEMO_CH_ATTEMPTS6make demo-data (WP-82): attempts per ClickHouse step on transient errors (closed connection, timeout, memory limit, too many parts); a retried insert adds only the rows ClickHouse does not hold yet.
DT_W74_DOCKER_ARGSscripts/wp74-chaos.sh (WP-82): extra docker run arguments for the stack's Redis / NATS / ClickHouse containers, e.g. --cpuset-cpus=2,3; with the script under taskset -c 2,3 the whole stack shares two CPUs like the GitHub runner.
DT_ORACLE_OUT.run/oracle.jsonmake oracle: path of the run summary (seed, events per tenant, runtime).
DT_W73_NAMEwp73scripts/wp73-stack.sh: name of the private oracle stack (containers dt--nats/redis); DT_W73_EDGE_PORT, DT_W73_INGEST_PORT, DT_W73_API_PORT, DT_W73_REPORTING_PORT, DT_W73_WORKERS_PORT, DT_W73_NATS_PORT, DT_W73_REDIS_PORT and DT_W73_CH_DB move it.
DT_DEV_CLOCK_SKEW_MSChaos-accuracy knob (WP-74): shifts the clock the edge stamps clicks with and ingest stamps conversions with by this many milliseconds (signed). Ignored when DT_ENV=production.
DT_W74_NAMEwp74scripts/wp74-chaos.sh: name of the private chaos-accuracy stack (containers dt--redis / dt--nats, scopes dt--); DT_W74_CH_DB, DT_W74_PORT_BASE (services, 28740), DT_W74_INFRA_BASE (Redis/NATS/fault proxy, 23840), DT_W74_PG_POOL (6) and DT_W74_SKIP_BUILD move it.
DT_W74_CH_DBdtrackier_wp74scripts/wp74-chaos.sh: ClickHouse database of the chaos-accuracy stack.
DT_W74_PORT_BASE28740scripts/wp74-chaos.sh: first service port (edge; ingest, api, reporting, workers, workers2 follow).
DT_W74_INFRA_BASE23840scripts/wp74-chaos.sh: base port of the private Redis (+1), NATS (+2, monitor +6) and fault proxy listeners (+11..+15, API +19).
DT_W74_PG_POOL6scripts/wp74-chaos.sh: pool_max_conns of every private service (Postgres allows 400 connections box-wide).
DT_W74_SKIP_BUILDscripts/wp74-chaos.sh up: 1 = reuse the binaries in .run/wp74.
DT_ACC_PROFILEquickmake chaos-accuracy: quick (one scenario per fault family) or full (all faults, combinations, sustained load). DT_ACC_SCENARIOS picks scenarios by name; DT_ACC_CLICK_RPS / DT_ACC_PB_RPS / DT_ACC_SECONDS size each scenario, DT_ACC_HOT_* the kill scenarios, DT_ACC_LOAD_* the sustained leg; DT_ACC_SETTLE_SECONDS the convergence deadline; DT_ACC_CAP_K the conversion cap; DT_ACC_KEEP=1 keeps the stack up; DT_ACC_TIMEOUT bounds the run.
DT_ACC_SCENARIOSmake chaos-accuracy: comma-separated scenario names (tools/replay/reconcile/wp74_scenarios.go).
DT_ACC_CTLAccuracy phase: the stack control script (scripts/wp74-chaos.sh); the phase refuses to run without it (it injects faults).
DT_ACC_CATCHER127.0.0.1:28749Accuracy phase: address of the partner-postback / webhook receiver.
DT_ACC_NATS_MONAccuracy phase: NATS monitoring URL of the private stack.
DT_RECON_SPREAD40sWindow over which make reconcile paces its labelled clicks (bursts look like click injection to the fraud rules).
DT_RECON_DEADLINE60sConvergence deadline per make reconcile check (docs/11: report visibility under 60 s).
DT_RECON_EDGEhttp://127.0.0.1:18080Edge base URL make reconcile clicks through; DT_RECON_INGEST, DT_RECON_API, DT_RECON_NATS and DT_RECON_CH point the other layers at private builds.
DT_LIVE_PHASEScore,lifecycleWhich phases scripts/live-e2e.py (make live-e2e) runs: core (link, click, postback, report, ledger, partner leg) and lifecycle (offer approval, caps, fraud, automation, email, statement, payout, dispute, invoice).
DT_W64_NAMEwp64scripts/wp64-stack.sh: name of the private stack (its Redis and NATS containers are dt--redis / dt--nats), so several worktrees can each run their own.
DT_RECON_REDISmake reconcile: host:port of the stack's own Redis; the caps phase then also checks that the built-in cap counter sits in the local-day bucket (skipped when unset).
DT_RECON_PG_DSNpostgres://dt:dt@127.0.0.1:25432/dtrackiermake reconcile: Postgres DSN (superuser) the throttle phase uses to switch the WP-64 add-on on for tenant-basic the way an operator does (defaults to the local dev database).
DT_E2E_MAILPIThttp://127.0.0.1:28025Mailpit web/API base URL read by the e2e specs, scripts/live-e2e.py and make email-audit to fetch delivered emails.
DT_E2E_APIhttp://127.0.0.1:18082/v1API base URL for scripts/live-e2e.py, live-api-audit.py and make anomaly-check (the anomaly check builds its own API on :19682 when unset).
DT_E2E_EDGEhttp://127.0.0.1:18080Edge (click redirect) base URL for scripts/live-e2e.py; point it at a private stack.
DT_E2E_INGESThttp://127.0.0.1:18081Ingest (postback) base URL for scripts/live-e2e.py; point it at a private stack.
DT_LIVE_PARTNER_IDscripts/live-e2e.py: reuse this seeded partner instead of creating one (CI sets Partner 01 so the portal e2e reports have rows).
DT_EDGE_URLhttp://127.0.0.1:18080Edge base URL make docs-quickstart clicks through (with DT_INGEST_URL for the postback and DT_API_URL for the API calls).
DT_INGEST_URLhttp://127.0.0.1:18081Ingest base URL make docs-quickstart sends the documented postback to.
DT_DOCS_E2E_PORT3106Port make docs-e2e serves the docs static export on for the Playwright crawl (never reuses a running server).
DT_WWW_E2E_PORT3181Port make www-e2e / www-a11y / www-demo-e2e serve the marketing-site export on.
DT_WWW_LEAD_FORM_URLBuild-time endpoint of the marketing site demo form: the public lead intake submit URL (POST /v1/public/lead-forms/{slug}/submit). Empty keeps the mailto fallback; apps/www/scripts/demo-intake.mjs provisions the form and prints it.
DT_WWW_INTAKE_TENANTtenant-basicWorkspace apps/www/scripts/demo-intake.mjs provisions the demo lead form in (with DT_WWW_INTAKE_EMAIL / DT_WWW_INTAKE_PASSWORD, its admin login, and DT_WWW_INTAKE_SLUG).
DT_WWW_ORIGINShttps://metrixed.com + local e2e portsComma-separated origins allowed to post to the demo lead form (CORS allow-list set by demo-intake.mjs).
DT_FA_PRIVATE_INGESTmake fraud-accuracy: 1 builds ingest from this checkout and sends conversions to a private instance instead of the shared one on :18081.
DT_PW_CHANNELBrowser channel for the portal and web-sdk Playwright suites; CI sets chrome to use the Google Chrome preinstalled on the runner instead of installing Playwright chromium (WP-69). Empty uses the bundled chromium.
DT_SERVICES_PREBUILTscripts/dev-services.sh: 1 starts the binaries already in bin/ instead of rebuilding them (CI restores them from a cache keyed by the Go sources, WP-69).
DT_MIGRATEgo run …/migrate@v4.18.1Migrate command used by make ci-stack, scripts/wp-db.sh and scripts/migrate-roundtrip.sh; CI passes a prebuilt bin/migrate (WP-69).
DT_SEED_BINgo run ./services/api/cmd/seedSeed command scripts/e2e-bootstrap.sh runs; CI passes a prebuilt bin/seed (WP-69).
DT_CI_DATACI only: absolute directory the Postgres and ClickHouse data dirs are bind-mounted from (infra/docker-compose.ci.yml), saved and restored as the seeded-stack snapshot (scripts/ci/stack.sh, WP-69).
DT_ACC_LOAD_MAX_QUEUED_PCTmake chaos-accuracy (WP-84): gate on the sustained-load leg: at most this percentage of its postbacks answered queued (check A-load-queued). The 2-vCPU nightly sets 5 with the leg sized to the runner; unset (the dev box full profile at 1 500 clicks/s) the share is reported only.
DT_ACC_PIDDIRmake chaos-accuracy (WP-84): directory with the stack services' .pid files; the harness reads their CPU time to print the capacity line (CPU per service, events/s per vCPU) of every scenario. scripts/wp74-chaos.sh sets it.
DT_ACC_PG_DBmake chaos-accuracy (WP-84): the stack's Postgres database; the capacity line adds the CPU of the Postgres backends serving it (a shared Postgres: other databases are not counted). scripts/wp74-chaos.sh sets it.
DT_ACC_CONTAINERSmake chaos-accuracy (WP-84): comma-separated containers (Redis, NATS, ClickHouse) whose cgroup CPU the capacity line includes. scripts/wp74-chaos.sh sets it.
DT_W74_PPROFscripts/wp74-chaos.sh (WP-84): 1 serves each service's pprof on 127.0.0.1:<its port + 1000> (DT_PPROF_ADDR).
DT_W84_CPUS4,10scripts/wp84-chaos.sh: CPUs the WP-84 2-vCPU chaos / capacity stack (services, Redis, NATS, ClickHouse, harness) is pinned to; "" = unpinned. DT_W84_NAME (wp84c), DT_W84_PORT_BASE (28960), DT_W84_INFRA_BASE (23960) and DT_W84_CH_DB (dtrackier_wp84c) move it.
DT_W84_NAMEwp84cscripts/wp84-chaos.sh: stack name (DT_W74_NAME).
DT_W84_PORT_BASE28960scripts/wp84-chaos.sh: first service port (DT_W74_PORT_BASE).
DT_W84_INFRA_BASE23960scripts/wp84-chaos.sh: infrastructure port base (DT_W74_INFRA_BASE).
DT_W84_CH_DBdtrackier_wp84cscripts/wp84-chaos.sh: ClickHouse database (DT_W74_CH_DB).
DT_W84S_CPUS2,8scripts/wp84-stack.sh: CPUs of the WP-84 small-node stack (oracle, reconcile, portals; services, Redis, NATS, ClickHouse and the load generator); "" = unpinned. DT_W84S_NAME (wp84s), DT_W84S_CH_MEM (4g) and DT_W84S_CH_SERVER_MEM move it.
DT_W84S_NAMEwp84sscripts/wp84-stack.sh: stack name (DT_W80_NAME).
DT_W84S_CH_MEM4gscripts/wp84-stack.sh: memory limit of the stack's ClickHouse container.
DT_W84S_CH_SERVER_MEM3200000000scripts/wp84-stack.sh: ClickHouse max_server_memory_usage (bytes, below the container limit).
DT_TEST_NATS_URLnats://127.0.0.1:24222Go tests that need JetStream (chwriter flush window, WP-84): the NATS to create their throwaway stream on.
DT_ACC_PB_DELAY_MS1500make chaos-accuracy (WP-85): delay before a click's postback (plus up to DT_ACC_PB_JITTER_MS). Raised above a short DT_CLICK_CACHE_TTL it sends every postback to the ClickHouse fallback (the cold-path capacity leg, docs/ops/SIZING.md).
DT_ACC_PB_JITTER_MS1500make chaos-accuracy (WP-85): random extra postback delay on top of DT_ACC_PB_DELAY_MS.
DT_ACC_RESEND_MIN_MS500make chaos-accuracy (WP-86): earliest resend of a resent postback after its original, for scenarios without their own resend window (a leg whose resends arrive after a short DT_CONV_HOT_TTL).
DT_ACC_RESEND_MAX_MS4000make chaos-accuracy (WP-86): latest resend of a resent postback after its original (see DT_ACC_RESEND_MIN_MS).
DT_ORACLE_COLDmake oracle (WP-85): 1 adds check O-cold — postbacks on clicks older than the click-cache hot window were resolved from ClickHouse. Set with an ingest on a short window (DT_CLICK_CACHE_LATE_TTL=0 and DT_CLICK_CACHE_TTL=30d in the nightly oracle job, 10d in scripts/wp85-stack.sh).
DT_ACC_MEM_SAMPLE4000make chaos-accuracy (WP-85): keys per Redis key family whose MEMORY USAGE the sustained leg samples for its "redis memory:" line (bytes per click by family).
DT_WP85_MEM_REDISWP-85 test TestWP85RedisMemoryLayouts: a throwaway Redis (it is flushed) on which the click-cache layouts are measured in bytes per click.
DT_W85_CPUS5,11scripts/wp85-chaos.sh: CPUs the WP-85 2-vCPU chaos / capacity stack is pinned to; "" = unpinned. DT_W85_NAME (wp85c), DT_W85_PORT_BASE (28980), DT_W85_INFRA_BASE (23980) and DT_W85_CH_DB (dtrackier_wp85c) move it.
DT_W85_NAMEwp85cscripts/wp85-chaos.sh: stack name (DT_W74_NAME).
DT_W85_PORT_BASE28980scripts/wp85-chaos.sh: first service port (DT_W74_PORT_BASE).
DT_W85_INFRA_BASE23980scripts/wp85-chaos.sh: infrastructure port base (DT_W74_INFRA_BASE).
DT_W85_CH_DBdtrackier_wp85cscripts/wp85-chaos.sh: ClickHouse database (DT_W74_CH_DB).
DT_W85S_CPUS3,9scripts/wp85-stack.sh: CPUs of the WP-85 small-node stack (oracle, reconcile); "" = unpinned. DT_W85S_NAME (wp85s), DT_W85S_CH_MEM (4g) and DT_W85S_CH_SERVER_MEM move it.
DT_W85S_NAMEwp85sscripts/wp85-stack.sh: stack name (DT_W80_NAME).
DT_W85S_CH_MEM4gscripts/wp85-stack.sh: memory limit of the stack's ClickHouse container.
DT_W85S_CH_SERVER_MEM3200000000scripts/wp85-stack.sh: ClickHouse max_server_memory_usage (bytes).
DT_W86_CPUS5,11scripts/wp86-chaos.sh: CPUs the WP-86 chaos / capacity stack is pinned to ("5,11" = 2 vCPU, "4,10,5,11" = 4, "2,8,3,9,4,10,5,11" = 8; "" = unpinned). DT_W86_NAME (wp86c), DT_W86_PORT_BASE (29180), DT_W86_INFRA_BASE (24180) and DT_W86_CH_DB (dtrackier_wp86c) move it.
DT_W86_NAMEwp86cscripts/wp86-chaos.sh: stack name.
DT_W86_PORT_BASE29180scripts/wp86-chaos.sh: first service port.
DT_W86_INFRA_BASE24180scripts/wp86-chaos.sh: infrastructure port base.
DT_W86_CH_DBdtrackier_wp86cscripts/wp86-chaos.sh: ClickHouse database.
DT_W86S_CPUS3,9scripts/wp86-stack.sh: CPUs of the WP-86 small-node stack (oracle, reconcile); "" = unpinned. DT_W86S_NAME (wp86s), DT_W86S_CH_MEM (4g) and DT_W86S_CH_SERVER_MEM move it.
DT_W86S_NAMEwp86sscripts/wp86-stack.sh: stack name (DT_W80_NAME).
DT_W86S_CH_MEM4gscripts/wp86-stack.sh: memory limit of the stack's ClickHouse container.
DT_W86S_CH_SERVER_MEM3200000000scripts/wp86-stack.sh: ClickHouse max_server_memory_usage (bytes).
DT_W87_CPUS5,11scripts/wp87-chaos.sh: CPUs the WP-87 chaos / capacity stack is pinned to ("" = unpinned); its pulsed verb injects Redis latency pulses into the capacity leg. DT_W87_NAME (wp87c), DT_W87_PORT_BASE (29380), DT_W87_INFRA_BASE (24380) and DT_W87_CH_DB (dtrackier_wp87c) move it.
DT_W87_NAMEwp87cscripts/wp87-chaos.sh: stack name.
DT_W87_PORT_BASE29380scripts/wp87-chaos.sh: first service port.
DT_W87_INFRA_BASE24380scripts/wp87-chaos.sh: infrastructure port base.
DT_W87_CH_DBdtrackier_wp87cscripts/wp87-chaos.sh: ClickHouse database.
DT_W87S_CPUS3,9scripts/wp87-stack.sh: CPUs of the WP-87 small-node stack (oracle, reconcile; oracle-pulsed stalls its Redis periodically); "" = unpinned. DT_W87S_NAME (wp87s), DT_W87S_CH_MEM (4g) and DT_W87S_CH_SERVER_MEM move it.
DT_W87S_NAMEwp87sscripts/wp87-stack.sh: stack name (DT_W80_NAME).
DT_W89_CPUS4,10scripts/wp89-stack.sh: CPUs of the WP-89 small-node stack (reconcile, oracle, burst: identical parallel postbacks under CPU contention); "" = unpinned. DT_W89_NAME (wp89), DT_W89_CH_MEM (4g) and DT_W89_CH_SERVER_MEM move it.
DT_BURST_ROUNDS200reconcile phase burst (WP-89, scripts/wp89-stack.sh burst): rounds of identical parallel postbacks; each round's answers and stored conversions (ClickHouse raw events + current, ledger) are checked.
DT_BURST_PAR20reconcile phase burst (WP-89): identical postbacks fired at once per round.
DT_BURST_BATCH100reconcile phase burst (WP-89): rounds whose clicks are created before they are fired.
DT_BURST_CONCURRENT1reconcile phase burst (WP-89): rounds fired at the same time (each on its own click).
DT_BURST_OUTreconcile phase burst (WP-89): file for one line per round (answers, created count, distinct created ids, queued / resolved, stored counts).
DT_W87S_CH_MEM4gscripts/wp87-stack.sh: memory limit of the stack's ClickHouse container.
DT_W87S_CH_SERVER_MEM3200000000scripts/wp87-stack.sh: ClickHouse max_server_memory_usage (bytes).
DT_WP86_LATWP-86 test TestWP86ClaimOfRecordLatency: 1 measures per-postback latency with and without the synchronous claim of record (DT_TEST_PG_DSN, a test Redis database).