Skip to content

Deletion support across the board: deletes never propagate from a platform to the eVault or to other platforms #1130

Description

@Bekiboo

Description

Deletion is not supported anywhere in the W3DS sync path. A user can delete a post, a message, a chat or a binding, and that deletion reaches no other platform — and in most cases never leaves the platform's own database. A delete has to be passed down five hops: platform database → web3-adapter → eVault → AaaS → every other platform holding a copy. Every hop is either missing or actively does the wrong thing, so fixing any one of them changes nothing user-visible.

Scope of this issue is all five hops, at record level. Decommissioning an eVault or removing a registry entry (identity-level erasure) is out of scope and should be tracked separately.

1. A platform's local delete never reaches the adapter. Platform services delete through TypeORM's repository.delete(id) and repository.softDelete(id) — query-builder forms that do not fire entity subscribers. The web3adapter watchers only hook afterRemove, and no subscriber implements afterSoftRemove. Only 7 call sites in the whole monorepo use remove()/softRemove(); every other delete is invisible to sync from the moment it happens.

2. The adapter cannot express a delete. EVaultClient (infrastructure/web3-adapter/src/evault/evault.ts) exposes storeMetaEnvelope, updateMetaEnvelopeById, uploadFile, fetchMetaEnvelope and storeReference — there is no delete method. adapter.ts has no delete branch. So on the rare path where afterRemove does fire, handleChange maps the removed entity and upserts a snapshot of it into the eVault — the opposite of the intent, and worse than doing nothing. mappingDb.deleteMapping() exists but is never called, so the local↔global id mapping leaks for every deleted record.

3. eVault deletes emit no awareness packet. removeMetaEnvelope (graphql-server.ts:642) and legacy deleteMetaEnvelope (:1643) append an operation log but never call notifyAwareness. Every other mutation fans out (create :427, update :575, bulk create :804, binding documents :980/:1090, uploadFile, legacy store/update). A delete made directly against a vault — which is what the eID wallet does when unbinding a social or personal binding — is invisible to every platform.

4. Receivers ignore the operation type. The AaaS packet type already declares operation?: "create" | "update" | "delete" (services/awareness-service/api/src/types.ts:10), stores it on Packet, and passes it to subscribers (DeliveryEngine.ts:177). All 10 platform WebhookControllers contain zero references to operation; every inbound packet is treated as an upsert.

5. There is no vocabulary for "deleted". eVault deletion is a hard DETACH DELETE (db.service.ts:549) with no tombstone, and only 7 of 42 ontology schemas carry an isArchived flag. A receiver that wanted to honour a delete has nothing to write, and no way to distinguish "deleted" from "never seen" or "not authorised".

Adjacent gaps that fall out of the same missing path:

  • Orphaned blobs. uploadFile writes an object plus a File meta-envelope. Deleting the meta-envelope never calls storage.deleteObject — that is only used on the upload-rollback path (graphql-server.ts:1531), so the blob stays in object storage forever.
  • No cascade semantics. Deleting a chat, group or post leaves undefined what happens to the messages, comments, votes and signatures that reference it.

Reference

  • Related: eVault: expose creation/update/deletion metadata on MetaEnvelope in GraphQL responses #1129 (creation/update/deletion metadata on MetaEnvelope) — the tombstone-vs-hard-delete decision below is shared with that issue and should be settled once for both.
  • infrastructure/evault-core/src/core/protocol/graphql-server.ts:642, :1643 — delete resolvers, no notifyAwareness
  • infrastructure/evault-core/src/core/db/db.service.ts:549DETACH DELETE
  • infrastructure/web3-adapter/src/evault/evault.ts — client with no delete method
  • infrastructure/web3-adapter/src/db/mapping.db.ts:140deleteMapping, zero callers
  • platforms/*/api/src/web3adapter/watchers/subscriber.tsafterRemove only
  • services/awareness-service/api/src/types.ts:10, DeliveryEngine.ts:177operation already carried end to end

Acceptance Criteria

  • The eVault emits an awareness packet with operation: "delete" for removeMetaEnvelope and legacy deleteMetaEnvelope, matching how create and update already fan out.
  • EVaultClient gains a delete method, and the adapter has a delete path that calls it instead of upserting the removed entity.
  • A local delete on a platform reliably reaches the adapter — either by moving services onto remove()/softRemove(), adding afterSoftRemove hooks, or making deletes explicit in the service layer rather than relying on subscribers.
  • deleteMapping is called when a record is deleted, so the id mapping does not leak.
  • Platform webhook controllers branch on operation; a delete packet removes or archives the local row instead of upserting it.
  • Receiving a delete for an unknown record is a no-op, not an error — deletes may arrive before or without the corresponding create.
  • Deletes do not ping-pong: honouring an inbound delete must not emit an outbound delete back to the origin (the same guarantee the requestingPlatform check gives create/update).
  • A delete of a File meta-envelope also removes the backing object from storage, or an agreed GC path does.
  • Cascade behaviour is specified per ontology: deleting a parent (chat, group, post) states what happens to its children.
  • ACL is enforced on the propagated delete — a platform cannot cause a deletion it lacks the DELETE bit for.
  • Documented: how a platform should represent a deleted record, and what a consumer may assume from the absence of a record.

Design decisions to settle

  1. Tombstone or hard delete. A tombstone makes propagation, "deleted vs never existed", and late-joining consumers straightforward, but retains a record of the deleted thing — which cuts against the erasure story. A hard delete plus a log-backed delete packet keeps nothing behind, but a consumer that was offline during the delete has no way to catch up. Shared with eVault: expose creation/update/deletion metadata on MetaEnvelope in GraphQL responses #1129; decide once for both.
  2. Soft or hard on the receiving side. Should an inbound delete hard-delete the local row, or set isArchived? Only 7 of 42 ontologies have such a flag today, so either it is generalised or receivers hard-delete.

Desired Output (may vary)

A user who deletes something in one platform sees it disappear from the eVault and from every other platform that holds a copy — and a platform receiving a delete has a defined way to represent it. Deletion becomes a first-class operation in the sync path, not a gap that silently resurrects data.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions