Repository navigation
Personal-data export / erasure / retention policy (GDPR + PH DPA) #272
Description
Activity
- changed the title
[-]Deploy: personal-data export / erasure / retention policy (GDPR + PH DPA)[/-][+]Personal-data export / erasure / retention policy (GDPR + PH DPA)[/+]on Jul 30, 2026 - added a commit that references this issue
on Aug 2, 2026 - addedsliceThin vertical work itemThin vertical work itemand removed
on Aug 5, 2026 Cross-link: epic #530 (Phase 1.6 — Multi-farm tenancy).
Not a slice of that epic and not blocked by it, but the two intersect in a way worth recording before either is designed further.
#530 chooses one shared database with row-level
AccountIdisolation over database-per-tenant. One thing that explicitly buys, and it lands on this issue: per-tenant erasure and per-tenant export become queries, notpg_dumpandDROP DATABASE. Whatever DSAR export and erasure paths this issue defines will have to be correct under the global query filter, and correct for the ~20IgnoreQueryFilters()call sites that deliberately step outside it (#536 enumerates and justifies all of them, which is directly useful groundwork here).Also newly relevant once farms are separate customers rather than one farm:
- Whose data is it. A farm suspended by CLI: suspend-account / reactivate-account operator verbs #534 is not a farm erased. CLI: suspend-account / reactivate-account operator verbs #534 blocks authentication and deletes nothing, on purpose — erasure stays entirely this issue's scope.
- Retention on abandonment is a real question the moment a second farm exists, and becomes urgent if public signup (Public self-serve farm signup (email verification, abuse controls, quotas) #540) ever ships.
- The audit trail's immutable
ActorEmailsnapshot exception argued here now has to hold across tenants, not just within one.
New PII category from #788: connected applications and their tokens.
#788 adds an OAuth 2.1 authorization server so MCP clients (AI assistants) can connect. That introduces personal data this issue's field classification does not yet cover, and it lands in all three of the areas named above — export, erasure, and retention.
New data, all user-linked:
- Authorizations — which apps a user has connected, when they approved, and what they granted. This is behavioural data about the person: it reveals which AI assistants they use and when they started.
- Tokens — reference tokens tied to a user, with issue and expiry timestamps, and last-used if tracking lands.
- Applications — self-registered client records. Not user PII in itself, but a dynamically-registered client can carry a user-supplied display name and redirect URIs that may identify an individual.
- Audit provenance — Build an OAuth 2.1 authorization server (OpenIddict) so MCP clients can authenticate #788 adds first-class columns to
AuditEventsrecording which app acted alongside the human actor. That inherits this issue's existingActorEmailproblem:AuditEventdeliberately snapshots immutably with no mutation surface, so the same erasure-versus-pseudonymisation tension now applies to the app dimension too.
Consequences for each of this issue's three deliverables:
- DSAR export must include a user's connected applications and approval history. A subject access request that omits "which AI assistants have access to my account" is incomplete in an obvious way.
- Erasure must revoke authorizations and delete tokens — not merely orphan them. An erased user whose OAuth authorization row survives is a live credential with no owner, which is worse than a data-protection gap: it is a security defect.
- Retention needs a rule for revoked authorizations and expired tokens. Build an OAuth 2.1 authorization server (OpenIddict) so MCP clients can authenticate #788 decided connections last indefinitely until revoked, so nothing expires them on its own; a retention policy is the only thing that would.
Sequencing: this does not block either issue. #788 ships first and should keep its data model simple; whoever implements this should treat the OAuth tables as in-scope from the start rather than retrofitting them. Worth noting that erasure touching OAuth tables is easier to build before there is production data in them.
Closing — out of scope for now, recorded as an accepted risk
Closed by the owner on 2026-09-13 during the issue cleanup. Not done, not disproved — deliberately
not being worked, and written down here so the position is a decision rather than a gap nobody
looked at.What the app does today, unchanged by this close
- Stores third-party personal data. Customer name, phone, email, address and free-text notes
(Customer.cs); user email and display name. - Has no per-subject export. Only the admin farm-wide CSV export (F24: CSV export / manual backup — per-dataset CSVs, full account export (zip), self-host dump instructions #95), whose dataset list omits
user records entirely (ExportQueries.cs). - Has no erasure or anonymisation path. None. A deletion request cannot be honoured by any
supported operation. - Has no retention policy for any personal data.
- Snapshots
ActorEmailimmutably inAuditEventwith no mutation surface, by design.
What is being accepted
If a data subject exercises a right of access or erasure under EU/UK GDPR or the Philippines
Data Privacy Act, Cluckwork cannot service it without a hand-written SQL migration authored for
that request. The PH exposure is the concrete one — the es/tl locales target PH users and the
product holds Philippine customer records now.The two widening factors recorded in the comments above stand and get worse with time, not better:
- EPIC: Phase 1.6 — Multi-farm tenancy #530's shared-database choice makes per-tenant erasure a query that must be correct under
the global query filter and across the ~20 justifiedIgnoreQueryFilters()sites (Cross-tenant isolation hardening: enumerating IgnoreQueryFilters guard + two-farm end-to-end matrix #536). Every
new bypass added between now and whenever this is picked up is another site that path must handle. - Build an OAuth 2.1 authorization server (OpenIddict) so MCP clients can authenticate #788 adds a new user-linked PII category — which applications a person connected, when they
approved, and what they granted. That is behavioural data about the person. Building OAuth before
the classification exists means classifying it retroactively.
What should reopen this
- A data-subject request of any kind arriving.
- Onboarding a farm in the EU/UK, or any customer who asks for a DPA.
- Build an OAuth 2.1 authorization server (OpenIddict) so MCP clients can authenticate #788 shipping, which materialises the connected-applications PII above.
- Any decision to market the product rather than run it for a known farm.
The cheap half, if this is ever revisited
The build (DSAR export endpoint, erasure path, backup propagation) is large. The policy half is
not: classify the PII fields, state retention rules, and decide erasure versus legally-required
pseudonymisation for the append-only audit trail. That is a documentation deliverable, it is most
of what a regulator asks to see first, and it makes the build schedulable. It was offered as a split
during this cleanup and declined along with the rest — noted here so the option is on the record.- Stores third-party personal data. Customer name, phone, email, address and free-text notes
Deployment-readiness gap (#244), RECOMMENDED (market-dependent) — flagged by 2 of 3 reviewers.
Problem
The app stores third-party personal data but has no data-subject rights or retention story:
AuditEventdeliberately snapshotsActorEmailimmutably (AuditEvent.cs) with no mutation surface.Relevant to EU/UK GDPR and the Philippines Data Privacy Act (the es/tl locales target PH/LatAm users).
Fix
Identify applicable jurisdiction/contract requirements; classify PII fields; set retention rules; define subject export and erasure vs legally-required pseudonymization (with a documented, audited exception for the append-only trail); propagate deletes into backups and telemetry. Test one full request end-to-end.
Verify
Run one DSAR export + one erasure on a staging subject; confirm the data is returned/removed everywhere except the justified audit exception.
Part of the #244 deployment-readiness audit; tracked in epic #15.