33.1 Overview
ADL provides built-in privacy compliance features for GDPR, CCPA, HIPAA, and FERPA. The system supports field-level privacy classification, data subject rights (access, erasure, portability, rectification), consent management, retention policies, and encryption at rest.
Privacy is declared at the field level and enforced by the runtime. The privacy configuration is the data map. The anonymization rules are the erasure procedure. The consent configuration is the processing control.
33.2 Privacy Classification
Privacy is configured at the domain level and applied to individual fields.
Domain Configuration
domain:
name: clinicalTrial
version: "1.0.0"
privacy:
enabled: true
classifications:
personalData:
description: "Standard personal data"
retention: 7 years
sensitiveData:
description: "Special category data (GDPR Art. 9)"
retention: 30 years
financialData:
description: "Financial and payment information"
retention: 10 years
subjectIdentifier:
primary: email
alternates: [subjectId, ssn, participantNumber]
anonymization:
method: anonymize
rules:
default: "Redacted"
dateOfBirth: "$fuzzyDate(5 years)"
diagnosis: "$preserve"
Field-Level Privacy
resources:
Participant:
object:
firstName:
type: string
required: true
privacy:
classification: personalData
pii: true
email:
type: string
required: true
privacy:
classification: personalData
pii: true
subjectIdentifier: true
dateOfBirth:
type: date
privacy:
classification: sensitiveData
pii: true
medicalHistory:
type: text
privacy:
classification: sensitiveData
sensitive: true
phi: true
ssn:
type: string
privacy:
classification: financialData
pii: true
encrypted: true
Privacy Field Properties
| Property | Type | Description |
|---|---|---|
classification | string | Privacy category from domain classifications |
pii | boolean | Personally Identifiable Information flag |
sensitive | boolean | Special category / sensitive data (GDPR Art. 9) |
phi | boolean | Protected Health Information (HIPAA) |
encrypted | boolean | Encrypt field at rest |
subjectIdentifier | boolean | This field identifies the data subject |
33.3 Data Subject Rights APIs
Right of Access (GDPR Art. 15)
GET /api/v1/{domain}/privacy/subject/{identifier}
?format=json
Returns all data for the subject across all resources:
{
"code": 200,
"data": {
"subject": "alice@example.com",
"domain": "clinicalTrial",
"generatedAt": "2026-05-17T14:30:00Z",
"resources": {
"Participant": {
"count": 1,
"fields": {
"personalData": ["email", "firstName", "lastName", "phone"],
"sensitiveData": ["dateOfBirth", "medicalHistory", "diagnosis"],
"financialData": ["ssn"]
},
"records": [
{
"id": "a1b2c3d4...",
"email": "alice@example.com",
"firstName": "Alice",
"lastName": "Johnson",
"enrollmentDate": "2025-06-15",
"status": "active"
}
]
},
"AdverseEvent": {
"count": 2,
"records": [...]
}
},
"audit": {
"count": 47,
"note": "47 audit records reference this subject."
},
"history": {
"count": 12,
"note": "12 history events reference this subject."
}
}
}
The runtime traverses every resource, queries for records where any field marked subjectIdentifier: true matches the identifier, and returns the complete data profile.
Right of Erasure (GDPR Art. 17)
POST /api/v1/{domain}/privacy/subject/{identifier}/erase
{
"method": "anonymize",
"reason": "Subject request — GDPR Art. 17",
"retainAudit": true,
"retainHistory": true,
"excludeResources": ["ConsentRecord"],
"dryRun": false
}
Dry run preview:
{
"code": 200,
"data": {
"dryRun": true,
"subject": "alice@example.com",
"affectedResources": {
"Participant": {
"records": 1,
"fieldsAnonymized": 8
},
"AdverseEvent": {
"records": 2,
"fieldsAnonymized": 3
}
},
"auditRecords": {
"count": 47,
"action": "anonymize subject references"
},
"blockers": []
}
}
Execution performs erasure in a transaction:
- For each resource with matching records, apply the anonymization method
- Anonymize audit records (retain change data, anonymize actor)
- Anonymize history records (retain event labels, anonymize actor)
- Record a
privacyErasureevent in the privacy log - Record a history event
Legal holds block erasure until resolved:
{
"blockers": [
{
"resource": "AdverseEvent",
"recordId": "ae-2026-001",
"reason": "Active regulatory filing — ICH E2B retention",
"regulation": "21 CFR §314.80"
}
]
}
If blockers exist and dryRun: false, returns HTTP 409 Conflict.
Right of Portability (GDPR Art. 20)
GET /api/v1/{domain}/privacy/subject/{identifier}/export
?format=json
Returns the same data as the access endpoint in a machine-readable, portable format for transfer to another controller. CSV format produces one file per resource.
Right of Rectification (GDPR Art. 16)
PATCH /api/v1/{domain}/privacy/subject/{identifier}/rectify
{
"resource": "Participant",
"recordId": "a1b2c3d4...",
"corrections": {
"email": "alice.johnson@newdomain.com",
"phone": "+1-555-0199"
},
"reason": "Subject request — updated contact information"
}
Applies corrections through the standard update path (respecting permissions and validation) and records a privacyRectification history event.
Right to Restrict Processing (GDPR Art. 18)
POST /api/v1/{domain}/privacy/subject/{identifier}/restrict
{
"reason": "Subject contest accuracy — GDPR Art. 18(1)(a)",
"restrictedOperations": ["update", "delete", "transition"],
"allowedOperations": ["read"]
}
Places a processing lock on the subject’s records. The runtime checks the restriction before every write operation. Read access continues.
Lift restriction:
POST /api/v1/{domain}/privacy/subject/{identifier}/unrestrict
{
"reason": "Accuracy verified — restriction lifted"
}
33.4 Anonymization Methods
The anonymization block defines how to handle erasure requests.
Three Methods
| Method | Behavior |
|---|---|
anonymize | Replace PII fields with redaction placeholders or anonymization functions |
pseudonymize | Replace PII with consistent pseudonyms (same pseudonym across all resources for the same subject) |
delete | Hard-delete records (use with caution — may break referential integrity) |
Anonymization Rules
anonymization:
method: anonymize
rules:
default: "Redacted"
firstName: "Redacted"
lastName: "Redacted"
email: "{$pseudonym}@redacted.local"
dateOfBirth: "$fuzzyDate(5 years)"
diagnosis: "$preserve"
studyId: "$preserve"
Anonymization functions:
| Function | Description |
|---|---|
Redacted | Fixed replacement string |
$fuzzyDate(offset) | Shift date by offset (e.g., 5 years) instead of deleting |
$pseudonym | Generate consistent pseudonym for this subject |
$hash6 | First 6 chars of SHA-256 hash |
$preserve | Do NOT anonymize (keep for research/integrity) |
Pseudonym Mapping
When method: pseudonymize, the runtime maintains a mapping table:
CREATE TABLE {domain}_pseudonym_map (
subject_identifier TEXT PRIMARY KEY,
pseudonym TEXT NOT NULL,
created_at TEXT NOT NULL,
salt TEXT NOT NULL
);
This enables re-identification if legally required. The table should be encrypted at rest and access-restricted to the privacy officer.
33.5 Encryption at Rest
Fields marked encrypted: true are encrypted before storage and decrypted on read:
privacy:
encryption:
provider: aes256gcm
keySource: environment
keyVariable: "${ENCRYPTION_KEY}"
keyRotation: 365 days
The runtime uses AES-256-GCM. Key rotation creates a new key and re-encrypts all encrypted fields in a background task. The old key is retained for a configurable period for disaster recovery.
33.6 Consent Management
privacy:
consent:
- purpose: primary_treatment
description: "Treatment and care"
required: true
withdrawable: false
- purpose: secondary_research
description: "Future research studies"
required: false
withdrawable: true
resources: [Participant, AdverseEvent, LabResult]
Consent API
Grant consent:
POST /api/v1/{domain}/privacy/subject/{identifier}/consent/grant
{
"purpose": "secondary_research",
"grantedBy": "subject",
"evidence": "Signed ICF-2026-001"
}
Withdraw consent:
POST /api/v1/{domain}/privacy/subject/{identifier}/consent/withdraw
{
"purpose": "secondary_research",
"reason": "Subject no longer wishes to participate"
}
When consent is withdrawn for a purpose marked withdrawable: true, the runtime:
- Records the withdrawal in the consent table
- Evaluates which resources are processed under that purpose
- Applies anonymization rules to resources processed solely under that purpose
- Records a history event
Consent for purposes marked required: true cannot be withdrawn.
33.7 Data Retention Policies
privacy:
retention:
- category: personalData
duration: 7 years
action: anonymize
from: lastActivity
- category: sensitiveData
duration: 30 years
action: archive
from: recordCreation
- category: financialData
duration: 10 years
action: delete
from: lastActivity
| Property | Description |
|---|---|
category | Privacy classification name |
duration | Retention period (e.g., 7 years, 90 days) |
action | What to do when expired: anonymize, archive, delete |
from | Start date: recordCreation, lastActivity, lastUpdate |
Retention Enforcement
The retention enforcement runs as a scheduled task. For each category:
- Query records where
fromdate +duration< now - Apply the configured
action - Record a history event for each affected record
- Log a privacy log entry
Legal Holds
POST /api/v1/{domain}/privacy/legal-hold
{
"resource": "AdverseEvent",
"recordId": "ae-2026-001",
"reason": "Active regulatory filing",
"regulation": "21 CFR §314.80",
"expiresAt": "2027-12-31"
}
Legal holds prevent retention enforcement and erasure requests from processing the record until the hold is released.
33.8 Privacy Log
All privacy operations are logged to {domain}_privacy_log:
CREATE TABLE {domain}_privacy_log (
id TEXT PRIMARY KEY,
eventType TEXT NOT NULL,
subjectIdentifier TEXT NOT NULL,
performedBy TEXT NOT NULL,
performedAt TEXT NOT NULL,
reason TEXT,
affectedResources TEXT,
detail TEXT
);
| Event Type | Description |
|---|---|
subjectAccess | Right of access request |
subjectExport | Right of portability request |
subjectErasure | Right of erasure executed |
subjectRectification | Data corrected |
processingRestriction | Processing restricted |
consentGranted | Consent granted for purpose |
consentWithdrawn | Consent withdrawn |
retentionEnforced | Retention policy applied |
legalHoldApplied | Legal hold placed |
33.9 Auto-Generated Data Map
GET /api/v1/{domain}/privacy/data-map
Returns the complete privacy data map:
{
"domain": "clinicalTrial",
"generatedAt": "2026-05-17T14:30:00Z",
"resources": {
"Participant": {
"personalData": ["firstName", "lastName", "email", "phone"],
"sensitiveData": ["dateOfBirth", "medicalHistory", "diagnosis"],
"financialData": ["ssn"],
"subjectIdentifiers": ["email"]
}
},
"retentionPolicies": [...]
}
This is the GDPR Article 30 record of processing activities — automatically generated from the schema, always current, never out of date.
33.10 Integration Points
| Feature | Integration |
|---|---|
| History (§21) | Privacy operations produce history events; erasure anonymizes actor references in history |
| Audit (§8) | Privacy operations are audited; erasure anonymizes actor in audit records |
| Schedules (§24) | Retention enforcement runs as scheduled task |
| Validation (§14) | Rectification requests are validated before applying corrections |
| Permissions (§11) | Privacy APIs are permission-gated; typically restricted to privacy officers |
| Encryption | Field-level encryption integrates with storage layer |
End of Chapter 33.
Next: Chapter 34 — Localization & i18n