A data-subject request (SubjectRequest) records an application by a person to see, correct, erase, or object to the processing of their personal data. The privacy module keeps the register, the identity check, the deadline, and the outcome. It never reads another module's tables: it fans out two EventBus events, and each module that holds personal data answers for its own tables in its privacy_handlers.py. An erasure is also written to a ledger outside the database so that a restored backup cannot bring the data back unnoticed. The module overview is on the privacy module page.
In short¶
- Four request types exist:
access,rectification,erasure,objection(src/cluster_erp/modules/privacy/requests/models.py:13-18). - Four statuses exist:
received,verified,completed,rejected; every transition is an action (src/cluster_erp/modules/privacy/requests/models.py:32-36,src/cluster_erp/modules/privacy/requests/services.py:196-527). - The due date is the receipt date plus the setting
privacy_response_days(src/cluster_erp/modules/privacy/requests/models.py:61-62,src/cluster_erp/modules/privacy/requests/services.py:157-170,src/cluster_erp/config/settings.py:733). CollectSubjectDatagathers a copy andAnonymiseSubjecterases by anonymisation; nine modules subscribe to both (src/cluster_erp/core/contracts.py:2385-2438).- A completed erasure is appended to a node-local ledger, sealed with the backup key when one is set, and replayed at every start-up (
src/cluster_erp/core/erasure_ledger.py:1-16,src/cluster_erp/server/app.py:255-311).
How it works¶
Request lifecycle¶
The state diagram shows the statuses and the actions that move them.
stateDiagram-v2
[*] --> received: create
received --> verified: verify
verified --> verified: export
verified --> completed: anonymise, erasure only
verified --> completed: complete
received --> rejected: reject
verified --> rejected: reject
completed --> [*]
rejected --> [*]In prose:
creategives the request a yearly business key, setsdue_at, and starts atreceived(src/cluster_erp/modules/privacy/requests/services.py:157-172).verifyrecords the identity method and movesreceivedtoverified(src/cluster_erp/modules/privacy/requests/services.py:198-207).prepare_export, allowed only whileverified, collects the data and stores one JSON file as an attachment on the request (src/cluster_erp/modules/privacy/requests/services.py:247-272).export_filereturns it (src/cluster_erp/modules/privacy/requests/services.py:274-295).anonymise, allowed only for anerasurerequest that isverified, requires the operator to type the request code, then erases and completes the request (src/cluster_erp/modules/privacy/requests/services.py:297-327).completecloses a verified request with a resolution note;rejectcloses a received or verified request with a reason (src/cluster_erp/modules/privacy/requests/services.py:507-527).- A completed or rejected request cannot be edited (
src/cluster_erp/modules/privacy/requests/services.py:174-186).
Collecting a copy¶
collect fetches the subject's e-mail addresses through GetPartyContactValues, emits CollectSubjectData with the party id, the optional user id, and those addresses, and keeps every answer that has sections (src/cluster_erp/modules/privacy/requests/services.py:209-245). Each answer has the shape {"module", "sections": [{"key", "label", "rows"}]} and must not contain secrets such as password hashes or tokens (src/cluster_erp/core/contracts.py:2385-2401).
Anonymising¶
_apply_erasure runs all modules in one transaction (src/cluster_erp/modules/privacy/requests/services.py:329-424):
- It emits
AnonymiseSubjectwith the marker fromprivacy_anonymised_marker, a reason, and three operator decisions:include_employment_records,include_correspondence, andinclude_documents(src/cluster_erp/modules/privacy/requests/services.py:345-357,src/cluster_erp/modules/privacy/requests/schemas.py:70-73). - Each handler overwrites identifying fields, deactivates the rows, and returns the affected record ids (
src/cluster_erp/core/contracts.py:2410-2425). - The service adds the attachments filed on the person's party record, removes reported files from storage, and retires their rows (
src/cluster_erp/modules/privacy/requests/services.py:358-395). - It replaces the change-history values of every affected record with a marker that names the request (
src/cluster_erp/modules/privacy/requests/services.py:396-406). - It stores a per-module summary, completes the request, and writes the ledger line (
src/cluster_erp/modules/privacy/requests/services.py:407-424).
Modules that answer¶
Nine modules subscribe to both events, found with grep for CollectSubjectData in module.py files:
| Module | Handler file | Subscription |
|---|---|---|
auth |
src/cluster_erp/modules/auth/privacy_handlers.py |
src/cluster_erp/modules/auth/module.py:101-108 |
crm |
src/cluster_erp/modules/crm/privacy_handlers.py |
src/cluster_erp/modules/crm/module.py:110-113 |
documents |
src/cluster_erp/modules/documents/privacy_handlers.py |
src/cluster_erp/modules/documents/module.py:89-96 |
intranet |
src/cluster_erp/modules/intranet/privacy_handlers.py |
src/cluster_erp/modules/intranet/module.py:216-223 |
logs |
src/cluster_erp/modules/logs/privacy_handlers.py |
src/cluster_erp/modules/logs/module.py:55-62 |
mail |
src/cluster_erp/modules/mail/privacy_handlers.py |
src/cluster_erp/modules/mail/module.py:63-70 |
owned_organisations |
src/cluster_erp/modules/owned_organisations/privacy_handlers.py |
src/cluster_erp/modules/owned_organisations/module.py:81-88 |
parties |
src/cluster_erp/modules/parties/privacy_handlers.py |
src/cluster_erp/modules/parties/module.py:159-166 |
procurement |
src/cluster_erp/modules/procurement/privacy_handlers.py |
src/cluster_erp/modules/procurement/module.py:136-137 |
As an example, the auth handler returns the sign-in account without the password hash, and on erasure overwrites the user name and e-mail, detaches the employee link, deactivates the account, and stamps last_logout_at so that every token is refused (src/cluster_erp/modules/auth/privacy_handlers.py:1-104).
Erasure ledger¶
erasure_ledger.append writes one line per completed erasure into a per-database file in a folder under the backup_dir setting; the line holds the request code, party id, user id, subject e-mail addresses, the three decisions, and the time (src/cluster_erp/core/erasure_ledger.py:38-74, src/cluster_erp/modules/privacy/requests/services.py:426-457). With backup_encryption_key set, each line is sealed; without it, the line is plain JSON (src/cluster_erp/core/erasure_ledger.py:42-53). At start-up, _replay_erasure_ledger reads the ledger of every database the node serves, re-applies any erasure that the database no longer shows as completed, and then runs the retention purge (src/cluster_erp/server/app.py:255-311, src/cluster_erp/modules/privacy/requests/services.py:459-505). tests/test_erasure_ledger.py (4 tests) pins the sealing.
Why it is built this way¶
- The module docstring states that the privacy module imports no other module; the modules that hold personal data answer for their own tables (
src/cluster_erp/modules/privacy/module.py:16-24). - The request row stays after completion as evidence that the request was answered in time, and it is never hard-deleted (
src/cluster_erp/modules/privacy/requests/models.py:40-46). - Anonymisation handlers must join the request session so that a failure anywhere rolls every module back (
src/cluster_erp/core/contracts.py:2411-2425). - Employment records, correspondence, and documents stay by default because they commonly sit under a statutory retention duty (
src/cluster_erp/core/contracts.py:2421-2433). - The ledger lives outside the database so that a restore cannot silently undo an erasure (
src/cluster_erp/core/erasure_ledger.py:3-10).
Rules and boundaries¶
- Every route requires a
subject_request.*code:read,create,update,delete,verify,export,anonymise,complete, orreject(src/cluster_erp/modules/privacy/requests/routes.py:39-215). - Credential and special-category values are redacted in change history in any case; see change history.
Interaction with other parts¶
- Parties answers
GetPartyContactValues(src/cluster_erp/core/contracts.py:2400-2408). - Attachments, notes, and storage stores the export file.
- Backups and restore explains the restore that the ledger guards against.
Limits and open points¶
- A ledger write failure is logged and does not undo the erasure (
src/cluster_erp/modules/privacy/requests/services.py:433-457). - Rectification has no automated step; it is closed with
completeand a resolution note (src/cluster_erp/modules/privacy/requests/services.py:507-516).
Terms introduced¶
| Term | Code identifier | Definition | Turkish term | Source |
|---|---|---|---|---|
| data-subject request | SubjectRequest |
A registered application by a person about their personal data. | İlgili Kişi Başvurusu | src/cluster_erp/modules/privacy/requests/models.py:39 |
| anonymisation | AnonymiseSubject |
Erasure carried out by overwriting identifying fields with a marker and deactivating rows. | anonimleştirme | src/cluster_erp/core/contracts.py:2411 |
| subject data collection | CollectSubjectData |
The fan-out in which every module returns what it holds about a person. | ilgili kişi verisi toplama | src/cluster_erp/core/contracts.py:2385 |
| erasure ledger | erasure_ledger |
The append-only record of completed erasures, kept outside the database and replayed after a restore. | silme defteri | src/cluster_erp/core/erasure_ledger.py:1 |
Related pages¶
Verified against commit 92f8051 on 2026-11-13.