Subject erasure (GDPR Article 17)
When a data subject exercises their right to be forgotten under GDPR Article 17 (or an equivalent regulation), Moʻolelo deletes every trace of that subject's personal data from the platform — across every region — and produces an audit trail that proves the deletion.
This page is the operator runbook for executing an erasure. It's not a legal guide; check with your DPO before serving any request.
What erasure does, in one sentence
It marks every vault token tied to a data subject as erased=true in every regional vault, which makes them permanently unresolvable. The global graph carries only tokens — never PII — so once the tokens become unresolvable, the personal data is gone.
What erasure does not do:
- Delete the chunks the subject's data appeared in. Chunks reference subjects by token; an erased token renders as
[REDACTED]at query time. - Delete derived data (entity rollups, aggregate statistics) that doesn't carry tokens.
- Affect audit-log entries from before the erasure was processed (the audit log itself contains tokens, not PII).
- Touch source systems. The subject must request deletion from each source separately.
The API
The webserver exposes:
POST /api/admin/tenants/{tenant}/subjects/{subject_id}/erase
Authorization: Bearer <admin-token>
Content-Type: application/json
{
"reason": "GDPR Article 17 request — ticket DSR-2026-0142",
"requestor": "dpo@acme.com"
}The response includes the count of tokens erased per region:
{
"subject": "person:7f9a4c…",
"tokens_erased": 1284,
"per_region": {
"eu-central": 982,
"us-east": 302
},
"completed_at": "2026-05-12T14:08:22Z"
}Erasure is synchronous from the caller's perspective — the response only comes back once every regional vault has confirmed the writes. Expect 10–60 seconds for a typical subject; longer if a region is temporarily unreachable (the request will retry up to the configured timeout, then return a partial result with the regions that succeeded so you can re-run for the rest).
Resolving the subject id
Erasure runs against a canonical person id (person:<sha256>), not a name or email. To resolve a real-world subject to their canonical id, use the identity-catalog lookup:
GET /api/admin/tenants/{tenant}/subjects/resolve?email=jane.doe@acme.comReturns the canonical id plus the source-system identities that resolve to it (so you can sanity-check that you're erasing the right person):
{
"subject": "person:7f9a4c…",
"linked_identities": [
{ "source": "github", "external_id": "jdoe" },
{ "source": "sharepoint", "external_id": "jane.doe@acme.com" },
{ "source": "confluence", "external_id": "62700" }
]
}If the lookup returns zero linked identities, the subject was never ingested and there's nothing to erase — record that fact in the DSR ticket and respond to the requestor.
What the audit trail looks like
Every erasure writes a row to the subject_erasures Iceberg table with:
| Column | Content |
|---|---|
tenant_id, subject_id | Who was erased |
requestor, reason | Free-text fields you populated in the API request |
tokens_erased, per_region_counts | Quantitative proof |
requested_at, completed_at | Timestamps |
regions_succeeded, regions_failed | Which regions confirmed (empty failed list means total success) |
Query the table directly via Trino, DuckDB, or any Iceberg client to produce the evidence packet for the requestor:
SELECT *
FROM subject_erasures
WHERE tenant_id = 'acme'
AND subject_id = 'person:7f9a4c…';The audit row is itself residency-clean — it carries the subject id, not the subject's name or email. Audit-log retrieval of the original identity requires a separate linked_identities lookup which is itself audited.
Re-ingestion after erasure
If the source system still holds the subject's data and the connector runs again, the subject will be re-ingested with a fresh canonical id. Erasure does not blacklist a subject — it only purges existing tokens.
If the regulator's order requires that the subject not be re-ingested, you must either:
- Delete the subject from the source system first (the right answer in most cases — Moʻolelo mirrors the source), or
- Configure a connector-level exclusion at the source. SharePoint and GitHub allow filtering on uploader / author identity; Confluence does not.
The platform itself does not maintain a "do not re-ingest" list; that's a source-system policy concern.
Partial failure handling
If one region is unavailable when erasure runs, the response includes that region in regions_failed. The recommended remediation:
- Restore the failed region (consult the region's runbook).
- Re-issue the same erasure request — it's idempotent. Tokens already erased stay erased; tokens in the failed region get processed on the retry.
- Update the DSR ticket with the final
completed_atfrom the second call.
Do not declare the erasure complete in the DSR ticket until every region has succeeded. The subject_erasures row's regions_failed field is the source of truth.
See also
- Privacy substrate enablement — how the vault interacts with the global graph.
- Data residency — why the vault is per-region in the first place.
- Audit warehouse — querying erasure history alongside other compliance events.

