Erasure at providers
When your app sends a person's data to another company, such as a payment
provider, erasing the person also asks that provider to erase them. Two
declarations make that work. The provider's package declares, in its
provider.jadpo, which call erases a person and what it takes. Your app's
connection names the field that holds the person's key at that provider:
// packages/stripe/v2024_06_20/provider.jadpoprovider { ... erasure: { call: customers.delete by: CustomerId }}// app.jadpostripe: { package: Stripe.v2024_06_20 domains: [payments] credential: config.stripe_secret_key erasure: { key: User.stripe_customer_id }}
Erasure emits a PersonErased event, which holds the person entity's name,
the person's id and when they were erased, and nothing personal, besides
the keys below. The erasure: is a declaration on the package, not part of
any capability, so Stripe and Postmark
are erased the same way.
-
Your app first, then the providers. When
User.eraseruns, Jadpo copies each connection's key, herestripe_customer_id, into the erasure event. It then erases the person in one transaction, as above. After that commits, a generated subscriber calls each provider's erasure operation, retried until the provider confirms. So a provider never holds the only record of someone your app still has. -
The completion email waits for every provider. The erasure-completed email, one of the built-in emails, goes only once every provider has confirmed. A delivery that gives up is parked for an operator and shown in the privacy request's state and in
jadpo privacy requests. It is never left quietly incomplete. -
A copied key that is itself personal data, such as an email address used as the provider's key, stays only in the outbox until it is delivered. The personal-data report lists it there.
-
A connection that receives personal data but can't erase it is a compile error. That is a connection whose package declares no
erasure:, or which names no key, or reached with a driver rather than a package; or one whose key is on one person entity while it also receives the data of another. An acceptance, a committed file in which a person states why the data stays there (tax records at an accounting provider, say), allows it, and the report lists the connection and the reason:/// Acme Books must keep invoices for seven years for tax.acceptance { changes: { accounting: ErasureNotPossible.data_stays }}
It holds for as long as the connection keeps data, so a project that already sends some needs it as soon as a change is judged, and later changes need no other. Taking it away takes away what it allowed. It covers the connection: a new field sent there later needs no other acceptance, and shows in the approval review's difference of the report. A store of files on the server's own disk is no other company, so it needs none: a file goes with the records that hold it.
-
The declarations are checked, since each lets a connection off that acceptance.
call:is an operation of the package that changes something and takes only the key: one field of its input of the typeby:names, any other optional (package.provider_invalid).key:is a field of aperson: trueentity, of that type (connection.value_invalid). -
Anything else your app sends data to, such as a CRM it calls through its own integration, can be erased by your own
PersonErasedsubscribers.
Markdown for agents: /docs/erasure-at-providers.md.Built from Jadpo main at 97f93be.