โ† All docs  ยท  Home

ADR 0021 โ€” Right to erasure: author-signed deletion replaces "append-only"

Context

The original design carried an implicit Nostr-style assumption: the annotation log is append-only, "deletes" are just newer annotations that supersede older ones (collapsed per (issuer, target) by /sync?latest_edits_only=true), and nothing is ever removed โ€” the plain GET /annotations/?target= read returns every annotation ever posted, forever.

That model is wrong for Freedback, for three reasons:

  1. Freedback is not a replication network. Federation happens at query time: a collection server polls feedback servers and keeps a cache. Readers and caches are never owners of the data. There is no swarm of peer relays whose copies we must treat as canonical โ€” the feedback server the author published to is the authoritative store.
  2. The author owns their feedback. The self-signed P-256 key (ADR 0003) is not just a provenance mechanism โ€” it is the ownership credential. The holder of the key that signed an annotation has the right to erase it (GDPR-style right to be forgotten), directly, without operator intervention โ€” not merely the ability to publish a newer statement on top.
  3. Deployments are not necessarily public. A Freedback server may be internal to a company. "Once published, always retrievable" is not an acceptable contract for personal data held in an identified storage the author can reach.

What deletion can and cannot promise: erasure is a guarantee at the server that executes it and a propagated instruction to protocol-level caches. Copies exported beyond protocol reach (screenshots, third-party scrapes) are out of scope โ€” as with any published data, takedown there is best-effort.

Decision

  1. Real deletion, authorized by authorship. The feedback server exposes DELETE /annotations/{dedup_id} (the WAP-conformant verb on the annotation resource). Authorization matches the identity that created the annotation:
    • Self-signed annotations: the request carries a detached ES256 signature over the RFC 8785 (JCS) canonical bytes of a delete document {"type": "Delete", "annotation": "<dedup_id>", "created": "<RFC3339>"}, verified against the same public key (kid) that signed the annotation. Same canonicalization, same signature scheme, same key โ€” no new cryptography (ADR 0002/0003 machinery reused).
    • OAuth annotations: a valid bearer resolving to the same composite (app_id, user_id) creator.
  2. Erase content, keep a content-free tombstone. On deletion the server removes the annotation (body, target, timestamps โ€” everything), retaining only {dedup_id, deleted_at, proof}. The tombstone contains no feedback content and no personal data beyond the issuer's already-public key. It exists so that the erasure itself can propagate:
    • tombstones are exposed to sync consumers (with deleted_at as their cursor position) so caches drop their copies on the next pull;
    • re-POST of a tombstoned dedup_id is rejected (410 Gone), so deleted content cannot resurrect through gossip, retries, or negentropy reconciliation.
  3. Re-statement is always possible. Because created is part of the content address (ADR 0002), an author who genuinely wants to say the same thing again produces a new created โ†’ a new dedup_id, unaffected by the old tombstone. The tombstone retires one record, not an opinion.
  4. Caches must honor tombstones. Collection servers and advanced-client local stores delete their cached copy when they see a tombstone. A cache that never syncs again simply ages out; politeness rules (ADR 0013) already bound how stale a cache may be.
  5. Edits are unchanged. Supersession per (issuer, target), newest wins, remains the edit model (/sync?latest_edits_only=true). Deletion is orthogonal: an edit says "this is my current opinion"; a delete says "remove my record".

Why not the alternatives

Consequences