inspeximuswe have inspected

GDPR Art. 17 · applied since 25 May 2018

How do you erase one person from an AI agent's memory, and prove it?

Every memory library has a delete call. Of the nine we read, none leaves a signed record that a named record was erased, under which request, and that it is gone. inspeximus writes each record with the subject it belongs to, erases by subject including what declares it as lineage, and issues a certificate a reader verifies with the public key alone. The transcript on this page is a real run; ids and hashes change per run, nothing else does.


the run, 2026-09-17, inspeximus 2.40.0

What does a right-to-erasure request look like in the store?

Three records, two of them Alice's. The request DSAR-17 arrives. First the dry run, then the erasure, then the certificate, then a verifier holding only the public key.

$ inspeximus writer-key --new --out key.txt
secret written to key.txt (0600)
public key: 90db3402bdba911466a781f830a5c851bc1fa1f350585e777c2a56551286b5d5

point the server at it:  INSPEXIMUS_WRITER_KEY_FILE=key.txt
pin this writer as trusted with trust_seeds={'key:90db3402bdba911466a781f830a5c851bc1fa1f350585e777c2a56551286b5d5'}
KEEP THE SECRET OUT OF GIT. It attests AUTHORSHIP, not truth.

$ inspeximus --path mem.json --receipts --receipt-key-file key.txt remember 'Alice prefers email' --source crm/alice
remembered 029e96ab3b

$ inspeximus --path mem.json --receipts --receipt-key-file key.txt remember 'Alice phone is +100' --key alice::phone --source crm/alice
remembered a917d4a167 [key=alice::phone]

$ inspeximus --path mem.json --receipts --receipt-key-file key.txt remember 'Bob phone is +300' --key bob::phone --source crm/bob
remembered 8e5e75f8c6 [key=bob::phone]

$ inspeximus --path mem.json --receipts --receipt-key-file key.txt forget-subject crm/alice --request-id DSAR-17 --dry-run
would erase 2 record(s): 2 naming the subject, 0 reached through lineage
  [direct   ] 029e96ab3b  Alice prefers email
  [direct   ] a917d4a167  Alice phone is +100

$ inspeximus --path mem.json --receipts --receipt-key-file key.txt forget-subject crm/alice --request-id DSAR-17 --basis 'Art. 17(1)(a)'
erased 2 record(s), 2 tombstone(s)

$ inspeximus --path mem.json --receipts --receipt-key-file key.txt erasure-certificate --request-id DSAR-17 --out DSAR-17.cert.json
wrote erasure certificate -> DSAR-17.cert.json  (2 erasure(s) attested, scoped to DSAR-17)

$ inspeximus erasure-verify DSAR-17.cert.json --store mem.json --expected-pubkey 90db3402bdba911466a781f830a5c851bc1fa1f350585e777c2a56551286b5d5
  OK   chain_intact
  OK   signatures_valid
  OK   signed
  OK   anchor_matches_tip
  OK   anchor_consistent
  n/a  anchor_witnessed
  OK   summary_derivable
  OK   attests_an_erasure
  OK   store_absent
  OK   store_bound
  OK   scope_intact
  NOTE NOT WITNESSED: no --expected-anchor, so the anchor was checked against itself only; a chain trimmed and re-anchored by the key holder passes. Pin an anchor you obtained outside the operator's control.

VERDICT: PASS  (2 erasure(s) attested, absence checked)

$ inspeximus --path mem.json list
- Bob phone is +300 [key=bob::phone]

Bob is untouched. Alice's two records are gone from the store's live rows, two signed tombstones say when and under which request, and the verifier pinned the writer's public key from the writer-key output. The NOTE line is part of the run: nothing on this page witnessed the anchor, and the verifier says so rather than passing quietly.

What does the certificate prove, and what does it not?

A reader checks it with the public key alone. Against a party who does not hold the signing key, the signatures and the chain prove the tombstones were not altered, the anchor describes the chain in every field (count, RFC 6962 root, tip), and, when the verifier is handed the store, that the ids in the chain are absent from that store's live rows and that the store is the one the certificate was issued from. Against the operator, who holds the key, the certificate proves consistency, not history: an operator can re-sign a rewritten chain. Pass --expected-pubkey with a key you obtained out of band, and --expected-anchor with an anchor witnessed outside the operator's control (a co-signature, a timestamp, your own earlier copy); a chain trimmed after it was witnessed then fails whoever holds the key.

It does not prove that no copy exists anywhere. Vector indexes, prompt logs, model weights and backups are outside the store, and the certificate names that scope in its own text. The write receipts beside the store keep a hash of each record's text and key; since 2.40.0 that hash includes a nonce that lives only in the record, so a receipt that outlives an erased record confirms no guessed text. Receipts written before 2.40.0 carry the unsalted hash, and mem.json.receipts.json stays personal data for those records. A record whose writer never declared derived_from carries no lineage, so the audit reports UNAUDITED rather than a pass, because a party that stops declaring lineage would otherwise always look clean.

Access and rectification travel the same path: export_subject() returns the copy of the records an erasure would remove, with provenance, for an Art. 15(3) request, and rectify() supersedes the value under its key and, when an action ledger is attached, records the actor and the reason in a rights entry (Art. 16). The EU AI Act and GDPR page maps every article to the call that answers it and marks where the mapping stops.

Which agent-memory libraries document verifiable erasure?

Of the nine memory products whose documentation we read in July 2026 for the evidence page, none documents an erasure that leaves a signed tombstone and a certificate a third party can check. They document a plain delete() or forget(). Outside those nine, Lethe (Apache-2.0, pgvector and Pinecone connectors) issues an Ed25519-signed deletion certificate with an absence re-query, and Deletable sells a crypto-erasure certificate; neither writes the subject into the record at write time, follows declared lineage, or leaves a tombstone. That is a reading of their documentation on those dates, not a proof of a universal negative. If a product added it since, open an issue and the table changes.

How do I run this on my own store?

pip install "inspeximus[crypto]"
inspeximus writer-key --new --out key.txt
inspeximus --path mem.json --receipts --receipt-key-file key.txt remember "..." --source <subject>
inspeximus --path mem.json --receipts --receipt-key-file key.txt forget-subject <subject> --request-id <DSAR id> --dry-run
inspeximus --path mem.json --receipts --receipt-key-file key.txt forget-subject <subject> --request-id <DSAR id>
inspeximus --path mem.json --receipts --receipt-key-file key.txt erasure-certificate --request-id <DSAR id> --out cert.json
inspeximus erasure-verify cert.json --store mem.json --expected-pubkey <the writer's public key>

The store has no dependencies; signing and signature checks need the [crypto] extra. The verifier is the same package, so anyone with the certificate, the store and the public key can run it. From Python the calls are remember(source=...), forget_subject(), erasure_certificate() and verify_erasure_certificate(). The MCP server exposes the same four to Claude Code, Cursor and any other client; see Claude Code, one line.