Privacy and GDPR alignment
Help draft ROPA tables, DPA annex bullets, and DPIA structure; map cross-border transfer mechanisms. Stress collaboration boundaries with DPO / legal—do not invent legal bases.
List purpose, categories, retention, recipients, and subprocessors in tables; the SKILL must forbid inventing legal bases—reference adopted internal policy clause IDs or leave <TBD>.
For access, erasure, portability, etc., map outputs to existing APIs or ticketing flows with SLAs and identity checks; minimize personal data in logs by default.
Compliance flow (ROPA →DPA →DPIA)
[ Inventory: processing activities / systems / data flows ]
│
▼
┌─────────────→ Table: purpose, legal basis (placeholder), categories, retention, recipients
→ROPA draft │──── Gaps tagged “needs legal/DPO”—do not auto-print “compliant—
└─────────────┘
│
▼
┌─────────────→ Processor: written DPA + annex (subject matter, duration, subprocessor changes)
→DPA align │──── Controller: instructions boundary, subcontracting, audit clauses (placeholders)
└─────────────┘
│
▼
┌─────────────→ High-risk processing →DPIA; supervisory consultation if needed (placeholder)
→DPIA │──── Necessity, risks, mitigations; version and reviewers on file
└─────────────┘
│
▼
┌─────────────→ Subject requests →ticket/API mapping; cross-border →SCC/BCR/local options
→Rights / xfer →
└─────────────┘
│
▼
—Major uncertainty →escalate DPO / legal (attach evidence and drafts)
Agent output is draft and checklist only; binding legal effect and clause choice remain with DPO, legal, or external counsel.
PII field annotation, encrypted storage, and consent implementation
PII field annotation (data model level)
// Prisma schema — annotate PII fields with comments
model User {
id String @id @default(cuid())
/// @pii:direct email address, retention 3 years
email String @unique
/// @pii:direct phone number, encrypted storage
phone String? // AES-256-GCM encrypted before storage
/// @pii:quasi date of birth, re-identifiable in combination
birthDate DateTime?
createdAt DateTime @default(now())
}
// PII encrypted storage (Node.js — AES-256-GCM)
import { createCipheriv, createDecipheriv, randomBytes } from 'crypto';
const KEY = Buffer.from(process.env.PII_ENCRYPTION_KEY!, 'base64'); // 32 bytes
export function encryptPII(plaintext: string): string {
const iv = randomBytes(12);
const cipher = createCipheriv('aes-256-gcm', KEY, iv);
const encrypted = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);
const tag = cipher.getAuthTag();
return Buffer.concat([iv, tag, encrypted]).toString('base64');
}
export function decryptPII(ciphertext: string): string {
const buf = Buffer.from(ciphertext, 'base64');
const iv = buf.subarray(0, 12);
const tag = buf.subarray(12, 28);
const encrypted = buf.subarray(28);
const decipher = createDecipheriv('aes-256-gcm', KEY, iv);
decipher.setAuthTag(tag);
return decipher.update(encrypted) + decipher.final('utf8');
}
// Access log: record who accessed which user's PII and when
await prisma.piiAccessLog.create({
data: {
accessorId: currentUser.id,
targetUserId: targetUser.id,
fields: ['email', 'phone'],
purpose: 'customer_support_ticket_#12345',
timestamp: new Date(),
}
});
User consent record data structure and queries
// Consent record table structure (SQL)
CREATE TABLE consent_records (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id),
purpose VARCHAR(100) NOT NULL, -- 'marketing_email', 'analytics', 'third_party_sharing'
version VARCHAR(20) NOT NULL, -- privacy policy version '2025-01-01'
granted BOOLEAN NOT NULL,
granted_at TIMESTAMPTZ,
withdrawn_at TIMESTAMPTZ,
ip_address INET, -- record operation source (itself PII, handle accordingly)
user_agent TEXT,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Query current valid consent (latest version, not withdrawn)
SELECT purpose, granted, version, granted_at
FROM consent_records
WHERE user_id = $1
AND withdrawn_at IS NULL
ORDER BY granted_at DESC;
- Use pseudonyms or hashes in examples—no real user data.
- Legal-basis column: only org-adopted references or
<TBD>—no fabricated applicability.
DPA (processor agreement) essentials
Standard modules typically cover: subject matter and duration, nature and purpose, personal data and data-subject categories, controller duties, processing only on documented instructions, confidentiality, security measures, subprocessors, assistance with data-subject and supervisory requests, breach assistance, deletion or return, audit and demonstration, and liability. Exact clauses depend on your jurisdiction’s templates.
Controller-side checks
- Scope matches product features; changes go through change management.
- Subprocessor list + objection mechanism (if applicable) has placeholder links.
- Transfer annex (e.g. SCC) version and signature status placeholders.
Processor-side checks
- Process only on documented instructions; no scope creep.
- Security implementation and audit cooperation as required (placeholders).
- Deletion/return timelines and proof at termination.
DPIA triggers and document structure
When systematic large-scale monitoring, automated decision-making/profiling, large-scale sensitive processing, or systematic monitoring of publicly accessible areas applies, assess whether a DPIA is mandatory (per regulator guidance and internal policy). Document processing description, necessity/proportionality, stakeholder consultation placeholders, risks, existing and planned mitigations, residual risk, and whether supervisory consultation is needed.
- Cross-link ROPA: same activity row links to the DPIA section.
- Major gaps flagged for DPO review—do not output “DPIA passed—conclusions.
Data Subject Rights API design
// Express.js — Data Subject Rights API endpoints
// 1. Data Access/Export (Right to Access & Portability)
// GET /api/gdpr/data-export
router.get('/data-export', authenticateUser, async (req, res) => {
const userId = req.user.id;
const userData = await collectUserData(userId); // aggregate all PII fields
const exportData = {
exported_at: new Date().toISOString(),
format_version: '1.0',
user_id: userId,
personal_data: {
profile: userData.profile,
orders: userData.orders,
consents: userData.consents,
}
};
await auditLog('DATA_EXPORT', userId, req.ip);
res.json(exportData);
// SLA: respond within 30 days (GDPR requirement)
});
// 2. Data Erasure (Right to Erasure)
// DELETE /api/gdpr/erasure
router.delete('/erasure', authenticateUser, async (req, res) => {
const userId = req.user.id;
// Check for legal hold obligations (e.g. tax records 7 years)
const legalHold = await checkLegalHold(userId);
if (legalHold.hasHold) {
return res.status(409).json({
error: 'LEGAL_HOLD',
reason: legalHold.reason,
retain_until: legalHold.until,
});
}
// Soft delete + redact (not immediate physical deletion)
await prisma.user.update({
where: { id: userId },
data: {
email: `deleted-${userId}@erased.invalid`,
phone: null,
deletedAt: new Date(),
erasureRequestedAt: new Date(),
}
});
await auditLog('ERASURE_COMPLETED', userId, req.ip);
res.json({ status: 'erasure_scheduled', completed_within: '30_days' });
});
// 3. Data Rectification (Right to Rectification)
// PATCH /api/gdpr/rectification
router.patch('/rectification', authenticateUser, validateBody, async (req, res) => {
const { field, new_value, reason } = req.body;
const ALLOWED_FIELDS = ['phone', 'address', 'name'];
if (!ALLOWED_FIELDS.includes(field)) {
return res.status(400).json({ error: 'FIELD_NOT_RECTIFIABLE' });
}
await prisma.user.update({
where: { id: req.user.id },
data: { [field]: field === 'phone' ? encryptPII(new_value) : new_value }
});
await auditLog('RECTIFICATION', req.user.id, req.ip, { field, reason });
res.json({ status: 'updated' });
});
Cross-border transfers and localization placeholders
List destinations, mechanisms (SCCs, BCRs, adequacy, local deployment, other lawful paths), effective status, and owner placeholders. For multiple regions, use separate columns—do not conflate frameworks.
- Subprocessor chain: one row per hop with recipient and legal basis each.
- Do not fabricate signed SCC IDs or regulator approvals.
Multi-jurisdiction terminology
When several frameworks apply, separate terms: e.g. “consent—under one statute vs GDPR consent; “legitimate interests—vs other lawful bases—cite the correct framework per document and note applicable jurisdiction.
- Do not mix concepts from different laws in one paragraph without explanation.
- Public privacy notices vs internal ROPA may link to a terminology crosswalk (placeholder).
DPA / DPIA checklist lab
Check common items to generate pasteable Markdown for tickets or Confluence; unchecked items stay [ ] for on-site completion.
For internal review only; clause choice, execution, and filings need legal / DPO sign-off. Use <TBD> for placeholders.
---
name: gdpr-privacy
description: Implement GDPR-aligned privacy controls: PII annotation, encryption, consent records, and data subject rights APIs
---
# Steps
1. Annotate PII fields in data models: @pii:direct / @pii:indirect / @pii:quasi
2. Encrypt PII at rest: AES-256-GCM, key stored in Secrets Manager (not in code)
3. PII access logging: record accessor, target user, fields accessed, purpose, timestamp
4. Consent records: SQL table with purpose/version/granted/withdrawn_at; query current valid consent
5. Data Subject Rights: implement GET /data-export, DELETE /erasure, PATCH /rectification
6. Legal hold check: before erasure, verify whether retention obligations apply
7. ROPA: table with purpose, legal basis (placeholder), categories, retention, recipients
8. DPA: align with processor agreement; note subprocessor list and audit clause placeholders
9. DPIA: trigger for high-risk processing; document necessity, risks, and mitigations
10. Cross-border transfers: list destinations, mechanisms (SCC/BCR/adequacy), and status
11. Minimize PII in logs: redact or hash PII fields by default
12. Escalate uncertainties: flag legal basis gaps as needing DPO/legal review
# Anti-patterns
- Do NOT fabricate legal bases or regulator approval IDs
- Do NOT output "DPIA passed" conclusions without evidence
- Do NOT store PII in application logs in plaintext