twin memory

Handling erasure requests

Answer a user's request to be forgotten, such as under the GDPR right to erasure, and keep a receipt of what was removed.

When a user asks you to erase what they told your product, you need to find what to erase, erase every copy, and keep a record that you did. twin memory gives you one call for the erasure and a receipt for the record.

This page describes what the API does. It is not legal advice about what a request requires of you.

1. Decide what to erase

The user asks to eraseCall
One message, document or noteforget with items: the ids you gave at add
A whole conversationforget with sessions: the session ids
EverythingDelete the end user in the console, or forget every session and item

forget also takes events (ev_… ids) and threads (th_… ids), as search and status return them. Each list holds at most 500 ids, and one request may mix them.

2. Look before you erase

Check what the memory holds, so you can confirm it is gone afterwards.

  • grep for a phrase the user named, such as an address or a phone number.
  • lineage of an item id lists what it became: its passages, the facts read from it and the lines of the core page built from those. That is exactly what a forget of it removes.
  • search with the user's own words.

3. Forget

curl https://api.twinmemory.dev/v1/memory/forget \
  -H "Authorization: Bearer $TWIN_MEMORY_API_KEY" \
  -H "X-Twin-Account: user_42" \
  -H "Content-Type: application/json" \
  -d '{ "items": ["pref-diet"], "sessions": ["onboarding"] }'

Forget takes the write key. Its cost grows with the size of the user's memory, because the rest of the memory is rebuilt without what went. Give it a longer timeout than a search.

4. Keep the receipt

The answer counts every table before and after, and what went from each layer:

{
  "events": 1,
  "threads_emptied": 1,
  "ledger": 1,
  "records": 0,
  "assertions": 0,
  "versions": 2,
  "log_emptied": true,
  "tables": {
    "events": { "before": 4, "after": 3 },
    "passages": { "before": 3, "after": 2 },
    "passages_fts": { "before": 3, "after": 2 },
    "files": { "before": 18, "after": 19 }
  },
  "inbox": 0,
  "queue": 0
}

This receipt is shortened; the forget reference lists every field. Store it with the request, the date and the end user id. It is your record of what was removed.

Two things to know when you read it:

  • files can rise. The record of the erasure is itself written, and pages built from the rest of the memory are written again.
  • queue is null when the queue could not be reached. The erasure of the stored memory stands. Call forget again to clear copies still waiting.

When nothing matched, the receipt is {"events": 0, "inbox": 0, "queue": 0}.

5. What happens to late copies

An item can still be on its way in when you forget it, for example from a retry. The id of every erased event is kept, without its content, so a copy that arrives later is not landed. If you add the same item again on purpose, that lifts it.

Deleted rows are not left readable in the database file: freed pages are zeroed, and the write-ahead log is emptied when it can be (log_emptied).

6. Confirm

  • status of an erased item id answers unknown.
  • versions of it answers "state": "forgotten", with one line saying when, and no word of what it said.
  • grep and search for the same words no longer return it.

Erase a user whole

In the console, open End users, find the user and delete them. Their memory is deleted as a whole, and anything still waiting for them in the queue is removed. A later add for the same id starts an empty memory.

What this does not cover

  • Copies in your own systems: your database, your logs, your analytics.
  • Text you sent to a model provider in prompts built from memory. Their retention is set by your agreement with them.
  • Encryption at rest with your own key Soon.

On this page