GDPR erasure
Fig. 2 · invalidate() · Art. 17

Lineleaf records how every memory, embedding, summary and agent plan was derived. When a source is erased, poisoned or corrected, you see the blast radius, repair only what it touched, and keep a signed receipt.
Design Partner Preview · v0.1 Spec · Open-source core planned
$ curl -s https://lineleaf.dev/llms.txt$ lineleaf inspect gmail:msg_8812 → 5 descendants · 4 stores · dry runMemory is write-optimized. One fact fans out into an embedding, a graph edge, a summary, a cached answer, and an agent’s next action. When the source changes, gets erased, or turns out poisoned, nobody knows what else is now wrong.
The source can be erased, poisoned or simply wrong. Each time, the copies have to follow. Only the copies.
Fig. 2 · invalidate() · Art. 17

Fig. 1 · derive()

Fig. 3 · why()

Fig. 4 · plan.execute()

Pick a trigger, run the dry run, then execute the cascade and keep the signed receipt.
The user invokes their right to erasure (GDPR Art. 17) for one email. Five records were derived from it, across four stores.
Wrap the agent runtime and every tool_output → context → memory_write hop is captured as an edge. Or call the three operations yourself, with TTLs, dry runs and receipts.
agent.py
from langgraph.store.postgres import PostgresStorefrom lineleaf.integrations.langgraph import instrument store = PostgresStore.from_conn_string(DB_URL)agent = instrument(builder.compile(store=store)) # your graph, unchanged # Every run now records its own lineage:# tool_output → context → memory_write → pgvector, redis# No derive() calls. Edges carry ids and sha256 fingerprints, never content.agent.invoke({"messages": [message]}, config)server.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";import { withLineage } from "@lineleaf/sdk/mcp"; const server = withLineage(new McpServer({ name: "crm", version: "1.0.0" })); // Tool results become sources. Whatever an agent writes from them// (memories, summaries, cache entries) is recorded as their child.server.registerTool("lookup_customer", { inputSchema: { id: z.string() } }, async ({ id }) => ({ content: [{ type: "text", text: await crm.lookup(id) }],}));tracing.py
from opentelemetry.sdk.trace import TracerProviderfrom lineleaf.otel import LineageSpanProcessor provider = TracerProvider()provider.add_span_processor(LineageSpanProcessor()) # gen_ai.* spans (tool calls, retrievals, memory writes) become# parent → child lineage edges. Nothing else in your app changes.lineage.py
from lineleaf import Lineleaf ll = Lineleaf(api_key=KEY) # derive(): record where an artifact came fromll.derive( output=("mem0", "mem_7f2a"), inputs=[("gmail", "msg_8812")], op="extract", ttl="180d",) # invalidate(): a dry run. Nothing is touched yet.plan = ll.invalidate(("gmail", "msg_8812"), reason="gdpr_erasure")plan.affected # purge mem0:mem_7f2a, purge pgvector:emb_3310, … receipt = plan.execute() # adapters act, webhooks firereceipt.verify() # sha256 chain + ES256 signature # why(): trace any artifact back to its sourcesll.why(("redis", "ans_88"))lineage.ts
import { Lineleaf } from "@lineleaf/sdk"; const ll = new Lineleaf({ apiKey: process.env.LINELEAF_KEY }); // derive(): record where an artifact came fromawait ll.derive({ output: { store: "mem0", id: "mem_7f2a" }, inputs: [{ store: "gmail", id: "msg_8812" }], op: "extract", ttl: "180d",}); // invalidate(): a dry run. Nothing is touched yet.const plan = await ll.invalidate({ store: "gmail", id: "msg_8812" }, { reason: "gdpr_erasure" });plan.affected; // purge mem0:mem_7f2a, purge pgvector:emb_3310, … const receipt = await plan.execute(); // adapters act, webhooks fireawait receipt.verify(); // sha256 chain + ES256 signature // why(): trace any artifact back to its sourcesawait ll.why({ store: "redis", id: "ans_88" });Design Partner Preview · v0.1 Spec
Read the v0.1 specWhat teams do now when a source is erased, poisoned or corrected, and what each approach leaves behind.
| Approach | Cross-store cascade | Surgical (preserves valid memory) | Notifies running agents | Signed audit receipt |
|---|---|---|---|---|
| Delete raw source & hopeBackflow: summaries, embeddings and caches keep the fact alive. | No | Yesnothing else is touched, not even the copies | No | No |
| Nuke & rebuild entire user memoryDestroys valuable learned state; high token cost. | Partialonly the stores you remember | No | No | No |
| Blanket TTL expirationStale or poisoned beliefs stay active until they expire. | Noeach store on its own clock | Novalid memory expires too | No | No |
| Single-store memory delete (Mem0 / Zep native)Deletes inside that product. Your other vector indexes, Redis caches and LangGraph state are out of its reach. | Noone store | Yeswithin that store | No | Nonone across stores |
| Lineleaf lineage graphv0.1 design: cross-store cascade, surgical rebuilds, live webhooks, signed receipts. | Yesvia adapters | Yesrebuilds from valid parents | Yeslineage.stale webhooks | Yessha256 chain · ES256 |
Delete raw source & hope
Backflow: summaries, embeddings and caches keep the fact alive.
Nuke & rebuild entire user memory
Destroys valuable learned state; high token cost.
Blanket TTL expiration
Stale or poisoned beliefs stay active until they expire.
Single-store memory delete (Mem0 / Zep native)
Deletes inside that product. Your other vector indexes, Redis caches and LangGraph state are out of its reach.
Lineleaf lineage graph
v0.1 design: cross-store cascade, surgical rebuilds, live webhooks, signed receipts.
Lineleaf tracks relationships, identifiers, and fingerprints, not another copy of the underlying content.
Lineleaf is looking for teams building assistants, agents, and AI products with persistent memory. Design partners will help shape the first adapters, invalidation workflows, and audit experience.