Add a "Policy templates" section to docs/compliance/README.md explaining the docs/compliance/templates/ directory, the copy-to-compliance/ workflow, the [FILL IN:] placeholder convention, and the verification one-liner. Add four glossary entries: fill-in template, [FILL IN:] marker, pre-launch compliance checklist, and compliance overview. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
docs/compliance — reference examples for the compliance module
This folder contains annotated example files that document the schema used by the compliance generators. It is not the live compliance artifact directory.
| Location | Contents | Edit manually? |
|---|---|---|
docs/compliance/ (this folder) |
Schema examples and this README | Yes — static reference |
compliance/ (repo root) |
Live generated artifacts | No — run the generators |
What each file in compliance/ contains
compliance/data-map.yml
A field-level PII inventory derived from every Payload collection in the workspace.
For each collection the generator records:
- auth — whether the collection has Payload authentication enabled
- piiFields — every field carrying a
custom.piitag, plus Payload auth defaults (email, etc.) for auth collections
Each PII field entry captures the category (e.g. contact-email, identification-username), one or more purposes (e.g. account-authentication, service-delivery), whether the field is exportable (GDPR Art. 15) and restrictable (GDPR Art. 18), the source (field-tag, auth-default, or auth-override), and an optional per-field retention override.
See docs/compliance/data-map.example.yml for every field with annotations.
compliance/retention-policy.yml
A collection-level retention schedule derived from custom.retention blocks in Payload collection configs.
For each collection the generator records:
- purgeSchedule — cadence for the background purge job (
daily|weekly|monthly); required on every collection - activeRetention (optional) — how long to keep a live record before triggering deletion
- coldArchive (optional) — long-term archive window for regulatory fixed-term obligations
- postDeletion — what happens after a DSR erasure or account-closure request (
hard-deleteorpseudonymizewith an ISO 8601 grace period)
See docs/compliance/retention-policy.example.yml for every field with annotations.
compliance/sub-processors.yml
An inventory of every third-party processor that receives personal data from this application. Entries come from two sources and are merged at emit time:
- Library decision traces (
docs/library-decisions/*.md) — npm packages whereis-sub-processor: truein the frontmatter. - Manual entries (
compliance/sub-processors.manual.yml) — non-npm vendors (REST APIs, SaaS integrations, infrastructure providers).
Each entry records the package name (sort key), data-sent description, region, dpa-signed and sccs-required booleans, a contact URL, the decision status, and a source discriminator (library-trace or manual).
See docs/compliance/sub-processors.example.yml for both entry kinds with annotations.
How the files are generated
All three artifacts are regenerated together:
pnpm compliance:emit-all
Or individually:
pnpm compliance:data-map # writes compliance/data-map.yml
pnpm compliance:retention-policy # writes compliance/retention-policy.yml
pnpm compliance:sub-processors # writes compliance/sub-processors.yml
Each script also supports two diagnostic modes:
pnpm compliance:data-map -- --print # write YAML to stdout (no file written)
pnpm compliance:data-map -- --check # diff vs committed file; exit 1 on mismatch
--check mode is used by the pre-commit hook and CI drift gate to detect uncommitted changes to collection configs that would cause compliance/*.yml to drift from the source of truth.
Keeping compliance/*.yml up to date
Regenerate and commit the artifacts whenever you:
- Add or modify a Payload collection in any feature package
- Change
custom.piitags on collection fields - Change
custom.retentionblocks on collection configs - Add a library decision trace with
is-sub-processor: true - Add or update entries in
compliance/sub-processors.manual.yml
The pre-commit hook and CI gate run emit-all --check and will reject the commit or PR if the committed YAML is stale.
Annotating PII fields in a collection
Add custom.pii to any field in a Payload collection config:
{
name: "phone",
type: "text",
custom: {
pii: {
category: "contact-phone", // PiiCategory
purpose: ["transactional-notifications"], // DataProcessingPurpose[]
exportable: true,
restrictable: true,
// Optional per-field retention override:
retention: {
duration: "P1Y", // ISO 8601
trigger: "from-last-access", // "from-creation" | "from-last-access" | "after-deletion"
action: "hard-delete", // "hard-delete" | "pseudonymize"
},
},
},
},
For auth collections, email is automatically classified via PAYLOAD_AUTH_PII_DEFAULTS. Override per-collection via custom.authPii:
{
slug: "members",
auth: true,
custom: {
authPii: {
// Extend or replace the email default for this collection only:
email: {
category: "contact-email",
purpose: ["account-authentication", "marketing-communications"],
exportable: true,
restrictable: true,
},
},
},
}
See packages/core-shared/src/payload/pii-types.ts for the full list of allowed PiiCategory and DataProcessingPurpose values.
Annotating retention policy in a collection
Add custom.retention to the collection config. purgeSchedule is required on every collection; the other fields are optional:
{
slug: "profiles",
custom: {
retention: {
purgeSchedule: "daily", // Required: "daily" | "weekly" | "monthly"
activeRetention: { // Optional: expire live records
duration: "P2Y",
trigger: "from-last-access",
},
coldArchive: { // Optional: regulatory fixed-term archive
duration: "P7Y",
trigger: "from-creation",
},
postDeletion: { // Optional: override the default post-deletion behaviour
action: "pseudonymize", // "hard-delete" | "pseudonymize"
duration: "P30D",
trigger: "after-deletion",
},
},
},
}
Registering a sub-processor
From an npm library (preferred)
Add sub-processor fields to the library's decision trace in docs/library-decisions/:
---
package: "@acme/notifications-sdk"
version: "^2.3.0"
decision: approved
is-sub-processor: true
data-sent: "user email address and display name for transactional notification delivery"
region: EU
dpa-signed: true
sccs-required: false
contact: https://acme-sdk.example/privacy/dpa
---
Run pnpm compliance:sub-processors to regenerate.
Non-npm vendors (REST APIs, SaaS, infrastructure)
Create compliance/sub-processors.manual.yml if it doesn't already exist and add an entry:
- package: acme-email-service # slug-style identifier (no @ prefix)
version: "REST API v3"
data-sent: "user email address and message body for transactional email delivery"
region: EU
dpa-signed: true
sccs-required: false
contact: https://acme-email.example/legal/dpa
decision: approved
compliance/sub-processors.manual.yml is a hand-authored file committed to the repository alongside the generated compliance/sub-processors.yml. The generator merges manual entries at emit time and injects source: "manual" automatically. Do not add source: manually — it will be overwritten.
Run pnpm compliance:sub-processors after any change to regenerate compliance/sub-processors.yml.
Policy templates
The docs/compliance/templates/ directory contains fill-in-the-blank runbooks for the operational policies required before a GDPR-covered launch. These are template originals — do not edit them in place.
Copy-to-compliance/ workflow
- Copy the desired template from
docs/compliance/templates/tocompliance/(repo root):cp docs/compliance/templates/dsr-procedure.template.md compliance/dsr-procedure.md - Open the copied file and replace every
[FILL IN:]marker with the project-specific value. - Commit the filled file to the repo as a live compliance artifact (same cadence as
compliance/*.yml).
[FILL IN:] convention
Every placeholder that requires a project-specific value is marked with the literal string [FILL IN:] followed by a short description of what is expected. This makes placeholders machine-detectable and easy to audit.
To verify that all placeholders in your live artifacts have been resolved, run:
grep -rn '\[FILL IN:' compliance/
A zero-line result means all templates have been fully filled in. Any output identifies the file and line that still needs attention.
sccs-required guidance
Set sccs-required: true when:
- The vendor's primary data-residency region is outside the EEA and
- There is no EU adequacy decision covering that country (e.g. US before Privacy Shield replacement, India, most of Asia-Pacific)
Set sccs-required: false when:
- The vendor is EU/EEA-resident, or
- The vendor's country has a current EU adequacy decision, or
- The data transfer is covered by Binding Corporate Rules
When sccs-required: true, ensure SCCs are incorporated in the DPA before marking dpa-signed: true.