From 2ff350ecf958b3bd0611ccaae8f22f9cce82dc5b Mon Sep 17 00:00:00 2001 From: Matthew Meszaros Date: Thu, 10 Sep 2026 19:14:34 +0200 Subject: [PATCH] feat: apply WARMBLY_POSTHOG_ERROR_TRACKING to the public form pages too, by having cmd/forms stamp an empty browser key when it is false, since the page can only act on whether a key arrived and the flag otherwise silenced the dashboard and the admin panel while leaving form pages reporting --- cmd/forms/main.go | 19 ++++++++++++++++++- docker-compose.yml | 1 + .../docs/development/configuration.mdx | 4 ++-- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/cmd/forms/main.go b/cmd/forms/main.go index 2c8b3464..eb157c63 100644 --- a/cmd/forms/main.go +++ b/cmd/forms/main.go @@ -62,7 +62,7 @@ func main() { // own credentials: form pages are public and their errors belong in a // frontend project, not the service's. Empty means the page loads no // reporting SDK at all, which is the self-host default. - BrowserPostHogKey: strings.TrimSpace(os.Getenv("WARMBLY_POSTHOG_KEY")), + BrowserPostHogKey: browserPostHogKey(), BrowserPostHogHost: strings.TrimSpace(os.Getenv("WARMBLY_POSTHOG_HOST")), BrowserSentryDSN: strings.TrimSpace(os.Getenv("WARMBLY_SENTRY_DSN")), Release: observability.Release(), @@ -91,6 +91,23 @@ func main() { } } +// browserPostHogKey is the key stamped into the form page, or nothing when the +// operator turned browser error tracking off. +// +// The toggle is applied here rather than in the page because the page has no +// way to be told: it is served to a stranger's browser and the only thing it +// can act on is whether a key arrived. Without this, +// WARMBLY_POSTHOG_ERROR_TRACKING would silence the dashboard and the admin +// panel and leave form pages reporting. +func browserPostHogKey() string { + if raw := strings.TrimSpace(os.Getenv("WARMBLY_POSTHOG_ERROR_TRACKING")); raw != "" { + if enabled, err := strconv.ParseBool(raw); err == nil && !enabled { + return "" + } + } + return strings.TrimSpace(os.Getenv("WARMBLY_POSTHOG_KEY")) +} + // appEnv is the deployment label, matching what InitEnv reports for this // process so the browser and the server halves agree. func appEnv() string { diff --git a/docker-compose.yml b/docker-compose.yml index 5c87ea5d..08113e04 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -434,6 +434,7 @@ services: SENTRY_DSN: ${SENTRY_DSN:-} WARMBLY_POSTHOG_KEY: ${WARMBLY_POSTHOG_KEY:-} WARMBLY_POSTHOG_HOST: ${WARMBLY_POSTHOG_HOST:-} + WARMBLY_POSTHOG_ERROR_TRACKING: ${WARMBLY_POSTHOG_ERROR_TRACKING:-} WARMBLY_SENTRY_DSN: ${WARMBLY_SENTRY_DSN:-} extra_hosts: - "host.docker.internal:host-gateway" diff --git a/docs/content/docs/development/configuration.mdx b/docs/content/docs/development/configuration.mdx index 6bbcecb2..12003a08 100644 --- a/docs/content/docs/development/configuration.mdx +++ b/docs/content/docs/development/configuration.mdx @@ -428,7 +428,7 @@ Set both and every error goes to both. Each variable is read by one process, so | `WARMBLY_POSTHOG_KEY` | web, admin containers | Browser error tracking, read at container start like the other `WARMBLY_*` values. On the dashboard the same key also carries the product analytics below; the admin panel reports only exceptions | unset | | `WARMBLY_POSTHOG_KEY` | forms service | Stamped into the public form page for the form app's browser errors. Separate from the forms service's own `POSTHOG_KEY`: one is a Go process, the other is a page a stranger loads | unset | | `WARMBLY_POSTHOG_HOST` | web, admin containers, forms service | Capture host for the browser | `https://us.i.posthog.com` | -| `WARMBLY_POSTHOG_ERROR_TRACKING` | web, admin containers | `false` reports no browser exceptions to PostHog. The dashboard keeps sending product analytics | `true` | +| `WARMBLY_POSTHOG_ERROR_TRACKING` | web, admin containers, forms service | `false` reports no browser exceptions to PostHog anywhere, including the form pages the forms service stamps. The dashboard keeps sending product analytics | `true` | | `WARMBLY_SENTRY_DSN` | web, admin containers | The Sentry half of the same thing. Unset means the SDK is never fetched and no host is contacted | unset | | `WARMBLY_SENTRY_DSN` | forms service | Stamped into the public form page, along with the service's `APP_ENV` as the environment | unset | | `WARMBLY_SENTRY_ENVIRONMENT` | web, admin containers | The environment label browser events carry, whichever backend receives them. Defaults to the build mode. Form pages do not read it: the forms service stamps its own `APP_ENV` into the page instead | unset | @@ -514,7 +514,7 @@ The public face of hosted forms (`cmd/forms`): it serves the React (TanStack) fo | `FORM_IP_RATE_LIMIT` | Public form submissions allowed per source IP per 10 minutes, per forms-service instance | `30` | | `POSTHOG_KEY`, `POSTHOG_HOST`, `POSTHOG_ERROR_TRACKING` | The service's own error tracking. Unset means none | unset | | `SENTRY_DSN` | The same through Sentry, alongside PostHog or instead of it | unset | -| `WARMBLY_POSTHOG_KEY`, `WARMBLY_POSTHOG_HOST` | Stamped into the form page so the browser app reports too. Unset means the page loads no reporting SDK at all | unset | +| `WARMBLY_POSTHOG_KEY`, `WARMBLY_POSTHOG_HOST`, `WARMBLY_POSTHOG_ERROR_TRACKING` | Stamped into the form page so the browser app reports too. Unset, or error tracking off, means the page loads no reporting SDK at all | unset | | `WARMBLY_SENTRY_DSN` | The Sentry half of the same stamping | unset | | `TRUSTED_PROXIES` | CIDRs whose `X-Forwarded-For` the service believes, same convention as the backend. Empty trusts nothing and uses the socket peer; set it behind a reverse proxy or the submit limiter throttles the proxy's address instead of the visitor's | empty |