§23. Right-to-be-Forgotten Tombstones
What this section covers
Cryptographic tombstones, recall-time suppression, federation propagation, and legal-hold mode. A tombstone is a signed, durable record that directs every node in the federation to suppress facts about a specified entity from all future recall responses.
Status: Experimental / opt-in source package on main
Source material: Archived evolutionary spec snapshots. This page is the maintained Spec-X home for RTBF tombstone semantics.
The tombstone signing format and federation propagation rules are still under security review. Until §23 reaches GA:
- Do not rely on tombstone federation for compliance workflows in multi-node deployments.
- Tombstone
DELETEoperations are locally reliable; cross-node propagation is best-effort. - Operators with GDPR/CCPA obligations should implement manual deletion coordination across nodes until federation is confirmed correct.
Each subsection below shows the most recent normative text from the spec source. When earlier spec drafts also contained text for the same subsection, those revisions are collapsed under a Revisions accordion beneath it — open one to see what changed. Subsections that only appear in one draft render as plain text with no accordion.
Status: Experimental. §23.1–§23.7 preserve proposed MUST/SHOULD/MAY language for ADR-008 review, but they are not part of the supported default install.
§23.1 Scope
This section defines the tombstone mechanism for compliance with right-to-be-forgotten (RTBF) obligations under data-protection frameworks (e.g., GDPR Art. 17, CCPA §1798.105).
Tombstones are a protocol primitive, not a legal determination.
Operators MUST obtain appropriate legal guidance before issuing or refusing tombstone requests.
§23.2 Tombstone Record Shape
§23.2.1 Schema
A tombstone record is a self-contained, cryptographically signed directive. It identifies the target entity, the scopes to suppress, and the admin who authorised the erasure. The signature field covers the canonical JSON of all other fields (except reason, which is excluded to allow redaction during federation rebroadcast).
TombstoneRecord:
id: string // globally unique tombstone ID; MUST use prefix "tomb_"
// followed by a UUID v7 (time-ordered) suffix
entity_uri: string // URI of the entity being tombstoned
scope: ScopePattern // which scope(s) this tombstone covers
reason: string | null // operator-supplied reason; MAY be redacted to "redacted"
// before forwarding to federation peers
signed_by: string // URI of the admin agent or service that signed the tombstone
key_id: string // SHA-256 hex of the signing key; REQUIRED
signature: string // base64url Ed25519 signature over canonical-JSON body
// (with "signature" and "reason" excluded)
created_at: ISO 8601 string
legal_hold: boolean // default false; see §24.3
§23.2.2 Invariants
idMUST be globally unique and MUST use the"tomb_"prefix followed by a UUID v7 (time-ordered) suffix.entity_uriMUST conform to the URI format defined in §9. Wildcards MUST NOT be used; each tombstone covers exactly one entity URI.signed_byMUST identify an agent or service that holds an active admin API key at the time of tombstone issuance. ThesignatureMUST be verifiable against the signing key in the org manifest.- Tombstone records MUST be stored in a dedicated
tombstonestable separate from thefactstable. Tombstones MUST NOT be stored as ordinary facts. - Tombstone records are immutable once written. To reinstate a tombstoned entity, a separate
TombstoneRevocationrecord MUST be issued.
§23.2.3 Scope Pattern
ScopePattern controls which scopes of facts the tombstone suppresses:
"*"local, team, company, public)."local" / "team" / "company" / "public"Operators SHOULD use the narrowest scope that satisfies the RTBF obligation.
A tombstone with scope: "*" is the broadest possible suppression.
§23.2.4 Canonical JSON for Signing
The canonical-JSON body for signature computation MUST be produced using RFC 8785 (JCS — JSON Canonicalization Scheme) over the TombstoneRecord object with the "signature" and "reason" fields excluded from the object before canonicalization (consistent with the field-exclusion pattern in §19.1.3). The signed_by value MUST be the plain string URI, not a reference.
Excluding "reason" from the signed body allows the issuing node to redact it to "redacted" before federation rebroadcast without invalidating the signature on peer nodes. Excluding "signature" avoids self-reference. Both exclusions MUST be applied before JCS serialization; implementations MUST NOT use empty-string sentinels in place of exclusion.
The "key_id" field MUST be included in the signed canonical body so that verifiers can resolve the correct historical signing key without trial-verifying against the entire rotation chain.
§23.2.5 Tombstone Revocation
A tombstone may be revoked by an admin who has a documented legal basis. Revocation is expressed via a separate TombstoneRevocation record rather than deleting the tombstone, because tombstone records are immutable.
TombstoneRevocationRecord:
id: string // "tombrevoke_" + UUID v7
tombstone_id: string // the "tomb_" record being revoked
reason: string // MUST be provided (e.g., court order reference)
signed_by: string
signature: string
created_at: ISO 8601 string
A revocation does NOT delete the tombstone record.
It instructs nodes to re-expose facts that were suppressed solely by the revoked tombstone. Both records are retained indefinitely for compliance evidence.
§23.3 Recall-Time Tombstone Filter
§23.3.1 Direct-Entity Suppression
At recall time, before returning any fact or memory card to the caller, the node MUST:
- Resolve the set of active tombstones for every
entity_urithat appears in the candidate result set (as theentityfield, or as a value of typeref). - Exclude any fact whose
entitymatches a tombstonedentity_uriwhere the tombstonescopecovers the fact'sscope. - Exclude any memory card whose
about_entitymatches a tombstoned entity under the same scope rule.
A tombstone is active if it is present in the tombstones table and has no corresponding tombstone_id entry in the tombstone_revocations table.
§23.3.2 Graph Reference Suppression
The tombstone filter MUST also suppress indirect graph references to tombstoned entities:
- During graph traversal (k-hop, §20.1), edges that reference a tombstoned entity as the
target_entity_uriMUST be excluded. The traversal MUST NOT propagate through tombstoned nodes. - Facts whose
valueis of typerefand whose ref value matches a tombstonedentity_uriMUST be excluded. - Memory cards in the candidate set: a card whose
about_entityis tombstoned MUST be suppressed entirely. A card where the tombstoned entity appears only inrelated_entitiesMUST have that entry omitted; if the text body still contains PII attributed to the tombstoned entity, the card MUST also be suppressed. - Facts with
derived_fromentries referencing tombstoned entities MUST represent those entries as{"hash": "...", "exists": false}.
§23.3.3 Completeness and Consistency
A result set that would have included tombstoned facts MUST be indistinguishable from one that never contained them.
No count, aggregate, or embedding may reveal the existence of
suppressed facts. Pagination totals and X-Total-Count headers MUST
be computed on the post-tombstone-filter set only.
After scope, before packing
The tombstone check MUST be applied after scope filtering and before token-budget packing.
All recall paths
Applies to GET /v1/recall, recall_instruction, and subscription event delivery.
60 s LRU refresh
Implementations MUST cache active tombstone IDs in an in-process LRU and refresh at most every 60 seconds.
SQLite / PG isolation
SQLite MUST use BEGIN IMMEDIATE; PostgreSQL MUST use at minimum READ COMMITTED with tombstone reads issued after the query plan is complete.
§23.4 Federation Propagation
§23.4.1 Outbound Rebroadcast
- When a node issues a new
TombstoneRecord, it MUST enqueue the tombstone for replication to all active federation peers under the existing federation protocol. - The tombstone MUST be replicated with the
signed_byandsignaturefields unchanged. Peer nodes MUST NOT re-sign tombstones; they MUST verify the original signature before applying the tombstone locally. - The
reasonfield MAY be redacted to"redacted"by the issuing node before rebroadcast. Peer nodes MUST NOT require a non-"redacted"reason to accept the tombstone.
§23.4.2 Inbound Tombstone Handling
On receiving an inbound tombstone from a federation peer, the node MUST:
- Verify the
signatureagainst the public key published in the org manifest for the entity URI identified bysigned_by. Resolving the relaying peer's manifest instead of thesigned_bymanifest is a protocol error. Usekey_idto select the correct historical key from the rotation chain. - Verify the
created_attimestamp is not older than the node's configured retention horizon. The ±5-minute replay-protection window from §22.5 does NOT apply to persistent tombstone records — they are durable records whosecreated_atis inherently in the past. - Write the tombstone to the local
tombstonestable if it does not already exist (idempotent onid). - Apply the recall-time filter within 60 seconds for the tombstoned entity.
Nodes MUST NOT silently drop inbound tombstones.
If tombstone verification fails, the node MUST emit a
tombstone_verification_failed audit log event and SHOULD alert the
operator. Nodes MUST accept tombstones from any trusted federation
peer, not only from the org that first issued the tombstone.
§23.4.3 Tombstone Federation Route
Tombstones are exchanged via a dedicated federation route, separate from the fact replication path. This separation ensures that tombstones can be propagated even when fact replication is paused or throttled.
GET /v1/federation/tombstones?since=<ISO8601>&limit=<N>
Authorization: Bearer <peer capability token>
→ 200 {
"tombstones": [ ...TombstoneRecord... ],
"revocations": [ ...TombstoneRevocationRecord... ],
"cursor": "<opaque pagination cursor>"
}
→ 401 if capability token invalid or scope insufficient
The peer capability token MUST include the "tombstone:read" verb in its verbs array. Peers MUST poll this route at least every 5 minutes in standard federation mode, and MUST poll within 60 seconds of receiving a tombstone_new event via the subscription channel.
§23.5 Storage-Trait Extension
§23.5.1 New Methods
tombstone(entity_uri, scope, reason, signed_by, signature, legal_hold) → TombstoneRecord
is_tombstoned(entity_uri, scope) → bool
list_tombstones(scope, since) → [TombstoneRecord]
revoke_tombstone(tombstone_id, reason, signed_by, signature) → TombstoneRevocationRecord
§23.5.2 Semantics
Idempotent tombstone()
Calling with the same entity_uri and scope when a matching active tombstone exists MUST return the existing tombstone without writing a new record.
Cached is_tombstoned()
Returns true if any active tombstone covers entity_uri with a ScopePattern that includes the given FactScope. SHOULD be cached per §23.3.3.
Revoke failures
revoke_tombstone() MUST fail with tombstone_not_found if no matching tombstone exists, and tombstone_already_revoked if a revocation already exists.
§23.5.3 Schema Migration
Migration 013a adds the tombstones and tombstone_revocations tables. Tombstones are indexed by entity_uri for the hot-path is_tombstoned() lookup. Migration 013c adds the fact_retractions table, an append-only log of retraction events needed by time-travel queries (§24).
-- Migration 013a: RTBF tombstones
CREATE TABLE tombstones (
id TEXT PRIMARY KEY,
entity_uri TEXT NOT NULL,
scope TEXT NOT NULL,
reason TEXT,
signed_by TEXT NOT NULL,
signature TEXT NOT NULL,
created_at TEXT NOT NULL,
legal_hold INTEGER NOT NULL DEFAULT 0
);
CREATE INDEX idx_tombstones_entity_uri ON tombstones(entity_uri);
CREATE TABLE tombstone_revocations (
id TEXT PRIMARY KEY,
tombstone_id TEXT NOT NULL REFERENCES tombstones(id),
reason TEXT NOT NULL,
signed_by TEXT NOT NULL,
signature TEXT NOT NULL,
created_at TEXT NOT NULL
);
-- Migration 013c: append-only retraction log (time-travel compat — §24.2.1 c.3)
CREATE TABLE fact_retractions (
id TEXT PRIMARY KEY,
fact_id TEXT NOT NULL REFERENCES facts(id),
retracted_at TEXT NOT NULL,
retracted_by TEXT
);
CREATE INDEX idx_fact_retractions_fact_id ON fact_retractions(fact_id);
CREATE INDEX idx_fact_retractions_retracted_at ON fact_retractions(retracted_at);
§23.6 Wire Format
§23.6.1 Issue a Tombstone
POST /v1/tombstones
Authorization: Bearer <admin api-key>
Content-Type: application/json
{
"entity_uri": "user:alice",
"scope": "*",
"reason": "GDPR Art. 17 erasure request #2026-042",
"legal_hold": false
}
→ 201 { ...TombstoneRecord with id, signature, created_at populated... }
→ 400 tombstone_invalid_scope
→ 400 tombstone_entity_uri_invalid
→ 403 if caller is not an admin API key
→ 409 tombstone_already_exists
§23.6.2 Check Tombstone Status
This endpoint MUST NOT be accessible with agent API keys.
The existence of a tombstone for a given entity is itself sensitive information that could reveal the identity of a data-erasure requester.
GET /v1/tombstones/:entity_uri_encoded
Authorization: Bearer <admin api-key>
→ 200 { "tombstoned": true, "tombstones": [...TombstoneRecord...], "revocations": [...] }
→ 200 { "tombstoned": false, "tombstones": [] }
→ 403 if caller is not an admin API key
§23.6.3 Revoke a Tombstone
POST /v1/tombstones/:tombstone_id/revoke
Authorization: Bearer <admin api-key>
Content-Type: application/json
{ "reason": "Court order #2026-CR-1234 reinstating data access" }
→ 200 { ...TombstoneRevocationRecord... }
→ 403 if caller is not an admin API key
→ 404 tombstone_not_found
→ 409 tombstone_already_revoked
§23.7 Error Reference
tombstone_invalid_scopescope value is not a valid ScopePattern.tombstone_entity_uri_invalidentity_uri does not conform to §9 namespace format.tombstone_verification_failedtombstone_access_deniedtombstone_not_foundtombstone_already_existstombstone_already_revokedSubsection anchors
Anchors below are provided so docs links to specific subsections always resolve, even when the subsection text lives only in earlier spec drafts.