Data-Processor Posture

The consent-certificate product (epic docs#19) captures personal data on behalf of publishers: a witnessed form-submission opt-in. AdPriva therefore acts as a data processor on the client’s documented instructions (GDPR Art. 28), with strict minimization. Compliance buyers audit this posture first — this page is the reviewed checklist they are pointed to.

1. Roles

PartyRoleBasis
Publisher / lead-gen partnerControllerDetermines purpose (proving consent for their own marketing)
AdPrivaProcessorActs only on the controller’s documented instructions (Art. 28)
DigitalOcean, Cloudflare, Horizen, and zkVerify (not used for new batches since 2026-10-16; legacy batches only)Sub-processorsEngaged under Art. 28(4) with equivalent terms

The certifiable consent event is the atomic unit we process: we seal it, anchor a hash, and serve a retrievable certificate. We do not determine the purpose of the underlying marketing.

2. Data Processing Agreement (DPA)

A processor DPA is offered to every partner before production traffic. Required clauses:

  • Subject matter & duration — consent-evidence sealing + retention for the agreed period.
  • Nature & purpose — tamper-evident proof of opt-in; no profiling, no resale.
  • Categories of data — form_url, sha256(consent wording), checkbox state, pseudonymous subject_ref, field labels (never values), behavioural aggregates (never raw input).
  • Art. 28(3) obligations — process only on instructions; confidentiality; security (Art. 32); sub-processor flow-down; assist with Art. 15–22 requests; delete/return on termination; audit.
  • Sub-processor authorisation — general authorisation with the list in §3 and 30-day change notice.

3. Sub-processors

Sub-processorPurposeData exposedLocation
DigitalOceanApplication + database hosting (App Platform, managed MySQL)All processed fields at restEU region
zkVerifyZK proof verification of the batch Merkle root. Not used for new batches since 2026-10-16 (legacy batches only)Hash only (Merkle root) — no PIIDecentralised
HorizenOn-chain anchoring of the batch root (Anchor.sol)Hash only (Merkle root) — no PIIPublic chain
CloudflareDNS, TLS and proxy in front of AdPriva’s hosts, including the ingest server, the API and the explorer. Hosting of the tag file and the static sites. R2 object storage for the model-training export (bucket adpriva-ml-data)Proxy and hosting: requests in transit, including the visitor’s IP address and user agent. R2: a pseudonymous copy of traffic proofs (§5). Consent certificates are not exportedGlobal edge; the R2 bucket is in Cloudflare’s EU jurisdiction

Only hashes ever reach the chain. The anchoring path is the evidentiary edge, not a data flow: no personal data is published on-chain.

4. Retention

Default window1095 days (3 years) from capture. This matches the CNIL norm for prospecting data and covers the evidentiary needs of loi n° 2025-594 art. 13
Per-controller override1 to 3650 days, set by the organization owner (PATCH /org with consent_retention_days; 0 clears the override). Ten years is the hard ceiling, so a mistyped value cannot mean “forever”
When the window is fixedAt capture. Each certificate is stamped once with retention_until = capture time + window, and that date is never revised. A later change to the window applies to future captures only
What happens at expiryThe public lookup returns 410 Gone with RETENTION_EXPIRED, and the certificate drops out of subject-access listings (§6). A daily job then erases the personal columns (subject_ref, field labels, behavioural aggregates) and records when it did so

Expiry erases the personal data but keeps the record. This is deliberate, and it is the point to explain to a DPO:

  • Deleting a row would break everyone else’s proof. A certificate’s signature is a leaf of its batch’s Merkle tree, and verification rebuilds the root from every sibling’s signature. Removing one certificate would silently invalidate the proof of every other certificate in the same batch.
  • The seal does not cover the personal columns. The HMAC seal binds only the non-personal evidence: the site, the form URL, the consent-wording hash, the checkbox state, the timestamp and the nonce. Erasing subject_ref, the field labels and the behavioural aggregates leaves the seal and the on-chain proof fully re-derivable.

What survives expiry is the claim this wording, this checkbox state, at this moment, sealed and anchored, with nothing left tying it to a person. Storage limitation (Art. 5(1)(e)) is met on the data that is actually personal, and the erasure timestamp is the accountability record (Art. 5(2)). If a controller needs certificates purged before their window ends, that is a deletion request under the DPA, not a change of window.

Known limitation: the form URL. The form URL is sealed content, so it is not erased at expiry. A form whose URL carries personal data in its query string (for example ?email=…) would keep that data for the life of the record. Controllers should not put personal data in form URLs.

On termination, certificates are erased or returned per the DPA. Raw operational logs follow the data-minimization policy in §5.

5. Data minimization

Consent certificates: consent wording is hashed (never stored raw); field labels only (never values); behavioural signals are aggregates (no raw coordinates/keystrokes — tag/src/behavior.ts); the public certificate is PII-free by construction (PII columns are never SELECTed).

No IP address or user agent is stored with a consent certificate. Certificates, including Consent Evidence (cert_v: 1 and record-only cert_v: 2) certificates, are written to their own tables, which have no IP or user-agent column. The IP/UA follow-up this page used to list as an open gap, server#136, is closed; the raw values that do exist live on traffic proofs and now expire (below). The other spin-off from this posture doc, api#222 (consent subject-access endpoint), has also shipped — see §6.

Traffic proofs (page views), for context. Each view proof is stored with the request’s raw IP address and user agent, keyed hashes of both, the coarse fields derived from them (country, device class, browser, OS), and the page URL and referrer.

  • The raw IP address and user agent are emptied after 30 days (api#319). The hashes and the derived fields stay. The page URL and referrer have no separate expiry.
  • Every hour, a pseudonymous copy of traffic proofs is exported to the private R2 bucket listed in §3, for training and testing the scoring models (api#324). Per proof it holds: the proof and event ids, site and organization, server and client timestamps, the keyed IP and user-agent hashes, country, device class, browser and OS, the rule verdict and score, the anchoring flag, and the scoring service’s latest result (label, score, reasons, signals, model versions, verdict hash) with score counts. It never holds the raw IP address, user agent, page URL, referrer, nonce or signature. Exported files have no automatic expiry today.

The windows above are the current production settings.

A data subject (or the controller on their behalf) can retrieve every certificate tied to their pseudonymous subject_ref — the tag never sends raw PII as the subject reference. This has now shipped: GET /certificates?subject_ref=<ref> (api#222) is live, authenticated and tenant-scoped, so an organization only ever sees certificates for sites it owns and no controller can enumerate another controller’s subjects. The design it implements:

  • subject_ref is chosen per capture, in this order (AdPriva/server#154):
    1. The controller’s own id, hashed in the browser. When the consent form carries data-adpriva-subject-ref="<your stable user id>", the tag sends sr1: + SHA-256(<site_id> + newline + <id>) and never the id itself. The controller computes the same value to query. The site is part of the input, so one person’s id on two sites gives two unrelated keys: certificates cannot be correlated across sites.
    2. Otherwise a one-shot random id. The tag stays identifier-free: no cookie and no stable identifier. Such a certificate cannot be listed by subject; it is located by its signature, or on request by form URL and submission time.
    3. A record-only certificate has none. A form recorded without consent (data-adpriva-record-without-consent, AdPriva/server#216) never carries a subject reference, even with data-adpriva-subject-ref. The controller finds it by the evidence id it stored with its own record.
  • Subject-access is controller-mediated: the controller authenticates the subject, then queries by subject_ref. AdPriva exposes the listing to authenticated controllers, not to the open internet (a public subject_ref listing would itself be a data leak).

7. Cross-border transfers (Ch. V) & breach notification

  • Transfers: hosting is pinned to an EU region; sub-processors that may process outside the EEA are covered by SCCs referenced in the DPA. On-chain anchoring transfers no personal data (hash only), so it is out of Ch. V scope.
  • Breach notification: on a confirmed personal-data breach, AdPriva notifies the affected controller without undue delay and within 72 hours of becoming aware, with the Art. 33(3) details (nature, categories, likely consequences, measures). Controllers notify their supervisory authority. How AdPriva notices and handles incidents is described in Incident Response & Transparency.

Compliance checklist

  • Processor role documented (Art. 28) with controller/processor split
  • DPA clause set defined (§2)
  • Sub-processor list published with data exposed + location (§3)
  • Retention window stated: 3-year default, 1–3650-day override, fixed at capture, 410-Gone + erasure at expiry (§4)
  • Minimization documented (§5)
  • Art. 15 subject-access design (pseudonymous subject_ref) (§6)
  • Ch. V transfer safeguards + 72-hour breach procedure (§7)
  • No IP/UA stored on consent certificates (server#136, closed); raw IP/UA on traffic proofs emptied after 30 days (api#319, live) (§5)
  • DPA + SCCs counter-signed with the first France design partner