From 05540b88b0081b01e60a5bf39da068ab0e39b997 Mon Sep 17 00:00:00 2001 From: AutoGenUI <231675956+AutoGenUI@users.noreply.github.com> Date: Wed, 14 Oct 2026 21:47:11 +0800 Subject: [PATCH] fix(mail): opt every Brevo recipient out of tracking --- AGENTS.md | 6 +++ EDITORIAL_POLICY.md | 2 +- README.md | 2 +- app/[locale]/privacy/page.tsx | 4 +- components/news/subscription.tsx | 2 +- docs/CURRENT_STATE.json | 50 ++++++++++++++++++ docs/PROJECT_CONTEXT.md | 8 ++- .../2026-10-14-brevo-tracking-consent.md | 18 +++++++ docs/operations/README.md | 4 +- docs/operations/email-subscriptions.md | 14 +++-- lib/news/mail-template.ts | 2 +- lib/news/mail.ts | 3 +- public/automation.md | 2 +- tests/news.test.mjs | 51 ++++++++++++++++++- 14 files changed, 152 insertions(+), 16 deletions(-) create mode 100644 docs/iterations/2026-10-14-brevo-tracking-consent.md diff --git a/AGENTS.md b/AGENTS.md index bb62293..f9d34d9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -80,3 +80,9 @@ current state rather than treating a dated snapshot as a permanent fact. not show the check-inbox notice. Owner mail-test diagnose is read-only and exact-configured-target scoped, never part of editorial schedules. No private identifiers or individual delivery records go into public documentation. + +- Every Brevo `to` recipient carries `contactPixelTrackingConsent: false` for + public and owner-test confirmation/digest mail. Preserve it if adding cc/bcc; + never retry by removing the flag. Account-side per-contact consent enablement + is a separate prerequisite; final pixel/link removal needs real-mail evidence. + See docs/iterations/2026-10-14-brevo-tracking-consent.md for current verification. diff --git a/EDITORIAL_POLICY.md b/EDITORIAL_POLICY.md index 3b44458..5671506 100644 --- a/EDITORIAL_POLICY.md +++ b/EDITORIAL_POLICY.md @@ -10,4 +10,4 @@ Sources include official blogs, specifications, release repositories, original r The older GitHub ecosystem watcher remains a supplementary source-drift issue reporter. Dynamic editorial operations are documented in public/automation.md and use owner-authorized MCP tools backed by D1. Reading a public Site grants no write, mail or subscriber-data permissions. -Email stays pending activation until Brevo free account/domain and real delivery acceptance pass. No new paid services or model APIs. No automatic upgrades, empty digests, tracking pixels or silent retry of uncertain submissions. +Public email activation is recorded in the v12 and v1.0.1 release records; preserve current production gates. Every Brevo recipient explicitly requests no open/click tracking with `contactPixelTrackingConsent: false`. Account-side enablement and complete removal of provider pixels/rewritten links require separate verification; keep bilingual disclosures accurate. Our templates add no tracking resources. No new paid services or model APIs, automatic upgrades, empty digests or silent retry of uncertain submissions. diff --git a/README.md b/README.md index 9fdf8bd..b85aee8 100644 --- a/README.md +++ b/README.md @@ -95,7 +95,7 @@ and waits for its own local Worker before running HTTP tests. Set secrets only through Sites environment variables. `SITE_OWNER_EMAIL` authorizes the verified Sites owner identity; `ANALYTICS_SECRET` derives daily visitor estimates; `SUBSCRIPTION_SECRET` signs confirmation and management links. Never use a Sites viewer or bypass token as owner authorization. -Email activation requires `BREVO_API_KEY`, `BREVO_SENDER_EMAIL`, `BREVO_WEBHOOK_SECRET`, `BREVO_DOMAIN_VERIFIED=true`, `EMAIL_ACCEPTANCE_PASSED=true` and `EMAIL_ENABLED=true`. Keep `EMAIL_ENABLED` and `EMAIL_ACCEPTANCE_PASSED` false until a real mailbox confirms, receives a digest, changes preferences and unsubscribes, and authenticated callbacks are verified. With the owner's acceptance and accurate public disclosure, anonymous Brevo open/click tracking may remain enabled; anonymizing tracking does not disable pixels or tracked links. The site does not use these statistics to decide whether to send mail or as proof that an email was read. Fully disabling tracking remains a future privacy improvement. Configure transactional webhooks with bearer authentication to `/api/brevo/webhook`; subscribe to delivered, hardBounce, softBounce, spam, invalid, blocked and unsubscribed. Official contracts: [secured webhooks](https://developers.brevo.com/docs/secured-webhooks), [transactional events](https://developers.brevo.com/docs/transactional-webhooks). +Email activation requires `BREVO_API_KEY`, `BREVO_SENDER_EMAIL`, `BREVO_WEBHOOK_SECRET`, `BREVO_DOMAIN_VERIFIED=true`, `EMAIL_ACCEPTANCE_PASSED=true` and `EMAIL_ENABLED=true`. Keep `EMAIL_ENABLED` and `EMAIL_ACCEPTANCE_PASSED` false until a real mailbox confirms, receives a digest, changes preferences and unsubscribes, and authenticated callbacks are verified. Every Brevo recipient explicitly carries `contactPixelTrackingConsent: false`. The account must first enable per-contact pixel tracking consent; set unknown-consent tracking to No. Account enablement and complete removal of pixels/rewritten links require separate verification; the current iteration records that gap. Open/click statistics never decide sending or prove reading. See [email operations](docs/operations/email-subscriptions.md). Configure transactional webhooks with bearer authentication to `/api/brevo/webhook`; subscribe to delivered, hardBounce, softBounce, spam, invalid, blocked and unsubscribed. Official contracts: [secured webhooks](https://developers.brevo.com/docs/secured-webhooks), [transactional events](https://developers.brevo.com/docs/transactional-webhooks). For real delivery acceptance before public activation, also configure `EMAIL_TEST_RECIPIENT` as a server-side secret containing the one explicitly authorized test address. Keep `EMAIL_ENABLED=false` and `EMAIL_ACCEPTANCE_PASSED=false`. The owner-only `newsroom_write` / `/api/admin` operation `mail-test` requires a verified domain, sender, API key, webhook secret and existing subscription signing secret. It cannot take a recipient argument, enable public email, or drain the production queue. Do not invoke it from scheduled editorial tasks. diff --git a/app/[locale]/privacy/page.tsx b/app/[locale]/privacy/page.tsx index 14a081a..5a569ad 100644 --- a/app/[locale]/privacy/page.tsx +++ b/app/[locale]/privacy/page.tsx @@ -40,8 +40,8 @@ export default async function Privacy({

