Data protection duties
The data protection work that falls to whoever runs an install, answering a subject access request, generating the impact assessment, and turning on and verifying the audit log's hash chain, with what the chain cannot detect.
You hold the data, so you are its controller: the duties below are yours, and these commands do the mechanical part. All three are in both binaries; what they cover follows the modules your binary carries.
| Duty | Command | How often |
|---|---|---|
| Answer a subject access request | engine-dmo sar | When someone asks; the law allows a month |
| Keep an impact assessment | engine-dmo dpia | Once, then after any configuration change |
| Show the audit log has not been altered | engine-dmo audit verify | On a schedule you choose, with audit.hash_chain on |
| Erase a person | engine-dmo crm erase | When someone asks (engine-dmo only) |
Subject access requests
engine-dmo sar --email someone@example.com --out sar.zip --out must not exist yet. The address is trimmed and lower-cased before it is matched.The archive holds a README.txt saying what each part holds, a request.json with the records the address was found to belong to and the counts, and one JSON file per section under sections/. A section exists only when your binary carries its module.
| From | What it holds |
|---|---|
| Every install | The staff account and its sessions; audit entries the person acted in, that are about one of their records or that name the address; timeline entries; notifications; revisions authored (not the content, which is the organisation’s); mail sent (not the bodies, which are not kept); suppression; agent connections |
| CRM | The person’s record with every custom field, their organisations, consents and broadcasts |
| Member portal | Sessions; passkeys (name and dates only); invitations; first-session steps; the enquiries they made or handled |
| Claims | Claims made by the address or naming the person, their checks and letters |
| Forms | Submissions whose data holds the address, or that the CRM person made |
| Newsletter | The subscriber, with consents, preferences and campaigns received |
| AI | Suggestions and assistance the staff account asked for or accepted, suggestions about one of the person’s records, and usage by day: what, when and for which document, never the text |
No password, second factor, token or key is ever included. An address that matches nothing still produces a bundle of whatever names it. Each run writes sar.export to the audit log with the records found and the counts, never the address.
The impact assessment
engine-dmo dpia --out dpia.md This writes a data protection impact assessment in the shape of the ICO’s template, as Markdown. It reads engine.yaml and the binary’s modules, never the database, and shows a configured address by its host, never a credential. Generate it again after a configuration change, since it states the retention periods and processors the install runs with.
| Filled in for you | Left for you, marked To complete by the controller |
|---|---|
| The personal data held and about whom, the typical lawful basis, each retention period, where it is stored | Why a DPIA is needed; scope; context; purposes |
| Each processor and what it receives | The contract and transfer mechanism for each processor; who you consulted |
| How the install supports each right | Why each kind of data is necessary, and why those periods suit it |
| The controls, including whether the audit log is hash chained | Each risk with its likelihood and severity; where backups are kept; sign off |
| An annex per module that has one, such as the AI data flow statement |
Check each row against how you run the install before you rely on it: the lawful bases are typical ones, and yours are your own to confirm.
The audit chain
The audit log is append-only to application code, but someone with access to the database could change it. The optional hash chain makes that detectable: each row takes the next link in its realm’s chain (staff, member or system), and each link is a hash of the row and of the link before it.
Turning it on
audit:
hash_chain: true Restart the engine on every replica. The engine writes the setting to the database when it starts, and rows are chained from then on; earlier rows stay outside the chain. It costs a lock: each realm’s audit rows are written one transaction at a time, held until that transaction commits. It is off by default for that reason.
With the chain on, a daily audit.anchor task logs each realm’s newest link as audit chain head, with realm, seq and hash. Ship your logs off the host and you hold a record of the chain outside the database (Monitoring).
Verifying it
engine-dmo audit verify flowchart TD
run["audit verify"] --> any{"Any chained rows?"}
any -->|no| off["Exit 3: nothing is chained"]
any -->|yes| walk["Walk each realm in link order"]
walk --> ok{"Any break?"}
ok -->|no| intact["Exit 0: intact<br/>keep the printed heads"]
ok -->|yes| broken["Exit 1: each break listed"]| What it prints | Means | What to do |
|---|---|---|
| Links starting above 1 | Retention pruned the oldest rows | Expected |
rewritten by erasure | A person’s erasure replaced their details with a tombstone. The row keeps its links | Expected |
written while the chain was off | audit.hash_chain was off for a while. Those rows are not protected | Expected, if you turned it off |
BREAK content | A row changed with the database trigger off | Treat as an incident |
BREAK rewrite | A row rewritten in a transaction that recorded no erasure | Treat as an incident |
BREAK link | A forged or recomputed link | Treat as an incident |
BREAK gap | Rows deleted from inside the chain | Treat as an incident |
For an incident, keep the database and your logs as they are, and compare with your backups and the heads you recorded.
What it cannot detect
Residual risk
Someone with the database owner’s rights can disable the triggers and rebuild the whole chain, and audit verify will then report it intact. Only a head recorded outside the database detects that: a later run or log line showing the same link with a different hash means the chain was rebuilt. Keep the audit chain head log lines, or a printout of audit verify, somewhere that person cannot reach.
A site leaving the install
engine-dmo site export keeps the site’s audit rows without their links, because a subset of a chain would report gaps for ever, and it does not carry the setting. The new install’s chain starts at link 1 once its own audit.hash_chain is on.
Related
audit,saranddpia: the command referenceaudit.hash_chainandretention: the keys- Backups and restore
Backups and restore
What a complete Meridian backup contains (the database, the media store, the project and the signing key), scripts to take one and restore it, the order that keeps them consistent, and exporting one site as a standalone install.
Upgrades
The production procedure for moving an install to a new engine version (check in CI, back up, migrate, apply the schema, roll out, verify), how a rolling upgrade behaves across replicas, and how to roll back.