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, which isn't built yet. 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 aee2e8f.