Zumik
Core concepts

Retention and purge

Delete revokes access; purge removes retained representations and returns a signed receipt. Guarantee classes, purge generations, and how resurrection is prevented.

Vague retention promises are easy to make and impossible to verify. Zumik splits deletion into two operations with sharply different meanings, returns signed evidence for the stronger one, and never claims a guarantee its underlying infrastructure cannot actually deliver.

Delete is not purge

OperationMeaning
DELETE /v2/artifacts/{id}Revoke the customer's access to a handle immediately
POST /v2/purge-jobsRemove retained representations and generate evidence
GET /v2/purge-jobs/{id}/receiptRetrieve the signed purge receipt

A delete is instant and cheap: the handle stops working. A purge is a workflow that reaches into every place a representation might persist and produces a receipt you can audit.

What a purge job does

Revoke and tombstone

Public handles stop working immediately; artifacts and bundles are marked tombstoned; dependent sessions and snapshots are invalidated.

Bump the namespace generation

The isolation-namespace purge generation increments. This is the resurrection-prevention step (see below).

Remove canonical state

Object-store blobs and materializations are removed; retained traces are removed according to their trace mode; runtime cache namespaces are invalidated.

Request provider-side deletion

Where the provider supports it (read from its capability manifest), provider-side deletion is requested and acknowledgments recorded.

Issue a signed receipt

A signed receipt records each processor's status, the overall guarantee class, and any remaining backup or provider-expiry window.

The receipt

{
  "id": "pur_01jy...",
  "object": "purge_receipt",
  "requested_at": "2026-06-09T20:00:00Z",
  "completed_at": "2026-06-09T20:04:10Z",
  "scope": { "project_id": "prj_...", "artifact_ids": ["art_..."] },
  "guarantee": "verified_namespace_invalidation",
  "processors": [
    { "name": "state_store",  "status": "purged" },
    { "name": "object_store", "status": "purged" },
    { "name": "byoc_runtime", "status": "namespace_invalidated" },
    { "name": "backup_store", "status": "expires_by",
      "expires_at": "2026-07-09T20:04:10Z" }
  ],
  "receipt_digest": "sig_..."
}

Guarantee classes

The guarantee field states exactly how strong the deletion is, from weakest to strongest:

ClassMeaning
access_revokedCustomer handles stop working immediately
best_effort_expiryA provider-controlled cache expires under provider policy
verified_namespace_invalidationStale physical cache can no longer be reused
verified_physical_purgeManaged storage and cache processors confirmed deletion
cryptographic_purgeKey destruction makes encrypted retained bytes unreadable

The receipt's overall guarantee is the weakest across all processors. One expiry-bound backend caps the whole receipt at best_effort_expiry, even if every other processor confirmed a physical purge. The platform never reports the strongest path and hides a weaker one.

This is why guarantees are honest. A provider whose manifest says manual_cache_clear_supported: false cannot be physically purged on demand, so a purge touching it yields at most best_effort_expiry with the expiry window stated - not a false claim of immediate deletion.

Resurrection prevention

The hardest part of deletion is making sure deleted content cannot quietly come back. After a purge:

  • old public IDs stay invalid forever;
  • the namespace generation has incremented, so every stale KV entry now fails the KV-compatibility check and cannot be served;
  • re-uploading identical content creates a brand-new opaque handle - it does not restore the old one;
  • internal fingerprint matches do not automatically rebuild deleted relationships.

The namespace generation lives inside the KV-compatibility key. Bumping it by one is enough to orphan every prior physical cache entry in that namespace at once, which is why purge can guarantee that stale state will not be reused even before every byte is physically gone.

On this page