Compliance with backd
What backd provides for privacy compliance (personal data scope, consents, erasure, retention) and what stays your job.
Apps that handle personal data usually have to answer four questions: what do you hold about me, did I agree to this, stop using my data for that and delete me. backd builds the mechanisms once; the decisions stay yours.
What backd provides, and what stays yours
| Need | What backd gives you | What is still yours |
|---|---|---|
| Knowing what is held about a person | A personal data scope per collection (personal_data:), a report of it (backd compliance scope), and the system data it adds; the same model as erasure | Declaring what exempts a collection, and checking that collections mixing several people’s data are narrowed with fields |
| A copy of the data | The scope and the per-user reads you build on (GET /_admin/users/{id}/owned, consent history); the export package itself is planned | Answering the request within your legal deadline, and verifying who asks |
| Consent | Versioned texts, records that name the exact words (a hash), withdrawal, re-acceptance, rules and email/push gating | The texts, which consents you need and why, and what each is used for |
| Erasure | A tombstone, per-collection policies, reduced consent records, retention | The policies you declare, backups, and data held outside backd (your function’s calls to other services) |
| Retention | audit.retention, functions.log_retention, compliance.consent_retention, soft-delete retention | The periods themselves: they are legal decisions |
| Minors | Nothing: age checks and parental consent belong to the app | Whether you need them; they can be recorded as consents or by functions |
What counts as a person’s data
A user’s data is, by default, the documents whose _meta.owner is that user (whatever their on_owner_delete action), the documents where their id or email is listed in a pull field of an erase policy, and the files those documents hold, plus the account itself: identities, sessions, devices, second-factor status, consents, audit records about them and the function invocations they made. Credentials, tokens, secrets and other people’s details are never part of it.
Override it per collection in its collection.yaml:
personal_data:
export: exclude
reason: "fraud scores: exempt from access requests under …"
or keep a collection but only the person’s own part (fields: [title, notes], with a reason). backd refuses to start without the reason. See Personal data scope.
A checklist per collection
Ask of each collection:
- Does it hold other people’s data next to the owner’s? (comments, reviews, shared notes.) Then narrow it with
fields, or the whole document goes to whoever owns it. - Is there a legal reason not to give it? Declare
export: excludewith that reason; a request can ask why. - Is the person named elsewhere (an array of members, a
paid_byfield)? That is whatpullandunsetinon_owner_deleteare for, and they also bring those documents into the scope. - Does it hold files? They follow their documents.
Then read backd compliance scope --realm <r> (or GET /_admin/compliance/scope): every collection with what is included, which fields and every exclusion with its reason.
Consents
Texts live with the config, backd records each acceptance and keeps what proves it, and apps, rules and functions use it. Read Consents for the whole mechanism. In short:
- Sign-up takes the consents accepted (
consents: { terms: "2026-10" }); a required one that is missing or outdated is400 consent_required. - A new version with
reaccept: trueasks people again at their next sign-in, and refuses open sessions afterreaccept_by. - Withdrawal is
DELETE /_auth/consents/<name>for optional consents; rules seeuser.consentsandhasConsent;ctx.email.sendandctx.push.sendtakerequires_consent. - Consents given outside the app are recorded by an administrator with the evidence.
Retention
Where backd keeps personal data, for how long, and who decides:
| Store | Holds | Kept | Decided by |
|---|---|---|---|
| Documents | whatever you store | until deleted, or by the collection’s policy at erasure | you: collection.yaml |
| Soft-deleted documents | the same, marked | until purged, or soft_delete.retention | you: collection.yaml |
| Audit trail | actor and target ids, actions, times | audit.retention (365 days by default) | you: realm.yaml |
| Function invocation records | caller, status, duration; the function’s own console output | functions.log_retention (7 days) | you: realm.yaml |
| Async job results | input and output of the job | functions.job_retention (24 hours) | you: realm.yaml |
| Consent records | consent, version, text hash, action, method, time; IP and user agent if consent_proof lists them | with the account; after an erase, reduced and kept compliance.consent_retention (3 years by default) | you: realm.yaml |
| Push send records | counts, never content | push.send_retention | you: realm.yaml |
| Backups | everything at that moment | your backup retention | you |
The periods are legal decisions for you. backd’s defaults are conveniences, not advice.
Recipes
A newsletter that respects a withdrawal
- Declare an optional consent (
marketing) and its text; put the checkbox in the sign-up form and a switch on the account page (backd.consents.accept/withdraw). - In the function that sends, say whose consent it needs:
ctx.email.send({ kind: "newsletter", to_user: id, requires_consent: "marketing" }). A member who withdrew is skipped and the call answersno_consent. - The tutorial’s chapter 17 builds exactly this.
Updating the terms
- Add the folder
consents/terms/<new version>/<locale>.mdfor every language, movecurrent, and addreaccept: true(and areaccept_bydate) if people have to agree again. - Deploy. Members are asked at their next sign-in; open sessions are refused after the date until they accept.
- Tell them if you want:
backd consents notify --realm <r> --consent terms. - Keep the old folder: it is the proof of what earlier members accepted.
Answering a request about a person
- Check who asks (that is on you).
- Read their records:
GET /_admin/users/{id}and/identities,/sessions,/devices,/consentsand/owned(the documents they own, by collection). - For consents given by phone or paper, record them with their evidence:
backd consents record. - For erasure,
backd user delete --realm <r> --email <e> --yesapplies the policies and reduces the consent records; the retention table above says what remains and for how long.
Minors
Age checks and parental consent belong to the app: backd has no notion of a person’s age. If you need them, collect the date or the guardian’s consent in your own collections or with a function, and record guardian consents with backd consents record and their evidence.