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.
| Variable | Default | Description |
|---|---|---|
DT_ADDR | 127.0.0.1:18080 (edge), :18081 (ingest), :18082 (api), :18083 (reporting); workers listen on 18084 | Listen address of the service. Each binary has its own default. |
DT_NATS_URL | nats://127.0.0.1:24222 | NATS server used by workers (and shared helpers). Creates or binds the JetStream streams on start. |
DT_REDIS_ADDR | 127.0.0.1:26379 | Redis address used by workers (dedupe tokens, counters, rate limits). |
DT_REDIS_URL | Redis 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_ADDR | 127.0.0.1:29000 | ClickHouse native address used by workers. |
DT_CH_USER | dt | ClickHouse 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_PASSWORD | dt | ClickHouse password used by workers. |
DT_CH_DATABASE | dtrackier | ClickHouse database used by workers. |
DT_INTERNAL_TOKEN | Shared 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_PREVIOUS | Retired 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_PASSWORD | Password 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_USERNAME | Redis ACL user name used with DT_REDIS_PASSWORD. Unset: the default user. | |
DT_REDIS_TLS | 0 | 1 enables TLS for Redis addresses given as host:port (a rediss:// URL enables it on its own). |
DT_REDIS_TLS_CA | PEM file of the CA that signed the Redis server certificate (enables TLS). | |
DT_REDIS_TLS_SERVER_NAME | Server name to verify in the Redis TLS certificate when it differs from the address host. | |
DT_NATS_USER | NATS 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_PASSWORD | Password of DT_NATS_USER. In the production compose profile it is also the server password embedded in every DT_*NATS_URL. | |
DT_NATS_TOKEN | NATS token authentication for every NATS client (instead of user/password). | |
DT_NATS_TLS | 0 | 1 requires TLS to NATS (a tls:// URL does the same). |
DT_NATS_TLS_CA | PEM file of the CA that signed the NATS server certificate (enables TLS). | |
DT_SECRETS_KEY | Key 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_DESTINATIONS | Set 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_ADDR | 127.0.0.1:21025 | SMTP relay for system email when a tenant has no email provider of its own. |
DT_SMTP_FROM | noreply@dtrackier.local | Envelope and From address of system email. |
DT_PLATFORM_LOGO_URL | Public 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_URL | http://127.0.0.1:18080 | Public base URL of the tracking host; used to build signed click and unsubscribe links in campaigns and communications. |
DT_PORTAL_URL | Base URL of the portals, prefixed to relative links in notification emails. | |
DT_PORTAL_BASE_URL | http://localhost:3100 | Base URL of the admin portal; default origin for the public status page and invitation links. |
DT_PORTAL_PARTNER_URL | Origin 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_URL | Origin 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_URL | http://127.0.0.1:18083 | Reporting service URL used by workers (automation rules read metrics through it). |
DT_NOTIFY_FALLBACK_TO | Comma list of mailboxes that receive notification email when a tenant has no recipient employees. | |
DT_ENV | development | Deployment 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_CLICKS | 768MiB | WP-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_DEFAULT | 64MiB | WP-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.
| Variable | Default | Description |
|---|---|---|
DT_API_DATABASE_URL | postgres://dt_app:dt_app@127.0.0.1:25432/dtrackier | Postgres 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_TENANT | Tenant 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_SIGNUP | Self-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_DOMAIN | localhost (dev) | Base domain for new workspaces' portal hosts: .partner. and .advertiser.. Required in production for signup. |
DT_SAAS_ADMIN_URL | DT_PORTAL_BASE_URL | Admin portal origin used in workspace verification links (/verify-workspace). |
DT_SAAS_SIGNUP_ORIGINS | Comma-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_HOUR | 10 | Workspace signups accepted per client IP per hour (counted from stored signups). |
DT_SAAS_SIGNUP_PER_IP_MIN | 5 | Workspace signup token bucket per client IP per minute. |
DT_SAAS_TRIAL_DAYS | 14 | Trial length recorded on new workspaces (informational; trial expiry is not enforced automatically). |
DT_ORG_SELF_SIGNUP | 0 | WP-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_LIMITS | JSON limits for new workspaces, e.g. {"seats":3,"domains":1,"clicks_per_month":100000}. Unset: unlimited until an operator sets limits. | |
DT_API_TRUSTED_PROXIES | Comma-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_URL | Public URL of the API, used for SSO redirect and callback URLs (SAML/OIDC). | |
DT_ANTHROPIC_API_KEY | Anthropic 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_URL | Public 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_MINUTE | 120 | Requests per minute one MCP token may send to /v1/mcp (429 beyond). |
DT_AI_MODEL | claude-opus-5-5 | Claude model used by the AI assistant. |
DT_AI_PROVIDER | Set to fake to run the assistant with a deterministic offline provider (demos and portal e2e tests) instead of the Anthropic API. | |
DT_ANTHROPIC_BASE_URL | Override the Anthropic API base URL for the assistant (an egress proxy or gateway). Unset: the public Anthropic API. | |
DT_API_REPORTING_URL | Reporting service the API proxies /reports, /logs, /dashboard and conversion search to. | |
DT_API_INGEST_URL | http://127.0.0.1:18081 | Ingest base URL used by the link/postback testers and for fraud restatements. |
DT_API_INGEST_PUBLIC_URL | Public ingest origin shown in app S2S/SDK setup instructions. | |
DT_API_EDGE_URL | http://127.0.0.1:18080 | Edge base URL used by the link tester. Testers refuse any other host. |
DT_API_WORKERS_URL | Workers base URL for webhook replay and the firehose SSE proxy. | |
DT_API_REDIS_ADDR | Redis used by call tracking for number-pool publishing and lease lookup. | |
DT_API_S3_ENDPOINT | 127.0.0.1:29002 | S3-compatible endpoint for creative uploads (bucket dt-creatives). |
DT_API_S3_KEY | dtminio | Access key for creative storage. |
DT_API_S3_SECRET | dtminio-secret | Secret key for creative storage. |
DT_API_S3_SSL | Set to 1 to use TLS towards the creative storage endpoint. | |
DT_API_S3_PUBLIC_ENDPOINT | Endpoint used in links handed to browsers (anonymous pub/ objects and presigned priv/ URLs) when it differs from the internal endpoint. | |
DT_API_S3_PUBLIC_SSL | Set to 1 when the public creative endpoint uses TLS. | |
DT_CALLS_INGEST_URL | http://localhost:18081 | Ingest origin embedded in the dynamic number insertion snippet and used to create call conversions (also read by the calls worker). |
DT_TWILIO_BASE_URL | Overrides the Twilio REST base URL (tests and proxies). | |
DT_TAX_ONLINE | Set to 1 to run live VIES and HMRC VAT checks. Without it the tax checker is offline. | |
DT_HEALTH_TARGETS | edge, ingest, api, reporting, workers on 127.0.0.1:18080-18084 | Comma list name=url of the services probed by Settings, System health (their /readyz). |
DT_STATUS_TENANT | Tenant 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.
| Variable | Default | Description |
|---|---|---|
DT_EDGE_PLAN_REFRESH | 10s | How often the edge reloads the set of workspaces whose traffic is blocked (Redis dt:saas:blocked_tenants). Go duration, min 1s. |
DT_EDGE_PG_DSN | Postgres 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_ADDR | Redis for the snapshot cache, caps, SmartLink round-robin counters and pending deep-link payloads. | |
DT_EDGE_REDIS_TIMEOUT_MS | 50 | Per-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_AFTER | 32 | Consecutive 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_URL | nats://127.0.0.1:24222 | NATS server that receives click events (stream DT_CLICKS). |
DT_EDGE_SNAPSHOT_FILE | config_snapshot JSON loaded at boot (development and replay). | |
DT_EDGE_REGION | Region tag written to every click event as edge_region. | |
DT_EDGE_TRUST_PROXY | Set to 1 to honour X-Forwarded-For for the visitor IP. Only behind a trusted proxy. | |
DT_EDGE_GOGC | 400 | Go 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_UPSTREAMS | Portal 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_ADDR | HTTPS 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_DIRECTORY | staging | ACME 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_EMAIL | Contact 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.
| Variable | Default | Description |
|---|---|---|
DT_CAPS_EPOCH | 1 | Set 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_DSN | Postgres DSN for conversion keys, API keys, mobile apps/devices and the config snapshot. | |
DT_INGEST_REDIS_ADDR | Redis for click cache, conversion dedupe, caps and mobile state. | |
DT_INGEST_NATS_URL | nats://127.0.0.1:24222 | NATS server that receives conversion and mobile events. |
DT_INGEST_CH_URL | ClickHouse endpoint used as the fallback for click lookups when Redis misses. | |
DT_INGEST_CONVERSION_RULES | 1 | WP-80: set to 0 to turn off conversion rules (reject / hold / mark at conversion time, rule sets with event_scope conversion). |
DT_INGEST_SKU_PAYOUTS | 1 | WP-80: set to 0 to turn off per-SKU pricing of basket lines (offers.sku_payouts). |
DT_INGEST_CH_USER | dt | ClickHouse user for ingest. |
DT_INGEST_CH_PASSWORD | dt | ClickHouse password for ingest. |
DT_INGEST_CH_DB | dtrackier | ClickHouse database for ingest. |
DT_INGEST_SNAPSHOT_FILE | config_snapshot JSON loaded at boot (development and replay). | |
DT_INGEST_API_KEYS | Static ingest API keys as rawkey=tenant_id pairs separated by commas (development and replay). Normally keys come from Postgres. | |
DT_INGEST_TRUST_PROXY | Set to 1 to honour X-Forwarded-For. | |
DT_INGEST_CLICK_WAIT_MS | 2000 | Upper 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_CLICKS | 50000 | WP-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_TTL | WP-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_MAX | 45d | WP-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_MIN | 1d | WP-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_TTL | 1h | WP-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_FORMAT | compact | WP-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_CLAIMS | 1 | WP-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_COMMIT | off | WP-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_MS | 1000 | WP-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_POOL | 16 | WP-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_TTL | 45d | WP-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_FORMAT | compact | WP-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_EPOCH | 1 | WP-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_SETTLE | 10m | WP-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_MS | 60 | WP-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_SUFFIX | Private 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_FILE | packages/web-sdk/dist/dtk.js | File served as /sdk.js (the web SDK build). |
DT_INGEST_SDK_ORIGINS | Comma list of origins allowed by CORS on /p and /sdk/v1/cookie (web SDK). | |
DT_INGEST_PAYOUT_HOOK | Set to 0 to switch off the payout-engine hook (overrides, tiers, FX locking) in ingest. | |
DT_INGEST_SHOPIFY_SECRET | HMAC secret that verifies Shopify webhooks on /integrations/shopify/webhook. | |
DT_INGEST_SHOPIFY_TENANT | Tenant that receives Shopify conversions. | |
DT_INGEST_BIGCOMMERCE_SECRET | Shared secret expected in X-DTK-Webhook-Secret on the BigCommerce webhook. | |
DT_INGEST_BIGCOMMERCE_TENANT | Tenant that receives BigCommerce conversions. | |
DT_INGEST_BIGCOMMERCE_API_URL | BigCommerce store API base URL used to read the cart metafield that carries the click id. | |
DT_INGEST_BIGCOMMERCE_API_TOKEN | BigCommerce API token for the metafield lookup. | |
DT_INGEST_MOBILE_MEMORY | Set to 1 to keep mobile state in memory instead of Postgres/Redis (replay and tests). | |
DT_INGEST_MOBILE_NO_NATS | Set to 1 to skip publishing mobile events to NATS (replay and tests). | |
DT_INGEST_DNI_REDIS_ADDR | Redis 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_WORKERS | 8 | Deferred conversions and state repairs the ingest replays at once from its recovery queue (one conversion's items stay in order). |
DT_INGEST_THROTTLE_HOOK | Set to 0 to disable the ingest postback-throttling hook (it is a no-op for networks without the add-on). | |
DT_INGEST_CLAIM_OWNER | 1 | WP-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_MS | 100 | WP-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_MS | 3000 | WP-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_MS | 60000 | WP-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_SCOPE | 1 | WP-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_MS | 2000 | WP-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_FORMAT | short | WP-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_KEYS | WP-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_WORKERS | 8 | Concurrent 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.
| Variable | Default | Description |
|---|---|---|
DT_REPORTING_PG_DSN | postgres://dt_app:dt_app@127.0.0.1:25432/dtrackier | Postgres DSN for saved reports, FX rates and entity names. |
DT_REPORTING_CH_ADDR | 127.0.0.1:29000 | ClickHouse native address. |
DT_REPORTING_CH_USER | dt | ClickHouse user. |
DT_REPORTING_CH_PASSWORD | dt | ClickHouse password. |
DT_REPORTING_CH_DB | dtrackier | ClickHouse database. |
DT_REPORT_CACHE_MB | 128 | Report result cache budget in MB (rows of analytics queries keyed by tenant, audience, data watermark and query). 0 disables the cache. |
DT_REPORT_CACHE_TTL | 10m | Longest a cached report result is kept (Go duration). New or restated data invalidates earlier through the watermark. |
DT_REPORT_CACHE_WM_MS | 1000 | How 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_DAILY | Set 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_TZ | Set 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_ENDPOINT | 127.0.0.1:29002 | S3-compatible endpoint where report exports are stored and presigned. |
DT_REPORTING_S3_BUCKET | dt-exports | Bucket for report exports. |
DT_REPORTING_S3_KEY | dtminio | Access key for exports. |
DT_REPORTING_S3_SECRET | dtminio-secret | Secret key for exports. |
DT_REPORTING_S3_SSL | Set to 1 for TLS towards the export endpoint. |
Workers (services/workers, port 18084)
Stream consumers and schedulers in one binary.
| Variable | Default | Description |
|---|---|---|
DT_DOMAIN_DEV_EDGE_ADDR | 127.0.0.1:18080 | Development only: host:port of the local edge used to health-check *.localhost tracking domains over plain HTTP (they have no certificate). |
DT_SAAS_USAGE_EVERY | 1m | How 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_EVERY | 1m | How 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_EVERY | 2s | How often the workers check every tenant's cap counter epoch marker in Redis (and rebuild lost counters). |
DT_CAP_EPOCH_SETTLE | 10s | Wait after a lost cap counter set is detected before counting from ClickHouse and Postgres, so events in flight are included. |
DT_CONV_EPOCH_EVERY | 2s | WP-86: how often the workers check every tenant's conversion dedupe epoch marker and renew a missing or stale one. |
DT_CONV_KEYS_RETENTION | 400d | WP-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_EVERY | 1h | WP-86: interval of the conversion_keys retention pass. |
DT_WORKERS_DATABASE_URL | postgres://dt_app:dt_app@127.0.0.1:25432/dtrackier | Postgres connection string of the workers (role dt_app). |
DT_CHW_BATCH_SIZE | 5000 | Rows per ClickHouse insert batch in the event writers. |
DT_CHW_FLUSH_MS | 1000 | ClickHouse 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_MS | 10000 | Timeout of one outgoing postback HTTP request. |
DT_PB_CONCURRENCY | 512 | Concurrent outgoing postbacks. |
DT_PB_MAX_INFLIGHT | 4096 | Postback messages processed concurrently per consumer (sliding window that keeps the dispatcher at ≥ 2k/s with slow endpoints without backlog growth). |
DT_PB_DISABLE_AFTER | 20 | Consecutive failed deliveries after which a postback is disabled and a notification is raised. |
DT_WH_DISABLE_AFTER | 20 | Consecutive failed deliveries after which a webhook is disabled. |
DT_FX_SOURCE | none | Exchange-rate source for the ledger worker: none or frankfurter. |
DT_FX_BASE | USD | Base currency requested from the FX source. |
DT_LEDGER_DEFAULT_LOCK_DAYS | 0 | Tenant default for how many days approved earnings stay locked before release, unless a partner sets billing.lock_days. |
DT_PAYRAILS_TICK_SECONDS | 15 | Poll period of the payout-rails executor. |
DT_DUNNING_TICK_SECONDS | 3600 | Poll period of the invoice dunning scheduler. |
DT_LIST_SYNC_EVERY | 1m | Poll period of URL-backed list sync (duration). |
DT_SCHEDULED_ACTIONS_EVERY | 15s | Poll period of scheduled actions (duration). |
DT_FRAUD_QUEUE_WORKERS | 8 | Conversions the fraud review queue writes at once (one conversion's events stay in order). |
DT_DATA_HEALTH_HINT_EVERY | 1m | How 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_HOUR | 3 | UTC hour of the nightly Redis-to-ClickHouse cap reconciliation. -1 turns it off. |
DT_WEEKLY_SUMMARY_EVERY | 1h | How 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_DAY | Set 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_EVERY | 5m | How often the advertiser notification worker checks cap alerts, due and overdue invoices and inbound postback failures (Go duration). |
DT_FRAUD_BATCH_HOUR | 3 | UTC hour of the nightly fraud batch. |
DT_FRAUD_BATCH_ON_START | Set to 1 to also run the fraud batch when the worker starts. | |
DT_FRAUD_WINDOW_DAYS | 7 | Days of history scanned by the fraud batch. |
DT_LOCKER_INTERVAL | 60s | Poll period of the data-locker exporter (duration). |
DT_LOCKER_DISABLE | Set to 1 to disable the data-locker worker. | |
DT_LEADGEN_DISABLE | Set to 1 to disable the lead-distribution workers. | |
DT_LEADGEN_SYNC_INTERVAL | 2s | Poll period of the lead outbox to ClickHouse sync. |
DT_LEADGEN_RETURNS_INTERVAL | 1m | Poll period of automatic lead return approval. |
DT_CALLS_DISABLE | Set to 1 to disable the call-tracking worker. | |
DT_CALLS_INTERVAL | Poll period of the call conversion sync (duration). | |
DT_SAFE_BROWSING_KEY | Google Safe Browsing API key used by tracking-domain health checks. Unset: the check is skipped. | |
DT_IDEMPOTENCY_RETENTION_HOURS | How long Idempotency-Key responses are kept and replayed (hours, default 24); older keys are purged hourly. | |
DT_THROTTLE_HOLD_EVERY_S | How 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_URL | http://127.0.0.1:18081 | Ingest origin the API and workers call to release a throttled conversion (postback throttling add-on). |
DT_HOLDS_DISABLE | WP-80: set to 1 to stop the conversion hold timer (held conversions stay extended until released by hand). | |
DT_HOLDS_EVERY | 10s | WP-80: how often the hold timer releases due holds (Go duration). |
DT_HOLDS_PLACEHOLDER_TTL | 24h | WP-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_URL | http://127.0.0.1:18081 | WP-80: ingest origin the hold timer calls to release a hold; falls back to DT_THROTTLE_INGEST_URL. |
DT_JS_MONITOR_EVERY_S | 15 | WP-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_AT | 0.8 | WP-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_RECONCILE | 1 | WP-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 %. |
Mobile, SKAN and deep links
Install attribution and Apple/Google postback verification.
| Variable | Default | Description |
|---|---|---|
DT_MOBILE_DISABLE | Set to 1 to disable the mobile workers (ClickHouse writers, install/event postbacks, uninstall and erase jobs). | |
DT_MOBILE_JOBS_INTERVAL | Poll period of the uninstall and erase jobs (duration). | |
DT_SKAN_PUBLIC_KEYS | Apple SKAdNetwork public keys as version=base64 pairs, for example 4.0=...,3.0=...,default=.... Unset entries keep Apple's published keys. | |
DT_SKAN_ACCEPT_UNVERIFIED | Set to 1 to accept SKAN postbacks whose signature does not verify (testing only). | |
DT_AAK_KEYS_URL | URL from which AdAttributionKit verification keys are fetched. | |
DT_AAK_JWKS | Inline JWKS used to verify AdAttributionKit JWS postbacks instead of fetching DT_AAK_KEYS_URL. |
Tracking domains and TLS
DNS verification, health checks and certificates.
| Variable | Default | Description |
|---|---|---|
DT_DOMAIN_CNAME_TARGETS | CNAME 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_RESOLVERS | Comma list of resolvers used for DNS verification instead of the system resolver. | |
DT_TLS_ASK_TOKEN | Secret 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_ADDR | host: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_INSECURE | 1 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_SECONDS | 30 | Period of the domain verification worker. |
DT_DOMAIN_HEALTH_SECONDS | 300 | Period of the domain health worker (TLS expiry, latency, DNSBL). |
DT_CF_ZONE_ID | Cloudflare zone id; with DT_CF_API_TOKEN enables the Cloudflare for SaaS custom hostname provider. | |
DT_CF_API_TOKEN | Cloudflare API token for custom hostnames. |
Observability and status page
Tracing, business gauges, the synthetic canary and the public status page.
| Variable | Default | Description |
|---|---|---|
DT_OTEL_ENDPOINT | OTLP/HTTP trace endpoint (host:port, or a full URL). Unset: tracing is off. | |
DT_OBS_TENANT_LIMIT | Maximum distinct tenant label values in metrics before the rest collapse into one bucket. | |
DT_OBS_DISABLE | Set to 1 to disable the observability worker (consumer lag and business gauges). | |
DT_OBS_LAG_SECONDS | 10 | Consumer-lag poll period. |
DT_OBS_BIZ_SECONDS | 60 | Business gauge poll period. |
DT_OBS_BIZ_WINDOW_SECONDS | 300 | Trailing window of the business gauges. |
DT_OBS_OPS_SECONDS | 60 | Poll period of the caps / fraud / online-reconcile gauges (dt_cap_, dt_fraud_, dt_reconcile_*). |
DT_OBS_OPS_WINDOW_SECONDS | 3600 | Trailing window for the cap-alert and fraud gauges. |
DT_PPROF_ADDR | WP-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_TENANT | Canary tenant that owns the synthetic monitor and the incidents it opens. Unset: the monitor is off. | |
DT_SYNTH_LINK | Canary click path on the edge, for example /c/<offer_hash>/<partner_hash>. Required for the monitor. | |
DT_SYNTH_HOST | Host header (the tenant tracking domain) sent with the canary click and postback. | |
DT_SYNTH_REGIONS | Edge endpoints to probe as name=url pairs, for example eu=https://eu.edge,us=https://us.edge. | |
DT_SYNTH_EDGE_URL | http://127.0.0.1:18080 | Edge URL used when DT_SYNTH_REGIONS is unset. |
DT_SYNTH_INGEST_URL | http://127.0.0.1:18081 | Ingest 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_HOSTS | host:port list whose certificate expiry is exported as dt_synth_cert_expiry_seconds. | |
DT_SYNTH_INTERVAL_SECONDS | 30 | Seconds between synthetic probes. |
DT_SYNTH_INTERVAL | Documented spelling of the probe interval in code comments; the variable actually read is DT_SYNTH_INTERVAL_SECONDS. | |
DT_DATA_HEALTH_DISABLE | Set to 1 to stop the data health self-audit (WP-75) in the workers. | |
DT_DATA_HEALTH_AUTO_REPAIR | 1 | Set to 0 to detect only: derived data (aggregates, ledger balances, raised cap counters) is then not repaired automatically. |
DT_DATA_HEALTH_EVERY_MINUTES | 60 | Period of the data health pass over today and yesterday. |
DT_DATA_HEALTH_DAILY_HOUR | 2 | UTC hour (1-23) from which the daily data health pass over the last 35 days runs. |
DT_TC_BACKFILL_DISABLE | Set 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_MINUTES | 60 | Pause between tenant-currency backfill passes (each finishes interrupted batches first, then the oldest months). |
DT_TC_BACKFILL_BATCHES | 12 | Tenant x month batches per tenant-currency backfill pass (0 = all). |
DT_SYNTH_FAIL_AFTER | 2 | Consecutive probe failures before an incident opens. |
DT_STATUS_PAGE_URL | Origin that serves /status-public; used in subscriber and incident emails. Defaults to DT_PORTAL_BASE_URL. | |
DT_STATUS_DISABLE | Set to 1 to disable the status-notify worker. |
NATS JetStream streams
Not environment variables: stream names that appear in code and logs.
| Variable | Default | Description |
|---|---|---|
DT_CLICKS | JetStream stream for dt.click.> created by the edge. | |
DT_INGEST_RECOVERY | Ingest 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_CONVERSIONS | Ingest-owned stream for conversion events when the workers stream does not already cover them. | |
DT_POSTBACK_IN | Ingest-owned stream for dt.postback_in.logged.v1. | |
DT_EVENTS | Catch-all stream the workers create for subject families no other stream covers. The main workers stream is called DT. | |
DT_MOBILE | JetStream stream for dt.mobile.>. | |
DT_MIGRATION | JetStream 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.
| Variable | Default | Description |
|---|---|---|
DT_E2E_PORT | Playwright: port of a worktree portal build to test (also DT_PORTAL_PORT); the shared dev portals own 3100-3102. | |
DT_E2E_PG_DB | Playwright: Postgres database the e2e helpers and teardown use (your wp-db.sh database), never the shared one. | |
NEXT_PUBLIC_DT_SIGNUP_API | Build-time: API base the marketing site's /signup form posts to (default https://app.metrixed.com/v1). | |
NEXT_PUBLIC_DT_SIGNUP_URL | Build-time: link to the marketing site's signup page from the admin /verify-workspace error state. | |
DT_OPERATOR_PASSWORD | Password read by services/api/cmd/operator create instead of prompting (16+ characters). | |
DT_OPERATOR_DATABASE_URL | DSN for services/api/cmd/operator (defaults to DT_API_DATABASE_URL). | |
DT_MCP_URL | dtrackier-mcp (stdio bridge): the MCP endpoint to forward to, e.g. https://app.example.com/v1/mcp. | |
DT_MCP_TOKEN | dtrackier-mcp (stdio bridge): the MCP token (dtm_…) sent as the bearer token. | |
DT_LOAD_TOOL | make load: which load generator to use (k6 via pinned docker image, or the built-in Go generator when k6 is unavailable). | |
DT_CH_DB | dtrackier | ClickHouse 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_SALT | Dev/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_MS | Test 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_ONLY | postbacks: 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_PREFIX | Postback probe: only postbacks whose rendered URL starts with this fire (the reconcile catcher). Required with DT_WORKERS_ONLY=postbacks. | |
DT_PB_PROBE_SUFFIX | -probe | Postback probe: suffix of its own JetStream consumer names and once-guard namespace. |
DT_RECON_REDIS_PAUSE_CMD | make 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_CMD | make reconcile phase chaos: shell command that releases that Redis again. | |
DT_RECON_PB_CATCHER | 127.0.0.1:28195 | make reconcile: listen address of the partner-postback catcher (phase postback). |
DT_PG_DB | Database 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_DB | dtrackier_test | ClickHouse database Go tests write throwaway rows to (scripts/test-db.sh). |
DT_PURGE_PG_DSN | postgres://dt:dt@127.0.0.1:25432/dtrackier | make dev-purge-test-tenants: Postgres DSN (compose superuser) of the shared dev DB. |
DT_PURGE_CH_URL | http://127.0.0.1:28123 | make dev-purge-test-tenants: ClickHouse HTTP URL. |
DT_PURGE_CH_DB | dtrackier | make dev-purge-test-tenants: ClickHouse database. |
DT_PURGE_REDIS_ADDR | 127.0.0.1:26379 | make dev-purge-test-tenants: Redis address (empty skips Redis). |
DT_TEST_GUARD | fail | make test fails when tenants appear in the shared dtrackier DB while it runs; warn only logs them. |
DT_PG_PORT | 25432 | Postgres host port used by scripts. |
DT_REDIS_PORT | 26379 | Redis host port used by dev-services.sh. |
DT_MINIO_PORT | 29002 | MinIO host port used by make up health checks. |
DT_TEST_PG_DSN | Postgres DSN used by Go tests that need a database; tests skip when unset. | |
DT_TEST_REDIS_ADDR | Redis address used by Go tests that need Redis. | |
DT_LOCKER_TEST_S3 | 127.0.0.1:29002 | MinIO endpoint used by the data-locker integration test. |
DT_LEDGER_ORACLE | Set to 1 by make ledger-oracle to run the ledger oracle test. | |
DT_LEDGER_BENCH | Number of events for the WP-76 ledger posting benchmark (services/workers/internal/ledger); unset skips it. Prints posted events/s. | |
DT_LEDGER_BENCH_SERIAL | Set to 1 to run the ledger benchmark with one transaction per event, for a before/after comparison. | |
DT_REPORTING_ORACLE | Set to 1 by make reporting-oracle to run the reporting oracle tests. | |
DT_UPDATE_FIXTURE | Set to 1 to rewrite expected fixture outputs in the SKAN fixture tests. | |
DT_UPDATE_REPORTMETA | Set to 1 to regenerate the report catalogue in go-shared/reportmeta from the engine registry. | |
DT_UPDATE_VECTORS | Set to 1 to regenerate sdks/test-vectors/signing.json. | |
DT_AUDIT_501 | Set to 1 to print the list of API operations that still answer 501 (the enforced gate is TestNoUnimplementedOperations). | |
DT_STAGE | Only appears as a Snowflake stage name (DT_STAGE) in data-connector tests; not a setting. | |
DT_URL_EDGE_REDIRECT | http://127.0.0.1:18080 | Base URL of a running edge for tools/replay (pattern DT_URL_, with the service name upper-cased and dashes as underscores). |
DT_URL_INGEST | http://127.0.0.1:18081 | Base URL of a running ingest for tools/replay and the replay Make targets. |
DT_REPLAY_NATS_URL | NATS server tools/replay uses to count published events when verifying a fixture. | |
DT_REPLAY_NATS_SUBJECT | Subject whose message count tools/replay checks after a fixture run. | |
DT_REPLAY_NATS_STREAM | Stream tools/replay reads the event count from. | |
DT_API_KEY | Convention 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_URL | Base 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_PHASES | stream,concurrency,matrix | Which phases make reconcile runs: stream (labelled clicks/postbacks reconciled at every layer), concurrency, matrix (redirect decisions, smartlink weights). |
DT_RECON_N | 240 | Number of labelled clicks in the make reconcile stream (the smartlink sample is 3x this). |
DT_RECON_SEED | time-based | Random seed of the make reconcile click mix; printed by every run so a failure can be replayed. |
DT_ORACLE_PROFILE | small | make oracle (WP-73 differential oracle): scenario size, small (~5k events, CI) or large (≥300k events, nightly). |
DT_ORACLE_RATE | 90 small / 900 large | make oracle: requests per second the scenario is sent at through the edge and ingest (all tenants together). |
DT_ORACLE_QUIESCE_MAX | 30m | make oracle / make reconcile (WP-78): longest wait for a quiet pipeline (spool empty, every consumer caught up, ledger stable) before comparing. |
DT_ORACLE_STALL_MAX | 5m | make 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_ATTEMPTS | 6 | make 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_ARGS | scripts/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.json | make oracle: path of the run summary (seed, events per tenant, runtime). |
DT_W73_NAME | wp73 | scripts/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_MS | Chaos-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_NAME | wp74 | scripts/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_DB | dtrackier_wp74 | scripts/wp74-chaos.sh: ClickHouse database of the chaos-accuracy stack. |
DT_W74_PORT_BASE | 28740 | scripts/wp74-chaos.sh: first service port (edge; ingest, api, reporting, workers, workers2 follow). |
DT_W74_INFRA_BASE | 23840 | scripts/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_POOL | 6 | scripts/wp74-chaos.sh: pool_max_conns of every private service (Postgres allows 400 connections box-wide). |
DT_W74_SKIP_BUILD | scripts/wp74-chaos.sh up: 1 = reuse the binaries in .run/wp74. | |
DT_ACC_PROFILE | quick | make 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_SCENARIOS | make chaos-accuracy: comma-separated scenario names (tools/replay/reconcile/wp74_scenarios.go). | |
DT_ACC_CTL | Accuracy phase: the stack control script (scripts/wp74-chaos.sh); the phase refuses to run without it (it injects faults). | |
DT_ACC_CATCHER | 127.0.0.1:28749 | Accuracy phase: address of the partner-postback / webhook receiver. |
DT_ACC_NATS_MON | Accuracy phase: NATS monitoring URL of the private stack. | |
DT_RECON_SPREAD | 40s | Window over which make reconcile paces its labelled clicks (bursts look like click injection to the fraud rules). |
DT_RECON_DEADLINE | 60s | Convergence deadline per make reconcile check (docs/11: report visibility under 60 s). |
DT_RECON_EDGE | http://127.0.0.1:18080 | Edge 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_PHASES | core,lifecycle | Which 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_NAME | wp64 | scripts/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_REDIS | make 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_DSN | postgres://dt:dt@127.0.0.1:25432/dtrackier | make 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_MAILPIT | http://127.0.0.1:28025 | Mailpit web/API base URL read by the e2e specs, scripts/live-e2e.py and make email-audit to fetch delivered emails. |
DT_E2E_API | http://127.0.0.1:18082/v1 | API 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_EDGE | http://127.0.0.1:18080 | Edge (click redirect) base URL for scripts/live-e2e.py; point it at a private stack. |
DT_E2E_INGEST | http://127.0.0.1:18081 | Ingest (postback) base URL for scripts/live-e2e.py; point it at a private stack. |
DT_LIVE_PARTNER_ID | scripts/live-e2e.py: reuse this seeded partner instead of creating one (CI sets Partner 01 so the portal e2e reports have rows). | |
DT_EDGE_URL | http://127.0.0.1:18080 | Edge base URL make docs-quickstart clicks through (with DT_INGEST_URL for the postback and DT_API_URL for the API calls). |
DT_INGEST_URL | http://127.0.0.1:18081 | Ingest base URL make docs-quickstart sends the documented postback to. |
DT_DOCS_E2E_PORT | 3106 | Port make docs-e2e serves the docs static export on for the Playwright crawl (never reuses a running server). |
DT_WWW_E2E_PORT | 3181 | Port make www-e2e / www-a11y / www-demo-e2e serve the marketing-site export on. |
DT_WWW_LEAD_FORM_URL | Build-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_TENANT | tenant-basic | Workspace 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_ORIGINS | https://metrixed.com + local e2e ports | Comma-separated origins allowed to post to the demo lead form (CORS allow-list set by demo-intake.mjs). |
DT_FA_PRIVATE_INGEST | make fraud-accuracy: 1 builds ingest from this checkout and sends conversions to a private instance instead of the shared one on :18081. | |
DT_PW_CHANNEL | Browser 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_PREBUILT | scripts/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_MIGRATE | go run …/migrate@v4.18.1 | Migrate 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_BIN | go run ./services/api/cmd/seed | Seed command scripts/e2e-bootstrap.sh runs; CI passes a prebuilt bin/seed (WP-69). |
DT_CI_DATA | CI 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_PCT | make 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_PIDDIR | make 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_DB | make 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_CONTAINERS | make 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_PPROF | scripts/wp74-chaos.sh (WP-84): 1 serves each service's pprof on 127.0.0.1:<its port + 1000> (DT_PPROF_ADDR). | |
DT_W84_CPUS | 4,10 | scripts/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_NAME | wp84c | scripts/wp84-chaos.sh: stack name (DT_W74_NAME). |
DT_W84_PORT_BASE | 28960 | scripts/wp84-chaos.sh: first service port (DT_W74_PORT_BASE). |
DT_W84_INFRA_BASE | 23960 | scripts/wp84-chaos.sh: infrastructure port base (DT_W74_INFRA_BASE). |
DT_W84_CH_DB | dtrackier_wp84c | scripts/wp84-chaos.sh: ClickHouse database (DT_W74_CH_DB). |
DT_W84S_CPUS | 2,8 | scripts/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_NAME | wp84s | scripts/wp84-stack.sh: stack name (DT_W80_NAME). |
DT_W84S_CH_MEM | 4g | scripts/wp84-stack.sh: memory limit of the stack's ClickHouse container. |
DT_W84S_CH_SERVER_MEM | 3200000000 | scripts/wp84-stack.sh: ClickHouse max_server_memory_usage (bytes, below the container limit). |
DT_TEST_NATS_URL | nats://127.0.0.1:24222 | Go tests that need JetStream (chwriter flush window, WP-84): the NATS to create their throwaway stream on. |
DT_ACC_PB_DELAY_MS | 1500 | make 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_MS | 1500 | make chaos-accuracy (WP-85): random extra postback delay on top of DT_ACC_PB_DELAY_MS. |
DT_ACC_RESEND_MIN_MS | 500 | make 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_MS | 4000 | make chaos-accuracy (WP-86): latest resend of a resent postback after its original (see DT_ACC_RESEND_MIN_MS). |
DT_ORACLE_COLD | make 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_SAMPLE | 4000 | make 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_REDIS | WP-85 test TestWP85RedisMemoryLayouts: a throwaway Redis (it is flushed) on which the click-cache layouts are measured in bytes per click. | |
DT_W85_CPUS | 5,11 | scripts/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_NAME | wp85c | scripts/wp85-chaos.sh: stack name (DT_W74_NAME). |
DT_W85_PORT_BASE | 28980 | scripts/wp85-chaos.sh: first service port (DT_W74_PORT_BASE). |
DT_W85_INFRA_BASE | 23980 | scripts/wp85-chaos.sh: infrastructure port base (DT_W74_INFRA_BASE). |
DT_W85_CH_DB | dtrackier_wp85c | scripts/wp85-chaos.sh: ClickHouse database (DT_W74_CH_DB). |
DT_W85S_CPUS | 3,9 | scripts/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_NAME | wp85s | scripts/wp85-stack.sh: stack name (DT_W80_NAME). |
DT_W85S_CH_MEM | 4g | scripts/wp85-stack.sh: memory limit of the stack's ClickHouse container. |
DT_W85S_CH_SERVER_MEM | 3200000000 | scripts/wp85-stack.sh: ClickHouse max_server_memory_usage (bytes). |
DT_W86_CPUS | 5,11 | scripts/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_NAME | wp86c | scripts/wp86-chaos.sh: stack name. |
DT_W86_PORT_BASE | 29180 | scripts/wp86-chaos.sh: first service port. |
DT_W86_INFRA_BASE | 24180 | scripts/wp86-chaos.sh: infrastructure port base. |
DT_W86_CH_DB | dtrackier_wp86c | scripts/wp86-chaos.sh: ClickHouse database. |
DT_W86S_CPUS | 3,9 | scripts/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_NAME | wp86s | scripts/wp86-stack.sh: stack name (DT_W80_NAME). |
DT_W86S_CH_MEM | 4g | scripts/wp86-stack.sh: memory limit of the stack's ClickHouse container. |
DT_W86S_CH_SERVER_MEM | 3200000000 | scripts/wp86-stack.sh: ClickHouse max_server_memory_usage (bytes). |
DT_W87_CPUS | 5,11 | scripts/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_NAME | wp87c | scripts/wp87-chaos.sh: stack name. |
DT_W87_PORT_BASE | 29380 | scripts/wp87-chaos.sh: first service port. |
DT_W87_INFRA_BASE | 24380 | scripts/wp87-chaos.sh: infrastructure port base. |
DT_W87_CH_DB | dtrackier_wp87c | scripts/wp87-chaos.sh: ClickHouse database. |
DT_W87S_CPUS | 3,9 | scripts/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_NAME | wp87s | scripts/wp87-stack.sh: stack name (DT_W80_NAME). |
DT_W89_CPUS | 4,10 | scripts/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_ROUNDS | 200 | reconcile 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_PAR | 20 | reconcile phase burst (WP-89): identical postbacks fired at once per round. |
DT_BURST_BATCH | 100 | reconcile phase burst (WP-89): rounds whose clicks are created before they are fired. |
DT_BURST_CONCURRENT | 1 | reconcile phase burst (WP-89): rounds fired at the same time (each on its own click). |
DT_BURST_OUT | reconcile phase burst (WP-89): file for one line per round (answers, created count, distinct created ids, queued / resolved, stored counts). | |
DT_W87S_CH_MEM | 4g | scripts/wp87-stack.sh: memory limit of the stack's ClickHouse container. |
DT_W87S_CH_SERVER_MEM | 3200000000 | scripts/wp87-stack.sh: ClickHouse max_server_memory_usage (bytes). |
DT_WP86_LAT | WP-86 test TestWP86ClaimOfRecordLatency: 1 measures per-postback latency with and without the synchronous claim of record (DT_TEST_PG_DSN, a test Redis database). |