docs(marketing): disclose Google user data handling in the privacy policy

Merged
HA
1mo ago
+64-9

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 one GOOGLE_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 at gmail.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

  • /privacy renders the new section 5 with its six subsections
  • Section numbering runs 1-13 with no gaps or duplicates
  • Inline code spans render legibly in the .prose container, 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.

  1. calendar.readonly is 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.
  2. Sign-in tokens are not encrypted at rest. better-auth writes auth.accounts.access_token/refresh_token as plain text (packages/db/src/schema/auth.ts:87-92); no encryptOAuthTokens is configured. 5.3 therefore claims encryption only for connector credentials, which is accurate.
  3. Recorded Gmail metadata has no pruner. automation_events.integration_connection_id is 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.readonly from the connector scopes, and the unused @googleapis/calendar dependency.
  • Encrypt better-auth OAuth tokens at rest.
  • Prune automation_events.payload on 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.readonly is 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.