Files
oO/docs/architecture/privacy.md
alvis 7f173f88d3 refactor: architecture revision — modular monolith, auth-commit, event protobuf, privacy-from-day-0
- ADR-0003: modular monolith for Phase 0 with documented extraction triggers
- ADR-0004: Auth.js + OIDC-shaped boundary; dedicated provider when mobile ships
- ADR-0005: protobuf for events, OpenAPI for HTTP, schema-registry CI gate
- New architecture docs: data-model, metrics (magic proxies), privacy (Phase-0 feature)
- Prime directives updated: privacy-as-feature, modular-by-package-deployable-by-stage
- Roadmap revised: Apple OAuth deferred to M1; web push in M1; k3s intermediate; tip-kind-aware UI
- PLAN updated: Phase-0 deletion endpoint, metrics baseline, compose profiles, import-boundary lint
- License decision in README (ARR with OSS plan in Phase 5)
2026-04-13 14:36:11 +00:00

2.8 KiB

Privacy architecture

Privacy is a Phase 0 feature, not a Phase 5 compliance project. This doc is the minimum.

Principles

  1. Data minimization. Store only what we need for the tip. Raw task titles stay at Todoist; we store references + computed features. If a feature doesn't lift a metric, its input data doesn't get stored.
  2. User-visible controls. Every connection shows exactly which scopes we hold and what we've computed. One tap disconnects and revokes.
  3. Deletion is real. Deleting an account revokes provider tokens, purges credentials immediately, and soft-deletes user data for a 30-day recovery window, then hard-deletes.
  4. No surprise sharing. Cross-user / collaborative features are opt-in, per category, per integration.
  5. Encryption in transit and at rest. TLS everywhere; column-level encryption for credentials; disk-level for backups.

Flows

Connect

User taps "Connect Todoist" → consent screen lists: scopes requested, what we store, what we compute, retention, revocation instructions → OAuth → stored credential is immediately testable and shows in /connect.

Disconnect

User taps disconnect → Credential.revoked_at set → provider-side revocation attempted (Todoist: token revocation endpoint) → credential erased on success → credential.revoked event → downstream modules drop associated cursors, caches, derived features for that (user, provider) pair.

Delete account

User taps "Delete account" in settings → hard confirm → User.deleted_at set, all sessions revoked, user.deletion_requested event fanned out → every module processes its portion (credentials revoked + purged; profile scrubbed; tip history anonymized to aggregate stats only or purged, per retention policy; events purged on schedule) → within 24 hours account is non-recoverable operationally; within 30 days all rows are hard-deleted.

Export (Phase 2)

GET /me/export returns a JSON bundle of everything we hold for the user: profile, consents, credentials-metadata (not secrets), events, tip history.

Scope boundaries

Each integration declares the scopes it requests and the features it derives. The Profile.consents column is the source of truth; a scope removed from consent short-circuits derived-feature computation at the feature store.

Audit

  • Privileged actions (admin-initiated deletions, credential decryption outside the normal refresh path) go to an append-only audit log from Phase 0.
  • Per-user access log available via GET /me/access-log (Phase 2).
  • Terms of Service + Privacy Policy documents shipped alongside the sign-in page.
  • Consent capture on first sign-in, with a versioned ToS/PP hash stored per user.
  • Data-subject request inbox (email) wired up before onboarding the first external user.