docs(marketing): disclose Google user data handling in the privacy policy
Summary
- Add a dedicated Google User Data section (5.1-5.6) to
superset.sh/privacy, covering every Google API scope we request, what we store, who it is shared with, the Limited Use commitments, and how to revoke and delete. - Renumber sections 6-13, add a Connected Accounts bullet to 2.1, bump
lastUpdated.
Why / Context
Google's app privacy policy requirements are what an OAuth consent screen is verified against. The structural half already passed: /privacy is a dedicated URL distinct from the homepage, on a domain we own, linked from the homepage footer (Footer.tsx:161) and the cookie banner.
The content half did not. Google appeared exactly once in the policy — as a sign-in option in 2.1 — with nothing about Gmail access, token storage, or retention. Google requires the policy to state, specifically, which Google user data the app accesses, how it is used, whether it is shared, how it is secured, and how long it is kept, and it explicitly rejects templates and sample policies.
How It Works
The scope list is transcribed from the code, not from a template, because a reviewer compares the policy against the live consent screen:
- 5.1 Sign-in only receives the account id, email, name, and picture, and reaches no Google service.
- 5.2 Names each scope with its purpose. The first-party Gmail trigger requests
gmail.readonly(packages/trpc/src/router/integration/google/constants.ts:1-5); the marketplace connector requests the broader set —gmail.compose,gmail.modify,gmail.labels,gmail.settings.basic,https://mail.google.com/(packages/shared/src/connectors/connectors.json:15-25). Both flows share oneGOOGLE_CLIENT_ID, so the policy has to cover the broadest set. - 5.3 What we store, and what we do not: the trigger path persists headers and labels only, never bodies or attachments (
syncMailbox.ts:126-160, field mask atgmail.ts:107-113). The encryption claim is scoped to connector tokens, which are the ones that actually go through AES-256-GCM (secret-box.ts). - 5.4 Discloses that mail content an agent fetches goes to our AI provider to fulfil the request and is not used to train models.
- 5.5 The Limited Use disclosure: no advertising, no data brokers, no credit decisions, no generalized model training, no transfers beyond providing the feature.
- 5.6 Concrete because the code is: disconnect revokes the grant with Google, stops the mailbox watch, and deletes the tokens (
google.ts:43-86).
Manual QA Checklist
Rendering
-
/privacyrenders the new section 5 with its six subsections - Section numbering runs 1-13 with no gaps or duplicates
- Inline
codespans render legibly in the.prosecontainer, light and dark - The three links resolve: Google's user data policy,
/subprocessors,myaccount.google.com/permissions - "Last updated: 29 September 2026" shows in the header
- Footer and cookie-banner links still reach the page
Accuracy
- Every scope listed in 5.2 matches the live consent screen, and the consent screen lists no scope absent from 5.2
Testing
Not run: this worktree has no installed dependencies, so MDX compile, next build, typecheck, and lint all fail on missing modules rather than on this change. The diff is one markdown file and contains no <, >, {, or }, so it is plain markdown to the MDX pipeline — but rendering is unverified and should be confirmed in the Vercel preview before merge.
check:i18n is unaffected: legal pages are MDX content, not JSX, and (legal) is not in packages/i18n/test/enforced-dirs.ts. Legal copy stays English-only, consistent with the other three legal pages.
Known Limitations
Three findings from auditing the code behind the policy. All are code changes, deliberately left out to keep this PR to the document, and the wording here is honest about each rather than aspirational.
calendar.readonlyis requested and never used. It sits in the connector scope list (connectors.json:18) with zero Calendar API calls anywhere in the repo — the only@googleapis/*import is gmail. An unused sensitive scope is a common verification rejection. 5.2 discloses it plainly ("Superset does not read your calendar"), which is defensible, but deleting the scope is the better fix.- Sign-in tokens are not encrypted at rest. better-auth writes
auth.accounts.access_token/refresh_tokenas plaintext(packages/db/src/schema/auth.ts:87-92); noencryptOAuthTokensis configured. 5.3 therefore claims encryption only for connector credentials, which is accurate. - Recorded Gmail metadata has no pruner.
automation_events.integration_connection_idis not a foreign key, so disconnecting does not remove the rows — only organization deletion cascades. 5.6 says exactly that and offers deletion on request.
Follow-ups
- Drop
calendar.readonlyfrom the connector scopes, and the unused@googleapis/calendardependency. - Encrypt better-auth OAuth tokens at rest.
- Prune
automation_events.payloadon a retention window, and revisit 5.6 once there is one to state.
Summary by cubic
Adds a Google User Data section to the privacy policy so Google OAuth verification can pass. Google requires the policy to name each requested scope and carry the Limited Use disclosure; the policy previously only mentioned Google as a sign-in option.
- Section 5.1–5.6 names every Google API scope we request, what we store and do not store, who mail content is shared with, the Limited Use commitments, and how to revoke and delete access.
- Renumbers sections 6–13 and adds a Connected Accounts bullet to 2.1.
- The wording is honest about current gaps:
calendar.readonlyis requested but unused, sign-in tokens are not encrypted at rest, and Gmail metadata has no pruner.
Summary by CodeRabbit
- Documentation
- Updated the privacy policy date to September 29, 2026.
- Added details about Google sign-in and optional Gmail connections, including requested permissions, data handling, use, retention, and deletion.
- Clarified how credentials and data from connected services are stored. Existing sections were renumbered; their content is unchanged.