{zh ? "邮件订阅" : "Email subscriptions"}

{zh - ? "邮箱用于确认订阅、精选周报和订阅偏好;订阅须完成邮件确认后才生效,可选择语言与主题。邮件由 Brevo 投递,当前匿名打开与点击统计不关联具体联系人,但邮件仍可能包含打开像素与链接跟踪。本站不依据这些统计决定发信,也不将其作为已读证明。每封邮件提供退订与偏好链接;退订、投诉和硬退信将停止后续投递。" - : "Email addresses are used for confirmation, the weekly digest and subscription preferences. A subscription takes effect only after email confirmation, and subscribers can choose their language and topics. Brevo delivers email; its current anonymous open and click statistics are not associated with individual contacts, but emails may still contain tracking pixels and tracked links. This site does not use these statistics to decide whether to send email or as proof that an email was read. Every email includes unsubscribe and preference links. Unsubscribes, complaints and hard bounces stop future delivery."} + ? "邮箱用于确认订阅、精选周报和订阅偏好;订阅须完成邮件确认后才生效,可选择语言与主题。邮件由 Brevo 投递。本站对每位收件人明确要求关闭打开与点击跟踪;最终邮件是否完全移除像素与链接改写仍待验证,不承诺邮件完全无跟踪。本站不依据打开或点击统计决定发信,也不将其作为已读证明。每封邮件提供退订与偏好链接;退订、投诉和硬退信将停止后续投递。" + : "Email addresses are used for confirmation, the weekly digest and subscription preferences. A subscription takes effect only after email confirmation, and subscribers can choose their language and topics. Brevo delivers email. We explicitly request no open/click tracking for each recipient. Complete removal of pixels and rewritten links from delivered mail remains unverified; we do not promise tracking-free email. Open/click statistics do not determine sending or prove reading. Every email includes unsubscribe and preference links. Unsubscribes, complaints and hard bounces stop future delivery."}

{zh diff --git a/components/news/subscription.tsx b/components/news/subscription.tsx index 865ced7..ea17851 100644 --- a/components/news/subscription.tsx +++ b/components/news/subscription.tsx @@ -296,7 +296,7 @@ function SubscriptionView({ locale, initialLink, onManagementUrl }: { })()} {form} {manage && !confirmationMode && (subscriber?.status === "confirmed" || subscriber?.status === "pending") &&

{zh ? "取消邮件订阅" : "Cancel email updates"}

{zh ? "停止后续邮件;仍可使用 RSS。" : "Stop future email. RSS remains available."}

} -

{zh ? "邮件由 Brevo 投递。当前匿名打开与点击统计不关联具体联系人,但仍可能包含像素与链接跟踪;本站不依据这些统计决定发信或判断已读。" : "Brevo delivers email. Anonymous opens/clicks are not tied to contacts; tracking pixels and links may remain. These statistics do not determine sending or prove reading."}

+

{zh ? "邮件由 Brevo 投递。本站要求关闭打开与点击跟踪,不以此判断已读或决定发信;像素与链接改写是否完全移除仍待验证。" : "Brevo delivers email. We request no open/click tracking and do not use it to decide sending or prove reading. Complete pixel/link removal remains unverified."}