diff --git a/.env.example b/.env.example index be1fec14..b19dd622 100644 --- a/.env.example +++ b/.env.example @@ -110,12 +110,16 @@ JMAP_SERVER_URL=https://your-jmap-server.com # Anonymous Telemetry # ============================================================================= -# Anonymous instance telemetry is enabled by default. Heartbeats contain no PII: -# version, platform, bucketed account counts, and feature toggles only. See +# Anonymous instance telemetry is OPT-IN and disabled by default. Enabling it +# helps us understand how Bulwark is used so we can make the product better. +# Heartbeats contain no PII: version, platform, bucketed account counts, and +# feature toggles only - never email addresses, hostnames, or IPs. See # https://bulwarkmail.org/docs/legal/privacy/telemetry for the full schema. # -# Disable telemetry entirely (overrides the admin UI): -# BULWARK_TELEMETRY=off +# Enable telemetry (also toggleable in the admin UI): +# BULWARK_TELEMETRY=on +# +# Setting this (on or off) locks the choice and disables the admin UI toggle. # Directory for telemetry state: instance id, consent, login HMACs # (default: ./data/telemetry). For Docker, the default resolves to diff --git a/FEATURES.md b/FEATURES.md index cf7a1bde..375ff236 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -136,7 +136,7 @@ Automatic browser detection with persistent preference. Configurable locale URL - Progressive Web App with service worker, install prompt, web push notifications for inbox mail, dynamic manifest, and configurable (per-domain) install screenshots - Automatic update check with server-side logging of new releases and a non-dismissible update notice - Structured logging (`text` or `json`) with category-based levels -- Anonymous instance telemetry (opt-out via admin UI or `BULWARK_TELEMETRY=off`) – version, platform, bucketed account counts, feature toggles only +- Anonymous instance telemetry (opt-in via admin UI, the installer, or `BULWARK_TELEMETRY=on`; off by default) – version, platform, bucketed account counts, feature toggles only - Release (`main`) and development (`dev`) Docker images on GHCR - Subpath deployment via `NEXT_PUBLIC_BASE_PATH` for mounting behind a reverse proxy - Demo mode with fixture data – no mail server required diff --git a/app/(main)/admin/_tabs/telemetry.tsx b/app/(main)/admin/_tabs/telemetry.tsx index 6ff3b760..0337966b 100644 --- a/app/(main)/admin/_tabs/telemetry.tsx +++ b/app/(main)/admin/_tabs/telemetry.tsx @@ -118,9 +118,10 @@ export function TelemetryTab() {

Anonymous Usage Stats

- Bulwark sends one anonymous heartbeat per day so we can see how many instances are - running, on what platforms, and which features they use. Enabled by default; - one click below disables it. No email addresses, no hostnames, no IPs are sent.{' '} + Bulwark can send one anonymous heartbeat per day so we can see how many instances are + running, on what platforms, and which features they use. It's off by + default; one click below enables it and helps us make the product better. No + email addresses, no hostnames, no IPs are sent.{' '} Status

{status.consent === 'pending' && 'Initialising - no heartbeats sent yet.'} - {status.consent === 'on' && 'Heartbeats are enabled (default).'} - {status.consent === 'off' && 'Heartbeats are off.'} + {status.consent === 'on' && 'Heartbeats are enabled. Thanks for helping us improve!'} + {status.consent === 'off' && 'Heartbeats are off (default).'} {envOverridden && ( <> Locked by BULWARK_TELEMETRY env var. )} diff --git a/lib/telemetry/state.ts b/lib/telemetry/state.ts index 48b1ab27..306b86ba 100644 --- a/lib/telemetry/state.ts +++ b/lib/telemetry/state.ts @@ -17,6 +17,7 @@ function idPath(): string { return path.join(getDir(), '.telemetry-id'); } function envOverride(): ConsentState | null { const v = (process.env.BULWARK_TELEMETRY ?? '').toLowerCase(); if (v === 'off' || v === 'false' || v === '0' || v === 'no') return 'off'; + if (v === 'on' || v === 'true' || v === '1' || v === 'yes') return 'on'; if (process.env.BULWARK_TELEMETRY_DISABLED) { const d = process.env.BULWARK_TELEMETRY_DISABLED.toLowerCase(); if (d === '1' || d === 'true' || d === 'yes') return 'off'; @@ -41,11 +42,13 @@ export async function getInstanceId(): Promise { return fresh; } -// Default consent is 'on' - telemetry is anonymous and enabled by default. -// Admins can disable via the UI, the BULWARK_TELEMETRY env var, or by clearing -// the endpoint. See https://bulwarkmail.org/docs/legal/privacy/telemetry. +// Default consent is 'off' - telemetry is opt-in. Admins can enable it during +// install (BULWARK_TELEMETRY=on in .env.local), via the BULWARK_TELEMETRY env +// var, or with one click in the admin UI. Heartbeats are anonymous: no PII, +// just version/platform/feature toggles. Enabling helps us improve the product. +// See https://bulwarkmail.org/docs/legal/privacy/telemetry. const DEFAULTS: TelemetryStateFile = { - consent: 'on', + consent: 'off', endpoint: DEFAULT_ENDPOINT, consentedAt: null, lastSentAt: null, @@ -64,13 +67,10 @@ export async function loadState(): Promise { error: err instanceof Error ? err.message : String(err), }); } - // First-ever load on a fresh install: persist the default-on state with - // an autoEnabledAt stamp so the admin UI can show "telemetry was - // auto-enabled at