GDPR ops
Every memory backend implements three operations for GDPR compliance:
| Operation | Purpose |
|---|---|
profile(user_id=) | Inspect what the agent knows about a user. |
forget(user_id=, ...) | Right-to-be-forgotten. Full or scoped erasure. |
export(user_id=) | Data-portability dump (DSAR). |
These are built-in methods on the Memory protocol, not optional
add-ons. Plumbing through the backend is the framework’s job.
profile(user_id=)
profile = await agent.memory.profile(user_id="alice")
# MemoryProfile(
# user_id="alice",
# episode_count=12,
# fact_count=5,
# last_seen=datetime(...),
# recent_sessions=["conv_42", "conv_43"],
# sample_facts=[
# Fact(subject="alice", predicate="works_at", object="Acme"),
# ...
# ],
# )Use this in:
- Customer support tickets (“here’s what we know about you”).
- Internal admin tools.
- Compliance reviews.
forget(user_id=, ...)
Full erasure of one user’s data:
await agent.memory.forget(user_id="alice")Or scoped. Just one conversation, just data older than a date:
# Just one session
await agent.memory.forget(user_id="alice", session_id="conv_42")
# Just data before a cutoff
from datetime import datetime, UTC
await agent.memory.forget(
user_id="alice",
before=datetime(2025, 1, 1, tzinfo=UTC),
)What gets deleted:
- Episodes (chat transcripts).
- Working memory blocks.
- Facts (in the bi-temporal store).
- Per-user budget bucket (resets the running totals).
What does NOT get deleted automatically:
- The audit log (immutable; legal-hold). Filter / redact at query time if you need to.
- The runtime journal (used for replay). Drop the whole journal DB if compliance requires it.
export(user_id=)
Returns a structured MemoryExport you can serialize to JSON for a
Data Subject Access Request:
export = await agent.memory.export(user_id="alice")
# MemoryExport(
# user_id="alice",
# episodes=[Episode(...), ...],
# facts=[Fact(...), ...],
# blocks={"profile": "...", ...},
# exported_at=datetime(...),
# )
import json
payload = export.model_dump_json(indent=2)The MemoryExport is a Pydantic model, .model_dump_json() gives
you a stable serialization with ISO-8601 dates.
Auditing the request
When audit_log= is wired, GDPR ops emit audit entries with action
memory_forget / memory_export. The HMAC signature covers
user_id, so a tampered log loses verification.
Backend coverage
| Backend | profile | forget | export |
|---|---|---|---|
InMemoryMemory | ✅ | ✅ | ✅ |
SqliteMemory | ✅ | ✅ | ✅ |
ChromaMemory | ✅ | ✅ | ✅ |
PostgresMemory | ✅ | ✅ | ✅ |
RedisMemory | ✅ | ✅ | ✅ |
All five backends share the same surface; the implementation differs underneath but the contract is identical.
Forget is a hard delete. There’s no “soft delete” mode. Once
you call forget(), the data is gone from memory. Test in a non-prod
environment first; back up the underlying database if you might need
recovery.
Recommended pattern
Wrap the operation in a tool so it’s auditable per-call and gated by your permissions policy:
from loomflow import Agent, tool, get_run_context
@tool(destructive=True)
async def gdpr_forget(target_user_id: str) -> str:
"""Erase all data for the given user. Requires admin approval."""
agent_ctx = get_run_context()
if not agent_ctx.metadata.get("is_admin"):
return "ERROR: requires admin role"
await agent.memory.forget(user_id=target_user_id)
return f"forgot {target_user_id}"With permissions=StandardPermissions(mode=Mode.DEFAULT) and a Slack
approval handler, every forget
call requires an explicit human approval and lands in the audit log
with user_id attribution.