Honour consent, export and erasure requests
Declare retention per data label, record consent, read a consent platform's string, export a person's data and erase it with a signed record.
Status Status is where the work is: Planned (in a milestone, not started), Building (tickets in progress), Preview (usable, may change), then the stability it ships at.
Stability The promise an interface ships with.
- Stable Follows semantic versioning; breaking changes need a new major, 12 months of overlap and a codemod.
- Beta Usable in production; may change before it becomes stable.
- Experimental May change or go away; needs an explicit opt-in in main.yaml.
You will declare how long each kind of data is kept, record a consent, export one person's data and erase it. It takes about 25 minutes.
You need
- A MainSDK app with
dband labelled fields (personal,sensitive,childorphi). - For erasing backups,
keyswithper-user: true.
1. Declare retention for every label in use
privacy:
retention:
personal: 365d
sensitive: 365d
logs: 30d
analytics: 13m
erase: { confirm-within: 30d, retries: 3, retry-delay: 1s }
consent:
tcf: { analytics: [1], marketing: [1, 2, 7] }A label in use with no rule fails check with MAIN-PRIV-0001. In retention, a bare m is months, so 13m is thirteen months, never minutes.
2. Record consent and gate tracking
import { privacy } from "mainsdk";
export async function onBanner(user: string, analytics: boolean) {
await privacy.consent(user).set({ purpose: "analytics", granted: analytics });
}
export async function onTcfString(user: string, tcf: string) {
await privacy.consent(user).signal({ tcf }); // no second banner
}
export function track(user: string, send: () => void) {
return privacy.guard(user, { purpose: "analytics", send }); // sends only on consent
}consent(user).get(purpose) is false until the person, or a consent platform's signal, has said yes. A string that is not TCF v2 or GPP is refused with MAIN-PRIV-0005.
3. Export
import { privacy } from "mainsdk";
declare const userId: string; // the signed-in person, from auth.require
const bundle = await privacy.export(userId); // one JSON file per driver, plus a manifestThe manifest lists each file's labels and region.
4. Erase
import { privacy } from "mainsdk";
declare const userId: string; // the signed-in person, from auth.require
const record = await privacy.erase(userId, { reason: "user request" });
privacy.verify(record); // the signature checks against the project keyErase fans out to every driver that holds the person's data and retries the ones that fail. If a driver has not confirmed after its retries, the record is partial (MAIN-PRIV-0006, logged, never thrown) and privacy.resume() retries. A person under a legal hold, privacy.hold(user, { until, reason }), is refused with MAIN-PRIV-0003; privacy.release(user) lifts it.
Register anything of your own that holds a person's data:
import { privacy } from "mainsdk";
declare const notes: { deleteFor(user: string): Promise<void>; dump(user: string): Promise<unknown> }; // your own notes store
privacy.register("support-notes", (user) => notes.deleteFor(user), { export: (user) => notes.dump(user) });5. Generate store labels
npx mainsdk privacy labels --format app-store
npx mainsdk privacy labels --format playLabels come from your data labels and drivers, never by hand.
Verify
npx mainsdk checkExit 0 means every label in use has a retention rule and the privacy section reads.
If it fails
| Code | What it means | Fix |
|---|---|---|
MAIN-PRIV-0001 | A label in use has no retention rule | Add privacy.retention.<label> |
MAIN-PRIV-0002 | A missing or malformed user id, reason, purpose or date | Pass what the call needs |
MAIN-PRIV-0003 | The person is under a legal hold | Release the hold when the case ends |
MAIN-PRIV-0004 | The privacy section does not read | Fix the key or duration the finding names |
MAIN-PRIV-0005 | A consent string is not TCF v2 or GPP | Pass the string the platform gave you |
For agents
Goal: add retention, consent and an erase route for personal data.
Touch: main.yaml (privacy) and the routes that export and erase.
Do not: write your own consent table, delete script or label list; skip a label's retention rule.
Verify: npx mainsdk check; erase a test user and privacy.verify the record.
Stop and ask if: a retention period is a legal question, or a module holds a person's data with no erase hook.Next
Was this page helpful?