Consent-gated logging + accurate privacy policy#504
Draft
kcarnold wants to merge 1 commit into
Draft
Conversation
Each user gets a loggingConsent level (none/usage/ai_output/document) stored on their Better Auth record. Logging and PostHog analytics are stripped to that level on both client and server; logs are keyed by Better Auth user id (the URL username is removed) and there is a deletion path. Backend: - consent.ts: 4-level model + authoritative content-stripping filter - auth.ts: loggingConsent/consentUpdatedAt user fields (server-controlled) and deleteUser enabled with a beforeDelete hook that purges logs - app.ts: /api/log requires a session, keys by user id, enforces consent; new POST /api/me/consent and DELETE /api/me/data; /api/protected returns id + loggingConsent - logging.ts deleteUserLogs(); posthog.ts deletePosthogPerson() (best-effort) - tests cover all tiers, id-keying, username-spoof rejection, 401s, deletion Frontend: - consent.ts mirrors the backend + UI labels; consent flows through deviceAuth -> appAuthContext - useLog hook: consent-aware, token-authenticated logging; draft refactored to it; dead log()/usernameAtom/userContext removed - PostHog starts opt-out-by-default; PostHogConsentBridge opts in + identifies only at consent >= usage Privacy policy rewritten: provider-flexible AI disclosure (OpenAI today), Google + PostHog listed, Auth0 removed, the four logging levels explained, account-associated + deletable framing, deletion + retention, contact filled in. Remaining work (consent UI, migration, decisions) tracked in #503. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR aligns the add-in’s logging/analytics behavior with a new, explicit 4-tier logging-consent model and updates the published privacy policy accordingly. It introduces consent-aware payload stripping on both client and server, switches log identity from client-supplied usernames to authenticated Better Auth user IDs (enabling deletion), and adds data-deletion endpoints plus PostHog opt-in behavior gated by consent.
Changes:
- Added shared consent levels (
none/usage/ai_output/document) with authoritative server-side stripping and mirrored client-side filtering. - Reworked event logging to require an authenticated session (Bearer token), key logs by stable user ID, and added endpoints to update consent and delete user data.
- Updated analytics initialization to be opt-out-by-default and updated the privacy policy to reflect actual processing/storage and third-party disclosures.
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| frontend/src/pages/draft/index.tsx | Switches draft-page event logging to the new consent-aware useLog hook. |
| frontend/src/pages/app/index.tsx | Makes PostHog opt-out-by-default and adds a consent→PostHog bridge for opt-in/identify behavior. |
| frontend/src/hooks/useLog.ts | Adds authenticated, consent-aware client logging with payload filtering and Bearer token attachment. |
| frontend/src/contexts/userContext.tsx | Removes legacy URL username identity source. |
| frontend/src/contexts/appAuthContext.tsx | Extends auth session shape to include user.id and resolved loggingConsent. |
| frontend/src/consent.ts | Adds client-side consent levels, UI labels, and payload filtering logic. |
| frontend/src/api/index.ts | Removes legacy anonymous log() API and points to useLog. |
| frontend/src/api/deviceAuth.ts | Extends /api/protected user shape to include id and loggingConsent. |
| frontend/public/privacypolicy.html | Rewrites privacy policy to match consent-gated logging and provider disclosures. |
| backend/src/posthog.ts | Adds best-effort deletion of a user’s PostHog person/events via management API (when configured). |
| backend/src/logging.ts | Adds deleteUserLogs() to support “delete my data” and account deletion hooks. |
| backend/src/consent.ts | Adds server-side consent-level ordering and authoritative content stripping. |
| backend/src/auth.ts | Adds loggingConsent / consentUpdatedAt user fields and a delete-user hook to purge logs. |
| backend/src/app.ts | Requires session for /api/log, gates content by consent, adds /api/me/consent and /api/me/data. |
| backend/src/tests/consent.test.ts | Adds consent filtering tests for all tiers and ordering. |
| backend/src/tests/app.test.ts | Adds tests for auth-required logging, ID-keying, consent gating, consent update, and deletion. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+38
to
+42
| none: { | ||
| title: 'No logging', | ||
| description: | ||
| 'No usage analytics or event logging. Only anonymous crash reports, to keep the app working.', | ||
| }, |
Comment on lines
+413
to
+419
| const analyticsAllowed = | ||
| isAuthenticated && consentRank(loggingConsent) >= consentRank('usage'); | ||
|
|
||
| if (analyticsAllowed) { | ||
| if (user?.id) posthog.identify(user.id); | ||
| posthog.opt_in_capturing(); | ||
| } else { |
Comment on lines
396
to
400
| docContext: docContextRef.current, | ||
| }); | ||
| getSuggestion(request, false); | ||
| }, [getFetcher, getSuggestion, modesToShow, shouldAutoRefresh, username]); | ||
| }, [getFetcher, getSuggestion, shouldAutoRefresh, log]); | ||
|
|
Comment on lines
+52
to
+55
| await fetch( | ||
| `${host}/api/projects/${projectId}/persons/?distinct_id=${encodeURIComponent(distinctId)}&delete_events=true`, | ||
| { method: 'DELETE', headers: { Authorization: `Bearer ${personalKey}` } }, | ||
| ); |
Comment on lines
+59
to
+61
| export async function deleteUserLogs(username: string): Promise<void> { | ||
| await rm(logFilePath(username), { force: true }); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
Reconciles the production add-in's data practices with its privacy policy. Previously
the policy promised document content was stored only on research opt-in, while the code
logged full document text + AI results to plaintext JSONL for every user with no consent
gate, OpenAI wasn't disclosed, and there was no deletion path.
This introduces a 4-level logging-consent model (
none/usage/ai_output/document) stored on the Better Auth user record, gates logging + analytics to thatlevel on both client and server, keys logs to a stable auth identity (so deletion is
possible), and rewrites the policy to match.
Backend
consent.ts— levels + authoritative content-stripping filterauth.ts—loggingConsent/consentUpdatedAtuser fields (server-controlled,input: false);deleteUserenabled with abeforeDeletehook that purges logsapp.ts—/api/logrequires a session, keys by user id, enforces consent;new
POST /api/me/consent+DELETE /api/me/data;/api/protectedreturnsid+loggingConsentlogging.tsdeleteUserLogs();posthog.tsdeletePosthogPerson()(best-effort)update, and deletion (25 passing)
Frontend
consent.tsmirrors the backend + UI labels; consent flowsdeviceAuth→appAuthContextuseLoghook — consent-aware, token-authenticated logging;draft/index.tsxrefactored to it; dead
log()/usernameAtom/userContext.tsxremovedPostHogConsentBridgeopts in +identifysonly at consent ≥
usagePrivacy policy
Rewritten: provider-flexible AI disclosure (OpenAI today), Google + PostHog listed,
Auth0 removed, four logging levels explained, account-associated + deletable framing,
deletion + retention, contact filled in.
Status
Backend + frontend typecheck; backend tests pass. Draft — the consent UI
(onboarding step + standalone account/consent page), the Better Auth migration, and a
few open decisions remain. Tracked in #503.
Base is
single-container-consolidation(this branch builds on it) so the diff showsonly the consent work.