Skip to main content

Data export & erasure

What Mesa provides when a data-subject request arrives — "give me my data" or "delete my data" — and precisely where its coverage ends. The two halves are not symmetrical: erasure is a built, auditable workflow; export is not built, and an access request is assembled by hand. Both are stated plainly below, because a procedure that overclaims is worse than none.

Where personal data lives

Erasure touches four stores; knowing them is what makes "done" checkable.

StoreWhat it holds
message_historyThe hot tier — recent messages, as ordinary database rows
message_archive_chunks + object storageThe cold tier — archived messages in chunks encrypted under the tenant's key; the catalog row holds ranges, counts and checksums, the object holds content
message_tombstonesMarkers recording that content was deleted, without retaining it
tenant_keysThe tenant's data-encryption key, wrapped by the deployment's key-management provider

A fifth table, gdpr_deletion_requests, is the audit trail: one row per erasure request, which survives execution by design — there is always a record of what was removed, when, and by whom.

Messages also exist wherever the platform keeps them — Telegram's servers and clients, email recipients' mailboxes. Those copies are outside Mesa entirely; no Mesa operation reaches them.

Who can run this

The erasure API lives under /api/admin/gdpr and is tenant-admin scoped: the caller must hold an admin or owner role in the identity-provider organization mapped to the tenant. Every route resolves the tenant from the authenticated caller and fails closed — a request cannot erase, or even list, another tenant's data by naming it. Deployments without the identity provider configured have no erasure API at all.

Step 1 — record the request

POST /api/admin/gdpr/request records the request without deleting anything. Intake and execution are deliberately separate calls, so the destructive step is always explicit.

scopeTypeErasesRequired scopePayload
tenantEverything the tenant owns in message storage, plus its encryption key
userOne platform user's messages across the tenantsubjectUserId
chatOne channel's messageschannelId (optional timestampStart/timestampEnd window)
message_rangeA time window within one channelchannelId, timestampStart, timestampEnd
message_idsSpecific messageschannelId, messageIds

The request lands in gdpr_deletion_requests as pending, with the requesting user and the scope on the row. Inspect with GET /api/admin/gdpr/request/:id, list with GET /api/admin/gdpr/requests, and get per-status counts with GET /api/admin/gdpr/stats.

Step 2 — export, before you erase

There is no export endpoint. If the data subject has also asked for a copy of their data (access or portability), assemble it before executing erasure — erasure is irreversible, and for tenant scope it is terminal.

Assembly today is manual: read the relevant conversation history through the standard message-history API — archived content is fetched back transparently, so hot and cold are both served — and account for platform-side copies separately. There is no per-subject bundle, no generated report, and no machine-readable package. If your compliance process requires one, that is work your team does by hand today.

Step 3 — execute

POST /api/admin/gdpr/request/:id/execute runs the erasure synchronously. In order:

  1. Tombstones — rows written to message_tombstones matching the scope. From this moment the read path filters tombstoned messages out of every reply and retrieval, hot and cold — and if the tombstone lookup itself fails, reads exclude rather than include.
  2. Hot delete — matching rows removed from message_history.
  3. Crypto-shred (tenant scope only) — the tenant's wrapped key in tenant_keys is overwritten with random data and marked inactive. Every archived chunk the tenant owns remains in object storage as ciphertext that can never again be decrypted.

Only a pending request can be executed. On error the request moves to failed with the error recorded on the row; a failed request is not retried — create a new one.

What "done" means

Final statusMeaning
logically_deletedHot rows are gone and tombstones hide every archived copy from all reads. The encrypted bytes of archived copies still exist in the cold tier. This is the terminal state for every scope except tenant.
physically_purgedNo recoverable copy remains inside Mesa: hot rows deleted and the archives cryptographically unrecoverable. Today only the tenant scope reaches this state.
failedNothing further will happen; the error is on the row. Create a new request.

The row also carries timestamps for each stage — hotDeletedAt, coldLogicalDeletedAt, and coldPurgedAt — so an auditor can reconstruct what happened when.

Crypto-shred vs compaction

There are two ways to make an archived message actually unrecoverable, and Mesa has one of them.

  • Destroy the key (crypto-shred). Immediate and total, but per tenant — encryption is per tenant, so the key cannot be destroyed for one user's messages while keeping a neighbour's readable. Built, and runs as part of tenant-scope execution.
  • Rewrite the chunks (compaction). Re-write each affected archive chunk without the tombstoned messages, then delete the old object. Selective — and not built. No job today rewrites archived chunks to drop tombstoned content.

This is why sub-tenant scopes stop at logically_deleted: nothing will ever serve the content, but the encrypted bytes persist in object storage until the tenant is eventually erased whole. If your obligations require physical destruction of specific messages in the cold tier, Mesa does not do that today — record it as such in your compliance process rather than assuming otherwise.

Tenant-scope erasure is terminal

After a crypto-shred the tenant's key is inactive, and every decryption and every new encryption for that tenant refuses. Archived content is gone for good, and archival of new content fails too, because there is no active key. Treat tenant-scope erasure as offboarding, not cleanup. There is no undo.

What erasure does not touch

The honest answer to a data subject names what remains, not only what was removed:

  • Platform copies — the platform's own servers and every participant's device or mailbox.
  • Model providers — content already sent to a model provider was subject to that provider's retention terms at the time.
  • Catalog, tombstone, and request metadatamessage_archive_chunks rows (ranges, counts, checksums), the tombstones, and the audit row survive. Tombstones necessarily record identifiers — channel, message ids, subject user id — of what was removed.
  • Logs and analytics — application logs redact message bodies, but log and analytics retention is the deployment's own policy.
  • Database backups — backups of the cold tier are covered by a crypto-shred (ciphertext without a key), but database backups are not: hot rows in a pre-erasure backup, and the wrapped key in a pre-shred backup, persist until backup rotation ages them out. Completing an erasure includes your backup window.

The procedure, end to end

  1. Intake. Record who asked, how their identity was verified, and which scope covers the request. Verifying that the requester is the data subject is your process — Mesa does not do it.
  2. Export first if access or portability was also requested (see step 2). After execution there is nothing left to export.
  3. Create the deletion request; note the returned id.
  4. Execute it.
  5. Verify. Fetch the request: the status should be physically_purged for tenant scope and logically_deleted for any other, with the stage timestamps set. Spot-check that a read no longer returns the content.
  6. Record completion — and record what remains: platform copies always, cold-tier ciphertext for sub-tenant scopes, and your backup window.

A note on the word "GDPR"

The API path says gdpr, and erasure maps naturally onto Article 17. But Mesa provides mechanics, not compliance: there is no export endpoint (Articles 15 and 20 are handled manually), no deadline tracking, no data-subject identity verification, and no propagation to platforms or model providers. Those remain the controller's own process — this page tells you exactly which parts Mesa carries and which parts it does not.