You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
We added consent-gated logging to the production add-in so the privacy policy and
the code agree: each user has a loggingConsent level (none / usage / ai_output / document) stored on their Better Auth record, and logging/analytics
are stripped to that level on both client and server. Logs are keyed by Better Auth
user id (the URL username is gone), and there's a deletion path.
The backend, client gating, and the rewritten privacy policy landed in #504. A
follow-on branch (feat/llm-usage-metering) then added per-user LLM cost metering
and reworked the deletion paths — see "What changed since this issue was written"
below. This issue tracks the remaining work: the consent UI.
Remaining: consent UI
Onboarding consent step. Default is strictly content-free; actively ask
the user to choose their level. Choice architecture (no dark patterns):
privacy-protective option pre-selected, equal visual weight, plain-language
descriptions (see CONSENT_LEVEL_LABELS in frontend/src/consent.ts), content
logging (ai_output/document) never pre-checked, and the longitudinal-study
opt-in presented as a separate explicit consent.
Standalone authenticated account/consent page. Must be reachable without
the Word task pane loaded — a new Vite entry (e.g. account.html / consent.html,
alongside taskpane.html/editor.html/logs.html) or surfaced within editor.html. Gated on a Better Auth session (Bearer via authTokenStore);
unauthenticated visitors go through sign-in first. Lets the user:
view and change their logging level -> POST /api/me/consent { level }
erase their logged activity -> DELETE /api/me/activity (withdrawal: study
logs + analytics profile are erased, the account stays, LLM usage records keep
accruing because the account is still billable)
delete their account -> Better Auth delete-user route (departure: the same
erasure, plus the account itself; usage records are anonymized, not deleted)
The two are deliberately different actions and the UI should say so — see the
"Your Rights and Choices" section of the privacy policy for the user-facing wording.
What changed since this issue was written (feat/llm-usage-metering)
DELETE /api/me/data is now DELETE /api/me/activity. The old name promised
more than it delivered (it never touched the account or the usage records).
Both deletion paths now route through one eraseLoggedData() (backend/src/erasure.ts),
so account deletion is by construction a superset of withdrawal. It previously
forgot to purge the PostHog person.
The auth DB is now the shared app.db (Better Auth's tables + ours, one
connection), with our own PRAGMA user_version migrations in backend/src/db.ts.
Run npx @better-auth/cli migrate and wire it into the deploy repo — done,
via a different mechanism (backend/src/migrate.ts applies both schemas; the
legacy auth.db is renamed on first open).
Verify deletePosthogPerson against our PostHog version — moved to Post-deploy verification: usage metering + data erasure #517
(post-deploy verification), where it belongs: it can only be checked against a
deployed environment, and it's now load-bearing for both deletion paths.
Decisions still open
none behavior: currently fully silent (PostHog opt-out also suppresses capture_exceptions). Decide whether we want an "errors-only at none" carve-out
(needs autocapture-off + manual exception capture rather than opt-out).
EU / ePrivacy: confirm the onboarding prompt satisfies EU opt-in expectations
for non-essential analytics (or add a region-aware consent banner).
Retention window: the policy says "while your account is active / until you
request deletion". Now there are two categories to decide on: logged study
events, and the content-free AI usage records (currently retained indefinitely
as billing history). Pick concrete caps if we want them.
Fail-closed in prod: with auth disabled, the proxy still serves requests on
the main key (metered as anonymous) rather than refusing. That keeps local dev
working, but means a prod deploy that forgot BETTER_AUTH_ENABLED=true would
quietly spend the main key unattributed. Consider keying the fallback on NODE_ENV instead.
Context
We added consent-gated logging to the production add-in so the privacy policy and
the code agree: each user has a
loggingConsentlevel (none/usage/ai_output/document) stored on their Better Auth record, and logging/analyticsare stripped to that level on both client and server. Logs are keyed by Better Auth
user id (the URL
usernameis gone), and there's a deletion path.The backend, client gating, and the rewritten privacy policy landed in #504. A
follow-on branch (
feat/llm-usage-metering) then added per-user LLM cost meteringand reworked the deletion paths — see "What changed since this issue was written"
below. This issue tracks the remaining work: the consent UI.
Remaining: consent UI
Onboarding consent step. Default is strictly content-free; actively ask
the user to choose their level. Choice architecture (no dark patterns):
privacy-protective option pre-selected, equal visual weight, plain-language
descriptions (see
CONSENT_LEVEL_LABELSinfrontend/src/consent.ts), contentlogging (
ai_output/document) never pre-checked, and the longitudinal-studyopt-in presented as a separate explicit consent.
Standalone authenticated account/consent page. Must be reachable without
the Word task pane loaded — a new Vite entry (e.g.
account.html/consent.html,alongside
taskpane.html/editor.html/logs.html) or surfaced withineditor.html. Gated on a Better Auth session (Bearer viaauthTokenStore);unauthenticated visitors go through sign-in first. Lets the user:
POST /api/me/consent { level }DELETE /api/me/activity(withdrawal: studylogs + analytics profile are erased, the account stays, LLM usage records keep
accruing because the account is still billable)
erasure, plus the account itself; usage records are anonymized, not deleted)
The two are deliberately different actions and the UI should say so — see the
"Your Rights and Choices" section of the privacy policy for the user-facing wording.
What changed since this issue was written (
feat/llm-usage-metering)DELETE /api/me/datais nowDELETE /api/me/activity. The old name promisedmore than it delivered (it never touched the account or the usage records).
eraseLoggedData()(backend/src/erasure.ts),so account deletion is by construction a superset of withdrawal. It previously
forgot to purge the PostHog person.
app.db(Better Auth's tables + ours, oneconnection), with our own
PRAGMA user_versionmigrations inbackend/src/db.ts.Run— done,npx @better-auth/cli migrateand wire it into the deploy repovia a different mechanism (
backend/src/migrate.tsapplies both schemas; thelegacy
auth.dbis renamed on first open).Verify— moved to Post-deploy verification: usage metering + data erasure #517deletePosthogPersonagainst our PostHog version(post-deploy verification), where it belongs: it can only be checked against a
deployed environment, and it's now load-bearing for both deletion paths.
Decisions still open
nonebehavior: currently fully silent (PostHog opt-out also suppressescapture_exceptions). Decide whether we want an "errors-only at none" carve-out(needs autocapture-off + manual exception capture rather than opt-out).
for non-essential analytics (or add a region-aware consent banner).
request deletion". Now there are two categories to decide on: logged study
events, and the content-free AI usage records (currently retained indefinitely
as billing history). Pick concrete caps if we want them.
the main key (metered as
anonymous) rather than refusing. That keeps local devworking, but means a prod deploy that forgot
BETTER_AUTH_ENABLED=truewouldquietly spend the main key unattributed. Consider keying the fallback on
NODE_ENVinstead.Related
llm_usagestays the ledger)