Adopting the GDPR and HIPAA compliance packs

Add structured linting for GDPR Art. 28/30/35 documentary artifacts and the HIPAA Security Rule BAA register to a ctxgrd-enabled repository.

What ctxgrd checks – and what it does not

ctxgrd checks the existence, required metadata, freshness, and cross-references of your compliance paper artifacts. It does not evaluate legal adequacy, determine whether processing was lawful, or verify that a safeguard actually operated. Substantive compliance is counsel’s domain.

Prerequisites

  • ctxgrd 0.41.0 or later.
  • A ctxgrd.toml in the repository root. If none exists, run ctxgrd pack add project-docs to create a baseline.

Adopt the GDPR pack

1. Add the pack

ctxgrd pack add gdpr

pack add gdpr automatically resolves the security base pack (which defines the POLICY, RISK, and VULN namespaces reused across compliance programs) and writes both packs’ namespace blocks into ctxgrd.toml in dependency order. You do not need a separate pack add security step.

Each block carries a provenance comment so pack outdated and pack migrate can track it:

# pack: [email protected] sha:…
[POLICY]


# pack: [email protected] sha:…
[ROPA]

To preview what would be written without touching ctxgrd.toml:

ctxgrd pack add gdpr --dry-run

2. Understand the namespaces

The gdpr pack defines three namespaces, all path-claimed under docs/compliance/gdpr/.

Namespace Path glob Regulation article
ROPA docs/compliance/gdpr/ropa/** Art. 30 – records of processing activities
DPIA docs/compliance/gdpr/dpia/** Art. 35 – data protection impact assessments
DPA docs/compliance/gdpr/dpa/** Art. 28 – processor / subprocessor agreement register

3. Author a ROPA record

A ROPA entry requires id, title, status, controller_or_processor, lawful_basis, data_categories, and retention. The closed vocabulary for controller_or_processor is controller, processor, or joint-controller. The closed vocabulary for lawful_basis matches the six Art. 6(1) bases: consent, contract, legal_obligation, vital_interests, public_task, or legitimate_interests.

Create a file at docs/compliance/gdpr/ropa/customer-crm.md:

---
id: ROPA-001
title: Customer CRM data processing
status: active
controller_or_processor: controller
lawful_basis: contract
data_categories: name, email, phone
retention: 7 years post-contract
---

# Customer CRM data processing

Processing activities for customer relationship management data.

Run the linter:

ctxgrd

A missing or invalid lawful_basis is caught by core.allowed-values at the ROPA namespace. An absent required field is caught by core.required-metadata.

4. The processor – DPA cross-reference rule

When controller_or_processor is processor, the gdpr.processor-dpa rule requires the ROPA entry to cross-reference the governing DPA record. The cross-reference may appear in depends_on frontmatter or as a body reference to a DPA-<number> ID.

First, create the DPA record at docs/compliance/gdpr/dpa/hosting-provider.md:

---
id: DPA-001
title: Hosting provider data processing agreement
status: executed
party: Acme Hosting Ltd
executed_date: 2025-01-15
---

Then reference it from the processor ROPA:

---
id: ROPA-002
title: Analytics processing on behalf of client
status: active
controller_or_processor: processor
lawful_basis: contract
data_categories: usage events
retention: 90 days
depends_on: [DPA-001]
---

5. Author a DPIA

A DPIA requires id, title, status, and reviewed_date. The core.calendar-freshness rule flags a DPIA whose reviewed_date is more than 365 days in the past.

---
id: DPIA-001
title: High-risk analytics pipeline assessment
status: active
reviewed_date: 2026-01-10
depends_on: [ROPA-002]
---

The depends_on cross-reference to the ROPA activity it assesses is checked by core.cross-ref. A DPIA with a stale reviewed_date produces a core.calendar-freshness diagnostic.

6. Lint and iterate

ctxgrd
ctxgrd --format json | jq '.[] | select(.code | startswith("gdpr"))'

Adopt the HIPAA pack

1. Add the pack

ctxgrd pack add hipaa

As with gdpr, pack add hipaa automatically pulls in the security base pack first.

2. Understand the namespaces

The hipaa pack defines two namespaces under docs/compliance/hipaa/.

Namespace Path glob Regulation section
SAFEGUARD docs/compliance/hipaa/safeguards/** 45 CFR 164.308/310/312 – Security Rule safeguard register
BAA docs/compliance/hipaa/baa/** 45 CFR 164.308(b)(1) / 164.314 – Business Associate Agreement register

3. Author a BAA record

A BAA entry requires id, title, status, party, executed_date, and reviewed_date. The core.calendar-freshness rule flags a BAA whose reviewed_date is more than 365 days in the past.

Create docs/compliance/hipaa/baa/lab-results-vendor.md:

---
id: BAA-001
title: Lab results processing vendor BAA
status: executed
party: MedData Systems Inc
executed_date: 2025-03-01
reviewed_date: 2026-03-01
---

# Lab results processing vendor BAA

Business Associate Agreement with MedData Systems Inc for processing lab
result data.

A BAA whose reviewed_date is stale produces a core.calendar-freshness diagnostic pointing to that field.

4. Map a safeguard to its evidence

The SAFEGUARD namespace carries the Security Rule safeguard-to-evidence register (45 CFR 164.308/310/312). A mapping requires id, title, status, and safeguard, where safeguard is drawn from the closed catalog of 42 Security Rule implementation specifications (e.g. 164.308.risk_analysis, 164.312.encryption_decryption) – core.allowed-values flags a mistyped or non-existent safeguard id.

The hipaa.safeguard-evidence rule then checks that the mapping points at implementing evidence:

  • Every in-scope safeguard must cross-reference a POLICY or an ADR (via depends_on or a body reference to its id).
  • A Required safeguard must have that evidence – no exceptions.
  • An Addressable safeguard may instead carry a justification field recording why an equivalent (or no) control is reasonable. The rule knows which safeguards are Addressable from the catalog: the generator projects the Required/Addressable flag from the regulation extract into the rule’s configuration.

A Required safeguard backed by a policy – create docs/compliance/hipaa/safeguards/risk-analysis.md:

---
id: SAFEGUARD-001
title: Risk analysis safeguard mapping
status: active
safeguard: 164.308.risk_analysis
depends_on: [POLICY-004]
---

# Risk analysis safeguard mapping

Implemented by our information-security risk-analysis policy.

An Addressable safeguard documented as not implemented – create docs/compliance/hipaa/safeguards/automatic-logoff.md:

---
id: SAFEGUARD-002
title: Automatic logoff safeguard mapping
status: active
safeguard: 164.312.automatic_logoff
justification: Workstations are single-tenant kiosks in a locked facility; session timeout is enforced at the OS image layer instead.
---

The same justification on a Required safeguard would still be flagged – hipaa.safeguard-evidence only accepts it for Addressable specifications.

Regenerating a pack from a regulation change

Each compliance pack’s pack.toml is generated from a canonical structured extract at packs/<regulation>/regulation.json. If the regulation changes – for example, the HIPAA catalog is updated or a new Art. 6 lawful basis is added – update the extract and regenerate:

cargo run --example gen_compliance_pack -- gdpr
cargo run --example gen_compliance_pack -- hipaa

Review the diff to confirm only the expected vocabulary or metadata changed, then apply the updated pack to your config:

ctxgrd pack migrate

Verify the config is clean

After adding either pack, run ctxgrd from the repo root to confirm no configuration errors:

ctxgrd

A cfg.rule-unknown diagnostic means the installed binary predates a rule in ctxgrd.toml. Reinstall the binary to match the config version.

Next steps