Skip to main content

Compliance Ledger Taxonomy (Draft)

The compliance ledger is the append-only record of privacy/security-significant events. Each event is a single row in compliance_ledger with:

  • event_type (string)
  • subject_id (UUID, optional)
  • metadata (JSON; small, stable keys only)
  • tenant_id (scope; enforced by RLS)

Current Events (implemented)

Event TypeProduced BySubject IDRequired Metadata
privacy.user.soft_deletedUser delete handler (DELETE /users/\{id\})User IDaction (soft_delete), job_type, subject_user_id
privacy.user.anonymizedPrivacyJobsWorker (anonymize_user job)User IDjob_id, job_type, status, subject_user_id, occurred_at, actor_type, actor_id?, receipt_ref, artifact_hash, action (anonymize_user), reason?
privacy.retention.anonymizePrivacyJobsWorker (anonymize_user job with reason=retention)User IDSame as privacy.user.anonymized; reason = retention; emitted by retention worker.
privacy.user.forgottenPrivacyJobsWorker (forget_user job)User IDjob_id, job_type, status, subject_user_id, occurred_at, actor_type, actor_id?, receipt_ref, artifact_hash, action (hybrid_scrub), assetsDeleted, textRedacted, legalBasis?
privacy.dsar.completedPrivacyJobsWorker (dsar_export job)User IDjob_id, job_type, status, subject_user_id, artifact_ref, receipt_ref, occurred_at, actor_type, actor_id?, legalBasis?
privacy.dsar.failedPrivacyJobsWorker (dsar_export job failure)User ID (if known)job_id, job_type, status=failed, subject_user_id?, error, occurred_at
privacy.evidence.deletedPrivacyJobsWorker (evidence_delete job)Evidence subject IDjob_id, job_type, status, subject_user_id, occurred_at, actor_type, actor_id?, deleted
privacy.user.unlinkedPrivacyJobsWorker (unlink_user job)User IDjob_id, job_type, status, subject_user_id, org_unit_id, roles_removed, remaining_roles, orphaned, occurred_at, actor_type, actor_id?, reason?, ack_orphan_risk?, orphan_risk_flag?
privacy.user.orphanedPrivacyJobsWorker (unlink completion) or orphan sweepUser IDsubject_user_id, org_unit_id?, detected_at, reason (unlinked/orphan_sweep), occurred_at? (metadata timestamp)
assignment.override.changedAssignment override upsertAssignment IDoverrideId, userId, extraAttempts, timeLimitExtension, reason
assignment.schedule.runAssignment schedule runnerAssignment IDscheduleId, runLabel
remediation.applyRemediation applyBatch IDbatchId, rescored, appliedAt, reason, evaluation
remediation.revertRemediation revertBatch IDbatchId, revertBatchId, rescored, revertedAppliedAt
results.reapDelivery reaper auto-submit(none)closed, grace, runMs, scope, tenant, started
certificate.issuedCertificate issuanceCertificate IDprogramId, enrolmentId, userId, issuedAt, expiresAt
privacy.jobs.failedPrivacy jobs worker (any job)Job IDjob_id, job_type, status=failed, subject_user_id?, error, occurred_at

Common metadata field names (privacy events)

  • job_id (UUID) — privacy_jobs.id that produced the ledger row.
  • job_typeanonymize_user | forget_user | dsar_export | evidence_delete | restrict_user | unlink_user.
  • subject_user_id — the user being acted on.
  • tenant_id, org_unit_id? — scope identifiers (also on the row).
  • occurred_at — UTC timestamp when work completed.
  • actor_typeUSER or SYSTEM; actor_id present when USER.
  • receipt_ref — storage URI for the JSON receipt; artifact_ref for DSAR exports.
  • artifact_hash — SHA256 of the receipt payload.
  • reason? — free-form reason (e.g., retention, user_request).
  • action — canonical action label (anonymize_user, hybrid_scrub, place, lift).
  • roles_removed, remaining_roles, orphaned, orphan_detected_at? — unlink/orphan context.

Required Events (to be implemented)

These must be added to achieve full coverage. Producers should emit a ledger row using CreateComplianceLedgerEntry with tenant-scoped context:

Event TypeSource/FlowSubject IDMetadata (suggested keys)
privacy.retention.failedRetention/anonymization failure (worker or job)User IDjob_id, job_type, error, reason (retention), subject_user_id

Emission rules

  • Emit inside the scoped transaction (via TxManager) so RLS tenant isolation applies.
  • Keep metadata small and stable; avoid embedding full payloads or PII beyond identifiers already scoped by RLS.
  • Prefer explicit IDs and summary fields over raw request bodies or large blobs.

Verification

  • API: /api/v1/compliance/ledger?limit=N (requires privacy.manage) should surface these events.
  • Tests should assert that each high-stakes flow writes the expected event type with the required metadata keys.

Status: Draft — only the “Current Events” are wired today; “Required Events” must be implemented in upcoming work.